diff --git a/Cargo.lock b/Cargo.lock index 9b31b98..5d1c579 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 4 + [[package]] name = "lazy_static" version = "1.4.0" @@ -8,40 +10,31 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "rustc_version" -version = "0.2.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "smallmap" -version = "1.3.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d61d73d5986b7f728a76234ca60f7826faa44dd9043d2954ca6583bfc7b875d" +checksum = "d6d0205d551de9bbfea07e51ace307c7f1ca224958a3df56fef6c2cfd244c199" dependencies = [ "rustc_version", ] [[package]] name = "units" -version = "0.1.0" +version = "0.2.0" dependencies = [ "lazy_static", "smallmap", diff --git a/Cargo.toml b/Cargo.toml index 5fade99..c4bb75e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,25 @@ [package] name = "units" -version = "0.1.0" +version = "0.2.0" authors = ["Avril "] -edition = "2018" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 +# TODO: Do we want to strip symbols if we plan to support loading plugins that can register units? +strip = true + + +[features] +default=["f128-math"] + +# Use 128-bit floating-point math internally where possible. +f128-math = [] + [dependencies] lazy_static = "1.4.0" smallmap = "1.3.0" diff --git a/src/conv/bytes.rs b/src/conv/bytes.rs index 9fe8682..808d2ec 100644 --- a/src/conv/bytes.rs +++ b/src/conv/bytes.rs @@ -4,10 +4,15 @@ use smallmap::Map; #[derive(Debug)] pub struct Bytes; -const KB: u64 = 1024; +type BytesUnit = u128; +type IntermediateUnit = f64; + +const KB: BytesUnit = 1024; lazy_static! { - static ref SUFFIX: Map = smallmap::smallmap! { + static ref SUFFIX: Map = smallmap::smallmap! { + {'b' => 1}, + {'k' => KB}, {'m' => KB * KB}, {'g' => KB * KB * KB}, @@ -15,27 +20,46 @@ lazy_static! { }; } + #[cfg(feature="f128-math")] +fn multiply(o: IntermediateUnit, u: BytesUnit) -> BytesUnit +{ + (o as f128 * (u as f128)).ceil() as BytesUnit +} + + #[cfg(not(feature="f128-math"))] +fn multiply(o: IntermediateUnit, u: BytesUnit) -> BytesUnit +{ + (o * (u as f64)).ceil() as BytesUnit +} + impl Conversion for Bytes { const UNIT: &'static str = "bytes"; - type Output = u64; + type Output = BytesUnit; + type Unit = BytesUnit; fn convert(&self, input: &str) -> Result<(Self::Output, Option), Self::Error> { - #[macro_export] macro_rules! parse { - ($non:expr) => (($non).parse::().map_err(|e| ConversionError(format!("Failed to parse u64: {}", e)))?) + //TODO: Apparently, we cannot parse `f128` from string? See if there's another better way of doing this, or if there are fractional types at all... + macro_rules! parse { + ($non:ident: $type:ty) => (($non).parse::<$type>().map_err(|e| ConversionError::new_for(self, format!("Failed to parse {}: {}", std::any::type_name::<$type>(), e)))?); + ($non:ident) => (parse!($non: Self::Output)); } let (o, u) = match input.char_indices().last().map(|(idx, chr)| (chr.to_lowercase().next().unwrap(), &input[..idx])) { - Some((chr, _)) if chr.is_numeric() => (parse!(input), 1), + Some((chr, _)) if chr.is_numeric() => return Ok((parse!(input: Self::Output), Some(1))), - Some((ref chr, non)) if SUFFIX.contains_key(chr) => (parse!(non), *SUFFIX.get(chr).unwrap()), + Some((ref chr, non)) if SUFFIX.contains_key(chr) => (parse!(non: IntermediateUnit), *SUFFIX.get(chr).unwrap()), - Some((chr, _)) => return Err(ConversionError(format!("Unknown suffix {}", chr))), - None => (0, 1), + Some((chr, _)) => return Err(ConversionError::new_for(self, format!("Unknown suffix {}", chr))), + None => (0 as IntermediateUnit, 1), }; - Ok((o * u, Some(u))) + if o.is_sign_negative() { + return Err(ConversionError::new_for(self, "Negative byte offsets not supported")); + } + + Ok((multiply(o, u), Some(u))) } } diff --git a/src/conv/mod.rs b/src/conv/mod.rs index 1610e6f..65460e2 100644 --- a/src/conv/mod.rs +++ b/src/conv/mod.rs @@ -1,5 +1,6 @@ use std::error; use std::fmt; +use std::borrow::Cow; use std::convert::Infallible; pub mod bytes; @@ -16,14 +17,38 @@ pub trait Conversion } #[derive(Debug)] -pub struct ConversionError(String); +pub struct ConversionError +{ + message: Box, + unit: Cow<'static, str>, +} + +impl ConversionError +{ + #[inline] + pub fn new_for(_conv: &C, message: impl Into>) -> Self + { + Self { + message: message.into(), + unit: Cow::Borrowed(C::UNIT), + } + } + #[inline] + pub fn new(unit: impl ToString, message: impl Into>) -> Self + { + Self { + message: message.into(), + unit: Cow::Owned(unit.to_string()), + } + } +} impl error::Error for ConversionError{} impl fmt::Display for ConversionError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "conversion failed: {}", self.0) + write!(f, "conversion of {} failed: {}", self.unit, self.message) } } @@ -51,7 +76,7 @@ pub fn dispatch_out(unit: impl AsRef, value: impl AsRef) -> Result<(), { match unit.as_ref() { bytes::Bytes::UNIT => print_output(bytes::Bytes.convert(value.as_ref())?), - name => return Err(ConversionError(format!("Unknown unit {}", name))), + name => return Err(ConversionError::new(name, "Unknown unit")), } Ok(()) diff --git a/src/main.rs b/src/main.rs index d7c35f5..f871b46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ #![feature(associated_type_defaults)] +#![cfg_attr(feature="f128-math", feature(f128))] #[macro_use] extern crate lazy_static; -#[macro_export] macro_rules! unwrap { +macro_rules! unwrap { ($err:expr) => { match $err { Ok(v) => v,