mirror of
https://github.com/YGGverse/aquatic.git
synced 2026-03-31 17:55:36 +00:00
123 lines
3.2 KiB
Rust
123 lines
3.2 KiB
Rust
use std::net::{Ipv4Addr, Ipv6Addr};
|
|
|
|
use aquatic_common::{
|
|
AmortizedIndexMap, IndexMap, SecondsSinceServerStart, ServerStartInstant, ValidUntil,
|
|
};
|
|
use aquatic_http_protocol::common::{AnnounceEvent, InfoHash, PeerId};
|
|
use aquatic_http_protocol::response::ResponsePeer;
|
|
|
|
pub trait Ip: ::std::fmt::Debug + Copy + Eq + ::std::hash::Hash {}
|
|
|
|
impl Ip for Ipv4Addr {}
|
|
impl Ip for Ipv6Addr {}
|
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
|
pub enum PeerStatus {
|
|
Seeding,
|
|
Leeching,
|
|
Stopped,
|
|
}
|
|
|
|
impl PeerStatus {
|
|
/// Determine peer status from announce event and number of bytes left.
|
|
///
|
|
/// Likely, the last branch will be taken most of the time.
|
|
#[inline]
|
|
pub fn from_event_and_bytes_left(event: AnnounceEvent, opt_bytes_left: Option<usize>) -> Self {
|
|
if let AnnounceEvent::Stopped = event {
|
|
Self::Stopped
|
|
} else if let Some(0) = opt_bytes_left {
|
|
Self::Seeding
|
|
} else {
|
|
Self::Leeching
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Peer<I: Ip> {
|
|
pub ip_address: I,
|
|
pub port: u16,
|
|
pub status: PeerStatus,
|
|
pub valid_until: ValidUntil,
|
|
}
|
|
|
|
impl<I: Ip> Peer<I> {
|
|
pub fn to_response_peer(&self) -> ResponsePeer<I> {
|
|
ResponsePeer {
|
|
ip_address: self.ip_address,
|
|
port: self.port,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct PeerMapKey<I: Ip> {
|
|
pub peer_id: PeerId,
|
|
pub ip_address: I,
|
|
}
|
|
|
|
pub type PeerMap<I> = IndexMap<PeerMapKey<I>, Peer<I>>;
|
|
|
|
pub struct TorrentData<I: Ip> {
|
|
pub peers: PeerMap<I>,
|
|
pub num_seeders: usize,
|
|
pub num_leechers: usize,
|
|
}
|
|
|
|
impl<I: Ip> Default for TorrentData<I> {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
Self {
|
|
peers: Default::default(),
|
|
num_seeders: 0,
|
|
num_leechers: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type TorrentMap<I> = AmortizedIndexMap<InfoHash, TorrentData<I>>;
|
|
|
|
#[derive(Default)]
|
|
pub struct TorrentMaps {
|
|
pub ipv4: TorrentMap<Ipv4Addr>,
|
|
pub ipv6: TorrentMap<Ipv6Addr>,
|
|
}
|
|
|
|
impl TorrentMaps {
|
|
pub fn clean(&mut self, server_start_instant: ServerStartInstant) {
|
|
let now = server_start_instant.seconds_elapsed();
|
|
|
|
Self::clean_torrent_map(&mut self.ipv4, now);
|
|
Self::clean_torrent_map(&mut self.ipv6, now);
|
|
}
|
|
|
|
fn clean_torrent_map<I: Ip>(torrent_map: &mut TorrentMap<I>, now: SecondsSinceServerStart) {
|
|
torrent_map.retain(|_, torrent_data| {
|
|
let num_seeders = &mut torrent_data.num_seeders;
|
|
let num_leechers = &mut torrent_data.num_leechers;
|
|
|
|
torrent_data.peers.retain(|_, peer| {
|
|
if peer.valid_until.valid(now) {
|
|
true
|
|
} else {
|
|
match peer.status {
|
|
PeerStatus::Seeding => {
|
|
*num_seeders -= 1;
|
|
}
|
|
PeerStatus::Leeching => {
|
|
*num_leechers -= 1;
|
|
}
|
|
_ => (),
|
|
};
|
|
|
|
false
|
|
}
|
|
});
|
|
|
|
!torrent_data.peers.is_empty()
|
|
});
|
|
|
|
torrent_map.shrink_to_fit();
|
|
}
|
|
}
|