From 11947bea15c58c7aa2bee71100ab6ef76ed09874 Mon Sep 17 00:00:00 2001 From: tison Date: Sun, 28 Jun 2026 09:54:00 +0800 Subject: [PATCH 1/5] feat: Add BSize::as_b Signed-off-by: tison --- bsize/src/types/mod.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/bsize/src/types/mod.rs b/bsize/src/types/mod.rs index 6c9e3f0..bbdeb6d 100644 --- a/bsize/src/types/mod.rs +++ b/bsize/src/types/mod.rs @@ -56,6 +56,24 @@ impl BSize { } } +macro_rules! impl_byte_accessor { + ($($ty:ty),* $(,)?) => { + $( + impl BSize<$ty> { + /// Returns byte count as bytes. + /// + /// The result is approximate when the byte count cannot be + /// represented exactly as `f64`. Use `.0` or [`BSize::with`] + /// for the exact underlying integer value. + #[inline(always)] + pub const fn as_b(&self) -> f64 { + self.0 as f64 + } + } + )* + }; +} + macro_rules! impl_accessors { ($ty:ty => { $($name:ident = $trait:ident::$size:ident => $unit:literal),* $(,)? }) => { impl BSize<$ty> { @@ -73,6 +91,8 @@ macro_rules! impl_accessors { }; } +impl_byte_accessor!(u8, u16, u32, u64, usize); + macro_rules! impl_usize_accessors { (through_kilo) => { impl_accessors!(usize => { @@ -175,6 +195,15 @@ mod tests { assert_eq!(BSize::::b(2).0, 2); } + #[test] + fn returns_byte_units() { + assert_close(BSize::::b(2).as_b(), 2.0); + assert_close(BSize::::b(2).as_b(), 2.0); + assert_close(BSize::::b(2).as_b(), 2.0); + assert_close(BSize::::b(2).as_b(), 2.0); + assert_close(BSize::::b(2).as_b(), 2.0); + } + #[test] fn maps_underlying_byte_count() { assert_eq!( From 337339121928e1b5c5948e6230e3dcb6fda3ffae Mon Sep 17 00:00:00 2001 From: tison Date: Sun, 28 Jun 2026 10:09:03 +0800 Subject: [PATCH 2/5] fixup Signed-off-by: tison --- CHANGELOG.md | 3 ++ bsize/src/display.rs | 10 ++--- bsize/src/lib.rs | 1 - bsize/src/traits/mod.rs | 71 +++++++++++++++++++++++++----------- bsize/src/types/mod.rs | 8 ++-- bsize/src/types/nightly.rs | 75 +++++++++++++++++++++++++++++++++++++- bsize/src/types/stable.rs | 1 + 7 files changed, 137 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30a443f..00149a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,13 @@ All significant changes to this software be documented in this file. ### Breaking changes * Renamed `BSize::with` to `BSize::map`. A new `BSize::with` method has been added that accepts a closure returning arbitrary type, allowing for mapping a `BSize` to any other type. This follows `LocalKey::with`'s naming conventions. +* Removed the `Displayable` trait. Use `ByteSize` directly for values accepted by `bsize::display`. ### New features * Added `nightly` feature for using `BSize` with nightly-only features like `const_ops` and `const_trait_impl`. +* Added `ByteSize::as_f64` for converting supported byte size underlying types to approximate `f64` values. +* Added `BSize::as_b` for returning the byte count as an approximate `f64`. ## v0.2.1 (2026-06-27) diff --git a/bsize/src/display.rs b/bsize/src/display.rs index f2a86db..4b9f268 100644 --- a/bsize/src/display.rs +++ b/bsize/src/display.rs @@ -16,22 +16,22 @@ use core::fmt; use core::fmt::Write as _; use crate::BSize; -use crate::Displayable; +use crate::ByteSize; /// Create a [`Display`] instance for displaying a byte size. /// /// See [`Display`] for examples. Use [`Display::new`] when the byte count is already represented /// as an `f64`. -pub fn display(size: impl Displayable) -> Display { - Display::new(size.canonicalize()) +pub fn display(size: impl ByteSize) -> Display { + Display::new(size.as_f64()) } -impl BSize { +impl BSize { /// Returns a [`Display`] wrapper. /// /// See [`Display`] for examples. pub fn display(&self) -> Display { - Display::new(self.0.canonicalize()) + Display::new(self.0.as_f64()) } } diff --git a/bsize/src/lib.rs b/bsize/src/lib.rs index 6ee6530..726fa1e 100644 --- a/bsize/src/lib.rs +++ b/bsize/src/lib.rs @@ -119,7 +119,6 @@ pub use self::display::DisplayUnitSystem; pub use self::display::display; pub use self::parse::ParseError; pub use self::traits::ByteSize; -pub use self::traits::Displayable; pub use self::traits::ExaByteSize; pub use self::traits::GigaByteSize; pub use self::traits::KiloByteSize; diff --git a/bsize/src/traits/mod.rs b/bsize/src/traits/mod.rs index 462229a..57b7a5c 100644 --- a/bsize/src/traits/mod.rs +++ b/bsize/src/traits/mod.rs @@ -23,9 +23,55 @@ mod private { impl_marker!(Sealed for u8, u16, u32, u64, usize); } -/// A marker trait for all supported byte size underneath type. -pub trait ByteSize: private::Sealed {} -impl_marker!(ByteSize for u8, u16, u32, u64, usize); +macro_rules! define_byte_size_trait { + (trait_prefix = [$($trait_prefix:tt)*];) => { + /// A marker trait for all supported byte size underlying types. + /// + /// The conversion to `f64` is approximate when the byte count cannot be + /// represented exactly as `f64`. + pub $($trait_prefix)* trait ByteSize: Copy + private::Sealed { + /// Returns the value as an approximate `f64`. + fn as_f64(&self) -> f64; + } + }; +} + +#[cfg(not(feature = "nightly"))] +define_byte_size_trait! { + trait_prefix = []; +} + +#[cfg(feature = "nightly")] +define_byte_size_trait! { + trait_prefix = [const]; +} + +macro_rules! impl_byte_size { + (impl_prefix = []; $($ty:ty),* $(,)?) => { + $( + impl ByteSize for $ty { + fn as_f64(&self) -> f64 { + *self as f64 + } + } + )* + }; + (impl_prefix = [const]; $($ty:ty),* $(,)?) => { + $( + const impl ByteSize for $ty { + fn as_f64(&self) -> f64 { + *self as f64 + } + } + )* + }; +} + +#[cfg(not(feature = "nightly"))] +impl_byte_size!(impl_prefix = []; u8, u16, u32, u64, usize); + +#[cfg(feature = "nightly")] +impl_byte_size!(impl_prefix = [const]; u8, u16, u32, u64, usize); macro_rules! define_unit_traits { ( @@ -203,22 +249,3 @@ mod stable; pub use self::nightly::*; #[cfg(not(feature = "nightly"))] pub use self::stable::*; - -/// A trait for all displayable byte size underneath type. -pub trait Displayable: ByteSize { - /// Convert the byte size payload to a canonicalized floating point representation, - /// which will then be used for display purposes. - fn canonicalize(&self) -> f64; -} - -macro_rules! impl_displayable { - ($($ty:ty),* $(,)?) => ($( - impl Displayable for $ty { - fn canonicalize(&self) -> f64 { - *self as f64 - } - } - )*) -} - -impl_displayable!(u8, u16, u32, u64, usize); diff --git a/bsize/src/types/mod.rs b/bsize/src/types/mod.rs index bbdeb6d..9b28462 100644 --- a/bsize/src/types/mod.rs +++ b/bsize/src/types/mod.rs @@ -56,7 +56,8 @@ impl BSize { } } -macro_rules! impl_byte_accessor { +#[cfg(not(feature = "nightly"))] +macro_rules! impl_byte_accessors { ($($ty:ty),* $(,)?) => { $( impl BSize<$ty> { @@ -74,6 +75,7 @@ macro_rules! impl_byte_accessor { }; } +#[cfg(not(feature = "nightly"))] macro_rules! impl_accessors { ($ty:ty => { $($name:ident = $trait:ident::$size:ident => $unit:literal),* $(,)? }) => { impl BSize<$ty> { @@ -91,8 +93,7 @@ macro_rules! impl_accessors { }; } -impl_byte_accessor!(u8, u16, u32, u64, usize); - +#[cfg(not(feature = "nightly"))] macro_rules! impl_usize_accessors { (through_kilo) => { impl_accessors!(usize => { @@ -122,6 +123,7 @@ macro_rules! impl_usize_accessors { }; } +#[cfg(not(feature = "nightly"))] macro_rules! impl_unit_accessors { () => { impl_accessors!(u16 => { diff --git a/bsize/src/types/nightly.rs b/bsize/src/types/nightly.rs index fc564ea..9382e4b 100644 --- a/bsize/src/types/nightly.rs +++ b/bsize/src/types/nightly.rs @@ -13,6 +13,7 @@ // limitations under the License. use super::BSize; +use crate::traits::ByteSize; use crate::traits::ExaByteSize; use crate::traits::GigaByteSize; use crate::traits::KiloByteSize; @@ -71,7 +72,70 @@ impl_constructors!(ExaByteSize => { eib = EIB, }); -impl_unit_accessors!(); +impl BSize { + /// Returns byte count as bytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. Use `.0` or [`BSize::with`] for the exact underlying + /// integer value. + #[inline(always)] + pub const fn as_b(&self) -> f64 + where + T: [const] ByteSize, + { + self.0.as_f64() + } +} + +macro_rules! impl_accessors { + ($trait:ident => { $($name:ident = $size:ident => $unit:literal),* $(,)? }) => { + impl BSize { + $( + #[doc = concat!("Returns byte count as ", $unit, ".")] + /// + /// The result is approximate when the byte count cannot be + /// represented exactly as `f64`. + #[inline(always)] + pub const fn $name(&self) -> f64 + where + T: [const] $trait + [const] ByteSize, + { + self.0.as_f64() / T::$size.as_f64() + } + )* + } + }; +} + +impl_accessors!(KiloByteSize => { + as_kb = KB => "kilobytes", + as_kib = KIB => "kibibytes", +}); + +impl_accessors!(MegaByteSize => { + as_mb = MB => "megabytes", + as_mib = MIB => "mebibytes", +}); + +impl_accessors!(GigaByteSize => { + as_gb = GB => "gigabytes", + as_gib = GIB => "gibibytes", +}); + +impl_accessors!(TeraByteSize => { + as_tb = TB => "terabytes", + as_tib = TIB => "tebibytes", +}); + +impl_accessors!(PetaByteSize => { + as_pb = PB => "petabytes", + as_pib = PIB => "pebibytes", +}); + +impl_accessors!(ExaByteSize => { + as_eb = EB => "exabytes", + as_eib = EIB => "exbibytes", +}); #[cfg(test)] mod tests { @@ -100,6 +164,15 @@ mod tests { assert_eq!(SIZE, BSize::b(16 * 1_024)); } + #[test] + fn inferred_accessors_are_const() { + const BYTES: f64 = BSize::b(16_u64).as_b(); + const KIB: f64 = BSize::kib(16_u64).as_kib(); + + assert_close(BYTES, 16.0); + assert_close(KIB, 16.0); + } + #[cfg(target_pointer_width = "16")] #[test] fn returns_usize_units() { diff --git a/bsize/src/types/stable.rs b/bsize/src/types/stable.rs index 83ac75f..605ad0f 100644 --- a/bsize/src/types/stable.rs +++ b/bsize/src/types/stable.rs @@ -97,6 +97,7 @@ impl_usize_constructors!(through_giga); #[cfg(target_pointer_width = "64")] impl_usize_constructors!(through_exa); +impl_byte_accessors!(u8, u16, u32, u64, usize); impl_unit_accessors!(); #[cfg(test)] From 87874273dbbaf052e9602841be700607898aa9c7 Mon Sep 17 00:00:00 2001 From: tison Date: Sun, 28 Jun 2026 10:38:22 +0800 Subject: [PATCH 3/5] refactor Signed-off-by: tison --- bsize/src/lib.rs | 11 + bsize/src/ops/mod.rs | 55 ++++ bsize/src/ops/nightly.rs | 83 ++++++ bsize/src/{ops.rs => ops/stable.rs} | 37 --- bsize/src/traits/mod.rs | 229 +--------------- bsize/src/traits/nightly.rs | 180 ++++++++++++- bsize/src/traits/stable.rs | 180 ++++++++++++- bsize/src/types/mod.rs | 158 +++-------- bsize/src/types/nightly.rs | 397 ++++++++++++++++++---------- bsize/src/types/stable.rs | 158 +++++++---- 10 files changed, 902 insertions(+), 586 deletions(-) create mode 100644 bsize/src/ops/mod.rs create mode 100644 bsize/src/ops/nightly.rs rename bsize/src/{ops.rs => ops/stable.rs} (63%) diff --git a/bsize/src/lib.rs b/bsize/src/lib.rs index 726fa1e..70d3106 100644 --- a/bsize/src/lib.rs +++ b/bsize/src/lib.rs @@ -127,6 +127,17 @@ pub use self::traits::PetaByteSize; pub use self::traits::TeraByteSize; pub use self::types::BSize; +#[cfg(test)] +fn assert_close(actual: f64, expected: f64) { + let delta = (actual - expected).abs(); + let tolerance = f64::EPSILON; + + assert!( + delta <= tolerance, + "actual: {actual}, expected: {expected}, delta: {delta}, tolerance: {tolerance}", + ); +} + #[cfg(test)] mod property_tests { use alloc::string::String; diff --git a/bsize/src/ops/mod.rs b/bsize/src/ops/mod.rs new file mode 100644 index 0000000..388269c --- /dev/null +++ b/bsize/src/ops/mod.rs @@ -0,0 +1,55 @@ +// Copyright 2026 FastLabs Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(feature = "nightly")] +mod nightly; +#[cfg(not(feature = "nightly"))] +mod stable; + +#[cfg(test)] +mod tests { + use crate::BSize; + + #[test] + fn adds_byte_sizes() { + assert_eq!((BSize::(3) + BSize(5)).0, 8); + assert_eq!((BSize::(3) + BSize(5)).0, 8); + assert_eq!((BSize::(3) + BSize(5)).0, 8); + assert_eq!((BSize::(3) + BSize(5)).0, 8); + assert_eq!((BSize::(3) + BSize(5)).0, 8); + } + + #[test] + fn add_assigns_byte_sizes() { + let mut size = BSize::(3); + size += BSize(5); + assert_eq!(size.0, 8); + } + + #[test] + fn subtracts_byte_sizes() { + assert_eq!((BSize::(8) - BSize(5)).0, 3); + assert_eq!((BSize::(8) - BSize(5)).0, 3); + assert_eq!((BSize::(8) - BSize(5)).0, 3); + assert_eq!((BSize::(8) - BSize(5)).0, 3); + assert_eq!((BSize::(8) - BSize(5)).0, 3); + } + + #[test] + fn sub_assigns_byte_sizes() { + let mut size = BSize::(8); + size -= BSize(5); + assert_eq!(size.0, 3); + } +} diff --git a/bsize/src/ops/nightly.rs b/bsize/src/ops/nightly.rs new file mode 100644 index 0000000..94cc58e --- /dev/null +++ b/bsize/src/ops/nightly.rs @@ -0,0 +1,83 @@ +// Copyright 2026 FastLabs Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::ops; + +use crate::traits::ByteSize; +use crate::types::BSize; + +const impl ops::Add> for BSize +where + T: [const] ByteSize + [const] ops::Add, +{ + type Output = Self; + + #[inline(always)] + fn add(self, rhs: BSize) -> Self::Output { + BSize(self.0 + rhs.0) + } +} + +const impl ops::AddAssign> for BSize +where + T: [const] ByteSize + [const] ops::AddAssign, +{ + #[inline(always)] + fn add_assign(&mut self, rhs: BSize) { + self.0 += rhs.0; + } +} + +const impl ops::Sub> for BSize +where + T: [const] ByteSize + [const] ops::Sub, +{ + type Output = Self; + + #[inline(always)] + fn sub(self, rhs: BSize) -> Self::Output { + BSize(self.0 - rhs.0) + } +} + +const impl ops::SubAssign> for BSize +where + T: [const] ByteSize + [const] ops::SubAssign, +{ + #[inline(always)] + fn sub_assign(&mut self, rhs: BSize) { + self.0 -= rhs.0; + } +} + +#[cfg(test)] +mod tests { + use crate::BSize; + + #[test] + fn arithmetic_is_const() { + const SUM: BSize = BSize::b(3_u64) + BSize::b(5_u64); + const DIFFERENCE: BSize = BSize::b(8_u64) - BSize::b(5_u64); + const ASSIGNED: BSize = { + let mut size = BSize::b(3_u64); + size += BSize::b(5_u64); + size -= BSize::b(2_u64); + size + }; + + assert_eq!(SUM, BSize::b(8)); + assert_eq!(DIFFERENCE, BSize::b(3)); + assert_eq!(ASSIGNED, BSize::b(6)); + } +} diff --git a/bsize/src/ops.rs b/bsize/src/ops/stable.rs similarity index 63% rename from bsize/src/ops.rs rename to bsize/src/ops/stable.rs index 015f579..7979c7a 100644 --- a/bsize/src/ops.rs +++ b/bsize/src/ops/stable.rs @@ -55,40 +55,3 @@ macro_rules! impl_ops { } impl_ops!(u8, u16, u32, u64, usize); - -#[cfg(test)] -mod tests { - use super::BSize; - - #[test] - fn adds_byte_sizes() { - assert_eq!((BSize::(3) + BSize(5)).0, 8); - assert_eq!((BSize::(3) + BSize(5)).0, 8); - assert_eq!((BSize::(3) + BSize(5)).0, 8); - assert_eq!((BSize::(3) + BSize(5)).0, 8); - assert_eq!((BSize::(3) + BSize(5)).0, 8); - } - - #[test] - fn add_assigns_byte_sizes() { - let mut size = BSize::(3); - size += BSize(5); - assert_eq!(size.0, 8); - } - - #[test] - fn subtracts_byte_sizes() { - assert_eq!((BSize::(8) - BSize(5)).0, 3); - assert_eq!((BSize::(8) - BSize(5)).0, 3); - assert_eq!((BSize::(8) - BSize(5)).0, 3); - assert_eq!((BSize::(8) - BSize(5)).0, 3); - assert_eq!((BSize::(8) - BSize(5)).0, 3); - } - - #[test] - fn sub_assigns_byte_sizes() { - let mut size = BSize::(8); - size -= BSize(5); - assert_eq!(size.0, 3); - } -} diff --git a/bsize/src/traits/mod.rs b/bsize/src/traits/mod.rs index 57b7a5c..c76936c 100644 --- a/bsize/src/traits/mod.rs +++ b/bsize/src/traits/mod.rs @@ -12,232 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -macro_rules! impl_marker { - ($t:ident for $($ty:ty),* $(,)?) => ($( - impl $t for $ty {} - )*) -} - mod private { pub trait Sealed {} - impl_marker!(Sealed for u8, u16, u32, u64, usize); -} - -macro_rules! define_byte_size_trait { - (trait_prefix = [$($trait_prefix:tt)*];) => { - /// A marker trait for all supported byte size underlying types. - /// - /// The conversion to `f64` is approximate when the byte count cannot be - /// represented exactly as `f64`. - pub $($trait_prefix)* trait ByteSize: Copy + private::Sealed { - /// Returns the value as an approximate `f64`. - fn as_f64(&self) -> f64; - } - }; -} - -#[cfg(not(feature = "nightly"))] -define_byte_size_trait! { - trait_prefix = []; -} - -#[cfg(feature = "nightly")] -define_byte_size_trait! { - trait_prefix = [const]; -} - -macro_rules! impl_byte_size { - (impl_prefix = []; $($ty:ty),* $(,)?) => { - $( - impl ByteSize for $ty { - fn as_f64(&self) -> f64 { - *self as f64 - } - } - )* - }; - (impl_prefix = [const]; $($ty:ty),* $(,)?) => { - $( - const impl ByteSize for $ty { - fn as_f64(&self) -> f64 { - *self as f64 - } - } - )* - }; -} - -#[cfg(not(feature = "nightly"))] -impl_byte_size!(impl_prefix = []; u8, u16, u32, u64, usize); - -#[cfg(feature = "nightly")] -impl_byte_size!(impl_prefix = [const]; u8, u16, u32, u64, usize); - -macro_rules! define_unit_traits { - ( - trait_prefix = [$($trait_prefix:tt)*]; - const_bound = [$($const_bound:tt)*]; - ) => { - /// A trait for byte size payload types that can represent kilobyte-scale units. - pub $($trait_prefix)* trait KiloByteSize: ByteSize + $($const_bound)* Mul + Sized { - /// Number of bytes in one kilobyte. - const KB: Self; - - /// Number of bytes in one kibibyte. - const KIB: Self; - } - - /// A trait for byte size payload types that can represent megabyte-scale units. - pub $($trait_prefix)* trait MegaByteSize: $($const_bound)* KiloByteSize { - /// Number of bytes in one megabyte. - const MB: Self; - - /// Number of bytes in one mebibyte. - const MIB: Self; - } - - /// A trait for byte size payload types that can represent gigabyte-scale units. - pub $($trait_prefix)* trait GigaByteSize: $($const_bound)* MegaByteSize { - /// Number of bytes in one gigabyte. - const GB: Self; - - /// Number of bytes in one gibibyte. - const GIB: Self; - } - - /// A trait for byte size payload types that can represent terabyte-scale units. - pub $($trait_prefix)* trait TeraByteSize: $($const_bound)* GigaByteSize { - /// Number of bytes in one terabyte. - const TB: Self; - - /// Number of bytes in one tebibyte. - const TIB: Self; - } - - /// A trait for byte size payload types that can represent petabyte-scale units. - pub $($trait_prefix)* trait PetaByteSize: $($const_bound)* TeraByteSize { - /// Number of bytes in one petabyte. - const PB: Self; - - /// Number of bytes in one pebibyte. - const PIB: Self; - } - - /// A trait for byte size payload types that can represent exabyte-scale units. - pub $($trait_prefix)* trait ExaByteSize: $($const_bound)* PetaByteSize { - /// Number of bytes in one exabyte. - const EB: Self; - - /// Number of bytes in one exbibyte. - const EIB: Self; - } - }; -} - -macro_rules! impl_size_trait { - ([$($impl_prefix:tt)*] $trait:ident for $ty:ty { $($name:ident = $value:literal),* $(,)? }) => { - $($impl_prefix)* impl $trait for $ty { - $(const $name: Self = $value;)* - } - }; -} - -macro_rules! impl_usize_size_traits { - ([$($impl_prefix:tt)*] through_kilo) => { - impl_size_trait!([$($impl_prefix)*] KiloByteSize for usize { - KB = 1_000, - KIB = 1_024, - }); - }; - ([$($impl_prefix:tt)*] through_giga) => { - impl_usize_size_traits!([$($impl_prefix)*] through_kilo); - impl_size_trait!([$($impl_prefix)*] MegaByteSize for usize { - MB = 1_000_000, - MIB = 1_048_576, - }); - impl_size_trait!([$($impl_prefix)*] GigaByteSize for usize { - GB = 1_000_000_000, - GIB = 1_073_741_824, - }); - }; - ([$($impl_prefix:tt)*] through_exa) => { - impl_usize_size_traits!([$($impl_prefix)*] through_giga); - impl_size_trait!([$($impl_prefix)*] TeraByteSize for usize { - TB = 1_000_000_000_000, - TIB = 1_099_511_627_776, - }); - impl_size_trait!([$($impl_prefix)*] PetaByteSize for usize { - PB = 1_000_000_000_000_000, - PIB = 1_125_899_906_842_624, - }); - impl_size_trait!([$($impl_prefix)*] ExaByteSize for usize { - EB = 1_000_000_000_000_000_000, - EIB = 1_152_921_504_606_846_976, - }); - }; -} - -macro_rules! impl_unit_traits { - (impl_prefix = [$($impl_prefix:tt)*];) => { - impl_size_trait!([$($impl_prefix)*] KiloByteSize for u16 { - KB = 1_000, - KIB = 1_024, - }); - - impl_size_trait!([$($impl_prefix)*] KiloByteSize for u32 { - KB = 1_000, - KIB = 1_024, - }); - - impl_size_trait!([$($impl_prefix)*] MegaByteSize for u32 { - MB = 1_000_000, - MIB = 1_048_576, - }); - - impl_size_trait!([$($impl_prefix)*] GigaByteSize for u32 { - GB = 1_000_000_000, - GIB = 1_073_741_824, - }); - - impl_size_trait!([$($impl_prefix)*] KiloByteSize for u64 { - KB = 1_000, - KIB = 1_024, - }); - - impl_size_trait!([$($impl_prefix)*] MegaByteSize for u64 { - MB = 1_000_000, - MIB = 1_048_576, - }); - - impl_size_trait!([$($impl_prefix)*] GigaByteSize for u64 { - GB = 1_000_000_000, - GIB = 1_073_741_824, - }); - - impl_size_trait!([$($impl_prefix)*] TeraByteSize for u64 { - TB = 1_000_000_000_000, - TIB = 1_099_511_627_776, - }); - - impl_size_trait!([$($impl_prefix)*] PetaByteSize for u64 { - PB = 1_000_000_000_000_000, - PIB = 1_125_899_906_842_624, - }); - - impl_size_trait!([$($impl_prefix)*] ExaByteSize for u64 { - EB = 1_000_000_000_000_000_000, - EIB = 1_152_921_504_606_846_976, - }); - - #[cfg(target_pointer_width = "16")] - impl_usize_size_traits!([$($impl_prefix)*] through_kilo); - - #[cfg(target_pointer_width = "32")] - impl_usize_size_traits!([$($impl_prefix)*] through_giga); - - #[cfg(target_pointer_width = "64")] - impl_usize_size_traits!([$($impl_prefix)*] through_exa); - }; + impl Sealed for u8 {} + impl Sealed for u16 {} + impl Sealed for u32 {} + impl Sealed for u64 {} + impl Sealed for usize {} } #[cfg(feature = "nightly")] diff --git a/bsize/src/traits/nightly.rs b/bsize/src/traits/nightly.rs index 9e9c41a..190a19a 100644 --- a/bsize/src/traits/nightly.rs +++ b/bsize/src/traits/nightly.rs @@ -14,13 +14,181 @@ use core::ops::Mul; -use super::ByteSize; +use super::private; -define_unit_traits! { - trait_prefix = [const]; - const_bound = [[const]]; +/// A marker trait for all supported byte size underlying types. +/// +/// The conversion to `f64` is approximate when the byte count cannot be +/// represented exactly as `f64`. +pub const trait ByteSize: Copy + private::Sealed { + /// Returns the value as an approximate `f64`. + fn as_f64(&self) -> f64; } -impl_unit_traits! { - impl_prefix = [const]; +macro_rules! impl_byte_size { + ($($ty:ty),* $(,)?) => { + $( + const impl ByteSize for $ty { + fn as_f64(&self) -> f64 { + *self as f64 + } + } + )* + }; } + +impl_byte_size!(u8, u16, u32, u64, usize); + +/// A trait for byte size payload types that can represent kilobyte-scale units. +pub const trait KiloByteSize: ByteSize + [const] Mul + Sized { + /// Number of bytes in one kilobyte. + const KB: Self; + + /// Number of bytes in one kibibyte. + const KIB: Self; +} + +/// A trait for byte size payload types that can represent megabyte-scale units. +pub const trait MegaByteSize: [const] KiloByteSize { + /// Number of bytes in one megabyte. + const MB: Self; + + /// Number of bytes in one mebibyte. + const MIB: Self; +} + +/// A trait for byte size payload types that can represent gigabyte-scale units. +pub const trait GigaByteSize: [const] MegaByteSize { + /// Number of bytes in one gigabyte. + const GB: Self; + + /// Number of bytes in one gibibyte. + const GIB: Self; +} + +/// A trait for byte size payload types that can represent terabyte-scale units. +pub const trait TeraByteSize: [const] GigaByteSize { + /// Number of bytes in one terabyte. + const TB: Self; + + /// Number of bytes in one tebibyte. + const TIB: Self; +} + +/// A trait for byte size payload types that can represent petabyte-scale units. +pub const trait PetaByteSize: [const] TeraByteSize { + /// Number of bytes in one petabyte. + const PB: Self; + + /// Number of bytes in one pebibyte. + const PIB: Self; +} + +/// A trait for byte size payload types that can represent exabyte-scale units. +pub const trait ExaByteSize: [const] PetaByteSize { + /// Number of bytes in one exabyte. + const EB: Self; + + /// Number of bytes in one exbibyte. + const EIB: Self; +} + +macro_rules! impl_size_trait { + ($trait:ident for $ty:ty { $($name:ident = $value:literal),* $(,)? }) => { + const impl $trait for $ty { + $(const $name: Self = $value;)* + } + }; +} + +macro_rules! impl_usize_size_traits { + (through_kilo) => { + impl_size_trait!(KiloByteSize for usize { + KB = 1_000, + KIB = 1_024, + }); + }; + (through_giga) => { + impl_usize_size_traits!(through_kilo); + impl_size_trait!(MegaByteSize for usize { + MB = 1_000_000, + MIB = 1_048_576, + }); + impl_size_trait!(GigaByteSize for usize { + GB = 1_000_000_000, + GIB = 1_073_741_824, + }); + }; + (through_exa) => { + impl_usize_size_traits!(through_giga); + impl_size_trait!(TeraByteSize for usize { + TB = 1_000_000_000_000, + TIB = 1_099_511_627_776, + }); + impl_size_trait!(PetaByteSize for usize { + PB = 1_000_000_000_000_000, + PIB = 1_125_899_906_842_624, + }); + impl_size_trait!(ExaByteSize for usize { + EB = 1_000_000_000_000_000_000, + EIB = 1_152_921_504_606_846_976, + }); + }; +} + +impl_size_trait!(KiloByteSize for u16 { + KB = 1_000, + KIB = 1_024, +}); + +impl_size_trait!(KiloByteSize for u32 { + KB = 1_000, + KIB = 1_024, +}); + +impl_size_trait!(MegaByteSize for u32 { + MB = 1_000_000, + MIB = 1_048_576, +}); + +impl_size_trait!(GigaByteSize for u32 { + GB = 1_000_000_000, + GIB = 1_073_741_824, +}); + +impl_size_trait!(KiloByteSize for u64 { + KB = 1_000, + KIB = 1_024, +}); + +impl_size_trait!(MegaByteSize for u64 { + MB = 1_000_000, + MIB = 1_048_576, +}); + +impl_size_trait!(GigaByteSize for u64 { + GB = 1_000_000_000, + GIB = 1_073_741_824, +}); + +impl_size_trait!(TeraByteSize for u64 { + TB = 1_000_000_000_000, + TIB = 1_099_511_627_776, +}); + +impl_size_trait!(PetaByteSize for u64 { + PB = 1_000_000_000_000_000, + PIB = 1_125_899_906_842_624, +}); + +impl_size_trait!(ExaByteSize for u64 { + EB = 1_000_000_000_000_000_000, + EIB = 1_152_921_504_606_846_976, +}); + +#[cfg(target_pointer_width = "16")] +impl_usize_size_traits!(through_kilo); +#[cfg(target_pointer_width = "32")] +impl_usize_size_traits!(through_giga); +#[cfg(target_pointer_width = "64")] +impl_usize_size_traits!(through_exa); diff --git a/bsize/src/traits/stable.rs b/bsize/src/traits/stable.rs index 8181837..95646c2 100644 --- a/bsize/src/traits/stable.rs +++ b/bsize/src/traits/stable.rs @@ -14,13 +14,181 @@ use core::ops::Mul; -use super::ByteSize; +use super::private; -define_unit_traits! { - trait_prefix = []; - const_bound = []; +/// A marker trait for all supported byte size underlying types. +/// +/// The conversion to `f64` is approximate when the byte count cannot be +/// represented exactly as `f64`. +pub trait ByteSize: Copy + private::Sealed { + /// Returns the value as an approximate `f64`. + fn as_f64(&self) -> f64; } -impl_unit_traits! { - impl_prefix = []; +macro_rules! impl_byte_size { + ($($ty:ty),* $(,)?) => { + $( + impl ByteSize for $ty { + fn as_f64(&self) -> f64 { + *self as f64 + } + } + )* + }; } + +impl_byte_size!(u8, u16, u32, u64, usize); + +/// A trait for byte size payload types that can represent kilobyte-scale units. +pub trait KiloByteSize: ByteSize + Mul + Sized { + /// Number of bytes in one kilobyte. + const KB: Self; + + /// Number of bytes in one kibibyte. + const KIB: Self; +} + +/// A trait for byte size payload types that can represent megabyte-scale units. +pub trait MegaByteSize: KiloByteSize { + /// Number of bytes in one megabyte. + const MB: Self; + + /// Number of bytes in one mebibyte. + const MIB: Self; +} + +/// A trait for byte size payload types that can represent gigabyte-scale units. +pub trait GigaByteSize: MegaByteSize { + /// Number of bytes in one gigabyte. + const GB: Self; + + /// Number of bytes in one gibibyte. + const GIB: Self; +} + +/// A trait for byte size payload types that can represent terabyte-scale units. +pub trait TeraByteSize: GigaByteSize { + /// Number of bytes in one terabyte. + const TB: Self; + + /// Number of bytes in one tebibyte. + const TIB: Self; +} + +/// A trait for byte size payload types that can represent petabyte-scale units. +pub trait PetaByteSize: TeraByteSize { + /// Number of bytes in one petabyte. + const PB: Self; + + /// Number of bytes in one pebibyte. + const PIB: Self; +} + +/// A trait for byte size payload types that can represent exabyte-scale units. +pub trait ExaByteSize: PetaByteSize { + /// Number of bytes in one exabyte. + const EB: Self; + + /// Number of bytes in one exbibyte. + const EIB: Self; +} + +macro_rules! impl_size_trait { + ($trait:ident for $ty:ty { $($name:ident = $value:literal),* $(,)? }) => { + impl $trait for $ty { + $(const $name: Self = $value;)* + } + }; +} + +macro_rules! impl_usize_size_traits { + (through_kilo) => { + impl_size_trait!(KiloByteSize for usize { + KB = 1_000, + KIB = 1_024, + }); + }; + (through_giga) => { + impl_usize_size_traits!(through_kilo); + impl_size_trait!(MegaByteSize for usize { + MB = 1_000_000, + MIB = 1_048_576, + }); + impl_size_trait!(GigaByteSize for usize { + GB = 1_000_000_000, + GIB = 1_073_741_824, + }); + }; + (through_exa) => { + impl_usize_size_traits!(through_giga); + impl_size_trait!(TeraByteSize for usize { + TB = 1_000_000_000_000, + TIB = 1_099_511_627_776, + }); + impl_size_trait!(PetaByteSize for usize { + PB = 1_000_000_000_000_000, + PIB = 1_125_899_906_842_624, + }); + impl_size_trait!(ExaByteSize for usize { + EB = 1_000_000_000_000_000_000, + EIB = 1_152_921_504_606_846_976, + }); + }; +} + +impl_size_trait!(KiloByteSize for u16 { + KB = 1_000, + KIB = 1_024, +}); + +impl_size_trait!(KiloByteSize for u32 { + KB = 1_000, + KIB = 1_024, +}); + +impl_size_trait!(MegaByteSize for u32 { + MB = 1_000_000, + MIB = 1_048_576, +}); + +impl_size_trait!(GigaByteSize for u32 { + GB = 1_000_000_000, + GIB = 1_073_741_824, +}); + +impl_size_trait!(KiloByteSize for u64 { + KB = 1_000, + KIB = 1_024, +}); + +impl_size_trait!(MegaByteSize for u64 { + MB = 1_000_000, + MIB = 1_048_576, +}); + +impl_size_trait!(GigaByteSize for u64 { + GB = 1_000_000_000, + GIB = 1_073_741_824, +}); + +impl_size_trait!(TeraByteSize for u64 { + TB = 1_000_000_000_000, + TIB = 1_099_511_627_776, +}); + +impl_size_trait!(PetaByteSize for u64 { + PB = 1_000_000_000_000_000, + PIB = 1_125_899_906_842_624, +}); + +impl_size_trait!(ExaByteSize for u64 { + EB = 1_000_000_000_000_000_000, + EIB = 1_152_921_504_606_846_976, +}); + +#[cfg(target_pointer_width = "16")] +impl_usize_size_traits!(through_kilo); +#[cfg(target_pointer_width = "32")] +impl_usize_size_traits!(through_giga); +#[cfg(target_pointer_width = "64")] +impl_usize_size_traits!(through_exa); diff --git a/bsize/src/types/mod.rs b/bsize/src/types/mod.rs index 9b28462..736401b 100644 --- a/bsize/src/types/mod.rs +++ b/bsize/src/types/mod.rs @@ -56,114 +56,6 @@ impl BSize { } } -#[cfg(not(feature = "nightly"))] -macro_rules! impl_byte_accessors { - ($($ty:ty),* $(,)?) => { - $( - impl BSize<$ty> { - /// Returns byte count as bytes. - /// - /// The result is approximate when the byte count cannot be - /// represented exactly as `f64`. Use `.0` or [`BSize::with`] - /// for the exact underlying integer value. - #[inline(always)] - pub const fn as_b(&self) -> f64 { - self.0 as f64 - } - } - )* - }; -} - -#[cfg(not(feature = "nightly"))] -macro_rules! impl_accessors { - ($ty:ty => { $($name:ident = $trait:ident::$size:ident => $unit:literal),* $(,)? }) => { - impl BSize<$ty> { - $( - #[doc = concat!("Returns byte count as ", $unit, ".")] - /// - /// The result is approximate when the byte count cannot be - /// represented exactly as `f64`. - #[inline(always)] - pub const fn $name(&self) -> f64 { - (self.0 as f64) / (<$ty as $crate::traits::$trait>::$size as f64) - } - )* - } - }; -} - -#[cfg(not(feature = "nightly"))] -macro_rules! impl_usize_accessors { - (through_kilo) => { - impl_accessors!(usize => { - as_kb = KiloByteSize::KB => "kilobytes", - as_kib = KiloByteSize::KIB => "kibibytes", - }); - }; - (through_giga) => { - impl_usize_accessors!(through_kilo); - impl_accessors!(usize => { - as_mb = MegaByteSize::MB => "megabytes", - as_mib = MegaByteSize::MIB => "mebibytes", - as_gb = GigaByteSize::GB => "gigabytes", - as_gib = GigaByteSize::GIB => "gibibytes", - }); - }; - (through_exa) => { - impl_usize_accessors!(through_giga); - impl_accessors!(usize => { - as_tb = TeraByteSize::TB => "terabytes", - as_tib = TeraByteSize::TIB => "tebibytes", - as_pb = PetaByteSize::PB => "petabytes", - as_pib = PetaByteSize::PIB => "pebibytes", - as_eb = ExaByteSize::EB => "exabytes", - as_eib = ExaByteSize::EIB => "exbibytes", - }); - }; -} - -#[cfg(not(feature = "nightly"))] -macro_rules! impl_unit_accessors { - () => { - impl_accessors!(u16 => { - as_kb = KiloByteSize::KB => "kilobytes", - as_kib = KiloByteSize::KIB => "kibibytes", - }); - - impl_accessors!(u32 => { - as_kb = KiloByteSize::KB => "kilobytes", - as_kib = KiloByteSize::KIB => "kibibytes", - as_mb = MegaByteSize::MB => "megabytes", - as_mib = MegaByteSize::MIB => "mebibytes", - as_gb = GigaByteSize::GB => "gigabytes", - as_gib = GigaByteSize::GIB => "gibibytes", - }); - - impl_accessors!(u64 => { - as_kb = KiloByteSize::KB => "kilobytes", - as_kib = KiloByteSize::KIB => "kibibytes", - as_mb = MegaByteSize::MB => "megabytes", - as_mib = MegaByteSize::MIB => "mebibytes", - as_gb = GigaByteSize::GB => "gigabytes", - as_gib = GigaByteSize::GIB => "gibibytes", - as_tb = TeraByteSize::TB => "terabytes", - as_tib = TeraByteSize::TIB => "tebibytes", - as_pb = PetaByteSize::PB => "petabytes", - as_pib = PetaByteSize::PIB => "pebibytes", - as_eb = ExaByteSize::EB => "exabytes", - as_eib = ExaByteSize::EIB => "exbibytes", - }); - - #[cfg(target_pointer_width = "16")] - impl_usize_accessors!(through_kilo); - #[cfg(target_pointer_width = "32")] - impl_usize_accessors!(through_giga); - #[cfg(target_pointer_width = "64")] - impl_usize_accessors!(through_exa); - }; -} - #[cfg(feature = "nightly")] mod nightly; #[cfg(not(feature = "nightly"))] @@ -172,16 +64,7 @@ mod stable; #[cfg(test)] mod tests { use super::BSize; - - fn assert_close(actual: f64, expected: f64) { - let delta = (actual - expected).abs(); - let tolerance = f64::EPSILON; - - assert!( - delta <= tolerance, - "actual: {actual}, expected: {expected}, delta: {delta}, tolerance: {tolerance}", - ); - } + use crate::assert_close; #[test] fn defaults() { @@ -280,6 +163,45 @@ mod tests { assert_close(BSize::::eib(2).as_eib(), 2.0); } + #[cfg(target_pointer_width = "16")] + #[test] + fn returns_usize_units() { + assert_eq!(BSize::::kb(2).0, 2_000); + assert_eq!(BSize::::kib(2).0, 2_048); + assert_close(BSize::::kb(2).as_kb(), 2.0); + assert_close(BSize::::kib(2).as_kib(), 2.0); + } + + #[cfg(target_pointer_width = "32")] + #[test] + fn returns_usize_units() { + assert_eq!(BSize::::kb(2).0, 2_000); + assert_eq!(BSize::::kib(2).0, 2_048); + assert_close(BSize::::kb(2).as_kb(), 2.0); + assert_close(BSize::::kib(2).as_kib(), 2.0); + assert_eq!(BSize::::gb(2).0, 2_000_000_000); + assert_eq!(BSize::::gib(2).0, 2_147_483_648); + assert_close(BSize::::gb(2).as_gb(), 2.0); + assert_close(BSize::::gib(2).as_gib(), 2.0); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn returns_usize_units() { + assert_eq!(BSize::::kb(2).0, 2_000); + assert_eq!(BSize::::kib(2).0, 2_048); + assert_close(BSize::::kb(2).as_kb(), 2.0); + assert_close(BSize::::kib(2).as_kib(), 2.0); + assert_eq!(BSize::::gb(2).0, 2_000_000_000); + assert_eq!(BSize::::gib(2).0, 2_147_483_648); + assert_close(BSize::::gb(2).as_gb(), 2.0); + assert_close(BSize::::gib(2).as_gib(), 2.0); + assert_eq!(BSize::::eb(2).0, 2_000_000_000_000_000_000); + assert_eq!(BSize::::eib(2).0, 2_305_843_009_213_693_952); + assert_close(BSize::::eb(2).as_eb(), 2.0); + assert_close(BSize::::eib(2).as_eib(), 2.0); + } + #[test] fn returns_large_fractional_u64_units() { let bytes = 9_876_543_210_987_654_321_u64; diff --git a/bsize/src/types/nightly.rs b/bsize/src/types/nightly.rs index 9382e4b..33e9699 100644 --- a/bsize/src/types/nightly.rs +++ b/bsize/src/types/nightly.rs @@ -21,135 +21,289 @@ use crate::traits::MegaByteSize; use crate::traits::PetaByteSize; use crate::traits::TeraByteSize; -macro_rules! impl_constructors { - ($trait:ident => { $($name:ident = $size:ident),* $(,)? }) => { - impl BSize { - $( - #[doc = concat!( - "Constructs a byte size wrapper from a quantity of `", - stringify!($name), - "` units." - )] - #[inline(always)] - pub const fn $name(size: T) -> Self - where - T: [const] $trait, - { - BSize(size * T::$size) - } - )* - } - }; +impl BSize { + /// Returns byte count as bytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. Use `.0` or [`BSize::with`] for the exact underlying + /// integer value. + #[inline(always)] + pub const fn as_b(&self) -> f64 + where + T: [const] ByteSize, + { + self.0.as_f64() + } } -impl_constructors!(KiloByteSize => { - kb = KB, - kib = KIB, -}); +impl BSize { + /// Constructs a byte size wrapper from a quantity of `kb` units. + #[inline(always)] + pub const fn kb(size: T) -> Self + where + T: [const] KiloByteSize, + { + BSize(size * T::KB) + } -impl_constructors!(MegaByteSize => { - mb = MB, - mib = MIB, -}); + /// Constructs a byte size wrapper from a quantity of `kib` units. + #[inline(always)] + pub const fn kib(size: T) -> Self + where + T: [const] KiloByteSize, + { + BSize(size * T::KIB) + } -impl_constructors!(GigaByteSize => { - gb = GB, - gib = GIB, -}); + /// Returns byte count as kilobytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_kb(&self) -> f64 + where + T: [const] KiloByteSize + [const] ByteSize, + { + self.0.as_f64() / T::KB.as_f64() + } -impl_constructors!(TeraByteSize => { - tb = TB, - tib = TIB, -}); + /// Returns byte count as kibibytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_kib(&self) -> f64 + where + T: [const] KiloByteSize + [const] ByteSize, + { + self.0.as_f64() / T::KIB.as_f64() + } +} -impl_constructors!(PetaByteSize => { - pb = PB, - pib = PIB, -}); +impl BSize { + /// Constructs a byte size wrapper from a quantity of `mb` units. + #[inline(always)] + pub const fn mb(size: T) -> Self + where + T: [const] MegaByteSize, + { + BSize(size * T::MB) + } -impl_constructors!(ExaByteSize => { - eb = EB, - eib = EIB, -}); + /// Constructs a byte size wrapper from a quantity of `mib` units. + #[inline(always)] + pub const fn mib(size: T) -> Self + where + T: [const] MegaByteSize, + { + BSize(size * T::MIB) + } -impl BSize { - /// Returns byte count as bytes. + /// Returns byte count as megabytes. /// /// The result is approximate when the byte count cannot be represented - /// exactly as `f64`. Use `.0` or [`BSize::with`] for the exact underlying - /// integer value. + /// exactly as `f64`. #[inline(always)] - pub const fn as_b(&self) -> f64 + pub const fn as_mb(&self) -> f64 where - T: [const] ByteSize, + T: [const] MegaByteSize + [const] ByteSize, { - self.0.as_f64() + self.0.as_f64() / T::MB.as_f64() + } + + /// Returns byte count as mebibytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_mib(&self) -> f64 + where + T: [const] MegaByteSize + [const] ByteSize, + { + self.0.as_f64() / T::MIB.as_f64() + } +} + +impl BSize { + /// Constructs a byte size wrapper from a quantity of `gb` units. + #[inline(always)] + pub const fn gb(size: T) -> Self + where + T: [const] GigaByteSize, + { + BSize(size * T::GB) + } + + /// Constructs a byte size wrapper from a quantity of `gib` units. + #[inline(always)] + pub const fn gib(size: T) -> Self + where + T: [const] GigaByteSize, + { + BSize(size * T::GIB) + } + + /// Returns byte count as gigabytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_gb(&self) -> f64 + where + T: [const] GigaByteSize + [const] ByteSize, + { + self.0.as_f64() / T::GB.as_f64() + } + + /// Returns byte count as gibibytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_gib(&self) -> f64 + where + T: [const] GigaByteSize + [const] ByteSize, + { + self.0.as_f64() / T::GIB.as_f64() } } -macro_rules! impl_accessors { - ($trait:ident => { $($name:ident = $size:ident => $unit:literal),* $(,)? }) => { - impl BSize { - $( - #[doc = concat!("Returns byte count as ", $unit, ".")] - /// - /// The result is approximate when the byte count cannot be - /// represented exactly as `f64`. - #[inline(always)] - pub const fn $name(&self) -> f64 - where - T: [const] $trait + [const] ByteSize, - { - self.0.as_f64() / T::$size.as_f64() - } - )* - } - }; +impl BSize { + /// Constructs a byte size wrapper from a quantity of `tb` units. + #[inline(always)] + pub const fn tb(size: T) -> Self + where + T: [const] TeraByteSize, + { + BSize(size * T::TB) + } + + /// Constructs a byte size wrapper from a quantity of `tib` units. + #[inline(always)] + pub const fn tib(size: T) -> Self + where + T: [const] TeraByteSize, + { + BSize(size * T::TIB) + } + + /// Returns byte count as terabytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_tb(&self) -> f64 + where + T: [const] TeraByteSize + [const] ByteSize, + { + self.0.as_f64() / T::TB.as_f64() + } + + /// Returns byte count as tebibytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_tib(&self) -> f64 + where + T: [const] TeraByteSize + [const] ByteSize, + { + self.0.as_f64() / T::TIB.as_f64() + } } -impl_accessors!(KiloByteSize => { - as_kb = KB => "kilobytes", - as_kib = KIB => "kibibytes", -}); +impl BSize { + /// Constructs a byte size wrapper from a quantity of `pb` units. + #[inline(always)] + pub const fn pb(size: T) -> Self + where + T: [const] PetaByteSize, + { + BSize(size * T::PB) + } -impl_accessors!(MegaByteSize => { - as_mb = MB => "megabytes", - as_mib = MIB => "mebibytes", -}); + /// Constructs a byte size wrapper from a quantity of `pib` units. + #[inline(always)] + pub const fn pib(size: T) -> Self + where + T: [const] PetaByteSize, + { + BSize(size * T::PIB) + } -impl_accessors!(GigaByteSize => { - as_gb = GB => "gigabytes", - as_gib = GIB => "gibibytes", -}); + /// Returns byte count as petabytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_pb(&self) -> f64 + where + T: [const] PetaByteSize + [const] ByteSize, + { + self.0.as_f64() / T::PB.as_f64() + } -impl_accessors!(TeraByteSize => { - as_tb = TB => "terabytes", - as_tib = TIB => "tebibytes", -}); + /// Returns byte count as pebibytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_pib(&self) -> f64 + where + T: [const] PetaByteSize + [const] ByteSize, + { + self.0.as_f64() / T::PIB.as_f64() + } +} -impl_accessors!(PetaByteSize => { - as_pb = PB => "petabytes", - as_pib = PIB => "pebibytes", -}); +impl BSize { + /// Constructs a byte size wrapper from a quantity of `eb` units. + #[inline(always)] + pub const fn eb(size: T) -> Self + where + T: [const] ExaByteSize, + { + BSize(size * T::EB) + } -impl_accessors!(ExaByteSize => { - as_eb = EB => "exabytes", - as_eib = EIB => "exbibytes", -}); + /// Constructs a byte size wrapper from a quantity of `eib` units. + #[inline(always)] + pub const fn eib(size: T) -> Self + where + T: [const] ExaByteSize, + { + BSize(size * T::EIB) + } + + /// Returns byte count as exabytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_eb(&self) -> f64 + where + T: [const] ExaByteSize + [const] ByteSize, + { + self.0.as_f64() / T::EB.as_f64() + } + + /// Returns byte count as exbibytes. + /// + /// The result is approximate when the byte count cannot be represented + /// exactly as `f64`. + #[inline(always)] + pub const fn as_eib(&self) -> f64 + where + T: [const] ExaByteSize + [const] ByteSize, + { + self.0.as_f64() / T::EIB.as_f64() + } +} #[cfg(test)] mod tests { use super::BSize; - - fn assert_close(actual: f64, expected: f64) { - let delta = (actual - expected).abs(); - let tolerance = f64::EPSILON; - - assert!( - delta <= tolerance, - "actual: {actual}, expected: {expected}, delta: {delta}, tolerance: {tolerance}", - ); - } + use crate::assert_close; #[test] fn infers_constructor_type_from_argument() { @@ -172,43 +326,4 @@ mod tests { assert_close(BYTES, 16.0); assert_close(KIB, 16.0); } - - #[cfg(target_pointer_width = "16")] - #[test] - fn returns_usize_units() { - assert_eq!(BSize::kb(2_usize).0, 2_000); - assert_eq!(BSize::kib(2_usize).0, 2_048); - assert_close(BSize::kb(2_usize).as_kb(), 2.0); - assert_close(BSize::kib(2_usize).as_kib(), 2.0); - } - - #[cfg(target_pointer_width = "32")] - #[test] - fn returns_usize_units() { - assert_eq!(BSize::kb(2_usize).0, 2_000); - assert_eq!(BSize::kib(2_usize).0, 2_048); - assert_close(BSize::kb(2_usize).as_kb(), 2.0); - assert_close(BSize::kib(2_usize).as_kib(), 2.0); - assert_eq!(BSize::gb(2_usize).0, 2_000_000_000); - assert_eq!(BSize::gib(2_usize).0, 2_147_483_648); - assert_close(BSize::gb(2_usize).as_gb(), 2.0); - assert_close(BSize::gib(2_usize).as_gib(), 2.0); - } - - #[cfg(target_pointer_width = "64")] - #[test] - fn returns_usize_units() { - assert_eq!(BSize::kb(2_usize).0, 2_000); - assert_eq!(BSize::kib(2_usize).0, 2_048); - assert_close(BSize::kb(2_usize).as_kb(), 2.0); - assert_close(BSize::kib(2_usize).as_kib(), 2.0); - assert_eq!(BSize::gb(2_usize).0, 2_000_000_000); - assert_eq!(BSize::gib(2_usize).0, 2_147_483_648); - assert_close(BSize::gb(2_usize).as_gb(), 2.0); - assert_close(BSize::gib(2_usize).as_gib(), 2.0); - assert_eq!(BSize::eb(2_usize).0, 2_000_000_000_000_000_000); - assert_eq!(BSize::eib(2_usize).0, 2_305_843_009_213_693_952); - assert_close(BSize::eb(2_usize).as_eb(), 2.0); - assert_close(BSize::eib(2_usize).as_eib(), 2.0); - } } diff --git a/bsize/src/types/stable.rs b/bsize/src/types/stable.rs index 605ad0f..1253732 100644 --- a/bsize/src/types/stable.rs +++ b/bsize/src/types/stable.rs @@ -61,6 +61,110 @@ macro_rules! impl_usize_constructors { }; } +macro_rules! impl_byte_accessors { + ($($ty:ty),* $(,)?) => { + $( + impl BSize<$ty> { + /// Returns byte count as bytes. + /// + /// The result is approximate when the byte count cannot be + /// represented exactly as `f64`. Use `.0` or [`BSize::with`] + /// for the exact underlying integer value. + #[inline(always)] + pub const fn as_b(&self) -> f64 { + self.0 as f64 + } + } + )* + }; +} + +macro_rules! impl_accessors { + ($ty:ty => { $($name:ident = $trait:ident::$size:ident => $unit:literal),* $(,)? }) => { + impl BSize<$ty> { + $( + #[doc = concat!("Returns byte count as ", $unit, ".")] + /// + /// The result is approximate when the byte count cannot be + /// represented exactly as `f64`. + #[inline(always)] + pub const fn $name(&self) -> f64 { + (self.0 as f64) / (<$ty as $crate::traits::$trait>::$size as f64) + } + )* + } + }; +} + +macro_rules! impl_usize_accessors { + (through_kilo) => { + impl_accessors!(usize => { + as_kb = KiloByteSize::KB => "kilobytes", + as_kib = KiloByteSize::KIB => "kibibytes", + }); + }; + (through_giga) => { + impl_usize_accessors!(through_kilo); + impl_accessors!(usize => { + as_mb = MegaByteSize::MB => "megabytes", + as_mib = MegaByteSize::MIB => "mebibytes", + as_gb = GigaByteSize::GB => "gigabytes", + as_gib = GigaByteSize::GIB => "gibibytes", + }); + }; + (through_exa) => { + impl_usize_accessors!(through_giga); + impl_accessors!(usize => { + as_tb = TeraByteSize::TB => "terabytes", + as_tib = TeraByteSize::TIB => "tebibytes", + as_pb = PetaByteSize::PB => "petabytes", + as_pib = PetaByteSize::PIB => "pebibytes", + as_eb = ExaByteSize::EB => "exabytes", + as_eib = ExaByteSize::EIB => "exbibytes", + }); + }; +} + +macro_rules! impl_unit_accessors { + () => { + impl_accessors!(u16 => { + as_kb = KiloByteSize::KB => "kilobytes", + as_kib = KiloByteSize::KIB => "kibibytes", + }); + + impl_accessors!(u32 => { + as_kb = KiloByteSize::KB => "kilobytes", + as_kib = KiloByteSize::KIB => "kibibytes", + as_mb = MegaByteSize::MB => "megabytes", + as_mib = MegaByteSize::MIB => "mebibytes", + as_gb = GigaByteSize::GB => "gigabytes", + as_gib = GigaByteSize::GIB => "gibibytes", + }); + + impl_accessors!(u64 => { + as_kb = KiloByteSize::KB => "kilobytes", + as_kib = KiloByteSize::KIB => "kibibytes", + as_mb = MegaByteSize::MB => "megabytes", + as_mib = MegaByteSize::MIB => "mebibytes", + as_gb = GigaByteSize::GB => "gigabytes", + as_gib = GigaByteSize::GIB => "gibibytes", + as_tb = TeraByteSize::TB => "terabytes", + as_tib = TeraByteSize::TIB => "tebibytes", + as_pb = PetaByteSize::PB => "petabytes", + as_pib = PetaByteSize::PIB => "pebibytes", + as_eb = ExaByteSize::EB => "exabytes", + as_eib = ExaByteSize::EIB => "exbibytes", + }); + + #[cfg(target_pointer_width = "16")] + impl_usize_accessors!(through_kilo); + #[cfg(target_pointer_width = "32")] + impl_usize_accessors!(through_giga); + #[cfg(target_pointer_width = "64")] + impl_usize_accessors!(through_exa); + }; +} + impl_constructors!(u16 => { kb = KiloByteSize::KB, kib = KiloByteSize::KIB, @@ -99,57 +203,3 @@ impl_usize_constructors!(through_exa); impl_byte_accessors!(u8, u16, u32, u64, usize); impl_unit_accessors!(); - -#[cfg(test)] -mod tests { - use super::BSize; - - fn assert_close(actual: f64, expected: f64) { - let delta = (actual - expected).abs(); - let tolerance = f64::EPSILON; - - assert!( - delta <= tolerance, - "actual: {actual}, expected: {expected}, delta: {delta}, tolerance: {tolerance}", - ); - } - - #[cfg(target_pointer_width = "16")] - #[test] - fn returns_usize_units() { - assert_eq!(BSize::::kb(2).0, 2_000); - assert_eq!(BSize::::kib(2).0, 2_048); - assert_close(BSize::::kb(2).as_kb(), 2.0); - assert_close(BSize::::kib(2).as_kib(), 2.0); - } - - #[cfg(target_pointer_width = "32")] - #[test] - fn returns_usize_units() { - assert_eq!(BSize::::kb(2).0, 2_000); - assert_eq!(BSize::::kib(2).0, 2_048); - assert_close(BSize::::kb(2).as_kb(), 2.0); - assert_close(BSize::::kib(2).as_kib(), 2.0); - assert_eq!(BSize::::gb(2).0, 2_000_000_000); - assert_eq!(BSize::::gib(2).0, 2_147_483_648); - assert_close(BSize::::gb(2).as_gb(), 2.0); - assert_close(BSize::::gib(2).as_gib(), 2.0); - } - - #[cfg(target_pointer_width = "64")] - #[test] - fn returns_usize_units() { - assert_eq!(BSize::::kb(2).0, 2_000); - assert_eq!(BSize::::kib(2).0, 2_048); - assert_close(BSize::::kb(2).as_kb(), 2.0); - assert_close(BSize::::kib(2).as_kib(), 2.0); - assert_eq!(BSize::::gb(2).0, 2_000_000_000); - assert_eq!(BSize::::gib(2).0, 2_147_483_648); - assert_close(BSize::::gb(2).as_gb(), 2.0); - assert_close(BSize::::gib(2).as_gib(), 2.0); - assert_eq!(BSize::::eb(2).0, 2_000_000_000_000_000_000); - assert_eq!(BSize::::eib(2).0, 2_305_843_009_213_693_952); - assert_close(BSize::::eb(2).as_eb(), 2.0); - assert_close(BSize::::eib(2).as_eib(), 2.0); - } -} From 31cfa2a2a821656934632c2ac432a65771115c07 Mon Sep 17 00:00:00 2001 From: tison Date: Sun, 28 Jun 2026 10:51:01 +0800 Subject: [PATCH 4/5] human in the loop Signed-off-by: tison --- CHANGELOG.md | 4 +-- bsize/src/display.rs | 4 +-- bsize/src/traits/nightly.rs | 39 +++++++++++++---------------- bsize/src/traits/stable.rs | 39 +++++++++++++---------------- bsize/src/types/nightly.rs | 50 ++++++++++++++++++------------------- 5 files changed, 65 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00149a8..025988f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,12 @@ All significant changes to this software be documented in this file. ### Breaking changes * Renamed `BSize::with` to `BSize::map`. A new `BSize::with` method has been added that accepts a closure returning arbitrary type, allowing for mapping a `BSize` to any other type. This follows `LocalKey::with`'s naming conventions. -* Removed the `Displayable` trait. Use `ByteSize` directly for values accepted by `bsize::display`. +* Removed the `Displayable` trait. `ByteSize` now has a `to_f64` method that is the same as the `Displayable::canonicalize` method. ### New features * Added `nightly` feature for using `BSize` with nightly-only features like `const_ops` and `const_trait_impl`. -* Added `ByteSize::as_f64` for converting supported byte size underlying types to approximate `f64` values. +* Added `ByteSize::to_f64` for converting supported byte size underlying types to approximate `f64` values. * Added `BSize::as_b` for returning the byte count as an approximate `f64`. ## v0.2.1 (2026-06-27) diff --git a/bsize/src/display.rs b/bsize/src/display.rs index 4b9f268..51bb3d6 100644 --- a/bsize/src/display.rs +++ b/bsize/src/display.rs @@ -23,7 +23,7 @@ use crate::ByteSize; /// See [`Display`] for examples. Use [`Display::new`] when the byte count is already represented /// as an `f64`. pub fn display(size: impl ByteSize) -> Display { - Display::new(size.as_f64()) + Display::new(size.to_f64()) } impl BSize { @@ -31,7 +31,7 @@ impl BSize { /// /// See [`Display`] for examples. pub fn display(&self) -> Display { - Display::new(self.0.as_f64()) + Display::new(self.0.to_f64()) } } diff --git a/bsize/src/traits/nightly.rs b/bsize/src/traits/nightly.rs index 190a19a..f8b09c1 100644 --- a/bsize/src/traits/nightly.rs +++ b/bsize/src/traits/nightly.rs @@ -17,30 +17,13 @@ use core::ops::Mul; use super::private; /// A marker trait for all supported byte size underlying types. -/// -/// The conversion to `f64` is approximate when the byte count cannot be -/// represented exactly as `f64`. -pub const trait ByteSize: Copy + private::Sealed { - /// Returns the value as an approximate `f64`. - fn as_f64(&self) -> f64; +pub const trait ByteSize: private::Sealed + Clone + Copy + Sized { + /// Returns the byte size as an approximate `f64`. + fn to_f64(&self) -> f64; } -macro_rules! impl_byte_size { - ($($ty:ty),* $(,)?) => { - $( - const impl ByteSize for $ty { - fn as_f64(&self) -> f64 { - *self as f64 - } - } - )* - }; -} - -impl_byte_size!(u8, u16, u32, u64, usize); - /// A trait for byte size payload types that can represent kilobyte-scale units. -pub const trait KiloByteSize: ByteSize + [const] Mul + Sized { +pub const trait KiloByteSize: [const] ByteSize + [const] Mul { /// Number of bytes in one kilobyte. const KB: Self; @@ -93,6 +76,20 @@ pub const trait ExaByteSize: [const] PetaByteSize { const EIB: Self; } +macro_rules! impl_byte_size { + ($($ty:ty),* $(,)?) => { + $( + const impl ByteSize for $ty { + fn to_f64(&self) -> f64 { + *self as f64 + } + } + )* + }; +} + +impl_byte_size!(u8, u16, u32, u64, usize); + macro_rules! impl_size_trait { ($trait:ident for $ty:ty { $($name:ident = $value:literal),* $(,)? }) => { const impl $trait for $ty { diff --git a/bsize/src/traits/stable.rs b/bsize/src/traits/stable.rs index 95646c2..7011cd9 100644 --- a/bsize/src/traits/stable.rs +++ b/bsize/src/traits/stable.rs @@ -17,30 +17,13 @@ use core::ops::Mul; use super::private; /// A marker trait for all supported byte size underlying types. -/// -/// The conversion to `f64` is approximate when the byte count cannot be -/// represented exactly as `f64`. -pub trait ByteSize: Copy + private::Sealed { - /// Returns the value as an approximate `f64`. - fn as_f64(&self) -> f64; +pub const trait ByteSize: private::Sealed + Clone + Copy + Sized { + /// Returns the byte size as an approximate `f64`. + fn to_f64(&self) -> f64; } -macro_rules! impl_byte_size { - ($($ty:ty),* $(,)?) => { - $( - impl ByteSize for $ty { - fn as_f64(&self) -> f64 { - *self as f64 - } - } - )* - }; -} - -impl_byte_size!(u8, u16, u32, u64, usize); - /// A trait for byte size payload types that can represent kilobyte-scale units. -pub trait KiloByteSize: ByteSize + Mul + Sized { +pub trait KiloByteSize: ByteSize + Mul { /// Number of bytes in one kilobyte. const KB: Self; @@ -93,6 +76,20 @@ pub trait ExaByteSize: PetaByteSize { const EIB: Self; } +macro_rules! impl_byte_size { + ($($ty:ty),* $(,)?) => { + $( + impl ByteSize for $ty { + fn to_f64(&self) -> f64 { + *self as f64 + } + } + )* + }; +} + +impl_byte_size!(u8, u16, u32, u64, usize); + macro_rules! impl_size_trait { ($trait:ident for $ty:ty { $($name:ident = $value:literal),* $(,)? }) => { impl $trait for $ty { diff --git a/bsize/src/types/nightly.rs b/bsize/src/types/nightly.rs index 33e9699..3cf6602 100644 --- a/bsize/src/types/nightly.rs +++ b/bsize/src/types/nightly.rs @@ -32,7 +32,7 @@ impl BSize { where T: [const] ByteSize, { - self.0.as_f64() + self.0.to_f64() } } @@ -62,9 +62,9 @@ impl BSize { #[inline(always)] pub const fn as_kb(&self) -> f64 where - T: [const] KiloByteSize + [const] ByteSize, + T: [const] KiloByteSize, { - self.0.as_f64() / T::KB.as_f64() + self.0.to_f64() / T::KB.to_f64() } /// Returns byte count as kibibytes. @@ -74,9 +74,9 @@ impl BSize { #[inline(always)] pub const fn as_kib(&self) -> f64 where - T: [const] KiloByteSize + [const] ByteSize, + T: [const] KiloByteSize, { - self.0.as_f64() / T::KIB.as_f64() + self.0.to_f64() / T::KIB.to_f64() } } @@ -106,9 +106,9 @@ impl BSize { #[inline(always)] pub const fn as_mb(&self) -> f64 where - T: [const] MegaByteSize + [const] ByteSize, + T: [const] MegaByteSize, { - self.0.as_f64() / T::MB.as_f64() + self.0.to_f64() / T::MB.to_f64() } /// Returns byte count as mebibytes. @@ -118,9 +118,9 @@ impl BSize { #[inline(always)] pub const fn as_mib(&self) -> f64 where - T: [const] MegaByteSize + [const] ByteSize, + T: [const] MegaByteSize, { - self.0.as_f64() / T::MIB.as_f64() + self.0.to_f64() / T::MIB.to_f64() } } @@ -150,9 +150,9 @@ impl BSize { #[inline(always)] pub const fn as_gb(&self) -> f64 where - T: [const] GigaByteSize + [const] ByteSize, + T: [const] GigaByteSize, { - self.0.as_f64() / T::GB.as_f64() + self.0.to_f64() / T::GB.to_f64() } /// Returns byte count as gibibytes. @@ -162,9 +162,9 @@ impl BSize { #[inline(always)] pub const fn as_gib(&self) -> f64 where - T: [const] GigaByteSize + [const] ByteSize, + T: [const] GigaByteSize, { - self.0.as_f64() / T::GIB.as_f64() + self.0.to_f64() / T::GIB.to_f64() } } @@ -194,9 +194,9 @@ impl BSize { #[inline(always)] pub const fn as_tb(&self) -> f64 where - T: [const] TeraByteSize + [const] ByteSize, + T: [const] TeraByteSize, { - self.0.as_f64() / T::TB.as_f64() + self.0.to_f64() / T::TB.to_f64() } /// Returns byte count as tebibytes. @@ -206,9 +206,9 @@ impl BSize { #[inline(always)] pub const fn as_tib(&self) -> f64 where - T: [const] TeraByteSize + [const] ByteSize, + T: [const] TeraByteSize, { - self.0.as_f64() / T::TIB.as_f64() + self.0.to_f64() / T::TIB.to_f64() } } @@ -238,9 +238,9 @@ impl BSize { #[inline(always)] pub const fn as_pb(&self) -> f64 where - T: [const] PetaByteSize + [const] ByteSize, + T: [const] PetaByteSize, { - self.0.as_f64() / T::PB.as_f64() + self.0.to_f64() / T::PB.to_f64() } /// Returns byte count as pebibytes. @@ -250,9 +250,9 @@ impl BSize { #[inline(always)] pub const fn as_pib(&self) -> f64 where - T: [const] PetaByteSize + [const] ByteSize, + T: [const] PetaByteSize, { - self.0.as_f64() / T::PIB.as_f64() + self.0.to_f64() / T::PIB.to_f64() } } @@ -282,9 +282,9 @@ impl BSize { #[inline(always)] pub const fn as_eb(&self) -> f64 where - T: [const] ExaByteSize + [const] ByteSize, + T: [const] ExaByteSize, { - self.0.as_f64() / T::EB.as_f64() + self.0.to_f64() / T::EB.to_f64() } /// Returns byte count as exbibytes. @@ -294,9 +294,9 @@ impl BSize { #[inline(always)] pub const fn as_eib(&self) -> f64 where - T: [const] ExaByteSize + [const] ByteSize, + T: [const] ExaByteSize, { - self.0.as_f64() / T::EIB.as_f64() + self.0.to_f64() / T::EIB.to_f64() } } From 084b33710d6fae3f550067e53257646bc1b36435 Mon Sep 17 00:00:00 2001 From: tison Date: Sun, 28 Jun 2026 11:38:41 +0800 Subject: [PATCH 5/5] macroweave Signed-off-by: tison --- Cargo.lock | 11 ++ README.md | 2 +- bsize/Cargo.toml | 1 + bsize/src/lib.rs | 2 +- bsize/src/ops/stable.rs | 72 ++++---- bsize/src/parse.rs | 36 ++-- bsize/src/traits/nightly.rs | 145 +++++----------- bsize/src/traits/stable.rs | 147 +++++----------- bsize/src/types/stable.rs | 324 ++++++++++++++++-------------------- 9 files changed, 286 insertions(+), 454 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6c1a29..788094f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,6 +72,7 @@ name = "bsize" version = "0.3.0-rc.1" dependencies = [ "insta", + "macroweave", "quickcheck", "serde", "serde_core", @@ -266,6 +267,16 @@ version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +[[package]] +name = "macroweave" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50ab787669b4105aad23153b64c43ba63499e90b3e9df0d5643cda24382d3dfe" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "memchr" version = "2.8.2" diff --git a/README.md b/README.md index 8859546..20c6615 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ This crate provides multiple semantic wrappers and utilities for byte size repre ## Features -* `#![no_std]`-capable, no dependencies, and uses no heap allocation. +* `#![no_std]`-capable, no heap allocation, and no runtime dependencies by default. * `BSize` wrappers over `u8`, `u16`, `u32`, `u64`, and `usize` for representing byte sizes with different underlying types. * `FromStr` impl for `BSize`, allowing for parsing string size representations like "1.5 KiB" and "521 TB". * `Display` impl for `BSize`, allowing for formatting byte sizes as human-readable strings in both binary (e.g., "1.5 MiB") and decimal (e.g., "1.5 MB") styles. diff --git a/bsize/Cargo.toml b/bsize/Cargo.toml index d2ed5d5..cf4ed7e 100644 --- a/bsize/Cargo.toml +++ b/bsize/Cargo.toml @@ -38,6 +38,7 @@ nightly = [] serde = ["dep:serde_core"] [dependencies] +macroweave = "0.1.0" serde_core = { version = "1", default-features = false, optional = true } [dev-dependencies] diff --git a/bsize/src/lib.rs b/bsize/src/lib.rs index 70d3106..b964584 100644 --- a/bsize/src/lib.rs +++ b/bsize/src/lib.rs @@ -20,7 +20,7 @@ //! //! # Features //! -//! * `#![no_std]`-capable, no dependencies, and uses no heap allocation. +//! * `#![no_std]`-capable, no heap allocation, and no runtime dependencies by default. //! * [`BSize`] wrappers over `u8`, `u16`, `u32`, `u64`, and `usize` for representing byte sizes //! with different underlying types. //! * `FromStr` impl for `BSize`, allowing for parsing string size representations like "1.5 KiB" diff --git a/bsize/src/ops/stable.rs b/bsize/src/ops/stable.rs index 7979c7a..49dcecb 100644 --- a/bsize/src/ops/stable.rs +++ b/bsize/src/ops/stable.rs @@ -16,42 +16,36 @@ use core::ops; use crate::types::BSize; -macro_rules! impl_ops { - ($($ty:ty),* $(,)?) => { - $( - impl ops::Add> for BSize<$ty> { - type Output = Self; - - #[inline(always)] - fn add(self, rhs: BSize<$ty>) -> Self::Output { - BSize(self.0 + rhs.0) - } - } - - impl ops::AddAssign> for BSize<$ty> { - #[inline(always)] - fn add_assign(&mut self, rhs: BSize<$ty>) { - self.0 += rhs.0; - } - } - - impl ops::Sub> for BSize<$ty> { - type Output = Self; - - #[inline(always)] - fn sub(self, rhs: BSize<$ty>) -> Self::Output { - BSize(self.0 - rhs.0) - } - } - - impl ops::SubAssign> for BSize<$ty> { - #[inline(always)] - fn sub_assign(&mut self, rhs: BSize<$ty>) { - self.0 -= rhs.0; - } - } - )* - }; -} - -impl_ops!(u8, u16, u32, u64, usize); +macroweave::repeat!(Ty in [u8, u16, u32, u64, usize] { + impl ops::Add> for BSize { + type Output = Self; + + #[inline(always)] + fn add(self, rhs: BSize) -> Self::Output { + BSize(self.0 + rhs.0) + } + } + + impl ops::AddAssign> for BSize { + #[inline(always)] + fn add_assign(&mut self, rhs: BSize) { + self.0 += rhs.0; + } + } + + impl ops::Sub> for BSize { + type Output = Self; + + #[inline(always)] + fn sub(self, rhs: BSize) -> Self::Output { + BSize(self.0 - rhs.0) + } + } + + impl ops::SubAssign> for BSize { + #[inline(always)] + fn sub_assign(&mut self, rhs: BSize) { + self.0 -= rhs.0; + } + } +}); diff --git a/bsize/src/parse.rs b/bsize/src/parse.rs index 7800783..8b181e1 100644 --- a/bsize/src/parse.rs +++ b/bsize/src/parse.rs @@ -12,10 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +use core::convert::TryFrom; use core::fmt; use core::str::FromStr; use crate::BSize; +use crate::ByteSize; /// The error returned when parsing a byte size fails. #[derive(Debug, Clone, Eq, PartialEq)] @@ -41,26 +43,24 @@ impl fmt::Display for ParseError { impl core::error::Error for ParseError {} -macro_rules! impl_from_str { - ($($ty:ty),* $(,)?) => { - $( - impl FromStr for BSize<$ty> { - type Err = ParseError; +macroweave::repeat!(Ty in [u8, u16, u32, u64, usize] { + impl FromStr for BSize { + type Err = ParseError; - fn from_str(s: &str) -> Result { - let size = parse_size(s.as_bytes())?; - if size <= <$ty>::MAX as u64 { - Ok(BSize(size as $ty)) - } else { - Err(ParseError::Overflow) - } - } - } - )* - }; -} + fn from_str(s: &str) -> Result { + bsize_from_u64(parse_size(s.as_bytes())?) + } + } +}); -impl_from_str!(u8, u16, u32, u64, usize); +fn bsize_from_u64(size: u64) -> Result, ParseError> +where + T: ByteSize + TryFrom, +{ + T::try_from(size) + .map(BSize) + .map_err(|_| ParseError::Overflow) +} // This is derived from `parse-size` [1]. // diff --git a/bsize/src/traits/nightly.rs b/bsize/src/traits/nightly.rs index f8b09c1..1ad77d1 100644 --- a/bsize/src/traits/nightly.rs +++ b/bsize/src/traits/nightly.rs @@ -76,116 +76,47 @@ pub const trait ExaByteSize: [const] PetaByteSize { const EIB: Self; } -macro_rules! impl_byte_size { - ($($ty:ty),* $(,)?) => { - $( - const impl ByteSize for $ty { - fn to_f64(&self) -> f64 { - *self as f64 - } - } - )* - }; -} - -impl_byte_size!(u8, u16, u32, u64, usize); - -macro_rules! impl_size_trait { - ($trait:ident for $ty:ty { $($name:ident = $value:literal),* $(,)? }) => { - const impl $trait for $ty { - $(const $name: Self = $value;)* +macroweave::repeat!(Ty in [u8, u16, u32, u64, usize] { + const impl ByteSize for Ty { + fn to_f64(&self) -> f64 { + *self as f64 } - }; -} - -macro_rules! impl_usize_size_traits { - (through_kilo) => { - impl_size_trait!(KiloByteSize for usize { - KB = 1_000, - KIB = 1_024, - }); - }; - (through_giga) => { - impl_usize_size_traits!(through_kilo); - impl_size_trait!(MegaByteSize for usize { - MB = 1_000_000, - MIB = 1_048_576, - }); - impl_size_trait!(GigaByteSize for usize { - GB = 1_000_000_000, - GIB = 1_073_741_824, - }); - }; - (through_exa) => { - impl_usize_size_traits!(through_giga); - impl_size_trait!(TeraByteSize for usize { - TB = 1_000_000_000_000, - TIB = 1_099_511_627_776, - }); - impl_size_trait!(PetaByteSize for usize { - PB = 1_000_000_000_000_000, - PIB = 1_125_899_906_842_624, - }); - impl_size_trait!(ExaByteSize for usize { - EB = 1_000_000_000_000_000_000, - EIB = 1_152_921_504_606_846_976, - }); - }; -} - -impl_size_trait!(KiloByteSize for u16 { - KB = 1_000, - KIB = 1_024, -}); - -impl_size_trait!(KiloByteSize for u32 { - KB = 1_000, - KIB = 1_024, -}); - -impl_size_trait!(MegaByteSize for u32 { - MB = 1_000_000, - MIB = 1_048_576, + } }); -impl_size_trait!(GigaByteSize for u32 { - GB = 1_000_000_000, - GIB = 1_073_741_824, +macroweave::repeat!((Trait, Ty, DecimalName, BinaryName, Scale) in [ + (KiloByteSize, u16, KB, KIB, 1), + (KiloByteSize, u32, KB, KIB, 1), + (MegaByteSize, u32, MB, MIB, 2), + (GigaByteSize, u32, GB, GIB, 3), + (KiloByteSize, u64, KB, KIB, 1), + (MegaByteSize, u64, MB, MIB, 2), + (GigaByteSize, u64, GB, GIB, 3), + (TeraByteSize, u64, TB, TIB, 4), + (PetaByteSize, u64, PB, PIB, 5), + (ExaByteSize, u64, EB, EIB, 6), +] { + const impl Trait for Ty { + const DecimalName: Self = Ty::pow(1000, Scale); + const BinaryName: Self = Ty::pow(1024, Scale); + } }); -impl_size_trait!(KiloByteSize for u64 { - KB = 1_000, - KIB = 1_024, +macroweave::repeat!((PointerWidth, Trait, DecimalName, BinaryName, Scale) in [ + ("16", KiloByteSize, KB, KIB, 1), + ("32", KiloByteSize, KB, KIB, 1), + ("32", MegaByteSize, MB, MIB, 2), + ("32", GigaByteSize, GB, GIB, 3), + ("64", KiloByteSize, KB, KIB, 1), + ("64", MegaByteSize, MB, MIB, 2), + ("64", GigaByteSize, GB, GIB, 3), + ("64", TeraByteSize, TB, TIB, 4), + ("64", PetaByteSize, PB, PIB, 5), + ("64", ExaByteSize, EB, EIB, 6), +] { + #[cfg(target_pointer_width = PointerWidth)] + const impl Trait for usize { + const DecimalName: Self = usize::pow(1000, Scale); + const BinaryName: Self = usize::pow(1024, Scale); + } }); - -impl_size_trait!(MegaByteSize for u64 { - MB = 1_000_000, - MIB = 1_048_576, -}); - -impl_size_trait!(GigaByteSize for u64 { - GB = 1_000_000_000, - GIB = 1_073_741_824, -}); - -impl_size_trait!(TeraByteSize for u64 { - TB = 1_000_000_000_000, - TIB = 1_099_511_627_776, -}); - -impl_size_trait!(PetaByteSize for u64 { - PB = 1_000_000_000_000_000, - PIB = 1_125_899_906_842_624, -}); - -impl_size_trait!(ExaByteSize for u64 { - EB = 1_000_000_000_000_000_000, - EIB = 1_152_921_504_606_846_976, -}); - -#[cfg(target_pointer_width = "16")] -impl_usize_size_traits!(through_kilo); -#[cfg(target_pointer_width = "32")] -impl_usize_size_traits!(through_giga); -#[cfg(target_pointer_width = "64")] -impl_usize_size_traits!(through_exa); diff --git a/bsize/src/traits/stable.rs b/bsize/src/traits/stable.rs index 7011cd9..982f0d0 100644 --- a/bsize/src/traits/stable.rs +++ b/bsize/src/traits/stable.rs @@ -17,7 +17,7 @@ use core::ops::Mul; use super::private; /// A marker trait for all supported byte size underlying types. -pub const trait ByteSize: private::Sealed + Clone + Copy + Sized { +pub trait ByteSize: private::Sealed + Clone + Copy + Sized { /// Returns the byte size as an approximate `f64`. fn to_f64(&self) -> f64; } @@ -76,116 +76,47 @@ pub trait ExaByteSize: PetaByteSize { const EIB: Self; } -macro_rules! impl_byte_size { - ($($ty:ty),* $(,)?) => { - $( - impl ByteSize for $ty { - fn to_f64(&self) -> f64 { - *self as f64 - } - } - )* - }; -} - -impl_byte_size!(u8, u16, u32, u64, usize); - -macro_rules! impl_size_trait { - ($trait:ident for $ty:ty { $($name:ident = $value:literal),* $(,)? }) => { - impl $trait for $ty { - $(const $name: Self = $value;)* +macroweave::repeat!(Ty in [u8, u16, u32, u64, usize] { + impl ByteSize for Ty { + fn to_f64(&self) -> f64 { + *self as f64 } - }; -} - -macro_rules! impl_usize_size_traits { - (through_kilo) => { - impl_size_trait!(KiloByteSize for usize { - KB = 1_000, - KIB = 1_024, - }); - }; - (through_giga) => { - impl_usize_size_traits!(through_kilo); - impl_size_trait!(MegaByteSize for usize { - MB = 1_000_000, - MIB = 1_048_576, - }); - impl_size_trait!(GigaByteSize for usize { - GB = 1_000_000_000, - GIB = 1_073_741_824, - }); - }; - (through_exa) => { - impl_usize_size_traits!(through_giga); - impl_size_trait!(TeraByteSize for usize { - TB = 1_000_000_000_000, - TIB = 1_099_511_627_776, - }); - impl_size_trait!(PetaByteSize for usize { - PB = 1_000_000_000_000_000, - PIB = 1_125_899_906_842_624, - }); - impl_size_trait!(ExaByteSize for usize { - EB = 1_000_000_000_000_000_000, - EIB = 1_152_921_504_606_846_976, - }); - }; -} - -impl_size_trait!(KiloByteSize for u16 { - KB = 1_000, - KIB = 1_024, -}); - -impl_size_trait!(KiloByteSize for u32 { - KB = 1_000, - KIB = 1_024, -}); - -impl_size_trait!(MegaByteSize for u32 { - MB = 1_000_000, - MIB = 1_048_576, + } }); -impl_size_trait!(GigaByteSize for u32 { - GB = 1_000_000_000, - GIB = 1_073_741_824, +macroweave::repeat!((Trait, Ty, DecimalName, BinaryName, Scale) in [ + (KiloByteSize, u16, KB, KIB, 1), + (KiloByteSize, u32, KB, KIB, 1), + (MegaByteSize, u32, MB, MIB, 2), + (GigaByteSize, u32, GB, GIB, 3), + (KiloByteSize, u64, KB, KIB, 1), + (MegaByteSize, u64, MB, MIB, 2), + (GigaByteSize, u64, GB, GIB, 3), + (TeraByteSize, u64, TB, TIB, 4), + (PetaByteSize, u64, PB, PIB, 5), + (ExaByteSize, u64, EB, EIB, 6), +] { + impl Trait for Ty { + const DecimalName: Self = Ty::pow(1000, Scale); + const BinaryName: Self = Ty::pow(1024, Scale); + } }); -impl_size_trait!(KiloByteSize for u64 { - KB = 1_000, - KIB = 1_024, +macroweave::repeat!((PointerWidth, Trait, DecimalName, BinaryName, Scale) in [ + ("16", KiloByteSize, KB, KIB, 1), + ("32", KiloByteSize, KB, KIB, 1), + ("32", MegaByteSize, MB, MIB, 2), + ("32", GigaByteSize, GB, GIB, 3), + ("64", KiloByteSize, KB, KIB, 1), + ("64", MegaByteSize, MB, MIB, 2), + ("64", GigaByteSize, GB, GIB, 3), + ("64", TeraByteSize, TB, TIB, 4), + ("64", PetaByteSize, PB, PIB, 5), + ("64", ExaByteSize, EB, EIB, 6), +] { + #[cfg(target_pointer_width = PointerWidth)] + impl Trait for usize { + const DecimalName: Self = usize::pow(1000, Scale); + const BinaryName: Self = usize::pow(1024, Scale); + } }); - -impl_size_trait!(MegaByteSize for u64 { - MB = 1_000_000, - MIB = 1_048_576, -}); - -impl_size_trait!(GigaByteSize for u64 { - GB = 1_000_000_000, - GIB = 1_073_741_824, -}); - -impl_size_trait!(TeraByteSize for u64 { - TB = 1_000_000_000_000, - TIB = 1_099_511_627_776, -}); - -impl_size_trait!(PetaByteSize for u64 { - PB = 1_000_000_000_000_000, - PIB = 1_125_899_906_842_624, -}); - -impl_size_trait!(ExaByteSize for u64 { - EB = 1_000_000_000_000_000_000, - EIB = 1_152_921_504_606_846_976, -}); - -#[cfg(target_pointer_width = "16")] -impl_usize_size_traits!(through_kilo); -#[cfg(target_pointer_width = "32")] -impl_usize_size_traits!(through_giga); -#[cfg(target_pointer_width = "64")] -impl_usize_size_traits!(through_exa); diff --git a/bsize/src/types/stable.rs b/bsize/src/types/stable.rs index 1253732..668cfe4 100644 --- a/bsize/src/types/stable.rs +++ b/bsize/src/types/stable.rs @@ -14,192 +14,156 @@ use super::BSize; -macro_rules! impl_constructors { - ($ty:ty => { $($name:ident = $trait:ident::$size:ident),* $(,)? }) => { - impl BSize<$ty> { - $( - #[doc = concat!( - "Constructs a byte size wrapper from a quantity of `", - stringify!($name), - "` units." - )] - #[inline(always)] - pub const fn $name(size: $ty) -> Self { - BSize(size * <$ty as $crate::traits::$trait>::$size) - } - )* +macroweave::repeat!((Ty, Name, Trait, Size) in [ + (u16, kb, KiloByteSize, KB), + (u16, kib, KiloByteSize, KIB), + (u32, kb, KiloByteSize, KB), + (u32, kib, KiloByteSize, KIB), + (u32, mb, MegaByteSize, MB), + (u32, mib, MegaByteSize, MIB), + (u32, gb, GigaByteSize, GB), + (u32, gib, GigaByteSize, GIB), + (u64, kb, KiloByteSize, KB), + (u64, kib, KiloByteSize, KIB), + (u64, mb, MegaByteSize, MB), + (u64, mib, MegaByteSize, MIB), + (u64, gb, GigaByteSize, GB), + (u64, gib, GigaByteSize, GIB), + (u64, tb, TeraByteSize, TB), + (u64, tib, TeraByteSize, TIB), + (u64, pb, PetaByteSize, PB), + (u64, pib, PetaByteSize, PIB), + (u64, eb, ExaByteSize, EB), + (u64, eib, ExaByteSize, EIB), +] { + impl BSize { + #[doc = concat!( + "Constructs a byte size wrapper from a quantity of `", + stringify!(Name), + "` units." + )] + #[inline(always)] + pub const fn Name(size: Ty) -> Self { + BSize(size * ::Size) } - }; -} - -macro_rules! impl_usize_constructors { - (through_kilo) => { - impl_constructors!(usize => { - kb = KiloByteSize::KB, - kib = KiloByteSize::KIB, - }); - }; - (through_giga) => { - impl_usize_constructors!(through_kilo); - impl_constructors!(usize => { - mb = MegaByteSize::MB, - mib = MegaByteSize::MIB, - gb = GigaByteSize::GB, - gib = GigaByteSize::GIB, - }); - }; - (through_exa) => { - impl_usize_constructors!(through_giga); - impl_constructors!(usize => { - tb = TeraByteSize::TB, - tib = TeraByteSize::TIB, - pb = PetaByteSize::PB, - pib = PetaByteSize::PIB, - eb = ExaByteSize::EB, - eib = ExaByteSize::EIB, - }); - }; -} - -macro_rules! impl_byte_accessors { - ($($ty:ty),* $(,)?) => { - $( - impl BSize<$ty> { - /// Returns byte count as bytes. - /// - /// The result is approximate when the byte count cannot be - /// represented exactly as `f64`. Use `.0` or [`BSize::with`] - /// for the exact underlying integer value. - #[inline(always)] - pub const fn as_b(&self) -> f64 { - self.0 as f64 - } - } - )* - }; -} + } +}); -macro_rules! impl_accessors { - ($ty:ty => { $($name:ident = $trait:ident::$size:ident => $unit:literal),* $(,)? }) => { - impl BSize<$ty> { - $( - #[doc = concat!("Returns byte count as ", $unit, ".")] - /// - /// The result is approximate when the byte count cannot be - /// represented exactly as `f64`. - #[inline(always)] - pub const fn $name(&self) -> f64 { - (self.0 as f64) / (<$ty as $crate::traits::$trait>::$size as f64) - } - )* +macroweave::repeat!((PointerWidth, Name, Trait, Size) in [ + ("16", kb, KiloByteSize, KB), + ("16", kib, KiloByteSize, KIB), + ("32", kb, KiloByteSize, KB), + ("32", kib, KiloByteSize, KIB), + ("32", mb, MegaByteSize, MB), + ("32", mib, MegaByteSize, MIB), + ("32", gb, GigaByteSize, GB), + ("32", gib, GigaByteSize, GIB), + ("64", kb, KiloByteSize, KB), + ("64", kib, KiloByteSize, KIB), + ("64", mb, MegaByteSize, MB), + ("64", mib, MegaByteSize, MIB), + ("64", gb, GigaByteSize, GB), + ("64", gib, GigaByteSize, GIB), + ("64", tb, TeraByteSize, TB), + ("64", tib, TeraByteSize, TIB), + ("64", pb, PetaByteSize, PB), + ("64", pib, PetaByteSize, PIB), + ("64", eb, ExaByteSize, EB), + ("64", eib, ExaByteSize, EIB), +] { + #[cfg(target_pointer_width = PointerWidth)] + impl BSize { + #[doc = concat!( + "Constructs a byte size wrapper from a quantity of `", + stringify!(Name), + "` units." + )] + #[inline(always)] + pub const fn Name(size: usize) -> Self { + BSize(size * ::Size) } - }; -} - -macro_rules! impl_usize_accessors { - (through_kilo) => { - impl_accessors!(usize => { - as_kb = KiloByteSize::KB => "kilobytes", - as_kib = KiloByteSize::KIB => "kibibytes", - }); - }; - (through_giga) => { - impl_usize_accessors!(through_kilo); - impl_accessors!(usize => { - as_mb = MegaByteSize::MB => "megabytes", - as_mib = MegaByteSize::MIB => "mebibytes", - as_gb = GigaByteSize::GB => "gigabytes", - as_gib = GigaByteSize::GIB => "gibibytes", - }); - }; - (through_exa) => { - impl_usize_accessors!(through_giga); - impl_accessors!(usize => { - as_tb = TeraByteSize::TB => "terabytes", - as_tib = TeraByteSize::TIB => "tebibytes", - as_pb = PetaByteSize::PB => "petabytes", - as_pib = PetaByteSize::PIB => "pebibytes", - as_eb = ExaByteSize::EB => "exabytes", - as_eib = ExaByteSize::EIB => "exbibytes", - }); - }; -} - -macro_rules! impl_unit_accessors { - () => { - impl_accessors!(u16 => { - as_kb = KiloByteSize::KB => "kilobytes", - as_kib = KiloByteSize::KIB => "kibibytes", - }); - - impl_accessors!(u32 => { - as_kb = KiloByteSize::KB => "kilobytes", - as_kib = KiloByteSize::KIB => "kibibytes", - as_mb = MegaByteSize::MB => "megabytes", - as_mib = MegaByteSize::MIB => "mebibytes", - as_gb = GigaByteSize::GB => "gigabytes", - as_gib = GigaByteSize::GIB => "gibibytes", - }); - - impl_accessors!(u64 => { - as_kb = KiloByteSize::KB => "kilobytes", - as_kib = KiloByteSize::KIB => "kibibytes", - as_mb = MegaByteSize::MB => "megabytes", - as_mib = MegaByteSize::MIB => "mebibytes", - as_gb = GigaByteSize::GB => "gigabytes", - as_gib = GigaByteSize::GIB => "gibibytes", - as_tb = TeraByteSize::TB => "terabytes", - as_tib = TeraByteSize::TIB => "tebibytes", - as_pb = PetaByteSize::PB => "petabytes", - as_pib = PetaByteSize::PIB => "pebibytes", - as_eb = ExaByteSize::EB => "exabytes", - as_eib = ExaByteSize::EIB => "exbibytes", - }); - - #[cfg(target_pointer_width = "16")] - impl_usize_accessors!(through_kilo); - #[cfg(target_pointer_width = "32")] - impl_usize_accessors!(through_giga); - #[cfg(target_pointer_width = "64")] - impl_usize_accessors!(through_exa); - }; -} - -impl_constructors!(u16 => { - kb = KiloByteSize::KB, - kib = KiloByteSize::KIB, + } }); -impl_constructors!(u32 => { - kb = KiloByteSize::KB, - kib = KiloByteSize::KIB, - mb = MegaByteSize::MB, - mib = MegaByteSize::MIB, - gb = GigaByteSize::GB, - gib = GigaByteSize::GIB, +macroweave::repeat!(Ty in [u8, u16, u32, u64, usize] { + impl BSize { + /// Returns byte count as bytes. + /// + /// The result is approximate when the byte count cannot be + /// represented exactly as `f64`. Use `.0` or [`BSize::with`] + /// for the exact underlying integer value. + #[inline(always)] + pub const fn as_b(&self) -> f64 { + self.0 as f64 + } + } }); -impl_constructors!(u64 => { - kb = KiloByteSize::KB, - kib = KiloByteSize::KIB, - mb = MegaByteSize::MB, - mib = MegaByteSize::MIB, - gb = GigaByteSize::GB, - gib = GigaByteSize::GIB, - tb = TeraByteSize::TB, - tib = TeraByteSize::TIB, - pb = PetaByteSize::PB, - pib = PetaByteSize::PIB, - eb = ExaByteSize::EB, - eib = ExaByteSize::EIB, +macroweave::repeat!((Ty, Name, Trait, Size, Unit) in [ + (u16, as_kb, KiloByteSize, KB, "kilobytes"), + (u16, as_kib, KiloByteSize, KIB, "kibibytes"), + (u32, as_kb, KiloByteSize, KB, "kilobytes"), + (u32, as_kib, KiloByteSize, KIB, "kibibytes"), + (u32, as_mb, MegaByteSize, MB, "megabytes"), + (u32, as_mib, MegaByteSize, MIB, "mebibytes"), + (u32, as_gb, GigaByteSize, GB, "gigabytes"), + (u32, as_gib, GigaByteSize, GIB, "gibibytes"), + (u64, as_kb, KiloByteSize, KB, "kilobytes"), + (u64, as_kib, KiloByteSize, KIB, "kibibytes"), + (u64, as_mb, MegaByteSize, MB, "megabytes"), + (u64, as_mib, MegaByteSize, MIB, "mebibytes"), + (u64, as_gb, GigaByteSize, GB, "gigabytes"), + (u64, as_gib, GigaByteSize, GIB, "gibibytes"), + (u64, as_tb, TeraByteSize, TB, "terabytes"), + (u64, as_tib, TeraByteSize, TIB, "tebibytes"), + (u64, as_pb, PetaByteSize, PB, "petabytes"), + (u64, as_pib, PetaByteSize, PIB, "pebibytes"), + (u64, as_eb, ExaByteSize, EB, "exabytes"), + (u64, as_eib, ExaByteSize, EIB, "exbibytes"), +] { + impl BSize { + #[doc = concat!("Returns byte count as ", Unit, ".")] + /// + /// The result is approximate when the byte count cannot be + /// represented exactly as `f64`. + #[inline(always)] + pub const fn Name(&self) -> f64 { + (self.0 as f64) / (::Size as f64) + } + } }); -#[cfg(target_pointer_width = "16")] -impl_usize_constructors!(through_kilo); -#[cfg(target_pointer_width = "32")] -impl_usize_constructors!(through_giga); -#[cfg(target_pointer_width = "64")] -impl_usize_constructors!(through_exa); - -impl_byte_accessors!(u8, u16, u32, u64, usize); -impl_unit_accessors!(); +macroweave::repeat!((PointerWidth, Name, Trait, Size, Unit) in [ + ("16", as_kb, KiloByteSize, KB, "kilobytes"), + ("16", as_kib, KiloByteSize, KIB, "kibibytes"), + ("32", as_kb, KiloByteSize, KB, "kilobytes"), + ("32", as_kib, KiloByteSize, KIB, "kibibytes"), + ("32", as_mb, MegaByteSize, MB, "megabytes"), + ("32", as_mib, MegaByteSize, MIB, "mebibytes"), + ("32", as_gb, GigaByteSize, GB, "gigabytes"), + ("32", as_gib, GigaByteSize, GIB, "gibibytes"), + ("64", as_kb, KiloByteSize, KB, "kilobytes"), + ("64", as_kib, KiloByteSize, KIB, "kibibytes"), + ("64", as_mb, MegaByteSize, MB, "megabytes"), + ("64", as_mib, MegaByteSize, MIB, "mebibytes"), + ("64", as_gb, GigaByteSize, GB, "gigabytes"), + ("64", as_gib, GigaByteSize, GIB, "gibibytes"), + ("64", as_tb, TeraByteSize, TB, "terabytes"), + ("64", as_tib, TeraByteSize, TIB, "tebibytes"), + ("64", as_pb, PetaByteSize, PB, "petabytes"), + ("64", as_pib, PetaByteSize, PIB, "pebibytes"), + ("64", as_eb, ExaByteSize, EB, "exabytes"), + ("64", as_eib, ExaByteSize, EIB, "exbibytes"), +] { + #[cfg(target_pointer_width = PointerWidth)] + impl BSize { + #[doc = concat!("Returns byte count as ", Unit, ".")] + /// + /// The result is approximate when the byte count cannot be + /// represented exactly as `f64`. + #[inline(always)] + pub const fn Name(&self) -> f64 { + (self.0 as f64) / (::Size as f64) + } + } +});