├── .github ├── dependabot.yml └── workflows │ ├── ci-version.yml │ └── ci.yml ├── src ├── unit │ ├── unit_type.rs │ ├── rocket_traits.rs │ ├── built_in_trait.rs │ ├── serde_traits.rs │ ├── parse.rs │ └── mod.rs ├── bit │ ├── schemars_traits.rs │ ├── adjusted │ │ ├── schemars_traits.rs │ │ ├── rocket_traits.rs │ │ ├── built_in_traits.rs │ │ ├── serde_traits.rs │ │ └── mod.rs │ ├── rocket_traits.rs │ ├── serde_traits.rs │ ├── constants.rs │ ├── decimal.rs │ ├── parse.rs │ ├── built_in_traits.rs │ └── mod.rs ├── byte │ ├── schemars_traits.rs │ ├── adjusted │ │ ├── schemars_traits.rs │ │ ├── rocket_traits.rs │ │ ├── built_in_traits.rs │ │ ├── serde_traits.rs │ │ └── mod.rs │ ├── rocket_traits.rs │ ├── serde_traits.rs │ ├── constants.rs │ ├── decimal.rs │ ├── parse.rs │ ├── built_in_traits.rs │ └── mod.rs ├── common.rs ├── errors.rs └── lib.rs ├── LICENSE ├── Cargo.toml ├── rustfmt.toml ├── .gitignore ├── tests ├── unit.rs ├── bit.rs └── byte.rs └── README.md /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /src/unit/unit_type.rs: -------------------------------------------------------------------------------- 1 | /// Choose how to find an appropriate unit based on a base of 2 or 10. 2 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 3 | pub enum UnitType { 4 | /// KiB, MiB, ..., etc. 5 | Binary, 6 | /// KB, MB, ..., etc. 7 | Decimal, 8 | /// Use both binary and decimal, choose the closest one. 9 | Both, 10 | } 11 | -------------------------------------------------------------------------------- /src/bit/schemars_traits.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::Cow, string::String}; 2 | 3 | use schemars::{JsonSchema, Schema, SchemaGenerator}; 4 | 5 | use super::Bit; 6 | 7 | impl JsonSchema for Bit { 8 | #[inline] 9 | fn inline_schema() -> bool { 10 | true 11 | } 12 | 13 | #[inline] 14 | fn schema_name() -> Cow<'static, str> { 15 | Cow::Borrowed("Bit") 16 | } 17 | 18 | #[inline] 19 | fn json_schema(generator: &mut SchemaGenerator) -> Schema { 20 | generator.subschema_for::() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/byte/schemars_traits.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::Cow, string::String}; 2 | 3 | use schemars::{JsonSchema, Schema, SchemaGenerator}; 4 | 5 | use super::Byte; 6 | 7 | impl JsonSchema for Byte { 8 | #[inline] 9 | fn inline_schema() -> bool { 10 | true 11 | } 12 | 13 | #[inline] 14 | fn schema_name() -> Cow<'static, str> { 15 | Cow::Borrowed("Byte") 16 | } 17 | 18 | #[inline] 19 | fn json_schema(generator: &mut SchemaGenerator) -> Schema { 20 | generator.subschema_for::() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/bit/adjusted/schemars_traits.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::Cow, string::String}; 2 | 3 | use schemars::{JsonSchema, Schema, SchemaGenerator}; 4 | 5 | use super::AdjustedBit; 6 | 7 | impl JsonSchema for AdjustedBit { 8 | #[inline] 9 | fn inline_schema() -> bool { 10 | true 11 | } 12 | 13 | #[inline] 14 | fn schema_name() -> Cow<'static, str> { 15 | Cow::Borrowed("AdjustedBit") 16 | } 17 | 18 | #[inline] 19 | fn json_schema(generator: &mut SchemaGenerator) -> Schema { 20 | generator.subschema_for::() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/byte/adjusted/schemars_traits.rs: -------------------------------------------------------------------------------- 1 | use alloc::{borrow::Cow, string::String}; 2 | 3 | use schemars::{JsonSchema, Schema, SchemaGenerator}; 4 | 5 | use super::AdjustedByte; 6 | 7 | impl JsonSchema for AdjustedByte { 8 | #[inline] 9 | fn inline_schema() -> bool { 10 | true 11 | } 12 | 13 | #[inline] 14 | fn schema_name() -> Cow<'static, str> { 15 | Cow::Borrowed("AdjustedByte") 16 | } 17 | 18 | #[inline] 19 | fn json_schema(generator: &mut SchemaGenerator) -> Schema { 20 | generator.subschema_for::() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/bit/rocket_traits.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use rocket::{ 4 | form::{self, FromFormField, ValueField}, 5 | request::FromParam, 6 | }; 7 | 8 | use super::Bit; 9 | use crate::ParseError; 10 | 11 | impl<'r> FromParam<'r> for Bit { 12 | type Error = ParseError; 13 | 14 | #[inline] 15 | fn from_param(v: &'r str) -> Result { 16 | Self::from_str(v) 17 | } 18 | } 19 | 20 | impl<'r> FromFormField<'r> for Bit { 21 | #[inline] 22 | fn from_value(v: ValueField<'r>) -> form::Result<'r, Self> { 23 | Ok(Self::from_str(v.value).map_err(form::Error::custom)?) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/byte/rocket_traits.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use rocket::{ 4 | form::{self, FromFormField, ValueField}, 5 | request::FromParam, 6 | }; 7 | 8 | use super::Byte; 9 | use crate::ParseError; 10 | 11 | impl<'r> FromParam<'r> for Byte { 12 | type Error = ParseError; 13 | 14 | #[inline] 15 | fn from_param(v: &'r str) -> Result { 16 | Self::from_str(v) 17 | } 18 | } 19 | 20 | impl<'r> FromFormField<'r> for Byte { 21 | #[inline] 22 | fn from_value(v: ValueField<'r>) -> form::Result<'r, Self> { 23 | Ok(Self::from_str(v.value).map_err(form::Error::custom)?) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/unit/rocket_traits.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use rocket::{ 4 | form::{self, FromFormField, ValueField}, 5 | request::FromParam, 6 | }; 7 | 8 | use super::Unit; 9 | use crate::UnitParseError; 10 | 11 | impl<'r> FromParam<'r> for Unit { 12 | type Error = UnitParseError; 13 | 14 | #[inline] 15 | fn from_param(v: &'r str) -> Result { 16 | Self::from_str(v) 17 | } 18 | } 19 | 20 | impl<'r> FromFormField<'r> for Unit { 21 | #[inline] 22 | fn from_value(v: ValueField<'r>) -> form::Result<'r, Self> { 23 | Ok(Self::from_str(v.value).map_err(form::Error::custom)?) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/bit/adjusted/rocket_traits.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use rocket::{ 4 | form::{self, FromFormField, ValueField}, 5 | request::FromParam, 6 | }; 7 | 8 | use super::AdjustedBit; 9 | use crate::ParseError; 10 | 11 | impl<'r> FromParam<'r> for AdjustedBit { 12 | type Error = ParseError; 13 | 14 | #[inline] 15 | fn from_param(v: &'r str) -> Result { 16 | Self::from_str(v) 17 | } 18 | } 19 | 20 | impl<'r> FromFormField<'r> for AdjustedBit { 21 | #[inline] 22 | fn from_value(v: ValueField<'r>) -> form::Result<'r, Self> { 23 | Ok(Self::from_str(v.value).map_err(form::Error::custom)?) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/byte/adjusted/rocket_traits.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use rocket::{ 4 | form::{self, FromFormField, ValueField}, 5 | request::FromParam, 6 | }; 7 | 8 | use super::AdjustedByte; 9 | use crate::ParseError; 10 | 11 | impl<'r> FromParam<'r> for AdjustedByte { 12 | type Error = ParseError; 13 | 14 | #[inline] 15 | fn from_param(v: &'r str) -> Result { 16 | Self::from_str(v) 17 | } 18 | } 19 | 20 | impl<'r> FromFormField<'r> for AdjustedByte { 21 | #[inline] 22 | fn from_value(v: ValueField<'r>) -> form::Result<'r, Self> { 23 | Ok(Self::from_str(v.value).map_err(form::Error::custom)?) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/unit/built_in_trait.rs: -------------------------------------------------------------------------------- 1 | use core::str::FromStr; 2 | 3 | use super::Unit; 4 | use crate::UnitParseError; 5 | 6 | impl FromStr for Unit { 7 | type Err = UnitParseError; 8 | 9 | /// `ignore_case` is set to `false`; `prefer_byte` is set to `true`. See [`Unit::parse_str`](#method.parse_str). 10 | #[inline] 11 | fn from_str(s: &str) -> Result { 12 | Unit::parse_str(s, false, true) 13 | } 14 | } 15 | 16 | impl From for u128 { 17 | /// See [`Unit::as_bits_u128`](#method.as_bits_u128). 18 | #[inline] 19 | fn from(unit: Unit) -> Self { 20 | unit.as_bits_u128() 21 | } 22 | } 23 | 24 | impl AsRef for Unit { 25 | #[inline] 26 | fn as_ref(&self) -> &str { 27 | self.as_str() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/bit/adjusted/built_in_traits.rs: -------------------------------------------------------------------------------- 1 | use core::str::FromStr; 2 | 3 | use super::{AdjustedBit, Bit, Unit, UnitType}; 4 | use crate::ParseError; 5 | 6 | impl From for AdjustedBit { 7 | /// `unit_type` is set to `UnitType::Both`. See [`Bit::get_appropriate_unit`](./struct.Bit.html#method.get_appropriate_unit). 8 | #[inline] 9 | fn from(value: Bit) -> Self { 10 | value.get_appropriate_unit(UnitType::Both) 11 | } 12 | } 13 | 14 | impl From for f64 { 15 | #[inline] 16 | fn from(value: AdjustedBit) -> Self { 17 | value.get_value() 18 | } 19 | } 20 | 21 | impl From for Unit { 22 | #[inline] 23 | fn from(value: AdjustedBit) -> Self { 24 | value.get_unit() 25 | } 26 | } 27 | 28 | impl From for Bit { 29 | #[inline] 30 | fn from(value: AdjustedBit) -> Self { 31 | value.get_bit() 32 | } 33 | } 34 | 35 | impl FromStr for AdjustedBit { 36 | type Err = ParseError; 37 | 38 | /// * `unit_type` is set to `UnitType::Both`. See [`Bit::get_appropriate_unit`](./struct.Bit.html#method.get_appropriate_unit). 39 | #[inline] 40 | fn from_str(s: &str) -> Result { 41 | Ok(Bit::parse_str(s)?.get_appropriate_unit(UnitType::Both)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/unit/serde_traits.rs: -------------------------------------------------------------------------------- 1 | use core::{fmt, fmt::Formatter, str::FromStr}; 2 | 3 | use serde::{ 4 | self, 5 | de::{Error as DeError, Visitor}, 6 | Deserialize, Deserializer, Serialize, Serializer, 7 | }; 8 | 9 | use super::Unit; 10 | 11 | impl Serialize for Unit { 12 | #[inline] 13 | fn serialize(&self, serializer: S) -> Result 14 | where 15 | S: Serializer, { 16 | serializer.serialize_str(self.as_str()) 17 | } 18 | } 19 | 20 | impl<'de> Deserialize<'de> for Unit { 21 | #[inline] 22 | fn deserialize(deserializer: D) -> Result 23 | where 24 | D: Deserializer<'de>, { 25 | struct MyVisitor; 26 | 27 | impl<'de> Visitor<'de> for MyVisitor { 28 | type Value = Unit; 29 | 30 | #[inline] 31 | fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { 32 | f.write_str("a string such as \"B\", \"KB\" or \"MiB\"") 33 | } 34 | 35 | #[inline] 36 | fn visit_str(self, v: &str) -> Result 37 | where 38 | E: DeError, { 39 | Unit::from_str(v).map_err(DeError::custom) 40 | } 41 | } 42 | 43 | deserializer.deserialize_str(MyVisitor) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "byte-unit" 3 | version = "5.2.0" 4 | authors = ["Magic Len "] 5 | edition = "2021" 6 | rust-version = "1.82" 7 | repository = "https://github.com/magiclen/byte-unit" 8 | homepage = "https://magiclen.org/byte-unit" 9 | keywords = ["byte", "unit", "kb", "mb", "gb"] 10 | categories = ["no-std", "parser-implementations", "value-formatting"] 11 | description = "A library for interacting with units of bytes." 12 | license = "MIT" 13 | include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] 14 | 15 | [dependencies] 16 | utf8-width = "0.1" 17 | 18 | serde = { version = "1", default-features = false, features = ["alloc"], optional = true } 19 | schemars = { version = "1", default-features = false, optional = true } 20 | rocket = { version = "0.5", optional = true } 21 | rust_decimal = { version = "1", default-features = false, optional = true } 22 | 23 | [dev-dependencies] 24 | serde_json = "1" 25 | 26 | [features] 27 | default = ["std", "byte"] 28 | 29 | serde = ["dep:serde"] 30 | schemars = ["dep:schemars"] 31 | rocket = ["dep:rocket", "std"] 32 | rust_decimal = ["dep:rust_decimal"] 33 | 34 | std = ["serde?/std", "schemars?/std", "rust_decimal?/std"] 35 | u128 = [] 36 | byte = ["rust_decimal"] 37 | bit = ["rust_decimal"] 38 | 39 | [package.metadata.docs.rs] 40 | all-features = true 41 | rustdoc-args = ["--cfg", "docsrs_1_92"] 42 | -------------------------------------------------------------------------------- /src/byte/adjusted/built_in_traits.rs: -------------------------------------------------------------------------------- 1 | use core::str::FromStr; 2 | 3 | use super::{AdjustedByte, Byte, Unit, UnitType}; 4 | use crate::ParseError; 5 | 6 | impl From for AdjustedByte { 7 | /// `unit_type` is set to `UnitType::Both`. See [`Byte::get_appropriate_unit`](./struct.Byte.html#method.get_appropriate_unit). 8 | #[inline] 9 | fn from(value: Byte) -> Self { 10 | value.get_appropriate_unit(UnitType::Both) 11 | } 12 | } 13 | 14 | impl From for f64 { 15 | #[inline] 16 | fn from(value: AdjustedByte) -> Self { 17 | value.get_value() 18 | } 19 | } 20 | 21 | impl From for Unit { 22 | #[inline] 23 | fn from(value: AdjustedByte) -> Self { 24 | value.get_unit() 25 | } 26 | } 27 | 28 | impl From for Byte { 29 | #[inline] 30 | fn from(value: AdjustedByte) -> Self { 31 | value.get_byte() 32 | } 33 | } 34 | 35 | impl FromStr for AdjustedByte { 36 | type Err = ParseError; 37 | 38 | /// * `ignore_case` is set to `false`. See [`Byte::parse_str`](./struct.Byte.html#method.parse_str). 39 | /// * `unit_type` is set to `UnitType::Both`. See [`Byte::get_appropriate_unit`](./struct.Byte.html#method.get_appropriate_unit). 40 | #[inline] 41 | fn from_str(s: &str) -> Result { 42 | Ok(Byte::parse_str(s, false)?.get_appropriate_unit(UnitType::Both)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/bit/adjusted/serde_traits.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::{self, Formatter}, 3 | str::FromStr, 4 | }; 5 | 6 | use serde::{ 7 | self, 8 | de::{Error as DeError, Visitor}, 9 | Deserialize, Deserializer, Serialize, Serializer, 10 | }; 11 | 12 | use super::AdjustedBit; 13 | 14 | impl Serialize for AdjustedBit { 15 | #[inline] 16 | fn serialize(&self, serializer: S) -> Result 17 | where 18 | S: Serializer, { 19 | if serializer.is_human_readable() { 20 | serializer.serialize_str(format!("{:#}", self).as_str()) 21 | } else { 22 | serializer.serialize_str(format!("{:-#}", self).as_str()) 23 | } 24 | } 25 | } 26 | 27 | impl<'de> Deserialize<'de> for AdjustedBit { 28 | #[inline] 29 | fn deserialize(deserializer: D) -> Result 30 | where 31 | D: Deserializer<'de>, { 32 | struct MyVisitor; 33 | 34 | impl<'de> Visitor<'de> for MyVisitor { 35 | type Value = AdjustedBit; 36 | 37 | #[inline] 38 | fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { 39 | f.write_str("a string such as \"123\", \"123Kib\", \"50.84 Mb\"") 40 | } 41 | 42 | #[inline] 43 | fn visit_str(self, v: &str) -> Result 44 | where 45 | E: DeError, { 46 | AdjustedBit::from_str(v).map_err(DeError::custom) 47 | } 48 | } 49 | 50 | deserializer.deserialize_str(MyVisitor) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/byte/adjusted/serde_traits.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::{self, Formatter}, 3 | str::FromStr, 4 | }; 5 | 6 | use serde::{ 7 | self, 8 | de::{Error as DeError, Visitor}, 9 | Deserialize, Deserializer, Serialize, Serializer, 10 | }; 11 | 12 | use super::AdjustedByte; 13 | 14 | impl Serialize for AdjustedByte { 15 | #[inline] 16 | fn serialize(&self, serializer: S) -> Result 17 | where 18 | S: Serializer, { 19 | if serializer.is_human_readable() { 20 | serializer.serialize_str(format!("{:#}", self).as_str()) 21 | } else { 22 | serializer.serialize_str(format!("{:-#}", self).as_str()) 23 | } 24 | } 25 | } 26 | 27 | impl<'de> Deserialize<'de> for AdjustedByte { 28 | #[inline] 29 | fn deserialize(deserializer: D) -> Result 30 | where 31 | D: Deserializer<'de>, { 32 | struct MyVisitor; 33 | 34 | impl<'de> Visitor<'de> for MyVisitor { 35 | type Value = AdjustedByte; 36 | 37 | #[inline] 38 | fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { 39 | f.write_str("a string such as \"123\", \"123KiB\", \"50.84 MB\"") 40 | } 41 | 42 | #[inline] 43 | fn visit_str(self, v: &str) -> Result 44 | where 45 | E: DeError, { 46 | AdjustedByte::from_str(v).map_err(DeError::custom) 47 | } 48 | } 49 | 50 | deserializer.deserialize_str(MyVisitor) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # array_width = 60 2 | # attr_fn_like_width = 70 3 | binop_separator = "Front" 4 | blank_lines_lower_bound = 0 5 | blank_lines_upper_bound = 1 6 | brace_style = "PreferSameLine" 7 | # chain_width = 60 8 | color = "Auto" 9 | # comment_width = 100 10 | condense_wildcard_suffixes = true 11 | control_brace_style = "AlwaysSameLine" 12 | empty_item_single_line = true 13 | enum_discrim_align_threshold = 80 14 | error_on_line_overflow = false 15 | error_on_unformatted = false 16 | # fn_call_width = 60 17 | fn_params_layout = "Tall" 18 | fn_single_line = false 19 | force_explicit_abi = true 20 | force_multiline_blocks = false 21 | format_code_in_doc_comments = true 22 | doc_comment_code_block_width = 80 23 | format_generated_files = true 24 | format_macro_matchers = true 25 | format_macro_bodies = true 26 | skip_macro_invocations = [] 27 | format_strings = true 28 | hard_tabs = false 29 | hex_literal_case = "Upper" 30 | imports_indent = "Block" 31 | imports_layout = "Mixed" 32 | indent_style = "Block" 33 | inline_attribute_width = 0 34 | match_arm_blocks = true 35 | match_arm_leading_pipes = "Never" 36 | match_block_trailing_comma = true 37 | max_width = 100 38 | merge_derives = true 39 | imports_granularity = "Crate" 40 | newline_style = "Unix" 41 | normalize_comments = false 42 | normalize_doc_attributes = true 43 | overflow_delimited_expr = true 44 | remove_nested_parens = true 45 | reorder_impl_items = true 46 | reorder_imports = true 47 | group_imports = "StdExternalCrate" 48 | reorder_modules = true 49 | short_array_element_width_threshold = 10 50 | # single_line_if_else_max_width = 50 51 | space_after_colon = true 52 | space_before_colon = false 53 | spaces_around_ranges = false 54 | struct_field_align_threshold = 80 55 | struct_lit_single_line = false 56 | # struct_lit_width = 18 57 | # struct_variant_width = 35 58 | tab_spaces = 4 59 | trailing_comma = "Vertical" 60 | trailing_semicolon = true 61 | type_punctuation_density = "Wide" 62 | use_field_init_shorthand = true 63 | use_small_heuristics = "Max" 64 | use_try_shorthand = true 65 | where_single_line = false 66 | wrap_comments = false -------------------------------------------------------------------------------- /src/bit/serde_traits.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::{self, Formatter}, 3 | str::FromStr, 4 | }; 5 | 6 | use serde::{ 7 | self, 8 | de::{Error as DeError, Unexpected, Visitor}, 9 | Deserialize, Deserializer, Serialize, Serializer, 10 | }; 11 | 12 | use super::Bit; 13 | #[cfg(feature = "u128")] 14 | use super::RONNABIT; 15 | 16 | impl Serialize for Bit { 17 | #[inline] 18 | fn serialize(&self, serializer: S) -> Result 19 | where 20 | S: Serializer, { 21 | if serializer.is_human_readable() { 22 | serializer.serialize_str(format!("{self:#}").as_str()) 23 | } else { 24 | serializer.serialize_u128(self.as_u128()) 25 | } 26 | } 27 | } 28 | 29 | impl<'de> Deserialize<'de> for Bit { 30 | #[inline] 31 | fn deserialize(deserializer: D) -> Result 32 | where 33 | D: Deserializer<'de>, { 34 | struct MyVisitor; 35 | 36 | impl<'de> Visitor<'de> for MyVisitor { 37 | type Value = Bit; 38 | 39 | #[inline] 40 | fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { 41 | f.write_str("a string such as \"123\", \"123Kib\", \"50.84 Mb\", or ")?; 42 | 43 | #[cfg(feature = "u128")] 44 | { 45 | f.write_fmt(format_args!("a positive integer smaller than {RONNABIT}")) 46 | } 47 | 48 | #[cfg(not(feature = "u128"))] 49 | { 50 | f.write_fmt(format_args!( 51 | "a positive integer smaller than {}", 52 | u64::MAX as u128 + 1 53 | )) 54 | } 55 | } 56 | 57 | #[inline] 58 | fn visit_u128(self, v: u128) -> Result 59 | where 60 | E: DeError, { 61 | Bit::from_u128(v).ok_or_else(|| { 62 | DeError::invalid_value(Unexpected::Other(format!("{v}").as_str()), &self) 63 | }) 64 | } 65 | 66 | #[inline] 67 | fn visit_str(self, v: &str) -> Result 68 | where 69 | E: DeError, { 70 | Bit::from_str(v).map_err(DeError::custom) 71 | } 72 | } 73 | 74 | if deserializer.is_human_readable() { 75 | deserializer.deserialize_str(MyVisitor) 76 | } else { 77 | deserializer.deserialize_u128(MyVisitor) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/byte/serde_traits.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::{self, Formatter}, 3 | str::FromStr, 4 | }; 5 | 6 | use serde::{ 7 | self, 8 | de::{Error as DeError, Unexpected, Visitor}, 9 | Deserialize, Deserializer, Serialize, Serializer, 10 | }; 11 | 12 | use super::Byte; 13 | #[cfg(feature = "u128")] 14 | use super::RONNABYTE; 15 | 16 | impl Serialize for Byte { 17 | #[inline] 18 | fn serialize(&self, serializer: S) -> Result 19 | where 20 | S: Serializer, { 21 | if serializer.is_human_readable() { 22 | serializer.serialize_str(format!("{self:#}").as_str()) 23 | } else { 24 | serializer.serialize_u128(self.as_u128()) 25 | } 26 | } 27 | } 28 | 29 | impl<'de> Deserialize<'de> for Byte { 30 | #[inline] 31 | fn deserialize(deserializer: D) -> Result 32 | where 33 | D: Deserializer<'de>, { 34 | struct MyVisitor; 35 | 36 | impl<'de> Visitor<'de> for MyVisitor { 37 | type Value = Byte; 38 | 39 | #[inline] 40 | fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { 41 | f.write_str("a string such as \"123\", \"123KiB\", \"50.84 MB\", or ")?; 42 | 43 | #[cfg(feature = "u128")] 44 | { 45 | f.write_fmt(format_args!("a positive integer smaller than {RONNABYTE}")) 46 | } 47 | 48 | #[cfg(not(feature = "u128"))] 49 | { 50 | f.write_fmt(format_args!( 51 | "a positive integer smaller than {}", 52 | u64::MAX as u128 + 1 53 | )) 54 | } 55 | } 56 | 57 | #[inline] 58 | fn visit_u128(self, v: u128) -> Result 59 | where 60 | E: DeError, { 61 | Byte::from_u128(v).ok_or_else(|| { 62 | DeError::invalid_value(Unexpected::Other(format!("{v}").as_str()), &self) 63 | }) 64 | } 65 | 66 | #[inline] 67 | fn visit_str(self, v: &str) -> Result 68 | where 69 | E: DeError, { 70 | Byte::from_str(v).map_err(DeError::custom) 71 | } 72 | } 73 | 74 | if deserializer.is_human_readable() { 75 | deserializer.deserialize_str(MyVisitor) 76 | } else { 77 | deserializer.deserialize_u128(MyVisitor) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Intellij+all ### 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 4 | 5 | # User-specific stuff 6 | .idea/**/workspace.xml 7 | .idea/**/tasks.xml 8 | .idea/**/usage.statistics.xml 9 | .idea/**/dictionaries 10 | .idea/**/shelf 11 | 12 | # AWS User-specific 13 | .idea/**/aws.xml 14 | 15 | # Generated files 16 | .idea/**/contentModel.xml 17 | 18 | # Sensitive or high-churn files 19 | .idea/**/dataSources/ 20 | .idea/**/dataSources.ids 21 | .idea/**/dataSources.local.xml 22 | .idea/**/sqlDataSources.xml 23 | .idea/**/dynamic.xml 24 | .idea/**/uiDesigner.xml 25 | .idea/**/dbnavigator.xml 26 | 27 | # Gradle 28 | .idea/**/gradle.xml 29 | .idea/**/libraries 30 | 31 | # Gradle and Maven with auto-import 32 | # When using Gradle or Maven with auto-import, you should exclude module files, 33 | # since they will be recreated, and may cause churn. Uncomment if using 34 | # auto-import. 35 | # .idea/artifacts 36 | # .idea/compiler.xml 37 | # .idea/jarRepositories.xml 38 | # .idea/modules.xml 39 | # .idea/*.iml 40 | # .idea/modules 41 | # *.iml 42 | # *.ipr 43 | 44 | # CMake 45 | cmake-build-*/ 46 | 47 | # Mongo Explorer plugin 48 | .idea/**/mongoSettings.xml 49 | 50 | # File-based project format 51 | *.iws 52 | 53 | # IntelliJ 54 | out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Cursive Clojure plugin 63 | .idea/replstate.xml 64 | 65 | # SonarLint plugin 66 | .idea/sonarlint/ 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Editor-based Rest Client 75 | .idea/httpRequests 76 | 77 | # Android studio 3.1+ serialized cache file 78 | .idea/caches/build_file_checksums.ser 79 | 80 | ### Intellij+all Patch ### 81 | # Ignore everything but code style settings and run configurations 82 | # that are supposed to be shared within teams. 83 | 84 | .idea/* 85 | 86 | !.idea/codeStyles 87 | !.idea/runConfigurations 88 | 89 | ### Rust ### 90 | # Generated by Cargo 91 | # will have compiled files and executables 92 | debug/ 93 | target/ 94 | 95 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 96 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 97 | Cargo.lock 98 | 99 | # These are backup files generated by rustfmt 100 | **/*.rs.bk 101 | 102 | # MSVC Windows builds of rustc generate these, which store debugging information 103 | *.pdb 104 | 105 | ### Vim ### 106 | # Swap 107 | [._]*.s[a-v][a-z] 108 | !*.svg # comment out if you don't need vector files 109 | [._]*.sw[a-p] 110 | [._]s[a-rt-v][a-z] 111 | [._]ss[a-gi-z] 112 | [._]sw[a-p] 113 | 114 | # Session 115 | Session.vim 116 | Sessionx.vim 117 | 118 | # Temporary 119 | .netrwhist 120 | *~ 121 | # Auto-generated tag files 122 | tags 123 | # Persistent undo 124 | [._]*.un~ 125 | 126 | ### VisualStudioCode ### 127 | .vscode/* 128 | !.vscode/settings.json 129 | !.vscode/tasks.json 130 | !.vscode/launch.json 131 | !.vscode/extensions.json 132 | !.vscode/*.code-snippets 133 | 134 | # Local History for Visual Studio Code 135 | .history/ 136 | 137 | # Built Visual Studio Code Extensions 138 | *.vsix 139 | 140 | ### VisualStudioCode Patch ### 141 | # Ignore all local history of files 142 | .history 143 | .ionide -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | use core::str::Bytes; 2 | 3 | #[cfg(any(feature = "byte", feature = "bit"))] 4 | use rust_decimal::prelude::*; 5 | 6 | /// # Safety 7 | /// Make sure the input is valid on your own. 8 | pub(crate) unsafe fn get_char_from_bytes(e: u8, mut bytes: Bytes) -> char { 9 | let width = utf8_width::get_width_assume_valid(e); 10 | 11 | let mut char_bytes = [0; 4]; 12 | 13 | char_bytes[0] = e; 14 | 15 | if width > 1 { 16 | for e in char_bytes[1..].iter_mut().take(width - 1) { 17 | *e = bytes.next().unwrap(); 18 | } 19 | } 20 | 21 | char::from_u32_unchecked(u32::from_ne_bytes(char_bytes)) 22 | } 23 | 24 | #[cfg(any(feature = "byte", feature = "bit"))] 25 | #[cfg(feature = "std")] 26 | #[inline] 27 | pub(crate) fn ceil_f64(v: f64) -> f64 { 28 | v.ceil() 29 | } 30 | 31 | #[cfg(any(feature = "byte", feature = "bit"))] 32 | #[cfg(not(feature = "std"))] 33 | #[inline] 34 | pub(crate) fn ceil_f64(v: f64) -> f64 { 35 | debug_assert!(v >= 0.0); 36 | 37 | Decimal::from_f64(v).unwrap().ceil().to_f64().unwrap() 38 | } 39 | 40 | #[cfg(any(feature = "byte", feature = "bit"))] 41 | #[cfg(feature = "std")] 42 | #[inline] 43 | pub(crate) fn ceil_f32(v: f32) -> f32 { 44 | debug_assert!(v >= 0.0); 45 | 46 | v.ceil() 47 | } 48 | 49 | #[cfg(any(feature = "byte", feature = "bit"))] 50 | #[cfg(not(feature = "std"))] 51 | #[inline] 52 | pub(crate) fn ceil_f32(v: f32) -> f32 { 53 | debug_assert!(v >= 0.0); 54 | 55 | Decimal::from_f32(v).unwrap().ceil().to_f32().unwrap() 56 | } 57 | 58 | #[cfg(any(feature = "byte", feature = "bit"))] 59 | #[cfg(feature = "std")] 60 | #[inline] 61 | pub fn round_fractional_part_f64(value: f64, mut precision: usize) -> f64 { 62 | if precision > 16 { 63 | precision = 16; 64 | } else if precision == 0 { 65 | return value.round(); 66 | } 67 | 68 | let scale = 10f64.powi(precision as i32); 69 | 70 | (value * scale).round() / scale 71 | } 72 | 73 | #[cfg(any(feature = "byte", feature = "bit"))] 74 | #[cfg(not(feature = "std"))] 75 | pub fn round_fractional_part_f64(value: f64, mut precision: usize) -> f64 { 76 | debug_assert!(value >= 0.0); 77 | 78 | let value = Decimal::from_f64(value).unwrap(); 79 | 80 | if precision > 16 { 81 | precision = 16; 82 | } else if precision == 0 { 83 | return value.round().to_f64().unwrap(); 84 | } 85 | 86 | let trunc = value.trunc(); 87 | let mut fract = value.fract(); 88 | 89 | let scale = Decimal::from(10u128.pow(precision as u32)); 90 | fract = (fract * scale).round() / scale; 91 | 92 | (trunc + fract).to_f64().unwrap() 93 | } 94 | 95 | #[cfg(any(feature = "byte", feature = "bit"))] 96 | #[inline] 97 | pub(crate) fn is_zero_remainder_decimal( 98 | a: Decimal, 99 | b: Decimal, 100 | precision: usize, 101 | ) -> Option { 102 | debug_assert!(a.is_sign_positive() && b.is_sign_positive()); 103 | debug_assert!(b > Decimal::ZERO); 104 | debug_assert!(precision <= 28); 105 | 106 | let quotient = a / b; 107 | 108 | let quotient_round = { 109 | if precision == 0 { 110 | quotient.round() 111 | } else { 112 | let trunc = quotient.trunc(); 113 | let mut fract = quotient.fract(); 114 | 115 | let scale = Decimal::from(10u128.pow(precision as u32)); 116 | 117 | fract = (fract * scale).round() / scale; 118 | 119 | trunc + fract 120 | } 121 | }; 122 | 123 | if b * quotient_round == a { 124 | Some(quotient_round) 125 | } else { 126 | None 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/bit/constants.rs: -------------------------------------------------------------------------------- 1 | use super::Bit; 2 | #[cfg(feature = "u128")] 3 | use super::RONNABIT; 4 | 5 | /// Constant `Bit`s. 6 | #[rustfmt::skip] 7 | impl Bit { 8 | /// One bit. 9 | pub const BIT: Bit = Bit(1); 10 | 11 | /// 1 Kbit = 103 bits. 12 | pub const KILOBIT: Bit = Bit(1_000); 13 | /// 1 Mbit = 106 bits. 14 | pub const MEGABIT: Bit = Bit(1_000_000); 15 | /// 1 Gbit = 109 bits. 16 | pub const GIGABIT: Bit = Bit(1_000_000_000); 17 | /// 1 Tbit = 1012 bits. 18 | pub const TERABIT: Bit = Bit(1_000_000_000_000); 19 | /// 1 Pbit = 1015 bits. 20 | pub const PETABIT: Bit = Bit(1_000_000_000_000_000); 21 | /// 1 Ebit = 1018 bits. 22 | pub const EXABIT: Bit = Bit(1_000_000_000_000_000_000); 23 | #[cfg(feature = "u128")] 24 | /// 1 Zbit = 1021 bits. 25 | pub const ZETTABIT: Bit = Bit(1_000_000_000_000_000_000_000); 26 | #[cfg(feature = "u128")] 27 | /// 1 Ybit = 1024 bits. 28 | pub const YOTTABIT: Bit = Bit(1_000_000_000_000_000_000_000_000); 29 | 30 | /// 1 Kibit = 210 bits. 31 | pub const KIBIBIT: Bit = Bit(1 << 10); 32 | /// 1 Mibit = 220 bits. 33 | pub const MEBIBIT: Bit = Bit(1 << 20); 34 | /// 1 Gibit = 230 bits. 35 | pub const GIBIBIT: Bit = Bit(1 << 30); 36 | /// 1 Tibit = 240 bits. 37 | pub const TEBIBIT: Bit = Bit(1 << 40); 38 | /// 1 Pibit = 250 bits. 39 | pub const PEBIBIT: Bit = Bit(1 << 50); 40 | /// 1 Eibit = 260 bits. 41 | pub const EXBIBIT: Bit = Bit(1 << 60); 42 | #[cfg(feature = "u128")] 43 | /// 1 Zibit = 270 bits. 44 | pub const ZEBIBIT: Bit = Bit(1 << 70); 45 | #[cfg(feature = "u128")] 46 | /// 1 Yibit = 280 bits. 47 | pub const YOBIBIT: Bit = Bit(1 << 80); 48 | 49 | 50 | /// 1 KB = 8 * 103 bits. 51 | pub const KILOBYTE: Bit = Bit::KILOBIT.mul_8(); 52 | /// 1 MB = 8 * 106 bits. 53 | pub const MEGABYTE: Bit = Bit::MEGABIT.mul_8(); 54 | /// 1 GB = 8 * 109 bits. 55 | pub const GIGABYTE: Bit = Bit::GIGABIT.mul_8(); 56 | /// 1 TB = 8 * 1012 bits. 57 | pub const TERABYTE: Bit = Bit::TERABIT.mul_8(); 58 | /// 1 PB = 8 * 1015 bits. 59 | pub const PETABYTE: Bit = Bit::PETABIT.mul_8(); 60 | /// 1 EB = 8 * 1018 bits. 61 | pub const EXABYTE: Bit = Bit::EXABIT.mul_8(); 62 | #[cfg(feature = "u128")] 63 | /// 1 ZB = 8 * 1021 bits. 64 | pub const ZETTABYTE: Bit = Bit::ZETTABIT.mul_8(); 65 | #[cfg(feature = "u128")] 66 | /// 1 YB = 8 * 1024 bits. 67 | pub const YOTTABYTE: Bit = Bit::YOTTABIT.mul_8(); 68 | 69 | 70 | /// 1 KiB = 213 bits. 71 | pub const KIBIBYTE: Bit = Bit::KIBIBIT.mul_8(); 72 | /// 1 MiB = 223 bits. 73 | pub const MEBIBYTE: Bit = Bit::MEBIBIT.mul_8(); 74 | /// 1 GiB = 233 bits. 75 | pub const GIBIBYTE: Bit = Bit::GIBIBIT.mul_8(); 76 | /// 1 TiB = 243 bits. 77 | pub const TEBIBYTE: Bit = Bit::TEBIBIT.mul_8(); 78 | /// 1 PiB = 253 bits. 79 | pub const PEBIBYTE: Bit = Bit::PEBIBIT.mul_8(); 80 | /// 1 EiB = 263 bits. 81 | pub const EXBIBYTE: Bit = Bit::EXBIBIT.mul_8(); 82 | #[cfg(feature = "u128")] 83 | /// 1 ZiB = 273 bits. 84 | pub const ZEBIBYTE: Bit = Bit::ZEBIBIT.mul_8(); 85 | #[cfg(feature = "u128")] 86 | /// 1 YiB = 283 bits. 87 | pub const YOBIBYTE: Bit = Bit::YOBIBIT.mul_8(); 88 | 89 | /// 0 bit. 90 | pub const MIN: Bit = Bit(0); 91 | /// **1027 - 1** bits if the `u128` feature is enabled, or **264 - 1** otherwise. 92 | pub const MAX: Bit = { 93 | #[cfg(feature = "u128")] 94 | { 95 | Bit(RONNABIT - 1) 96 | } 97 | 98 | #[cfg(not(feature = "u128"))] 99 | { 100 | Bit(u64::MAX) 101 | } 102 | }; 103 | } 104 | -------------------------------------------------------------------------------- /src/byte/constants.rs: -------------------------------------------------------------------------------- 1 | use super::Byte; 2 | #[cfg(feature = "u128")] 3 | use super::RONNABYTE; 4 | 5 | /// Constant `Byte`s. 6 | #[rustfmt::skip] 7 | impl Byte { 8 | /// One byte. 9 | pub const BYTE: Byte = Byte(1); 10 | 11 | /// 1 KB = 103 bytes. 12 | pub const KILOBYTE: Byte = Byte(1_000); 13 | /// 1 MB = 106 bytes. 14 | pub const MEGABYTE: Byte = Byte(1_000_000); 15 | /// 1 GB = 109 bytes. 16 | pub const GIGABYTE: Byte = Byte(1_000_000_000); 17 | /// 1 TB = 1012 bytes. 18 | pub const TERABYTE: Byte = Byte(1_000_000_000_000); 19 | /// 1 PB = 1015 bytes. 20 | pub const PETABYTE: Byte = Byte(1_000_000_000_000_000); 21 | /// 1 EB = 1018 bytes. 22 | pub const EXABYTE: Byte = Byte(1_000_000_000_000_000_000); 23 | #[cfg(feature = "u128")] 24 | /// 1 ZB = 1021 bytes. 25 | pub const ZETTABYTE: Byte = Byte(1_000_000_000_000_000_000_000); 26 | #[cfg(feature = "u128")] 27 | /// 1 YB = 1024 bytes. 28 | pub const YOTTABYTE: Byte = Byte(1_000_000_000_000_000_000_000_000); 29 | 30 | /// 1 KiB = 210 bytes. 31 | pub const KIBIBYTE: Byte = Byte(1 << 10); 32 | /// 1 MiB = 220 bytes. 33 | pub const MEBIBYTE: Byte = Byte(1 << 20); 34 | /// 1 GiB = 230 bytes. 35 | pub const GIBIBYTE: Byte = Byte(1 << 30); 36 | /// 1 TiB = 240 bytes. 37 | pub const TEBIBYTE: Byte = Byte(1 << 40); 38 | /// 1 PiB = 250 bytes. 39 | pub const PEBIBYTE: Byte = Byte(1 << 50); 40 | /// 1 EiB = 260 bytes. 41 | pub const EXBIBYTE: Byte = Byte(1 << 60); 42 | #[cfg(feature = "u128")] 43 | /// 1 ZiB = 270 bytes. 44 | pub const ZEBIBYTE: Byte = Byte(1 << 70); 45 | #[cfg(feature = "u128")] 46 | /// 1 YiB = 280 bytes. 47 | pub const YOBIBYTE: Byte = Byte(1 << 80); 48 | 49 | 50 | /// 1 Kbit = 125 bytes. 51 | pub const KILOBIT: Byte = Byte::KILOBYTE.div_8(); 52 | /// 1 Mbit = 125 * 103 bytes. 53 | pub const MEGABIT: Byte = Byte::MEGABYTE.div_8(); 54 | /// 1 Gbit = 125 * 106 bytes. 55 | pub const GIGABIT: Byte = Byte::GIGABYTE.div_8(); 56 | /// 1 Tbit = 125 * 109 bytes. 57 | pub const TERABIT: Byte = Byte::TERABYTE.div_8(); 58 | /// 1 Pbit = 125 * 1012 bytes. 59 | pub const PETABIT: Byte = Byte::PETABYTE.div_8(); 60 | /// 1 Ebit = 125 * 1015 bytes. 61 | pub const EXABIT: Byte = Byte::EXABYTE.div_8(); 62 | #[cfg(feature = "u128")] 63 | /// 1 Zbit = 125 * 1018 bytes. 64 | pub const ZETTABIT: Byte = Byte::ZETTABYTE.div_8(); 65 | #[cfg(feature = "u128")] 66 | /// 1 Ybit = 125 * 1021 bytes. 67 | pub const YOTTABIT: Byte = Byte::YOTTABYTE.div_8(); 68 | 69 | 70 | /// 1 Kibit = 27 bytes. 71 | pub const KIBIBIT: Byte = Byte::KIBIBYTE.div_8(); 72 | /// 1 Mibit = 217 bytes. 73 | pub const MEBIBIT: Byte = Byte::MEBIBYTE.div_8(); 74 | /// 1 Gibit = 227 bytes. 75 | pub const GIBIBIT: Byte = Byte::GIBIBYTE.div_8(); 76 | /// 1 Tibit = 237 bytes. 77 | pub const TEBIBIT: Byte = Byte::TEBIBYTE.div_8(); 78 | /// 1 Pibit = 247 bytes. 79 | pub const PEBIBIT: Byte = Byte::PEBIBYTE.div_8(); 80 | /// 1 Eibit = 257 bytes. 81 | pub const EXBIBIT: Byte = Byte::EXBIBYTE.div_8(); 82 | #[cfg(feature = "u128")] 83 | /// 1 Zibit = 267 bytes. 84 | pub const ZEBIBIT: Byte = Byte::ZEBIBYTE.div_8(); 85 | #[cfg(feature = "u128")] 86 | /// 1 Yibit = 277 bytes. 87 | pub const YOBIBIT: Byte = Byte::YOBIBYTE.div_8(); 88 | 89 | /// 0 byte. 90 | pub const MIN: Byte = Byte(0); 91 | /// **1027 - 1** bytes if the `u128` feature is enabled, or **264 - 1** otherwise. 92 | pub const MAX: Byte = { 93 | #[cfg(feature = "u128")] 94 | { 95 | Byte(RONNABYTE - 1) 96 | } 97 | 98 | #[cfg(not(feature = "u128"))] 99 | { 100 | Byte(u64::MAX) 101 | } 102 | }; 103 | } 104 | -------------------------------------------------------------------------------- /.github/workflows/ci-version.yml: -------------------------------------------------------------------------------- 1 | name: CI-version 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | tests: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: 17 | - ubuntu-latest 18 | - macos-latest 19 | - windows-latest 20 | toolchain: 21 | - stable 22 | - nightly 23 | features: 24 | - 25 | - --features u128 26 | - --features serde --features schemars 27 | - --features rocket 28 | - --features bit 29 | - --features bit --features serde --features schemars 30 | - --features bit --features rocket 31 | - --features u128 --features serde --features schemars 32 | - --features u128 --features bit 33 | - --features u128 --features bit --features serde --features schemars 34 | - --no-default-features 35 | - --no-default-features --features byte 36 | - --no-default-features --features byte --features serde --features schemars 37 | - --no-default-features --features byte --features rocket 38 | - --no-default-features --features bit 39 | - --no-default-features --features bit --features serde --features schemars 40 | - --no-default-features --features bit --features rocket 41 | - --no-default-features --features byte --features bit 42 | - --no-default-features --features u128 43 | - --no-default-features --features u128 --features byte 44 | - --no-default-features --features u128 --features byte --features serde --features schemars 45 | - --no-default-features --features u128 --features bit 46 | - --no-default-features --features u128 --features bit --features serde --features schemars 47 | - --no-default-features --features u128 --features byte --features bit 48 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 49 | runs-on: ${{ matrix.os }} 50 | steps: 51 | - uses: actions/checkout@v6 52 | - uses: actions-rust-lang/setup-rust-toolchain@v1 53 | with: 54 | toolchain: ${{ matrix.toolchain }} 55 | - run: cargo test --release ${{ matrix.features }} 56 | - run: cargo doc --release ${{ matrix.features }} 57 | 58 | MSRV: 59 | strategy: 60 | fail-fast: false 61 | matrix: 62 | os: 63 | - ubuntu-latest 64 | - macos-latest 65 | - windows-latest 66 | toolchain: 67 | - "1.82" 68 | features: 69 | - 70 | - --features u128 71 | - --features serde --features schemars 72 | - --features rocket 73 | - --features bit 74 | - --features bit --features serde --features schemars 75 | - --features bit --features rocket 76 | - --features u128 --features serde --features schemars 77 | - --features u128 --features bit 78 | - --features u128 --features bit --features serde --features schemars 79 | - --no-default-features 80 | - --no-default-features --features byte 81 | - --no-default-features --features byte --features serde --features schemars 82 | - --no-default-features --features byte --features rocket 83 | - --no-default-features --features bit 84 | - --no-default-features --features bit --features serde --features schemars 85 | - --no-default-features --features bit --features rocket 86 | - --no-default-features --features byte --features bit 87 | - --no-default-features --features u128 88 | - --no-default-features --features u128 --features byte 89 | - --no-default-features --features u128 --features byte --features serde --features schemars 90 | - --no-default-features --features u128 --features bit 91 | - --no-default-features --features u128 --features bit --features serde --features schemars 92 | - --no-default-features --features u128 --features byte --features bit 93 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 94 | runs-on: ${{ matrix.os }} 95 | steps: 96 | - uses: actions/checkout@v6 97 | - uses: actions-rust-lang/setup-rust-toolchain@v1 98 | with: 99 | toolchain: ${{ matrix.toolchain }} 100 | - run: cargo test --release --lib --bins ${{ matrix.features }} -------------------------------------------------------------------------------- /tests/unit.rs: -------------------------------------------------------------------------------- 1 | use byte_unit::Unit; 2 | 3 | #[test] 4 | fn parse_str() { 5 | #[allow(unused_mut, clippy::useless_vec)] 6 | let mut cases = vec![ 7 | (Ok(Unit::Bit) as Result, "", true, false), 8 | (Ok(Unit::B), "", true, true), 9 | (Ok(Unit::Bit), "b", false, false), 10 | (Ok(Unit::B), "b", true, false), 11 | (Err(()), "i", true, false), 12 | (Err(()), "c", true, false), 13 | (Err(()), "bc", true, false), 14 | (Err(()), "bi", true, false), 15 | (Ok(Unit::Bit), "bit", true, false), 16 | (Err(()), "bic", true, false), 17 | (Ok(Unit::Bit), "bits", true, false), 18 | (Err(()), "bitc", true, false), 19 | (Ok(Unit::Kbit), "K", false, false), 20 | (Ok(Unit::KB), "K", false, true), 21 | (Err(()), "Kc", false, true), 22 | (Ok(Unit::Kibit), "Ki", false, false), 23 | (Ok(Unit::KiB), "Ki", false, true), 24 | (Err(()), "Kic", false, true), 25 | (Ok(Unit::Kbit), "Kb", false, false), 26 | (Ok(Unit::Kbit), "kb", false, false), 27 | (Ok(Unit::KB), "Kb", true, false), 28 | (Err(()), "Kbc", true, false), 29 | (Ok(Unit::Kbit), "Kbit", true, false), 30 | (Ok(Unit::Kibit), "Kib", false, false), 31 | (Ok(Unit::KiB), "Kib", true, false), 32 | (Err(()), "Kibc", true, false), 33 | (Ok(Unit::Kibit), "Kibit", true, false), 34 | (Ok(Unit::Kibit), "Kibits", true, false), 35 | (Ok(Unit::KB), "KB", true, false), 36 | (Ok(Unit::KiB), "KiB", true, false), 37 | // M 38 | (Ok(Unit::Mbit), "Mb", false, false), 39 | (Ok(Unit::MB), "Mb", true, false), 40 | (Ok(Unit::Mibit), "Mib", false, false), 41 | (Ok(Unit::MiB), "Mib", true, false), 42 | (Ok(Unit::MB), "MB", true, false), 43 | (Ok(Unit::MiB), "MiB", true, false), 44 | // G 45 | (Ok(Unit::Gbit), "Gb", false, false), 46 | (Ok(Unit::GB), "Gb", true, false), 47 | (Ok(Unit::Gibit), "Gib", false, false), 48 | (Ok(Unit::GiB), "Gib", true, false), 49 | (Ok(Unit::GB), "GB", true, false), 50 | (Ok(Unit::GiB), "GiB", true, false), 51 | // T 52 | (Ok(Unit::Tbit), "Tb", false, false), 53 | (Ok(Unit::TB), "Tb", true, false), 54 | (Ok(Unit::Tibit), "Tib", false, false), 55 | (Ok(Unit::TiB), "Tib", true, false), 56 | (Ok(Unit::TB), "TB", true, false), 57 | (Ok(Unit::TiB), "TiB", true, false), 58 | // P 59 | (Ok(Unit::Pbit), "Pb", false, false), 60 | (Ok(Unit::PB), "Pb", true, false), 61 | (Ok(Unit::Pibit), "Pib", false, false), 62 | (Ok(Unit::PiB), "Pib", true, false), 63 | (Ok(Unit::PB), "PB", true, false), 64 | (Ok(Unit::PiB), "PiB", true, false), 65 | // E 66 | (Ok(Unit::Ebit), "Eb", false, false), 67 | (Ok(Unit::EB), "Eb", true, false), 68 | (Ok(Unit::Eibit), "Eib", false, false), 69 | (Ok(Unit::EiB), "Eib", true, false), 70 | (Ok(Unit::EB), "EB", true, false), 71 | (Ok(Unit::EiB), "EiB", true, false), 72 | ]; 73 | 74 | #[cfg(feature = "u128")] 75 | cases.extend_from_slice(&[ 76 | // Z 77 | (Ok(Unit::Zbit), "Zb", false, false), 78 | (Ok(Unit::ZB), "Zb", true, false), 79 | (Ok(Unit::Zibit), "Zib", false, false), 80 | (Ok(Unit::ZiB), "Zib", true, false), 81 | (Ok(Unit::ZB), "ZB", true, false), 82 | (Ok(Unit::ZiB), "ZiB", true, false), 83 | // Y 84 | (Ok(Unit::Ybit), "Yb", false, false), 85 | (Ok(Unit::YB), "Yb", true, false), 86 | (Ok(Unit::Yibit), "Yib", false, false), 87 | (Ok(Unit::YiB), "Yib", true, false), 88 | (Ok(Unit::YB), "YB", true, false), 89 | (Ok(Unit::YiB), "YiB", true, false), 90 | ]); 91 | 92 | for (i, case) in cases.iter().enumerate() { 93 | let result = Unit::parse_str(case.1, case.2, case.3); 94 | 95 | match case.0 { 96 | Ok(unit) => match result { 97 | Ok(result) => { 98 | assert_eq!(unit, result, "{i}"); 99 | }, 100 | Err(error) => { 101 | panic!("{i}\n{error}"); 102 | }, 103 | }, 104 | Err(_) => { 105 | assert!(result.is_err(), "{i}") 106 | }, 107 | } 108 | } 109 | } 110 | 111 | #[cfg(feature = "serde")] 112 | #[test] 113 | fn tests() { 114 | let cases = [ 115 | ("\"Mb\"", Unit::Mbit), 116 | ("\"Mib\"", Unit::Mibit), 117 | ("\"MB\"", Unit::MB), 118 | ("\"MiB\"", Unit::MiB), 119 | ]; 120 | 121 | for (i, case) in cases.iter().enumerate() { 122 | assert_eq!(case.0, serde_json::to_string(&case.1).unwrap(), "{i}"); 123 | assert_eq!(case.1, serde_json::from_str(case.0).unwrap(), "{i}"); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | rustfmt: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v6 13 | - uses: actions-rust-lang/setup-rust-toolchain@v1 14 | with: 15 | toolchain: nightly 16 | components: rustfmt 17 | - uses: actions-rust-lang/rustfmt@v1 18 | 19 | clippy: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v6 23 | - uses: actions-rust-lang/setup-rust-toolchain@v1 24 | with: 25 | components: clippy 26 | - run: cargo clippy --all-targets --all-features -- -D warnings 27 | 28 | tests: 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | os: 33 | - ubuntu-latest 34 | - macos-latest 35 | - windows-latest 36 | toolchain: 37 | - stable 38 | - nightly 39 | features: 40 | - 41 | - --features u128 42 | - --features serde --features schemars 43 | - --features rocket 44 | - --features bit 45 | - --features bit --features serde --features schemars 46 | - --features bit --features rocket 47 | - --features u128 --features serde --features schemars 48 | - --features u128 --features bit 49 | - --features u128 --features bit --features serde --features schemars 50 | - --no-default-features 51 | - --no-default-features --features byte 52 | - --no-default-features --features byte --features serde --features schemars 53 | - --no-default-features --features byte --features rocket 54 | - --no-default-features --features bit 55 | - --no-default-features --features bit --features serde --features schemars 56 | - --no-default-features --features bit --features rocket 57 | - --no-default-features --features byte --features bit 58 | - --no-default-features --features u128 59 | - --no-default-features --features u128 --features byte 60 | - --no-default-features --features u128 --features byte --features serde --features schemars 61 | - --no-default-features --features u128 --features bit 62 | - --no-default-features --features u128 --features bit --features serde --features schemars 63 | - --no-default-features --features u128 --features byte --features bit 64 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 65 | runs-on: ${{ matrix.os }} 66 | steps: 67 | - uses: actions/checkout@v6 68 | - uses: actions-rust-lang/setup-rust-toolchain@v1 69 | with: 70 | toolchain: ${{ matrix.toolchain }} 71 | - run: cargo test ${{ matrix.features }} 72 | - run: cargo doc ${{ matrix.features }} 73 | 74 | MSRV: 75 | strategy: 76 | fail-fast: false 77 | matrix: 78 | os: 79 | - ubuntu-latest 80 | - macos-latest 81 | - windows-latest 82 | toolchain: 83 | - "1.82" 84 | features: 85 | - 86 | - --features u128 87 | - --features serde --features schemars 88 | - --features rocket 89 | - --features bit 90 | - --features bit --features serde --features schemars 91 | - --features bit --features rocket 92 | - --features u128 --features serde --features schemars 93 | - --features u128 --features bit 94 | - --features u128 --features bit --features serde --features schemars 95 | - --no-default-features 96 | - --no-default-features --features byte 97 | - --no-default-features --features byte --features serde --features schemars 98 | - --no-default-features --features byte --features rocket 99 | - --no-default-features --features bit 100 | - --no-default-features --features bit --features serde --features schemars 101 | - --no-default-features --features bit --features rocket 102 | - --no-default-features --features byte --features bit 103 | - --no-default-features --features u128 104 | - --no-default-features --features u128 --features byte 105 | - --no-default-features --features u128 --features byte --features serde --features schemars 106 | - --no-default-features --features u128 --features bit 107 | - --no-default-features --features u128 --features bit --features serde --features schemars 108 | - --no-default-features --features u128 --features byte --features bit 109 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 110 | runs-on: ${{ matrix.os }} 111 | steps: 112 | - uses: actions/checkout@v6 113 | - uses: actions-rust-lang/setup-rust-toolchain@v1 114 | with: 115 | toolchain: ${{ matrix.toolchain }} 116 | - run: cargo test --lib --bins ${{ matrix.features }} -------------------------------------------------------------------------------- /src/bit/decimal.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::prelude::*; 2 | 3 | use super::Bit; 4 | use crate::{common::is_zero_remainder_decimal, Unit}; 5 | 6 | /// Associated functions for building `Bit` instances using `Decimal`. 7 | impl Bit { 8 | /// Create a new `Bit` instance from a size in bits. 9 | /// 10 | /// # Examples 11 | /// 12 | /// ``` 13 | /// use byte_unit::Bit; 14 | /// use rust_decimal::Decimal; 15 | /// 16 | /// let bit = Bit::from_decimal(Decimal::from(15000000u64)).unwrap(); // 15 Mb 17 | /// ``` 18 | /// 19 | /// # Points to Note 20 | /// 21 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) or not greater than or equal to **0**, this function will return `None`. 22 | /// * The fractional part will be rounded up. 23 | #[inline] 24 | pub fn from_decimal(size: Decimal) -> Option { 25 | if size >= Decimal::ZERO { 26 | #[cfg(feature = "u128")] 27 | { 28 | let size = size.ceil(); 29 | 30 | match size.to_u128() { 31 | Some(n) => Self::from_u128(n), 32 | None => None, 33 | } 34 | } 35 | 36 | #[cfg(not(feature = "u128"))] 37 | { 38 | let size = size.ceil(); 39 | 40 | size.to_u64().map(Self::from_u64) 41 | } 42 | } else { 43 | None 44 | } 45 | } 46 | } 47 | 48 | /// Associated functions for building `Bit` instances using `Decimal` (with `Unit`). 49 | impl Bit { 50 | /// Create a new `Bit` instance from a size of bits with a unit. 51 | /// 52 | /// # Examples 53 | /// 54 | /// ``` 55 | /// use byte_unit::{Bit, Unit}; 56 | /// use rust_decimal::Decimal; 57 | /// 58 | /// let bit = Bit::from_decimal_with_unit(Decimal::from(15u64), Unit::Mbit).unwrap(); // 15 Mb 59 | /// ``` 60 | /// 61 | /// # Points to Note 62 | /// 63 | /// * If the calculated bit is too large or not greater than or equal to **0**, this function will return `None`. 64 | /// * The calculated bit will be rounded up. 65 | #[inline] 66 | pub fn from_decimal_with_unit(size: Decimal, unit: Unit) -> Option { 67 | let v = { 68 | match unit { 69 | Unit::Bit => size, 70 | _ => size.checked_mul(Decimal::from(unit.as_bits_u128()))?, 71 | } 72 | }; 73 | 74 | Self::from_decimal(v) 75 | } 76 | } 77 | 78 | /// Methods for finding an unit using `Decimal`. 79 | impl Bit { 80 | /// Find the appropriate unit and value that can be used to recover back to this `Bit` precisely. 81 | /// 82 | /// # Examples 83 | /// 84 | /// ``` 85 | /// use byte_unit::{Bit, Unit}; 86 | /// 87 | /// let bit = Bit::from_u64(3670016); 88 | /// 89 | /// assert_eq!( 90 | /// (3.5f64.try_into().unwrap(), Unit::Mibit), 91 | /// bit.get_recoverable_unit(false, 3) 92 | /// ); 93 | /// ``` 94 | /// 95 | /// ``` 96 | /// use byte_unit::{Bit, Unit}; 97 | /// 98 | /// let bit = Bit::from_u64(28000000); 99 | /// 100 | /// assert_eq!( 101 | /// (3.5f64.try_into().unwrap(), Unit::MB), 102 | /// bit.get_recoverable_unit(true, 3) 103 | /// ); 104 | /// ``` 105 | /// 106 | /// ``` 107 | /// use byte_unit::{Bit, Unit}; 108 | /// 109 | /// let bit = Bit::from_u64(437500); 110 | /// 111 | /// assert_eq!( 112 | /// (437.5f64.try_into().unwrap(), Unit::Kbit), 113 | /// bit.get_recoverable_unit(false, 3) 114 | /// ); 115 | /// ``` 116 | /// 117 | /// # Points to Note 118 | /// 119 | /// * `precision` should be smaller or equal to `26` if the `u128` feature is enabled, otherwise `19`. The typical `precision` is `3`. 120 | #[inline] 121 | pub fn get_recoverable_unit( 122 | self, 123 | allow_in_bytes: bool, 124 | mut precision: usize, 125 | ) -> (Decimal, Unit) { 126 | let bits_v = self.as_u128(); 127 | let bits_vd = Decimal::from(bits_v); 128 | 129 | let a = if allow_in_bytes { Unit::get_multiples() } else { Unit::get_multiples_bits() }; 130 | let mut i = a.len() - 1; 131 | 132 | if precision >= 28 { 133 | precision = 28; 134 | } 135 | 136 | loop { 137 | let unit = a[i]; 138 | 139 | let unit_v = unit.as_bits_u128(); 140 | 141 | if bits_v >= unit_v { 142 | let unit_vd = Decimal::from(unit_v); 143 | 144 | if let Some(quotient) = is_zero_remainder_decimal(bits_vd, unit_vd, precision) { 145 | return (quotient, unit); 146 | } 147 | } 148 | 149 | if i == 0 { 150 | break; 151 | } 152 | 153 | i -= 1; 154 | } 155 | 156 | (bits_vd, Unit::Bit) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/byte/decimal.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::prelude::*; 2 | 3 | use super::Byte; 4 | use crate::{common::is_zero_remainder_decimal, Unit}; 5 | 6 | const DECIMAL_EIGHT: Decimal = Decimal::from_parts(8, 0, 0, false, 0); 7 | 8 | /// Associated functions for building `Byte` instances using `Decimal`. 9 | impl Byte { 10 | /// Create a new `Byte` instance from a size in bytes. 11 | /// 12 | /// # Examples 13 | /// 14 | /// ``` 15 | /// use byte_unit::Byte; 16 | /// use rust_decimal::Decimal; 17 | /// 18 | /// let byte = Byte::from_decimal(Decimal::from(15000000u64)).unwrap(); // 15 MB 19 | /// ``` 20 | /// 21 | /// # Points to Note 22 | /// 23 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) or not greater than or equal to **0**, this function will return `None`. 24 | /// * The fractional part will be rounded up. 25 | #[inline] 26 | pub fn from_decimal(size: Decimal) -> Option { 27 | if size >= Decimal::ZERO { 28 | #[cfg(feature = "u128")] 29 | { 30 | let size = size.ceil(); 31 | 32 | match size.to_u128() { 33 | Some(n) => Self::from_u128(n), 34 | None => None, 35 | } 36 | } 37 | 38 | #[cfg(not(feature = "u128"))] 39 | { 40 | let size = size.ceil(); 41 | 42 | size.to_u64().map(Self::from_u64) 43 | } 44 | } else { 45 | None 46 | } 47 | } 48 | } 49 | 50 | /// Associated functions for building `Byte` instances using `Decimal` (with `Unit`). 51 | impl Byte { 52 | /// Create a new `Byte` instance from a size of bytes with a unit. 53 | /// 54 | /// # Examples 55 | /// 56 | /// ``` 57 | /// use byte_unit::{Byte, Unit}; 58 | /// use rust_decimal::Decimal; 59 | /// 60 | /// let byte = Byte::from_decimal_with_unit(Decimal::from(15u64), Unit::MB).unwrap(); // 15 MB 61 | /// ``` 62 | /// 63 | /// # Points to Note 64 | /// 65 | /// * If the calculated byte is too large or not greater than or equal to **0**, this function will return `None`. 66 | /// * The calculated byte will be rounded up. 67 | #[inline] 68 | pub fn from_decimal_with_unit(size: Decimal, unit: Unit) -> Option { 69 | let v = { 70 | match unit { 71 | Unit::Bit => (size / DECIMAL_EIGHT).ceil(), 72 | Unit::B => size, 73 | _ => size.checked_mul(Decimal::from(unit.as_bytes_u128()))?, 74 | } 75 | }; 76 | 77 | Self::from_decimal(v) 78 | } 79 | } 80 | 81 | /// Methods for finding an unit using `Decimal`. 82 | impl Byte { 83 | /// Find the appropriate unit and value that can be used to recover back to this `Byte` precisely. 84 | /// 85 | /// # Examples 86 | /// 87 | /// ``` 88 | /// use byte_unit::{Byte, Unit}; 89 | /// 90 | /// let byte = Byte::from_u64(3670016); 91 | /// 92 | /// assert_eq!( 93 | /// (3.5f64.try_into().unwrap(), Unit::MiB), 94 | /// byte.get_recoverable_unit(false, 3) 95 | /// ); 96 | /// ``` 97 | /// 98 | /// ``` 99 | /// use byte_unit::{Byte, Unit}; 100 | /// 101 | /// let byte = Byte::from_u64(437500); 102 | /// 103 | /// assert_eq!( 104 | /// (3.5f64.try_into().unwrap(), Unit::Mbit), 105 | /// byte.get_recoverable_unit(true, 3) 106 | /// ); 107 | /// ``` 108 | /// 109 | /// ``` 110 | /// use byte_unit::{Byte, Unit}; 111 | /// 112 | /// let byte = Byte::from_u64(437500); 113 | /// 114 | /// assert_eq!( 115 | /// (437.5f64.try_into().unwrap(), Unit::KB), 116 | /// byte.get_recoverable_unit(false, 3) 117 | /// ); 118 | /// ``` 119 | /// 120 | /// # Points to Note 121 | /// 122 | /// * `precision` should be smaller or equal to `26` if the `u128` feature is enabled, otherwise `19`. The typical `precision` is `3`. 123 | #[inline] 124 | pub fn get_recoverable_unit( 125 | self, 126 | allow_in_bits: bool, 127 | mut precision: usize, 128 | ) -> (Decimal, Unit) { 129 | let bytes_v = self.as_u128(); 130 | let bytes_vd = Decimal::from(bytes_v); 131 | 132 | let a = if allow_in_bits { Unit::get_multiples() } else { Unit::get_multiples_bytes() }; 133 | let mut i = a.len() - 1; 134 | 135 | if precision >= 28 { 136 | precision = 28; 137 | } 138 | 139 | loop { 140 | let unit = a[i]; 141 | 142 | let unit_v = unit.as_bytes_u128(); 143 | 144 | if bytes_v >= unit_v { 145 | let unit_vd = Decimal::from(unit_v); 146 | 147 | if let Some(quotient) = is_zero_remainder_decimal(bytes_vd, unit_vd, precision) { 148 | return (quotient, unit); 149 | } 150 | } 151 | 152 | if i == 0 { 153 | break; 154 | } 155 | 156 | i -= 1; 157 | } 158 | 159 | (bytes_vd, Unit::B) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/bit/parse.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::prelude::*; 2 | 3 | use super::Bit; 4 | use crate::{common::get_char_from_bytes, unit::parse::read_xib, ParseError, ValueParseError}; 5 | 6 | /// Associated functions for parsing strings. 7 | impl Bit { 8 | /// Create a new `Bit` instance from a string. 9 | /// The string may be `"10"`, `"10B"`, `"10M"`, `"10MB"`, `"10MiB"`, `"80b"`, `"80Mb"`, `"80Mbit"`. 10 | /// 11 | /// You can ignore the case of **"B"** (bit), which means **b** will still be treated as bits instead of bits. 12 | /// 13 | /// # Examples 14 | /// 15 | /// ``` 16 | /// # use byte_unit::Bit; 17 | /// let bit = Bit::parse_str("123Kib").unwrap(); // 123 * 1024 bits 18 | /// ``` 19 | pub fn parse_str>(s: S) -> Result { 20 | let s = s.as_ref().trim(); 21 | 22 | let mut bits = s.bytes(); 23 | 24 | let mut value = match bits.next() { 25 | Some(e) => match e { 26 | b'0'..=b'9' => Decimal::from(e - b'0'), 27 | _ => { 28 | return Err(ValueParseError::NotNumber(unsafe { 29 | get_char_from_bytes(e, bits) 30 | }) 31 | .into()); 32 | }, 33 | }, 34 | None => return Err(ValueParseError::NoValue.into()), 35 | }; 36 | 37 | let e = 'outer: loop { 38 | match bits.next() { 39 | Some(e) => match e { 40 | b'0'..=b'9' => { 41 | value = value 42 | .checked_mul(Decimal::TEN) 43 | .ok_or(ValueParseError::NumberTooLong)? 44 | .checked_add(Decimal::from(e - b'0')) 45 | .ok_or(ValueParseError::NumberTooLong)?; 46 | }, 47 | b'.' => { 48 | let mut i = 1u32; 49 | 50 | loop { 51 | match bits.next() { 52 | Some(e) => match e { 53 | b'0'..=b'9' => { 54 | value += { 55 | let mut d = Decimal::from(e - b'0'); 56 | 57 | d.set_scale(i) 58 | .map_err(|_| ValueParseError::NumberTooLong)?; 59 | 60 | d 61 | }; 62 | 63 | i += 1; 64 | }, 65 | _ => { 66 | if i == 1 { 67 | return Err(ValueParseError::NotNumber(unsafe { 68 | get_char_from_bytes(e, bits) 69 | }) 70 | .into()); 71 | } 72 | 73 | match e { 74 | b' ' => loop { 75 | match bits.next() { 76 | Some(e) => match e { 77 | b' ' => (), 78 | _ => break 'outer Some(e), 79 | }, 80 | None => break 'outer None, 81 | } 82 | }, 83 | _ => break 'outer Some(e), 84 | } 85 | }, 86 | }, 87 | None => { 88 | if i == 1 { 89 | return Err(ValueParseError::NotNumber(unsafe { 90 | get_char_from_bytes(e, bits) 91 | }) 92 | .into()); 93 | } 94 | 95 | break 'outer None; 96 | }, 97 | } 98 | } 99 | }, 100 | b' ' => loop { 101 | match bits.next() { 102 | Some(e) => match e { 103 | b' ' => (), 104 | _ => break 'outer Some(e), 105 | }, 106 | None => break 'outer None, 107 | } 108 | }, 109 | _ => break 'outer Some(e), 110 | }, 111 | None => break None, 112 | } 113 | }; 114 | 115 | let unit = read_xib(e, bits, false, false)?; 116 | 117 | Self::from_decimal_with_unit(value, unit) 118 | .ok_or_else(|| ValueParseError::ExceededBounds(value).into()) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Display, Formatter}; 2 | #[cfg(any(feature = "byte", feature = "bit"))] 3 | pub use core::num::TryFromIntError; 4 | #[cfg(feature = "std")] 5 | use std::error::Error; 6 | 7 | #[cfg(any(feature = "byte", feature = "bit"))] 8 | use rust_decimal::Decimal; 9 | 10 | #[cfg(any(feature = "byte", feature = "bit"))] 11 | /// The error type returned when it exceeds representation range. 12 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 13 | pub struct ExceededBoundsError; 14 | 15 | #[cfg(any(feature = "byte", feature = "bit"))] 16 | impl Display for ExceededBoundsError { 17 | #[inline] 18 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 19 | f.write_str("value exceeds the valid range") 20 | } 21 | } 22 | 23 | #[cfg(any(feature = "byte", feature = "bit"))] 24 | #[cfg(feature = "std")] 25 | impl Error for ExceededBoundsError {} 26 | 27 | #[cfg(any(feature = "byte", feature = "bit"))] 28 | /// The error type returned when parsing values. 29 | #[derive(Debug, Clone)] 30 | pub enum ValueParseError { 31 | ExceededBounds(Decimal), 32 | NotNumber(char), 33 | NoValue, 34 | NumberTooLong, 35 | } 36 | 37 | #[cfg(any(feature = "byte", feature = "bit"))] 38 | impl Display for ValueParseError { 39 | #[inline] 40 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 41 | match self { 42 | Self::ExceededBounds(value) => { 43 | f.write_fmt(format_args!("the value {value:?} exceeds the valid range")) 44 | }, 45 | Self::NotNumber(c) => f.write_fmt(format_args!("the character {c:?} is not a number")), 46 | Self::NoValue => f.write_str("no value can be found"), 47 | Self::NumberTooLong => f.write_str("value number is too long"), 48 | } 49 | } 50 | } 51 | 52 | #[cfg(any(feature = "byte", feature = "bit"))] 53 | #[cfg(feature = "std")] 54 | impl Error for ValueParseError {} 55 | 56 | /// The error type returned when parsing units. 57 | #[derive(Debug, Clone)] 58 | pub struct UnitParseError { 59 | pub character: char, 60 | pub expected_characters: &'static [char], 61 | pub also_expect_no_character: bool, 62 | } 63 | 64 | impl Display for UnitParseError { 65 | #[inline] 66 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 67 | let Self { 68 | character, 69 | expected_characters, 70 | also_expect_no_character, 71 | } = self; 72 | 73 | let expected_characters_length = expected_characters.len(); 74 | 75 | f.write_fmt(format_args!("the character {character:?} is incorrect",))?; 76 | 77 | if expected_characters_length == 0 { 78 | f.write_str(" (no character is expected)") 79 | } else { 80 | f.write_fmt(format_args!( 81 | " ({expected_character:?}", 82 | expected_character = expected_characters[0] 83 | ))?; 84 | 85 | if expected_characters_length > 1 { 86 | for expected_character in 87 | expected_characters[1..].iter().take(expected_characters_length - 2) 88 | { 89 | f.write_fmt(format_args!(", {expected_character:?}"))?; 90 | } 91 | 92 | if *also_expect_no_character { 93 | f.write_fmt(format_args!( 94 | ", {expected_character:?} or no character", 95 | expected_character = expected_characters[expected_characters_length - 1] 96 | ))?; 97 | } else { 98 | f.write_fmt(format_args!( 99 | " or {expected_character:?} is expected)", 100 | expected_character = expected_characters[expected_characters_length - 1] 101 | ))?; 102 | } 103 | } 104 | 105 | if *also_expect_no_character { 106 | f.write_str(" or no character")?; 107 | } 108 | 109 | f.write_str(" is expected)") 110 | } 111 | } 112 | } 113 | 114 | #[cfg(feature = "std")] 115 | impl Error for UnitParseError {} 116 | 117 | #[cfg(any(feature = "byte", feature = "bit"))] 118 | /// The error type returned when parsing values with a unit. 119 | #[derive(Debug, Clone)] 120 | pub enum ParseError { 121 | Value(ValueParseError), 122 | Unit(UnitParseError), 123 | } 124 | 125 | #[cfg(any(feature = "byte", feature = "bit"))] 126 | impl From for ParseError { 127 | #[inline] 128 | fn from(error: ValueParseError) -> Self { 129 | Self::Value(error) 130 | } 131 | } 132 | 133 | #[cfg(any(feature = "byte", feature = "bit"))] 134 | impl From for ParseError { 135 | #[inline] 136 | fn from(error: UnitParseError) -> Self { 137 | Self::Unit(error) 138 | } 139 | } 140 | 141 | #[cfg(any(feature = "byte", feature = "bit"))] 142 | impl Display for ParseError { 143 | #[inline] 144 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 145 | match self { 146 | ParseError::Value(error) => Display::fmt(error, f), 147 | ParseError::Unit(error) => Display::fmt(error, f), 148 | } 149 | } 150 | } 151 | 152 | #[cfg(any(feature = "byte", feature = "bit"))] 153 | #[cfg(feature = "std")] 154 | impl Error for ParseError {} 155 | -------------------------------------------------------------------------------- /tests/bit.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "bit")] 2 | 3 | use byte_unit::{Bit, Unit, UnitType}; 4 | use rust_decimal::prelude::*; 5 | 6 | #[test] 7 | fn parse_str() { 8 | #[allow(unused_mut, clippy::useless_vec)] 9 | let mut cases = vec![ 10 | (Err(()) as Result, ""), 11 | (Ok(Bit::from_u64(0)), "0"), 12 | (Err(()), "-0"), 13 | (Err(()), "b"), 14 | (Err(()), "i"), 15 | (Err(()), "c"), 16 | (Ok(Bit::from_u64(0)), "0b"), 17 | (Ok(Bit::from_u64(1)), "1"), 18 | (Err(()), "-1"), 19 | (Ok(Bit::from_u64(1)), "1b"), 20 | (Ok(Bit::from_u64(8)), "1B"), 21 | (Err(()), "1i"), 22 | (Err(()), "1c"), 23 | (Err(()), "1bc"), 24 | (Ok(Bit::from_u64(1)), "1 b"), 25 | (Ok(Bit::from_u64(1)), "1 b"), 26 | (Ok(Bit::from_u64(1)), "1 b"), 27 | (Ok(Bit::from_u64(2)), "1.1"), 28 | (Ok(Bit::from_u64(2)), "1.1b"), 29 | (Ok(Bit::from_u64(0)), "0kb"), 30 | (Ok(Bit::from_u64(1200)), "1.2kb"), 31 | (Ok(Bit::from_u64(9600)), "1.2kB"), 32 | (Ok(Bit::from_u64(2048)), "2 kib"), 33 | (Ok(Bit::from_u64(76650000)), "76.65 Mb"), 34 | (Ok(Bit::from_u64(16000000000000000000)), "16 Eb"), 35 | (Ok(Bit::from_u64(18446744073709551615)), "18446744073709551615"), 36 | ]; 37 | 38 | #[cfg(feature = "u128")] 39 | cases.extend_from_slice(&[ 40 | (Ok(Bit::from_u128(u64::MAX as u128 + 1).unwrap()), "16 Eib"), 41 | (Ok(Bit::from_u128(16000000000000000000000000).unwrap()), "16 Yb"), 42 | (Ok(Bit::from_u128(999000000000000000000000000).unwrap()), "999 Yb"), 43 | (Err(()), "1000 Yb"), 44 | ]); 45 | 46 | #[cfg(not(feature = "u128"))] 47 | cases.extend_from_slice(&[(Err(()), "16 Eib")]); 48 | 49 | for (i, case) in cases.iter().enumerate() { 50 | let result = Bit::parse_str(case.1); 51 | 52 | match case.0 { 53 | Ok(unit) => match result { 54 | Ok(result) => { 55 | assert_eq!(unit, result, "{i}"); 56 | }, 57 | Err(error) => { 58 | panic!("{i}\n{error}"); 59 | }, 60 | }, 61 | Err(_) => { 62 | assert!(result.is_err(), "{i}") 63 | }, 64 | } 65 | } 66 | } 67 | 68 | #[test] 69 | fn exact_unit() { 70 | #[allow(unused_mut, clippy::useless_vec)] 71 | let mut cases = vec![ 72 | ((0, Unit::Bit), "0"), 73 | ((1, Unit::Bit), "1"), 74 | ((123456, Unit::Bit), "123456b"), 75 | ((1, Unit::Tbit), "1T"), 76 | ((1, Unit::Tibit), "1Ti"), 77 | ((10, Unit::Tbit), "10T"), 78 | ((10, Unit::Tibit), "10Ti"), 79 | ((125, Unit::TiB), "1000Ti"), 80 | ((1, Unit::Pibit), "1024Ti"), 81 | ]; 82 | 83 | #[cfg(feature = "u128")] 84 | cases.extend_from_slice(&[((1, Unit::Yibit), "1Yi")]); 85 | 86 | for (i, case) in cases.iter().enumerate() { 87 | assert_eq!(case.0, Bit::parse_str(case.1).unwrap().get_exact_unit(true), "{i}"); 88 | } 89 | } 90 | 91 | #[test] 92 | fn recoverable_unit() { 93 | #[allow(unused_mut, clippy::useless_vec)] 94 | let mut cases = vec![ 95 | ((0.0, Unit::Bit), "0"), 96 | ((1.0, Unit::Bit), "1"), 97 | ((15.432, Unit::KB), "123456b"), 98 | ((1.0, Unit::Tbit), "1T"), 99 | ((1.0, Unit::Tibit), "1Ti"), 100 | ((1.25, Unit::TB), "10T"), 101 | ((1.25, Unit::TiB), "10Ti"), 102 | ((125.0, Unit::TiB), "1000Ti"), 103 | ((1.0, Unit::Pibit), "1024Ti"), 104 | ]; 105 | 106 | #[cfg(feature = "u128")] 107 | cases.extend_from_slice(&[((1.0, Unit::Yibit), "1Yi")]); 108 | 109 | for (i, case) in cases.iter().enumerate() { 110 | assert_eq!( 111 | (Decimal::from_f64(case.0 .0).unwrap(), case.0 .1), 112 | Bit::parse_str(case.1).unwrap().get_recoverable_unit(true, 3), 113 | "{i}" 114 | ); 115 | } 116 | } 117 | 118 | #[test] 119 | fn adjusted_unit() { 120 | #[allow(unused_mut, clippy::useless_vec)] 121 | let mut cases = vec![ 122 | ("15 Mb", 15000000.0, UnitType::Decimal), 123 | ("15.000001 Mb", 15000001.0, UnitType::Decimal), 124 | ("14.30511474609375 Mib", 15000000.0, UnitType::Binary), 125 | ("14.30511474609375 Mib", 15000000.0, UnitType::Both), 126 | ]; 127 | 128 | #[cfg(feature = "u128")] 129 | cases.extend_from_slice(&[("100 Yb", 100000000000000000000000000.0, UnitType::Decimal)]); 130 | 131 | for (i, case) in cases.iter().enumerate() { 132 | assert_eq!( 133 | case.0, 134 | Bit::from_f64(case.1).unwrap().get_appropriate_unit(case.2).to_string(), 135 | "{i}" 136 | ); 137 | } 138 | } 139 | 140 | #[cfg(feature = "serde")] 141 | #[test] 142 | fn tests() { 143 | let cases = [ 144 | ("\"1 Mb\"", (1.0, Unit::Mbit)), 145 | ("\"2 Mib\"", (2.0, Unit::Mibit)), 146 | ("\"5.5 Kib\"", (5.5, Unit::Kibit)), 147 | ("\"1.234 Mb\"", (1.234, Unit::Mbit)), 148 | ("\"1234.5 Kb\"", (1.2345, Unit::Mbit)), 149 | ]; 150 | 151 | for (i, case) in cases.iter().enumerate() { 152 | let bit = Bit::from_f64_with_unit(case.1 .0, case.1 .1).unwrap(); 153 | 154 | assert_eq!(case.0, serde_json::to_string(&bit).unwrap(), "{i}"); 155 | assert_eq!(bit, serde_json::from_str::(case.0).unwrap(), "{i}"); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/byte/parse.rs: -------------------------------------------------------------------------------- 1 | use rust_decimal::prelude::*; 2 | 3 | use super::Byte; 4 | use crate::{common::get_char_from_bytes, unit::parse::read_xib, ParseError, ValueParseError}; 5 | 6 | /// Associated functions for parsing strings. 7 | impl Byte { 8 | /// Create a new `Byte` instance from a string. 9 | /// The string may be `"10"`, `"10B"`, `"10M"`, `"10MB"`, `"10MiB"`, `"80b"`, `"80Mb"`, `"80Mbit"`. 10 | /// 11 | /// You can ignore the case of **"B"** (byte), which means **b** will still be treated as bytes instead of bits. 12 | /// 13 | /// # Examples 14 | /// 15 | /// ``` 16 | /// # use byte_unit::Byte; 17 | /// let byte = Byte::parse_str("123Kib", true).unwrap(); // 123 * 1024 bytes 18 | /// ``` 19 | /// 20 | /// ``` 21 | /// # use byte_unit::Byte; 22 | /// let byte = Byte::parse_str("123Kib", false).unwrap(); // 123 * 1024 bits = 123 * 1024 / 8 bytes 23 | /// ``` 24 | pub fn parse_str>(s: S, ignore_case: bool) -> Result { 25 | let s = s.as_ref().trim(); 26 | 27 | let mut bytes = s.bytes(); 28 | 29 | let mut value = match bytes.next() { 30 | Some(e) => match e { 31 | b'0'..=b'9' => Decimal::from(e - b'0'), 32 | _ => { 33 | return Err(ValueParseError::NotNumber(unsafe { 34 | get_char_from_bytes(e, bytes) 35 | }) 36 | .into()); 37 | }, 38 | }, 39 | None => return Err(ValueParseError::NoValue.into()), 40 | }; 41 | 42 | let e = 'outer: loop { 43 | match bytes.next() { 44 | Some(e) => match e { 45 | b'0'..=b'9' => { 46 | value = value 47 | .checked_mul(Decimal::TEN) 48 | .ok_or(ValueParseError::NumberTooLong)? 49 | .checked_add(Decimal::from(e - b'0')) 50 | .ok_or(ValueParseError::NumberTooLong)?; 51 | }, 52 | b'.' => { 53 | let mut i = 1u32; 54 | 55 | loop { 56 | match bytes.next() { 57 | Some(e) => match e { 58 | b'0'..=b'9' => { 59 | value += { 60 | let mut d = Decimal::from(e - b'0'); 61 | 62 | d.set_scale(i) 63 | .map_err(|_| ValueParseError::NumberTooLong)?; 64 | 65 | d 66 | }; 67 | 68 | i += 1; 69 | }, 70 | _ => { 71 | if i == 1 { 72 | return Err(ValueParseError::NotNumber(unsafe { 73 | get_char_from_bytes(e, bytes) 74 | }) 75 | .into()); 76 | } 77 | 78 | match e { 79 | b' ' => loop { 80 | match bytes.next() { 81 | Some(e) => match e { 82 | b' ' => (), 83 | _ => break 'outer Some(e), 84 | }, 85 | None => break 'outer None, 86 | } 87 | }, 88 | _ => break 'outer Some(e), 89 | } 90 | }, 91 | }, 92 | None => { 93 | if i == 1 { 94 | return Err(ValueParseError::NotNumber(unsafe { 95 | get_char_from_bytes(e, bytes) 96 | }) 97 | .into()); 98 | } 99 | 100 | break 'outer None; 101 | }, 102 | } 103 | } 104 | }, 105 | b' ' => loop { 106 | match bytes.next() { 107 | Some(e) => match e { 108 | b' ' => (), 109 | _ => break 'outer Some(e), 110 | }, 111 | None => break 'outer None, 112 | } 113 | }, 114 | _ => break 'outer Some(e), 115 | }, 116 | None => break None, 117 | } 118 | }; 119 | 120 | let unit = read_xib(e, bytes, ignore_case, true)?; 121 | 122 | Self::from_decimal_with_unit(value, unit) 123 | .ok_or_else(|| ValueParseError::ExceededBounds(value).into()) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /tests/byte.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "byte")] 2 | 3 | use byte_unit::{Byte, Unit, UnitType}; 4 | use rust_decimal::prelude::*; 5 | 6 | #[test] 7 | fn parse_str() { 8 | #[allow(unused_mut, clippy::useless_vec)] 9 | let mut cases = vec![ 10 | (Err(()) as Result, "", false), 11 | (Ok(Byte::from_u64(0)), "0", false), 12 | (Err(()), "-0", false), 13 | (Err(()), "b", false), 14 | (Err(()), "i", false), 15 | (Err(()), "c", false), 16 | (Ok(Byte::from_u64(0)), "0b", false), 17 | (Ok(Byte::from_u64(1)), "1", false), 18 | (Err(()), "-1", false), 19 | (Ok(Byte::from_u64(1)), "1b", false), 20 | (Ok(Byte::from_u64(1)), "1b", true), 21 | (Err(()), "1i", false), 22 | (Err(()), "1c", false), 23 | (Err(()), "1bc", false), 24 | (Ok(Byte::from_u64(1)), "1 b", true), 25 | (Ok(Byte::from_u64(1)), "1 b", true), 26 | (Ok(Byte::from_u64(1)), "1 b", true), 27 | (Ok(Byte::from_u64(2)), "1.1", false), 28 | (Ok(Byte::from_u64(1)), "1.1b", false), 29 | (Ok(Byte::from_u64(2)), "1.1b", true), 30 | (Ok(Byte::from_u64(0)), "0kb", false), 31 | (Ok(Byte::from_u64(150)), "1.2kb", false), 32 | (Ok(Byte::from_u64(1200)), "1.2kb", true), 33 | (Ok(Byte::from_u64(2048)), "2 kiB", true), 34 | (Ok(Byte::from_u64(76650000)), "76.65 MB", false), 35 | (Ok(Byte::from_u64(16000000000000000000)), "16 EB", false), 36 | (Ok(Byte::from_u64(18446744073709551615)), "18446744073709551615", false), 37 | ]; 38 | 39 | #[cfg(feature = "u128")] 40 | cases.extend_from_slice(&[ 41 | (Ok(Byte::from_u128(u64::MAX as u128 + 1).unwrap()), "16 EiB", false), 42 | (Ok(Byte::from_u128(16000000000000000000000000).unwrap()), "16 YB", false), 43 | (Ok(Byte::from_u128(999000000000000000000000000).unwrap()), "999 YB", false), 44 | (Err(()), "1000 YB", false), 45 | ]); 46 | 47 | #[cfg(not(feature = "u128"))] 48 | cases.extend_from_slice(&[(Err(()), "16 EiB", false)]); 49 | 50 | for (i, case) in cases.iter().enumerate() { 51 | let result = Byte::parse_str(case.1, case.2); 52 | 53 | match case.0 { 54 | Ok(unit) => match result { 55 | Ok(result) => { 56 | assert_eq!(unit, result, "{i}"); 57 | }, 58 | Err(error) => { 59 | panic!("{i}\n{error}"); 60 | }, 61 | }, 62 | Err(_) => { 63 | assert!(result.is_err(), "{i}") 64 | }, 65 | } 66 | } 67 | } 68 | 69 | #[test] 70 | fn exact_unit() { 71 | #[allow(unused_mut, clippy::useless_vec)] 72 | let mut cases = vec![ 73 | ((0, Unit::B), "0"), 74 | ((1, Unit::B), "1"), 75 | ((123456, Unit::B), "123456B"), 76 | ((1, Unit::TB), "1T"), 77 | ((1, Unit::TiB), "1Ti"), 78 | ((10, Unit::TB), "10T"), 79 | ((10, Unit::TiB), "10Ti"), 80 | ((1000, Unit::TiB), "1000Ti"), 81 | ((1, Unit::PiB), "1024Ti"), 82 | ]; 83 | 84 | #[cfg(feature = "u128")] 85 | cases.extend_from_slice(&[((1, Unit::YiB), "1Yi")]); 86 | 87 | for (i, case) in cases.iter().enumerate() { 88 | assert_eq!(case.0, Byte::parse_str(case.1, true).unwrap().get_exact_unit(true), "{i}"); 89 | } 90 | } 91 | 92 | #[test] 93 | fn recoverable_unit() { 94 | #[allow(unused_mut, clippy::useless_vec)] 95 | let mut cases = vec![ 96 | ((0.0, Unit::B), "0"), 97 | ((1.0, Unit::B), "1"), 98 | ((123.456, Unit::KB), "123456B"), 99 | ((1.0, Unit::TB), "1T"), 100 | ((1.0, Unit::TiB), "1Ti"), 101 | ((10.0, Unit::TB), "10T"), 102 | ((10.0, Unit::TiB), "10Ti"), 103 | ((1000.0, Unit::TiB), "1000Ti"), 104 | ((1.0, Unit::PiB), "1024Ti"), 105 | ]; 106 | 107 | #[cfg(feature = "u128")] 108 | cases.extend_from_slice(&[((1.0, Unit::YiB), "1Yi")]); 109 | 110 | for (i, case) in cases.iter().enumerate() { 111 | assert_eq!( 112 | (Decimal::from_f64(case.0 .0).unwrap(), case.0 .1), 113 | Byte::parse_str(case.1, true).unwrap().get_recoverable_unit(true, 3), 114 | "{i}" 115 | ); 116 | } 117 | } 118 | 119 | #[test] 120 | fn adjusted_unit() { 121 | #[allow(unused_mut, clippy::useless_vec)] 122 | let mut cases = vec![ 123 | ("15 MB", 15000000.0, UnitType::Decimal), 124 | ("15.000001 MB", 15000001.0, UnitType::Decimal), 125 | ("14.30511474609375 MiB", 15000000.0, UnitType::Binary), 126 | ("14.30511474609375 MiB", 15000000.0, UnitType::Both), 127 | ]; 128 | 129 | #[cfg(feature = "u128")] 130 | cases.extend_from_slice(&[("100 YB", 100000000000000000000000000.0, UnitType::Decimal)]); 131 | 132 | for (i, case) in cases.iter().enumerate() { 133 | assert_eq!( 134 | case.0, 135 | Byte::from_f64(case.1).unwrap().get_appropriate_unit(case.2).to_string(), 136 | "{i}" 137 | ); 138 | } 139 | } 140 | 141 | #[cfg(feature = "serde")] 142 | #[test] 143 | fn tests() { 144 | let cases = [ 145 | ("\"1 MB\"", (1.0, Unit::MB)), 146 | ("\"2 MiB\"", (2.0, Unit::MiB)), 147 | ("\"5.5 KiB\"", (5.5, Unit::KiB)), 148 | ("\"1.234 MB\"", (1.234, Unit::MB)), 149 | ("\"1234.5 KB\"", (1.2345, Unit::MB)), 150 | ]; 151 | 152 | for (i, case) in cases.iter().enumerate() { 153 | let byte = Byte::from_f64_with_unit(case.1 .0, case.1 .1).unwrap(); 154 | 155 | assert_eq!(case.0, serde_json::to_string(&byte).unwrap(), "{i}"); 156 | assert_eq!(byte, serde_json::from_str::(case.0).unwrap(), "{i}"); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Byte Unit 2 | ==================== 3 | 4 | [![CI](https://github.com/magiclen/Byte-Unit/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/Byte-Unit/actions/workflows/ci.yml) 5 | 6 | A library for interaction with units of bytes. 7 | 8 | The units are **B** for 1 byte, **KB** for 1000 bytes, **MiB** for 1048576 bytes, **GB** for 1000000000 bytes, etc, and up to **E** or **Y** (if the `u128` feature is enabled). 9 | 10 | ## Usage 11 | 12 | The data types for storing the size in bits/bytes are `u64` by default, meaning the highest supported unit is up to **E**. If the `u128` feature is enabled, the data types will use `u128`, increasing the highest supported unit up to **Y**. 13 | 14 | ### Unit 15 | 16 | The enum `Unit` can be used for representing the unit of bits/bytes. 17 | 18 | ```rust 19 | use byte_unit::Unit; 20 | 21 | assert_eq!("KB", Unit::KB.as_str()); 22 | assert_eq!("MiB", Unit::MiB.as_str()); 23 | 24 | assert_eq!(Unit::KB, Unit::parse_str("K", true, true).unwrap()); 25 | assert_eq!(Unit::Kbit, Unit::parse_str("K", true, false).unwrap()); 26 | 27 | assert_eq!(Unit::KB, Unit::parse_str("KB", true, true).unwrap()); 28 | assert_eq!(Unit::KB, Unit::parse_str("Kb", true, true).unwrap()); 29 | assert_eq!(Unit::Kbit, Unit::parse_str("Kbit", true, true).unwrap()); 30 | 31 | assert_eq!(Unit::KB, Unit::parse_str("KB", false, true).unwrap()); 32 | assert_eq!(Unit::Kbit, Unit::parse_str("Kb", false, true).unwrap()); 33 | ``` 34 | 35 | ### Byte 36 | 37 | The `Byte` struct can be used for representing a size in bytes. 38 | 39 | The `from_*` associated functions can be used to create a `Byte` instance from different data types. The `as_*` methods can retrieve the size as a primitive type. 40 | 41 | ```rust 42 | use byte_unit::{Byte, Unit}; 43 | 44 | assert_eq!(15000, Byte::from_u64(15000).as_u64()); 45 | assert_eq!(15000, Byte::from_u64_with_unit(15, Unit::KB).unwrap().as_u64()); 46 | ``` 47 | 48 | You can also parse a string to create a `Byte` instance. 49 | 50 | ```rust 51 | use byte_unit::Byte; 52 | 53 | assert_eq!(50840000, Byte::parse_str("50.84 MB", true).unwrap().as_u64()); 54 | ``` 55 | 56 | A `Byte` instance can be formatted to string precisely. For more detailed usage, please refer to the implementation documentation of `Display::fmt` for `Byte`. 57 | 58 | ```rust 59 | use byte_unit::Byte; 60 | 61 | let byte = Byte::from_u64(15500); 62 | 63 | assert_eq!("15500", byte.to_string()); 64 | 65 | assert_eq!("15.5 KB", format!("{byte:#}")); 66 | assert_eq!("15500 B", format!("{byte:#.0}")); 67 | ``` 68 | 69 | #### Arithmetic 70 | 71 | There are `add`, `subtract`, `multiply`, and `divide` methods. 72 | 73 | ```rust 74 | use byte_unit::Byte; 75 | 76 | let a = Byte::from_u64(15500); 77 | let b = Byte::from_u64(500); 78 | 79 | assert_eq!(16000, a.add(b).unwrap().as_u64()); 80 | assert_eq!(15000, a.subtract(b).unwrap().as_u64()); 81 | 82 | assert_eq!(31000, a.multiply(2).unwrap().as_u64()); 83 | assert_eq!(3100, a.divide(5).unwrap().as_u64()); 84 | ``` 85 | 86 | #### Find Out an Appropriate Unit 87 | 88 | The `get_exact_unit` and `get_recoverable_unit` methods is useful if you want to find out a unit that is appropriate for a `Byte` instance. 89 | 90 | ```rust 91 | use byte_unit::{Byte, Unit}; 92 | 93 | let byte = Byte::from_u64(50840000); 94 | 95 | assert_eq!((50840, Unit::KB), byte.get_exact_unit(false)); 96 | assert_eq!((50.84f64.try_into().unwrap(), Unit::MB), byte.get_recoverable_unit(false, 2)); 97 | assert_eq!((50840.into(), Unit::KB), byte.get_recoverable_unit(false, 0)); 98 | ``` 99 | 100 | #### AdjustedByte 101 | 102 | The `AdjustedByte` struct can be used for roughly representing a size of bytes with a unit. 103 | 104 | To change the unit of a `Byte` instance, you can use the `get_adjusted_unit` method. 105 | 106 | An `AdjustedByte` instance can be formatted to string. For more detailed usage, please refer to the implementation documentation of `Display::fmt` for `AdjustedByte`. 107 | 108 | ```rust 109 | use byte_unit::{Byte, Unit}; 110 | 111 | let byte = Byte::parse_str("123KiB", true).unwrap(); 112 | 113 | let adjusted_byte = byte.get_adjusted_unit(Unit::KB); 114 | 115 | assert_eq!("125.952 KB", adjusted_byte.to_string()); 116 | assert_eq!("125.95 KB", format!("{adjusted_byte:.2}")); 117 | ``` 118 | 119 | The `get_appropriate_unit` method can be used to automatically find an appropriate unit for creating an `AdjustedByte` instance. 120 | 121 | ```rust 122 | use byte_unit::{Byte, Unit, UnitType}; 123 | 124 | let byte = Byte::from_u64(1500000); 125 | 126 | let adjusted_byte = byte.get_appropriate_unit(UnitType::Binary); 127 | 128 | assert_eq!("1.43 MiB", format!("{adjusted_byte:.2}")); 129 | ``` 130 | 131 | ### Bit 132 | 133 | The `Bit` struct can be used for representing a size in bits. 134 | 135 | The `bit` feature must be enabled. 136 | 137 | Usage of the `Bit` struct and the `Byte` struct is very similar. Also, There is the `AdjustedBit` struct. The difference lies in the fact that the `parse_str` method of the `Bit` struct cannot be configured to ignore case; it always does not ignore case. 138 | 139 | ```rust 140 | use byte_unit::{Bit, Unit}; 141 | 142 | let bit = Bit::parse_str("123Kib").unwrap(); 143 | 144 | let adjusted_bit = bit.get_adjusted_unit(Unit::Kbit); 145 | 146 | assert_eq!("125.952 Kb", adjusted_bit.to_string()); 147 | assert_eq!("125.95 Kb", format!("{adjusted_bit:.2}")); 148 | ``` 149 | 150 | ## No Std 151 | 152 | Disable the default features to compile this crate without std. 153 | 154 | ```toml 155 | [dependencies.byte-unit] 156 | version = "*" 157 | default-features = false 158 | features = ["byte"] 159 | ``` 160 | 161 | ## Serde Support 162 | 163 | Enable the `serde` feature to support the serde framework. 164 | 165 | ```toml 166 | [dependencies.byte-unit] 167 | version = "*" 168 | features = ["serde"] 169 | ``` 170 | 171 | ## Schemars Support 172 | 173 | Enable the `schemars` feature to support Schemars. Probably the `serde` feature is also needed. 174 | 175 | ```toml 176 | [dependencies.byte-unit] 177 | version = "*" 178 | features = ["serde", "schemars"] 179 | ``` 180 | 181 | ## Rocket Support 182 | 183 | Enable the `rocket` feature to support the Rocket framework. 184 | 185 | ```toml 186 | [dependencies.byte-unit] 187 | version = "*" 188 | features = ["rocket"] 189 | ``` 190 | 191 | ## Crates.io 192 | 193 | https://crates.io/crates/byte-unit 194 | 195 | ## Documentation 196 | 197 | https://docs.rs/byte-unit 198 | 199 | ## License 200 | 201 | [MIT](LICENSE) 202 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Byte Unit 3 | 4 | A library for interaction with units of bytes. 5 | 6 | The units are **B** for 1 byte, **KB** for 1000 bytes, **MiB** for 1048576 bytes, **GB** for 1000000000 bytes, etc, and up to **E** or **Y** (if the `u128` feature is enabled). 7 | 8 | ## Usage 9 | 10 | The data types for storing the size in bits/bytes are `u64` by default, meaning the highest supported unit is up to **E**. If the `u128` feature is enabled, the data types will use `u128`, increasing the highest supported unit up to **Y**. 11 | 12 | ### Unit 13 | 14 | The enum `Unit` can be used for representing the unit of bits/bytes. 15 | 16 | ```rust 17 | use byte_unit::Unit; 18 | 19 | assert_eq!("KB", Unit::KB.as_str()); 20 | assert_eq!("MiB", Unit::MiB.as_str()); 21 | 22 | assert_eq!(Unit::KB, Unit::parse_str("K", true, true).unwrap()); 23 | assert_eq!(Unit::Kbit, Unit::parse_str("K", true, false).unwrap()); 24 | 25 | assert_eq!(Unit::KB, Unit::parse_str("KB", true, true).unwrap()); 26 | assert_eq!(Unit::KB, Unit::parse_str("Kb", true, true).unwrap()); 27 | assert_eq!(Unit::Kbit, Unit::parse_str("Kbit", true, true).unwrap()); 28 | 29 | assert_eq!(Unit::KB, Unit::parse_str("KB", false, true).unwrap()); 30 | assert_eq!(Unit::Kbit, Unit::parse_str("Kb", false, true).unwrap()); 31 | ``` 32 | 33 | ### Byte 34 | 35 | The `Byte` struct can be used for representing a size in bytes. 36 | 37 | The `from_*` associated functions can be used to create a `Byte` instance from different data types. The `as_*` methods can retrieve the size as a primitive type. 38 | 39 | ```rust 40 | # #[cfg(feature = "byte")] 41 | # { 42 | use byte_unit::{Byte, Unit}; 43 | 44 | assert_eq!(15000, Byte::from_u64(15000).as_u64()); 45 | assert_eq!(15000, Byte::from_u64_with_unit(15, Unit::KB).unwrap().as_u64()); 46 | # } 47 | ``` 48 | 49 | You can also parse a string to create a `Byte` instance. 50 | 51 | ```rust 52 | # #[cfg(feature = "byte")] 53 | # { 54 | use byte_unit::Byte; 55 | 56 | assert_eq!(50840000, Byte::parse_str("50.84 MB", true).unwrap().as_u64()); 57 | # } 58 | ``` 59 | 60 | A `Byte` instance can be formatted to string precisely. For more detailed usage, please refer to the implementation documentation of `Display::fmt` for `Byte`. 61 | 62 | ```rust 63 | # #[cfg(feature = "byte")] 64 | # { 65 | use byte_unit::Byte; 66 | 67 | let byte = Byte::from_u64(15500); 68 | 69 | assert_eq!("15500", byte.to_string()); 70 | 71 | assert_eq!("15.5 KB", format!("{byte:#}")); 72 | assert_eq!("15500 B", format!("{byte:#.0}")); 73 | # } 74 | ``` 75 | 76 | #### Arithmetic 77 | 78 | There are `add`, `subtract`, `multiply`, and `divide` methods. 79 | 80 | ```rust 81 | # #[cfg(feature = "byte")] 82 | # { 83 | use byte_unit::Byte; 84 | 85 | let a = Byte::from_u64(15500); 86 | let b = Byte::from_u64(500); 87 | 88 | assert_eq!(16000, a.add(b).unwrap().as_u64()); 89 | assert_eq!(15000, a.subtract(b).unwrap().as_u64()); 90 | 91 | assert_eq!(31000, a.multiply(2).unwrap().as_u64()); 92 | assert_eq!(3100, a.divide(5).unwrap().as_u64()); 93 | # } 94 | ``` 95 | 96 | #### Find Out an Appropriate Unit 97 | 98 | The `get_exact_unit` and `get_recoverable_unit` methods is useful if you want to find out a unit that is appropriate for a `Byte` instance. 99 | 100 | ```rust 101 | # #[cfg(feature = "byte")] 102 | # { 103 | use byte_unit::{Byte, Unit}; 104 | 105 | let byte = Byte::from_u64(50840000); 106 | 107 | assert_eq!((50840, Unit::KB), byte.get_exact_unit(false)); 108 | assert_eq!((50.84f64.try_into().unwrap(), Unit::MB), byte.get_recoverable_unit(false, 2)); 109 | assert_eq!((50840.into(), Unit::KB), byte.get_recoverable_unit(false, 0)); 110 | # } 111 | ``` 112 | 113 | #### AdjustedByte 114 | 115 | The `AdjustedByte` struct can be used for roughly representing a size of bytes with a unit. 116 | 117 | To change the unit of a `Byte` instance, you can use the `get_adjusted_unit` method. 118 | 119 | An `AdjustedByte` instance can be formatted to string. For more detailed usage, please refer to the implementation documentation of `Display::fmt` for `AdjustedByte`. 120 | 121 | ```rust 122 | # #[cfg(feature = "byte")] 123 | # { 124 | use byte_unit::{Byte, Unit}; 125 | 126 | let byte = Byte::parse_str("123KiB", true).unwrap(); 127 | 128 | let adjusted_byte = byte.get_adjusted_unit(Unit::KB); 129 | 130 | assert_eq!("125.952 KB", adjusted_byte.to_string()); 131 | assert_eq!("125.95 KB", format!("{adjusted_byte:.2}")); 132 | # } 133 | ``` 134 | 135 | The `get_appropriate_unit` method can be used to automatically find an appropriate unit for creating an `AdjustedByte` instance. 136 | 137 | ```rust 138 | # #[cfg(feature = "byte")] 139 | # { 140 | use byte_unit::{Byte, Unit, UnitType}; 141 | 142 | let byte = Byte::from_u64(1500000); 143 | 144 | let adjusted_byte = byte.get_appropriate_unit(UnitType::Binary); 145 | 146 | assert_eq!("1.43 MiB", format!("{adjusted_byte:.2}")); 147 | # } 148 | ``` 149 | 150 | ### Bit 151 | 152 | The `Bit` struct can be used for representing a size in bits. 153 | 154 | The `bit` feature must be enabled. 155 | 156 | Usage of the `Bit` struct and the `Byte` struct is very similar. Also, There is the `AdjustedBit` struct. The difference lies in the fact that the `parse_str` method of the `Bit` struct cannot be configured to ignore case; it always does not ignore case. 157 | 158 | ```rust 159 | # #[cfg(feature = "bit")] 160 | # { 161 | use byte_unit::{Bit, Unit}; 162 | 163 | let bit = Bit::parse_str("123Kib").unwrap(); 164 | 165 | let adjusted_bit = bit.get_adjusted_unit(Unit::Kbit); 166 | 167 | assert_eq!("125.952 Kb", adjusted_bit.to_string()); 168 | assert_eq!("125.95 Kb", format!("{adjusted_bit:.2}")); 169 | # } 170 | ``` 171 | 172 | ## No Std 173 | 174 | Disable the default features to compile this crate without std. 175 | 176 | ```toml 177 | [dependencies.byte-unit] 178 | version = "*" 179 | default-features = false 180 | features = ["byte"] 181 | ``` 182 | 183 | ## Serde Support 184 | 185 | Enable the `serde` feature to support the serde framework. 186 | 187 | ```toml 188 | [dependencies.byte-unit] 189 | version = "*" 190 | features = ["serde"] 191 | ``` 192 | 193 | ## Schemars Support 194 | 195 | Enable the `schemars` feature to support Schemars. Probably the `serde` feature is also needed. 196 | 197 | ```toml 198 | [dependencies.byte-unit] 199 | version = "*" 200 | features = ["serde", "schemars"] 201 | ``` 202 | 203 | ## Rocket Support 204 | 205 | Enable the `rocket` feature to support the Rocket framework. 206 | 207 | ```toml 208 | [dependencies.byte-unit] 209 | version = "*" 210 | features = ["rocket"] 211 | ``` 212 | */ 213 | 214 | #![cfg_attr(not(feature = "std"), no_std)] 215 | #![allow(unexpected_cfgs)] 216 | #![cfg_attr(docsrs_1_92, feature(doc_cfg))] 217 | 218 | #[cfg(all(feature = "serde", any(feature = "byte", feature = "bit")))] 219 | #[macro_use] 220 | extern crate alloc; 221 | #[cfg(feature = "rust_decimal")] 222 | pub extern crate rust_decimal; 223 | 224 | #[cfg(feature = "bit")] 225 | mod bit; 226 | #[cfg(feature = "byte")] 227 | mod byte; 228 | mod common; 229 | mod errors; 230 | mod unit; 231 | 232 | #[cfg(feature = "bit")] 233 | pub use bit::*; 234 | #[cfg(feature = "byte")] 235 | pub use byte::*; 236 | pub use errors::*; 237 | pub use unit::*; 238 | -------------------------------------------------------------------------------- /src/bit/built_in_traits.rs: -------------------------------------------------------------------------------- 1 | use core::{cmp::Ordering, str::FromStr}; 2 | 3 | use super::Bit; 4 | use crate::{ExceededBoundsError, ParseError, TryFromIntError}; 5 | 6 | impl TryFrom for Bit { 7 | type Error = ExceededBoundsError; 8 | 9 | #[inline] 10 | fn try_from(value: u128) -> Result { 11 | Bit::from_u128(value).ok_or(ExceededBoundsError) 12 | } 13 | } 14 | 15 | impl From for Bit { 16 | #[inline] 17 | fn from(value: u64) -> Self { 18 | Bit::from_u64(value) 19 | } 20 | } 21 | 22 | impl From for Bit { 23 | #[inline] 24 | fn from(value: u32) -> Self { 25 | Bit::from_u64(value as u64) 26 | } 27 | } 28 | 29 | impl From for Bit { 30 | #[inline] 31 | fn from(value: u16) -> Self { 32 | Bit::from_u64(value as u64) 33 | } 34 | } 35 | 36 | impl From for Bit { 37 | #[inline] 38 | fn from(value: u8) -> Self { 39 | Bit::from_u64(value as u64) 40 | } 41 | } 42 | 43 | impl From for Bit { 44 | #[allow(unexpected_cfgs)] 45 | #[inline] 46 | fn from(value: usize) -> Self { 47 | #[cfg(target_pointer_width = "128")] 48 | { 49 | Byte::from_u128(value as u128).unwrap_or(Byte::MAX) 50 | } 51 | 52 | #[cfg(not(target_pointer_width = "128"))] 53 | { 54 | Bit::from_u64(value as u64) 55 | } 56 | } 57 | } 58 | 59 | impl TryFrom for Bit { 60 | type Error = ExceededBoundsError; 61 | 62 | #[inline] 63 | fn try_from(value: i128) -> Result { 64 | Bit::from_i128(value).ok_or(ExceededBoundsError) 65 | } 66 | } 67 | 68 | impl TryFrom for Bit { 69 | type Error = ExceededBoundsError; 70 | 71 | #[inline] 72 | fn try_from(value: i64) -> Result { 73 | Bit::from_i64(value).ok_or(ExceededBoundsError) 74 | } 75 | } 76 | 77 | impl TryFrom for Bit { 78 | type Error = ExceededBoundsError; 79 | 80 | #[inline] 81 | fn try_from(value: i32) -> Result { 82 | Bit::from_i64(value as i64).ok_or(ExceededBoundsError) 83 | } 84 | } 85 | 86 | impl TryFrom for Bit { 87 | type Error = ExceededBoundsError; 88 | 89 | #[inline] 90 | fn try_from(value: i16) -> Result { 91 | Bit::from_i64(value as i64).ok_or(ExceededBoundsError) 92 | } 93 | } 94 | 95 | impl TryFrom for Bit { 96 | type Error = ExceededBoundsError; 97 | 98 | #[inline] 99 | fn try_from(value: i8) -> Result { 100 | Bit::from_i64(value as i64).ok_or(ExceededBoundsError) 101 | } 102 | } 103 | 104 | impl TryFrom for Bit { 105 | type Error = ExceededBoundsError; 106 | 107 | #[allow(unexpected_cfgs)] 108 | #[inline] 109 | fn try_from(value: isize) -> Result { 110 | #[cfg(target_pointer_width = "128")] 111 | { 112 | Bit::from_i128(value as i128).ok_or(ExceededBoundsError) 113 | } 114 | 115 | #[cfg(not(target_pointer_width = "128"))] 116 | { 117 | Bit::from_i64(value as i64).ok_or(ExceededBoundsError) 118 | } 119 | } 120 | } 121 | 122 | impl TryFrom for Bit { 123 | type Error = ExceededBoundsError; 124 | 125 | #[inline] 126 | fn try_from(value: f64) -> Result { 127 | Bit::from_f64(value).ok_or(ExceededBoundsError) 128 | } 129 | } 130 | 131 | impl TryFrom for Bit { 132 | type Error = ExceededBoundsError; 133 | 134 | #[inline] 135 | fn try_from(value: f32) -> Result { 136 | Bit::from_f32(value).ok_or(ExceededBoundsError) 137 | } 138 | } 139 | 140 | impl From for u128 { 141 | #[inline] 142 | fn from(bit: Bit) -> Self { 143 | bit.as_u128() 144 | } 145 | } 146 | 147 | impl From for u64 { 148 | #[inline] 149 | fn from(bit: Bit) -> Self { 150 | bit.as_u64() 151 | } 152 | } 153 | 154 | impl TryFrom for u32 { 155 | type Error = TryFromIntError; 156 | 157 | #[inline] 158 | fn try_from(bit: Bit) -> Result { 159 | u32::try_from(bit.as_u64()) 160 | } 161 | } 162 | 163 | impl TryFrom for u16 { 164 | type Error = TryFromIntError; 165 | 166 | #[inline] 167 | fn try_from(bit: Bit) -> Result { 168 | u16::try_from(bit.as_u64()) 169 | } 170 | } 171 | 172 | impl TryFrom for u8 { 173 | type Error = TryFromIntError; 174 | 175 | #[inline] 176 | fn try_from(bit: Bit) -> Result { 177 | u8::try_from(bit.as_u64()) 178 | } 179 | } 180 | 181 | impl TryFrom for usize { 182 | type Error = TryFromIntError; 183 | 184 | #[allow(unexpected_cfgs)] 185 | #[inline] 186 | fn try_from(bit: Bit) -> Result { 187 | #[cfg(target_pointer_width = "128")] 188 | { 189 | usize::try_from(bit.as_u128()) 190 | } 191 | 192 | #[cfg(not(target_pointer_width = "128"))] 193 | { 194 | usize::try_from(bit.as_u64()) 195 | } 196 | } 197 | } 198 | 199 | impl FromStr for Bit { 200 | type Err = ParseError; 201 | 202 | #[inline] 203 | fn from_str(s: &str) -> Result { 204 | Bit::parse_str(s) 205 | } 206 | } 207 | 208 | impl PartialEq for Bit { 209 | #[cfg(feature = "u128")] 210 | #[inline] 211 | fn eq(&self, other: &u64) -> bool { 212 | self.0 == *other as u128 213 | } 214 | 215 | #[cfg(not(feature = "u128"))] 216 | #[inline] 217 | fn eq(&self, other: &u64) -> bool { 218 | self.0 == *other 219 | } 220 | } 221 | 222 | impl PartialEq for Bit { 223 | #[cfg(feature = "u128")] 224 | #[inline] 225 | fn eq(&self, other: &u128) -> bool { 226 | self.0 == *other 227 | } 228 | 229 | #[cfg(not(feature = "u128"))] 230 | #[inline] 231 | fn eq(&self, other: &u128) -> bool { 232 | self.0 as u128 == *other 233 | } 234 | } 235 | 236 | impl PartialEq for u64 { 237 | #[cfg(feature = "u128")] 238 | #[inline] 239 | fn eq(&self, other: &Bit) -> bool { 240 | *self as u128 == other.0 241 | } 242 | 243 | #[cfg(not(feature = "u128"))] 244 | #[inline] 245 | fn eq(&self, other: &Bit) -> bool { 246 | *self == other.0 247 | } 248 | } 249 | 250 | impl PartialEq for u128 { 251 | #[cfg(feature = "u128")] 252 | #[inline] 253 | fn eq(&self, other: &Bit) -> bool { 254 | *self == other.0 255 | } 256 | 257 | #[cfg(not(feature = "u128"))] 258 | #[inline] 259 | fn eq(&self, other: &Bit) -> bool { 260 | *self == other.0 as u128 261 | } 262 | } 263 | 264 | impl PartialOrd for Bit { 265 | #[cfg(feature = "u128")] 266 | #[inline] 267 | fn partial_cmp(&self, other: &u64) -> Option { 268 | self.0.partial_cmp(&(*other as u128)) 269 | } 270 | 271 | #[cfg(not(feature = "u128"))] 272 | #[inline] 273 | fn partial_cmp(&self, other: &u64) -> Option { 274 | self.0.partial_cmp(other) 275 | } 276 | } 277 | 278 | impl PartialOrd for Bit { 279 | #[cfg(feature = "u128")] 280 | #[inline] 281 | fn partial_cmp(&self, other: &u128) -> Option { 282 | self.0.partial_cmp(other) 283 | } 284 | 285 | #[cfg(not(feature = "u128"))] 286 | #[inline] 287 | fn partial_cmp(&self, other: &u128) -> Option { 288 | (self.0 as u128).partial_cmp(other) 289 | } 290 | } 291 | 292 | impl PartialOrd for u64 { 293 | #[cfg(feature = "u128")] 294 | #[inline] 295 | fn partial_cmp(&self, other: &Bit) -> Option { 296 | (*self as u128).partial_cmp(&other.0) 297 | } 298 | 299 | #[cfg(not(feature = "u128"))] 300 | #[inline] 301 | fn partial_cmp(&self, other: &Bit) -> Option { 302 | self.partial_cmp(&other.0) 303 | } 304 | } 305 | 306 | impl PartialOrd for u128 { 307 | #[cfg(feature = "u128")] 308 | #[inline] 309 | fn partial_cmp(&self, other: &Bit) -> Option { 310 | self.partial_cmp(&other.0) 311 | } 312 | 313 | #[cfg(not(feature = "u128"))] 314 | #[inline] 315 | fn partial_cmp(&self, other: &Bit) -> Option { 316 | self.partial_cmp(&(other.0 as u128)) 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/byte/built_in_traits.rs: -------------------------------------------------------------------------------- 1 | use core::{cmp::Ordering, str::FromStr}; 2 | 3 | use super::Byte; 4 | use crate::{ExceededBoundsError, ParseError, TryFromIntError}; 5 | 6 | impl TryFrom for Byte { 7 | type Error = ExceededBoundsError; 8 | 9 | #[inline] 10 | fn try_from(value: u128) -> Result { 11 | Byte::from_u128(value).ok_or(ExceededBoundsError) 12 | } 13 | } 14 | 15 | impl From for Byte { 16 | #[inline] 17 | fn from(value: u64) -> Self { 18 | Byte::from_u64(value) 19 | } 20 | } 21 | 22 | impl From for Byte { 23 | #[inline] 24 | fn from(value: u32) -> Self { 25 | Byte::from_u64(value as u64) 26 | } 27 | } 28 | 29 | impl From for Byte { 30 | #[inline] 31 | fn from(value: u16) -> Self { 32 | Byte::from_u64(value as u64) 33 | } 34 | } 35 | 36 | impl From for Byte { 37 | #[inline] 38 | fn from(value: u8) -> Self { 39 | Byte::from_u64(value as u64) 40 | } 41 | } 42 | 43 | impl From for Byte { 44 | #[allow(unexpected_cfgs)] 45 | #[inline] 46 | fn from(value: usize) -> Self { 47 | #[cfg(target_pointer_width = "128")] 48 | { 49 | Byte::from_u128(value as u128).unwrap_or(Byte::MAX) 50 | } 51 | 52 | #[cfg(not(target_pointer_width = "128"))] 53 | { 54 | Byte::from_u64(value as u64) 55 | } 56 | } 57 | } 58 | 59 | impl TryFrom for Byte { 60 | type Error = ExceededBoundsError; 61 | 62 | #[inline] 63 | fn try_from(value: i128) -> Result { 64 | Byte::from_i128(value).ok_or(ExceededBoundsError) 65 | } 66 | } 67 | 68 | impl TryFrom for Byte { 69 | type Error = ExceededBoundsError; 70 | 71 | #[inline] 72 | fn try_from(value: i64) -> Result { 73 | Byte::from_i64(value).ok_or(ExceededBoundsError) 74 | } 75 | } 76 | 77 | impl TryFrom for Byte { 78 | type Error = ExceededBoundsError; 79 | 80 | #[inline] 81 | fn try_from(value: i32) -> Result { 82 | Byte::from_i64(value as i64).ok_or(ExceededBoundsError) 83 | } 84 | } 85 | 86 | impl TryFrom for Byte { 87 | type Error = ExceededBoundsError; 88 | 89 | #[inline] 90 | fn try_from(value: i16) -> Result { 91 | Byte::from_i64(value as i64).ok_or(ExceededBoundsError) 92 | } 93 | } 94 | 95 | impl TryFrom for Byte { 96 | type Error = ExceededBoundsError; 97 | 98 | #[inline] 99 | fn try_from(value: i8) -> Result { 100 | Byte::from_i64(value as i64).ok_or(ExceededBoundsError) 101 | } 102 | } 103 | 104 | impl TryFrom for Byte { 105 | type Error = ExceededBoundsError; 106 | 107 | #[allow(unexpected_cfgs)] 108 | #[inline] 109 | fn try_from(value: isize) -> Result { 110 | #[cfg(target_pointer_width = "128")] 111 | { 112 | Byte::from_i128(value as i128).ok_or(ExceededBoundsError) 113 | } 114 | 115 | #[cfg(not(target_pointer_width = "128"))] 116 | { 117 | Byte::from_i64(value as i64).ok_or(ExceededBoundsError) 118 | } 119 | } 120 | } 121 | 122 | impl TryFrom for Byte { 123 | type Error = ExceededBoundsError; 124 | 125 | #[inline] 126 | fn try_from(value: f64) -> Result { 127 | Byte::from_f64(value).ok_or(ExceededBoundsError) 128 | } 129 | } 130 | 131 | impl TryFrom for Byte { 132 | type Error = ExceededBoundsError; 133 | 134 | #[inline] 135 | fn try_from(value: f32) -> Result { 136 | Byte::from_f32(value).ok_or(ExceededBoundsError) 137 | } 138 | } 139 | 140 | impl From for u128 { 141 | #[inline] 142 | fn from(byte: Byte) -> Self { 143 | byte.as_u128() 144 | } 145 | } 146 | 147 | impl From for u64 { 148 | #[inline] 149 | fn from(byte: Byte) -> Self { 150 | byte.as_u64() 151 | } 152 | } 153 | 154 | impl TryFrom for u32 { 155 | type Error = TryFromIntError; 156 | 157 | #[inline] 158 | fn try_from(byte: Byte) -> Result { 159 | u32::try_from(byte.as_u64()) 160 | } 161 | } 162 | 163 | impl TryFrom for u16 { 164 | type Error = TryFromIntError; 165 | 166 | #[inline] 167 | fn try_from(byte: Byte) -> Result { 168 | u16::try_from(byte.as_u64()) 169 | } 170 | } 171 | 172 | impl TryFrom for u8 { 173 | type Error = TryFromIntError; 174 | 175 | #[inline] 176 | fn try_from(byte: Byte) -> Result { 177 | u8::try_from(byte.as_u64()) 178 | } 179 | } 180 | 181 | impl TryFrom for usize { 182 | type Error = TryFromIntError; 183 | 184 | #[allow(unexpected_cfgs)] 185 | #[inline] 186 | fn try_from(byte: Byte) -> Result { 187 | #[cfg(target_pointer_width = "128")] 188 | { 189 | usize::try_from(byte.as_u128()) 190 | } 191 | 192 | #[cfg(not(target_pointer_width = "128"))] 193 | { 194 | usize::try_from(byte.as_u64()) 195 | } 196 | } 197 | } 198 | 199 | impl FromStr for Byte { 200 | type Err = ParseError; 201 | 202 | /// `ignore_case` is set to `false`. See [`Byte::parse_str`](#method.parse_str). 203 | #[inline] 204 | fn from_str(s: &str) -> Result { 205 | Byte::parse_str(s, false) 206 | } 207 | } 208 | 209 | impl PartialEq for Byte { 210 | #[cfg(feature = "u128")] 211 | #[inline] 212 | fn eq(&self, other: &u64) -> bool { 213 | self.0 == *other as u128 214 | } 215 | 216 | #[cfg(not(feature = "u128"))] 217 | #[inline] 218 | fn eq(&self, other: &u64) -> bool { 219 | self.0 == *other 220 | } 221 | } 222 | 223 | impl PartialEq for Byte { 224 | #[cfg(feature = "u128")] 225 | #[inline] 226 | fn eq(&self, other: &u128) -> bool { 227 | self.0 == *other 228 | } 229 | 230 | #[cfg(not(feature = "u128"))] 231 | #[inline] 232 | fn eq(&self, other: &u128) -> bool { 233 | self.0 as u128 == *other 234 | } 235 | } 236 | 237 | impl PartialEq for u64 { 238 | #[cfg(feature = "u128")] 239 | #[inline] 240 | fn eq(&self, other: &Byte) -> bool { 241 | *self as u128 == other.0 242 | } 243 | 244 | #[cfg(not(feature = "u128"))] 245 | #[inline] 246 | fn eq(&self, other: &Byte) -> bool { 247 | *self == other.0 248 | } 249 | } 250 | 251 | impl PartialEq for u128 { 252 | #[cfg(feature = "u128")] 253 | #[inline] 254 | fn eq(&self, other: &Byte) -> bool { 255 | *self == other.0 256 | } 257 | 258 | #[cfg(not(feature = "u128"))] 259 | #[inline] 260 | fn eq(&self, other: &Byte) -> bool { 261 | *self == other.0 as u128 262 | } 263 | } 264 | 265 | impl PartialOrd for Byte { 266 | #[cfg(feature = "u128")] 267 | #[inline] 268 | fn partial_cmp(&self, other: &u64) -> Option { 269 | self.0.partial_cmp(&(*other as u128)) 270 | } 271 | 272 | #[cfg(not(feature = "u128"))] 273 | #[inline] 274 | fn partial_cmp(&self, other: &u64) -> Option { 275 | self.0.partial_cmp(other) 276 | } 277 | } 278 | 279 | impl PartialOrd for Byte { 280 | #[cfg(feature = "u128")] 281 | #[inline] 282 | fn partial_cmp(&self, other: &u128) -> Option { 283 | self.0.partial_cmp(other) 284 | } 285 | 286 | #[cfg(not(feature = "u128"))] 287 | #[inline] 288 | fn partial_cmp(&self, other: &u128) -> Option { 289 | (self.0 as u128).partial_cmp(other) 290 | } 291 | } 292 | 293 | impl PartialOrd for u64 { 294 | #[cfg(feature = "u128")] 295 | #[inline] 296 | fn partial_cmp(&self, other: &Byte) -> Option { 297 | (*self as u128).partial_cmp(&other.0) 298 | } 299 | 300 | #[cfg(not(feature = "u128"))] 301 | #[inline] 302 | fn partial_cmp(&self, other: &Byte) -> Option { 303 | self.partial_cmp(&other.0) 304 | } 305 | } 306 | 307 | impl PartialOrd for u128 { 308 | #[cfg(feature = "u128")] 309 | #[inline] 310 | fn partial_cmp(&self, other: &Byte) -> Option { 311 | self.partial_cmp(&other.0) 312 | } 313 | 314 | #[cfg(not(feature = "u128"))] 315 | #[inline] 316 | fn partial_cmp(&self, other: &Byte) -> Option { 317 | self.partial_cmp(&(other.0 as u128)) 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/unit/parse.rs: -------------------------------------------------------------------------------- 1 | use core::str::Bytes; 2 | 3 | use super::Unit; 4 | use crate::{common::get_char_from_bytes, UnitParseError}; 5 | 6 | /// Associated functions for parsing strings. 7 | impl Unit { 8 | /// Create a new `Unit` instance from a string. 9 | /// The string may be `""`, `"B"`, `"M"`, `"MB"`, `"MiB"`, `"b"`, `"Mb"`, `"Mbit"`. 10 | /// 11 | /// You can ignore the case of **"B"** (byte), which means **b** will still be treated as bytes instead of bits. 12 | /// 13 | /// If the input string is empty, it will return `B` if `prefer_byte` is true; otherwise, it will return `b`. Similarly, if the string is not empty but it does not explicitly contains `"B"`, `"b"`, or `"bit"`, it will imply the base is `"B"` if `prefer_byte` is true; otherwise, imply the base is `"b"`. 14 | /// 15 | /// # Examples 16 | /// 17 | /// ``` 18 | /// # use byte_unit::Unit; 19 | /// let unit = Unit::parse_str("Kib", true, true).unwrap(); // KiB 20 | /// ``` 21 | /// 22 | /// ``` 23 | /// # use byte_unit::Unit; 24 | /// let unit = Unit::parse_str("Kib", false, true).unwrap(); // Kibit 25 | /// ``` 26 | pub fn parse_str>( 27 | s: S, 28 | ignore_case: bool, 29 | prefer_byte: bool, 30 | ) -> Result { 31 | let s = s.as_ref().trim(); 32 | 33 | let mut bytes = s.bytes(); 34 | 35 | read_xib(bytes.next(), bytes, ignore_case, prefer_byte) 36 | } 37 | } 38 | 39 | pub(crate) fn read_xib( 40 | e: Option, 41 | bytes: Bytes, 42 | ignore_case: bool, 43 | prefer_byte: bool, 44 | ) -> Result { 45 | match e { 46 | Some(e) => match e.to_ascii_uppercase() { 47 | b'B' => { 48 | let byte = read_b(bytes, if ignore_case { true } else { e == b'B' })?; 49 | 50 | if byte { 51 | Ok(Unit::B) 52 | } else { 53 | Ok(Unit::Bit) 54 | } 55 | }, 56 | b'K' => { 57 | let (i, byte) = read_ib(bytes, ignore_case, prefer_byte)?; 58 | 59 | if i { 60 | if byte { 61 | Ok(Unit::KiB) 62 | } else { 63 | Ok(Unit::Kibit) 64 | } 65 | } else if byte { 66 | Ok(Unit::KB) 67 | } else { 68 | Ok(Unit::Kbit) 69 | } 70 | }, 71 | b'M' => { 72 | let (i, byte) = read_ib(bytes, ignore_case, prefer_byte)?; 73 | 74 | if i { 75 | if byte { 76 | Ok(Unit::MiB) 77 | } else { 78 | Ok(Unit::Mibit) 79 | } 80 | } else if byte { 81 | Ok(Unit::MB) 82 | } else { 83 | Ok(Unit::Mbit) 84 | } 85 | }, 86 | b'G' => { 87 | let (i, byte) = read_ib(bytes, ignore_case, prefer_byte)?; 88 | 89 | if i { 90 | if byte { 91 | Ok(Unit::GiB) 92 | } else { 93 | Ok(Unit::Gibit) 94 | } 95 | } else if byte { 96 | Ok(Unit::GB) 97 | } else { 98 | Ok(Unit::Gbit) 99 | } 100 | }, 101 | b'T' => { 102 | let (i, byte) = read_ib(bytes, ignore_case, prefer_byte)?; 103 | 104 | if i { 105 | if byte { 106 | Ok(Unit::TiB) 107 | } else { 108 | Ok(Unit::Tibit) 109 | } 110 | } else if byte { 111 | Ok(Unit::TB) 112 | } else { 113 | Ok(Unit::Tbit) 114 | } 115 | }, 116 | b'P' => { 117 | let (i, byte) = read_ib(bytes, ignore_case, prefer_byte)?; 118 | 119 | if i { 120 | if byte { 121 | Ok(Unit::PiB) 122 | } else { 123 | Ok(Unit::Pibit) 124 | } 125 | } else if byte { 126 | Ok(Unit::PB) 127 | } else { 128 | Ok(Unit::Pbit) 129 | } 130 | }, 131 | b'E' => { 132 | let (i, byte) = read_ib(bytes, ignore_case, prefer_byte)?; 133 | 134 | if i { 135 | if byte { 136 | Ok(Unit::EiB) 137 | } else { 138 | Ok(Unit::Eibit) 139 | } 140 | } else if byte { 141 | Ok(Unit::EB) 142 | } else { 143 | Ok(Unit::Ebit) 144 | } 145 | }, 146 | #[cfg(feature = "u128")] 147 | b'Z' => { 148 | let (i, byte) = read_ib(bytes, ignore_case, prefer_byte)?; 149 | 150 | if i { 151 | if byte { 152 | Ok(Unit::ZiB) 153 | } else { 154 | Ok(Unit::Zibit) 155 | } 156 | } else if byte { 157 | Ok(Unit::ZB) 158 | } else { 159 | Ok(Unit::Zbit) 160 | } 161 | }, 162 | #[cfg(feature = "u128")] 163 | b'Y' => { 164 | let (i, byte) = read_ib(bytes, ignore_case, prefer_byte)?; 165 | 166 | if i { 167 | if byte { 168 | Ok(Unit::YiB) 169 | } else { 170 | Ok(Unit::Yibit) 171 | } 172 | } else if byte { 173 | Ok(Unit::YB) 174 | } else { 175 | Ok(Unit::Ybit) 176 | } 177 | }, 178 | _ => { 179 | #[cfg(feature = "u128")] 180 | { 181 | Err(UnitParseError { 182 | character: unsafe { get_char_from_bytes(e, bytes) }, 183 | expected_characters: &['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 184 | also_expect_no_character: true, 185 | }) 186 | } 187 | #[cfg(not(feature = "u128"))] 188 | { 189 | Err(UnitParseError { 190 | character: unsafe { get_char_from_bytes(e, bytes) }, 191 | expected_characters: &['B', 'K', 'M', 'G', 'T', 'P', 'E'], 192 | also_expect_no_character: true, 193 | }) 194 | } 195 | }, 196 | }, 197 | None => Ok(if prefer_byte { Unit::B } else { Unit::Bit }), 198 | } 199 | } 200 | fn read_ib( 201 | mut bytes: Bytes, 202 | ignore_case: bool, 203 | default_upper_case: bool, 204 | ) -> Result<(bool, bool), UnitParseError> { 205 | match bytes.next() { 206 | Some(mut e) => { 207 | let i = e == b'i' || e == b'I'; 208 | 209 | if i { 210 | match bytes.next() { 211 | Some(ne) => e = ne, 212 | None => return Ok((true, default_upper_case)), 213 | } 214 | } 215 | 216 | match e { 217 | b'b' | b'B' => Ok((i, read_b(bytes, if ignore_case { true } else { e == b'B' })?)), 218 | _ => { 219 | let expected_characters: &[char] = if ignore_case { 220 | if default_upper_case { 221 | &['B'] 222 | } else { 223 | &['b'] 224 | } 225 | } else { 226 | &['B', 'b'] 227 | }; 228 | 229 | Err(UnitParseError { 230 | character: unsafe { get_char_from_bytes(e, bytes) }, 231 | expected_characters, 232 | also_expect_no_character: true, 233 | }) 234 | }, 235 | } 236 | }, 237 | None => Ok((false, default_upper_case)), 238 | } 239 | } 240 | 241 | fn read_b(mut bytes: Bytes, byte: bool) -> Result { 242 | match bytes.next() { 243 | Some(e) => match e.to_ascii_lowercase() { 244 | b'i' => match bytes.next() { 245 | Some(e) => match e.to_ascii_lowercase() { 246 | b't' => match bytes.next() { 247 | Some(e) => match e.to_ascii_lowercase() { 248 | b's' => match bytes.next() { 249 | Some(e) => Err(UnitParseError { 250 | character: unsafe { 251 | get_char_from_bytes(e, bytes) 252 | }, 253 | expected_characters: &[], 254 | also_expect_no_character: true, 255 | }), 256 | None => Ok(false), 257 | }, 258 | _ => Err(UnitParseError { 259 | character: unsafe { get_char_from_bytes(e, bytes) }, 260 | expected_characters: &['s'], 261 | also_expect_no_character: true, 262 | }), 263 | }, 264 | None => Ok(false), 265 | }, 266 | _ => Err(UnitParseError { 267 | character: unsafe { get_char_from_bytes(e, bytes) }, 268 | expected_characters: &['t'], 269 | also_expect_no_character: false, 270 | }), 271 | }, 272 | None => Err(UnitParseError { 273 | character: 'i', 274 | expected_characters: &[], 275 | also_expect_no_character: true, 276 | }), 277 | }, 278 | _ => Err(UnitParseError { 279 | character: unsafe { get_char_from_bytes(e, bytes) }, 280 | expected_characters: &['i'], 281 | also_expect_no_character: true, 282 | }), 283 | }, 284 | None => Ok(byte), 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/bit/adjusted/mod.rs: -------------------------------------------------------------------------------- 1 | mod built_in_traits; 2 | #[cfg(feature = "rocket")] 3 | mod rocket_traits; 4 | #[cfg(feature = "schemars")] 5 | mod schemars_traits; 6 | #[cfg(feature = "serde")] 7 | mod serde_traits; 8 | 9 | use core::{ 10 | cmp::Ordering, 11 | fmt::{self, Alignment, Display, Formatter, Write}, 12 | }; 13 | 14 | use super::{Bit, Unit}; 15 | use crate::{common::round_fractional_part_f64, UnitType}; 16 | 17 | /// Generated from the [`Bit::get_adjusted_unit`](./struct.Bit.html#method.get_adjusted_unit) method or the the [`Bit::get_appropriate_unit`](./struct.Bit.html#method.get_appropriate_unit) method. 18 | /// 19 | /// For accuracy representation, utilize the `Bit` struct. 20 | #[derive(Debug, Clone, Copy)] 21 | pub struct AdjustedBit { 22 | pub(crate) value: f64, 23 | pub(crate) unit: Unit, 24 | } 25 | 26 | impl PartialEq for AdjustedBit { 27 | #[inline] 28 | fn eq(&self, other: &AdjustedBit) -> bool { 29 | let s = self.get_bit(); 30 | let o = other.get_bit(); 31 | 32 | s.eq(&o) 33 | } 34 | } 35 | 36 | impl Eq for AdjustedBit {} 37 | 38 | impl PartialOrd for AdjustedBit { 39 | #[inline] 40 | fn partial_cmp(&self, other: &AdjustedBit) -> Option { 41 | Some(self.cmp(other)) 42 | } 43 | } 44 | 45 | impl Ord for AdjustedBit { 46 | #[inline] 47 | fn cmp(&self, other: &AdjustedBit) -> Ordering { 48 | let s = self.get_bit(); 49 | let o = other.get_bit(); 50 | 51 | s.cmp(&o) 52 | } 53 | } 54 | 55 | impl Display for AdjustedBit { 56 | /// Formats the value using the given formatter. 57 | /// 58 | /// # Examples 59 | /// 60 | /// ``` 61 | /// use byte_unit::{Bit, Unit}; 62 | /// 63 | /// let bit = Bit::from_u64_with_unit(1555, Unit::Kbit).unwrap(); 64 | /// 65 | /// let adjusted_bit = bit.get_adjusted_unit(Unit::Mbit); 66 | /// 67 | /// assert_eq!("1.555 Mb", adjusted_bit.to_string()); 68 | /// ``` 69 | /// 70 | /// ``` 71 | /// use byte_unit::{Bit, UnitType}; 72 | /// 73 | /// let bit = Bit::from_u64(10000); 74 | /// 75 | /// let adjusted_bit_based_2 = bit.get_appropriate_unit(UnitType::Binary); 76 | /// let adjusted_bit_based_10 = bit.get_appropriate_unit(UnitType::Decimal); 77 | /// 78 | /// assert_eq!("9.765625 Kib", format!("{adjusted_bit_based_2}")); 79 | /// assert_eq!("10 Kb", format!("{adjusted_bit_based_10}")); 80 | /// 81 | /// // with precision 82 | /// assert_eq!("9.77 Kib", format!("{adjusted_bit_based_2:.2}")); 83 | /// assert_eq!("10.00 Kb", format!("{adjusted_bit_based_10:.2}")); 84 | /// 85 | /// // without any unnecessary fractional part 86 | /// assert_eq!("9.77 Kib", format!("{adjusted_bit_based_2:#.2}")); 87 | /// assert_eq!("10 Kb", format!("{adjusted_bit_based_10:#.2}")); 88 | /// 89 | /// // with a width, left alignment 90 | /// assert_eq!("9.77 Kib", format!("{adjusted_bit_based_2:10.2}")); 91 | /// assert_eq!("10.00 Kb", format!("{adjusted_bit_based_10:10.2}")); 92 | /// 93 | /// // with a width, right alignment 94 | /// assert_eq!(" 9.77 Kib", format!("{adjusted_bit_based_2:>10.2}")); 95 | /// assert_eq!(" 10.00 Kb", format!("{adjusted_bit_based_10:>10.2}")); 96 | /// 97 | /// // with a width, right alignment, more spaces between the value and the unit 98 | /// assert_eq!(" 9.77 Kib", format!("{adjusted_bit_based_2:>+10.2}")); 99 | /// assert_eq!(" 10.00 Kb", format!("{adjusted_bit_based_10:>+10.2}")); 100 | /// 101 | /// // no spaces between the value and the unit 102 | /// assert_eq!("9.765625Kib", format!("{adjusted_bit_based_2:-}")); 103 | /// assert_eq!("10Kb", format!("{adjusted_bit_based_10:-}")); 104 | /// ``` 105 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 106 | let Self { 107 | value, 108 | unit, 109 | } = self; 110 | let handle_basic_precision = |precision: usize, f: &mut Formatter<'_>| -> fmt::Result { 111 | if f.alternate() { 112 | let value = round_fractional_part_f64(*value, precision); 113 | 114 | f.write_fmt(format_args!("{value}")) 115 | } else if matches!(unit, Unit::Bit | Unit::B) { 116 | f.write_fmt(format_args!("{value}")) 117 | } else { 118 | f.write_fmt(format_args!("{value:.precision$}")) 119 | } 120 | }; 121 | 122 | let space_length = if f.sign_plus() { 123 | 4 - unit.as_str().len() 124 | } else if f.sign_minus() { 125 | 0 126 | } else { 127 | 1 128 | }; 129 | 130 | if let Some(mut width) = f.width() { 131 | let l = unit.as_str().len() + space_length; 132 | 133 | if let Some(precision) = f.precision() { 134 | if width > l + 1 { 135 | width -= l; 136 | 137 | let alignment = f.align().unwrap_or(Alignment::Left); 138 | 139 | if f.alternate() { 140 | let value = round_fractional_part_f64(*value, precision); 141 | 142 | match alignment { 143 | Alignment::Left | Alignment::Center => { 144 | f.write_fmt(format_args!("{value: f.write_fmt(format_args!("{value:>width$}"))?, 147 | } 148 | } else { 149 | match alignment { 150 | Alignment::Left | Alignment::Center => { 151 | f.write_fmt(format_args!("{value: { 154 | f.write_fmt(format_args!("{value:>width$.precision$}"))? 155 | }, 156 | } 157 | } 158 | } else { 159 | handle_basic_precision(precision, f)?; 160 | } 161 | } else if width > l + 1 { 162 | width -= l; 163 | 164 | let alignment = f.align().unwrap_or(Alignment::Left); 165 | 166 | match alignment { 167 | Alignment::Left | Alignment::Center => { 168 | f.write_fmt(format_args!("{value: f.write_fmt(format_args!("{value:>width$}"))?, 171 | } 172 | } else { 173 | f.write_fmt(format_args!("{value}"))?; 174 | } 175 | } else if let Some(precision) = f.precision() { 176 | handle_basic_precision(precision, f)?; 177 | } else { 178 | f.write_fmt(format_args!("{value}"))?; 179 | } 180 | 181 | for _ in 0..space_length { 182 | f.write_char(' ')?; 183 | } 184 | 185 | f.write_fmt(format_args!("{unit}")) 186 | } 187 | } 188 | 189 | /// Methods for getting values. 190 | impl AdjustedBit { 191 | /// Get the value. 192 | #[inline] 193 | pub const fn get_value(&self) -> f64 { 194 | self.value 195 | } 196 | 197 | /// Get the unit. 198 | #[inline] 199 | pub const fn get_unit(&self) -> Unit { 200 | self.unit 201 | } 202 | 203 | /// Create a new `Bit` instance from this `AdjustedBit` instance. 204 | /// 205 | /// # Examples 206 | /// 207 | /// ``` 208 | /// use byte_unit::{Bit, Unit}; 209 | /// 210 | /// let bit = Bit::from_u64_with_unit(1555, Unit::Kbit).unwrap(); 211 | /// 212 | /// let adjusted_bit = bit.get_adjusted_unit(Unit::Mbit); 213 | /// 214 | /// let bit_back = adjusted_bit.get_bit(); 215 | /// 216 | /// assert_eq!(bit, bit_back); 217 | /// ``` 218 | /// 219 | /// # Points to Note 220 | /// 221 | /// * The result may not be logically equal to the original `Bit` instance due to the accuracy of floating-point numbers. 222 | #[inline] 223 | pub fn get_bit(&self) -> Bit { 224 | Bit::from_f64_with_unit(self.value, self.unit).unwrap() 225 | } 226 | } 227 | 228 | /// Associated functions for generating `AdjustedBit`. 229 | impl Bit { 230 | /// Adjust the unit and value for this `Bit` instance. 231 | /// 232 | /// # Examples 233 | /// 234 | /// ``` 235 | /// use byte_unit::{AdjustedBit, Bit, Unit}; 236 | /// 237 | /// let bit = Bit::parse_str("123Kib").unwrap(); 238 | /// 239 | /// let adjusted_bit = bit.get_adjusted_unit(Unit::Kbit); 240 | /// 241 | /// assert_eq!("125.952 Kb", adjusted_bit.to_string()); 242 | /// ``` 243 | /// 244 | /// ``` 245 | /// use byte_unit::{AdjustedBit, Bit, Unit}; 246 | /// 247 | /// let bit = Bit::parse_str("50.84 Mb").unwrap(); 248 | /// 249 | /// let adjusted_bit = bit.get_adjusted_unit(Unit::Mibit); 250 | /// 251 | /// assert_eq!("48.48480224609375 Mib", adjusted_bit.to_string()); 252 | /// ``` 253 | #[inline] 254 | pub fn get_adjusted_unit(self, unit: Unit) -> AdjustedBit { 255 | let bit_v = self.as_u128(); 256 | 257 | let value = match unit { 258 | Unit::Bit => (bit_v << 3) as f64, 259 | Unit::B => bit_v as f64, 260 | _ => bit_v as f64 / unit.as_bits_u128() as f64, 261 | }; 262 | 263 | AdjustedBit { 264 | value, 265 | unit, 266 | } 267 | } 268 | 269 | /// Find the appropriate unit and value for this `Bit` instance. 270 | /// 271 | /// # Examples 272 | /// 273 | /// ``` 274 | /// use byte_unit::{Bit, UnitType}; 275 | /// 276 | /// let bit = Bit::parse_str("123Kib").unwrap(); 277 | /// 278 | /// let adjusted_bit = bit.get_appropriate_unit(UnitType::Decimal); 279 | /// 280 | /// assert_eq!("125.952 Kb", adjusted_bit.to_string()); 281 | /// ``` 282 | /// 283 | /// ``` 284 | /// use byte_unit::{Bit, UnitType}; 285 | /// 286 | /// let bit = Bit::parse_str("50.84 Mb").unwrap(); 287 | /// 288 | /// let adjusted_bit = bit.get_appropriate_unit(UnitType::Binary); 289 | /// 290 | /// assert_eq!("48.48480224609375 Mib", adjusted_bit.to_string()); 291 | /// ``` 292 | pub fn get_appropriate_unit(&self, unit_type: UnitType) -> AdjustedBit { 293 | let a = Unit::get_multiples_bits(); 294 | 295 | let (skip, step) = match unit_type { 296 | UnitType::Binary => (0, 2), 297 | UnitType::Decimal => (1, 2), 298 | UnitType::Both => (0, 1), 299 | }; 300 | 301 | let bits_v = self.as_u128(); 302 | 303 | for unit in a.iter().rev().skip(skip).step_by(step) { 304 | if bits_v >= unit.as_bits_u128() { 305 | return self.get_adjusted_unit(*unit); 306 | } 307 | } 308 | 309 | self.get_adjusted_unit(Unit::B) 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /src/byte/adjusted/mod.rs: -------------------------------------------------------------------------------- 1 | mod built_in_traits; 2 | #[cfg(feature = "rocket")] 3 | mod rocket_traits; 4 | #[cfg(feature = "schemars")] 5 | mod schemars_traits; 6 | #[cfg(feature = "serde")] 7 | mod serde_traits; 8 | 9 | use core::{ 10 | cmp::Ordering, 11 | fmt::{self, Alignment, Display, Formatter, Write}, 12 | }; 13 | 14 | use super::{Byte, Unit}; 15 | use crate::{common::round_fractional_part_f64, UnitType}; 16 | 17 | /// Generated from the [`Byte::get_adjusted_unit`](./struct.Byte.html#method.get_adjusted_unit) method or the the [`Byte::get_appropriate_unit`](./struct.Byte.html#method.get_appropriate_unit) method. 18 | /// 19 | /// For accuracy representation, utilize the `Byte` struct. 20 | #[derive(Debug, Clone, Copy)] 21 | pub struct AdjustedByte { 22 | pub(crate) value: f64, 23 | pub(crate) unit: Unit, 24 | } 25 | 26 | impl PartialEq for AdjustedByte { 27 | #[inline] 28 | fn eq(&self, other: &AdjustedByte) -> bool { 29 | let s = self.get_byte(); 30 | let o = other.get_byte(); 31 | 32 | s.eq(&o) 33 | } 34 | } 35 | 36 | impl Eq for AdjustedByte {} 37 | 38 | impl PartialOrd for AdjustedByte { 39 | #[inline] 40 | fn partial_cmp(&self, other: &AdjustedByte) -> Option { 41 | Some(self.cmp(other)) 42 | } 43 | } 44 | 45 | impl Ord for AdjustedByte { 46 | #[inline] 47 | fn cmp(&self, other: &AdjustedByte) -> Ordering { 48 | let s = self.get_byte(); 49 | let o = other.get_byte(); 50 | 51 | s.cmp(&o) 52 | } 53 | } 54 | 55 | impl Display for AdjustedByte { 56 | /// Formats the value using the given formatter. 57 | /// 58 | /// # Examples 59 | /// 60 | /// ``` 61 | /// use byte_unit::{Byte, Unit}; 62 | /// 63 | /// let byte = Byte::from_u64_with_unit(1555, Unit::KB).unwrap(); 64 | /// 65 | /// let adjusted_byte = byte.get_adjusted_unit(Unit::MB); 66 | /// 67 | /// assert_eq!("1.555 MB", adjusted_byte.to_string()); 68 | /// ``` 69 | /// 70 | /// ``` 71 | /// use byte_unit::{Byte, UnitType}; 72 | /// 73 | /// let byte = Byte::from_u64(10000); 74 | /// 75 | /// let adjusted_byte_based_2 = byte.get_appropriate_unit(UnitType::Binary); 76 | /// let adjusted_byte_based_10 = byte.get_appropriate_unit(UnitType::Decimal); 77 | /// 78 | /// assert_eq!("9.765625 KiB", format!("{adjusted_byte_based_2}")); 79 | /// assert_eq!("10 KB", format!("{adjusted_byte_based_10}")); 80 | /// 81 | /// // with precision 82 | /// assert_eq!("9.77 KiB", format!("{adjusted_byte_based_2:.2}")); 83 | /// assert_eq!("10.00 KB", format!("{adjusted_byte_based_10:.2}")); 84 | /// 85 | /// // without any unnecessary fractional part 86 | /// assert_eq!("9.77 KiB", format!("{adjusted_byte_based_2:#.2}")); 87 | /// assert_eq!("10 KB", format!("{adjusted_byte_based_10:#.2}")); 88 | /// 89 | /// // with a width, left alignment 90 | /// assert_eq!("9.77 KiB", format!("{adjusted_byte_based_2:10.2}")); 91 | /// assert_eq!("10.00 KB", format!("{adjusted_byte_based_10:10.2}")); 92 | /// 93 | /// // with a width, right alignment 94 | /// assert_eq!(" 9.77 KiB", format!("{adjusted_byte_based_2:>10.2}")); 95 | /// assert_eq!(" 10.00 KB", format!("{adjusted_byte_based_10:>10.2}")); 96 | /// 97 | /// // with a width, right alignment, more spaces between the value and the unit 98 | /// assert_eq!(" 9.77 KiB", format!("{adjusted_byte_based_2:>+10.2}")); 99 | /// assert_eq!(" 10.00 KB", format!("{adjusted_byte_based_10:>+10.2}")); 100 | /// 101 | /// // no spaces between the value and the unit 102 | /// assert_eq!("9.765625KiB", format!("{adjusted_byte_based_2:-}")); 103 | /// assert_eq!("10KB", format!("{adjusted_byte_based_10:-}")); 104 | /// ``` 105 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 106 | let Self { 107 | value, 108 | unit, 109 | } = self; 110 | let handle_basic_precision = |precision: usize, f: &mut Formatter<'_>| -> fmt::Result { 111 | if f.alternate() { 112 | let value = round_fractional_part_f64(*value, precision); 113 | 114 | f.write_fmt(format_args!("{value}")) 115 | } else if matches!(unit, Unit::Bit | Unit::B) { 116 | f.write_fmt(format_args!("{value}")) 117 | } else { 118 | f.write_fmt(format_args!("{value:.precision$}")) 119 | } 120 | }; 121 | 122 | let space_length = if f.sign_plus() { 123 | 4 - unit.as_str().len() 124 | } else if f.sign_minus() { 125 | 0 126 | } else { 127 | 1 128 | }; 129 | 130 | if let Some(mut width) = f.width() { 131 | let l = unit.as_str().len() + space_length; 132 | 133 | if let Some(precision) = f.precision() { 134 | if width > l + 1 { 135 | width -= l; 136 | 137 | let alignment = f.align().unwrap_or(Alignment::Left); 138 | 139 | if f.alternate() { 140 | let value = round_fractional_part_f64(*value, precision); 141 | 142 | match alignment { 143 | Alignment::Left | Alignment::Center => { 144 | f.write_fmt(format_args!("{value: f.write_fmt(format_args!("{value:>width$}"))?, 147 | } 148 | } else { 149 | match alignment { 150 | Alignment::Left | Alignment::Center => { 151 | f.write_fmt(format_args!("{value: { 154 | f.write_fmt(format_args!("{value:>width$.precision$}"))? 155 | }, 156 | } 157 | } 158 | } else { 159 | handle_basic_precision(precision, f)?; 160 | } 161 | } else if width > l + 1 { 162 | width -= l; 163 | 164 | let alignment = f.align().unwrap_or(Alignment::Left); 165 | 166 | match alignment { 167 | Alignment::Left | Alignment::Center => { 168 | f.write_fmt(format_args!("{value: f.write_fmt(format_args!("{value:>width$}"))?, 171 | } 172 | } else { 173 | f.write_fmt(format_args!("{value}"))?; 174 | } 175 | } else if let Some(precision) = f.precision() { 176 | handle_basic_precision(precision, f)?; 177 | } else { 178 | f.write_fmt(format_args!("{value}"))?; 179 | } 180 | 181 | for _ in 0..space_length { 182 | f.write_char(' ')?; 183 | } 184 | 185 | f.write_fmt(format_args!("{unit}")) 186 | } 187 | } 188 | 189 | /// Methods for getting values. 190 | impl AdjustedByte { 191 | /// Get the value. 192 | #[inline] 193 | pub const fn get_value(&self) -> f64 { 194 | self.value 195 | } 196 | 197 | /// Get the unit. 198 | #[inline] 199 | pub const fn get_unit(&self) -> Unit { 200 | self.unit 201 | } 202 | 203 | /// Create a new `Byte` instance from this `AdjustedByte` instance. 204 | /// 205 | /// # Examples 206 | /// 207 | /// ``` 208 | /// use byte_unit::{Byte, Unit}; 209 | /// 210 | /// let byte = Byte::from_u64_with_unit(1555, Unit::KB).unwrap(); 211 | /// 212 | /// let adjusted_byte = byte.get_adjusted_unit(Unit::MB); 213 | /// 214 | /// let byte_back = adjusted_byte.get_byte(); 215 | /// 216 | /// assert_eq!(byte, byte_back); 217 | /// ``` 218 | /// 219 | /// # Points to Note 220 | /// 221 | /// * The result may not be logically equal to the original `Byte` instance due to the accuracy of floating-point numbers. 222 | #[inline] 223 | pub fn get_byte(&self) -> Byte { 224 | Byte::from_f64_with_unit(self.value, self.unit).unwrap() 225 | } 226 | } 227 | 228 | /// Associated functions for generating `AdjustedByte`. 229 | impl Byte { 230 | /// Adjust the unit and value for this `Byte` instance. 231 | /// 232 | /// # Examples 233 | /// 234 | /// ``` 235 | /// use byte_unit::{AdjustedByte, Byte, Unit}; 236 | /// 237 | /// let byte = Byte::parse_str("123KiB", true).unwrap(); 238 | /// 239 | /// let adjusted_byte = byte.get_adjusted_unit(Unit::KB); 240 | /// 241 | /// assert_eq!("125.952 KB", adjusted_byte.to_string()); 242 | /// ``` 243 | /// 244 | /// ``` 245 | /// use byte_unit::{AdjustedByte, Byte, Unit}; 246 | /// 247 | /// let byte = Byte::parse_str("50.84 MB", true).unwrap(); 248 | /// 249 | /// let adjusted_byte = byte.get_adjusted_unit(Unit::MiB); 250 | /// 251 | /// assert_eq!("48.48480224609375 MiB", adjusted_byte.to_string()); 252 | /// ``` 253 | #[inline] 254 | pub fn get_adjusted_unit(self, unit: Unit) -> AdjustedByte { 255 | let byte_v = self.as_u128(); 256 | 257 | let value = match unit { 258 | Unit::Bit => (byte_v << 3) as f64, 259 | Unit::B => byte_v as f64, 260 | _ => byte_v as f64 / unit.as_bytes_u128() as f64, 261 | }; 262 | 263 | AdjustedByte { 264 | value, 265 | unit, 266 | } 267 | } 268 | 269 | /// Find the appropriate unit and value for this `Byte` instance. 270 | /// 271 | /// # Examples 272 | /// 273 | /// ``` 274 | /// use byte_unit::{Byte, UnitType}; 275 | /// 276 | /// let byte = Byte::parse_str("123KiB", true).unwrap(); 277 | /// 278 | /// let adjusted_byte = byte.get_appropriate_unit(UnitType::Decimal); 279 | /// 280 | /// assert_eq!("125.952 KB", adjusted_byte.to_string()); 281 | /// ``` 282 | /// 283 | /// ``` 284 | /// use byte_unit::{Byte, UnitType}; 285 | /// 286 | /// let byte = Byte::parse_str("50.84 MB", true).unwrap(); 287 | /// 288 | /// let adjusted_byte = byte.get_appropriate_unit(UnitType::Binary); 289 | /// 290 | /// assert_eq!("48.48480224609375 MiB", adjusted_byte.to_string()); 291 | /// ``` 292 | pub fn get_appropriate_unit(&self, unit_type: UnitType) -> AdjustedByte { 293 | let a = Unit::get_multiples_bytes(); 294 | 295 | let (skip, step) = match unit_type { 296 | UnitType::Binary => (0, 2), 297 | UnitType::Decimal => (1, 2), 298 | UnitType::Both => (0, 1), 299 | }; 300 | 301 | let bytes_v = self.as_u128(); 302 | 303 | for unit in a.iter().rev().skip(skip).step_by(step) { 304 | if bytes_v >= unit.as_bytes_u128() { 305 | return self.get_adjusted_unit(*unit); 306 | } 307 | } 308 | 309 | self.get_adjusted_unit(Unit::B) 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /src/unit/mod.rs: -------------------------------------------------------------------------------- 1 | mod built_in_trait; 2 | pub(crate) mod parse; 3 | #[cfg(feature = "rocket")] 4 | mod rocket_traits; 5 | #[cfg(feature = "serde")] 6 | mod serde_traits; 7 | #[cfg(any(feature = "byte", feature = "bit"))] 8 | mod unit_type; 9 | 10 | use core::fmt::{self, Display, Formatter}; 11 | 12 | #[cfg(any(feature = "byte", feature = "bit"))] 13 | pub use unit_type::*; 14 | 15 | /// The unit of bits/bytes. 16 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 17 | pub enum Unit { 18 | /// 8 Bit = 1 byte. 19 | Bit, 20 | /// 1 B = 1 byte. 21 | B, 22 | /// 1 Kbit = 125 bytes. 23 | Kbit, 24 | /// 1 Kibit = 27 bytes. 25 | Kibit, 26 | /// 1 KB = 103 bytes. 27 | KB, 28 | /// 1 KiB = 210 bytes. 29 | KiB, 30 | /// 1 Mbit = 125 * 103 bytes. 31 | Mbit, 32 | /// 1 Mibit = 217 bytes. 33 | Mibit, 34 | /// 1 MB = 106 bytes. 35 | MB, 36 | /// 1 MiB = 220 bytes. 37 | MiB, 38 | /// 1 Gbit = 125 * 106 bytes. 39 | Gbit, 40 | /// 1 Gibit = 227 bytes. 41 | Gibit, 42 | /// 1 GB = 109 bytes. 43 | GB, 44 | /// 1 GiB = 230 bytes. 45 | GiB, 46 | /// 1 Tbit = 125 * 109 bytes. 47 | Tbit, 48 | /// 1 Tibit = 237 bytes. 49 | Tibit, 50 | /// 1 TB = 1012 bytes. 51 | TB, 52 | /// 1 TiB = 240 bytes. 53 | TiB, 54 | /// 1 Pbit = 125 * 1012 bytes. 55 | Pbit, 56 | /// 1 Pibit = 247 bytes. 57 | Pibit, 58 | /// 1 PB = 1015 bytes. 59 | PB, 60 | /// 1 PiB = 250 bytes. 61 | PiB, 62 | /// 1 Ebit = 125 * 1015 bytes. 63 | Ebit, 64 | /// 1 Eibit = 257 bytes. 65 | Eibit, 66 | /// 1 EB = 1018 bytes. 67 | EB, 68 | /// 1 EiB = 260 bytes. 69 | EiB, 70 | #[cfg(feature = "u128")] 71 | /// 1 Zbit = 125 * 1018 bytes. 72 | Zbit, 73 | #[cfg(feature = "u128")] 74 | /// 1 Zibit = 267 bytes. 75 | Zibit, 76 | #[cfg(feature = "u128")] 77 | /// 1 ZB = 1021 bytes. 78 | ZB, 79 | #[cfg(feature = "u128")] 80 | /// 1 ZiB = 270 bytes. 81 | ZiB, 82 | #[cfg(feature = "u128")] 83 | /// 1 Ybit = 125 * 1021 bytes. 84 | Ybit, 85 | #[cfg(feature = "u128")] 86 | /// 1 Yibit = 277 bytes. 87 | Yibit, 88 | #[cfg(feature = "u128")] 89 | /// 1 YB = 1024 bytes. 90 | YB, 91 | #[cfg(feature = "u128")] 92 | /// 1 YiB = 280 bytes. 93 | YiB, 94 | } 95 | 96 | impl Display for Unit { 97 | #[inline] 98 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 99 | Display::fmt(self.as_str(), f) 100 | } 101 | } 102 | 103 | /// Methods for converting a `Unit` instance into a primitive integer. 104 | impl Unit { 105 | /// Retrieve the bit represented by this `Unit` instance. 106 | /// 107 | /// # Examples 108 | /// 109 | /// ``` 110 | /// use byte_unit::Unit; 111 | /// 112 | /// assert_eq!(1, Unit::Bit.as_bits_u128()); 113 | /// assert_eq!(8, Unit::B.as_bits_u128()); 114 | /// assert_eq!(8000, Unit::KB.as_bits_u128()); 115 | /// assert_eq!(1024, Unit::Kibit.as_bits_u128()); 116 | /// ``` 117 | #[inline] 118 | pub const fn as_bits_u128(self) -> u128 { 119 | match self { 120 | Unit::Bit => 1, 121 | Unit::B => 1 << 3, 122 | Unit::Kbit => 1_000, 123 | Unit::Kibit => 1 << 10, 124 | Unit::KB => Unit::Kbit.as_bits_u128() << 3, 125 | Unit::KiB => Unit::Kibit.as_bits_u128() << 3, 126 | Unit::Mbit => 1_000_000, 127 | Unit::Mibit => 1 << 20, 128 | Unit::MB => Unit::Mbit.as_bits_u128() << 3, 129 | Unit::MiB => Unit::Mibit.as_bits_u128() << 3, 130 | Unit::Gbit => 1_000_000_000, 131 | Unit::Gibit => 1 << 30, 132 | Unit::GB => Unit::Gbit.as_bits_u128() << 3, 133 | Unit::GiB => Unit::Gibit.as_bits_u128() << 3, 134 | Unit::Tbit => 1_000_000_000_000, 135 | Unit::Tibit => 1 << 40, 136 | Unit::TB => Unit::Tbit.as_bits_u128() << 3, 137 | Unit::TiB => Unit::Tibit.as_bits_u128() << 3, 138 | Unit::Pbit => 1_000_000_000_000_000, 139 | Unit::Pibit => 1 << 50, 140 | Unit::PB => Unit::Pbit.as_bits_u128() << 3, 141 | Unit::PiB => Unit::Pibit.as_bits_u128() << 3, 142 | Unit::Ebit => 1_000_000_000_000_000_000, 143 | Unit::Eibit => 1 << 60, 144 | Unit::EB => Unit::Ebit.as_bits_u128() << 3, 145 | Unit::EiB => Unit::Eibit.as_bits_u128() << 3, 146 | #[cfg(feature = "u128")] 147 | Unit::Zbit => 1_000_000_000_000_000_000_000, 148 | #[cfg(feature = "u128")] 149 | Unit::Zibit => 1 << 70, 150 | #[cfg(feature = "u128")] 151 | Unit::ZB => Unit::Zbit.as_bits_u128() << 3, 152 | #[cfg(feature = "u128")] 153 | Unit::ZiB => Unit::Zibit.as_bits_u128() << 3, 154 | #[cfg(feature = "u128")] 155 | Unit::Ybit => 1_000_000_000_000_000_000_000_000, 156 | #[cfg(feature = "u128")] 157 | Unit::Yibit => 1 << 80, 158 | #[cfg(feature = "u128")] 159 | Unit::YB => Unit::Ybit.as_bits_u128() << 3, 160 | #[cfg(feature = "u128")] 161 | Unit::YiB => Unit::Yibit.as_bits_u128() << 3, 162 | } 163 | } 164 | 165 | #[cfg(any(feature = "byte", feature = "bit"))] 166 | #[cfg(not(feature = "u128"))] 167 | #[inline] 168 | pub(crate) const fn as_bits_u64(self) -> u64 { 169 | self.as_bits_u128() as u64 170 | } 171 | 172 | #[cfg(feature = "byte")] 173 | #[inline] 174 | pub(crate) const fn as_bytes_u128(self) -> u128 { 175 | debug_assert!(!matches!(self, Unit::Bit)); 176 | 177 | self.as_bits_u128() >> 3 178 | } 179 | 180 | #[cfg(feature = "byte")] 181 | #[cfg(not(feature = "u128"))] 182 | #[inline] 183 | pub(crate) const fn as_bytes_u64(self) -> u64 { 184 | debug_assert!(!matches!(self, Unit::Bit)); 185 | 186 | self.as_bits_u64() >> 3 187 | } 188 | } 189 | 190 | /// Methods for converting a `Unit` instance into a string. 191 | impl Unit { 192 | /// Retrieve the string represented by this `Unit` instance. 193 | /// 194 | /// # Examples 195 | /// 196 | /// ``` 197 | /// use byte_unit::Unit; 198 | /// 199 | /// assert_eq!("B", Unit::B.as_str()); 200 | /// assert_eq!("KB", Unit::KB.as_str()); 201 | /// assert_eq!("MiB", Unit::MiB.as_str()); 202 | /// assert_eq!("Gb", Unit::Gbit.as_str()); 203 | /// assert_eq!("Tib", Unit::Tibit.as_str()); 204 | /// ``` 205 | #[inline] 206 | pub const fn as_str(self) -> &'static str { 207 | match self { 208 | Self::Bit => "b", 209 | Self::B => "B", 210 | Self::Kbit => "Kb", 211 | Self::Kibit => "Kib", 212 | Self::KB => "KB", 213 | Self::KiB => "KiB", 214 | Self::Mbit => "Mb", 215 | Self::Mibit => "Mib", 216 | Self::MB => "MB", 217 | Self::MiB => "MiB", 218 | Self::Gbit => "Gb", 219 | Self::Gibit => "Gib", 220 | Self::GB => "GB", 221 | Self::GiB => "GiB", 222 | Self::Tbit => "Tb", 223 | Self::Tibit => "Tib", 224 | Self::TB => "TB", 225 | Self::TiB => "TiB", 226 | Self::Pbit => "Pb", 227 | Self::Pibit => "Pib", 228 | Self::PB => "PB", 229 | Self::PiB => "PiB", 230 | Self::Ebit => "Eb", 231 | Self::Eibit => "Eib", 232 | Self::EB => "EB", 233 | Self::EiB => "EiB", 234 | #[cfg(feature = "u128")] 235 | Self::Zbit => "Zb", 236 | #[cfg(feature = "u128")] 237 | Self::Zibit => "Zib", 238 | #[cfg(feature = "u128")] 239 | Self::ZB => "ZB", 240 | #[cfg(feature = "u128")] 241 | Self::ZiB => "ZiB", 242 | #[cfg(feature = "u128")] 243 | Self::Ybit => "Yb", 244 | #[cfg(feature = "u128")] 245 | Self::Yibit => "Yib", 246 | #[cfg(feature = "u128")] 247 | Self::YB => "YB", 248 | #[cfg(feature = "u128")] 249 | Self::YiB => "YiB", 250 | } 251 | } 252 | } 253 | 254 | /// Methods for categorizing variants. 255 | impl Unit { 256 | /// Check whether the unit is based on bits. 257 | /// 258 | /// # Examples 259 | /// 260 | /// ``` 261 | /// use byte_unit::Unit; 262 | /// 263 | /// assert_eq!(false, Unit::KB.is_bit()); 264 | /// assert_eq!(true, Unit::Kbit.is_bit()); 265 | /// ``` 266 | #[inline] 267 | pub const fn is_bit(self) -> bool { 268 | #[cfg(feature = "u128")] 269 | { 270 | matches!( 271 | self, 272 | Self::Bit 273 | | Self::Kbit 274 | | Self::Kibit 275 | | Self::Mbit 276 | | Self::Mibit 277 | | Self::Gbit 278 | | Self::Gibit 279 | | Self::Tbit 280 | | Self::Tibit 281 | | Self::Pbit 282 | | Self::Pibit 283 | | Self::Ebit 284 | | Self::Eibit 285 | | Self::Zbit 286 | | Self::Zibit 287 | | Self::Ybit 288 | | Self::Yibit 289 | ) 290 | } 291 | #[cfg(not(feature = "u128"))] 292 | { 293 | matches!( 294 | self, 295 | Self::Bit 296 | | Self::Kbit 297 | | Self::Kibit 298 | | Self::Mbit 299 | | Self::Mibit 300 | | Self::Gbit 301 | | Self::Gibit 302 | | Self::Tbit 303 | | Self::Tibit 304 | | Self::Pbit 305 | | Self::Pibit 306 | | Self::Ebit 307 | | Self::Eibit 308 | ) 309 | } 310 | } 311 | 312 | /// Check whether the unit is based on powers of **2**. 313 | /// 314 | /// # Examples 315 | /// 316 | /// ``` 317 | /// use byte_unit::Unit; 318 | /// 319 | /// assert_eq!(false, Unit::KB.is_binary_multiples()); 320 | /// assert_eq!(true, Unit::KiB.is_binary_multiples()); 321 | /// ``` 322 | #[inline] 323 | pub const fn is_binary_multiples(self) -> bool { 324 | #[cfg(feature = "u128")] 325 | { 326 | matches!( 327 | self, 328 | Self::B 329 | | Self::Kibit 330 | | Self::KiB 331 | | Self::Mibit 332 | | Self::MiB 333 | | Self::Gibit 334 | | Self::GiB 335 | | Self::Tibit 336 | | Self::TiB 337 | | Self::Pibit 338 | | Self::PiB 339 | | Self::Eibit 340 | | Self::EiB 341 | | Self::Zibit 342 | | Self::ZiB 343 | | Self::Yibit 344 | | Self::YiB 345 | ) 346 | } 347 | #[cfg(not(feature = "u128"))] 348 | { 349 | matches!( 350 | self, 351 | Self::B 352 | | Self::Kibit 353 | | Self::KiB 354 | | Self::Mibit 355 | | Self::MiB 356 | | Self::Gibit 357 | | Self::GiB 358 | | Self::Tibit 359 | | Self::TiB 360 | | Self::Pibit 361 | | Self::PiB 362 | | Self::Eibit 363 | | Self::EiB 364 | ) 365 | } 366 | } 367 | } 368 | 369 | impl Unit { 370 | #[cfg(any(feature = "byte", feature = "bit"))] 371 | #[allow(dead_code)] 372 | #[inline] 373 | pub(crate) const fn get_multiples() -> &'static [Self] { 374 | &[ 375 | Self::Kbit, 376 | Self::Kibit, 377 | Self::KB, 378 | Self::KiB, 379 | Self::Mbit, 380 | Self::Mibit, 381 | Self::MB, 382 | Self::MiB, 383 | Self::Gbit, 384 | Self::Gibit, 385 | Self::GB, 386 | Self::GiB, 387 | Self::Tbit, 388 | Self::Tibit, 389 | Self::TB, 390 | Self::TiB, 391 | Self::Pbit, 392 | Self::Pibit, 393 | Self::PB, 394 | Self::PiB, 395 | Self::Ebit, 396 | Self::Eibit, 397 | Self::EB, 398 | Self::EiB, 399 | #[cfg(feature = "u128")] 400 | Self::Zbit, 401 | #[cfg(feature = "u128")] 402 | Self::Zibit, 403 | #[cfg(feature = "u128")] 404 | Self::ZB, 405 | #[cfg(feature = "u128")] 406 | Self::ZiB, 407 | #[cfg(feature = "u128")] 408 | Self::Ybit, 409 | #[cfg(feature = "u128")] 410 | Self::Yibit, 411 | #[cfg(feature = "u128")] 412 | Self::YB, 413 | #[cfg(feature = "u128")] 414 | Self::YiB, 415 | ] 416 | } 417 | 418 | #[cfg(feature = "byte")] 419 | #[inline] 420 | pub(crate) const fn get_multiples_bytes() -> &'static [Self] { 421 | &[ 422 | Self::KB, 423 | Self::KiB, 424 | Self::MB, 425 | Self::MiB, 426 | Self::GB, 427 | Self::GiB, 428 | Self::TB, 429 | Self::TiB, 430 | Self::PB, 431 | Self::PiB, 432 | Self::EB, 433 | Self::EiB, 434 | #[cfg(feature = "u128")] 435 | Self::ZB, 436 | #[cfg(feature = "u128")] 437 | Self::ZiB, 438 | #[cfg(feature = "u128")] 439 | Self::YB, 440 | #[cfg(feature = "u128")] 441 | Self::YiB, 442 | ] 443 | } 444 | 445 | #[cfg(feature = "bit")] 446 | #[allow(dead_code)] 447 | #[inline] 448 | pub(crate) const fn get_multiples_bits() -> &'static [Self] { 449 | &[ 450 | Self::Kbit, 451 | Self::Kibit, 452 | Self::Mbit, 453 | Self::Mibit, 454 | Self::Gbit, 455 | Self::Gibit, 456 | Self::Tbit, 457 | Self::Tibit, 458 | Self::Pbit, 459 | Self::Pibit, 460 | Self::Ebit, 461 | Self::Eibit, 462 | #[cfg(feature = "u128")] 463 | Self::Zbit, 464 | #[cfg(feature = "u128")] 465 | Self::Zibit, 466 | #[cfg(feature = "u128")] 467 | Self::Ybit, 468 | #[cfg(feature = "u128")] 469 | Self::Yibit, 470 | ] 471 | } 472 | } 473 | -------------------------------------------------------------------------------- /src/bit/mod.rs: -------------------------------------------------------------------------------- 1 | mod adjusted; 2 | mod built_in_traits; 3 | mod constants; 4 | mod decimal; 5 | mod parse; 6 | #[cfg(feature = "rocket")] 7 | mod rocket_traits; 8 | #[cfg(feature = "schemars")] 9 | mod schemars_traits; 10 | #[cfg(feature = "serde")] 11 | mod serde_traits; 12 | 13 | use core::fmt::{self, Alignment, Display, Formatter, Write}; 14 | 15 | pub use adjusted::*; 16 | use rust_decimal::prelude::*; 17 | 18 | use crate::{ 19 | common::{ceil_f32, ceil_f64}, 20 | Unit, 21 | }; 22 | 23 | #[cfg(feature = "u128")] 24 | const RONNABIT: u128 = 1_000_000_000_000_000_000_000_000_000; // RB 25 | 26 | #[cfg(feature = "u128")] 27 | #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Default)] 28 | /// Representing the size in bits. 29 | pub struct Bit(u128); 30 | 31 | #[cfg(not(feature = "u128"))] 32 | #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Default)] 33 | /// Representing the size in bits. 34 | pub struct Bit(u64); 35 | 36 | impl Display for Bit { 37 | /// Formats the value using the given formatter. 38 | /// 39 | /// # Examples 40 | /// 41 | /// ``` 42 | /// use byte_unit::{Bit, Unit}; 43 | /// 44 | /// let bit = Bit::from_u64_with_unit(1555, Unit::Kbit).unwrap(); 45 | /// 46 | /// assert_eq!("1555000", bit.to_string()); 47 | /// ``` 48 | /// 49 | /// ``` 50 | /// use byte_unit::{Bit, UnitType}; 51 | /// 52 | /// let bit_based_2 = Bit::from_u64(10240); 53 | /// let bit_based_10 = Bit::from_u64(10000); 54 | /// 55 | /// assert_eq!("10240", format!("{bit_based_2}")); 56 | /// assert_eq!("10000", format!("{bit_based_10}")); 57 | /// 58 | /// // with an exact unit 59 | /// assert_eq!("10 Kib", format!("{bit_based_2:#}")); 60 | /// assert_eq!("10 Kb", format!("{bit_based_10:#}")); 61 | /// 62 | /// // with an exact unit, no spaces between the value and the unit 63 | /// assert_eq!("10Kib", format!("{bit_based_2:-#}")); 64 | /// assert_eq!("10Kb", format!("{bit_based_10:-#}")); 65 | /// 66 | /// // with a width, left alignment 67 | /// assert_eq!("10 Kib", format!("{bit_based_2:#10}")); 68 | /// assert_eq!("10 Kb", format!("{bit_based_10:#10}")); 69 | /// 70 | /// // with a width, right alignment 71 | /// assert_eq!(" 10 Kib", format!("{bit_based_2:>#10}")); 72 | /// assert_eq!(" 10 Kb", format!("{bit_based_10:>#10}")); 73 | /// 74 | /// // with a width, right alignment, more spaces between the value and the unit 75 | /// assert_eq!(" 10 Kib", format!("{bit_based_2:>+#10}")); 76 | /// assert_eq!(" 10 Kb", format!("{bit_based_10:>+#10}")); 77 | /// ``` 78 | /// 79 | /// ``` 80 | /// use byte_unit::{Bit, UnitType}; 81 | /// 82 | /// let bit = Bit::from_u64(3211776); 83 | /// 84 | /// assert_eq!("3211776", format!("{bit}")); 85 | /// 86 | /// // with a unit, still precisely 87 | /// assert_eq!("3136.5 Kib", format!("{bit:#}")); 88 | /// 89 | /// // with a unit and a larger precision (default is 3), still precisely 90 | /// assert_eq!("3.211776 Mb", format!("{bit:#.6}")); 91 | /// 92 | /// // with a unit and a smaller precision (default is 3), still precisely 93 | /// assert_eq!("3211776 b", format!("{bit:#.0}")); 94 | /// ``` 95 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 96 | if f.alternate() { 97 | let precision = f.precision().unwrap_or(3); 98 | 99 | let (mut value, unit) = self.get_recoverable_unit(false, precision); 100 | 101 | value = value.normalize(); 102 | 103 | let space_length = if f.sign_plus() { 104 | 4 - unit.as_str().len() 105 | } else if f.sign_minus() { 106 | 0 107 | } else { 108 | 1 109 | }; 110 | 111 | if let Some(mut width) = f.width() { 112 | let l = unit.as_str().len() + space_length; 113 | 114 | if width > l + 1 { 115 | width -= l; 116 | 117 | let alignment = f.align().unwrap_or(Alignment::Left); 118 | 119 | match alignment { 120 | Alignment::Left | Alignment::Center => { 121 | f.write_fmt(format_args!("{value: f.write_fmt(format_args!("{value:>width$}"))?, 124 | } 125 | } else { 126 | f.write_fmt(format_args!("{value}"))?; 127 | } 128 | } else { 129 | f.write_fmt(format_args!("{value}"))?; 130 | } 131 | 132 | for _ in 0..space_length { 133 | f.write_char(' ')?; 134 | } 135 | 136 | f.write_fmt(format_args!("{unit}")) 137 | } else { 138 | Display::fmt(&self.0, f) 139 | } 140 | } 141 | } 142 | 143 | /// Associated functions for building `Bit` instances. 144 | impl Bit { 145 | /// Create a new `Bit` instance from a size in bits. 146 | /// 147 | /// # Examples 148 | /// 149 | /// ``` 150 | /// # use byte_unit::Bit; 151 | /// let bit = Bit::from_u128(15000000).unwrap(); // 15 Mb 152 | /// ``` 153 | /// 154 | /// # Points to Note 155 | /// 156 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise), this function will return `None`. 157 | #[inline] 158 | pub const fn from_u128(size: u128) -> Option { 159 | #[cfg(feature = "u128")] 160 | { 161 | if size < RONNABIT { 162 | Some(Bit(size)) 163 | } else { 164 | None 165 | } 166 | } 167 | 168 | #[cfg(not(feature = "u128"))] 169 | { 170 | if size <= u64::MAX as u128 { 171 | Some(Bit(size as u64)) 172 | } else { 173 | None 174 | } 175 | } 176 | } 177 | 178 | /// Create a new `Bit` instance from a size in bits. 179 | /// 180 | /// # Examples 181 | /// 182 | /// ``` 183 | /// # use byte_unit::Bit; 184 | /// let bit = unsafe { Bit::from_u128_unsafe(15000000) }; // 15 Mb 185 | /// ``` 186 | /// 187 | /// # Safety 188 | /// You must ensure the input **size** is not too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) on your own. 189 | #[inline] 190 | pub const unsafe fn from_u128_unsafe(size: u128) -> Self { 191 | #[cfg(feature = "u128")] 192 | { 193 | Bit(size) 194 | } 195 | 196 | #[cfg(not(feature = "u128"))] 197 | { 198 | Bit(size as u64) 199 | } 200 | } 201 | 202 | /// Create a new `Bit` instance from a size in bits. 203 | /// 204 | /// # Examples 205 | /// 206 | /// ``` 207 | /// # use byte_unit::Bit; 208 | /// let bit = Bit::from_u64(15000000); // 15 Mb 209 | /// ``` 210 | #[inline] 211 | pub const fn from_u64(size: u64) -> Self { 212 | #[cfg(feature = "u128")] 213 | { 214 | Bit(size as u128) 215 | } 216 | 217 | #[cfg(not(feature = "u128"))] 218 | { 219 | Bit(size) 220 | } 221 | } 222 | 223 | /// Create a new `Bit` instance from a size in bits. 224 | /// 225 | /// # Examples 226 | /// 227 | /// ``` 228 | /// # use byte_unit::Bit; 229 | /// let bit = Bit::from_f64(15000000.0).unwrap(); // 15 Mb 230 | /// ``` 231 | /// 232 | /// # Points to Note 233 | /// 234 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) or not greater than or equal to **0**, this function will return `None`. 235 | /// * The fractional part will be rounded up. 236 | #[inline] 237 | pub fn from_f64(size: f64) -> Option { 238 | if size >= 0.0 { 239 | #[cfg(feature = "u128")] 240 | { 241 | let size = ceil_f64(size) as u128; 242 | 243 | if size < RONNABIT { 244 | Some(Bit(size)) 245 | } else { 246 | None 247 | } 248 | } 249 | 250 | #[cfg(not(feature = "u128"))] 251 | { 252 | let size = ceil_f64(size) as u64; 253 | 254 | if size < u64::MAX { 255 | Some(Bit(size)) 256 | } else { 257 | None 258 | } 259 | } 260 | } else { 261 | None 262 | } 263 | } 264 | 265 | /// Create a new `Bit` instance from a size in bits. 266 | /// 267 | /// # Examples 268 | /// 269 | /// ``` 270 | /// # use byte_unit::Bit; 271 | /// let bit = Bit::from_f32(15000000.0).unwrap(); // 15 Mb 272 | /// ``` 273 | /// 274 | /// # Points to Note 275 | /// 276 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) or not greater than or equal to **0**, this function will return `None`. 277 | /// * The fractional part will be rounded up. 278 | #[inline] 279 | pub fn from_f32(size: f32) -> Option { 280 | if size >= 0.0 { 281 | #[cfg(feature = "u128")] 282 | { 283 | let size = ceil_f32(size) as u128; 284 | 285 | if size < RONNABIT { 286 | Some(Bit(size)) 287 | } else { 288 | None 289 | } 290 | } 291 | 292 | #[cfg(not(feature = "u128"))] 293 | { 294 | let size = ceil_f32(size) as u64; 295 | 296 | if size < u64::MAX { 297 | Some(Bit(size)) 298 | } else { 299 | None 300 | } 301 | } 302 | } else { 303 | None 304 | } 305 | } 306 | 307 | /// Create a new `Bit` instance from a size in bits. 308 | /// 309 | /// # Examples 310 | /// 311 | /// ``` 312 | /// # use byte_unit::Bit; 313 | /// let bit = Bit::from_i128(15000000).unwrap(); // 15 Mb 314 | /// ``` 315 | /// 316 | /// # Points to Note 317 | /// 318 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) or negative, this function will return `None`. 319 | #[inline] 320 | pub const fn from_i128(size: i128) -> Option { 321 | if size >= 0 { 322 | Self::from_u128(size as u128) 323 | } else { 324 | None 325 | } 326 | } 327 | 328 | /// Create a new `Bit` instance from a size in bits. 329 | /// 330 | /// # Examples 331 | /// 332 | /// ``` 333 | /// # use byte_unit::Bit; 334 | /// let bit = Bit::from_i64(15000000).unwrap(); // 15 Mb 335 | /// ``` 336 | /// 337 | /// # Points to Note 338 | /// 339 | /// * If the input **size** is negative, this function will return `None`. 340 | #[inline] 341 | pub const fn from_i64(size: i64) -> Option { 342 | if size >= 0 { 343 | Some(Self::from_u64(size as u64)) 344 | } else { 345 | None 346 | } 347 | } 348 | } 349 | 350 | /// Associated functions for building `Bit` instances (with `Unit`). 351 | impl Bit { 352 | /// Create a new `Bit` instance from a size of bits with a unit. 353 | /// 354 | /// # Examples 355 | /// 356 | /// ``` 357 | /// use byte_unit::{Bit, Unit}; 358 | /// 359 | /// let bit = Bit::from_u128_with_unit(15, Unit::Mbit).unwrap(); // 15 Mb 360 | /// ``` 361 | /// 362 | /// # Points to Note 363 | /// 364 | /// * If the calculated bit is too large, this function will return `None`. 365 | #[inline] 366 | pub const fn from_u128_with_unit(size: u128, unit: Unit) -> Option { 367 | let v = { 368 | match unit { 369 | Unit::Bit => size, 370 | _ => match size.checked_mul(unit.as_bits_u128()) { 371 | Some(v) => v, 372 | None => return None, 373 | }, 374 | } 375 | }; 376 | 377 | Self::from_u128(v) 378 | } 379 | 380 | /// Create a new `Bit` instance from a size of bits with a unit. 381 | /// 382 | /// # Examples 383 | /// 384 | /// ``` 385 | /// use byte_unit::{Bit, Unit}; 386 | /// 387 | /// let bit = Bit::from_u64_with_unit(15, Unit::Mbit).unwrap(); // 15 Mb 388 | /// ``` 389 | /// 390 | /// # Points to Note 391 | /// 392 | /// * If the calculated bit is too large, this function will return `None`. 393 | /// * If the input **unit** is `Bit`, the calculated bit will be rounded up. 394 | #[inline] 395 | pub const fn from_u64_with_unit(size: u64, unit: Unit) -> Option { 396 | #[cfg(feature = "u128")] 397 | { 398 | Self::from_u128_with_unit(size as u128, unit) 399 | } 400 | 401 | #[cfg(not(feature = "u128"))] 402 | { 403 | let v = { 404 | match unit { 405 | Unit::Bit => size, 406 | _ => match size.checked_mul(unit.as_bits_u64()) { 407 | Some(v) => v, 408 | None => return None, 409 | }, 410 | } 411 | }; 412 | 413 | Some(Self::from_u64(v)) 414 | } 415 | } 416 | 417 | /// Create a new `Bit` instance from a size of bits with a unit. 418 | /// 419 | /// # Examples 420 | /// 421 | /// ``` 422 | /// use byte_unit::{Bit, Unit}; 423 | /// 424 | /// let bit = Bit::from_f64_with_unit(15.0, Unit::Mbit).unwrap(); // 15 Mb 425 | /// ``` 426 | /// 427 | /// # Points to Note 428 | /// 429 | /// * If the calculated bit is too large or not greater than or equal to **0**, this function will return `None`. 430 | /// * The calculated bit will be rounded up. 431 | #[inline] 432 | pub fn from_f64_with_unit(size: f64, unit: Unit) -> Option { 433 | match Decimal::from_f64(size) { 434 | Some(size) => Self::from_decimal_with_unit(size, unit), 435 | None => None, 436 | } 437 | } 438 | 439 | /// Create a new `Bit` instance from a size of bits with a unit. 440 | /// 441 | /// # Examples 442 | /// 443 | /// ``` 444 | /// use byte_unit::{Bit, Unit}; 445 | /// 446 | /// let bit = Bit::from_f32_with_unit(15.0, Unit::Mbit).unwrap(); // 15 Mb 447 | /// ``` 448 | /// 449 | /// # Points to Note 450 | /// 451 | /// * If the calculated bit is too large or not greater than or equal to **0**, this function will return `None`. 452 | /// * The calculated bit will be rounded up. 453 | #[inline] 454 | pub fn from_f32_with_unit(size: f32, unit: Unit) -> Option { 455 | match Decimal::from_f32(size) { 456 | Some(size) => Self::from_decimal_with_unit(size, unit), 457 | None => None, 458 | } 459 | } 460 | 461 | /// Create a new `Bit` instance from a size of bits with a unit. 462 | /// 463 | /// # Examples 464 | /// 465 | /// ``` 466 | /// use byte_unit::{Bit, Unit}; 467 | /// 468 | /// let bit = Bit::from_i128_with_unit(15, Unit::Mibit).unwrap(); // 15 Mb 469 | /// ``` 470 | /// 471 | /// # Points to Note 472 | /// 473 | /// * If the calculated bit is too large or negative, this function will return `None`. 474 | #[inline] 475 | pub const fn from_i128_with_unit(size: i128, unit: Unit) -> Option { 476 | if size >= 0 { 477 | Self::from_u128_with_unit(size as u128, unit) 478 | } else { 479 | None 480 | } 481 | } 482 | 483 | /// Create a new `Bit` instance from a size of bits with a unit. 484 | /// 485 | /// # Examples 486 | /// 487 | /// ``` 488 | /// use byte_unit::{Bit, Unit}; 489 | /// 490 | /// let bit = Bit::from_i64_with_unit(15, Unit::Mbit).unwrap(); // 15 Mb 491 | /// ``` 492 | /// 493 | /// # Points to Note 494 | /// 495 | /// * If the calculated bit is too large or negative, this function will return `None`. 496 | #[inline] 497 | pub const fn from_i64_with_unit(size: i64, unit: Unit) -> Option { 498 | if size >= 0 { 499 | Self::from_u64_with_unit(size as u64, unit) 500 | } else { 501 | None 502 | } 503 | } 504 | } 505 | 506 | /// Methods for converting a `Bit` instance into a primitive integer. 507 | impl Bit { 508 | /// Retrieve the bit represented by this `Bit` instance. 509 | /// 510 | /// # Examples 511 | /// 512 | /// ``` 513 | /// use byte_unit::Bit; 514 | /// 515 | /// let bit = Bit::parse_str("123KiB").unwrap(); 516 | /// 517 | /// let result = bit.as_u128(); 518 | /// 519 | /// assert_eq!(1007616, result); 520 | /// ``` 521 | /// 522 | /// ``` 523 | /// use byte_unit::Bit; 524 | /// 525 | /// let bit = Bit::parse_str("123Kib").unwrap(); 526 | /// 527 | /// let result = bit.as_u128(); 528 | /// 529 | /// assert_eq!(125952, result); 530 | /// ``` 531 | #[inline] 532 | pub const fn as_u128(self) -> u128 { 533 | #[cfg(feature = "u128")] 534 | { 535 | self.0 536 | } 537 | 538 | #[cfg(not(feature = "u128"))] 539 | { 540 | self.0 as u128 541 | } 542 | } 543 | 544 | /// Retrieve the bit represented by this `Bit` instance. When the `u128` feature is enabled, if the bit is actually greater than **264 - 1**, it will return **264 - 1**. 545 | /// 546 | /// # Examples 547 | /// 548 | /// ``` 549 | /// use byte_unit::Bit; 550 | /// 551 | /// let bit = Bit::parse_str("1kb").unwrap(); 552 | /// 553 | /// let result = bit.as_u64(); 554 | /// 555 | /// assert_eq!(1000, result); 556 | /// ``` 557 | /// 558 | /// ``` 559 | /// # #[cfg(feature = "u128")] 560 | /// # { 561 | /// use byte_unit::Bit; 562 | /// 563 | /// let bit = Bit::parse_str("1zb").unwrap(); 564 | /// 565 | /// let result = bit.as_u64(); 566 | /// 567 | /// assert_eq!(u64::MAX, result); 568 | /// # } 569 | /// ``` 570 | #[inline] 571 | pub const fn as_u64(self) -> u64 { 572 | #[cfg(feature = "u128")] 573 | { 574 | if self.0 <= u64::MAX as u128 { 575 | self.0 as u64 576 | } else { 577 | u64::MAX 578 | } 579 | } 580 | 581 | #[cfg(not(feature = "u128"))] 582 | { 583 | self.0 584 | } 585 | } 586 | 587 | /// Retrieve the bit represented by this `Bit` instance. 588 | /// 589 | /// # Examples 590 | /// 591 | /// ``` 592 | /// use byte_unit::Bit; 593 | /// 594 | /// let bit = Bit::parse_str("1k").unwrap(); 595 | /// 596 | /// let result = bit.as_u64_checked(); 597 | /// 598 | /// assert_eq!(Some(1000), result); 599 | /// ``` 600 | /// 601 | /// ``` 602 | /// # #[cfg(feature = "u128")] 603 | /// # { 604 | /// use byte_unit::Bit; 605 | /// 606 | /// let bit = Bit::parse_str("1zb").unwrap(); 607 | /// 608 | /// let result = bit.as_u64_checked(); 609 | /// 610 | /// assert_eq!(None, result); 611 | /// # } 612 | /// ``` 613 | #[inline] 614 | pub const fn as_u64_checked(self) -> Option { 615 | #[cfg(feature = "u128")] 616 | { 617 | if self.0 <= u64::MAX as u128 { 618 | Some(self.0 as u64) 619 | } else { 620 | None 621 | } 622 | } 623 | 624 | #[cfg(not(feature = "u128"))] 625 | { 626 | Some(self.0) 627 | } 628 | } 629 | } 630 | 631 | /// Methods for calculation. 632 | impl Bit { 633 | /// Add another `Bit` instance. 634 | /// 635 | /// # Examples 636 | /// 637 | /// ``` 638 | /// use byte_unit::Bit; 639 | /// 640 | /// let bit_1 = Bit::from_u64(1024); 641 | /// let bit_2 = Bit::from_u64(512); 642 | /// 643 | /// let bit = bit_1.add(bit_2).unwrap(); 644 | /// 645 | /// assert_eq!(1536, bit.as_u64()); 646 | /// ``` 647 | /// 648 | /// # Points to Note 649 | /// 650 | /// * If the calculated bit is too large, this function will return `None`. 651 | #[inline] 652 | pub const fn add(self, rhs: Bit) -> Option { 653 | match self.0.checked_add(rhs.0) { 654 | Some(v) => Some(Bit(v)), 655 | None => None, 656 | } 657 | } 658 | 659 | /// Subtract another `Bit` instance. 660 | /// 661 | /// # Examples 662 | /// 663 | /// ``` 664 | /// use byte_unit::Bit; 665 | /// 666 | /// let bit_1 = Bit::from_u64(1024); 667 | /// let bit_2 = Bit::from_u64(512); 668 | /// 669 | /// let bit = bit_1.subtract(bit_2).unwrap(); 670 | /// 671 | /// assert_eq!(512, bit.as_u64()); 672 | /// ``` 673 | /// 674 | /// # Points to Note 675 | /// 676 | /// * If the right-hand side is bigger then this `Bit` instance, this function will return `None`. 677 | #[inline] 678 | pub const fn subtract(self, rhs: Bit) -> Option { 679 | match self.0.checked_sub(rhs.0) { 680 | Some(v) => Some(Bit(v)), 681 | None => None, 682 | } 683 | } 684 | 685 | /// Multiplied by an unsigned integer. 686 | /// 687 | /// # Examples 688 | /// 689 | /// ``` 690 | /// use byte_unit::Bit; 691 | /// 692 | /// let count = 100; 693 | /// let bit = Bit::from_u64(1024); 694 | /// 695 | /// let total_bit = bit.multiply(100).unwrap(); 696 | /// 697 | /// assert_eq!(102400, total_bit.as_u64()); 698 | /// ``` 699 | /// 700 | /// # Points to Note 701 | /// 702 | /// * If the calculated bit is too large, this function will return `None`. 703 | #[allow(unexpected_cfgs)] 704 | #[inline] 705 | pub const fn multiply(self, rhs: usize) -> Option { 706 | #[cfg(feature = "u128")] 707 | { 708 | match self.0.checked_mul(rhs as u128) { 709 | Some(v) => Some(Bit(v)), 710 | None => None, 711 | } 712 | } 713 | 714 | #[cfg(not(feature = "u128"))] 715 | { 716 | #[cfg(target_pointer_width = "128")] 717 | { 718 | if rhs > u64::MAX as usize { 719 | return None; 720 | } 721 | } 722 | 723 | match self.0.checked_mul(rhs as u64) { 724 | Some(v) => Some(Bit(v)), 725 | None => None, 726 | } 727 | } 728 | } 729 | 730 | /// Divided by an unsigned integer. 731 | /// 732 | /// # Examples 733 | /// 734 | /// ``` 735 | /// use byte_unit::Bit; 736 | /// 737 | /// let count = 100; 738 | /// let bit = Bit::from_u64(1024); 739 | /// 740 | /// let total_bit = bit.divide(100).unwrap(); 741 | /// 742 | /// assert_eq!(10, total_bit.as_u64()); 743 | /// ``` 744 | /// 745 | /// # Points to Note 746 | /// 747 | /// * If the input right-hand side is zero, this function will return `None`. 748 | /// * The result will be rounded down. 749 | #[allow(unexpected_cfgs)] 750 | #[inline] 751 | pub const fn divide(self, rhs: usize) -> Option { 752 | #[cfg(feature = "u128")] 753 | { 754 | match self.0.checked_div(rhs as u128) { 755 | Some(v) => Some(Bit(v)), 756 | None => None, 757 | } 758 | } 759 | 760 | #[cfg(not(feature = "u128"))] 761 | { 762 | #[cfg(target_pointer_width = "128")] 763 | { 764 | if rhs > u64::MAX as usize { 765 | return None; 766 | } 767 | } 768 | 769 | match self.0.checked_div(rhs as u64) { 770 | Some(v) => Some(Bit(v)), 771 | None => None, 772 | } 773 | } 774 | } 775 | 776 | #[inline] 777 | pub(crate) const fn mul_8(self) -> Bit { 778 | Bit(self.0 * 8) 779 | } 780 | } 781 | 782 | /// Methods for finding an unit. 783 | impl Bit { 784 | /// Obtain the largest unit which is the greatest factor of this `Bit` instance. 785 | /// 786 | /// # Examples 787 | /// 788 | /// ``` 789 | /// use byte_unit::{Bit, Unit}; 790 | /// 791 | /// let bit = Bit::from_u64(3145728); 792 | /// 793 | /// let (n, unit) = bit.get_exact_unit(true); 794 | /// 795 | /// assert_eq!(3, n); 796 | /// assert_eq!(Unit::Mibit, unit); 797 | /// ``` 798 | /// 799 | /// ``` 800 | /// use byte_unit::{Bit, Unit}; 801 | /// 802 | /// let bit = Bit::from_u64(24000000); 803 | /// 804 | /// let (n, unit) = bit.get_exact_unit(true); 805 | /// 806 | /// assert_eq!(3, n); 807 | /// assert_eq!(Unit::MB, unit); 808 | /// ``` 809 | /// 810 | /// ``` 811 | /// use byte_unit::{Bit, Unit}; 812 | /// 813 | /// let bit = Bit::from_u64(24000000); 814 | /// 815 | /// let (n, unit) = bit.get_exact_unit(false); 816 | /// 817 | /// assert_eq!(24, n); 818 | /// assert_eq!(Unit::Mbit, unit); 819 | /// ``` 820 | #[inline] 821 | pub const fn get_exact_unit(self, allow_in_bytes: bool) -> (u128, Unit) { 822 | let bits_v = self.as_u128(); 823 | 824 | let a = if allow_in_bytes { Unit::get_multiples() } else { Unit::get_multiples_bits() }; 825 | let mut i = a.len() - 1; 826 | 827 | loop { 828 | let unit = a[i]; 829 | 830 | let unit_v = unit.as_bits_u128(); 831 | 832 | if bits_v >= unit_v && bits_v % unit_v == 0 { 833 | return (bits_v / unit_v, unit); 834 | } 835 | 836 | if i == 0 { 837 | break; 838 | } 839 | 840 | i -= 1; 841 | } 842 | 843 | (bits_v, Unit::Bit) 844 | } 845 | } 846 | -------------------------------------------------------------------------------- /src/byte/mod.rs: -------------------------------------------------------------------------------- 1 | mod adjusted; 2 | mod built_in_traits; 3 | mod constants; 4 | mod decimal; 5 | mod parse; 6 | #[cfg(feature = "rocket")] 7 | mod rocket_traits; 8 | #[cfg(feature = "schemars")] 9 | mod schemars_traits; 10 | #[cfg(feature = "serde")] 11 | mod serde_traits; 12 | 13 | use core::fmt::{self, Alignment, Display, Formatter, Write}; 14 | 15 | pub use adjusted::*; 16 | use rust_decimal::prelude::*; 17 | 18 | use crate::{ 19 | common::{ceil_f32, ceil_f64}, 20 | Unit, 21 | }; 22 | 23 | #[cfg(feature = "u128")] 24 | const RONNABYTE: u128 = 1_000_000_000_000_000_000_000_000_000; // RB 25 | 26 | #[cfg(feature = "u128")] 27 | #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Default)] 28 | /// Representing the size in bytes. 29 | pub struct Byte(u128); 30 | 31 | #[cfg(not(feature = "u128"))] 32 | #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Default)] 33 | /// Representing the size in bytes. 34 | pub struct Byte(u64); 35 | 36 | impl Display for Byte { 37 | /// Formats the value using the given formatter. 38 | /// 39 | /// # Examples 40 | /// 41 | /// ``` 42 | /// use byte_unit::{Byte, Unit}; 43 | /// 44 | /// let byte = Byte::from_u64_with_unit(1555, Unit::KB).unwrap(); 45 | /// 46 | /// assert_eq!("1555000", byte.to_string()); 47 | /// ``` 48 | /// 49 | /// ``` 50 | /// use byte_unit::{Byte, UnitType}; 51 | /// 52 | /// let byte_based_2 = Byte::from_u64(10240); 53 | /// let byte_based_10 = Byte::from_u64(10000); 54 | /// 55 | /// assert_eq!("10240", format!("{byte_based_2}")); 56 | /// assert_eq!("10000", format!("{byte_based_10}")); 57 | /// 58 | /// // with an exact unit 59 | /// assert_eq!("10 KiB", format!("{byte_based_2:#}")); 60 | /// assert_eq!("10 KB", format!("{byte_based_10:#}")); 61 | /// 62 | /// // with an exact unit, no spaces between the value and the unit 63 | /// assert_eq!("10KiB", format!("{byte_based_2:-#}")); 64 | /// assert_eq!("10KB", format!("{byte_based_10:-#}")); 65 | /// 66 | /// // with a width, left alignment 67 | /// assert_eq!("10 KiB", format!("{byte_based_2:#10}")); 68 | /// assert_eq!("10 KB", format!("{byte_based_10:#10}")); 69 | /// 70 | /// // with a width, right alignment 71 | /// assert_eq!(" 10 KiB", format!("{byte_based_2:>#10}")); 72 | /// assert_eq!(" 10 KB", format!("{byte_based_10:>#10}")); 73 | /// 74 | /// // with a width, right alignment, more spaces between the value and the unit 75 | /// assert_eq!(" 10 KiB", format!("{byte_based_2:>+#10}")); 76 | /// assert_eq!(" 10 KB", format!("{byte_based_10:>+#10}")); 77 | /// ``` 78 | /// 79 | /// ``` 80 | /// use byte_unit::{Byte, UnitType}; 81 | /// 82 | /// let byte = Byte::from_u64(3211776); 83 | /// 84 | /// assert_eq!("3211776", format!("{byte}")); 85 | /// 86 | /// // with a unit, still precisely 87 | /// assert_eq!("3136.5 KiB", format!("{byte:#}")); 88 | /// 89 | /// // with a unit and a larger precision (default is 3), still precisely 90 | /// assert_eq!("3.211776 MB", format!("{byte:#.6}")); 91 | /// 92 | /// // with a unit and a smaller precision (default is 3), still precisely 93 | /// assert_eq!("3211776 B", format!("{byte:#.0}")); 94 | /// ``` 95 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 96 | if f.alternate() { 97 | let precision = f.precision().unwrap_or(3); 98 | 99 | let (mut value, unit) = self.get_recoverable_unit(false, precision); 100 | 101 | value = value.normalize(); 102 | 103 | let space_length = if f.sign_plus() { 104 | 4 - unit.as_str().len() 105 | } else if f.sign_minus() { 106 | 0 107 | } else { 108 | 1 109 | }; 110 | 111 | if let Some(mut width) = f.width() { 112 | let l = unit.as_str().len() + space_length; 113 | 114 | if width > l + 1 { 115 | width -= l; 116 | 117 | let alignment = f.align().unwrap_or(Alignment::Left); 118 | 119 | match alignment { 120 | Alignment::Left | Alignment::Center => { 121 | f.write_fmt(format_args!("{value: f.write_fmt(format_args!("{value:>width$}"))?, 124 | } 125 | } else { 126 | f.write_fmt(format_args!("{value}"))?; 127 | } 128 | } else { 129 | f.write_fmt(format_args!("{value}"))?; 130 | } 131 | 132 | for _ in 0..space_length { 133 | f.write_char(' ')?; 134 | } 135 | 136 | f.write_fmt(format_args!("{unit}")) 137 | } else { 138 | Display::fmt(&self.0, f) 139 | } 140 | } 141 | } 142 | 143 | /// Associated functions for building `Byte` instances. 144 | impl Byte { 145 | /// Create a new `Byte` instance from a size in bytes. 146 | /// 147 | /// # Examples 148 | /// 149 | /// ``` 150 | /// # use byte_unit::Byte; 151 | /// let byte = Byte::from_u128(15000000).unwrap(); // 15 MB 152 | /// ``` 153 | /// 154 | /// # Points to Note 155 | /// 156 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise), this function will return `None`. 157 | #[inline] 158 | pub const fn from_u128(size: u128) -> Option { 159 | #[cfg(feature = "u128")] 160 | { 161 | if size < RONNABYTE { 162 | Some(Byte(size)) 163 | } else { 164 | None 165 | } 166 | } 167 | 168 | #[cfg(not(feature = "u128"))] 169 | { 170 | if size <= u64::MAX as u128 { 171 | Some(Byte(size as u64)) 172 | } else { 173 | None 174 | } 175 | } 176 | } 177 | 178 | /// Create a new `Byte` instance from a size in bytes. 179 | /// 180 | /// # Examples 181 | /// 182 | /// ``` 183 | /// # use byte_unit::Byte; 184 | /// let byte = unsafe { Byte::from_u128_unsafe(15000000) }; // 15 MB 185 | /// ``` 186 | /// 187 | /// # Safety 188 | /// You must ensure the input **size** is not too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) on your own. 189 | #[inline] 190 | pub const unsafe fn from_u128_unsafe(size: u128) -> Self { 191 | #[cfg(feature = "u128")] 192 | { 193 | Byte(size) 194 | } 195 | 196 | #[cfg(not(feature = "u128"))] 197 | { 198 | Byte(size as u64) 199 | } 200 | } 201 | 202 | /// Create a new `Byte` instance from a size in bytes. 203 | /// 204 | /// # Examples 205 | /// 206 | /// ``` 207 | /// # use byte_unit::Byte; 208 | /// let byte = Byte::from_u64(15000000); // 15 MB 209 | /// ``` 210 | #[inline] 211 | pub const fn from_u64(size: u64) -> Self { 212 | #[cfg(feature = "u128")] 213 | { 214 | Byte(size as u128) 215 | } 216 | 217 | #[cfg(not(feature = "u128"))] 218 | { 219 | Byte(size) 220 | } 221 | } 222 | 223 | /// Create a new `Byte` instance from a size in bytes. 224 | /// 225 | /// # Examples 226 | /// 227 | /// ``` 228 | /// # use byte_unit::Byte; 229 | /// let byte = Byte::from_f64(15000000.0).unwrap(); // 15 MB 230 | /// ``` 231 | /// 232 | /// # Points to Note 233 | /// 234 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) or not greater than or equal to **0**, this function will return `None`. 235 | /// * The fractional part will be rounded up. 236 | #[inline] 237 | pub fn from_f64(size: f64) -> Option { 238 | if size >= 0.0 { 239 | #[cfg(feature = "u128")] 240 | { 241 | let size = ceil_f64(size) as u128; 242 | 243 | if size < RONNABYTE { 244 | Some(Byte(size)) 245 | } else { 246 | None 247 | } 248 | } 249 | 250 | #[cfg(not(feature = "u128"))] 251 | { 252 | let size = ceil_f64(size) as u64; 253 | 254 | if size < u64::MAX { 255 | Some(Byte(size)) 256 | } else { 257 | None 258 | } 259 | } 260 | } else { 261 | None 262 | } 263 | } 264 | 265 | /// Create a new `Byte` instance from a size in bytes. 266 | /// 267 | /// # Examples 268 | /// 269 | /// ``` 270 | /// # use byte_unit::Byte; 271 | /// let byte = Byte::from_f32(15000000.0).unwrap(); // 15 MB 272 | /// ``` 273 | /// 274 | /// # Points to Note 275 | /// 276 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) or not greater than or equal to **0**, this function will return `None`. 277 | /// * The fractional part will be rounded up. 278 | #[inline] 279 | pub fn from_f32(size: f32) -> Option { 280 | if size >= 0.0 { 281 | #[cfg(feature = "u128")] 282 | { 283 | let size = ceil_f32(size) as u128; 284 | 285 | if size < RONNABYTE { 286 | Some(Byte(size)) 287 | } else { 288 | None 289 | } 290 | } 291 | 292 | #[cfg(not(feature = "u128"))] 293 | { 294 | let size = ceil_f32(size) as u64; 295 | 296 | if size < u64::MAX { 297 | Some(Byte(size)) 298 | } else { 299 | None 300 | } 301 | } 302 | } else { 303 | None 304 | } 305 | } 306 | 307 | /// Create a new `Byte` instance from a size in bytes. 308 | /// 309 | /// # Examples 310 | /// 311 | /// ``` 312 | /// # use byte_unit::Byte; 313 | /// let byte = Byte::from_i128(15000000).unwrap(); // 15 MB 314 | /// ``` 315 | /// 316 | /// # Points to Note 317 | /// 318 | /// * If the input **size** is too large (the maximum is **1027 - 1** if the `u128` feature is enabled, or **264 - 1** otherwise) or negative, this function will return `None`. 319 | #[inline] 320 | pub const fn from_i128(size: i128) -> Option { 321 | if size >= 0 { 322 | Self::from_u128(size as u128) 323 | } else { 324 | None 325 | } 326 | } 327 | 328 | /// Create a new `Byte` instance from a size in bytes. 329 | /// 330 | /// # Examples 331 | /// 332 | /// ``` 333 | /// # use byte_unit::Byte; 334 | /// let byte = Byte::from_i64(15000000).unwrap(); // 15 MB 335 | /// ``` 336 | /// 337 | /// # Points to Note 338 | /// 339 | /// * If the input **size** is negative, this function will return `None`. 340 | #[inline] 341 | pub const fn from_i64(size: i64) -> Option { 342 | if size >= 0 { 343 | Some(Self::from_u64(size as u64)) 344 | } else { 345 | None 346 | } 347 | } 348 | } 349 | 350 | /// Associated functions for building `Byte` instances (with `Unit`). 351 | impl Byte { 352 | /// Create a new `Byte` instance from a size of bytes with a unit. 353 | /// 354 | /// # Examples 355 | /// 356 | /// ``` 357 | /// use byte_unit::{Byte, Unit}; 358 | /// 359 | /// let byte = Byte::from_u128_with_unit(15, Unit::MB).unwrap(); // 15 MB 360 | /// ``` 361 | /// 362 | /// # Points to Note 363 | /// 364 | /// * If the calculated byte is too large, this function will return `None`. 365 | /// * If the input **unit** is `Bit`, the calculated byte will be rounded up. 366 | #[inline] 367 | pub const fn from_u128_with_unit(size: u128, unit: Unit) -> Option { 368 | let v = { 369 | match unit { 370 | Unit::Bit => { 371 | if size & 11 > 0 { 372 | (size >> 3) + 1 373 | } else { 374 | size >> 3 375 | } 376 | }, 377 | Unit::B => size, 378 | _ => match size.checked_mul(unit.as_bytes_u128()) { 379 | Some(v) => v, 380 | None => return None, 381 | }, 382 | } 383 | }; 384 | 385 | Self::from_u128(v) 386 | } 387 | 388 | /// Create a new `Byte` instance from a size of bytes with a unit. 389 | /// 390 | /// # Examples 391 | /// 392 | /// ``` 393 | /// use byte_unit::{Byte, Unit}; 394 | /// 395 | /// let byte = Byte::from_u64_with_unit(15, Unit::MB).unwrap(); // 15 MB 396 | /// ``` 397 | /// 398 | /// # Points to Note 399 | /// 400 | /// * If the calculated byte is too large, this function will return `None`. 401 | /// * If the input **unit** is `Bit`, the calculated byte will be rounded up. 402 | #[inline] 403 | pub const fn from_u64_with_unit(size: u64, unit: Unit) -> Option { 404 | #[cfg(feature = "u128")] 405 | { 406 | Self::from_u128_with_unit(size as u128, unit) 407 | } 408 | 409 | #[cfg(not(feature = "u128"))] 410 | { 411 | let v = { 412 | match unit { 413 | Unit::Bit => { 414 | if size & 11 > 0 { 415 | (size >> 3) + 1 416 | } else { 417 | size >> 3 418 | } 419 | }, 420 | Unit::B => size, 421 | _ => match size.checked_mul(unit.as_bytes_u64()) { 422 | Some(v) => v, 423 | None => return None, 424 | }, 425 | } 426 | }; 427 | 428 | Some(Self::from_u64(v)) 429 | } 430 | } 431 | 432 | /// Create a new `Byte` instance from a size of bytes with a unit. 433 | /// 434 | /// # Examples 435 | /// 436 | /// ``` 437 | /// use byte_unit::{Byte, Unit}; 438 | /// 439 | /// let byte = Byte::from_f64_with_unit(15.0, Unit::MB).unwrap(); // 15 MB 440 | /// ``` 441 | /// 442 | /// # Points to Note 443 | /// 444 | /// * If the calculated byte is too large or not greater than or equal to **0**, this function will return `None`. 445 | /// * The calculated byte will be rounded up. 446 | #[inline] 447 | pub fn from_f64_with_unit(size: f64, unit: Unit) -> Option { 448 | match Decimal::from_f64(size) { 449 | Some(size) => Self::from_decimal_with_unit(size, unit), 450 | None => None, 451 | } 452 | } 453 | 454 | /// Create a new `Byte` instance from a size of bytes with a unit. 455 | /// 456 | /// # Examples 457 | /// 458 | /// ``` 459 | /// use byte_unit::{Byte, Unit}; 460 | /// 461 | /// let byte = Byte::from_f32_with_unit(15.0, Unit::MB).unwrap(); // 15 MB 462 | /// ``` 463 | /// 464 | /// # Points to Note 465 | /// 466 | /// * If the calculated byte is too large or not greater than or equal to **0**, this function will return `None`. 467 | /// * The calculated byte will be rounded up. 468 | #[inline] 469 | pub fn from_f32_with_unit(size: f32, unit: Unit) -> Option { 470 | match Decimal::from_f32(size) { 471 | Some(size) => Self::from_decimal_with_unit(size, unit), 472 | None => None, 473 | } 474 | } 475 | 476 | /// Create a new `Byte` instance from a size of bytes with a unit. 477 | /// 478 | /// # Examples 479 | /// 480 | /// ``` 481 | /// use byte_unit::{Byte, Unit}; 482 | /// 483 | /// let byte = Byte::from_i128_with_unit(15, Unit::MB).unwrap(); // 15 MB 484 | /// ``` 485 | /// 486 | /// # Points to Note 487 | /// 488 | /// * If the calculated byte is too large or negative, this function will return `None`. 489 | /// * The calculated byte will be rounded up. 490 | #[inline] 491 | pub const fn from_i128_with_unit(size: i128, unit: Unit) -> Option { 492 | if size >= 0 { 493 | Self::from_u128_with_unit(size as u128, unit) 494 | } else { 495 | None 496 | } 497 | } 498 | 499 | /// Create a new `Byte` instance from a size of bytes with a unit. 500 | /// 501 | /// # Examples 502 | /// 503 | /// ``` 504 | /// use byte_unit::{Byte, Unit}; 505 | /// 506 | /// let byte = Byte::from_i64_with_unit(15, Unit::MB).unwrap(); // 15 MB 507 | /// ``` 508 | /// 509 | /// # Points to Note 510 | /// 511 | /// * If the calculated byte is too large or negative, this function will return `None`. 512 | /// * The calculated byte will be rounded up. 513 | #[inline] 514 | pub const fn from_i64_with_unit(size: i64, unit: Unit) -> Option { 515 | if size >= 0 { 516 | Self::from_u64_with_unit(size as u64, unit) 517 | } else { 518 | None 519 | } 520 | } 521 | } 522 | 523 | /// Methods for converting a `Byte` instance into a primitive integer. 524 | impl Byte { 525 | /// Retrieve the byte represented by this `Byte` instance. 526 | /// 527 | /// # Examples 528 | /// 529 | /// ``` 530 | /// use byte_unit::Byte; 531 | /// 532 | /// let byte = Byte::parse_str("123Kib", true).unwrap(); 533 | /// 534 | /// let result = byte.as_u128(); 535 | /// 536 | /// assert_eq!(125952, result); 537 | /// ``` 538 | /// 539 | /// ``` 540 | /// use byte_unit::Byte; 541 | /// 542 | /// let byte = Byte::parse_str("123Kib", false).unwrap(); 543 | /// 544 | /// let result = byte.as_u128(); 545 | /// 546 | /// assert_eq!(15744, result); 547 | /// ``` 548 | #[inline] 549 | pub const fn as_u128(self) -> u128 { 550 | #[cfg(feature = "u128")] 551 | { 552 | self.0 553 | } 554 | 555 | #[cfg(not(feature = "u128"))] 556 | { 557 | self.0 as u128 558 | } 559 | } 560 | 561 | /// Retrieve the byte represented by this `Byte` instance. When the `u128` feature is enabled, if the byte is actually greater than **264 - 1**, it will return **264 - 1**. 562 | /// 563 | /// # Examples 564 | /// 565 | /// ``` 566 | /// use byte_unit::Byte; 567 | /// 568 | /// let byte = Byte::parse_str("1kb", true).unwrap(); 569 | /// 570 | /// let result = byte.as_u64(); 571 | /// 572 | /// assert_eq!(1000, result); 573 | /// ``` 574 | /// 575 | /// ``` 576 | /// # #[cfg(feature = "u128")] 577 | /// # { 578 | /// use byte_unit::Byte; 579 | /// 580 | /// let byte = Byte::parse_str("1zb", true).unwrap(); 581 | /// 582 | /// let result = byte.as_u64(); 583 | /// 584 | /// assert_eq!(u64::MAX, result); 585 | /// # } 586 | /// ``` 587 | #[inline] 588 | pub const fn as_u64(self) -> u64 { 589 | #[cfg(feature = "u128")] 590 | { 591 | if self.0 <= u64::MAX as u128 { 592 | self.0 as u64 593 | } else { 594 | u64::MAX 595 | } 596 | } 597 | 598 | #[cfg(not(feature = "u128"))] 599 | { 600 | self.0 601 | } 602 | } 603 | 604 | /// Retrieve the byte represented by this `Byte` instance. 605 | /// 606 | /// # Examples 607 | /// 608 | /// ``` 609 | /// use byte_unit::Byte; 610 | /// 611 | /// let byte = Byte::parse_str("1kb", true).unwrap(); 612 | /// 613 | /// let result = byte.as_u64_checked(); 614 | /// 615 | /// assert_eq!(Some(1000), result); 616 | /// ``` 617 | /// 618 | /// ``` 619 | /// # #[cfg(feature = "u128")] 620 | /// # { 621 | /// use byte_unit::Byte; 622 | /// 623 | /// let byte = Byte::parse_str("1zb", true).unwrap(); 624 | /// 625 | /// let result = byte.as_u64_checked(); 626 | /// 627 | /// assert_eq!(None, result); 628 | /// # } 629 | /// ``` 630 | #[inline] 631 | pub const fn as_u64_checked(self) -> Option { 632 | #[cfg(feature = "u128")] 633 | { 634 | if self.0 <= u64::MAX as u128 { 635 | Some(self.0 as u64) 636 | } else { 637 | None 638 | } 639 | } 640 | 641 | #[cfg(not(feature = "u128"))] 642 | { 643 | Some(self.0) 644 | } 645 | } 646 | } 647 | 648 | /// Methods for calculation. 649 | impl Byte { 650 | /// Add another `Byte` instance. 651 | /// 652 | /// # Examples 653 | /// 654 | /// ``` 655 | /// use byte_unit::Byte; 656 | /// 657 | /// let byte_1 = Byte::from_u64(1024); 658 | /// let byte_2 = Byte::from_u64(512); 659 | /// 660 | /// let byte = byte_1.add(byte_2).unwrap(); 661 | /// 662 | /// assert_eq!(1536, byte.as_u64()); 663 | /// ``` 664 | /// 665 | /// # Points to Note 666 | /// 667 | /// * If the calculated byte is too large, this function will return `None`. 668 | #[inline] 669 | pub const fn add(self, rhs: Byte) -> Option { 670 | match self.0.checked_add(rhs.0) { 671 | Some(v) => Some(Byte(v)), 672 | None => None, 673 | } 674 | } 675 | 676 | /// Subtract another `Byte` instance. 677 | /// 678 | /// # Examples 679 | /// 680 | /// ``` 681 | /// use byte_unit::Byte; 682 | /// 683 | /// let byte_1 = Byte::from_u64(1024); 684 | /// let byte_2 = Byte::from_u64(512); 685 | /// 686 | /// let byte = byte_1.subtract(byte_2).unwrap(); 687 | /// 688 | /// assert_eq!(512, byte.as_u64()); 689 | /// ``` 690 | /// 691 | /// # Points to Note 692 | /// 693 | /// * If the right-hand side is bigger then this `Byte` instance, this function will return `None`. 694 | #[inline] 695 | pub const fn subtract(self, rhs: Byte) -> Option { 696 | match self.0.checked_sub(rhs.0) { 697 | Some(v) => Some(Byte(v)), 698 | None => None, 699 | } 700 | } 701 | 702 | /// Multiplied by an unsigned integer. 703 | /// 704 | /// # Examples 705 | /// 706 | /// ``` 707 | /// use byte_unit::Byte; 708 | /// 709 | /// let count = 100; 710 | /// let byte = Byte::from_u64(1024); 711 | /// 712 | /// let total_byte = byte.multiply(100).unwrap(); 713 | /// 714 | /// assert_eq!(102400, total_byte.as_u64()); 715 | /// ``` 716 | /// 717 | /// # Points to Note 718 | /// 719 | /// * If the calculated byte is too large, this function will return `None`. 720 | #[allow(unexpected_cfgs)] 721 | #[inline] 722 | pub const fn multiply(self, rhs: usize) -> Option { 723 | #[cfg(feature = "u128")] 724 | { 725 | match self.0.checked_mul(rhs as u128) { 726 | Some(v) => Some(Byte(v)), 727 | None => None, 728 | } 729 | } 730 | 731 | #[cfg(not(feature = "u128"))] 732 | { 733 | #[cfg(target_pointer_width = "128")] 734 | { 735 | if rhs > u64::MAX as usize { 736 | return None; 737 | } 738 | } 739 | 740 | match self.0.checked_mul(rhs as u64) { 741 | Some(v) => Some(Byte(v)), 742 | None => None, 743 | } 744 | } 745 | } 746 | 747 | /// Divided by an unsigned integer. 748 | /// 749 | /// # Examples 750 | /// 751 | /// ``` 752 | /// use byte_unit::Byte; 753 | /// 754 | /// let count = 100; 755 | /// let byte = Byte::from_u64(1024); 756 | /// 757 | /// let total_byte = byte.divide(100).unwrap(); 758 | /// 759 | /// assert_eq!(10, total_byte.as_u64()); 760 | /// ``` 761 | /// 762 | /// # Points to Note 763 | /// 764 | /// * If the input right-hand side is zero, this function will return `None`. 765 | /// * The result will be rounded down. 766 | #[allow(unexpected_cfgs)] 767 | #[inline] 768 | pub const fn divide(self, rhs: usize) -> Option { 769 | #[cfg(feature = "u128")] 770 | { 771 | match self.0.checked_div(rhs as u128) { 772 | Some(v) => Some(Byte(v)), 773 | None => None, 774 | } 775 | } 776 | 777 | #[cfg(not(feature = "u128"))] 778 | { 779 | #[cfg(target_pointer_width = "128")] 780 | { 781 | if rhs > u64::MAX as usize { 782 | return None; 783 | } 784 | } 785 | 786 | match self.0.checked_div(rhs as u64) { 787 | Some(v) => Some(Byte(v)), 788 | None => None, 789 | } 790 | } 791 | } 792 | 793 | #[inline] 794 | pub(crate) const fn div_8(self) -> Byte { 795 | Byte(self.0 / 8) 796 | } 797 | } 798 | 799 | /// Methods for finding an unit. 800 | impl Byte { 801 | /// Obtain the largest unit which is the greatest factor of this `Byte` instance. 802 | /// 803 | /// # Examples 804 | /// 805 | /// ``` 806 | /// use byte_unit::{Byte, Unit}; 807 | /// 808 | /// let byte = Byte::from_u64(3145728); 809 | /// 810 | /// let (n, unit) = byte.get_exact_unit(true); 811 | /// 812 | /// assert_eq!(3, n); 813 | /// assert_eq!(Unit::MiB, unit); 814 | /// ``` 815 | /// 816 | /// ``` 817 | /// use byte_unit::{Byte, Unit}; 818 | /// 819 | /// let byte = Byte::from_u64(375000); 820 | /// 821 | /// let (n, unit) = byte.get_exact_unit(true); 822 | /// 823 | /// assert_eq!(3, n); 824 | /// assert_eq!(Unit::Mbit, unit); 825 | /// ``` 826 | /// 827 | /// ``` 828 | /// use byte_unit::{Byte, Unit}; 829 | /// 830 | /// let byte = Byte::from_u64(375000); 831 | /// 832 | /// let (n, unit) = byte.get_exact_unit(false); 833 | /// 834 | /// assert_eq!(375, n); 835 | /// assert_eq!(Unit::KB, unit); 836 | /// ``` 837 | #[inline] 838 | pub const fn get_exact_unit(self, allow_in_bits: bool) -> (u128, Unit) { 839 | let bytes_v = self.as_u128(); 840 | 841 | let a = if allow_in_bits { Unit::get_multiples() } else { Unit::get_multiples_bytes() }; 842 | let mut i = a.len() - 1; 843 | 844 | loop { 845 | let unit = a[i]; 846 | 847 | let unit_v = unit.as_bytes_u128(); 848 | 849 | if bytes_v >= unit_v && bytes_v % unit_v == 0 { 850 | return (bytes_v / unit_v, unit); 851 | } 852 | 853 | if i == 0 { 854 | break; 855 | } 856 | 857 | i -= 1; 858 | } 859 | 860 | (bytes_v, Unit::B) 861 | } 862 | } 863 | --------------------------------------------------------------------------------