Skip to main content

quiche/recovery/
bandwidth.rs

1// Copyright (C) 2023, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use std::time::Duration;
28
29const NUM_MILLIS_PER_SECOND: u64 = 1000;
30const NUM_MICROS_PER_MILLI: u64 = 1000;
31const NUM_MICROS_PER_SECOND: u64 = NUM_MICROS_PER_MILLI * NUM_MILLIS_PER_SECOND;
32const NUM_NANOS_PER_SECOND: u64 = 1000 * NUM_MICROS_PER_SECOND;
33
34#[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Copy)]
35pub struct Bandwidth {
36    bits_per_second: u64,
37}
38
39impl std::ops::Mul<f64> for Bandwidth {
40    type Output = Bandwidth;
41
42    fn mul(self, rhs: f64) -> Self::Output {
43        Bandwidth {
44            bits_per_second: (self.bits_per_second as f64 * rhs).round() as u64,
45        }
46    }
47}
48
49impl std::ops::Mul<f32> for Bandwidth {
50    type Output = Bandwidth;
51
52    fn mul(self, rhs: f32) -> Self::Output {
53        self * rhs as f64
54    }
55}
56
57impl std::ops::Sub<Bandwidth> for Bandwidth {
58    type Output = Option<Bandwidth>;
59
60    fn sub(self, rhs: Bandwidth) -> Self::Output {
61        self.bits_per_second
62            .checked_sub(rhs.bits_per_second)
63            .map(|bps| Bandwidth {
64                bits_per_second: bps,
65            })
66    }
67}
68
69impl std::ops::Add<Bandwidth> for Bandwidth {
70    type Output = Bandwidth;
71
72    fn add(self, rhs: Bandwidth) -> Self::Output {
73        Bandwidth {
74            bits_per_second: self.bits_per_second.add(rhs.bits_per_second),
75        }
76    }
77}
78
79impl std::ops::Mul<Duration> for Bandwidth {
80    type Output = u64;
81
82    fn mul(self, rhs: Duration) -> Self::Output {
83        self.to_bytes_per_period(rhs)
84    }
85}
86
87impl Bandwidth {
88    pub const fn from_bytes_and_time_delta(
89        bytes: usize, time_delta: Duration,
90    ) -> Self {
91        if bytes == 0 {
92            return Bandwidth { bits_per_second: 0 };
93        }
94
95        let mut nanos = time_delta.as_nanos() as u64;
96        if nanos == 0 {
97            nanos = 1;
98        }
99
100        let num_nano_bits = 8 * bytes as u64 * NUM_NANOS_PER_SECOND;
101        if num_nano_bits < nanos {
102            return Bandwidth { bits_per_second: 1 };
103        }
104
105        Bandwidth {
106            bits_per_second: num_nano_bits / nanos,
107        }
108    }
109
110    #[allow(dead_code)]
111    pub const fn from_bytes_per_second(bytes_per_second: u64) -> Self {
112        Bandwidth {
113            bits_per_second: bytes_per_second * 8,
114        }
115    }
116
117    #[allow(dead_code)]
118    pub const fn to_bits_per_second(self) -> u64 {
119        self.bits_per_second
120    }
121
122    pub const fn to_bytes_per_second(self) -> u64 {
123        self.bits_per_second / 8
124    }
125
126    pub const fn from_kbits_per_second(k_bits_per_second: u64) -> Self {
127        Bandwidth {
128            bits_per_second: k_bits_per_second * 1_000,
129        }
130    }
131
132    #[allow(dead_code)]
133    pub const fn from_mbits_per_second(m_bits_per_second: u64) -> Self {
134        Bandwidth::from_kbits_per_second(m_bits_per_second * 1_000)
135    }
136
137    /// Returns a sentinel representing infinite bandwidth.
138    pub const fn infinite() -> Self {
139        Bandwidth {
140            bits_per_second: u64::MAX,
141        }
142    }
143
144    pub const fn zero() -> Self {
145        Bandwidth { bits_per_second: 0 }
146    }
147
148    /// Returns the time to transfer `bytes` at this bandwidth.
149    ///
150    /// Returns `Duration::ZERO` for infinite or zero bandwidth.
151    /// Saturates to `Duration::from_nanos(u64::MAX)` if the
152    /// calculation would overflow.
153    pub fn transfer_time(&self, bytes: u64) -> Duration {
154        // Handle infinite bandwidth sentinel: transfer is instantaneous
155        if self.bits_per_second == u64::MAX {
156            return Duration::ZERO;
157        }
158
159        if self.bits_per_second == 0 {
160            return Duration::ZERO;
161        }
162
163        // Fast path: try u64 arithmetic first. At typical packet sizes
164        // (< 10 KB) and bandwidths, this won't overflow.
165        if let Some(nanos) = bytes.checked_mul(8 * NUM_NANOS_PER_SECOND) {
166            return Duration::from_nanos(nanos / self.bits_per_second);
167        }
168
169        // Slow path: use u128 for intermediate calculation to avoid overflow.
170        // At very large byte counts, bytes * 8 * NUM_NANOS_PER_SECOND can
171        // overflow u64.
172        let nanos = (bytes as u128) * (8 * NUM_NANOS_PER_SECOND) as u128;
173        let nanos = nanos / (self.bits_per_second as u128);
174
175        // Saturate to Duration::MAX if result exceeds u64 range.
176        Duration::from_nanos(nanos.min(u64::MAX as u128) as u64)
177    }
178
179    /// Returns the number of bytes that can be sent in
180    /// `time_period` at this bandwidth.
181    ///
182    /// Returns `u64::MAX` for infinite bandwidth (unless
183    /// `time_period` is zero). Saturates to `u64::MAX` if the
184    /// calculation would overflow.
185    pub fn to_bytes_per_period(self, time_period: Duration) -> u64 {
186        // Handle infinite bandwidth sentinel.
187        if self.bits_per_second == u64::MAX {
188            if time_period != Duration::ZERO {
189                return u64::MAX;
190            } else {
191                return 0;
192            }
193        }
194
195        // Fast path: try u64 arithmetic first. At typical bandwidths (< 10
196        // Gbps) and short time periods (< 1 second), this won't overflow.
197        if let Ok(time_nanos) = u64::try_from(time_period.as_nanos()) {
198            if let Some(bits) = self.bits_per_second.checked_mul(time_nanos) {
199                return bits / (8 * NUM_NANOS_PER_SECOND);
200            }
201        }
202
203        // Slow path: use u128 for intermediate calculation to avoid overflow.
204        // At high bandwidths (e.g., 10+ Gbps) with non-trivial time periods,
205        // bits_per_second * time_period.as_nanos() can overflow u64.
206        let time_nanos = time_period.as_nanos();
207        let bits = (self.bits_per_second as u128).saturating_mul(time_nanos);
208        let bytes = bits / (8 * NUM_NANOS_PER_SECOND) as u128;
209
210        // Saturate to u64::MAX if result exceeds u64 range.
211        bytes.min(u64::MAX as u128) as u64
212    }
213}
214
215impl std::fmt::Debug for Bandwidth {
216    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
217        match self.bits_per_second {
218            x if x < 1_000_000 => write!(f, "{:.2} Kbps", x as f64 / 1_000.),
219            x if x < 1_000_000_000 => {
220                write!(f, "{:.2} Mbps", x as f64 / 1_000_000.)
221            },
222            x => write!(f, "{:.2} Gbps", x as f64 / 1_000_000_000.),
223        }
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230
231    #[test]
232    fn constructors() {
233        // Internal representation is bits per second.
234        assert_eq!(Bandwidth::from_bytes_per_second(100).bits_per_second, 800);
235        let bw = Bandwidth::from_bytes_per_second(100);
236        assert_eq!(bw.to_bits_per_second(), 800);
237        assert_eq!(bw.to_bytes_per_second(), 100);
238
239        // kbits == 1000 bits
240        assert_eq!(
241            Bandwidth::from_kbits_per_second(100).bits_per_second,
242            100_000
243        );
244
245        // mbits == 1000,000 bits
246        assert_eq!(
247            Bandwidth::from_mbits_per_second(100).bits_per_second,
248            100_000_000
249        );
250
251        assert_eq!(Bandwidth::infinite().bits_per_second, u64::MAX);
252        assert_eq!(Bandwidth::zero().bits_per_second, 0);
253    }
254
255    #[test]
256    fn arithmetic_ops() {
257        let bw_1k = Bandwidth::from_kbits_per_second(1);
258        let bw_5k = Bandwidth::from_kbits_per_second(5);
259        let bw_6k = Bandwidth::from_kbits_per_second(6);
260
261        // Addition
262        assert_eq!(bw_1k + bw_5k, bw_6k);
263
264        // Subtraction
265        assert_eq!(bw_6k - bw_5k, Some(bw_1k));
266        assert_eq!(bw_6k - bw_6k, Some(Bandwidth::zero()));
267
268        // Negative bw is not defined.
269        assert_eq!(bw_1k - bw_5k, None);
270
271        // Multiplication by scalars
272        assert_eq!(bw_1k * 6.0f64, bw_6k);
273        assert_eq!(bw_1k * 6.0f32, bw_6k);
274        assert_eq!(bw_5k * 0.0, Bandwidth::zero());
275        assert_eq!(bw_5k * 1.0, bw_5k);
276
277        // Multiplication saturates on overflow and underflow.
278        assert_eq!(Bandwidth::infinite() * -1.0, Bandwidth::zero());
279        assert_eq!((Bandwidth::infinite() * 2.0f64).bits_per_second, u64::MAX);
280
281        // Multiplication rounds up.
282        assert_eq!(
283            (Bandwidth::infinite() * 0.5f64).bits_per_second,
284            u64::MAX / 2 + 1
285        );
286    }
287
288    #[test]
289    fn from_bytes_and_time_delta() {
290        assert_eq!(
291            Bandwidth::from_bytes_and_time_delta(10, Duration::from_millis(1000))
292                .bits_per_second,
293            80
294        );
295        assert_eq!(
296            Bandwidth::from_bytes_and_time_delta(10, Duration::from_millis(100))
297                .bits_per_second,
298            800
299        );
300        assert_eq!(
301            Bandwidth::from_bytes_and_time_delta(
302                100,
303                Duration::from_millis(1000)
304            )
305            .bits_per_second,
306            800
307        );
308    }
309
310    #[test]
311    fn transfer_time() {
312        let one_kbit_sec = Bandwidth::from_kbits_per_second(1);
313        assert_eq!(one_kbit_sec.transfer_time(0), Duration::ZERO);
314        assert_eq!(one_kbit_sec.transfer_time(100), Duration::from_millis(800));
315    }
316
317    // Test that large byte values that would overflow u64 are handled
318    // correctly using u128 arithmetic.
319    #[test]
320    fn transfer_time_overflow() {
321        let low_bandwidth = Bandwidth::from_kbits_per_second(1);
322
323        // This value would overflow: u64::MAX * 8 * NUM_MICROS_PER_SECOND
324        // which exceeds u64::MAX.
325        let large_bytes = u64::MAX;
326        let result = low_bandwidth.transfer_time(large_bytes);
327
328        // At 1 kbit/s = 125 bytes/s, transferring u64::MAX bytes would take
329        // an astronomically long time. Result should saturate to Duration::MAX
330        // (u64::MAX nanoseconds).
331        assert_eq!(result, Duration::from_nanos(u64::MAX));
332
333        // Test a more realistic large value: 10 GiB at 1 Gbps should work.
334        let one_gbps = Bandwidth::from_mbits_per_second(1_000); // 1 Gbps
335        let ten_gib = 10 * 1024 * 1024 * 1024;
336        // 10 GiB * 8 bits/byte / 1 Gbit/s = 85.899... seconds
337        let expected = Duration::from_nanos(85_899_345_920);
338        assert_eq!(one_gbps.transfer_time(ten_gib), expected);
339    }
340
341    #[test]
342    fn transfer_time_infinite() {
343        // Infinite bandwidth should have zero transfer time (instantaneous)
344        let inf = Bandwidth::infinite();
345
346        // Zero bytes
347        assert_eq!(inf.transfer_time(0), Duration::ZERO);
348
349        // Small transfers
350        assert_eq!(inf.transfer_time(1), Duration::ZERO);
351        assert_eq!(inf.transfer_time(100), Duration::ZERO);
352        assert_eq!(inf.transfer_time(1024), Duration::ZERO);
353
354        // Large transfers
355        assert_eq!(inf.transfer_time(1_000_000), Duration::ZERO);
356        assert_eq!(inf.transfer_time(u64::MAX), Duration::ZERO);
357    }
358
359    #[test]
360    fn to_bytes_per_period() {
361        let one_kbit_sec = Bandwidth::from_kbits_per_second(1);
362        assert_eq!(
363            one_kbit_sec.to_bytes_per_period(Duration::from_millis(10_000)),
364            1250
365        );
366        assert_eq!(
367            one_kbit_sec.to_bytes_per_period(Duration::from_millis(1000)),
368            125
369        );
370        assert_eq!(
371            one_kbit_sec.to_bytes_per_period(Duration::from_millis(100)),
372            12
373        );
374        assert_eq!(
375            one_kbit_sec.to_bytes_per_period(Duration::from_millis(10)),
376            1
377        );
378        assert_eq!(
379            one_kbit_sec.to_bytes_per_period(Duration::from_millis(1)),
380            0
381        );
382
383        // Mul<Duration> implementation.
384        assert_eq!(one_kbit_sec * Duration::from_millis(10_000), 1250);
385    }
386
387    #[test]
388    fn to_bytes_per_period_high_bandwidth() {
389        // 10 Gbps with 1 second would overflow u64 in the old implementation.
390        let ten_gbps = Bandwidth::from_mbits_per_second(10_000);
391        assert_eq!(
392            ten_gbps.to_bytes_per_period(Duration::from_secs(1)),
393            1_250_000_000
394        );
395
396        // 100 Gbps with 100ms.
397        let hundred_gbps = Bandwidth::from_mbits_per_second(100_000);
398        assert_eq!(
399            hundred_gbps.to_bytes_per_period(Duration::from_millis(100)),
400            1_250_000_000
401        );
402
403        // 1 Tbps with 10ms.
404        let one_tbps = Bandwidth::from_mbits_per_second(1_000_000);
405        assert_eq!(
406            one_tbps.to_bytes_per_period(Duration::from_millis(10)),
407            1_250_000_000
408        );
409    }
410
411    #[test]
412    fn to_bytes_per_period_overflow_intermediate() {
413        // Test case that would overflow u64 in intermediate calculation:
414        // bits_per_second=10^19, time_period=1sec would give 10^19 * 10^9 =
415        // 10^28.
416        let huge_bw = Bandwidth {
417            bits_per_second: 10_000_000_000_000_000_000,
418        };
419        let result = huge_bw.to_bytes_per_period(Duration::from_secs(1));
420        assert_eq!(result, 1_250_000_000_000_000_000);
421    }
422
423    #[test]
424    fn to_bytes_per_period_saturate_very_high_bandwidth() {
425        // Test case where result exceeds u64::MAX and should saturate.
426        // 2^63 bits/sec * 100 seconds / 8 = 6.25 * u64::MAX bytes.
427        let very_high_bw = Bandwidth {
428            bits_per_second: 1u64 << 63, // 2^63
429        };
430        let result = very_high_bw.to_bytes_per_period(Duration::from_secs(100));
431        // Should saturate to u64::MAX since result exceeds u64 range.
432        assert_eq!(result, u64::MAX);
433    }
434
435    #[test]
436    fn to_bytes_per_period_saturate_long_period() {
437        // Test saturation case: high bandwidth, long period.
438        let high_bw = Bandwidth {
439            bits_per_second: u64::MAX / 2,
440        };
441        let result = high_bw.to_bytes_per_period(Duration::from_secs(100));
442        // Should saturate to u64::MAX.
443        assert_eq!(result, u64::MAX);
444    }
445
446    #[test]
447    fn to_bytes_per_period_large_no_saturate() {
448        // Test large but reasonable case that doesn't saturate.
449        let high_bw = Bandwidth::from_mbits_per_second(100_000); // 100 Gbps
450        let one_hour = Duration::from_secs(3600);
451        let result = high_bw.to_bytes_per_period(one_hour);
452        assert_eq!(result, 45_000_000_000_000); // 45 TB
453    }
454
455    #[test]
456    fn to_bytes_per_period_infinite() {
457        // Infinite bandwidth sentinel should return u64::MAX.
458        let inf = Bandwidth::infinite();
459        assert_eq!(inf.to_bytes_per_period(Duration::from_secs(1)), u64::MAX);
460        assert_eq!(inf.to_bytes_per_period(Duration::from_millis(1)), u64::MAX);
461        assert_eq!(inf.to_bytes_per_period(Duration::ZERO), 0);
462
463        // Mul<Duration> should also return u64::MAX.
464        assert_eq!(inf * Duration::from_secs(1), u64::MAX);
465    }
466
467    #[test]
468    fn to_bytes_per_period_duration_exceeds_u64_nanos() {
469        // Test case where duration.as_nanos() exceeds u64::MAX.
470        // u64::MAX nanoseconds is ~584 years. We can create a Duration larger
471        // than that. Duration::MAX is ~584 billion years.
472        let bw = Bandwidth::from_mbits_per_second(1000); // 1 Gbps
473
474        // Create a duration that exceeds u64::MAX nanoseconds.
475        // u64::MAX = 18_446_744_073_709_551_615 nanoseconds
476        // = 18_446_744_073 seconds + 709_551_615 nanoseconds
477        // So any duration > 18_446_744_073 seconds will exceed u64::MAX nanos.
478        let huge_duration = Duration::from_secs(20_000_000_000); // ~634 years
479
480        // Should fall back to u128 arithmetic and not panic.
481        // 1 Gbps * 20 billion seconds = 2.5 * 10^18 bytes
482        let result = bw.to_bytes_per_period(huge_duration);
483        assert_eq!(result, 2_500_000_000_000_000_000);
484    }
485
486    #[test]
487    fn to_bytes_per_period_u128_overflow() {
488        // Test case where even u128 multiplication would overflow.
489        // u128::MAX is ~3.4 * 10^38. Duration::MAX.as_nanos() is ~10^29.
490        // We need bits_per_second * time_nanos > u128::MAX.
491        // Use maximum possible values: u64::MAX - 1 bits/sec (since u64::MAX
492        // is infinite sentinel) and Duration::MAX.
493        let huge_bw = Bandwidth {
494            bits_per_second: u64::MAX - 1,
495        };
496        let max_duration = Duration::MAX;
497
498        // u128 multiplication should overflow and saturate to u64::MAX.
499        // (u64::MAX - 1) * Duration::MAX.as_nanos() > u128::MAX
500        let result = huge_bw.to_bytes_per_period(max_duration);
501        assert_eq!(result, u64::MAX);
502    }
503
504    #[test]
505    fn debug() {
506        assert_eq!(
507            format!("{:?}", Bandwidth { bits_per_second: 1 }),
508            "0.00 Kbps"
509        );
510        assert_eq!(
511            format!("{:?}", Bandwidth {
512                bits_per_second: 12
513            }),
514            "0.01 Kbps"
515        );
516        assert_eq!(
517            format!("{:?}", Bandwidth {
518                bits_per_second: 123
519            }),
520            "0.12 Kbps"
521        );
522        assert_eq!(
523            format!("{:?}", Bandwidth {
524                bits_per_second: 1234
525            }),
526            "1.23 Kbps"
527        );
528        assert_eq!(
529            format!("{:?}", Bandwidth {
530                bits_per_second: 12345
531            }),
532            "12.35 Kbps"
533        );
534        assert_eq!(
535            format!("{:?}", Bandwidth {
536                bits_per_second: 123456
537            }),
538            "123.46 Kbps"
539        );
540        assert_eq!(
541            format!("{:?}", Bandwidth {
542                bits_per_second: 1234567
543            }),
544            "1.23 Mbps"
545        );
546        assert_eq!(
547            format!("{:?}", Bandwidth {
548                bits_per_second: 12345678
549            }),
550            "12.35 Mbps"
551        );
552        assert_eq!(
553            format!("{:?}", Bandwidth {
554                bits_per_second: 123456789
555            }),
556            "123.46 Mbps"
557        );
558        assert_eq!(
559            format!("{:?}", Bandwidth {
560                bits_per_second: 1234567890
561            }),
562            "1.23 Gbps"
563        );
564    }
565}