1 unstable release

new 0.1.0 Jan 9, 2025

#11 in #byte-size

MIT/Apache

32KB
627 lines

Helper crate to work with bit, byte, and block sizes.

This crate provides three dedicated types, NumBits, NumBytes, and NumBlocks, to represent numbers of bits, bytes, and blocks, respectively. It implements the usual traits for numeric operators such that calculations on them can be carried out with succinct syntax. All operations will panic on errors, such as over- or underflows. This is an intentional design decision to prevent subtly incorrect results and behavior. In addition, this crate provides formatting and parsing for byte sizes.

This crate is no_std-compatible.

Conversions

The provided types support convenient conversions to each other:

assert_eq!(NumBits::new(15).to_bytes_ceil(), NumBytes::bytes(2));
assert_eq!(NumBits::new(15).to_bytes_floor(), NumBytes::bytes(1));

assert_eq!(NumBytes::bytes(2).to_bits(), NumBits::new(16));
assert_eq!(NumBits::from(NumBytes::bytes(2)), NumBits::new(16));

const BLOCK_SIZE: NumBytes = NumBytes::kibibytes(4);
assert_eq!(NumBytes::bytes(8193).to_blocks_ceil(BLOCK_SIZE), NumBlocks::new(3));
assert_eq!(NumBytes::bytes(8193).to_blocks_floor(BLOCK_SIZE), NumBlocks::new(2));

assert_eq!(NumBlocks::new(2).to_bytes(BLOCK_SIZE), NumBytes::kibibytes(8));

Calculations

Calculations can be performed with the types as well as with [u64] integers:

assert_eq!(NumBytes::bytes(10) / NumBytes::bytes(2), NumBytes::bytes(5));
assert_eq!(NumBytes::bytes(10) / 2, NumBytes::bytes(5));

assert_eq!(NumBits::new(5) + NumBits::new(8), NumBits::new(13));
assert_eq!(NumBits::new(5) * 2, NumBits::new(10));

assert_eq!(NumBlocks::new(10) + 2, NumBlocks::new(12));

assert_eq!(NumBits::new(2) + NumBytes::bytes(1), NumBits::new(10));

Comparisons

Comparisons are supported on the types as well as with [u64] integers:

assert!(NumBytes::bytes(10) < 20);
assert!(NumBytes::bytes(10) != 0);

assert_eq!(NumBits::new(5), 5);

assert_eq!(NumBits::new(16), NumBytes::new(2));
assert!(NumBits::new(15) < NumBytes::new(2));

Formatting

Formatting of byte sizes maximizes the unit while minimizing the integer part towards one. For example:

assert_eq!(NumBytes::mebibytes(128).to_string(), "128MiB");
assert_eq!(NumBytes::gigabytes(1).to_string(), "1GB");
assert_eq!(NumBytes::bytes(1023).to_string(), "1.023kB");
assert_eq!(NumBytes::bytes(1000).to_string(), "1kB");
assert_eq!(NumBytes::bytes(999).to_string(), "999B");
assert_eq!(NumBytes::bytes(2560).to_string(), "2.5KiB");

The usual formatting syntax can be used to limit the precision:

assert_eq!(format!("{:.2}", NumBytes::terabytes(2)), "1.81TiB");

Parsing

Byte sizes must follow the following syntax:

⟨byte-size⟩  ::=  ⟨int⟩ [ '.' ⟨int⟩ ] [ ' '* ⟨unit⟩ ]
⟨int⟩  ::=  [0-9_]+
⟨unit⟩ ::=  'B' | 'K' … 'E' | 'kB' … 'EB' | 'KiB' … 'EiB' (case-insensitive)

The units (K ... E) are interpreted as binary units (KiB ... EiB). Generally, unit parsing is case-insensitive.

assert_eq!(NumBytes::from_str("5").unwrap(), NumBytes::bytes(5));
assert_eq!(NumBytes::from_str("2.5KiB").unwrap(), NumBytes::bytes(2560));
assert_eq!(NumBytes::from_str("2_000kB").unwrap(), NumBytes::megabytes(2));

Parsing also works in const contexts using NumBytes::parse_str or NumBytes::parse_ascii:

const BLOCK_SIZE: NumBytes = match NumBytes::parse_str("4KiB") {
    Ok(value) => value,
    Err(_) => panic!("invalid format"),
};

The parser has been extensively fuzz-tested, ensuring that no input leads to panics.

Serialization and Deserialization

By enabling the serde feature, NumBits, NumBytes, and NumBlocks can be serialized and deserialized. All tree types always serialize as [u64] integers. Deserialization of NumBytes is also supported from strings.

Dependencies

~160KB