parallel_processor/
memory_data_size.rs

1//! Data measurements utils, taken from https://github.com/jocull/rust-measurements.git
2
3/// This is a special macro that creates the code to implement
4/// `std::fmt::Display`.
5#[macro_export]
6macro_rules! implement_display {
7    ($($t:ty)*) => ($(
8
9        impl ::std::fmt::Display for $t {
10            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
11                let (unit, value) = self.get_appropriate_units();
12                value.fmt(f)?;      // Value
13                write!(f, "\u{00A0}{}", unit)
14            }
15        }
16    )*)
17}
18
19/// This is a special macro that creates the code to implement
20/// operator and comparison overrides.
21macro_rules! implement_measurement {
22    ($($t:ty)*) => ($(
23
24        implement_display!( $t );
25
26        impl ::std::ops::Add for $t {
27            type Output = Self;
28
29            fn add(self, rhs: Self) -> Self {
30                Self::from_base_units(self.as_base_units() + rhs.as_base_units())
31            }
32        }
33
34        impl ::std::ops::Sub for $t {
35            type Output = Self;
36
37            fn sub(self, rhs: Self) -> Self {
38                Self::from_base_units(self.as_base_units() - rhs.as_base_units())
39            }
40        }
41
42        // Dividing a `$t` by another `$t` returns a ratio.
43        //
44        impl ::std::ops::Div<$t> for $t {
45            type Output = f64;
46
47            fn div(self, rhs: Self) -> f64 {
48                self.as_base_units() / rhs.as_base_units()
49            }
50        }
51
52        // Dividing a `$t` by a factor returns a new portion of the measurement.
53        //
54        impl ::std::ops::Div<f64> for $t {
55            type Output = Self;
56
57            fn div(self, rhs: f64) -> Self {
58                Self::from_base_units(self.as_base_units() / rhs)
59            }
60        }
61
62        // Multiplying a `$t` by a factor increases (or decreases) that
63        // measurement a number of times.
64        impl ::std::ops::Mul<f64> for $t {
65            type Output = Self;
66
67            fn mul(self, rhs: f64) -> Self {
68                Self::from_base_units(self.as_base_units() * rhs)
69            }
70        }
71
72        // Multiplying `$t` by a factor is commutative
73        impl ::std::ops::Mul<$t> for f64 {
74            type Output = $t;
75
76            fn mul(self, rhs: $t) -> $t {
77                rhs * self
78            }
79        }
80
81        impl ::std::cmp::Eq for $t { }
82        impl ::std::cmp::PartialEq for $t {
83            fn eq(&self, other: &Self) -> bool {
84                self.as_base_units() == other.as_base_units()
85            }
86        }
87
88        impl ::std::cmp::PartialOrd for $t {
89            fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
90                self.as_base_units().partial_cmp(&other.as_base_units())
91            }
92        }
93    )*)
94}
95
96// Types and constants for handling amounts of data (in octets, or bits).
97
98/// The `Data` struct can be used to deal with computer information in a common way.
99/// Common legacy and SI units are supported.
100///
101/// # Example
102///
103#[derive(Copy, Clone, Debug)]
104pub struct MemoryDataSize {
105    /// Number of octets
106    pub octets: f64,
107}
108
109impl MemoryDataSize {
110    // Constants
111    pub const OCTET_BIT_FACTOR: f64 = 0.125;
112
113    // Constants, legacy
114    pub const OCTET_KILOOCTET_FACTOR: u64 = 1000;
115    pub const OCTET_MEGAOCTET_FACTOR: u64 = 1000 * 1000;
116    pub const OCTET_GIGAOCTET_FACTOR: u64 = 1000 * 1000 * 1000;
117    pub const OCTET_TERAOCTET_FACTOR: u64 = 1000 * 1000 * 1000 * 1000;
118
119    // Constants, SI
120    pub const OCTET_KIBIOCTET_FACTOR: u64 = 1024;
121    pub const OCTET_MEBIOCTET_FACTOR: u64 = 1024 * 1024;
122    pub const OCTET_GIBIOCTET_FACTOR: u64 = 1024 * 1024 * 1024;
123    pub const OCTET_TEBIOCTET_FACTOR: u64 = 1024 * 1024 * 1024 * 1024;
124
125    /// Create new Data from floating point value in Octets
126    pub const fn from_octets(octets: f64) -> Self {
127        MemoryDataSize { octets }
128    }
129
130    /// Create new Data from floating point value in Bits
131    pub fn from_bits(bits: f64) -> Self {
132        Self::from_octets(bits * Self::OCTET_BIT_FACTOR)
133    }
134
135    // Inputs, legacy
136    /// Create new Data from floating point value in Kilooctets (1000 octets)
137    pub const fn from_kilooctets(kilooctets: u64) -> Self {
138        Self::from_octets((kilooctets * Self::OCTET_KILOOCTET_FACTOR) as f64)
139    }
140
141    /// Create new Data from floating point value in Megaoctets (1e6 octets)
142    pub const fn from_megaoctets(megaoctets: u64) -> Self {
143        Self::from_octets((megaoctets * Self::OCTET_MEGAOCTET_FACTOR) as f64)
144    }
145
146    /// Create new Data from floating point value in Gigaoctets (1e9 octets)
147    pub const fn from_gigaoctets(gigaoctets: u64) -> Self {
148        Self::from_octets((gigaoctets * Self::OCTET_GIGAOCTET_FACTOR) as f64)
149    }
150
151    /// Create new Data from floating point value in Teraoctets (1e12 octets)
152    pub const fn from_teraoctets(teraoctets: u64) -> Self {
153        Self::from_octets((teraoctets * Self::OCTET_TERAOCTET_FACTOR) as f64)
154    }
155
156    /// Create new Data from floating point value in Kibioctets (1024 octets)
157    pub const fn from_kibioctets(kibioctets: u64) -> Self {
158        Self::from_octets((kibioctets * Self::OCTET_KIBIOCTET_FACTOR) as f64)
159    }
160
161    /// Create new Data from floating point value in Mebioctets (1024**2 octets)
162    pub const fn from_mebioctets(mebioctets: u64) -> Self {
163        Self::from_octets((mebioctets * Self::OCTET_MEBIOCTET_FACTOR) as f64)
164    }
165
166    /// Create new Data from floating point value in Gibioctets (1024**3 octets)
167    pub const fn from_gibioctets(gibioctets: u64) -> Self {
168        Self::from_octets((gibioctets * Self::OCTET_GIBIOCTET_FACTOR) as f64)
169    }
170
171    /// Create new Data from floating point value in Tebioctets (1024**4 octets)
172    pub const fn from_tebioctets(tebioctets: u64) -> Self {
173        Self::from_octets((tebioctets * Self::OCTET_TEBIOCTET_FACTOR) as f64)
174    }
175
176    /// Convert this Data to a floating point value in Octets
177    pub fn as_octets(&self) -> f64 {
178        self.octets
179    }
180
181    /// Convert this Data to a floating point value in Bits
182    pub fn as_bits(&self) -> f64 {
183        self.octets / Self::OCTET_BIT_FACTOR
184    }
185
186    /// Convert this Data to a floating point value in Kilooctets (1000 octets)
187    pub fn as_kilooctets(&self) -> f64 {
188        self.octets / (Self::OCTET_KILOOCTET_FACTOR as f64)
189    }
190
191    /// Convert this Data to a floating point value in Megaoctets (1e6 octets)
192    pub fn as_megaoctets(&self) -> f64 {
193        self.octets / (Self::OCTET_MEGAOCTET_FACTOR as f64)
194    }
195
196    /// Convert this Data to a floating point value in Gigaoctets (1e9 octets)
197    pub fn as_gigaoctets(&self) -> f64 {
198        self.octets / (Self::OCTET_GIGAOCTET_FACTOR as f64)
199    }
200
201    /// Convert this Data to a floating point value in Teraoctets (1e12 octets)
202    pub fn as_teraoctets(&self) -> f64 {
203        self.octets / (Self::OCTET_TERAOCTET_FACTOR as f64)
204    }
205
206    /// Convert this Data to a floating point value in Kibioctets (1024 octets)
207    pub fn as_kibioctets(&self) -> f64 {
208        self.octets / (Self::OCTET_KIBIOCTET_FACTOR as f64)
209    }
210
211    /// Convert this Data to a floating point value in Mebioctets (1024**2 octets)
212    pub fn as_mebioctets(&self) -> f64 {
213        self.octets / (Self::OCTET_MEBIOCTET_FACTOR as f64)
214    }
215
216    /// Convert this Data to a floating point value in Gibioctets (1024**3 octets)
217    pub fn as_gibioctets(&self) -> f64 {
218        self.octets / (Self::OCTET_GIBIOCTET_FACTOR as f64)
219    }
220
221    /// Convert this Data to a floating point value in Tebioctets (1024**4 octets)
222    pub fn as_tebioctets(&self) -> f64 {
223        self.octets / (Self::OCTET_TEBIOCTET_FACTOR as f64)
224    }
225
226    fn as_base_units(&self) -> f64 {
227        self.octets
228    }
229
230    fn from_base_units(units: f64) -> Self {
231        Self::from_octets(units)
232    }
233
234    fn get_appropriate_units(&self) -> (&'static str, f64) {
235        // Smallest to largest
236        let list = [
237            ("octets", 1.0),
238            ("KiB", 1024.0),
239            ("MiB", 1024.0 * 1024.0),
240            ("GiB", 1024.0 * 1024.0 * 1024.0),
241            ("TiB", 1024.0 * 1024.0 * 1024.0 * 1024.0),
242            ("PiB", 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0),
243            ("EiB", 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0),
244        ];
245        self.pick_appropriate_units(&list)
246    }
247
248    /// Given a list of units and their scale relative to the base unit,
249    /// select the most appropriate one.
250    ///
251    /// The list must be smallest to largest, e.g. ("nanometre", 10-9) to
252    /// ("kilometre", 10e3)
253    fn pick_appropriate_units(&self, list: &[(&'static str, f64)]) -> (&'static str, f64) {
254        for &(unit, ref scale) in list.iter().rev() {
255            let value = self.as_base_units() / scale;
256            if value >= 1.0 || value <= -1.0 {
257                return (unit, value);
258            }
259        }
260        (list[0].0, self.as_base_units() / list[0].1)
261    }
262
263    pub fn as_bytes(&self) -> usize {
264        self.as_base_units() as usize
265    }
266    pub fn from_bytes(bytes: usize) -> Self {
267        Self::from_base_units(bytes as f64)
268    }
269    pub fn max(self, other: Self) -> Self {
270        Self::from_base_units(self.as_base_units().max(other.as_base_units()))
271    }
272}
273
274implement_measurement! { MemoryDataSize }
275
276#[cfg(test)]
277mod test {
278    use crate::memory_data_size::MemoryDataSize;
279
280    const DEFAULT_DELTA: f64 = 1e-5;
281
282    /// Check two floating point values are approximately equal using some given delta (a fraction of the inputs)
283    fn almost_eq_delta(a: f64, b: f64, d: f64) -> bool {
284        ((a - b).abs() / a) < d
285    }
286
287    /// Assert two floating point values are approximately equal using some given delta (a fraction of the inputs)
288    fn assert_almost_eq_delta(a: f64, b: f64, d: f64) {
289        if !almost_eq_delta(a, b, d) {
290            panic!("assertion failed: {:?} != {:?} (within {:?})", a, b, d);
291        }
292    }
293
294    /// Assert two floating point values are approximately equal
295    fn assert_almost_eq(a: f64, b: f64) {
296        assert_almost_eq_delta(a, b, DEFAULT_DELTA);
297    }
298
299    // Metric
300    #[test]
301    fn bits() {
302        let i1 = MemoryDataSize::from_octets(100.0);
303        let r1 = i1.as_bits();
304
305        let i2 = MemoryDataSize::from_bits(100.0);
306        let r2 = i2.as_octets();
307
308        assert_almost_eq(r1, 800.0);
309        assert_almost_eq(r2, 12.5);
310    }
311
312    #[test]
313    fn kilooctet() {
314        let i1 = MemoryDataSize::from_octets(100.0);
315        let r1 = i1.as_kilooctets();
316
317        let i2 = MemoryDataSize::from_kilooctets(100);
318        let r2 = i2.as_octets();
319
320        assert_almost_eq(r1, 0.1);
321        assert_almost_eq(r2, 1e5);
322    }
323
324    #[test]
325    fn megaoctet() {
326        let i1 = MemoryDataSize::from_octets(100.0);
327        let r1 = i1.as_megaoctets();
328
329        let i2 = MemoryDataSize::from_megaoctets(100);
330        let r2 = i2.as_octets();
331
332        assert_almost_eq(r1, 0.0001);
333        assert_almost_eq(r2, 1e8);
334    }
335
336    #[test]
337    fn gigaoctet() {
338        let i1 = MemoryDataSize::from_octets(100.0);
339        let r1 = i1.as_gigaoctets();
340
341        let i2 = MemoryDataSize::from_gigaoctets(100);
342        let r2 = i2.as_octets();
343
344        assert_almost_eq(r1, 1e-7);
345        assert_almost_eq(r2, 1e11);
346    }
347
348    #[test]
349    fn teraoctet() {
350        let i1 = MemoryDataSize::from_octets(100.0);
351        let r1 = i1.as_teraoctets();
352
353        let i2 = MemoryDataSize::from_teraoctets(100);
354        let r2 = i2.as_octets();
355
356        assert_almost_eq(r1, 1e-10);
357        assert_almost_eq(r2, 1e14);
358    }
359
360    // Imperial
361    #[test]
362    fn kibioctet() {
363        let i1 = MemoryDataSize::from_octets(100.0);
364        let r1 = i1.as_kibioctets();
365
366        let i2 = MemoryDataSize::from_kibioctets(100);
367        let r2 = i2.as_octets();
368
369        assert_almost_eq(r1, 0.09765625);
370        assert_almost_eq(r2, 102400.0);
371    }
372
373    #[test]
374    fn mebioctet() {
375        let i1 = MemoryDataSize::from_octets(100.0);
376        let r1 = i1.as_mebioctets();
377
378        let i2 = MemoryDataSize::from_mebioctets(100);
379        let r2 = i2.as_octets();
380
381        assert_almost_eq(r1, 9.536743e-5);
382        assert_almost_eq(r2, 104857600.0);
383    }
384
385    #[test]
386    fn gibioctets() {
387        let i1 = MemoryDataSize::from_octets(100.0);
388        let r1 = i1.as_gibioctets();
389
390        let i2 = MemoryDataSize::from_gibioctets(100);
391        let r2 = i2.as_octets();
392
393        assert_almost_eq(r1, 9.313226e-8);
394        assert_almost_eq(r2, 107374182400.0);
395    }
396
397    #[test]
398    fn tebioctets() {
399        let i1 = MemoryDataSize::from_octets(100.0);
400        let r1 = i1.as_tebioctets();
401
402        let i2 = MemoryDataSize::from_tebioctets(100);
403        let r2 = i2.as_octets();
404
405        assert_almost_eq(r1, 9.094947e-11);
406        assert_almost_eq(r2, 109951162777600.0);
407    }
408
409    // Traits
410    #[test]
411    fn add() {
412        let a = MemoryDataSize::from_octets(2.0);
413        let b = MemoryDataSize::from_octets(4.0);
414        let c = a + b;
415        assert_almost_eq(c.as_octets(), 6.0);
416    }
417
418    #[test]
419    fn sub() {
420        let a = MemoryDataSize::from_octets(2.0);
421        let b = MemoryDataSize::from_octets(4.0);
422        let c = a - b;
423        assert_almost_eq(c.as_octets(), -2.0);
424    }
425
426    #[test]
427    fn mul() {
428        let b = MemoryDataSize::from_octets(4.0);
429        let d = b * 2.0;
430        assert_almost_eq(d.as_octets(), 8.0);
431    }
432
433    #[test]
434    fn div() {
435        let b = MemoryDataSize::from_octets(4.0);
436        let d = b / 2.0;
437        assert_almost_eq(d.as_octets(), 2.0);
438    }
439
440    #[test]
441    fn eq() {
442        let a = MemoryDataSize::from_octets(2.0);
443        let b = MemoryDataSize::from_octets(2.0);
444        assert_eq!(a == b, true);
445    }
446
447    #[test]
448    fn neq() {
449        let a = MemoryDataSize::from_octets(2.0);
450        let b = MemoryDataSize::from_octets(4.0);
451        assert_eq!(a == b, false);
452    }
453
454    #[test]
455    fn cmp() {
456        let a = MemoryDataSize::from_octets(2.0);
457        let b = MemoryDataSize::from_octets(4.0);
458        assert_eq!(a < b, true);
459        assert_eq!(a <= b, true);
460        assert_eq!(a > b, false);
461        assert_eq!(a >= b, false);
462    }
463}