[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