1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// License: see LICENSE file at root directory of `master` branch

//! # KiB
//!
//! ## Project
//!
//! - Repository: <https://bitbucket.org/haibison/kib>
//! - License: Nice License 1.0.0 _(see LICENSE file at root directory of `master` branch)_
//! - _This project follows [Semantic Versioning 2.0.0]_
//!
//! ## Failures
//!
//! Panics, missing documentation, missing tests... are my failures. It would help me a lot if you file new issues when you encounter such
//! problems.
//!
//! Thank you,
//!
//! [Semantic Versioning 2.0.0]: https://semver.org/spec/v2.0.0.html

#![no_std]

// ╔═════════════════╗
// ║   IDENTIFIERS   ║
// ╚═════════════════╝

macro_rules! crate_code_name    { () => { "kib" }}
macro_rules! crate_version      { () => { "3.0.0" }}

/// # Crate name
pub const NAME: &str = "KiB";

/// # Crate code name
pub const CODE_NAME: &str = crate_code_name!();

/// # ID of this crate
pub const ID: &str = concat!(
    "c8f97a99-2ba79d4a-0a355f69-9f387d8b-0c0cefe2-67b252af-f02c443d-76b31b5a-",
    "e1afecef-f1ca8727-5231b155-99654c9f-9e48339a-f4a5896d-9ffef1ef-a7e7c9e9",
);

/// # Crate version
pub const VERSION: &str = crate_version!();

/// # Crate release date (year/month/day)
pub const RELEASE_DATE: (u16, u8, u8) = (2019, 8, 18);

/// Tag, which can be used for logging...
pub const TAG: &str = concat!(crate_code_name!(), "::c8f97a99::", crate_version!());

// ╔════════════════════╗
// ║   IMPLEMENTATION   ║
// ╚════════════════════╝

extern crate alloc;

use alloc::{
    string::{String, ToString},
};

use core::mem;

pub mod version_info;

#[test]
fn test_crate_version() {
    assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
}

/// One KiB in bytes.
pub const KIB: u16 = 1024;

/// One MiB in bytes.
pub const MIB: u32 = 1024 * KIB as u32;

/// One GiB in bytes.
pub const GIB: u32 = 1024 * MIB;

/// One TiB in bytes.
pub const TIB: u64 = 1024 * GIB as u64;

/// One PiB in bytes.
pub const PIB: u64 = 1024 * TIB;

/// One EiB in bytes.
pub const EIB: u64 = 1024 * PIB;

/// One ZiB in bytes.
pub const ZIB: u128 = 1024 * EIB as u128;

/// One YiB in bytes.
pub const YIB: u128 = 1024 * ZIB;

/// Units.
const UNITS: &[&str] = &["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];

/// # Bytes
pub type Bytes = u128;

/// # Bits of Bytes
const BITS: usize = mem::size_of::<Bytes>() * 8;

#[test]
fn tests() {
    assert_eq!(BITS, Bytes::min_value().leading_zeros() as usize);
    assert_eq!(BITS as u32, Bytes::min_value().leading_zeros());
}

/// # Formats bytes into string.
///
/// The result is a human-readable string, e.g: `1.9 KiB`, `9.9 TiB`...
pub fn format<B>(bytes: B) -> String where B: Into<Bytes> {
    let (bytes, unit) = format2(bytes);
    alloc::format!("{bytes}{space}{unit}", bytes=bytes, space=' ', unit=unit)
}

/// # Formats bytes into 2 strings
///
/// - The first one is size.
/// - The second one is unit.
pub fn format2<'a, B>(bytes: B) -> (String, &'a str) where B: Into<Bytes> {
    let bytes = bytes.into();

    if bytes < u128::from(KIB) {
        return (bytes.to_string(), match bytes { 1 => "byte", _ => "bytes" });
    }

    let x = (BITS - bytes.leading_zeros() as usize).saturating_sub(1) / 10;
    let unit_index = x.min(UNITS.len()).saturating_sub(1);
    (
        alloc::format!("{:.2}", bytes as f64 / {
            let mut kib = KIB as f64;
            for _ in 0..unit_index {
                kib *= KIB as f64;
            }
            kib
        }),
        UNITS[unit_index],
    )
}