From: Dietmar Maurer <dietmar@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [RFC proxmox-backup v2 4/4] HumanByte: do not store unit (always compute canonical form)
Date: Wed, 17 Nov 2021 14:37:20 +0100 [thread overview]
Message-ID: <20211117133720.1799124-5-dietmar@proxmox.com> (raw)
In-Reply-To: <20211117133720.1799124-1-dietmar@proxmox.com>
---
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
prev parent reply other threads:[~2021-11-17 13:37 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-17 13:37 [pbs-devel] [RFC proxmox-backup v2 0/4] more flexible HumanByte type Dietmar Maurer
2021-11-17 13:37 ` [pbs-devel] [RFC proxmox-backup v2 1/4] pbs-api-types: " Dietmar Maurer
2021-11-17 13:37 ` [pbs-devel] [RFC proxmox-backup v2 2/4] use HumanByte for traffic-control config Dietmar Maurer
2021-11-17 13:37 ` [pbs-devel] [RFC proxmox-backup v2 3/4] HumanByte: use u64 instead of f64 to store size Dietmar Maurer
2021-11-17 13:37 ` Dietmar Maurer [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211117133720.1799124-5-dietmar@proxmox.com \
--to=dietmar@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.