Skip to main content

str0m_proto/
bandwidth.rs

1use std::fmt;
2use std::iter::Sum;
3use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
4use std::time::Duration;
5
6/// A data rate expressed as bits per second(bps).
7///
8/// Internally the value is tracked as a floating point number for accuracy in the presence of
9/// repeated calculations that can yield decimal values.
10#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
11pub struct Bitrate(f64);
12
13impl Bitrate {
14    /// A bitrate of zero bit/s.
15    pub const ZERO: Self = Self::bps(0);
16    /// The maximum bitrate that can be represented.
17    pub const MAX: Self = Self::bps(u64::MAX);
18    /// Positive infinity, useful as an invalid value with comparison semantics
19    pub const INFINITY: Self = Self(f64::INFINITY);
20    /// Negative infinity, useful as an invalid value with comparison semantics
21    pub const NEG_INFINITY: Self = Self(f64::NEG_INFINITY);
22
23    /// Create a bitrate of some bit per second(bps).
24    pub const fn bps(bps: u64) -> Self {
25        Bitrate(bps as f64)
26    }
27
28    /// Create a bitrate of some **Kilobits** per second(kbps).
29    pub const fn kbps(kbps: u64) -> Self {
30        Self::bps(kbps * 10_u64.pow(3))
31    }
32
33    /// Create a bitrate of some **Megabits** per second(mbps).
34    pub const fn mbps(mbps: u64) -> Self {
35        Self::bps(mbps * 10_u64.pow(6))
36    }
37
38    /// Create a bitrate of some **Gigabits** per second(gbps).
39    pub const fn gbps(gbps: u64) -> Self {
40        Self::bps(gbps * 10_u64.pow(9))
41    }
42
43    /// The number of bits per second as f64.
44    pub fn as_f64(&self) -> f64 {
45        self.0
46    }
47
48    /// The number of bits per second rounded upwards as u64.
49    pub fn as_u64(&self) -> u64 {
50        self.0.ceil() as u64
51    }
52
53    /// Clamp the value between a min and a max.
54    pub fn clamp(&self, min: Self, max: Self) -> Self {
55        Self(self.0.clamp(min.0, max.0))
56    }
57
58    /// Return the minimum bitrate between `self` and `other`.
59    pub fn min(&self, other: Self) -> Self {
60        Self(self.0.min(other.0))
61    }
62
63    /// Return the maximum bitrate between `self` and `other`.
64    pub fn max(&self, other: Self) -> Self {
65        Self(self.0.max(other.0))
66    }
67
68    /// Whether this bitrate is valid
69    pub fn is_valid(&self) -> bool {
70        self.0.is_finite()
71    }
72
73    /// Turn self into `Option<Bitrate>` based on its validity
74    pub fn as_valid(&self) -> Option<Bitrate> {
75        self.is_valid().then_some(*self)
76    }
77
78    /// Whether this value is zero
79    pub fn is_zero(&self) -> bool {
80        *self == Bitrate::ZERO
81    }
82}
83
84impl From<u64> for Bitrate {
85    fn from(value: u64) -> Self {
86        Self::bps(value)
87    }
88}
89
90impl From<f64> for Bitrate {
91    fn from(value: f64) -> Self {
92        Self(value)
93    }
94}
95
96impl Mul<Duration> for Bitrate {
97    type Output = DataSize;
98
99    fn mul(self, rhs: Duration) -> Self::Output {
100        let bits = self.0 * rhs.as_secs_f64();
101        let bytes = bits / 8.0;
102
103        DataSize::bytes(bytes.round() as i64)
104    }
105}
106
107impl Mul<f64> for Bitrate {
108    type Output = Bitrate;
109
110    fn mul(self, rhs: f64) -> Self::Output {
111        Bitrate(self.0 * rhs)
112    }
113}
114
115impl Sub<Bitrate> for Bitrate {
116    type Output = Bitrate;
117
118    fn sub(self, rhs: Bitrate) -> Self::Output {
119        assert!(
120            self.0 >= rhs.0,
121            "Attempted to subtract Bitrates that would result in overflow. lhs={}, rhs={}",
122            self,
123            rhs
124        );
125
126        Self(self.0 - rhs.0)
127    }
128}
129
130impl Add<Bitrate> for Bitrate {
131    type Output = Bitrate;
132
133    fn add(self, rhs: Bitrate) -> Self::Output {
134        Self(self.0 + rhs.0)
135    }
136}
137
138impl fmt::Display for Bitrate {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        let rate = self.0;
141        let log = rate.log10().floor() as u64;
142
143        match log {
144            0..=2 => write!(f, "{rate}bit/s"),
145            3..=5 => write!(f, "{:.3}kbit/s", rate / 10.0_f64.powf(3.0)),
146            6..=8 => write!(f, "{:.3}Mbit/s", rate / 10.0_f64.powf(6.0)),
147            9..=11 => write!(f, "{:.3}Gbit/s", rate / 10.0_f64.powf(9.0)),
148            12.. => write!(f, "{:.3}Tbit/s", rate / 10.0_f64.powf(12.0)),
149        }
150    }
151}
152
153/// A data size expressed in bytes.
154#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
155pub struct DataSize(i64);
156
157impl DataSize {
158    /// A data size of zero bytes.
159    pub const ZERO: Self = DataSize::bytes(0);
160
161    /// Create a data size of some bytes.
162    pub const fn bytes(bytes: i64) -> DataSize {
163        Self(bytes)
164    }
165
166    /// Create a data size of some kilobytes.
167    pub const fn kbytes(kbytes: i64) -> DataSize {
168        Self(kbytes * 1024)
169    }
170
171    /// Create a data size of some megabytes.
172    pub const fn mbytes(mbytes: i64) -> DataSize {
173        Self(mbytes * 1024 * 1024)
174    }
175
176    /// Create a data size of some gigabytes.
177    pub const fn gbytes(gbytes: i64) -> DataSize {
178        Self(gbytes * 1024 * 1024 * 1024)
179    }
180
181    /// The number of bytes as f64.
182    pub fn as_bytes_f64(&self) -> f64 {
183        self.0 as f64
184    }
185
186    /// The number of bytes as usize.
187    pub fn as_bytes_usize(&self) -> usize {
188        self.0
189            .try_into()
190            .expect("DataSize i64 value must fit in usize")
191    }
192
193    /// The number of bytes as i64.
194    pub fn as_bytes_i64(&self) -> i64 {
195        self.0
196    }
197
198    /// Subtract, saturating at zero.
199    pub fn saturating_sub(self, rhs: Self) -> Self {
200        Self((self.0 - rhs.0).max(0))
201    }
202
203    /// The number of kilobytes as f64.
204    pub fn as_kb(&self) -> f64 {
205        self.0 as f64 / 1000.0
206    }
207}
208
209impl From<usize> for DataSize {
210    fn from(value: usize) -> Self {
211        Self(value as i64)
212    }
213}
214
215impl From<u8> for DataSize {
216    fn from(value: u8) -> Self {
217        Self(value as i64)
218    }
219}
220
221impl Div<Duration> for DataSize {
222    type Output = Bitrate;
223
224    fn div(self, rhs: Duration) -> Self::Output {
225        let bytes = self.as_bytes_f64();
226        let s = rhs.as_secs_f64();
227
228        if s == 0.0 {
229            return Bitrate::ZERO;
230        }
231
232        let bps = (bytes * 8.0) / s;
233
234        bps.into()
235    }
236}
237
238impl Div<Bitrate> for DataSize {
239    type Output = Duration;
240
241    fn div(self, rhs: Bitrate) -> Self::Output {
242        let bits = self.as_bytes_f64() * 8.0;
243        let rhs = rhs.as_f64();
244
245        if rhs == 0.0 {
246            return Duration::ZERO;
247        }
248
249        let seconds = bits / rhs;
250
251        Duration::from_secs_f64(seconds)
252    }
253}
254
255impl Mul<i64> for DataSize {
256    type Output = i64;
257
258    fn mul(self, rhs: i64) -> Self::Output {
259        self.as_bytes_i64() * rhs
260    }
261}
262
263impl Div<f64> for Bitrate {
264    type Output = Bitrate;
265
266    fn div(self, rhs: f64) -> Self::Output {
267        if rhs == 0.0 {
268            return Self::ZERO;
269        }
270
271        Self(self.0 / rhs)
272    }
273}
274
275impl Mul<u64> for DataSize {
276    type Output = DataSize;
277
278    fn mul(self, rhs: u64) -> Self::Output {
279        Self(self.0 * rhs as i64)
280    }
281}
282
283impl AddAssign<DataSize> for DataSize {
284    fn add_assign(&mut self, rhs: DataSize) {
285        self.0 += rhs.0;
286    }
287}
288
289impl Sub<DataSize> for DataSize {
290    type Output = DataSize;
291
292    fn sub(self, rhs: DataSize) -> Self::Output {
293        Self(self.0 - rhs.0)
294    }
295}
296
297impl SubAssign<DataSize> for DataSize {
298    fn sub_assign(&mut self, rhs: DataSize) {
299        self.0 -= rhs.0;
300    }
301}
302
303impl Add<DataSize> for DataSize {
304    type Output = DataSize;
305
306    fn add(self, rhs: DataSize) -> Self::Output {
307        Self(self.0 + rhs.0)
308    }
309}
310
311impl Sum<DataSize> for DataSize {
312    fn sum<I: Iterator<Item = DataSize>>(iter: I) -> Self {
313        iter.fold(DataSize::ZERO, |acc, s| acc + s)
314    }
315}
316
317impl fmt::Display for DataSize {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        let size = self.0 as f64;
320        let log = (size as u64).ilog10();
321
322        match log {
323            0..=2 => write!(f, "{size}B"),
324            3..=5 => write!(f, "{:.3}kB", size / 10.0_f64.powf(3.0)),
325            6..=8 => write!(f, "{:.3}MB", size / 10.0_f64.powf(6.0)),
326            9..=11 => write!(f, "{:.3}GB", size / 10.0_f64.powf(9.0)),
327            12.. => write!(f, "{:.3}TB", size / 10.0_f64.powf(12.0)),
328        }
329    }
330}
331
332#[cfg(test)]
333mod test {
334    use std::time::Duration;
335
336    use super::{Bitrate, DataSize};
337
338    #[test]
339    fn test_bitrate_display() {
340        let rate = Bitrate::bps(1);
341        assert_eq!(rate.to_string(), "1bit/s");
342
343        let rate = Bitrate::bps(12);
344        assert_eq!(rate.to_string(), "12bit/s");
345
346        let rate = Bitrate::bps(123);
347        assert_eq!(rate.to_string(), "123bit/s");
348
349        let rate = Bitrate::bps(1234);
350        assert_eq!(rate.to_string(), "1.234kbit/s");
351
352        let rate = Bitrate::bps(12345);
353        assert_eq!(rate.to_string(), "12.345kbit/s");
354
355        let rate = Bitrate::bps(123456);
356        assert_eq!(rate.to_string(), "123.456kbit/s");
357
358        let rate = Bitrate::bps(1234567);
359        assert_eq!(rate.to_string(), "1.235Mbit/s");
360
361        let rate = Bitrate::bps(12345678);
362        assert_eq!(rate.to_string(), "12.346Mbit/s");
363
364        let rate = Bitrate::bps(123456789);
365        assert_eq!(rate.to_string(), "123.457Mbit/s");
366
367        let rate = Bitrate::bps(1234567898);
368        assert_eq!(rate.to_string(), "1.235Gbit/s");
369
370        let rate = Bitrate::bps(12345678987);
371        assert_eq!(rate.to_string(), "12.346Gbit/s");
372
373        let rate = Bitrate::bps(123456789876);
374        assert_eq!(rate.to_string(), "123.457Gbit/s");
375
376        let rate = Bitrate::bps(1234567898765);
377        assert_eq!(rate.to_string(), "1.235Tbit/s");
378    }
379
380    #[test]
381    fn test_data_size_div_duration() {
382        let size = DataSize::bytes(2_500_000);
383        let rate = size / Duration::from_secs(1);
384
385        assert_eq!(rate.as_u64(), 20_000_000);
386    }
387
388    #[test]
389    fn test_data_size_div_bitrate() {
390        let size = DataSize::bytes(12_500);
391        let rate = Bitrate::kbps(2_500);
392        let duration = size / rate;
393
394        assert_eq!(duration.as_millis(), 40);
395    }
396
397    #[test]
398    fn test_bitrate_div_f64() {
399        let rate = Bitrate::kbps(2_500);
400        let new_rate = rate / 2.0;
401
402        assert_eq!(new_rate, Bitrate::kbps(1250));
403    }
404
405    #[test]
406    fn test_datasize_negative() {
407        let size = DataSize::bytes(-100);
408        assert_eq!(size.as_bytes_i64(), -100);
409
410        // Test negative kilobytes
411        let size_kb = DataSize::kbytes(-5);
412        assert_eq!(size_kb.as_bytes_i64(), -5 * 1024);
413
414        // Test that negative values work in comparisons
415        let positive = DataSize::bytes(50);
416        let negative = DataSize::bytes(-50);
417        assert!(negative < positive);
418        assert!(negative < DataSize::ZERO);
419    }
420
421    #[test]
422    fn test_datasize_mul_negative() {
423        let size = DataSize::bytes(100);
424
425        // Multiply by -1 to negate
426        let negated = size * -1i64;
427        assert_eq!(negated, -100);
428
429        // Multiply by -2
430        let doubled_neg = size * -2i64;
431        assert_eq!(doubled_neg, -200);
432
433        // Negative size multiplied by -1
434        let negative_size = DataSize::bytes(-50);
435        let positive_result = negative_size * -1i64;
436        assert_eq!(positive_result, 50);
437    }
438
439    #[test]
440    fn test_datasize_arithmetic_with_negatives() {
441        let positive = DataSize::bytes(100);
442        let negative = DataSize::bytes(-50);
443
444        // Add negative to positive
445        let result = positive + negative;
446        assert_eq!(result.as_bytes_i64(), 50);
447
448        // Subtract negative from positive (should add)
449        let result2 = positive - negative;
450        assert_eq!(result2.as_bytes_i64(), 150);
451    }
452}