1#![doc = include_str!("../README.md")]
2
3#[cfg(test)]
4mod tests;
5
6#[cfg(feature = "serde")]
7mod serde;
8
9use core::num::{IntErrorKind, ParseFloatError, ParseIntError};
10use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
11use core::str::FromStr;
12
13pub const B: u64 = 1;
15
16pub const KB: u64 = 10u64.pow(3);
18pub const MB: u64 = 10u64.pow(6);
20pub const GB: u64 = 10u64.pow(9);
22pub const TB: u64 = 10u64.pow(12);
24pub const PB: u64 = 10u64.pow(15);
26pub const EB: u64 = 10u64.pow(18);
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
31pub struct ByteSizeSi(pub u64);
32
33impl ByteSizeSi {
34 const PREFIXES: &'static [char] = &['k', 'M', 'G', 'T', 'P', 'E'];
35}
36
37impl core::fmt::Display for ByteSizeSi {
38 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
39 if self.0 < KB {
40 write!(f, "{}B", self.0)
41 } else {
42 let size = self.0 as f64;
43 let exp = (size.log(KB as f64) as usize).min(Self::PREFIXES.len());
44 let number = size / (KB.pow(exp as u32) as f64);
45 let prefix = Self::PREFIXES[exp - 1];
46
47 write!(f, "{number:.1}{prefix}B")
48 }
49 }
50}
51
52impl FromStr for ByteSizeSi {
53 type Err = Error;
54
55 fn from_str(s: &str) -> Result<Self, Self::Err> {
56 parse_byte_size(s).map(Self)
57 }
58}
59
60impl ByteSizeSi {
61 pub const MIN: Self = Self(u64::MIN);
63
64 pub const MAX: Self = Self(u64::MAX);
66
67 #[inline]
68 pub fn b(n: impl Into<u64>) -> Self {
69 Self(n.into())
70 }
71
72 #[inline(always)]
73 pub const fn kb(n: u64) -> Self {
74 Self(n * KB)
75 }
76
77 #[inline(always)]
78 pub const fn mb(n: u64) -> Self {
79 Self(n * MB)
80 }
81
82 #[inline(always)]
83 pub const fn gb(n: u64) -> Self {
84 Self(n * GB)
85 }
86
87 #[inline(always)]
88 pub const fn tb(n: u64) -> Self {
89 Self(n * TB)
90 }
91
92 #[inline(always)]
93 pub const fn pb(n: u64) -> Self {
94 Self(n * PB)
95 }
96
97 #[inline(always)]
98 pub const fn eb(n: u64) -> Self {
99 Self(n * EB)
100 }
101
102 #[inline(always)]
104 pub const fn iec(self) -> ByteSizeIec {
105 ByteSizeIec(self.0)
106 }
107}
108
109impl From<u64> for ByteSizeSi {
110 fn from(n: u64) -> Self {
111 Self(n)
112 }
113}
114
115impl From<ByteSizeSi> for u64 {
116 fn from(bs: ByteSizeSi) -> Self {
117 bs.0
118 }
119}
120
121impl Add for ByteSizeSi {
122 type Output = Self;
123
124 #[inline(always)]
125 fn add(self, rhs: Self) -> Self::Output {
126 Self(self.0 + rhs.0)
127 }
128}
129
130impl AddAssign for ByteSizeSi {
131 #[inline(always)]
132 fn add_assign(&mut self, rhs: Self) {
133 self.0 += rhs.0;
134 }
135}
136
137impl Sub for ByteSizeSi {
138 type Output = Self;
139
140 #[inline(always)]
141 fn sub(self, rhs: Self) -> Self::Output {
142 Self(self.0 - rhs.0)
143 }
144}
145
146impl SubAssign for ByteSizeSi {
147 #[inline(always)]
148 fn sub_assign(&mut self, rhs: Self) {
149 self.0 -= rhs.0;
150 }
151}
152
153impl<T> Mul<T> for ByteSizeSi
154where
155 T: Into<u64>,
156{
157 type Output = Self;
158
159 #[inline]
160 fn mul(self, rhs: T) -> Self::Output {
161 Self(self.0 * rhs.into())
162 }
163}
164
165impl<T> MulAssign<T> for ByteSizeSi
166where
167 T: Into<u64>,
168{
169 #[inline]
170 fn mul_assign(&mut self, rhs: T) {
171 self.0 *= rhs.into();
172 }
173}
174
175pub const KIB: u64 = 2u64.pow(10);
177pub const MIB: u64 = 2u64.pow(20);
179pub const GIB: u64 = 2u64.pow(30);
181pub const TIB: u64 = 2u64.pow(40);
183pub const PIB: u64 = 2u64.pow(50);
185pub const EIB: u64 = 2u64.pow(60);
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
190pub struct ByteSizeIec(pub u64);
191
192impl ByteSizeIec {
193 const PREFIXES: &'static [char] = &['K', 'M', 'G', 'T', 'P', 'E'];
194}
195
196impl core::fmt::Display for ByteSizeIec {
197 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
198 if self.0 < KIB {
199 write!(f, "{}B", self.0)
200 } else {
201 let size = self.0 as f64;
202 let exp = (size.log(KIB as f64) as usize).min(Self::PREFIXES.len());
203 let number = size / (KIB.pow(exp as u32) as f64);
204 let prefix = Self::PREFIXES[exp - 1];
205
206 write!(f, "{number:.1}{prefix}iB")
207 }
208 }
209}
210
211impl FromStr for ByteSizeIec {
212 type Err = Error;
213
214 fn from_str(s: &str) -> Result<Self, Self::Err> {
215 parse_byte_size(s).map(Self)
216 }
217}
218
219impl ByteSizeIec {
220 pub const MIN: Self = Self(u64::MIN);
222
223 pub const MAX: Self = Self(u64::MAX);
225
226 #[inline]
227 pub fn b(n: impl Into<u64>) -> Self {
228 Self(n.into())
229 }
230
231 #[inline(always)]
232 pub const fn kib(n: u64) -> Self {
233 Self(n * KIB)
234 }
235
236 #[inline(always)]
237 pub const fn mib(n: u64) -> Self {
238 Self(n * MIB)
239 }
240
241 #[inline(always)]
242 pub const fn gib(n: u64) -> Self {
243 Self(n * GIB)
244 }
245
246 #[inline(always)]
247 pub const fn tib(n: u64) -> Self {
248 Self(n * TIB)
249 }
250
251 #[inline(always)]
252 pub const fn pib(n: u64) -> Self {
253 Self(n * PIB)
254 }
255
256 #[inline(always)]
257 pub const fn eib(n: u64) -> Self {
258 Self(n * EIB)
259 }
260
261 #[inline(always)]
263 pub const fn si(self) -> ByteSizeSi {
264 ByteSizeSi(self.0)
265 }
266}
267
268impl From<u64> for ByteSizeIec {
269 fn from(n: u64) -> Self {
270 Self(n)
271 }
272}
273
274impl From<ByteSizeIec> for u64 {
275 fn from(bs: ByteSizeIec) -> Self {
276 bs.0
277 }
278}
279
280impl Add for ByteSizeIec {
281 type Output = Self;
282
283 #[inline(always)]
284 fn add(self, rhs: Self) -> Self::Output {
285 Self(self.0 + rhs.0)
286 }
287}
288
289impl AddAssign for ByteSizeIec {
290 #[inline(always)]
291 fn add_assign(&mut self, rhs: Self) {
292 self.0 += rhs.0;
293 }
294}
295
296impl Sub for ByteSizeIec {
297 type Output = Self;
298
299 #[inline(always)]
300 fn sub(self, rhs: Self) -> Self::Output {
301 Self(self.0 - rhs.0)
302 }
303}
304
305impl SubAssign for ByteSizeIec {
306 #[inline(always)]
307 fn sub_assign(&mut self, rhs: Self) {
308 self.0 -= rhs.0;
309 }
310}
311
312impl<T> Mul<T> for ByteSizeIec
313where
314 T: Into<u64>,
315{
316 type Output = Self;
317
318 #[inline]
319 fn mul(self, rhs: T) -> Self::Output {
320 Self(self.0 * rhs.into())
321 }
322}
323
324impl<T> MulAssign<T> for ByteSizeIec
325where
326 T: Into<u64>,
327{
328 #[inline]
329 fn mul_assign(&mut self, rhs: T) {
330 self.0 *= rhs.into();
331 }
332}
333
334fn parse_byte_size(input: &str) -> Result<u64, Error> {
335 let Some(i) = input.find(|c: char| !c.is_ascii_digit()) else {
336 return input.parse::<u64>().map_err(Into::into);
337 };
338
339 if i == 0 {
340 return Err(Error::Invalid);
341 }
342
343 let (integer, rest) = input.split_at(i);
344
345 if let Some(rest) = rest.strip_prefix('.') {
346 let i = i
347 + 1
348 + rest
349 .find(|c: char| !c.is_ascii_digit())
350 .filter(|n| *n > 0)
351 .ok_or(Error::Invalid)?;
352
353 let (float, rest) = input.split_at(i);
354 let float = float.parse::<f64>()?;
355 let unit = parse_unit(rest.trim_start_matches(' '))?;
356 Ok((float * unit as f64) as u64)
357 } else {
358 let integer = integer.parse::<u64>()?;
359 let unit = parse_unit(rest.trim_start_matches(' '))?;
360 Ok(integer * unit)
361 }
362}
363
364fn parse_unit(input: &str) -> Result<u64, Error> {
365 match input.to_lowercase().as_str() {
366 "b" => Ok(B),
367 "k" | "kb" => Ok(KB),
369 "m" | "mb" => Ok(MB),
370 "g" | "gb" => Ok(GB),
371 "t" | "tb" => Ok(TB),
372 "p" | "pb" => Ok(PB),
373 "e" | "eb" => Ok(EB),
374 "ki" | "kib" => Ok(KIB),
376 "mi" | "mib" => Ok(MIB),
377 "gi" | "gib" => Ok(GIB),
378 "ti" | "tib" => Ok(TIB),
379 "pi" | "pib" => Ok(PIB),
380 "ei" | "eib" => Ok(EIB),
381 _ => Err(Error::Unit),
382 }
383}
384
385impl From<ByteSizeIec> for ByteSizeSi {
386 fn from(iec: ByteSizeIec) -> Self {
387 iec.si()
388 }
389}
390
391impl From<ByteSizeSi> for ByteSizeIec {
392 fn from(si: ByteSizeSi) -> Self {
393 si.iec()
394 }
395}
396
397#[derive(Debug, PartialEq, Eq)]
398pub enum Error {
399 Empty,
400 Invalid,
401 Unit,
402}
403
404impl core::error::Error for Error {}
405
406impl core::fmt::Display for Error {
407 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
408 let msg = match self {
409 Self::Empty => "cannot parse bytesize from empty string",
410 Self::Invalid => "invalid number found in string",
411 Self::Unit => "cannot recognize byte unit in string",
412 };
413 f.write_str(msg)
414 }
415}
416
417impl From<ParseIntError> for Error {
418 fn from(e: ParseIntError) -> Self {
419 match e.kind() {
420 IntErrorKind::Empty => Self::Empty,
421 _ => Self::Invalid,
422 }
423 }
424}
425
426impl From<ParseFloatError> for Error {
427 fn from(_: ParseFloatError) -> Self {
428 Self::Invalid
429 }
430}