microsandbox_utils/size.rs
1//! Byte-size types and conversion helpers.
2//!
3//! Provides [`ByteSize`], [`Bytes`], and [`Mebibytes`] for type-safe size
4//! specification across the project. The [`SizeExt`] trait adds `.bytes()`,
5//! `.kib()`, `.mib()`, and `.gib()` helpers to integer literals.
6//!
7//! ```ignore
8//! use microsandbox_utils::size::{SizeExt, Mebibytes};
9//!
10//! // All equivalent — 512 MiB:
11//! let a: Mebibytes = 512.into(); // bare integer
12//! let b: Mebibytes = 512.mib().into(); // explicit unit
13//!
14//! // Cross-unit conversion:
15//! let c: Mebibytes = 1.gib().into(); // 1 GiB → 1024 MiB
16//! ```
17
18//--------------------------------------------------------------------------------------------------
19// Types
20//--------------------------------------------------------------------------------------------------
21
22/// A byte-size value returned by [`SizeExt`] helpers.
23///
24/// Acts as the universal intermediate type that converts [`Into`] both
25/// [`Bytes`] and [`Mebibytes`].
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct ByteSize(u64);
28
29/// A size measured in bytes.
30///
31/// Accepted by APIs that operate at byte-level precision (e.g. filesystem
32/// capacity, rlimit values).
33///
34/// **Bare integer path:** `u64` converts directly via [`From`].
35/// **Helper path:** any [`ByteSize`] (from `.kib()`, `.mib()`, `.gib()`)
36/// converts via [`From`].
37#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
38pub struct Bytes(u64);
39
40/// A size measured in mebibytes (MiB).
41///
42/// Accepted by APIs that operate at MiB-level precision (e.g. sandbox
43/// memory, volume quota, tmpfs size).
44///
45/// **Bare integer path:** `u32` converts directly via [`From`].
46/// **Helper path:** any [`ByteSize`] (from `.kib()`, `.mib()`, `.gib()`)
47/// converts via [`From`] (truncates to whole MiB).
48#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub struct Mebibytes(u32);
50
51/// Helper trait for readable byte sizes.
52///
53/// Implemented for common integer types so that literals like `512.mib()`
54/// or `1.gib()` return a [`ByteSize`] that converts into either [`Bytes`]
55/// or [`Mebibytes`].
56pub trait SizeExt {
57 /// Create a [`ByteSize`] representing this many bytes.
58 fn bytes(self) -> ByteSize;
59 /// Create a [`ByteSize`] representing this many kibibytes (×1024).
60 fn kib(self) -> ByteSize;
61 /// Create a [`ByteSize`] representing this many mebibytes (×1024²).
62 fn mib(self) -> ByteSize;
63 /// Create a [`ByteSize`] representing this many gibibytes (×1024³).
64 fn gib(self) -> ByteSize;
65}
66
67//--------------------------------------------------------------------------------------------------
68// Methods
69//--------------------------------------------------------------------------------------------------
70
71impl ByteSize {
72 /// Get the raw byte count.
73 pub fn as_bytes(self) -> u64 {
74 self.0
75 }
76
77 /// Get the value in whole mebibytes (truncates sub-MiB remainder).
78 pub fn as_mib(self) -> u32 {
79 (self.0 / (1024 * 1024)) as u32
80 }
81}
82
83impl Bytes {
84 /// Get the raw byte count.
85 pub fn as_u64(self) -> u64 {
86 self.0
87 }
88}
89
90impl Mebibytes {
91 /// Get the MiB count.
92 pub fn as_u32(self) -> u32 {
93 self.0
94 }
95}
96
97//--------------------------------------------------------------------------------------------------
98// Trait Implementations
99//--------------------------------------------------------------------------------------------------
100
101// ByteSize → destination types.
102
103impl From<ByteSize> for Bytes {
104 fn from(bs: ByteSize) -> Self {
105 Self(bs.0)
106 }
107}
108
109impl From<ByteSize> for Mebibytes {
110 fn from(bs: ByteSize) -> Self {
111 Self((bs.0 / (1024 * 1024)) as u32)
112 }
113}
114
115// Bare integers → destination types.
116
117impl From<u64> for Bytes {
118 fn from(v: u64) -> Self {
119 Self(v)
120 }
121}
122
123impl From<u32> for Mebibytes {
124 fn from(v: u32) -> Self {
125 Self(v)
126 }
127}
128
129// SizeExt implementations for common integer types.
130
131macro_rules! impl_size_ext {
132 ($($t:ty),*) => {
133 $(
134 impl SizeExt for $t {
135 fn bytes(self) -> ByteSize { ByteSize(self as u64) }
136 fn kib(self) -> ByteSize { ByteSize(self as u64 * 1024) }
137 fn mib(self) -> ByteSize { ByteSize(self as u64 * 1024 * 1024) }
138 fn gib(self) -> ByteSize { ByteSize(self as u64 * 1024 * 1024 * 1024) }
139 }
140 )*
141 };
142}
143
144impl_size_ext!(u8, u16, u32, u64, usize, i32);
145
146//--------------------------------------------------------------------------------------------------
147// Tests
148//--------------------------------------------------------------------------------------------------
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_size_ext_helpers() {
156 assert_eq!(1u64.kib().as_bytes(), 1024);
157 assert_eq!(1u64.mib().as_bytes(), 1024 * 1024);
158 assert_eq!(1u64.gib().as_bytes(), 1024 * 1024 * 1024);
159 assert_eq!(512u64.mib().as_bytes(), 512 * 1024 * 1024);
160 assert_eq!(64i32.mib().as_bytes(), 64 * 1024 * 1024);
161 }
162
163 #[test]
164 fn test_bytesize_to_mebibytes() {
165 let mib: Mebibytes = 512.mib().into();
166 assert_eq!(mib.as_u32(), 512);
167
168 let mib: Mebibytes = 1.gib().into();
169 assert_eq!(mib.as_u32(), 1024);
170 }
171
172 #[test]
173 fn test_bytesize_to_bytes() {
174 let b: Bytes = 64.mib().into();
175 assert_eq!(b.as_u64(), 64 * 1024 * 1024);
176 }
177
178 #[test]
179 fn test_bare_u32_to_mebibytes() {
180 let mib: Mebibytes = 512u32.into();
181 assert_eq!(mib.as_u32(), 512);
182 }
183
184 #[test]
185 fn test_bare_u64_to_bytes() {
186 let b: Bytes = 4096u64.into();
187 assert_eq!(b.as_u64(), 4096);
188 }
189
190 #[test]
191 fn test_truncation() {
192 // 1.5 MiB → truncates to 1 MiB
193 let bs = ByteSize(1024 * 1024 + 512 * 1024);
194 let mib: Mebibytes = bs.into();
195 assert_eq!(mib.as_u32(), 1);
196 }
197
198 #[test]
199 fn test_bytes_helper() {
200 assert_eq!(4096.bytes().as_bytes(), 4096);
201 }
202}