1use std::{borrow, marker, num::NonZero, time};
4
5pub trait DurationExt: Sized {
7 fn average<const N: u64>(self, sample: Self) -> Self;
9}
10
11impl DurationExt for time::Duration {
12 fn average<const N: u64>(self, sample: Self) -> Self {
13 let count = const {
14 assert!(N == 2 || N == 4 || N == 8);
15 NonZero::<u64>::MIN.saturating_add(N - 1)
16 };
17 let weight = const { N - 1 };
18 let secs = weight * (self.as_secs() / count) + sample.as_secs() / count;
19 let nanos = (weight * (self.as_secs() % count) + sample.as_secs() % count)
20 * const { 1_000_000_000 / N }
21 + (weight * u64::from(self.subsec_nanos()) + u64::from(sample.subsec_nanos())) / count;
22 Self::new(secs + nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32)
23 }
24}
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
29#[repr(transparent)]
30pub struct Positive(time::Duration);
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub struct Zero;
34
35impl Positive {
36 pub const fn from_secs<const N: u64>() -> Self {
37 const { assert!(N != 0) };
38 Self(time::Duration::from_secs(N))
39 }
40
41 pub const fn from_millis<const N: u64>() -> Self {
42 const { assert!(N != 0) };
43 Self(time::Duration::from_millis(N))
44 }
45
46 pub const fn from_nanos<const N: u64>() -> Self {
47 const { assert!(N != 0) };
48 Self(time::Duration::from_nanos(N))
49 }
50
51 pub const fn try_new(duration: time::Duration) -> Result<Self, Zero> {
52 if duration.is_zero() {
53 Err(Zero)
54 } else {
55 Ok(Self(duration))
56 }
57 }
58
59 pub const fn get(self) -> time::Duration {
60 self.0
61 }
62}
63
64impl borrow::Borrow<time::Duration> for Positive {
65 fn borrow(&self) -> &time::Duration {
66 &self.0
67 }
68}
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
71pub struct Nanos;
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
74pub struct Millis;
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
77pub struct Seconds;
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
81#[repr(transparent)]
82pub struct Unix<U>(u64, marker::PhantomData<fn(U) -> U>);
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub enum Add {
86 Scale {
87 at: Unix<Nanos>,
88 duration: time::Duration,
89 },
90 Range {
91 at: Unix<Nanos>,
92 duration: time::Duration,
93 },
94}
95
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
97pub enum Sub {
98 Scale {
99 at: Unix<Nanos>,
100 duration: time::Duration,
101 },
102 Range {
103 at: Unix<Nanos>,
104 duration: time::Duration,
105 },
106}
107
108impl<U> Unix<U> {
109 pub const ZERO: Self = Self::new(0);
110
111 pub const fn new(ticks: u64) -> Self {
112 Self(ticks, marker::PhantomData)
113 }
114
115 pub const fn ticks(&self) -> u64 {
116 self.0
117 }
118}
119
120impl Unix<Nanos> {
121 pub fn try_add(self, duration: time::Duration) -> Result<Self, Add> {
122 let Some(ticks) = Self::nanos(duration) else {
123 return Err(Add::Scale { at: self, duration });
124 };
125 if self.0 > u64::MAX - ticks {
126 return Err(Add::Range { at: self, duration });
127 }
128 Ok(Self::new(self.0 + ticks))
129 }
130
131 pub fn try_sub(self, duration: time::Duration) -> Result<Self, Sub> {
132 let Some(ticks) = Self::nanos(duration) else {
133 return Err(Sub::Scale { at: self, duration });
134 };
135 if ticks > self.0 {
136 return Err(Sub::Range { at: self, duration });
137 }
138 Ok(Self::new(self.0 - ticks))
139 }
140
141 fn nanos(duration: time::Duration) -> Option<u64> {
142 let nanos = duration.as_nanos();
143 (nanos <= u64::MAX as u128).then_some(nanos as u64)
144 }
145
146 pub const fn floor_millis(self) -> Unix<Millis> {
147 Unix::new(self.0 / 1_000_000)
148 }
149
150 pub const fn floor_secs(self) -> Unix<Seconds> {
151 Unix::new(self.0 / 1_000_000_000)
152 }
153}
154
155impl Unix<Millis> {
156 pub const fn floor_secs(self) -> Unix<Seconds> {
157 Unix::new(self.0 / 1_000)
158 }
159}
160
161const _: () = {
162 assert!(std::mem::size_of::<Positive>() == std::mem::size_of::<time::Duration>());
163 assert!(std::mem::align_of::<Positive>() == std::mem::align_of::<time::Duration>());
164 assert!(!std::mem::needs_drop::<Positive>());
165};
166const _: () = {
167 assert!(std::mem::size_of::<Unix<Nanos>>() == std::mem::size_of::<u64>());
168 assert!(std::mem::align_of::<Unix<Nanos>>() == std::mem::align_of::<u64>());
169 assert!(!std::mem::needs_drop::<Unix<Nanos>>());
170};
171const _: () = {
172 assert!(
173 std::mem::size_of::<Add>() <= std::mem::size_of::<(Unix<Nanos>, time::Duration, u64)>()
174 );
175 assert!(
176 std::mem::align_of::<Add>() <= std::mem::align_of::<(Unix<Nanos>, time::Duration, u64)>()
177 );
178 assert!(!std::mem::needs_drop::<Add>());
179 assert!(std::mem::size_of::<Sub>() == std::mem::size_of::<Add>());
180 assert!(std::mem::align_of::<Sub>() == std::mem::align_of::<Add>());
181 assert!(!std::mem::needs_drop::<Sub>());
182};