[pbs-devel] [RFC proxmox-backup v2 4/4] HumanByte: do not store unit (always compute canonical form)

Dietmar Maurer dietmar at proxmox.com
Wed Nov 17 14:37:20 CET 2021


---
 pbs-api-types/src/human_byte.rs | 161 ++++++++++++++------------------
 src/cached_traffic_control.rs   |  16 ++--
 2 files changed, 80 insertions(+), 97 deletions(-)

diff --git a/pbs-api-types/src/human_byte.rs b/pbs-api-types/src/human_byte.rs
index 4d8c4f21..f233c9fb 100644
--- a/pbs-api-types/src/human_byte.rs
+++ b/pbs-api-types/src/human_byte.rs
@@ -8,15 +8,10 @@ use proxmox_schema::{ApiStringFormat, ApiType, StringSchema, Schema, UpdaterType
 pub enum SizeUnit {
     None,
     Byte,
-    Kilo,
     KByte,
-    Mega,
     MByte,
-    Giga,
     GByte,
-    Tera,
     TByte,
-    Peta,
     PByte,
     Kibi,
     Mebi,
@@ -31,11 +26,11 @@ impl SizeUnit {
             SizeUnit::None => 1,
             SizeUnit::Byte => 1,
 
-            SizeUnit::Kilo | SizeUnit::KByte  => 1_000,
-            SizeUnit::Mega | SizeUnit::MByte => 1_000_000,
-            SizeUnit::Giga | SizeUnit::GByte => 1_000_000_000,
-            SizeUnit::Tera | SizeUnit::TByte => 1_000_000_000_000,
-            SizeUnit::Peta | SizeUnit::PByte => 1_000_000_000_000_000,
+            SizeUnit::KByte  => 1_000,
+            SizeUnit::MByte => 1_000_000,
+            SizeUnit::GByte => 1_000_000_000,
+            SizeUnit::TByte => 1_000_000_000_000,
+            SizeUnit::PByte => 1_000_000_000_000_000,
 
             SizeUnit::Kibi => 1024,
             SizeUnit::Mebi => 1024*1024,
@@ -50,12 +45,6 @@ impl SizeUnit {
             SizeUnit::None => "",
             SizeUnit::Byte => "B",
 
-            SizeUnit::Kilo => "K",
-            SizeUnit::Mega => "M",
-            SizeUnit::Giga => "G",
-            SizeUnit::Tera => "T",
-            SizeUnit::Peta => "P",
-
             SizeUnit::KByte => "KB",
             SizeUnit::MByte => "MB",
             SizeUnit::GByte => "GB",
@@ -74,19 +63,19 @@ impl SizeUnit {
 fn strip_unit(v: &str) -> (&str, SizeUnit) {
 
     if let Some(v) = v.strip_suffix(&['k', 'K'][..]) {
-        return (v, SizeUnit::Kilo);
+        return (v, SizeUnit::KByte);
     }
     if let Some(v) = v.strip_suffix(&['m', 'M'][..]) {
-        return (v, SizeUnit::Mega);
+        return (v, SizeUnit::MByte);
     }
     if let Some(v) = v.strip_suffix(&['g', 'G'][..]) {
-        return (v, SizeUnit::Giga);
+        return (v, SizeUnit::GByte);
     }
     if let Some(v) = v.strip_suffix(&['t', 'T'][..]) {
-        return (v, SizeUnit::Tera);
+        return (v, SizeUnit::TByte);
     }
     if let Some(v) = v.strip_suffix(&['p', 'P'][..]) {
-        return (v, SizeUnit::Peta);
+        return (v, SizeUnit::PByte);
     }
 
     if let Some(mut v) = v.strip_suffix(&['b', 'B'][..]) {
@@ -122,8 +111,8 @@ fn strip_unit(v: &str) -> (&str, SizeUnit) {
 #[derive(Debug, Copy, Clone, UpdaterType)]
 /// Byte size with unit
 pub struct HumanByte {
-    size: u64,
-    unit: SizeUnit,
+    pub size: u64,
+    binary: bool,
 }
 
 fn verify_human_byte(s: &str) -> Result<(), Error> {
@@ -144,64 +133,54 @@ impl ApiType for HumanByte {
 impl HumanByte {
 
     pub fn new_decimal(size: u64) -> Self {
-        let this = HumanByte { size: size, unit: SizeUnit::None };
-        this.auto_unit_decimal()
+        HumanByte { size: size, binary: false }
     }
 
     pub fn new_binary(size: u64) -> Self {
-        let this = HumanByte { size: size, unit: SizeUnit::None };
-        this.auto_unit_binary()
-    }
-
-    pub fn as_u64(&self) -> u64 {
-        self.size as u64
+        HumanByte { size: size, binary: true }
     }
 
-    pub fn auto_unit_binary(mut self) -> Self {
-        let size = self.size as u64;
-        let zeroes =  size.leading_zeros();
-        let bits = 63 - zeroes;
-        self.unit = if bits >= 50 {
-            SizeUnit::Pebi
-        } else if bits >= 40 {
-            SizeUnit::Tebi
-        } else if bits >= 30 {
-            SizeUnit::Gibi
-        } else if bits >= 20 {
-            SizeUnit::Mebi
-        } else if bits >= 10 {
-            SizeUnit::Kibi
-        } else {
-            SizeUnit::None
-        };
-        self
-    }
-
-    pub fn auto_unit_decimal(mut self) -> Self {
-        self.unit = if self.size >= 1_000_000_000_000_000 {
-            SizeUnit::PByte
-        } else if self.size >= 1_000_000_000_000 {
-            SizeUnit::TByte
-        } else if self.size >= 1_000_000_000 {
-            SizeUnit::GByte
-        } else if self.size >= 1_000_000 {
-            SizeUnit::MByte
-        } else if self.size >= 1_000 {
-            SizeUnit::KByte
+    fn auto_unit(&self) -> SizeUnit {
+        if self.binary {
+            let zeroes =  self.size.leading_zeros();
+            let bits = 63 - zeroes;
+            if bits >= 50 {
+                SizeUnit::Pebi
+            } else if bits >= 40 {
+                SizeUnit::Tebi
+            } else if bits >= 30 {
+                SizeUnit::Gibi
+            } else if bits >= 20 {
+                SizeUnit::Mebi
+            } else if bits >= 10 {
+                SizeUnit::Kibi
+            } else {
+                SizeUnit::None
+            }
         } else {
-            SizeUnit::None
-        };
-        self
+            if self.size >= 1_000_000_000_000_000 {
+                SizeUnit::PByte
+            } else if self.size >= 1_000_000_000_000 {
+                SizeUnit::TByte
+            } else if self.size >= 1_000_000_000 {
+                SizeUnit::GByte
+            } else if self.size >= 1_000_000 {
+                SizeUnit::MByte
+            } else if self.size >= 1_000 {
+                SizeUnit::KByte
+            } else {
+                SizeUnit::None
+            }
+        }
     }
 }
 
 impl std::fmt::Display for HumanByte {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        let size = self.size;
 
-        let unit = self.unit;
+        let unit = self.auto_unit();
 
-        let size = (size as f64)/(unit.factor() as f64);
+        let size = (self.size as f64)/(unit.factor() as f64);
         let unit_str = unit.unit_str();
 
         if unit == SizeUnit::Byte || unit == SizeUnit::None {
@@ -239,9 +218,16 @@ impl FromStr for HumanByte {
 
         let size = (size*(unit.factor() as f64)) as u64;
 
-        Ok(Self { size, unit })
-    }
+        let binary = match unit {
+            SizeUnit::Kibi | SizeUnit::Mebi | SizeUnit::Gibi |
+            SizeUnit::Tebi | SizeUnit::Pebi => true,
 
+            SizeUnit::None | SizeUnit::Byte | SizeUnit::KByte |
+            SizeUnit::MByte | SizeUnit::GByte | SizeUnit::TByte |
+            SizeUnit::PByte => false,
+        };
+        Ok(Self { size, binary})
+    }
 }
 
 proxmox::forward_deserialize_to_from_str!(HumanByte);
@@ -252,15 +238,12 @@ fn test_human_byte_parser() -> Result<(), Error> {
 
     assert!("-10".parse::<HumanByte>().is_err()); // negative size
 
-    fn test(v: &str, size: u64, unit: SizeUnit, as_str: &str) -> Result<(), Error> {
+    fn test(v: &str, size: u64, as_str: &str) -> Result<(), Error> {
         let h: HumanByte = v.parse()?;
 
         if h.size != size {
             bail!("got unexpected size for '{}' ({} != {})", v, h.size, size);
         }
-        if h.unit != unit {
-            bail!("got unexpected unit for '{}' ({:?} != {:?})", v, h.unit, unit);
-        }
 
         let new = h.to_string();
         if &new != as_str {
@@ -271,29 +254,29 @@ fn test_human_byte_parser() -> Result<(), Error> {
         Ok(())
     }
 
-    test("14.4", 14, SizeUnit::None, "14")?;
+    test("14.4", 14, "14")?;
 
-    test("14", 14, SizeUnit::None, "14")?;
-    test("987654321", 987654321, SizeUnit::None, "987654321")?;
+    test("14", 14, "14")?;
+    test("987654321", 987654321, "987.654 MB")?;
 
-    test("1300b", 1300, SizeUnit::Byte, "1300 B")?;
-    test("1300B", 1300, SizeUnit::Byte, "1300 B")?;
+    test("1300b", 1300, "1.3 KB")?;
+    test("1300B", 1300, "1.3 KB")?;
 
-    test("1.5KB", 1500, SizeUnit::KByte, "1.5 KB")?;
-    test("1.5kb", 1500, SizeUnit::KByte, "1.5 KB")?;
-    test("1.654321MB", 1_654_321, SizeUnit::MByte, "1.654 MB")?;
+    test("1.5KB", 1500, "1.5 KB")?;
+    test("1.5kb", 1500, "1.5 KB")?;
+    test("1.654321MB", 1_654_321, "1.654 MB")?;
 
-    test("2.0GB", 2_000_000_000, SizeUnit::GByte, "2 GB")?;
+    test("2.0GB", 2_000_000_000, "2 GB")?;
 
-    test("1.4TB", 1_400_000_000_000, SizeUnit::TByte, "1.4 TB")?;
-    test("1.4tb", 1_400_000_000_000, SizeUnit::TByte, "1.4 TB")?;
+    test("1.4TB", 1_400_000_000_000, "1.4 TB")?;
+    test("1.4tb", 1_400_000_000_000, "1.4 TB")?;
 
-    test("2KiB", 2048, SizeUnit::Kibi, "2 KiB")?;
-    test("2kib", 2048, SizeUnit::Kibi, "2 KiB")?;
+    test("2KiB", 2048, "2 KiB")?;
+    test("2kib", 2048, "2 KiB")?;
 
-    test("2.3456MiB", (2.3456*1024.0*1024.0) as u64, SizeUnit::Mebi, "2.345 MiB")?;
+    test("2.3456MiB", (2.3456*1024.0*1024.0) as u64, "2.345 MiB")?;
 
-    test("4gib", (4.0*1024.0*1024.0*1024.0) as u64, SizeUnit::Gibi, "4 GiB")?;
+    test("4gib", (4.0*1024.0*1024.0*1024.0) as u64, "4 GiB")?;
 
     Ok(())
 }
diff --git a/src/cached_traffic_control.rs b/src/cached_traffic_control.rs
index b45e564c..05bf8a60 100644
--- a/src/cached_traffic_control.rs
+++ b/src/cached_traffic_control.rs
@@ -225,8 +225,8 @@ impl TrafficControlCache {
                     match rule.rate_in {
                         Some(rate_in) => {
                             read_limiter.update_rate(
-                                rate_in.as_u64(),
-                                rule.burst_in.unwrap_or(rate_in).as_u64(),
+                                rate_in.size,
+                                rule.burst_in.unwrap_or(rate_in).size,
                             );
                         }
                         None => entry.0 = None,
@@ -238,8 +238,8 @@ impl TrafficControlCache {
                         let limiter = create_limiter(
                             self.use_shared_memory,
                             &name,
-                            rate_in.as_u64(),
-                            rule.burst_in.unwrap_or(rate_in).as_u64(),
+                            rate_in.size,
+                            rule.burst_in.unwrap_or(rate_in).size,
                         )?;
                         entry.0 = Some(limiter);
                     }
@@ -251,8 +251,8 @@ impl TrafficControlCache {
                     match rule.rate_out {
                         Some(rate_out) => {
                             write_limiter.update_rate(
-                                rate_out.as_u64(),
-                                rule.burst_out.unwrap_or(rate_out).as_u64(),
+                                rate_out.size,
+                                rule.burst_out.unwrap_or(rate_out).size,
                             );
                         }
                         None => entry.1 = None,
@@ -264,8 +264,8 @@ impl TrafficControlCache {
                         let limiter = create_limiter(
                             self.use_shared_memory,
                             &name,
-                            rate_out.as_u64(),
-                            rule.burst_out.unwrap_or(rate_out).as_u64(),
+                            rate_out.size,
+                            rule.burst_out.unwrap_or(rate_out).size,
                         )?;
                         entry.1 = Some(limiter);
                     }
-- 
2.30.2






More information about the pbs-devel mailing list