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