net_bytes/
download_speed.rs

1use std::time::Duration;
2
3use crate::{FileSizeFormat, format_parts_scaled, SCALE};
4
5/// Download speed in bytes per second (fixed-point representation)
6///
7/// Internally stores `bytes_per_second * SCALE` for precision.
8///
9/// 表示下载速度(单位:字节每秒,定点数表示)
10///
11/// 内部存储 `bytes_per_second * SCALE` 以保持精度。
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
13pub struct DownloadSpeed {
14    /// Scaled value: actual_bytes_per_second * SCALE
15    scaled_bps: u128,
16}
17
18impl DownloadSpeed {
19    /// Create a new download speed from raw bytes per second
20    ///
21    /// # Parameters
22    /// - `bytes_per_second`: Speed in bytes per second (can be zero)
23    ///
24    /// 从原始字节/秒创建一个新的下载速度实例
25    ///
26    /// # 参数
27    /// - `bytes_per_second`: 字节/秒速度(可以为零)
28    #[inline]
29    pub fn from_raw(bytes_per_second: u64) -> Self {
30        Self {
31            scaled_bps: (bytes_per_second as u128) * SCALE,
32        }
33    }
34
35    /// Create a new download speed from bytes downloaded and time taken
36    ///
37    /// Uses pure integer arithmetic with nanosecond precision.
38    ///
39    /// # Parameters
40    /// - `bytes`: Number of bytes downloaded (can be zero)
41    /// - `duration`: Time taken for the download
42    ///
43    /// 从下载的字节数和所用时间创建一个新的下载速度实例
44    ///
45    /// 使用纯整数运算,保持纳秒级精度。
46    ///
47    /// # 参数
48    /// - `bytes`: 下载的字节数(可以为零)
49    /// - `duration`: 下载所用的时间
50    pub fn new(bytes: u64, duration: Duration) -> Self {
51        let nanos = duration.as_nanos();
52        if nanos == 0 {
53            return Self { scaled_bps: 0 };
54        }
55
56        // bytes_per_second = bytes / (nanos / 1e9) = bytes * 1e9 / nanos
57        // scaled_bps = bytes_per_second * SCALE = bytes * 1e9 * SCALE / nanos
58        // To avoid overflow, we compute: (bytes * SCALE) * 1e9 / nanos
59        // But for very large bytes, we need to be careful about overflow
60        // u128 max is ~3.4e38, bytes max is ~1.8e19, SCALE is 1e6, 1e9
61        // So bytes * SCALE * 1e9 could be ~1.8e34 which fits in u128
62        let scaled_bps = (bytes as u128) * SCALE * 1_000_000_000 / nanos;
63        
64        Self { scaled_bps }
65    }
66
67    /// Get the internal scaled value (for advanced usage)
68    ///
69    /// 获取内部缩放值(高级用途)
70    #[inline]
71    pub fn as_scaled(&self) -> u128 {
72        self.scaled_bps
73    }
74
75    /// Get the speed in bytes per second as `u64` (floored)
76    ///
77    /// 以 `u64` 的形式获取字节每秒(向下取整)
78    #[inline]
79    pub fn as_u64(&self) -> u64 {
80        (self.scaled_bps / SCALE) as u64
81    }
82
83    /// Get the speed as f64 (for compatibility)
84    ///
85    /// 以 f64 的形式获取速度(兼容用途)
86    #[inline]
87    pub fn as_f64(&self) -> f64 {
88        self.scaled_bps as f64 / SCALE as f64
89    }
90}
91
92impl FileSizeFormat for DownloadSpeed {
93    /// Returns the formatted value and unit in SI (base-1000) standard
94    ///
95    /// 返回 SI (base-1000) 标准的 (formatted_value, unit)
96    fn get_si_parts(&self) -> (String, &'static str) {
97        const UNITS: &[&str] = &[
98            "B/s", "KB/s", "MB/s", "GB/s", "TB/s", "PB/s", "EB/s", "ZB/s", "YB/s",
99        ];
100        format_parts_scaled(self.scaled_bps, 1000, UNITS)
101    }
102
103    /// Returns the formatted value and unit in IEC (base-1024) standard
104    ///
105    /// 返回 IEC (base-1024) 标准的 (formatted_value, unit)
106    fn get_iec_parts(&self) -> (String, &'static str) {
107        const UNITS: &[&str] = &[
108            "B/s", "KiB/s", "MiB/s", "GiB/s", "TiB/s", "PiB/s", "EiB/s", "ZiB/s", "YiB/s",
109        ];
110        format_parts_scaled(self.scaled_bps, 1024, UNITS)
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use std::time::Duration;
117
118    use super::DownloadSpeed;
119    use crate::{SizeStandard, FileSizeFormat};
120
121    /// Helper function - SI standard
122    ///
123    /// 辅助函数 - SI 标准
124    fn format_test_si(bytes: u64) -> String {
125        DownloadSpeed::from_raw(bytes).to_formatted(SizeStandard::SI).to_string()
126    }
127
128    /// Helper function - IEC standard
129    ///
130    /// 辅助函数 - IEC 标准
131    fn format_test_iec(bytes: u64) -> String {
132        DownloadSpeed::from_raw(bytes).to_formatted(SizeStandard::IEC).to_string()
133    }
134
135    // --- Tests for SI (base-1000) standard ---
136    // --- SI (base-1000) 测试 ---
137    #[test]
138    fn test_si_speed() {
139        assert_eq!(format_test_si(512), "512.0 B/s");
140        assert_eq!(format_test_si(1000), "1.00 KB/s");
141        assert_eq!(format_test_si(1024), "1.02 KB/s");
142        assert_eq!(format_test_si(9999), "10.00 KB/s");
143        assert_eq!(format_test_si(10_000), "10.0 KB/s");
144        assert_eq!(format_test_si(100_000), "100.0 KB/s");
145    }
146
147    // --- Tests for IEC (base-1024) standard ---
148    // --- IEC (base-1024) 测试 ---
149    #[test]
150    fn test_iec_speed() {
151        assert_eq!(format_test_iec(0), "0.00 B/s");
152        assert_eq!(format_test_iec(1024), "1.00 KiB/s");
153        assert_eq!(format_test_iec(1500), "1.46 KiB/s");
154
155        let bytes_near_10 = (9.999 * 1024.0) as u64;
156        assert_eq!(format_test_iec(bytes_near_10), "10.00 KiB/s");
157
158        assert_eq!(format_test_iec(10 * 1024), "10.0 KiB/s");
159        assert_eq!(format_test_iec(100 * 1024), "100.0 KiB/s");
160    }
161
162    #[test]
163    fn test_zero_speed() {
164        let zero_speed = DownloadSpeed::from_raw(0);
165        assert_eq!(zero_speed.as_u64(), 0);
166        assert_eq!(zero_speed.as_scaled(), 0);
167
168        let formatted = zero_speed.to_formatted(SizeStandard::SI).to_string();
169        assert_eq!(formatted, "0.00 B/s");
170
171        let zero_duration = Duration::from_secs(0);
172        let zero_speed = DownloadSpeed::new(1000, zero_duration);
173        assert_eq!(zero_speed.as_u64(), 0);
174    }
175
176    // --- Tests for `new` function ---
177    // --- `new` 函数测试 ---
178    #[test]
179    fn test_new_basic() {
180        // 1000 bytes in 1 second = 1000 B/s
181        let speed = DownloadSpeed::new(1000, Duration::from_secs(1));
182        assert_eq!(speed.as_u64(), 1000);
183
184        // 2000 bytes in 2 seconds = 1000 B/s
185        let speed = DownloadSpeed::new(2000, Duration::from_secs(2));
186        assert_eq!(speed.as_u64(), 1000);
187
188        // 1000 bytes in 0.5 seconds = 2000 B/s
189        let speed = DownloadSpeed::new(1000, Duration::from_millis(500));
190        assert_eq!(speed.as_u64(), 2000);
191    }
192
193    #[test]
194    fn test_new_with_subsec_nanos() {
195        // 1000 bytes in 1.5 seconds = 666 B/s (floored)
196        let speed = DownloadSpeed::new(1000, Duration::from_millis(1500));
197        assert_eq!(speed.as_u64(), 666);
198
199        // 1000 bytes in 100ms = 10000 B/s
200        let speed = DownloadSpeed::new(1000, Duration::from_millis(100));
201        assert_eq!(speed.as_u64(), 10000);
202
203        // Precise nanosecond calculation: 1_000_000 bytes in 1.000_000_001 seconds
204        let speed = DownloadSpeed::new(1_000_000, Duration::new(1, 1));
205        assert!(speed.as_u64() >= 999_999); // approximately 1_000_000 B/s
206    }
207
208    #[test]
209    fn test_new_zero_bytes() {
210        // 0 bytes in any duration = 0 B/s
211        let speed = DownloadSpeed::new(0, Duration::from_secs(10));
212        assert_eq!(speed.as_u64(), 0);
213        assert_eq!(speed.as_scaled(), 0);
214    }
215
216    #[test]
217    fn test_new_zero_duration() {
218        // Any bytes in 0 duration = 0 B/s (prevent division by zero)
219        let speed = DownloadSpeed::new(1000, Duration::ZERO);
220        assert_eq!(speed.as_u64(), 0);
221        assert_eq!(speed.as_scaled(), 0);
222    }
223
224    #[test]
225    fn test_new_large_values() {
226        // 1 GB in 1 second = 1 GB/s
227        let speed = DownloadSpeed::new(1_000_000_000, Duration::from_secs(1));
228        assert_eq!(speed.as_u64(), 1_000_000_000);
229
230        // 10 GB in 10 seconds = 1 GB/s
231        let speed = DownloadSpeed::new(10_000_000_000, Duration::from_secs(10));
232        assert_eq!(speed.as_u64(), 1_000_000_000);
233    }
234
235    #[test]
236    fn test_new_extreme_values() {
237        // 1 byte in 1 second = 1 B/s
238        let speed = DownloadSpeed::new(1, Duration::from_secs(1));
239        assert_eq!(speed.as_u64(), 1);
240
241        // 1 byte in 100 years = 0 B/s (floored)
242        let speed = DownloadSpeed::new(1, Duration::from_secs(100 * 365 * 24 * 60 * 60));
243        assert_eq!(speed.as_u64(), 0);
244    }
245
246    #[test]
247    fn test_new_edge_cases() {
248        // 1 byte in 1 nanosecond = 1_000_000_000 B/s
249        let speed = DownloadSpeed::new(1, Duration::from_nanos(1));
250        assert_eq!(speed.as_u64(), 1_000_000_000);
251
252        // 1 byte in 1 second = 1 B/s
253        let speed = DownloadSpeed::new(1, Duration::from_secs(1));
254        assert_eq!(speed.as_u64(), 1);
255    }
256}