file_system/fundamentals/
size.rs

1//! Size representation for file system operations.
2//!
3//! This module provides the [`Size`] wrapper around `u64` for representing
4//! sizes, lengths, and byte counts in file system operations. It provides type
5//! safety and consistent handling of size values throughout the file system.
6
7use core::{
8    fmt::{self, Display, Formatter},
9    ops::{Add, AddAssign},
10};
11
12/// Type-safe wrapper for size values in file system operations.
13///
14/// `Size` represents sizes, lengths, and byte counts as a 64-bit unsigned integer.
15/// This provides a range of 0 to approximately 18 exabytes, which is sufficient for
16/// any practical file system operation. The type provides various conversion methods
17/// and arithmetic operations for convenient size manipulation.
18///
19/// # Examples
20///
21/// ```rust
22/// use file_system::Size;
23///
24/// // Create a size representing 1024 bytes
25/// let size = Size::new(1024);
26/// assert_eq!(size.As_u64(), 1024);
27///
28/// // Convert from usize
29/// let size_from_usize: Size = 512usize.into();
30/// assert_eq!(size_from_usize.As_u64(), 512);
31///
32/// // Arithmetic operations
33/// let total = size + size_from_usize;
34/// assert_eq!(total.As_u64(), 1536);
35/// ```
36///
37/// # Type Safety
38///
39/// Using `Size` instead of raw integers helps prevent mixing up different
40/// numeric types and provides clearer API signatures throughout the file system.
41#[derive(Default, PartialOrd, PartialEq, Eq, Ord, Clone, Copy, Debug)]
42#[repr(transparent)]
43pub struct Size(u64);
44
45impl Display for Size {
46    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
47        write!(f, "{}", self.0)
48    }
49}
50
51impl Size {
52    /// Create a new size value from a u64.
53    ///
54    /// # Arguments
55    ///
56    /// * `Item` - The size value in bytes
57    ///
58    /// # Examples
59    ///
60    /// ```rust
61    /// use file_system::Size;
62    ///
63    /// let size = Size::new(2048);
64    /// assert_eq!(size.As_u64(), 2048);
65    /// ```
66    pub const fn new(item: u64) -> Self {
67        Size(item)
68    }
69
70    /// Get the size value as a u64.
71    ///
72    /// # Returns
73    ///
74    /// The size value in bytes as a 64-bit unsigned integer.
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// use file_system::Size;
80    ///
81    /// let size = Size::new(4096);
82    /// assert_eq!(size.As_u64(), 4096);
83    /// ```
84    pub const fn as_u64(&self) -> u64 {
85        self.0
86    }
87}
88
89impl PartialEq<usize> for Size {
90    fn eq(&self, other: &usize) -> bool {
91        self.0 == *other as u64
92    }
93}
94
95impl From<usize> for Size {
96    fn from(item: usize) -> Self {
97        Size(item as u64)
98    }
99}
100
101impl From<u64> for Size {
102    fn from(item: u64) -> Self {
103        Size(item)
104    }
105}
106
107impl From<Size> for usize {
108    fn from(item: Size) -> Self {
109        item.0 as usize
110    }
111}
112
113impl From<Size> for u64 {
114    fn from(item: Size) -> Self {
115        item.0
116    }
117}
118
119impl Add<Size> for Size {
120    type Output = Size;
121
122    fn add(self, rhs: Size) -> Self::Output {
123        Size(self.0 + rhs.0)
124    }
125}
126
127impl Add<usize> for Size {
128    type Output = Size;
129
130    fn add(self, rhs: usize) -> Self::Output {
131        Size(self.0 + rhs as u64)
132    }
133}
134
135impl Add<u64> for Size {
136    type Output = Size;
137
138    fn add(self, rhs: u64) -> Self::Output {
139        Size(self.0 + rhs)
140    }
141}
142
143impl Add<Size> for usize {
144    type Output = Size;
145
146    fn add(self, rhs: Size) -> Self::Output {
147        Size(self as u64 + rhs.0)
148    }
149}
150
151impl Add<Size> for u64 {
152    type Output = Size;
153
154    fn add(self, rhs: Size) -> Self::Output {
155        Size(self + rhs.0)
156    }
157}
158
159impl AddAssign<Size> for Size {
160    fn add_assign(&mut self, rhs: Size) {
161        self.0 += rhs.0;
162    }
163}
164
165impl AddAssign<usize> for Size {
166    fn add_assign(&mut self, rhs: usize) {
167        self.0 += rhs as u64;
168    }
169}
170
171impl AddAssign<u64> for Size {
172    fn add_assign(&mut self, rhs: u64) {
173        self.0 += rhs;
174    }
175}
176
177impl AddAssign<Size> for usize {
178    fn add_assign(&mut self, rhs: Size) {
179        *self += rhs.0 as usize;
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186    use alloc::format;
187
188    #[test]
189    fn test_size_creation() {
190        let size = Size::new(1024);
191        assert_eq!(size.as_u64(), 1024);
192    }
193
194    #[test]
195    fn test_size_default() {
196        let size = Size::default();
197        assert_eq!(size.as_u64(), 0);
198    }
199
200    #[test]
201    fn test_size_conversions() {
202        // From usize
203        let size_from_usize: Size = 512usize.into();
204        assert_eq!(size_from_usize.as_u64(), 512);
205
206        // From u64
207        let size_from_u64: Size = 1024u64.into();
208        assert_eq!(size_from_u64.as_u64(), 1024);
209
210        // To usize
211        let as_usize: usize = size_from_u64.into();
212        assert_eq!(as_usize, 1024);
213
214        // To u64
215        let as_u64: u64 = size_from_u64.into();
216        assert_eq!(as_u64, 1024);
217    }
218
219    #[test]
220    fn test_size_equality() {
221        let size1 = Size::new(100);
222        let size2 = Size::new(100);
223        let size3 = Size::new(200);
224
225        assert_eq!(size1, size2);
226        assert_ne!(size1, size3);
227
228        // Test equality with usize
229        assert_eq!(size1, 100usize);
230        assert_ne!(size1, 200usize);
231    }
232
233    #[test]
234    fn test_size_comparison() {
235        let small = Size::new(100);
236        let large = Size::new(200);
237
238        assert!(small < large);
239        assert!(large > small);
240        assert!(small <= large);
241        assert!(large >= small);
242        assert!(small <= small);
243        assert!(large >= large);
244    }
245
246    #[test]
247    fn test_size_addition_with_size() {
248        let size1 = Size::new(100);
249        let size2 = Size::new(200);
250        let result = size1 + size2;
251        assert_eq!(result.as_u64(), 300);
252    }
253
254    #[test]
255    fn test_size_addition_with_usize() {
256        let size = Size::new(100);
257        let result = size + 50usize;
258        assert_eq!(result.as_u64(), 150);
259
260        // Test commutative property
261        let result2 = 50usize + size;
262        assert_eq!(result2.as_u64(), 150);
263    }
264
265    #[test]
266    fn test_size_addition_with_u64() {
267        let size = Size::new(100);
268        let result = size + 75u64;
269        assert_eq!(result.as_u64(), 175);
270
271        // Test commutative property
272        let result2 = 75u64 + size;
273        assert_eq!(result2.as_u64(), 175);
274    }
275
276    #[test]
277    fn test_size_add_assign_with_size() {
278        let mut size = Size::new(100);
279        let other = Size::new(50);
280        size += other;
281        assert_eq!(size.as_u64(), 150);
282    }
283
284    #[test]
285    fn test_size_add_assign_with_usize() {
286        let mut size = Size::new(100);
287        size += 25usize;
288        assert_eq!(size.as_u64(), 125);
289
290        // Test adding to usize
291        let mut value = 100usize;
292        value += Size::new(25);
293        assert_eq!(value, 125);
294    }
295
296    #[test]
297    fn test_size_add_assign_with_u64() {
298        let mut size = Size::new(100);
299        size += 30u64;
300        assert_eq!(size.as_u64(), 130);
301    }
302
303    #[test]
304    fn test_size_display() {
305        let size = Size::new(12345);
306        let display_str = format!("{size}");
307        assert_eq!(display_str, "12345");
308    }
309
310    #[test]
311    fn test_size_debug() {
312        let size = Size::new(67890);
313        let debug_str = format!("{size:?}");
314        assert_eq!(debug_str, "Size(67890)");
315    }
316
317    #[test]
318    fn test_size_clone_copy() {
319        let original = Size::new(999);
320        let cloned = original;
321        let copied = original;
322
323        assert_eq!(original, cloned);
324        assert_eq!(original, copied);
325        assert_eq!(cloned, copied);
326
327        // Test that we can still use original after copying
328        assert_eq!(original.as_u64(), 999);
329    }
330
331    #[test]
332    fn test_size_zero() {
333        let zero = Size::new(0);
334        assert_eq!(zero.as_u64(), 0);
335        assert_eq!(zero, 0usize);
336        assert_eq!(zero, Size::default());
337    }
338
339    #[test]
340    fn test_size_max_value() {
341        let max_size = Size::new(u64::MAX);
342        assert_eq!(max_size.as_u64(), u64::MAX);
343    }
344
345    #[test]
346    fn test_size_arithmetic_overflow_safety() {
347        // Test large values that might overflow in some operations
348        let large1 = Size::new(u64::MAX / 2);
349        let large2 = Size::new(u64::MAX / 2);
350
351        // This would overflow, but we're testing the types work correctly
352        // In practice, overflow behavior depends on debug/release mode
353        let _ = large1 + large2; // Should wrap around in release mode
354    }
355
356    #[test]
357    fn test_size_type_safety() {
358        // Verify that Size is a zero-cost abstraction
359        use core::mem::{align_of, size_of};
360
361        assert_eq!(size_of::<Size>(), size_of::<u64>());
362        assert_eq!(align_of::<Size>(), align_of::<u64>());
363    }
364
365    #[test]
366    fn test_size_const_operations() {
367        // Test that New and As_u64 are const functions
368        const SIZE: Size = Size::new(42);
369        const VALUE: u64 = SIZE.as_u64();
370
371        assert_eq!(VALUE, 42);
372        assert_eq!(SIZE.as_u64(), 42);
373    }
374
375    #[test]
376    fn test_size_mixed_arithmetic() {
377        let size = Size::new(100);
378
379        // Chain multiple additions
380        let result = size + 50usize + 25u64 + Size::new(10);
381        assert_eq!(result.as_u64(), 185);
382    }
383
384    #[test]
385    fn test_size_compound_assignments() {
386        let mut size = Size::new(10);
387
388        size += 5usize;
389        size += 3u64;
390        size += Size::new(2);
391
392        assert_eq!(size.as_u64(), 20);
393    }
394
395    #[test]
396    fn test_size_comparison_edge_cases() {
397        let zero = Size::new(0);
398        let one = Size::new(1);
399        let max = Size::new(u64::MAX);
400
401        assert!(zero < one);
402        assert!(one < max);
403        assert!(zero < max);
404
405        assert!(max > one);
406        assert!(one > zero);
407        assert!(max > zero);
408    }
409
410    #[test]
411    fn test_size_conversion_edge_cases() {
412        // Test conversion from max usize
413        let max_usize_as_size: Size = usize::MAX.into();
414        let back_to_usize: usize = max_usize_as_size.into();
415
416        // On 64-bit systems, this should be lossless
417        // On 32-bit systems, there might be some differences
418        if core::mem::size_of::<usize>() == 8 {
419            assert_eq!(back_to_usize, usize::MAX);
420        }
421    }
422
423    #[test]
424    fn test_size_ordering() {
425        let mut sizes = [
426            Size::new(300),
427            Size::new(100),
428            Size::new(200),
429            Size::new(50),
430        ];
431
432        sizes.sort();
433
434        assert_eq!(sizes[0], Size::new(50));
435        assert_eq!(sizes[1], Size::new(100));
436        assert_eq!(sizes[2], Size::new(200));
437        assert_eq!(sizes[3], Size::new(300));
438    }
439}