number_prefix-0.4.0/.gitignore010064400007650000024000000000241323633516400145710ustar0000000000000000/target /Cargo.lock number_prefix-0.4.0/Cargo.toml.orig010064400007650000024000000011341364275560200154770ustar0000000000000000[package] name = "number_prefix" description = "Library for numeric prefixes (kilo, giga, kibi)." version = "0.4.0" authors = [ "Benjamin Sago " ] categories = ["algorithms", "no-std"] documentation = "https://docs.rs/number_prefix" exclude = ["/README.md", "/LICENCE", "/.rustfmt.toml", "/.travis.yml"] repository = "https://github.com/ogham/rust-number-prefix" keywords = ["mathematics", "numerics"] license = "MIT" readme = "README.md" [features] default = ["std"] std = [] [package.metadata.docs.rs] features = ["std"] [[example]] name = "conversions" required-features = ["std"] number_prefix-0.4.0/Cargo.toml0000644000000021451364275572300120160ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "number_prefix" version = "0.4.0" authors = ["Benjamin Sago "] exclude = ["/README.md", "/LICENCE", "/.rustfmt.toml", "/.travis.yml"] description = "Library for numeric prefixes (kilo, giga, kibi)." documentation = "https://docs.rs/number_prefix" readme = "README.md" keywords = ["mathematics", "numerics"] categories = ["algorithms", "no-std"] license = "MIT" repository = "https://github.com/ogham/rust-number-prefix" [package.metadata.docs.rs] features = ["std"] [[example]] name = "conversions" required-features = ["std"] [features] default = ["std"] std = [] number_prefix-0.4.0/examples/conversions.rs010064400007650000024000000042051364274447200173510ustar0000000000000000/// This example prints out the conversions for increasingly-large numbers, to /// showcase how the numbers change as the input gets bigger. /// It results in this: /// /// ```text /// 1000 bytes is 1.000 kB and 1000 bytes /// 1000000 bytes is 1.000 MB and 976.562 KiB /// 1000000000 bytes is 1.000 GB and 953.674 MiB /// 1000000000000 bytes is 1.000 TB and 931.323 GiB /// 1000000000000000 bytes is 1.000 PB and 909.495 TiB /// 1000000000000000000 bytes is 1.000 EB and 888.178 PiB /// 1000000000000000000000 bytes is 1.000 ZB and 867.362 EiB /// 1000000000000000000000000 bytes is 1.000 YB and 847.033 ZiB /// /// 1024 bytes is 1.000 KiB and 1.024 kB /// 1048576 bytes is 1.000 MiB and 1.049 MB /// 1073741824 bytes is 1.000 GiB and 1.074 GB /// 1099511627776 bytes is 1.000 TiB and 1.100 TB /// 1125899906842624 bytes is 1.000 PiB and 1.126 PB /// 1152921504606847000 bytes is 1.000 EiB and 1.153 EB /// 1180591620717411300000 bytes is 1.000 ZiB and 1.181 ZB /// 1208925819614629200000000 bytes is 1.000 YiB and 1.209 YB /// ``` extern crate number_prefix; use number_prefix::NumberPrefix; use std::fmt::Display; fn main() { // part one, decimal prefixes let mut n = 1_f64; for _ in 0 .. 8 { n *= 1000_f64; let decimal = format_prefix(NumberPrefix::decimal(n)); let binary = format_prefix(NumberPrefix::binary(n)); println!("{:26} bytes is {} and {:10}", n, decimal, binary); } println!(); // part two, binary prefixes let mut n = 1_f64; for _ in 0 .. 8 { n *= 1024_f64; let decimal = format_prefix(NumberPrefix::decimal(n)); let binary = format_prefix(NumberPrefix::binary(n)); println!("{:26} bytes is {} and {:10}", n, binary, decimal); } } fn format_prefix(np: NumberPrefix) -> String { match np { NumberPrefix::Prefixed(prefix, n) => format!("{:.3} {}B", n, prefix), NumberPrefix::Standalone(bytes) => format!("{} bytes", bytes), } } number_prefix-0.4.0/src/lib.rs010064400007650000024000000412241364275512600145200ustar0000000000000000#![deny(unsafe_code)] #![warn(missing_copy_implementations)] #![warn(missing_debug_implementations)] #![warn(missing_docs)] #![warn(nonstandard_style)] #![warn(trivial_numeric_casts)] #![warn(unreachable_pub)] #![warn(unused)] //! This is a library for formatting numbers with numeric prefixes, such as //! turning “3000 metres” into “3 kilometres”, or “8705 bytes” into “8.5 KiB”. //! //! //! # Usage //! //! The function [`NumberPrefix::decimal`](enum.NumberPrefix.html#method.decimal) //! returns either a pair of the resulting number and its prefix, or a //! notice that the number was too small to have any prefix applied to it. For //! example: //! //! ``` //! use number_prefix::NumberPrefix; //! //! let amount = 8542_f32; //! let result = match NumberPrefix::decimal(amount) { //! NumberPrefix::Standalone(bytes) => { //! format!("The file is {} bytes in size", bytes) //! } //! NumberPrefix::Prefixed(prefix, n) => { //! format!("The file is {:.1} {}B in size", n, prefix) //! } //! }; //! //! assert_eq!("The file is 8.5 kB in size", result); //! ``` //! //! The `{:.1}` part of the formatting string tells it to restrict the //! output to only one decimal place. This value is calculated by repeatedly //! dividing the number by 1000 until it becomes less than that, which in this //! case results in 8.542, which gets rounded down. Because only one division //! had to take place, the function also returns the decimal prefix `Kilo`, //! which gets converted to its internationally-recognised symbol when //! formatted as a string. //! //! If the value is too small to have any prefixes applied to it — in this case, //! if it’s under 1000 — then the standalone value will be returned: //! //! ``` //! use number_prefix::NumberPrefix; //! //! let amount = 705_f32; //! let result = match NumberPrefix::decimal(amount) { //! NumberPrefix::Standalone(bytes) => { //! format!("The file is {} bytes in size", bytes) //! } //! NumberPrefix::Prefixed(prefix, n) => { //! format!("The file is {:.1} {}B in size", n, prefix) //! } //! }; //! //! assert_eq!("The file is 705 bytes in size", result); //! ``` //! //! In this particular example, the user expects different formatting for //! both bytes and kilobytes: while prefixed values are given more precision, //! there’s no point using anything other than whole numbers for just byte //! amounts. This is why the function pays attention to values without any //! prefixes — they often need to be special-cased. //! //! //! ## Binary Prefixes //! //! This library also allows you to use the *binary prefixes*, which use the //! number 1024 (210) as the multiplier, rather than the more common 1000 //! (103). This uses the //! [`NumberPrefix::binary`](enum.NumberPrefix.html#method.binary) function. //! For example: //! //! ``` //! use number_prefix::NumberPrefix; //! //! let amount = 8542_f32; //! let result = match NumberPrefix::binary(amount) { //! NumberPrefix::Standalone(bytes) => { //! format!("The file is {} bytes in size", bytes) //! } //! NumberPrefix::Prefixed(prefix, n) => { //! format!("The file is {:.1} {}B in size", n, prefix) //! } //! }; //! //! assert_eq!("The file is 8.3 KiB in size", result); //! ``` //! //! A kibibyte is slightly larger than a kilobyte, so the number is smaller //! in the result; but other than that, it works in exactly the same way, with //! the binary prefix being converted to a symbol automatically. //! //! //! ## Which type of prefix should I use? //! //! There is no correct answer this question! Common practice is to use //! the binary prefixes for numbers of *bytes*, while still using the decimal //! prefixes for everything else. Computers work with powers of two, rather than //! powers of ten, and by using the binary prefixes, you get a more accurate //! representation of the amount of data. //! //! //! ## Prefix Names //! //! If you need to describe your unit in actual words, rather than just with the //! symbol, use one of the `upper`, `caps`, `lower`, or `symbol`, which output the //! prefix in a variety of formats. For example: //! //! ``` //! use number_prefix::NumberPrefix; //! //! let amount = 8542_f32; //! let result = match NumberPrefix::decimal(amount) { //! NumberPrefix::Standalone(bytes) => { //! format!("The file is {} bytes in size", bytes) //! } //! NumberPrefix::Prefixed(prefix, n) => { //! format!("The file is {:.1} {}bytes in size", n, prefix.lower()) //! } //! }; //! //! assert_eq!("The file is 8.5 kilobytes in size", result); //! ``` //! //! //! ## String Parsing //! //! There is a `FromStr` implementation for `NumberPrefix` that parses //! strings containing numbers and trailing prefixes, such as `7.5E`. //! //! Currently, the only supported units are `b` and `B` for bytes, and `m` for //! metres. Whitespace is allowed between the number and the rest of the string. //! //! ``` //! use number_prefix::{NumberPrefix, Prefix}; //! //! assert_eq!("7.05E".parse::>(), //! Ok(NumberPrefix::Prefixed(Prefix::Exa, 7.05_f64))); //! //! assert_eq!("7.05".parse::>(), //! Ok(NumberPrefix::Standalone(7.05_f64))); //! //! assert_eq!("7.05 GiB".parse::>(), //! Ok(NumberPrefix::Prefixed(Prefix::Gibi, 7.05_f64))); //! ``` #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "std")] mod parse; #[cfg(not(feature = "std"))] use core::ops::{Neg, Div}; #[cfg(feature = "std")] use std::{fmt, ops::{Neg, Div}}; /// A numeric prefix, either binary or decimal. #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum Prefix { /// _kilo_, 103 or 10001. /// From the Greek ‘χίλιοι’ (‘chilioi’), meaning ‘thousand’. Kilo, /// _mega_, 106 or 10002. /// From the Ancient Greek ‘μέγας’ (‘megas’), meaning ‘great’. Mega, /// _giga_, 109 or 10003. /// From the Greek ‘γίγας’ (‘gigas’), meaning ‘giant’. Giga, /// _tera_, 1012 or 10004. /// From the Greek ‘τέρας’ (‘teras’), meaning ‘monster’. Tera, /// _peta_, 1015 or 10005. /// From the Greek ‘πέντε’ (‘pente’), meaning ‘five’. Peta, /// _exa_, 1018 or 10006. /// From the Greek ‘ἕξ’ (‘hex’), meaning ‘six’. Exa, /// _zetta_, 1021 or 10007. /// From the Latin ‘septem’, meaning ‘seven’. Zetta, /// _yotta_, 1024 or 10008. /// From the Green ‘οκτώ’ (‘okto’), meaning ‘eight’. Yotta, /// _kibi_, 210 or 10241. /// The binary version of _kilo_. Kibi, /// _mebi_, 220 or 10242. /// The binary version of _mega_. Mebi, /// _gibi_, 230 or 10243. /// The binary version of _giga_. Gibi, /// _tebi_, 240 or 10244. /// The binary version of _tera_. Tebi, /// _pebi_, 250 or 10245. /// The binary version of _peta_. Pebi, /// _exbi_, 260 or 10246. /// The binary version of _exa_. Exbi, // you can download exa binaries at https://exa.website/#installation /// _zebi_, 270 or 10247. /// The binary version of _zetta_. Zebi, /// _yobi_, 280 or 10248. /// The binary version of _yotta_. Yobi, } /// The result of trying to apply a prefix to a floating-point value. #[derive(PartialEq, Eq, Clone, Debug)] pub enum NumberPrefix { /// A **standalone** value is returned when the number is too small to /// have any prefixes applied to it. This is commonly a special case, so /// is handled separately. Standalone(F), /// A **prefixed** value *is* large enough for prefixes. This holds the /// prefix, as well as the resulting value. Prefixed(Prefix, F), } impl NumberPrefix { /// Formats the given floating-point number using **decimal** prefixes. /// /// This function accepts both `f32` and `f64` values. If you’re trying to /// format an integer, you’ll have to cast it first. /// /// # Examples /// /// ``` /// use number_prefix::{Prefix, NumberPrefix}; /// /// assert_eq!(NumberPrefix::decimal(1_000_000_000_f32), /// NumberPrefix::Prefixed(Prefix::Giga, 1_f32)); /// ``` pub fn decimal(amount: F) -> Self { use self::Prefix::*; Self::format_number(amount, Amounts::NUM_1000, [Kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta]) } /// Formats the given floating-point number using **binary** prefixes. /// /// This function accepts both `f32` and `f64` values. If you’re trying to /// format an integer, you’ll have to cast it first. /// /// # Examples /// /// ``` /// use number_prefix::{Prefix, NumberPrefix}; /// /// assert_eq!(NumberPrefix::binary(1_073_741_824_f64), /// NumberPrefix::Prefixed(Prefix::Gibi, 1_f64)); /// ``` pub fn binary(amount: F) -> Self { use self::Prefix::*; Self::format_number(amount, Amounts::NUM_1024, [Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi]) } fn format_number(mut amount: F, kilo: F, prefixes: [Prefix; 8]) -> Self { // For negative numbers, flip it to positive, do the processing, then // flip it back to negative again afterwards. let was_negative = if amount.is_negative() { amount = -amount; true } else { false }; let mut prefix = 0; while amount >= kilo && prefix < 8 { amount = amount / kilo; prefix += 1; } if was_negative { amount = -amount; } if prefix == 0 { NumberPrefix::Standalone(amount) } else { NumberPrefix::Prefixed(prefixes[prefix - 1], amount) } } } #[cfg(feature = "std")] impl fmt::Display for Prefix { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.symbol()) } } impl Prefix { /// Returns the name in uppercase, such as “KILO”. /// /// # Examples /// /// ``` /// use number_prefix::Prefix; /// /// assert_eq!("GIGA", Prefix::Giga.upper()); /// assert_eq!("GIBI", Prefix::Gibi.upper()); /// ``` pub fn upper(self) -> &'static str { use self::Prefix::*; match self { Kilo => "KILO", Mega => "MEGA", Giga => "GIGA", Tera => "TERA", Peta => "PETA", Exa => "EXA", Zetta => "ZETTA", Yotta => "YOTTA", Kibi => "KIBI", Mebi => "MEBI", Gibi => "GIBI", Tebi => "TEBI", Pebi => "PEBI", Exbi => "EXBI", Zebi => "ZEBI", Yobi => "YOBI", } } /// Returns the name with the first letter capitalised, such as “Mega”. /// /// # Examples /// /// ``` /// use number_prefix::Prefix; /// /// assert_eq!("Giga", Prefix::Giga.caps()); /// assert_eq!("Gibi", Prefix::Gibi.caps()); /// ``` pub fn caps(self) -> &'static str { use self::Prefix::*; match self { Kilo => "Kilo", Mega => "Mega", Giga => "Giga", Tera => "Tera", Peta => "Peta", Exa => "Exa", Zetta => "Zetta", Yotta => "Yotta", Kibi => "Kibi", Mebi => "Mebi", Gibi => "Gibi", Tebi => "Tebi", Pebi => "Pebi", Exbi => "Exbi", Zebi => "Zebi", Yobi => "Yobi", } } /// Returns the name in lowercase, such as “giga”. /// /// # Examples /// /// ``` /// use number_prefix::Prefix; /// /// assert_eq!("giga", Prefix::Giga.lower()); /// assert_eq!("gibi", Prefix::Gibi.lower()); /// ``` pub fn lower(self) -> &'static str { use self::Prefix::*; match self { Kilo => "kilo", Mega => "mega", Giga => "giga", Tera => "tera", Peta => "peta", Exa => "exa", Zetta => "zetta", Yotta => "yotta", Kibi => "kibi", Mebi => "mebi", Gibi => "gibi", Tebi => "tebi", Pebi => "pebi", Exbi => "exbi", Zebi => "zebi", Yobi => "yobi", } } /// Returns the short-hand symbol, such as “T” (for “tera”). /// /// # Examples /// /// ``` /// use number_prefix::Prefix; /// /// assert_eq!("G", Prefix::Giga.symbol()); /// assert_eq!("Gi", Prefix::Gibi.symbol()); /// ``` pub fn symbol(self) -> &'static str { use self::Prefix::*; match self { Kilo => "k", Mega => "M", Giga => "G", Tera => "T", Peta => "P", Exa => "E", Zetta => "Z", Yotta => "Y", Kibi => "Ki", Mebi => "Mi", Gibi => "Gi", Tebi => "Ti", Pebi => "Pi", Exbi => "Ei", Zebi => "Zi", Yobi => "Yi", } } } /// Traits for floating-point values for both the possible multipliers. They /// need to be Copy, have defined 1000 and 1024s, and implement a bunch of /// operators. pub trait Amounts: Copy + Sized + PartialOrd + Div + Neg { /// The constant representing 1000, for decimal prefixes. const NUM_1000: Self; /// The constant representing 1024, for binary prefixes. const NUM_1024: Self; /// Whether this number is negative. /// This is used internally. fn is_negative(self) -> bool; } impl Amounts for f32 { const NUM_1000: Self = 1000_f32; const NUM_1024: Self = 1024_f32; fn is_negative(self) -> bool { self.is_sign_negative() } } impl Amounts for f64 { const NUM_1000: Self = 1000_f64; const NUM_1024: Self = 1024_f64; fn is_negative(self) -> bool { self.is_sign_negative() } } #[cfg(test)] mod test { use super::{NumberPrefix, Prefix}; #[test] fn decimal_minus_one_billion() { assert_eq!(NumberPrefix::decimal(-1_000_000_000_f64), NumberPrefix::Prefixed(Prefix::Giga, -1f64)) } #[test] fn decimal_minus_one() { assert_eq!(NumberPrefix::decimal(-1f64), NumberPrefix::Standalone(-1f64)) } #[test] fn decimal_0() { assert_eq!(NumberPrefix::decimal(0f64), NumberPrefix::Standalone(0f64)) } #[test] fn decimal_999() { assert_eq!(NumberPrefix::decimal(999f32), NumberPrefix::Standalone(999f32)) } #[test] fn decimal_1000() { assert_eq!(NumberPrefix::decimal(1000f32), NumberPrefix::Prefixed(Prefix::Kilo, 1f32)) } #[test] fn decimal_1030() { assert_eq!(NumberPrefix::decimal(1030f32), NumberPrefix::Prefixed(Prefix::Kilo, 1.03f32)) } #[test] fn decimal_1100() { assert_eq!(NumberPrefix::decimal(1100f64), NumberPrefix::Prefixed(Prefix::Kilo, 1.1f64)) } #[test] fn decimal_1111() { assert_eq!(NumberPrefix::decimal(1111f64), NumberPrefix::Prefixed(Prefix::Kilo, 1.111f64)) } #[test] fn binary_126456() { assert_eq!(NumberPrefix::binary(126_456f32), NumberPrefix::Prefixed(Prefix::Kibi, 123.492188f32)) } #[test] fn binary_1048576() { assert_eq!(NumberPrefix::binary(1_048_576f64), NumberPrefix::Prefixed(Prefix::Mebi, 1f64)) } #[test] fn binary_1073741824() { assert_eq!(NumberPrefix::binary(2_147_483_648f32), NumberPrefix::Prefixed(Prefix::Gibi, 2f32)) } #[test] fn giga() { assert_eq!(NumberPrefix::decimal(1_000_000_000f64), NumberPrefix::Prefixed(Prefix::Giga, 1f64)) } #[test] fn tera() { assert_eq!(NumberPrefix::decimal(1_000_000_000_000f64), NumberPrefix::Prefixed(Prefix::Tera, 1f64)) } #[test] fn peta() { assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000f64), NumberPrefix::Prefixed(Prefix::Peta, 1f64)) } #[test] fn exa() { assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000_000f64), NumberPrefix::Prefixed(Prefix::Exa, 1f64)) } #[test] fn zetta() { assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000_000_000f64), NumberPrefix::Prefixed(Prefix::Zetta, 1f64)) } #[test] fn yotta() { assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000_000_000_000f64), NumberPrefix::Prefixed(Prefix::Yotta, 1f64)) } #[test] #[allow(overflowing_literals)] fn and_so_on() { // When you hit yotta, don't keep going assert_eq!(NumberPrefix::decimal(1_000_000_000_000_000_000_000_000_000f64), NumberPrefix::Prefixed(Prefix::Yotta, 1000f64)) } } number_prefix-0.4.0/src/parse.rs010064400007650000024000000051411364275173700150660ustar0000000000000000use std::{error::Error, fmt, str}; use super::{NumberPrefix, Prefix}; impl str::FromStr for NumberPrefix { type Err = NumberPrefixParseError; fn from_str(s: &str) -> Result { let splitted = s.find(|p| { p == 'k' || p == 'K' || p == 'M' || p == 'G' || p == 'T' || p == 'P' || p == 'E' || p == 'Z' || p == 'Y' }); let num_prefix = s.split_at(splitted.unwrap_or(s.len())); let num = match num_prefix.0.trim().parse::() { Ok(n) => n, Err(_) => return Err(NumberPrefixParseError(())), }; let prefix_unit = num_prefix.1.trim_matches(|p| p == 'b' || p == 'B' || p == 'm' ); let prefix = match prefix_unit { "k" | "K" => Prefix::Kilo, "M" => Prefix::Mega, "G" => Prefix::Giga, "T" => Prefix::Tera, "P" => Prefix::Peta, "E" => Prefix::Exa, "Z" => Prefix::Zetta, "Y" => Prefix::Yotta, "Ki" => Prefix::Kibi, "Mi" => Prefix::Mebi, "Gi" => Prefix::Gibi, "Ti" => Prefix::Tebi, "Pi" => Prefix::Pebi, "Ei" => Prefix::Exbi, "Zi" => Prefix::Zebi, "Yi" => Prefix::Yobi, "" => return Ok(NumberPrefix::Standalone(num)), _ => return Err(NumberPrefixParseError(())), }; Ok(NumberPrefix::Prefixed(prefix, num)) } } /// The error returned when a `NumberPrefix` is failed to be parsed. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct NumberPrefixParseError(()); impl fmt::Display for NumberPrefixParseError { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.write_str("invalid prefix syntax") } } impl Error for NumberPrefixParseError { } #[cfg(test)] mod test { use super::*; #[test] fn parse_examples() { let parse_example_a = "7.05E".parse::>(); let parse_example_b = "7.05".parse::>(); let parse_example_c = "7.05 GiB".parse::>(); assert_eq!(parse_example_a, Ok(NumberPrefix::Prefixed(Prefix::Exa, 7.05_f64))); assert_eq!(parse_example_b, Ok(NumberPrefix::Standalone(7.05_f64))); assert_eq!(parse_example_c, Ok(NumberPrefix::Prefixed(Prefix::Gibi, 7.05_f64))); } #[test] fn bad_parse() { let parsed = "bogo meters per second".parse::>(); assert_ne!(parsed, Ok(NumberPrefix::Prefixed(Prefix::Kilo, 7.05_f64))); } } number_prefix-0.4.0/.cargo_vcs_info.json0000644000000001121364275572300140100ustar00{ "git": { "sha1": "eb6ebd215d50df1b199737f0356b988bebaedc84" } } number_prefix-0.4.0/Cargo.lock0000644000000002211364275572300117640ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "number_prefix" version = "0.4.0"