diff --git a/src/databases/sqlite.rs b/src/databases/sqlite.rs index ff080306d..a329b6bfc 100644 --- a/src/databases/sqlite.rs +++ b/src/databases/sqlite.rs @@ -7,7 +7,7 @@ use r2d2_sqlite::SqliteConnectionManager; use crate::databases::database; use crate::databases::database::{Database, Error}; -use crate::protocol::clock::clock::SinceUnixEpoch; +use crate::protocol::clock::clock::DurationSinceUnixEpoch; use crate::tracker::key::AuthKey; use crate::InfoHash; @@ -86,7 +86,7 @@ impl Database for SqliteDatabase { Ok(AuthKey { key, - valid_until: Some(SinceUnixEpoch::from_secs(valid_until as u64)), + valid_until: Some(DurationSinceUnixEpoch::from_secs(valid_until as u64)), }) })?; @@ -193,7 +193,7 @@ impl Database for SqliteDatabase { Ok(AuthKey { key, - valid_until: Some(SinceUnixEpoch::from_secs(valid_until_i64 as u64)), + valid_until: Some(DurationSinceUnixEpoch::from_secs(valid_until_i64 as u64)), }) } else { Err(database::Error::QueryReturnedNoRows) diff --git a/src/protocol/clock/clock.rs b/src/protocol/clock/clock.rs index db59170b3..538edcfed 100644 --- a/src/protocol/clock/clock.rs +++ b/src/protocol/clock/clock.rs @@ -1,7 +1,7 @@ use std::num::IntErrorKind; pub use std::time::Duration; -pub type SinceUnixEpoch = Duration; +pub type DurationSinceUnixEpoch = Duration; #[derive(Debug)] pub enum ClockType { @@ -22,14 +22,14 @@ pub type DefaultClock = WorkingClock; pub type DefaultClock = StoppedClock; pub trait Time: Sized { - fn now() -> SinceUnixEpoch; + fn now() -> DurationSinceUnixEpoch; } pub trait TimeNow: Time { - fn add(add_time: &Duration) -> Option { + fn add(add_time: &Duration) -> Option { Self::now().checked_add(*add_time) } - fn sub(sub_time: &Duration) -> Option { + fn sub(sub_time: &Duration) -> Option { Self::now().checked_sub(*sub_time) } } @@ -57,10 +57,10 @@ mod tests { mod working_clock { use std::time::SystemTime; - use super::{SinceUnixEpoch, Time, TimeNow, WorkingClock}; + use super::{DurationSinceUnixEpoch, Time, TimeNow, WorkingClock}; impl Time for WorkingClock { - fn now() -> SinceUnixEpoch { + fn now() -> DurationSinceUnixEpoch { SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap() } } @@ -69,9 +69,9 @@ mod working_clock { } pub trait StoppedTime: TimeNow { - fn local_set(unix_time: &SinceUnixEpoch); + fn local_set(unix_time: &DurationSinceUnixEpoch); fn local_set_to_unix_epoch() { - Self::local_set(&SinceUnixEpoch::ZERO) + Self::local_set(&DurationSinceUnixEpoch::ZERO) } fn local_set_to_app_start_time(); fn local_set_to_system_time_now(); @@ -84,10 +84,10 @@ mod stopped_clock { use std::num::IntErrorKind; use std::time::Duration; - use super::{SinceUnixEpoch, StoppedClock, StoppedTime, Time, TimeNow}; + use super::{DurationSinceUnixEpoch, StoppedClock, StoppedTime, Time, TimeNow}; impl Time for StoppedClock { - fn now() -> SinceUnixEpoch { + fn now() -> DurationSinceUnixEpoch { detail::FIXED_TIME.with(|time| { return *time.borrow(); }) @@ -97,7 +97,7 @@ mod stopped_clock { impl TimeNow for StoppedClock {} impl StoppedTime for StoppedClock { - fn local_set(unix_time: &SinceUnixEpoch) { + fn local_set(unix_time: &DurationSinceUnixEpoch) { detail::FIXED_TIME.with(|time| { *time.borrow_mut() = *unix_time; }) @@ -147,11 +147,11 @@ mod stopped_clock { use std::thread; use std::time::Duration; - use crate::protocol::clock::clock::{SinceUnixEpoch, StoppedClock, StoppedTime, Time, TimeNow, WorkingClock}; + use crate::protocol::clock::clock::{DurationSinceUnixEpoch, StoppedClock, StoppedTime, Time, TimeNow, WorkingClock}; #[test] fn it_should_default_to_zero_when_testing() { - assert_eq!(StoppedClock::now(), SinceUnixEpoch::ZERO) + assert_eq!(StoppedClock::now(), DurationSinceUnixEpoch::ZERO) } #[test] @@ -206,26 +206,26 @@ mod stopped_clock { use std::cell::RefCell; use std::time::SystemTime; - use crate::protocol::clock::clock::SinceUnixEpoch; + use crate::protocol::clock::clock::DurationSinceUnixEpoch; use crate::static_time; - pub fn get_app_start_time() -> SinceUnixEpoch { + pub fn get_app_start_time() -> DurationSinceUnixEpoch { (*static_time::TIME_AT_APP_START) .duration_since(SystemTime::UNIX_EPOCH) .unwrap() } #[cfg(not(test))] - pub fn get_default_fixed_time() -> SinceUnixEpoch { + pub fn get_default_fixed_time() -> DurationSinceUnixEpoch { get_app_start_time() } #[cfg(test)] - pub fn get_default_fixed_time() -> SinceUnixEpoch { - SinceUnixEpoch::ZERO + pub fn get_default_fixed_time() -> DurationSinceUnixEpoch { + DurationSinceUnixEpoch::ZERO } - thread_local!(pub static FIXED_TIME: RefCell = RefCell::new(get_default_fixed_time())); + thread_local!(pub static FIXED_TIME: RefCell = RefCell::new(get_default_fixed_time())); #[cfg(test)] mod tests { diff --git a/src/protocol/clock/clockperiod.rs b/src/protocol/clock/clockperiod.rs new file mode 100644 index 000000000..11c7c3671 --- /dev/null +++ b/src/protocol/clock/clockperiod.rs @@ -0,0 +1,86 @@ +use std::num::TryFromIntError; +use std::time::Duration; + +use super::clock::{ClockType, StoppedClock, TimeNow, WorkingClock}; +use super::period::{self, Periods}; + +pub trait ClockPeriods: Sized +where + T: TimeNow, +{ + fn now(length: &period::PeriodLength) -> Option> { + T::now().as_nanos().checked_div(length.as_nanos()) + .map(|count| match period::PeriodCount::try_from(count) { + Err(error) => Err(error), + Ok(count) => Ok(period::PeriodSinceUnixEpoch::new(length, &count)), + }) + } + + fn now_add( + length: &period::PeriodLength, + add_time: &Duration, + ) -> Option> { + match T::add(add_time) { + None => None, + Some(time) => time.as_nanos().checked_div(length.as_nanos()) + .map(|count| match period::PeriodCount::try_from(count) { + Err(error) => Err(error), + Ok(count) => Ok(period::PeriodSinceUnixEpoch::new(length, &count)), + }), + } + } + fn now_sub( + length: &period::PeriodLength, + sub_time: &Duration, + ) -> Option> { + match T::sub(sub_time) { + None => None, + Some(time) => time.as_nanos().checked_div(length.as_nanos()) + .map(|count| match period::PeriodCount::try_from(count) { + Err(error) => Err(error), + Ok(count) => Ok(period::PeriodSinceUnixEpoch::new(length, &count)), + }), + } + } +} + +#[derive(Debug)] +pub struct PeriodClock {} + +pub type ClockPeriodWorking = PeriodClock<{ ClockType::WorkingClock as usize }>; + +pub type StoppedPeriodClock = PeriodClock<{ ClockType::StoppedClock as usize }>; + +impl ClockPeriods for ClockPeriodWorking {} + +impl ClockPeriods for StoppedPeriodClock {} + +#[cfg(not(test))] +pub type DefaultPeriodClock = ClockPeriodWorking; + +#[cfg(test)] +pub type DefaultPeriodClock = StoppedPeriodClock; + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use crate::protocol::clock::clock::{DefaultClock, DurationSinceUnixEpoch, StoppedTime}; + use crate::protocol::clock::clockperiod::{ClockPeriods, DefaultPeriodClock}; + use crate::protocol::clock::period::Period; + + #[test] + fn it_should_get_the_current_period() { + assert_eq!( + DefaultPeriodClock::now(&Duration::from_secs(2)).unwrap().unwrap(), + Period::from_secs(2, 0) + ); + + DefaultClock::local_set(&DurationSinceUnixEpoch::from_secs(12387687123)); + + assert_eq!( + DefaultPeriodClock::now(&Duration::from_secs(2)).unwrap().unwrap(), + Period::from_secs(2, 6193843561) + ); + } +} diff --git a/src/protocol/clock/mod.rs b/src/protocol/clock/mod.rs index 159730d2b..e60fb403b 100644 --- a/src/protocol/clock/mod.rs +++ b/src/protocol/clock/mod.rs @@ -1 +1,3 @@ pub mod clock; +pub mod clockperiod; +pub mod period; diff --git a/src/protocol/clock/period.rs b/src/protocol/clock/period.rs new file mode 100644 index 000000000..5a32709b3 --- /dev/null +++ b/src/protocol/clock/period.rs @@ -0,0 +1,99 @@ +use std::num::{IntErrorKind, TryFromIntError}; +use std::time::Duration; + +pub type PeriodLength = Duration; +pub type PeriodCount = u64; +pub type PeriodTotalTime = Duration; +pub type PeriodSinceUnixEpoch = Period; + +/// A `Period` type with a `length` as `Duration` and a `count` as `u64`, +/// to represent a time duration and how many times that duration will occur or has occurred. +#[derive(Debug, Default, Hash, PartialEq)] +pub struct Period { + pub length: PeriodLength, + pub count: PeriodCount, +} + +impl Period { + /// Returns a new `Period`. + pub fn new(length: &PeriodLength, count: PeriodCount) -> Self { + Self { + length: *length, + count, + } + } + + /// Returns a new `Period` from seconds. + pub const fn from_secs(seconds: u64, count: PeriodCount) -> Self { + Self { + length: Duration::from_secs(seconds), + count, + } + } + + /// Return `Self.length` * `Self.count` as `Duration`. + pub fn total_time(&self) -> Result, TryFromIntError> { + match u32::try_from(self.count) { + Err(error) => Err(error), + Ok(count) => Ok(self.length.checked_mul(count)), + } + } + + /// Return `Self.length` * (`Self.count` + 1) as `Duration`. + pub fn total_time_end(&self) -> Result, TryFromIntError> { + match u32::try_from(self.count) { + Err(e) => Err(e), + Ok(count) => match count.checked_add(1) { + None => Ok(None), + Some(count) => match self.length.checked_mul(count) { + None => Ok(None), + Some(time) => Ok(Some(time)), + }, + }, + } + } + + /// Increase `Self.count` by `u64`. + pub fn add_count(&self, add: PeriodCount) -> Result { + match self.count.checked_add(add) { + None => Err(IntErrorKind::PosOverflow), + Some(count) => Ok(Self { + length: self.length, + count, + }), + } + } + + /// Decrease `Self.count` by `u64`. + pub fn sub_count(&self, sub: PeriodCount) -> Result { + match self.count.checked_sub(sub) { + None => Err(IntErrorKind::NegOverflow), + Some(count) => Ok(Self { + length: self.length, + count, + }), + } + } +} + +#[cfg(test)] +mod test { + + use std::time::Duration; + + use crate::protocol::clock::period::Period; + + #[test] + fn it_should_get_the_total_time_of_a_period() { + assert_eq!(Period::default().total_time().unwrap().unwrap(), Duration::ZERO); + + assert_eq!( + Period::from_secs(12, 12).total_time().unwrap().unwrap(), + Duration::from_secs(144) + ); + assert_eq!( + Period::from_secs(12, 12).total_time_end().unwrap().unwrap(), + Duration::from_secs(156) + ); + } +} diff --git a/src/protocol/utils.rs b/src/protocol/utils.rs index f2a68fdb3..127baa4eb 100644 --- a/src/protocol/utils.rs +++ b/src/protocol/utils.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; use aquatic_udp_protocol::ConnectionId; -use super::clock::clock::{DefaultClock, SinceUnixEpoch, Time}; +use super::clock::clock::{DefaultClock, DurationSinceUnixEpoch, Time}; pub fn get_connection_id(remote_address: &SocketAddr) -> ConnectionId { ConnectionId(((current_time() / 3600) | ((remote_address.port() as u64) << 36)) as i64) @@ -12,6 +12,6 @@ pub fn current_time() -> u64 { DefaultClock::now().as_secs() } -pub fn ser_unix_time_value(unix_time_value: &SinceUnixEpoch, ser: S) -> Result { +pub fn ser_unix_time_value(unix_time_value: &DurationSinceUnixEpoch, ser: S) -> Result { ser.serialize_u64(unix_time_value.as_millis() as u64) } diff --git a/src/tracker/key.rs b/src/tracker/key.rs index 8ba19ab12..76ac21527 100644 --- a/src/tracker/key.rs +++ b/src/tracker/key.rs @@ -6,7 +6,7 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use serde::Serialize; -use crate::protocol::clock::clock::{DefaultClock, SinceUnixEpoch, Time, TimeNow}; +use crate::protocol::clock::clock::{DefaultClock, DurationSinceUnixEpoch, Time, TimeNow}; use crate::AUTH_KEY_LENGTH; pub fn generate_auth_key(lifetime: Duration) -> AuthKey { @@ -25,7 +25,7 @@ pub fn generate_auth_key(lifetime: Duration) -> AuthKey { } pub fn verify_auth_key(auth_key: &AuthKey) -> Result<(), Error> { - let current_time: SinceUnixEpoch = DefaultClock::now(); + let current_time: DurationSinceUnixEpoch = DefaultClock::now(); if auth_key.valid_until.is_none() { return Err(Error::KeyInvalid); } @@ -39,7 +39,7 @@ pub fn verify_auth_key(auth_key: &AuthKey) -> Result<(), Error> { #[derive(Serialize, Debug, Eq, PartialEq, Clone)] pub struct AuthKey { pub key: String, - pub valid_until: Option, + pub valid_until: Option, } impl AuthKey { diff --git a/src/tracker/peer.rs b/src/tracker/peer.rs index b37090b8e..b10c97bcc 100644 --- a/src/tracker/peer.rs +++ b/src/tracker/peer.rs @@ -5,7 +5,7 @@ use serde; use serde::Serialize; use crate::http::AnnounceRequest; -use crate::protocol::clock::clock::{DefaultClock, SinceUnixEpoch, Time}; +use crate::protocol::clock::clock::{DefaultClock, DurationSinceUnixEpoch, Time}; use crate::protocol::common::{AnnounceEventDef, NumberOfBytesDef}; use crate::protocol::utils::ser_unix_time_value; use crate::PeerId; @@ -15,7 +15,7 @@ pub struct TorrentPeer { pub peer_id: PeerId, pub peer_addr: SocketAddr, #[serde(serialize_with = "ser_unix_time_value")] - pub updated: SinceUnixEpoch, + pub updated: DurationSinceUnixEpoch, #[serde(with = "NumberOfBytesDef")] pub uploaded: NumberOfBytes, #[serde(with = "NumberOfBytesDef")]