1use crate::u64_is_multiple_of;
2use core::fmt::Debug;
3use core::fmt::Display;
4use core::ops::Deref;
5use core::ops::DerefMut;
6use core::str::FromStr;
7
8#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Size {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Size", &&self.0)
}
}Debug, #[automatically_derived]
impl ::core::default::Default for Size {
#[inline]
fn default() -> Size { Size(::core::default::Default::default()) }
}Default, #[automatically_derived]
impl ::core::clone::Clone for Size {
#[inline]
fn clone(&self) -> Size {
let _: ::core::clone::AssertParamIsClone<u64>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Size { }Copy, #[automatically_derived]
impl ::core::hash::Hash for Size {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, #[automatically_derived]
impl ::core::cmp::PartialEq for Size {
#[inline]
fn eq(&self, other: &Size) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for Size {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u64>;
}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for Size {
#[inline]
fn partial_cmp(&self, other: &Size)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
}
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for Size {
#[inline]
fn cmp(&self, other: &Size) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}Ord)]
15#[cfg_attr(all(test, feature = "std"), derive(arbitrary::Arbitrary))]
16#[repr(transparent)]
17pub struct Size(pub u64);
18
19impl Size {
20 pub const MAX_STRING_LEN: usize = 20;
22}
23
24impl Display for Size {
25 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
26 let mut size = self.0;
27 let unit = if size == 0 {
28 ""
29 } else {
30 let mut unit = "";
31 for u in UNITS {
32 if !u64_is_multiple_of(size, 1024) {
33 break;
34 }
35 size /= 1024;
36 unit = u;
37 }
38 unit
39 };
40 f.write_fmt(format_args!("{0}{1}", size, unit))write!(f, "{size}{unit}")
41 }
42}
43
44impl FromStr for Size {
45 type Err = SizeError;
46 fn from_str(other: &str) -> Result<Self, Self::Err> {
47 let other = other.trim();
48 match other.rfind(char::is_numeric) {
49 None => Err(SizeError),
50 Some(i) => {
51 let size: u64 = other[..=i].parse().map_err(|_| SizeError)?;
52 let unit = other[(i + 1)..].trim();
53 let factor = match unit.len() {
54 0 => 1_u64,
55 1 => unit_to_factor(unit.as_bytes()[0])?,
56 _ => return Err(SizeError),
57 };
58 let value = size.checked_mul(factor).ok_or(SizeError)?;
59 Ok(Self(value))
60 }
61 }
62 }
63}
64
65impl From<u64> for Size {
66 fn from(other: u64) -> Self {
67 Self(other)
68 }
69}
70
71impl From<Size> for u64 {
72 fn from(other: Size) -> Self {
73 other.0
74 }
75}
76
77impl Deref for Size {
78 type Target = u64;
79
80 fn deref(&self) -> &Self::Target {
81 &self.0
82 }
83}
84
85impl DerefMut for Size {
86 fn deref_mut(&mut self) -> &mut Self::Target {
87 &mut self.0
88 }
89}
90
91#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SizeError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "SizeError")
}
}Debug)]
93pub struct SizeError;
94
95impl Display for SizeError {
96 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
97 Debug::fmt(self, f)
98 }
99}
100
101#[cfg(feature = "std")]
102impl std::error::Error for SizeError {}
103
104const fn unit_to_factor(unit: u8) -> Result<u64, SizeError> {
105 match unit {
106 b'k' | b'K' => Ok(1024_u64),
107 b'm' | b'M' => Ok(1024_u64 * 1024_u64),
108 b'g' | b'G' => Ok(1024_u64 * 1024_u64 * 1024_u64),
109 b't' | b'T' => Ok(1024_u64 * 1024_u64 * 1024_u64 * 1024_u64),
110 _ => Err(SizeError),
111 }
112}
113
114const UNITS: [&str; 4] = ["k", "m", "g", "t"];
115
116#[cfg(all(test, feature = "std"))]
117mod tests {
118
119 use std::ops::AddAssign;
120
121 use arbtest::arbtest;
122
123 use super::*;
124
125 #[test]
126 fn test_display() {
127 assert_eq!("0", Size(0).to_string());
128 assert_eq!("1023", Size(1023).to_string());
129 assert_eq!("1k", Size(1024).to_string());
130 assert_eq!("1025", Size(1025).to_string());
131 }
132
133 #[test]
134 fn test_parse() {
135 assert_eq!("Err(SizeError)", format!("{:?}", "2km".parse::<Size>()));
136 assert_eq!("Err(SizeError)", format!("{:?}", "2s".parse::<Size>()));
137 assert_eq!("Err(SizeError)", format!("{:?}", "k".parse::<Size>()));
138 assert_eq!("Err(SizeError)", format!("{:?}", "".parse::<Size>()));
139 assert_eq!(
140 "Err(SizeError)",
141 format!("{:?}", format!("{}0", u64::MAX).parse::<Size>())
142 );
143 }
144
145 #[test]
146 fn test_deref() {
147 assert_eq!(1, *Size(1));
148 let mut tmp = Size(1);
149 tmp.add_assign(1);
150 assert_eq!(2, *tmp);
151 }
152
153 #[test]
154 fn test_from_into() {
155 let d1 = Size(1);
156 let d2: u64 = d1.into();
157 let d3: Size = d2.into();
158 assert_eq!(d1, d3);
159 assert_eq!(d1.0, d2);
160 }
161
162 #[test]
163 fn from_str_overflow_does_not_panic() {
164 let expected = u64::MAX;
165 let string = format!("{expected}t");
166 assert!(string.parse::<Size>().is_err(), "string = {string:?}");
167 }
168
169 #[test]
170 fn display_parse_symmetry() {
171 arbtest(|u| {
172 let expected: Size = u.arbitrary()?;
173 let string = expected.to_string();
174 let actual: Size = string.parse().unwrap();
175 assert_eq!(expected, actual, "string = `{string}`");
176 Ok(())
177 });
178 }
179
180 #[test]
181 fn parse_display_symmetry() {
182 arbtest(|u| {
183 let (unit, max) = *u
184 .choose(&[
185 ("", u64::MAX),
186 ("k", u64::MAX >> 10),
187 ("m", u64::MAX >> 20),
188 ("g", u64::MAX >> 30),
189 ("t", u64::MAX >> 40),
190 ])
191 .unwrap();
192 let mut unit = unit.to_string();
193 if u.arbitrary::<bool>()? {
194 unit.make_ascii_uppercase();
195 }
196 let number: u64 = u.int_in_range(0_u64..=max)?;
197 let prefix = *u.choose(&["", " ", " "]).unwrap();
198 let infix = *u.choose(&["", " ", " "]).unwrap();
199 let suffix = *u.choose(&["", " ", " "]).unwrap();
200 let expected = format!("{prefix}{number}{infix}{unit}{suffix}");
201 let expected_size: Size = expected.parse().unwrap();
202 let actual = expected_size.to_string();
203 let actual_size: Size = actual.parse().unwrap();
204 assert_eq!(
205 expected_size, actual_size,
206 "string 1 = `{expected}`, string 2 = `{actual}`"
207 );
208 assert!(expected == actual || u64_is_multiple_of(actual_size.0, number));
209 Ok(())
210 });
211 }
212}