use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize, Ordering::Relaxed};
use crate::wire_protocol::Bitfield;
#[derive(Debug, Default)]
pub struct DownloadStats {
downloaded_bytes: AtomicU64,
uploaded_bytes: AtomicU64,
wasted_bytes: AtomicU64,
verified_bytes: AtomicU64,
resumed_bytes: AtomicU64,
total_bytes: AtomicU64,
completed_pieces: AtomicU32,
resumed_pieces: AtomicU32,
total_pieces: AtomicU32,
in_flight_pieces: AtomicU32,
hash_failures: AtomicU32,
active_peers: AtomicUsize,
}
impl DownloadStats {
pub fn add_downloaded(&self, bytes: u64) {
self.downloaded_bytes.fetch_add(bytes, Relaxed);
}
pub fn add_uploaded(&self, bytes: u64) {
self.uploaded_bytes.fetch_add(bytes, Relaxed);
}
pub fn add_wasted(&self, bytes: u64) {
self.wasted_bytes.fetch_add(bytes, Relaxed);
}
pub fn piece_verified(&self, bytes: u64) {
self.verified_bytes.fetch_add(bytes, Relaxed);
self.completed_pieces.fetch_add(1, Relaxed);
}
pub fn piece_failed_hash(&self) {
self.hash_failures.fetch_add(1, Relaxed);
}
pub fn set_totals(&self, pieces: u32, bytes: u64) {
self.total_pieces.store(pieces, Relaxed);
self.total_bytes.store(bytes, Relaxed);
}
pub fn set_resumed(&self, pieces: u32, bytes: u64) {
self.resumed_pieces.store(pieces, Relaxed);
self.resumed_bytes.store(bytes, Relaxed);
}
pub fn piece_claimed(&self) {
self.in_flight_pieces.fetch_add(1, Relaxed);
}
pub fn piece_released(&self) {
let _ = self
.in_flight_pieces
.fetch_update(Relaxed, Relaxed, |count| Some(count.saturating_sub(1)));
}
pub fn peer_connected(&self) {
self.active_peers.fetch_add(1, Relaxed);
}
pub fn peer_disconnected(&self) {
let _ = self
.active_peers
.fetch_update(Relaxed, Relaxed, |count| Some(count.saturating_sub(1)));
}
pub fn downloaded_bytes(&self) -> u64 {
self.downloaded_bytes.load(Relaxed)
}
pub fn uploaded_bytes(&self) -> u64 {
self.uploaded_bytes.load(Relaxed)
}
pub fn wasted_bytes(&self) -> u64 {
self.wasted_bytes.load(Relaxed)
}
pub fn verified_bytes(&self) -> u64 {
self.verified_bytes.load(Relaxed)
}
pub fn total_bytes(&self) -> u64 {
self.total_bytes.load(Relaxed)
}
pub fn held_bytes(&self) -> u64 {
self.verified_bytes() + self.resumed_bytes.load(Relaxed)
}
pub fn held_pieces(&self) -> u32 {
self.completed_pieces() + self.resumed_pieces.load(Relaxed)
}
pub fn remaining_bytes(&self) -> u64 {
self.total_bytes().saturating_sub(self.held_bytes())
}
pub fn completed_pieces(&self) -> u32 {
self.completed_pieces.load(Relaxed)
}
pub fn total_pieces(&self) -> u32 {
self.total_pieces.load(Relaxed)
}
pub fn in_flight_pieces(&self) -> u32 {
self.in_flight_pieces.load(Relaxed)
}
pub fn hash_failures(&self) -> u32 {
self.hash_failures.load(Relaxed)
}
pub fn active_peers(&self) -> usize {
self.active_peers.load(Relaxed)
}
pub fn is_complete(&self) -> bool {
let total = self.total_pieces();
total > 0 && self.held_pieces() >= total
}
}
#[derive(Clone, Debug)]
pub struct PieceProgress {
pub completed_pieces: u32,
pub total_pieces: u32,
pub verified_bytes: u64,
pub total_bytes: u64,
pub bitfield: Bitfield,
pub extracted: bool,
}
impl Default for PieceProgress {
fn default() -> Self {
Self {
completed_pieces: 0,
total_pieces: 0,
verified_bytes: 0,
total_bytes: 0,
bitfield: Bitfield(Vec::new()),
extracted: false,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum DownloadState {
#[default]
Starting,
Downloading,
Finalizing,
Seeding,
}
#[derive(Clone, Debug, Default)]
pub struct DownloadStatus {
pub state: DownloadState,
pub pieces: PieceProgress,
pub downloaded_bytes: u64,
pub uploaded_bytes: u64,
pub wasted_bytes: u64,
pub hash_failures: u32,
pub in_flight_pieces: u32,
pub active_peers: usize,
pub download_rate: u64,
pub upload_rate: u64,
}
impl DownloadStatus {
pub fn progress(&self) -> f64 {
if self.pieces.total_bytes == 0 {
return 0.0;
}
self.pieces.verified_bytes as f64 / self.pieces.total_bytes as f64
}
}