typed_bytesize/
lib.rs

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
13/// 1 byte
14pub const B: u64 = 1;
15
16/// 1 kilobyte
17pub const KB: u64 = 10u64.pow(3);
18/// 1 megabyte
19pub const MB: u64 = 10u64.pow(6);
20/// 1 gigabyte
21pub const GB: u64 = 10u64.pow(9);
22/// 1 terabyte
23pub const TB: u64 = 10u64.pow(12);
24/// 1 petabyte
25pub const PB: u64 = 10u64.pow(15);
26/// 1 exabyte
27pub const EB: u64 = 10u64.pow(18);
28
29/// Decimal prefix bytesize
30#[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    /// The smallest value that can be represented.
62    pub const MIN: Self = Self(u64::MIN);
63
64    /// The largest value that can be represented.
65    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    /// Convert into binary prefix unit
103    #[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
175/// 1 kibibyte
176pub const KIB: u64 = 2u64.pow(10);
177/// 1 mebibyte
178pub const MIB: u64 = 2u64.pow(20);
179/// 1 gibibyte
180pub const GIB: u64 = 2u64.pow(30);
181/// 1 tebibyte
182pub const TIB: u64 = 2u64.pow(40);
183/// 1 pebibyte
184pub const PIB: u64 = 2u64.pow(50);
185/// 1 exbibyte
186pub const EIB: u64 = 2u64.pow(60);
187
188/// Binary prefix bytesize
189#[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    /// The smallest value that can be represented.
221    pub const MIN: Self = Self(u64::MIN);
222
223    /// The largest value that can be represented.
224    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    /// Convert into decimal prefix unit
262    #[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        // SI
368        "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        // IEC 60027-2
375        "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}