use crate::bitfield::Bitfield;
use crate::consts::BLOCK_LEN;
use crate::error::Result;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PieceState {
Missing,
Partial,
Have,
}
#[derive(Debug, Clone)]
pub struct PieceTracker {
piece_count: u32,
piece_length: u32,
have: Bitfield,
in_flight: Bitfield,
partial: BTreeMap<u32, Bitfield>,
requested: BTreeMap<u32, Bitfield>,
blocks_received_total: u64,
}
impl PieceTracker {
pub fn new(piece_count: u32, piece_length: u32) -> Self {
PieceTracker {
piece_count,
piece_length,
have: Bitfield::new(piece_count),
in_flight: Bitfield::new(piece_count),
partial: BTreeMap::new(),
requested: BTreeMap::new(),
blocks_received_total: 0,
}
}
pub fn piece_count(&self) -> u32 {
self.piece_count
}
pub fn piece_length(&self) -> u32 {
self.piece_length
}
pub fn block_count(&self, piece: u32) -> u16 {
if piece >= self.piece_count {
return 0;
}
let len = if piece + 1 == self.piece_count && self.piece_length > 0 {
self.piece_length
} else {
self.piece_length
};
(len as u64).div_ceil(BLOCK_LEN as u64) as u16
}
pub fn is_have(&self, piece: u32) -> bool {
self.have.get(piece)
}
pub fn is_in_flight(&self, piece: u32) -> bool {
self.in_flight.get(piece)
}
pub fn set_in_flight(&mut self, piece: u32, on: bool) {
if on {
self.in_flight.set(piece);
} else {
self.in_flight.clear(piece);
}
}
pub fn state(&self, piece: u32) -> PieceState {
if self.have.get(piece) {
PieceState::Have
} else if self.partial.contains_key(&piece) {
PieceState::Partial
} else {
PieceState::Missing
}
}
pub fn blocks_received(&self, piece: u32) -> u16 {
self.partial
.get(&piece)
.map(|b| b.count() as u16)
.unwrap_or(0)
}
pub fn block_received(&self, piece: u32, block: u16) -> bool {
self.partial
.get(&piece)
.map(|b| b.get(block as u32))
.unwrap_or(false)
}
pub fn block_requested(&self, piece: u32, block: u16) -> bool {
self.requested
.get(&piece)
.map(|b| b.get(block as u32))
.unwrap_or(false)
}
pub fn mark_block_requested(&mut self, piece: u32, block: u16, total_blocks: u16) {
if piece >= self.piece_count || block >= total_blocks {
return;
}
let entry = self
.requested
.entry(piece)
.or_insert_with(|| Bitfield::new(total_blocks as u32));
entry.set(block as u32);
self.in_flight.set(piece);
}
pub fn clear_block_requested(&mut self, piece: u32, block: u16, total_blocks: u16) {
if piece >= self.piece_count || block >= total_blocks {
return;
}
if let Some(entry) = self.requested.get_mut(&piece) {
entry.clear(block as u32);
if entry.count() == 0 {
self.requested.remove(&piece);
self.in_flight.clear(piece);
}
}
}
pub fn clear_piece_requests(&mut self, piece: u32) {
self.requested.remove(&piece);
self.in_flight.clear(piece);
}
pub fn mark_block_received(&mut self, piece: u32, block: u16, total_blocks: u16) -> bool {
if piece >= self.piece_count || block >= total_blocks {
return false;
}
let entry = self
.partial
.entry(piece)
.or_insert_with(|| Bitfield::new(total_blocks as u32));
if entry.get(block as u32) {
return false;
}
entry.set(block as u32);
self.blocks_received_total += 1;
true
}
pub fn piece_data_complete(&self, piece: u32, total_blocks: u16) -> bool {
self.partial
.get(&piece)
.map(|b| b.count() == total_blocks as u32)
.unwrap_or(false)
}
pub fn mark_piece_have(&mut self, piece: u32) {
self.have.set(piece);
self.in_flight.clear(piece);
self.partial.remove(&piece);
self.requested.remove(&piece);
}
pub fn reset_piece(&mut self, piece: u32) {
self.in_flight.clear(piece);
self.partial.remove(&piece);
self.requested.remove(&piece);
}
pub fn have_count(&self) -> u32 {
self.have.count()
}
pub fn have_bitfield(&self) -> &Bitfield {
&self.have
}
pub fn downloaded_bytes(&self) -> u64 {
self.blocks_received_total * BLOCK_LEN as u64
}
pub fn outstanding_pieces(&self) -> u32 {
self.piece_count - self.have.count()
}
pub fn set_have_from_bytes(&mut self, bytes: &[u8], count: u32) -> Result<()> {
let mut b = Bitfield::new(0);
b.from_bytes(bytes, count)?;
self.have = b;
Ok(())
}
pub fn candidates(&self) -> CandidateIter<'_> {
CandidateIter {
inner: self,
next: 0,
}
}
pub fn snapshot(&self) -> (Vec<u8>, Vec<(u32, Vec<u8>)>) {
let have = self.have.to_bytes();
let partial = self
.partial
.iter()
.map(|(p, b)| (*p, b.to_bytes()))
.collect();
(have, partial)
}
pub fn restore(&mut self, have: &[u8], partial: &[(u32, Vec<u8>)]) -> Result<()> {
self.have.from_bytes(have, self.piece_count)?;
self.partial.clear();
for (p, bytes) in partial {
if *p >= self.piece_count {
continue;
}
let total = self.block_count(*p);
let mut b = Bitfield::new(0);
b.from_bytes(bytes, total as u32)?;
self.partial.insert(*p, b);
}
self.blocks_received_total =
self.partial.values().map(|b| b.count() as u64).sum::<u64>() * BLOCK_LEN as u64;
Ok(())
}
}
pub struct CandidateIter<'a> {
inner: &'a PieceTracker,
next: u32,
}
impl<'a> Iterator for CandidateIter<'a> {
type Item = u32;
fn next(&mut self) -> Option<u32> {
while self.next < self.inner.piece_count {
let i = self.next;
self.next += 1;
if !self.inner.have.get(i) && !self.inner.in_flight.get(i) {
return Some(i);
}
}
None
}
}
pub fn block_count_for(piece_len: u32) -> u16 {
(piece_len as u64).div_ceil(BLOCK_LEN as u64) as u16
}
pub fn block_at(piece_len: u32, offset: u32, block_len: u32) -> (u16, u32) {
let idx = offset / block_len;
let start = idx * block_len;
let len = core::cmp::min(block_len, piece_len - start);
(idx as u16, len)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn block_accounting() {
let mut t = PieceTracker::new(4, 32 * 1024);
assert_eq!(t.block_count(0), 2);
assert!(t.mark_block_received(0, 0, 2)); assert!(!t.mark_block_received(0, 0, 2)); assert!(t.mark_block_received(0, 1, 2));
assert!(t.piece_data_complete(0, 2));
assert!(!t.piece_data_complete(1, 2));
t.mark_piece_have(0);
assert!(t.is_have(0));
assert_eq!(t.state(1), PieceState::Missing);
assert_eq!(t.have_count(), 1);
assert_eq!(t.outstanding_pieces(), 3);
}
#[test]
fn snapshot_roundtrip() {
let mut t = PieceTracker::new(8, 16 * 1024);
t.mark_block_received(2, 0, 1);
t.mark_piece_have(5);
let (have, partial) = t.snapshot();
let mut t2 = PieceTracker::new(8, 16 * 1024);
t2.restore(&have, &partial).unwrap();
assert_eq!(t.have_bitfield(), t2.have_bitfield());
assert_eq!(t.blocks_received(2), t2.blocks_received(2));
}
#[test]
fn candidate_iteration() {
let mut t = PieceTracker::new(5, 16 * 1024);
t.mark_piece_have(0);
t.set_in_flight(1, true);
let cands: Vec<u32> = t.candidates().collect();
assert_eq!(cands, vec![2, 3, 4]);
}
}