use std::collections::HashSet;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Bitfield {
bits: Vec<u8>,
len: usize,
}
impl Bitfield {
pub fn new(len: usize) -> Self {
Bitfield {
bits: vec![0u8; len.div_ceil(8)],
len,
}
}
pub fn from_bytes(bytes: &[u8], len: usize) -> Self {
let mut bf = Bitfield::new(len);
let n = bytes.len().min(bf.bits.len());
bf.bits[..n].copy_from_slice(&bytes[..n]);
if !len.is_multiple_of(8) {
if let Some(last) = bf.bits.last_mut() {
*last &= 0xffu8 << (8 - len % 8);
}
}
bf
}
pub fn from_wire(bytes: &[u8], len: usize) -> Option<Self> {
if bytes.len() != len.div_ceil(8) {
return None;
}
if !len.is_multiple_of(8) {
let spare = 0xffu8 >> (len % 8);
if bytes.last().is_some_and(|&b| b & spare != 0) {
return None;
}
}
Some(Bitfield {
bits: bytes.to_vec(),
len,
})
}
pub fn merge(&mut self, other: &Bitfield) {
for (a, b) in self.bits.iter_mut().zip(&other.bits) {
*a |= *b;
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn has(&self, i: usize) -> bool {
if i >= self.len {
return false;
}
self.bits[i / 8] & (0x80 >> (i % 8)) != 0
}
pub fn set(&mut self, i: usize) {
if i < self.len {
self.bits[i / 8] |= 0x80 >> (i % 8);
}
}
pub fn unset(&mut self, i: usize) {
if i < self.len {
self.bits[i / 8] &= !(0x80 >> (i % 8));
}
}
pub fn count(&self) -> usize {
(0..self.len).filter(|&i| self.has(i)).count()
}
pub fn is_complete(&self) -> bool {
(0..self.len).all(|i| self.has(i))
}
pub fn as_bytes(&self) -> &[u8] {
&self.bits
}
}
pub struct Picker {
availability: Vec<u32>,
}
impl Picker {
pub fn new(num_pieces: usize) -> Self {
Picker {
availability: vec![0; num_pieces],
}
}
pub fn add_have(&mut self, index: usize) {
if let Some(c) = self.availability.get_mut(index) {
*c += 1;
}
}
pub fn add_bitfield(&mut self, bf: &Bitfield) {
for i in 0..bf.len() {
if bf.has(i) {
self.add_have(i);
}
}
}
pub fn remove_bitfield(&mut self, bf: &Bitfield) {
for i in 0..bf.len() {
if bf.has(i) {
if let Some(c) = self.availability.get_mut(i) {
*c = c.saturating_sub(1);
}
}
}
}
pub fn pick(
&self,
ours: &Bitfield,
peer_has: &Bitfield,
exclude: &HashSet<usize>,
) -> Option<usize> {
let mut best: Option<(u32, usize)> = None;
for i in 0..self.availability.len() {
if ours.has(i) || !peer_has.has(i) || exclude.contains(&i) {
continue;
}
let avail = self.availability[i];
match best {
Some((b, _)) if b <= avail => {}
_ => best = Some((avail, i)),
}
}
best.map(|(_, i)| i)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bitfield_msb_first() {
let mut bf = Bitfield::new(10);
assert!(!bf.has(0));
bf.set(0);
bf.set(9);
assert!(bf.has(0));
assert!(bf.has(9));
assert!(!bf.has(5));
assert_eq!(bf.count(), 2);
assert_eq!(bf.as_bytes(), &[0x80, 0x40]);
assert!(!bf.is_complete());
}
#[test]
fn from_bytes_round_trips() {
let bf = Bitfield::from_bytes(&[0b1010_0000], 3);
assert!(bf.has(0));
assert!(!bf.has(1));
assert!(bf.has(2));
}
#[test]
fn from_wire_validates_length_and_spare_bits() {
assert!(Bitfield::from_wire(&[0xff, 0xc0], 10).is_some());
assert!(Bitfield::from_wire(&[0xff], 10).is_none(), "too short");
assert!(
Bitfield::from_wire(&[0xff, 0xc0, 0], 10).is_none(),
"too long"
);
assert!(
Bitfield::from_wire(&[0xff, 0xc1], 10).is_none(),
"spare bit set"
);
assert!(Bitfield::from_wire(&[0xff], 8).is_some());
assert!(Bitfield::from_wire(&[], 0).is_some());
}
#[test]
fn merge_and_unset() {
let mut a = Bitfield::new(10);
a.set(1);
let mut b = Bitfield::new(10);
b.set(9);
a.merge(&b);
assert!(a.has(1) && a.has(9));
a.unset(1);
assert!(!a.has(1) && a.has(9));
}
#[test]
fn from_bytes_clears_spare_bits() {
let bf = Bitfield::from_bytes(&[0xff, 0xff], 10);
assert_eq!(bf.as_bytes(), &[0xff, 0xc0]);
assert_eq!(bf.count(), 10);
}
#[test]
fn picker_prefers_rarest() {
let mut p = Picker::new(4);
let ours = Bitfield::new(4);
let mut a = Bitfield::new(4);
a.set(0);
a.set(1);
a.set(2);
p.add_bitfield(&a); p.add_have(0);
p.add_have(0);
p.add_have(2);
let mut peer = Bitfield::new(4);
peer.set(0);
peer.set(1);
peer.set(2);
let exclude = HashSet::new();
assert_eq!(p.pick(&ours, &peer, &exclude), Some(1));
let exclude: HashSet<usize> = [1].into_iter().collect();
assert_eq!(p.pick(&ours, &peer, &exclude), Some(2));
}
#[test]
fn picker_skips_owned_and_unavailable() {
let p = Picker::new(3);
let mut ours = Bitfield::new(3);
ours.set(0);
let mut peer = Bitfield::new(3);
peer.set(0); peer.set(1); assert_eq!(p.pick(&ours, &peer, &HashSet::new()), Some(1));
}
}