From 68b1763d239ab3d95b644716f47923beb9263440 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Fri, 10 Jan 2025 12:52:43 -0600 Subject: [PATCH] Simplify the doc annotations. This removes all but the lint internal features, making it so `doc_autocfg` can work as expected, and avoiding disconnects between the docs and the required features. This cannot have backward-incompatible issues since we forced compile errors if incorrect feature combinations were used. --- CHANGELOG | 1 + extras/benchmark/algorithm/Cargo.toml | 4 +- extras/benchmark/parse-float/Cargo.toml | 4 +- extras/benchmark/parse-integer/Cargo.toml | 4 +- extras/benchmark/write-float/Cargo.toml | 4 +- extras/benchmark/write-integer/Cargo.toml | 4 +- extras/util/Cargo.toml | 12 +- extras/util/tests/div128_tests.rs | 2 +- lexical-core/Cargo.toml | 20 +- lexical-core/src/lib.rs | 92 +++---- lexical-core/tests/issue_97_tests.rs | 2 +- lexical-util/Cargo.toml | 16 +- lexical-util/src/algorithm.rs | 9 +- lexical-util/src/api.rs | 12 +- lexical-util/src/assert.rs | 13 +- lexical-util/src/constants.rs | 3 +- lexical-util/src/digit.rs | 19 +- lexical-util/src/div128.rs | 10 +- lexical-util/src/extended_float.rs | 3 +- lexical-util/src/format.rs | 11 +- lexical-util/src/format_builder.rs | 51 ---- lexical-util/src/iterator.rs | 3 +- lexical-util/src/lib.rs | 27 +- lexical-util/src/libm.rs | 269 +++++++++++++++++++ lexical-util/src/noskip.rs | 2 +- lexical-util/src/num.rs | 299 +--------------------- lexical-util/src/options.rs | 8 +- lexical-util/src/skip.rs | 2 +- lexical-util/src/step.rs | 7 +- lexical-util/tests/algorithm_tests.rs | 6 +- lexical-util/tests/digit_tests.rs | 19 +- lexical-util/tests/iterator_tests.rs | 2 +- lexical-util/tests/num_tests.rs | 4 +- lexical-util/tests/skip_tests.rs | 2 +- lexical-write-float/src/options.rs | 3 - lexical/Cargo.toml | 20 +- lexical/src/lib.rs | 76 ++---- 37 files changed, 436 insertions(+), 609 deletions(-) create mode 100644 lexical-util/src/libm.rs diff --git a/CHANGELOG b/CHANGELOG index 641fadfd..0481b000 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Deprecated `Options::set_*` in our write float API since options should be considered immutable. - Removed `static_assertions` dependency. - Migrate to using an external crate for our half-precision floats. +- Simplify feature detection internally to make auto-doc more reliable. ### Fixed diff --git a/extras/benchmark/algorithm/Cargo.toml b/extras/benchmark/algorithm/Cargo.toml index b22da46a..7677f7de 100644 --- a/extras/benchmark/algorithm/Cargo.toml +++ b/extras/benchmark/algorithm/Cargo.toml @@ -24,8 +24,8 @@ serde_json = "1.0" [features] default = ["std", "integers", "floats", "json"] std = ["lexical-util/std", "lexical-parse-float/std"] -integers = ["lexical-util/integers"] -floats = ["lexical-util/floats"] +integers = ["lexical-util/parse-integers", "lexical-util/write-integers"] +floats = ["lexical-util/parse-floats", "lexical-util/write-floats"] json = [] [[bench]] diff --git a/extras/benchmark/parse-float/Cargo.toml b/extras/benchmark/parse-float/Cargo.toml index a6f5e224..2e0e996d 100644 --- a/extras/benchmark/parse-float/Cargo.toml +++ b/extras/benchmark/parse-float/Cargo.toml @@ -30,8 +30,8 @@ power-of-two = ["lexical-util/power-of-two", "lexical-parse-float/power-of-two"] format = ["lexical-util/format", "lexical-parse-float/format"] compact = ["lexical-util/compact", "lexical-parse-float/compact"] asm = [] -integers = ["lexical-util/integers"] -floats = ["lexical-util/floats"] +floats = ["lexical-util/parse-floats"] +integers = [] # needed for feature detection json = [] [[bench]] diff --git a/extras/benchmark/parse-integer/Cargo.toml b/extras/benchmark/parse-integer/Cargo.toml index df8b28db..f1d1dde8 100644 --- a/extras/benchmark/parse-integer/Cargo.toml +++ b/extras/benchmark/parse-integer/Cargo.toml @@ -29,8 +29,8 @@ radix = ["lexical-util/radix", "lexical-parse-integer/radix"] power-of-two = ["lexical-util/power-of-two", "lexical-parse-integer/power-of-two"] format = ["lexical-util/format", "lexical-parse-integer/format"] compact = ["lexical-util/compact", "lexical-parse-integer/compact"] -integers = ["lexical-util/integers"] -floats = ["lexical-util/floats"] +floats = [] # needed for feature detection +integers = ["lexical-util/parse-integers"] json = [] [[bench]] diff --git a/extras/benchmark/write-float/Cargo.toml b/extras/benchmark/write-float/Cargo.toml index b126b735..811cce68 100644 --- a/extras/benchmark/write-float/Cargo.toml +++ b/extras/benchmark/write-float/Cargo.toml @@ -31,8 +31,8 @@ radix = ["lexical-util/radix", "lexical-write-float/radix"] power-of-two = ["lexical-util/power-of-two", "lexical-write-float/power-of-two"] format = ["lexical-util/format", "lexical-write-float/format"] compact = ["lexical-util/compact", "lexical-write-float/compact"] -integers = ["lexical-util/integers"] -floats = ["lexical-util/floats"] +floats = ["lexical-util/write-floats"] +integers = [] # needed for feature detection json = [] [[bench]] diff --git a/extras/benchmark/write-integer/Cargo.toml b/extras/benchmark/write-integer/Cargo.toml index d5fc9b87..e841f5f4 100644 --- a/extras/benchmark/write-integer/Cargo.toml +++ b/extras/benchmark/write-integer/Cargo.toml @@ -30,8 +30,8 @@ radix = ["lexical-util/radix", "lexical-write-integer/radix"] power-of-two = ["lexical-util/power-of-two", "lexical-write-integer/power-of-two"] format = ["lexical-util/format", "lexical-write-integer/format"] compact = ["lexical-util/compact", "lexical-write-integer/compact"] -integers = ["lexical-util/integers"] -floats = ["lexical-util/floats"] +floats = [] # needed for feature detection +integers = ["lexical-util/write-integers"] json = [] [[bench]] diff --git a/extras/util/Cargo.toml b/extras/util/Cargo.toml index 67b9e9bf..89b08ddf 100644 --- a/extras/util/Cargo.toml +++ b/extras/util/Cargo.toml @@ -27,14 +27,10 @@ std = ["lexical-util/std"] power-of-two = ["lexical-util/power-of-two"] radix = ["lexical-util/radix"] format = ["lexical-util/format"] -write-integers = ["lexical-util/write-integers", "write", "integers"] -write-floats = ["lexical-util/write-floats", "write", "floats"] -parse-integers = ["lexical-util/parse-integers", "parse", "integers"] -parse-floats = ["lexical-util/parse-floats", "parse", "floats"] +write-integers = ["lexical-util/write-integers"] +write-floats = ["lexical-util/write-floats"] +parse-integers = ["lexical-util/parse-integers"] +parse-floats = ["lexical-util/parse-floats"] compact = ["lexical-util/compact"] f16 = ["lexical-util/f16", "parse-floats", "write-floats"] lint = ["lexical-util/lint"] -write = ["lexical-util/write"] -parse = ["lexical-util/parse"] -integers = ["lexical-util/integers"] -floats = ["lexical-util/floats"] diff --git a/extras/util/tests/div128_tests.rs b/extras/util/tests/div128_tests.rs index 245ebd5c..b561a67a 100644 --- a/extras/util/tests/div128_tests.rs +++ b/extras/util/tests/div128_tests.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "compact"))] -#![cfg(feature = "write")] +#![cfg(any(feature = "write-floats", feature = "write-integers"))] mod util; diff --git a/lexical-core/Cargo.toml b/lexical-core/Cargo.toml index d6236f0a..2a197072 100644 --- a/lexical-core/Cargo.toml +++ b/lexical-core/Cargo.toml @@ -59,13 +59,13 @@ std = [ "lexical-parse-float/std" ] # Add support for writing integers. -write-integers = ["lexical-write-integer", "write", "integers"] +write-integers = ["lexical-write-integer"] # Add support for writing floats. -write-floats = ["lexical-write-float", "write", "floats"] +write-floats = ["lexical-write-float"] # Add support for parsing integers. -parse-integers = ["lexical-parse-integer", "parse", "integers"] +parse-integers = ["lexical-parse-integer"] # Add support for parsing floats. -parse-floats = ["lexical-parse-float", "parse", "floats"] +parse-floats = ["lexical-parse-float"] # Add support for parsing power-of-two float strings. power-of-two = [ @@ -117,18 +117,6 @@ lint = [ "lexical-parse-integer?/lint", "lexical-parse-float?/lint" ] -# Add support for writing numbers. -# Library users should use `write-integers` and `write-floats` instead. -write = ["lexical-util/write"] -# Add support for parsing numbers. -# Library users should use `parse-integers` and `parse-floats` instead. -parse = ["lexical-util/parse"] -# Add support for conversions to or from integers. -# Library users should use `write-integers` and `parse-integers` instead. -integers = ["lexical-util/integers"] -# Add support for conversions to or from floats. -# Library users should use `write-floats` and `parse-floats` instead. -floats = ["lexical-util/floats"] # UNSUPPORTED # ----------- diff --git a/lexical-core/src/lib.rs b/lexical-core/src/lib.rs index ee4b4139..2a60b6a8 100644 --- a/lexical-core/src/lib.rs +++ b/lexical-core/src/lib.rs @@ -91,14 +91,14 @@ //! //! #![cfg_attr( - feature = "write", + any(feature = "write-floats", feature = "write-integers"), doc = " [`FormattedSize`]: FormattedSize [`T::FORMATTED_SIZE_DECIMAL`]: FormattedSize::FORMATTED_SIZE_DECIMAL " )] #![cfg_attr( - not(feature = "write"), + not(any(feature = "write-floats", feature = "write-integers")), doc = " [`FormattedSize`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FormattedSize.html [`T::FORMATTED_SIZE_DECIMAL`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FormattedSize.html#associatedconstant.FORMATTED_SIZE_DECIMAL @@ -124,9 +124,12 @@ //! [`core-write`]: core::fmt::Display::fmt //! //! -#![cfg_attr(feature = "write", doc = "- [`write`]: Write a number to string.")] #![cfg_attr( - feature = "parse", + any(feature = "write-floats", feature = "write-integers"), + doc = "- [`write`]: Write a number to string." +)] +#![cfg_attr( + any(feature = "parse-floats", feature = "parse-integers"), doc = " - [`parse`]: Parse a number from string validating the complete string is a number. - [`parse_partial`]: Parse a number from string returning the number and the number @@ -167,25 +170,25 @@ //! //! #![cfg_attr( - all(feature = "write", feature = "floats"), + feature = "write-floats", doc = "[`nan_string`]: WriteFloatOptionsBuilder::nan_string" )] #![cfg_attr( - all(not(feature = "write"), feature = "parse", feature = "floats"), + all(not(feature = "write-floats"), feature = "parse-floats"), doc = "[`nan_string`]: ParseFloatOptionsBuilder::nan_string" )] #![cfg_attr( - any(not(feature = "floats"), all(not(feature = "write"), not(feature = "parse"))), + all(not(feature = "write-floats"), not(feature = "parse-floats")), doc = "[`nan_string`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.nan_string" )] //! //! #![cfg_attr( - feature = "write", + any(feature = "write-floats", feature = "write-integers"), doc = "- [`write_with_options`]: Write a number to string using custom formatting options." )] #![cfg_attr( - feature = "parse", + any(feature = "parse-floats", feature = "parse-integers"), doc = " - [`parse_with_options`]: Parse a number from string using custom formatting options, validating the complete string is a number. @@ -628,21 +631,21 @@ //! //! #![cfg_attr( - feature = "write", + any(feature = "write-floats", feature = "write-integers"), doc = " [`write`]: crate::write [`write_with_options`]: crate::write_with_options " )] #![cfg_attr( - not(feature = "write"), + not(any(feature = "write-floats", feature = "write-integers")), doc = " [`write`]: https://docs.rs/lexical-core/latest/lexical_core/fn.write.html [`write_with_options`]: https://docs.rs/lexical-core/latest/lexical_core/fn.write_with_options.html " )] #![cfg_attr( - feature = "parse", + any(feature = "parse-floats", feature = "parse-integers"), doc = " [`parse`]: crate::parse [`parse_partial`]: crate::parse_partial @@ -651,7 +654,7 @@ " )] #![cfg_attr( - not(feature = "parse"), + not(any(feature = "parse-floats", feature = "parse-integers")), doc = " [`parse`]: https://docs.rs/lexical-core/latest/lexical_core/fn.parse.html [`parse_partial`]: https://docs.rs/lexical-core/latest/lexical_core/fn.parse_partial.html @@ -704,26 +707,6 @@ )] #![cfg_attr(rustfmt, rustfmt_skip)] // reason = "this simplifies our imports" -// Ensure our features are properly enabled. This means no parse without -// parse support, etc. -#[cfg(all(feature = "parse", not(any(feature = "parse-integers", feature = "parse-floats"))))] -compile_error!( - "Do not use the `parse` feature directly. Use `parse-integers` and/or `parse-floats` instead." -); - -#[cfg(all(feature = "write", not(any(feature = "write-integers", feature = "write-floats"))))] -compile_error!( - "Do not use the `write` feature directly. Use `write-integers` and/or `write-floats` instead." -); - -#[cfg(all(feature = "integers", not(any(feature = "write-integers", feature = "parse-integers"))))] -compile_error!("Do not use the `integers` feature directly. Use `write-integers` and/or `parse-integers` instead."); - -#[cfg(all(feature = "floats", not(any(feature = "write-floats", feature = "parse-floats"))))] -compile_error!( - "Do not use the `floats` feature directly. Use `write-floats` and/or `parse-floats` instead." -); - // Re-exports pub use lexical_util::Error; pub use lexical_util::result::Result; @@ -746,11 +729,10 @@ pub use lexical_util::f16::f16; // PARSE -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub use lexical_util::options::ParseOptions; -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] use lexical_util::{from_lexical, from_lexical_with_options}; #[cfg(feature = "parse-floats")] @@ -779,15 +761,13 @@ use lexical_parse_integer::{ // WRITE -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub use lexical_util::options::WriteOptions; -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] use lexical_util::{to_lexical, to_lexical_with_options}; -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub use lexical_util::constants::{FormattedSize, BUFFER_SIZE}; #[cfg(feature = "write-floats")] @@ -812,7 +792,7 @@ use lexical_write_integer::{ToLexical as ToInteger, ToLexicalWithOptions as ToIn // API // --- -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] from_lexical!( "lexical_core", 1234, @@ -821,7 +801,7 @@ from_lexical!( #[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] ); -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] from_lexical_with_options!( "lexical_core", 1234, @@ -831,7 +811,7 @@ from_lexical_with_options!( #[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] ); -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] to_lexical!( "lexical_core", 1234, @@ -839,7 +819,7 @@ to_lexical!( #[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] ); -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] to_lexical_with_options!( "lexical_core", 1234, @@ -856,7 +836,7 @@ to_lexical_with_options!( /// * `from_lexical_with_options` - The internal trait that implements /// `from_lexical`. /// * `options` - The options type to configure settings. -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] macro_rules! from_lexical_impl { ($t:ident, $from:ident, $from_options:ident, $options:ident) => { impl FromLexical for $t { @@ -923,7 +903,7 @@ float_from_lexical! { f32 f64 } /// * `to_lexical_with_options` - The internal trait that implements /// `to_lexical`. /// * `options` - The options type to configure settings. -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] macro_rules! to_lexical_impl { ($t:ident, $to:ident, $to_options:ident, $options:ident) => { impl ToLexical for $t { @@ -1014,8 +994,7 @@ float_to_lexical! { f32 f64 } /// # } /// ``` #[inline] -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub fn write(n: N, bytes: &mut [u8]) -> &mut [u8] { n.to_lexical(bytes) } @@ -1079,8 +1058,7 @@ pub fn write(n: N, bytes: &mut [u8]) -> &mut [u8] { /// # } /// ``` #[inline] -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub fn write_with_options<'a, N: ToLexicalWithOptions, const FORMAT: u128>( n: N, bytes: &'a mut [u8], @@ -1106,8 +1084,7 @@ pub fn write_with_options<'a, N: ToLexicalWithOptions, const FORMAT: u128>( /// # } /// ``` #[inline] -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub fn parse(bytes: &[u8]) -> Result { N::from_lexical(bytes) } @@ -1130,8 +1107,7 @@ pub fn parse(bytes: &[u8]) -> Result { /// # } /// ``` #[inline] -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub fn parse_partial(bytes: &[u8]) -> Result<(N, usize)> { N::from_lexical_partial(bytes) } @@ -1157,8 +1133,7 @@ pub fn parse_partial(bytes: &[u8]) -> Result<(N, usize)> { /// # } /// ``` #[inline] -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub fn parse_with_options( bytes: &[u8], options: &N::Options, @@ -1188,8 +1163,7 @@ pub fn parse_with_options( /// # } /// ``` #[inline] -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub fn parse_partial_with_options( bytes: &[u8], options: &N::Options, diff --git a/lexical-core/tests/issue_97_tests.rs b/lexical-core/tests/issue_97_tests.rs index 3203cdec..50a4f936 100644 --- a/lexical-core/tests/issue_97_tests.rs +++ b/lexical-core/tests/issue_97_tests.rs @@ -1,4 +1,4 @@ -#![cfg(all(feature = "parse", feature = "format"))] +#![cfg(all(any(feature = "parse-floats", feature = "parse-integers"), feature = "format"))] use core::num; diff --git a/lexical-util/Cargo.toml b/lexical-util/Cargo.toml index 30aa44ea..669feefb 100644 --- a/lexical-util/Cargo.toml +++ b/lexical-util/Cargo.toml @@ -39,13 +39,13 @@ radix = ["power-of-two"] # Add support for parsing custom numerical formats. format = [] # Add support for writing integers. -write-integers = ["write", "integers"] +write-integers = [] # Add support for writing floats. -write-floats = ["write", "floats"] +write-floats = [] # Add support for parsing integers. -parse-integers = ["parse", "integers"] +parse-integers = [] # Add support for parsing floats. -parse-floats = ["parse", "floats"] +parse-floats = [] # Reduce code size at the cost of performance. compact = [] # Add support for the `f16` and `b16` half-point floating point numbers. @@ -54,14 +54,6 @@ f16 = ["parse-floats", "write-floats", "float16"] # Internal only features. # Enable the lint checks. lint = [] -# Add support for writing numbers. -write = [] -# Add support for parsing numbers. -parse = [] -# Add support for conversions to or from integers. -integers = [] -# Add support for conversions to or from floats. -floats = [] # UNSUPPORTED # ----------- diff --git a/lexical-util/src/algorithm.rs b/lexical-util/src/algorithm.rs index 06fe6396..c01c7aff 100644 --- a/lexical-util/src/algorithm.rs +++ b/lexical-util/src/algorithm.rs @@ -7,8 +7,7 @@ use crate::num::Integer; /// This is only used in our compact and radix integer formatted, so /// performance isn't the highest consideration here. #[inline(always)] -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub fn copy_to_dst>(dst: &mut [T], src: Bytes) -> usize { let src = src.as_ref(); dst[..src.len()].copy_from_slice(src); @@ -18,16 +17,14 @@ pub fn copy_to_dst>(dst: &mut [T], src: Bytes) -> usi /// Count the number of trailing characters equal to a given value. #[inline(always)] -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub fn rtrim_char_count(slc: &[u8], c: u8) -> usize { slc.iter().rev().take_while(|&&si| si == c).count() } /// Count the number of leading characters equal to a given value. #[inline(always)] -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub fn ltrim_char_count(slc: &[u8], c: u8) -> usize { slc.iter().take_while(|&&si| si == c).count() } diff --git a/lexical-util/src/api.rs b/lexical-util/src/api.rs index 88df0e34..1e783795 100644 --- a/lexical-util/src/api.rs +++ b/lexical-util/src/api.rs @@ -22,8 +22,7 @@ /// /// [`FromLexical`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FromLexical.html #[macro_export] -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] macro_rules! from_lexical { ($name:literal, $value:literal, $t:ty, $len:literal $(, #[$attr:meta])? $(,)?) => { /// Trait for numerical types that can be parsed from bytes. @@ -92,8 +91,7 @@ macro_rules! from_lexical { /// /// [`FromLexicalWithOptions`]: https://docs.rs/lexical-core/latest/lexical_core/trait.FromLexicalWithOptions.html #[macro_export] -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] macro_rules! from_lexical_with_options { ($name:literal, $value:literal, $t:ty, $len:literal, $ops_t:ty $(, #[$attr:meta])? $(,)?) => { /// Trait for numerical types that can be parsed from bytes with custom options. @@ -206,8 +204,7 @@ macro_rules! from_lexical_with_options { /// /// [`ToLexical`]: https://docs.rs/lexical-core/latest/lexical_core/trait.ToLexical.html #[macro_export] -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] macro_rules! to_lexical { ($name:literal, $value:literal, $t:ty $(, #[$attr:meta])? $(,)?) => { /// Trait for numerical types that can be serialized to bytes. @@ -277,8 +274,7 @@ macro_rules! to_lexical { /// /// [`ToLexicalWithOptions`]: https://docs.rs/lexical-core/latest/lexical_core/trait.ToLexicalWithOptions.html #[macro_export] -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] macro_rules! to_lexical_with_options { ($name:literal, $value:literal, $t:ty, $ops_t:ty $(, #[$attr:meta])? $(,)?) => { /// Trait for numerical types that can be serialized to bytes with custom diff --git a/lexical-util/src/assert.rs b/lexical-util/src/assert.rs index 9e01c910..881db493 100644 --- a/lexical-util/src/assert.rs +++ b/lexical-util/src/assert.rs @@ -2,7 +2,7 @@ #![doc(hidden)] -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] use crate::constants::FormattedSize; // RADIX @@ -32,8 +32,8 @@ pub fn debug_assert_radix(radix: u32) { /// Assertion the buffer has sufficient room for the output. #[inline(always)] -#[cfg(all(feature = "power-of-two", feature = "write"))] -pub fn assert_buffer(radix: u32, len: usize) { +#[cfg(all(feature = "power-of-two", any(feature = "write-floats", feature = "write-integers")))] +pub const fn assert_buffer(radix: u32, len: usize) { assert!( match radix { 10 => len >= T::FORMATTED_SIZE_DECIMAL, @@ -45,8 +45,11 @@ pub fn assert_buffer(radix: u32, len: usize) { /// Assertion the buffer has sufficient room for the output. #[inline(always)] -#[cfg(all(not(feature = "power-of-two"), feature = "write"))] -pub fn assert_buffer(_: u32, len: usize) { +#[cfg(all( + not(feature = "power-of-two"), + any(feature = "write-floats", feature = "write-integers") +))] +pub const fn assert_buffer(_: u32, len: usize) { assert!( len >= T::FORMATTED_SIZE_DECIMAL, "Buffer is too small: may overwrite buffer, panicking!" diff --git a/lexical-util/src/constants.rs b/lexical-util/src/constants.rs index 149c52c8..1e6b0c06 100644 --- a/lexical-util/src/constants.rs +++ b/lexical-util/src/constants.rs @@ -1,8 +1,7 @@ //! Pre-defined constants for numeric types. #![doc(hidden)] -#![cfg(feature = "write")] -#![cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#![cfg(any(feature = "write-floats", feature = "write-integers"))] #[cfg(feature = "f16")] use crate::bf16::bf16; diff --git a/lexical-util/src/digit.rs b/lexical-util/src/digit.rs index 40a8380a..6bb08fe4 100644 --- a/lexical-util/src/digit.rs +++ b/lexical-util/src/digit.rs @@ -54,15 +54,7 @@ pub const fn char_is_digit_const(c: u8, radix: u32) -> bool { /// This optimizes for cases where radix is <= 10, and uses a decent, /// match-based fallback algorithm. #[inline(always)] -#[cfg(any(feature = "write", feature = "floats"))] -#[cfg_attr( - docsrs, - doc(cfg(any( - feature = "parse-floats", - feature = "write-floats", - feature = "write-integers", - ))) -)] +#[cfg(any(feature = "parse-floats", feature = "write-floats", feature = "write-integers"))] pub const fn digit_to_char_const(digit: u32, radix: u32) -> u8 { if radix <= 10 || digit < 10 { // Can short-circuit if we know the radix is small at compile time. @@ -81,8 +73,7 @@ pub const fn digit_to_char_const(digit: u32, radix: u32) -> u8 { /// Convert a character to a digit. #[inline(always)] -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub const fn char_to_digit(c: u8, radix: u32) -> Option { // Fallback, still decently fast. let digit = match c { @@ -100,8 +91,7 @@ pub const fn char_to_digit(c: u8, radix: u32) -> Option { /// Determine if a character is a digit. #[inline(always)] -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub const fn char_is_digit(c: u8, radix: u32) -> bool { char_to_digit(c, radix).is_some() } @@ -113,8 +103,7 @@ pub const fn char_is_digit(c: u8, radix: u32) -> bool { /// /// Panics if `digit >= 36`. #[inline(always)] -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub fn digit_to_char(digit: u32) -> u8 { const TABLE: [u8; 36] = [ b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', diff --git a/lexical-util/src/div128.rs b/lexical-util/src/div128.rs index c3569ecd..717793d5 100644 --- a/lexical-util/src/div128.rs +++ b/lexical-util/src/div128.rs @@ -41,14 +41,8 @@ //! [`etc/div128.py`]: https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-util/etc/div128.py #![cfg(not(feature = "compact"))] -#![cfg(feature = "write")] -#![cfg_attr( - docsrs, - doc(cfg(all( - any(feature = "write-floats", feature = "write-integers"), - not(feature = "compact") - ))) -)] +#![cfg(any(feature = "write-floats", feature = "write-integers"))] + use crate::assert::debug_assert_radix; use crate::mul::mulhi; diff --git a/lexical-util/src/extended_float.rs b/lexical-util/src/extended_float.rs index 46d7e425..a6091202 100644 --- a/lexical-util/src/extended_float.rs +++ b/lexical-util/src/extended_float.rs @@ -7,8 +7,7 @@ //! for performance). Since there is no storage for the sign bit, //! this only works for positive floats. -#![cfg(feature = "floats")] -#![cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "write-floats"))))] +#![cfg(any(feature = "parse-floats", feature = "write-floats"))] use crate::num::UnsignedInteger; diff --git a/lexical-util/src/format.rs b/lexical-util/src/format.rs index ac02ba3e..a3646b40 100644 --- a/lexical-util/src/format.rs +++ b/lexical-util/src/format.rs @@ -332,16 +332,19 @@ )] //! #![cfg_attr( - feature = "parse", + any(feature = "parse-floats", feature = "parse-integers"), doc = "[`FromLexicalWithOptions`]: crate::from_lexical_with_options" )] #![cfg_attr( - not(feature = "parse"), + not(any(feature = "parse-floats", feature = "parse-integers")), doc = "[`FromLexicalWithOptions`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/api.rs#L45" )] -#![cfg_attr(feature = "write", doc = "[`ToLexicalWithOptions`]: crate::to_lexical_with_options")] #![cfg_attr( - not(feature = "write"), + any(feature = "write-floats", feature = "write-integers"), + doc = "[`ToLexicalWithOptions`]: crate::to_lexical_with_options" +)] +#![cfg_attr( + not(any(feature = "write-floats", feature = "write-integers")), doc = "[`ToLexicalWithOptions`]: https://github.com/Alexhuszagh/rust-lexical/blob/c6c5052/lexical-util/src/api.rs#L151" )] //! diff --git a/lexical-util/src/format_builder.rs b/lexical-util/src/format_builder.rs index 80078868..b1e72b73 100644 --- a/lexical-util/src/format_builder.rs +++ b/lexical-util/src/format_builder.rs @@ -503,14 +503,12 @@ impl NumberFormatBuilder { /// Create number format for standard, binary number. #[cfg(feature = "power-of-two")] - #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn binary() -> u128 { Self::from_radix(2) } /// Create number format for standard, octal number. #[cfg(feature = "power-of-two")] - #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn octal() -> u128 { Self::from_radix(8) } @@ -526,7 +524,6 @@ impl NumberFormatBuilder { /// Create number format for standard, hexadecimal number. #[cfg(feature = "power-of-two")] - #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn hexadecimal() -> u128 { Self::from_radix(16) } @@ -546,7 +543,6 @@ impl NumberFormatBuilder { // FIXME: Use `build_strict` when we can have a breaking change. #[allow(deprecated)] #[cfg(feature = "power-of-two")] - #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn from_radix(radix: u8) -> u128 { Self::new() .radix(radix) @@ -1420,7 +1416,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn digit_separator(mut self, character: OptionU8) -> Self { self.digit_separator = character; self @@ -1436,7 +1431,6 @@ impl NumberFormatBuilder { /// - Write Integer #[inline(always)] #[cfg(feature = "power-of-two")] - #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn radix(self, radix: u8) -> Self { self.mantissa_radix(radix) } @@ -1463,7 +1457,6 @@ impl NumberFormatBuilder { /// - Write Integer #[inline(always)] #[cfg(feature = "power-of-two")] - #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn mantissa_radix(mut self, radix: u8) -> Self { self.mantissa_radix = radix; self @@ -1482,7 +1475,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "power-of-two")] - #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn exponent_base(mut self, base: OptionU8) -> Self { self.exponent_base = base; self @@ -1509,7 +1501,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "power-of-two")] - #[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "radix"))))] pub const fn exponent_radix(mut self, radix: OptionU8) -> Self { self.exponent_radix = radix; self @@ -1540,7 +1531,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(all(feature = "power-of-two", feature = "format"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "power-of-two", feature = "format"))))] pub const fn base_prefix(mut self, base_prefix: OptionU8) -> Self { self.base_prefix = base_prefix; self @@ -1569,7 +1559,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(all(feature = "power-of-two", feature = "format"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "power-of-two", feature = "format"))))] pub const fn base_suffix(mut self, base_suffix: OptionU8) -> Self { self.base_suffix = base_suffix; self @@ -1593,7 +1582,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_integer_digits(mut self, flag: bool) -> Self { self.required_integer_digits = flag; self @@ -1617,7 +1605,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_fraction_digits(mut self, flag: bool) -> Self { self.required_fraction_digits = flag; self @@ -1642,7 +1629,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_exponent_digits(mut self, flag: bool) -> Self { self.required_exponent_digits = flag; self @@ -1668,7 +1654,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_mantissa_digits(mut self, flag: bool) -> Self { self.required_mantissa_digits = flag; self @@ -1697,7 +1682,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_digits(mut self, flag: bool) -> Self { self = self.required_integer_digits(flag); self = self.required_fraction_digits(flag); @@ -1725,7 +1709,6 @@ impl NumberFormatBuilder { /// - Write Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_positive_mantissa_sign(mut self, flag: bool) -> Self { self.no_positive_mantissa_sign = flag; self @@ -1750,7 +1733,6 @@ impl NumberFormatBuilder { /// - Write Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_mantissa_sign(mut self, flag: bool) -> Self { self.required_mantissa_sign = flag; self @@ -1775,7 +1757,6 @@ impl NumberFormatBuilder { /// - Write Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_exponent_notation(mut self, flag: bool) -> Self { self.no_exponent_notation = flag; self @@ -1799,7 +1780,6 @@ impl NumberFormatBuilder { /// - Write Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_positive_exponent_sign(mut self, flag: bool) -> Self { self.no_positive_exponent_sign = flag; self @@ -1823,7 +1803,6 @@ impl NumberFormatBuilder { /// - Write Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_exponent_sign(mut self, flag: bool) -> Self { self.required_exponent_sign = flag; self @@ -1847,7 +1826,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_exponent_without_fraction(mut self, flag: bool) -> Self { self.no_exponent_without_fraction = flag; self @@ -1871,7 +1849,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_special(mut self, flag: bool) -> Self { self.no_special = flag; self @@ -1887,7 +1864,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn case_sensitive_special(mut self, flag: bool) -> Self { self.case_sensitive_special = flag; self @@ -1910,7 +1886,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_integer_leading_zeros(mut self, flag: bool) -> Self { self.no_integer_leading_zeros = flag; self @@ -1937,7 +1912,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn no_float_leading_zeros(mut self, flag: bool) -> Self { self.no_float_leading_zeros = flag; self @@ -1962,7 +1936,6 @@ impl NumberFormatBuilder { /// - Write Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn required_exponent_notation(mut self, flag: bool) -> Self { self.required_exponent_notation = flag; self @@ -1978,7 +1951,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn case_sensitive_exponent(mut self, flag: bool) -> Self { self.case_sensitive_exponent = flag; self @@ -1995,7 +1967,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(all(feature = "power-of-two", feature = "format"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "power-of-two", feature = "format"))))] pub const fn case_sensitive_base_prefix(mut self, flag: bool) -> Self { self.case_sensitive_base_prefix = flag; self @@ -2012,7 +1983,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(all(feature = "power-of-two", feature = "format"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "power-of-two", feature = "format"))))] pub const fn case_sensitive_base_suffix(mut self, flag: bool) -> Self { self.case_sensitive_base_suffix = flag; self @@ -2042,7 +2012,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_internal_digit_separator(mut self, flag: bool) -> Self { self.integer_internal_digit_separator = flag; self @@ -2071,7 +2040,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_internal_digit_separator(mut self, flag: bool) -> Self { self.fraction_internal_digit_separator = flag; self @@ -2100,7 +2068,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_internal_digit_separator(mut self, flag: bool) -> Self { self.exponent_internal_digit_separator = flag; self @@ -2119,7 +2086,6 @@ impl NumberFormatBuilder { /// [`exponent_internal_digit_separator`]: Self::exponent_internal_digit_separator #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn internal_digit_separator(mut self, flag: bool) -> Self { self = self.integer_internal_digit_separator(flag); self = self.fraction_internal_digit_separator(flag); @@ -2150,7 +2116,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_leading_digit_separator(mut self, flag: bool) -> Self { self.integer_leading_digit_separator = flag; self @@ -2178,7 +2143,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_leading_digit_separator(mut self, flag: bool) -> Self { self.fraction_leading_digit_separator = flag; self @@ -2206,7 +2170,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_leading_digit_separator(mut self, flag: bool) -> Self { self.exponent_leading_digit_separator = flag; self @@ -2225,7 +2188,6 @@ impl NumberFormatBuilder { /// [`exponent_leading_digit_separator`]: Self::exponent_leading_digit_separator #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn leading_digit_separator(mut self, flag: bool) -> Self { self = self.integer_leading_digit_separator(flag); self = self.fraction_leading_digit_separator(flag); @@ -2256,7 +2218,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_trailing_digit_separator(mut self, flag: bool) -> Self { self.integer_trailing_digit_separator = flag; self @@ -2283,7 +2244,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_trailing_digit_separator(mut self, flag: bool) -> Self { self.fraction_trailing_digit_separator = flag; self @@ -2311,7 +2271,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_trailing_digit_separator(mut self, flag: bool) -> Self { self.exponent_trailing_digit_separator = flag; self @@ -2330,7 +2289,6 @@ impl NumberFormatBuilder { /// [`exponent_trailing_digit_separator`]: Self::exponent_trailing_digit_separator #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn trailing_digit_separator(mut self, flag: bool) -> Self { self = self.integer_trailing_digit_separator(flag); self = self.fraction_trailing_digit_separator(flag); @@ -2350,7 +2308,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_consecutive_digit_separator(mut self, flag: bool) -> Self { self.integer_consecutive_digit_separator = flag; self @@ -2367,7 +2324,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_consecutive_digit_separator(mut self, flag: bool) -> Self { self.fraction_consecutive_digit_separator = flag; self @@ -2384,7 +2340,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_consecutive_digit_separator(mut self, flag: bool) -> Self { self.exponent_consecutive_digit_separator = flag; self @@ -2401,7 +2356,6 @@ impl NumberFormatBuilder { /// [`exponent_consecutive_digit_separator`]: Self::exponent_consecutive_digit_separator #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn consecutive_digit_separator(mut self, flag: bool) -> Self { self = self.integer_consecutive_digit_separator(flag); self = self.fraction_consecutive_digit_separator(flag); @@ -2420,7 +2374,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn special_digit_separator(mut self, flag: bool) -> Self { self.special_digit_separator = flag; self @@ -2438,7 +2391,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn digit_separator_flags(mut self, flag: bool) -> Self { self = self.integer_digit_separator_flags(flag); self = self.fraction_digit_separator_flags(flag); @@ -2458,7 +2410,6 @@ impl NumberFormatBuilder { /// - Parse Integer #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn integer_digit_separator_flags(mut self, flag: bool) -> Self { self = self.integer_internal_digit_separator(flag); self = self.integer_leading_digit_separator(flag); @@ -2477,7 +2428,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn fraction_digit_separator_flags(mut self, flag: bool) -> Self { self = self.fraction_internal_digit_separator(flag); self = self.fraction_leading_digit_separator(flag); @@ -2496,7 +2446,6 @@ impl NumberFormatBuilder { /// - Parse Float #[inline(always)] #[cfg(feature = "format")] - #[cfg_attr(docsrs, doc(cfg(feature = "format")))] pub const fn exponent_digit_separator_flags(mut self, flag: bool) -> Self { self = self.exponent_internal_digit_separator(flag); self = self.exponent_leading_digit_separator(flag); diff --git a/lexical-util/src/iterator.rs b/lexical-util/src/iterator.rs index 6077de98..342f56f0 100644 --- a/lexical-util/src/iterator.rs +++ b/lexical-util/src/iterator.rs @@ -4,8 +4,7 @@ //! which then can be used for contiguous or non-contiguous iterables, //! including containers or iterators of any kind. -#![cfg(feature = "parse")] -#![cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#![cfg(any(feature = "parse-floats", feature = "parse-integers"))] use core::mem; diff --git a/lexical-util/src/lib.rs b/lexical-util/src/lib.rs index e1639bd9..9252c95a 100644 --- a/lexical-util/src/lib.rs +++ b/lexical-util/src/lib.rs @@ -106,26 +106,6 @@ clippy::semicolon_inside_block, )] -// Ensure our features are properly enabled. This means no parse without -// parse support, etc. -#[cfg(all(feature = "parse", not(any(feature = "parse-integers", feature = "parse-floats"))))] -compile_error!( - "Do not use the `parse` feature directly. Use `parse-integers` and/or `parse-floats` instead." -); - -#[cfg(all(feature = "write", not(any(feature = "write-integers", feature = "write-floats"))))] -compile_error!( - "Do not use the `write` feature directly. Use `write-integers` and/or `write-floats` instead." -); - -#[cfg(all(feature = "integers", not(any(feature = "write-integers", feature = "parse-integers"))))] -compile_error!("Do not use the `integers` feature directly. Use `write-integers` and/or `parse-integers` instead."); - -#[cfg(all(feature = "floats", not(any(feature = "write-floats", feature = "parse-floats"))))] -compile_error!( - "Do not use the `floats` feature directly. Use `write-floats` and/or `parse-floats` instead." -); - pub mod algorithm; pub mod ascii; pub mod assert; @@ -148,17 +128,18 @@ mod api; mod feature_format; mod format_builder; mod format_flags; +mod libm; mod noskip; mod not_feature_format; mod prebuilt_formats; mod skip; -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub use constants::{FormattedSize, BUFFER_SIZE}; pub use error::Error; pub use format::{NumberFormat, NumberFormatBuilder}; -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub use options::ParseOptions; -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub use options::WriteOptions; pub use result::Result; diff --git a/lexical-util/src/libm.rs b/lexical-util/src/libm.rs new file mode 100644 index 00000000..da9413ba --- /dev/null +++ b/lexical-util/src/libm.rs @@ -0,0 +1,269 @@ +//! Float helpers for a `no_std` environment. +//! +//! These are adapted from libm, a port of musl libc's libm to Rust. +//! libm can be found online [here](https://github.com/rust-lang/libm), +//! and is similarly licensed under an Apache2.0/MIT license + +#![cfg(all(not(feature = "std"), any(feature = "parse-floats", feature = "write-floats")))] +#![cfg_attr(any(), rustfmt::skip)] + +/// # Safety +/// +/// Safe as long as `e` is properly initialized. +macro_rules! volatile { +($e:expr) => { + // SAFETY: safe as long as `$e` has been properly initialized. + unsafe { + core::ptr::read_volatile(&$e); + } +}; +} + +/// Floor (f64) +/// +/// Finds the nearest integer less than or equal to `x`. +pub(crate) fn floord(x: f64) -> f64 { + const TOINT: f64 = 1. / f64::EPSILON; + + let ui = x.to_bits(); + let e = ((ui >> 52) & 0x7ff) as i32; + + if (e >= 0x3ff + 52) || (x == 0.) { + return x; + } + /* y = int(x) - x, where int(x) is an integer neighbor of x */ + let y = if (ui >> 63) != 0 { + x - TOINT + TOINT - x + } else { + x + TOINT - TOINT - x + }; + /* special case because of non-nearest rounding modes */ + if e < 0x3ff { + volatile!(y); + return if (ui >> 63) != 0 { + -1. + } else { + 0. + }; + } + if y > 0. { + x + y - 1. + } else { + x + y + } +} + +/// Floor (f32) +/// +/// Finds the nearest integer less than or equal to `x`. +pub(crate) fn floorf(x: f32) -> f32 { + let mut ui = x.to_bits(); + let e = (((ui >> 23) as i32) & 0xff) - 0x7f; + + if e >= 23 { + return x; + } + if e >= 0 { + let m: u32 = 0x007fffff >> e; + if (ui & m) == 0 { + return x; + } + volatile!(x + f32::from_bits(0x7b800000)); + if ui >> 31 != 0 { + ui += m; + } + ui &= !m; + } else { + volatile!(x + f32::from_bits(0x7b800000)); + if ui >> 31 == 0 { + ui = 0; + } else if ui << 1 != 0 { + return -1.0; + } + } + f32::from_bits(ui) +} + +/* origin: FreeBSD /usr/src/lib/msun/src/e_log.c */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ +/* log(x) + * Return the logarithm of x + * + * Method : + * 1. Argument Reduction: find k and f such that + * x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * 2. Approximation of log(1+f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Remez algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s + * (the values of Lg1 to Lg7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lg1*s +...+Lg7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log(1+f) = f - s*(f - R) (if f is not too large) + * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) + * + * 3. Finally, log(x) = k*ln2 + log(1+f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log(x) is NaN with signal if x < 0 (including -INF) ; + * log(+INF) is +INF; log(0) is -INF with signal; + * log(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#[allow(clippy::eq_op, clippy::excessive_precision)] // reason="values need to be exact under all conditions" +pub(crate) fn logd(mut x: f64) -> f64 { + const LN2_HI: f64 = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */ + const LN2_LO: f64 = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */ + const LG1: f64 = 6.666666666666735130e-01; /* 3FE55555 55555593 */ + const LG2: f64 = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */ + const LG3: f64 = 2.857142874366239149e-01; /* 3FD24924 94229359 */ + const LG4: f64 = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */ + const LG5: f64 = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */ + const LG6: f64 = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */ + const LG7: f64 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + + let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 + + let mut ui = x.to_bits(); + let mut hx: u32 = (ui >> 32) as u32; + let mut k: i32 = 0; + + if (hx < 0x00100000) || ((hx >> 31) != 0) { + /* x < 2**-126 */ + if ui << 1 == 0 { + return -1. / (x * x); /* log(+-0)=-inf */ + } + if hx >> 31 != 0 { + return (x - x) / 0.0; /* log(-#) = NaN */ + } + /* subnormal number, scale x up */ + k -= 54; + x *= x1p54; + ui = x.to_bits(); + hx = (ui >> 32) as u32; + } else if hx >= 0x7ff00000 { + return x; + } else if hx == 0x3ff00000 && ui << 32 == 0 { + return 0.; + } + + /* reduce x into [sqrt(2)/2, sqrt(2)] */ + hx += 0x3ff00000 - 0x3fe6a09e; + k += ((hx >> 20) as i32) - 0x3ff; + hx = (hx & 0x000fffff) + 0x3fe6a09e; + ui = ((hx as u64) << 32) | (ui & 0xffffffff); + x = f64::from_bits(ui); + + let f: f64 = x - 1.0; + let hfsq: f64 = 0.5 * f * f; + let s: f64 = f / (2.0 + f); + let z: f64 = s * s; + let w: f64 = z * z; + let t1: f64 = w * (LG2 + w * (LG4 + w * LG6)); + let t2: f64 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7))); + let r: f64 = t2 + t1; + let dk: f64 = k as f64; + s * (hfsq + r) + dk * LN2_LO - hfsq + f + dk * LN2_HI +} + +/* origin: FreeBSD /usr/src/lib/msun/src/e_logf.c */ +/* + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#[allow(clippy::eq_op, clippy::excessive_precision)] // reason="values need to be exact under all conditions" +pub(crate) fn logf(mut x: f32) -> f32 { + const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ + const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ + /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ + const LG1: f32 = 0.66666662693; /* 0xaaaaaa.0p-24 */ + const LG2: f32 = 0.40000972152; /* 0xccce13.0p-25 */ + const LG3: f32 = 0.28498786688; /* 0x91e9ee.0p-25 */ + const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ + + let x1p25 = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 + + let mut ix = x.to_bits(); + let mut k = 0i32; + + if (ix < 0x00800000) || ((ix >> 31) != 0) { + /* x < 2**-126 */ + if ix << 1 == 0 { + return -1. / (x * x); /* log(+-0)=-inf */ + } + if (ix >> 31) != 0 { + return (x - x) / 0.; /* log(-#) = NaN */ + } + /* subnormal number, scale up x */ + k -= 25; + x *= x1p25; + ix = x.to_bits(); + } else if ix >= 0x7f800000 { + return x; + } else if ix == 0x3f800000 { + return 0.; + } + + /* reduce x into [sqrt(2)/2, sqrt(2)] */ + ix += 0x3f800000 - 0x3f3504f3; + k += ((ix >> 23) as i32) - 0x7f; + ix = (ix & 0x007fffff) + 0x3f3504f3; + x = f32::from_bits(ix); + + let f = x - 1.; + let s = f / (2. + f); + let z = s * s; + let w = z * z; + let t1 = w * (LG2 + w * LG4); + let t2 = z * (LG1 + w * LG3); + let r = t2 + t1; + let hfsq = 0.5 * f * f; + let dk = k as f32; + s * (hfsq + r) + dk * LN2_LO - hfsq + f + dk * LN2_HI +} diff --git a/lexical-util/src/noskip.rs b/lexical-util/src/noskip.rs index 70a0c4d5..5852ba73 100644 --- a/lexical-util/src/noskip.rs +++ b/lexical-util/src/noskip.rs @@ -3,7 +3,7 @@ //! This iterator has both the length of the original slice, as //! well as the current position of the iterator in the buffer. -#![cfg(all(feature = "parse", not(feature = "format")))] +#![cfg(all(any(feature = "parse-floats", feature = "parse-integers"), not(feature = "format")))] use core::{mem, ptr}; diff --git a/lexical-util/src/num.rs b/lexical-util/src/num.rs index 3e6d637b..cd32dc62 100644 --- a/lexical-util/src/num.rs +++ b/lexical-util/src/num.rs @@ -4,14 +4,14 @@ //! types, and trait bounds, and conversions for working with //! numbers in generic code. -#![cfg_attr(any(), rustfmt::skip)] - use core::{fmt, mem, ops}; #[cfg(feature = "f16")] use crate::bf16::bf16; #[cfg(feature = "f16")] use crate::f16::f16; +#[cfg(all(not(feature = "std"), any(feature = "parse-floats", feature = "write-floats")))] +use crate::libm; // AS PRIMITIVE // ------------ @@ -852,8 +852,7 @@ unsigned_integer_impl! { u8 u16 u32 u64 u128 usize } /// exponent of base 2. /// /// [`floats`]: https://en.wikipedia.org/wiki/Floating-point_arithmetic -#[cfg(feature = "floats")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "write-floats"))))] +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] pub trait Float: Number + ops::Neg { /// Unsigned type of the same size. type Unsigned: UnsignedInteger; @@ -998,7 +997,8 @@ pub trait Float: Number + ops::Neg { self.to_bits() & Self::EXPONENT_MASK == Self::Unsigned::ZERO } - /// Returns true if the float is NaN, positive infinity, or negative infinity. + /// Returns true if the float is NaN, positive infinity, or negative + /// infinity. #[inline(always)] fn is_special(self) -> bool { self.to_bits() & Self::EXPONENT_MASK == Self::EXPONENT_MASK @@ -1180,7 +1180,7 @@ pub trait Float: Number + ops::Neg { } /// Define the float literals. -#[cfg(feature = "floats")] +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] macro_rules! float_literals { ($float:ty) => { const ZERO: $float = 0.0; @@ -1196,7 +1196,7 @@ macro_rules! float_literals { } /// Define the float masks. -#[cfg(feature = "floats")] +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] macro_rules! float_masks { ( float => @@ -1374,7 +1374,7 @@ impl Float for bf16 { } } -#[cfg(feature = "floats")] +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] impl Float for f32 { type Unsigned = u32; float_literals!(f32); @@ -1407,7 +1407,7 @@ impl Float for f32 { return f32::ln(self); #[cfg(not(feature = "std"))] - return logf(self); + return libm::logf(self); } #[inline(always)] @@ -1416,7 +1416,7 @@ impl Float for f32 { return f32::floor(self); #[cfg(not(feature = "std"))] - return floorf(self); + return libm::floorf(self); } #[inline(always)] @@ -1430,7 +1430,7 @@ impl Float for f32 { } } -#[cfg(feature = "floats")] +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] impl Float for f64 { type Unsigned = u64; float_literals!(f64); @@ -1463,7 +1463,7 @@ impl Float for f64 { return f64::ln(self); #[cfg(not(feature = "std"))] - return logd(self); + return libm::logd(self); } #[inline(always)] @@ -1472,7 +1472,7 @@ impl Float for f64 { return f64::floor(self); #[cfg(not(feature = "std"))] - return floord(self); + return libm::floord(self); } #[inline(always)] @@ -1503,276 +1503,3 @@ impl Float for f64 { // const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; // const MAX_EXPONENT: i32 = 0x7FFF - Self::EXPONENT_BIAS; // } - -// FLOAT HELPERS -// ------------- - -// These are adapted from libm, a port of musl libc's libm to Rust. -// libm can be found online [here](https://github.com/rust-lang/libm), -// and is similarly licensed under an Apache2.0/MIT license - -/// # Safety -/// -/// Safe as long as `e` is properly initialized. -#[cfg(all(not(feature = "std"), feature = "floats"))] -macro_rules! volatile { -($e:expr) => { - // SAFETY: safe as long as `$e` has been properly initialized. - unsafe { - core::ptr::read_volatile(&$e); - } -}; -} - -/// Floor (f64) -/// -/// Finds the nearest integer less than or equal to `x`. -#[cfg(all(not(feature = "std"), feature = "floats"))] -fn floord(x: f64) -> f64 { - const TOINT: f64 = 1. / f64::EPSILON; - - let ui = x.to_bits(); - let e = ((ui >> 52) & 0x7ff) as i32; - - if (e >= 0x3ff + 52) || (x == 0.) { - return x; - } - /* y = int(x) - x, where int(x) is an integer neighbor of x */ - let y = if (ui >> 63) != 0 { - x - TOINT + TOINT - x - } else { - x + TOINT - TOINT - x - }; - /* special case because of non-nearest rounding modes */ - if e < 0x3ff { - volatile!(y); - return if (ui >> 63) != 0 { - -1. - } else { - 0. - }; - } - if y > 0. { - x + y - 1. - } else { - x + y - } -} - -/// Floor (f32) -/// -/// Finds the nearest integer less than or equal to `x`. -#[cfg(all(not(feature = "std"), feature = "floats"))] -fn floorf(x: f32) -> f32 { - let mut ui = x.to_bits(); - let e = (((ui >> 23) as i32) & 0xff) - 0x7f; - - if e >= 23 { - return x; - } - if e >= 0 { - let m: u32 = 0x007fffff >> e; - if (ui & m) == 0 { - return x; - } - volatile!(x + f32::from_bits(0x7b800000)); - if ui >> 31 != 0 { - ui += m; - } - ui &= !m; - } else { - volatile!(x + f32::from_bits(0x7b800000)); - if ui >> 31 == 0 { - ui = 0; - } else if ui << 1 != 0 { - return -1.0; - } - } - f32::from_bits(ui) -} - -/* origin: FreeBSD /usr/src/lib/msun/src/e_log.c */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunSoft, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ -/* log(x) - * Return the logarithm of x - * - * Method : - * 1. Argument Reduction: find k and f such that - * x = 2^k * (1+f), - * where sqrt(2)/2 < 1+f < sqrt(2) . - * - * 2. Approximation of log(1+f). - * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) - * = 2s + 2/3 s**3 + 2/5 s**5 + ....., - * = 2s + s*R - * We use a special Remez algorithm on [0,0.1716] to generate - * a polynomial of degree 14 to approximate R The maximum error - * of this polynomial approximation is bounded by 2**-58.45. In - * other words, - * 2 4 6 8 10 12 14 - * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s - * (the values of Lg1 to Lg7 are listed in the program) - * and - * | 2 14 | -58.45 - * | Lg1*s +...+Lg7*s - R(z) | <= 2 - * | | - * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. - * In order to guarantee error in log below 1ulp, we compute log - * by - * log(1+f) = f - s*(f - R) (if f is not too large) - * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) - * - * 3. Finally, log(x) = k*ln2 + log(1+f). - * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) - * Here ln2 is split into two floating point number: - * ln2_hi + ln2_lo, - * where n*ln2_hi is always exact for |n| < 2000. - * - * Special cases: - * log(x) is NaN with signal if x < 0 (including -INF) ; - * log(+INF) is +INF; log(0) is -INF with signal; - * log(NaN) is that NaN with no signal. - * - * Accuracy: - * according to an error analysis, the error is always less than - * 1 ulp (unit in the last place). - * - * Constants: - * The hexadecimal values are the intended ones for the following - * constants. The decimal values may be used, provided that the - * compiler will convert from decimal to binary accurately enough - * to produce the hexadecimal values shown. - */ - -#[allow(clippy::eq_op, clippy::excessive_precision)] // reason="values need to be exact under all conditions" -#[cfg(all(not(feature = "std"), feature = "floats"))] -fn logd(mut x: f64) -> f64 { - const LN2_HI: f64 = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */ - const LN2_LO: f64 = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */ - const LG1: f64 = 6.666666666666735130e-01; /* 3FE55555 55555593 */ - const LG2: f64 = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */ - const LG3: f64 = 2.857142874366239149e-01; /* 3FD24924 94229359 */ - const LG4: f64 = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */ - const LG5: f64 = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */ - const LG6: f64 = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */ - const LG7: f64 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ - - let x1p54 = f64::from_bits(0x4350000000000000); // 0x1p54 === 2 ^ 54 - - let mut ui = x.to_bits(); - let mut hx: u32 = (ui >> 32) as u32; - let mut k: i32 = 0; - - if (hx < 0x00100000) || ((hx >> 31) != 0) { - /* x < 2**-126 */ - if ui << 1 == 0 { - return -1. / (x * x); /* log(+-0)=-inf */ - } - if hx >> 31 != 0 { - return (x - x) / 0.0; /* log(-#) = NaN */ - } - /* subnormal number, scale x up */ - k -= 54; - x *= x1p54; - ui = x.to_bits(); - hx = (ui >> 32) as u32; - } else if hx >= 0x7ff00000 { - return x; - } else if hx == 0x3ff00000 && ui << 32 == 0 { - return 0.; - } - - /* reduce x into [sqrt(2)/2, sqrt(2)] */ - hx += 0x3ff00000 - 0x3fe6a09e; - k += ((hx >> 20) as i32) - 0x3ff; - hx = (hx & 0x000fffff) + 0x3fe6a09e; - ui = ((hx as u64) << 32) | (ui & 0xffffffff); - x = f64::from_bits(ui); - - let f: f64 = x - 1.0; - let hfsq: f64 = 0.5 * f * f; - let s: f64 = f / (2.0 + f); - let z: f64 = s * s; - let w: f64 = z * z; - let t1: f64 = w * (LG2 + w * (LG4 + w * LG6)); - let t2: f64 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7))); - let r: f64 = t2 + t1; - let dk: f64 = k as f64; - s * (hfsq + r) + dk * LN2_LO - hfsq + f + dk * LN2_HI -} - -/* origin: FreeBSD /usr/src/lib/msun/src/e_logf.c */ -/* - * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. - */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -#[allow(clippy::eq_op, clippy::excessive_precision)] // reason="values need to be exact under all conditions" -#[cfg(all(not(feature = "std"), feature = "floats"))] -fn logf(mut x: f32) -> f32 { - const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ - const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ - /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ - const LG1: f32 = 0.66666662693; /* 0xaaaaaa.0p-24 */ - const LG2: f32 = 0.40000972152; /* 0xccce13.0p-25 */ - const LG3: f32 = 0.28498786688; /* 0x91e9ee.0p-25 */ - const LG4: f32 = 0.24279078841; /* 0xf89e26.0p-26 */ - - let x1p25 = f32::from_bits(0x4c000000); // 0x1p25f === 2 ^ 25 - - let mut ix = x.to_bits(); - let mut k = 0i32; - - if (ix < 0x00800000) || ((ix >> 31) != 0) { - /* x < 2**-126 */ - if ix << 1 == 0 { - return -1. / (x * x); /* log(+-0)=-inf */ - } - if (ix >> 31) != 0 { - return (x - x) / 0.; /* log(-#) = NaN */ - } - /* subnormal number, scale up x */ - k -= 25; - x *= x1p25; - ix = x.to_bits(); - } else if ix >= 0x7f800000 { - return x; - } else if ix == 0x3f800000 { - return 0.; - } - - /* reduce x into [sqrt(2)/2, sqrt(2)] */ - ix += 0x3f800000 - 0x3f3504f3; - k += ((ix >> 23) as i32) - 0x7f; - ix = (ix & 0x007fffff) + 0x3f3504f3; - x = f32::from_bits(ix); - - let f = x - 1.; - let s = f / (2. + f); - let z = s * s; - let w = z * z; - let t1 = w * (LG2 + w * LG4); - let t2 = z * (LG1 + w * LG3); - let r = t2 + t1; - let hfsq = 0.5 * f * f; - let dk = k as f32; - s * (hfsq + r) + dk * LN2_LO - hfsq + f + dk * LN2_HI -} diff --git a/lexical-util/src/options.rs b/lexical-util/src/options.rs index 323433a3..07549486 100644 --- a/lexical-util/src/options.rs +++ b/lexical-util/src/options.rs @@ -18,7 +18,7 @@ //! - Long infinity: (`*_INFINITY`): `Infinity` (including `+Infinity` and //! `-Infinity`) -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] use crate::constants::FormattedSize; // TRAITS @@ -54,8 +54,7 @@ depending on the radix. } /// Shared trait for all writer options. -#[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "write-integers"))))] -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub trait WriteOptions: Default { /// Determine if the options are valid. fn is_valid(&self) -> bool; @@ -89,8 +88,7 @@ pub trait WriteOptions: Default { } /// Shared trait for all parser options. -#[cfg_attr(docsrs, doc(cfg(any(feature = "power-of-two", feature = "write-integers"))))] -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub trait ParseOptions: Default { /// Determine if the options are valid. fn is_valid(&self) -> bool; diff --git a/lexical-util/src/skip.rs b/lexical-util/src/skip.rs index 987d1f2e..66a3ff23 100644 --- a/lexical-util/src/skip.rs +++ b/lexical-util/src/skip.rs @@ -39,7 +39,7 @@ //! For example, `next_ilt` means that consumer can skip internal, //! leading, and trailing digit separators, but not consecutive ones. -#![cfg(all(feature = "format", feature = "parse"))] +#![cfg(all(feature = "format", any(feature = "parse-floats", feature = "parse-integers")))] use core::{mem, ptr}; diff --git a/lexical-util/src/step.rs b/lexical-util/src/step.rs index 0db79890..7515c6f5 100644 --- a/lexical-util/src/step.rs +++ b/lexical-util/src/step.rs @@ -11,7 +11,12 @@ //! //! [`etc/step.py`]: https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-util/etc/step.py -#![cfg(any(feature = "parse", feature = "write"))] +#![cfg(any( + feature = "parse-floats", + feature = "parse-integers", + feature = "write-floats", + feature = "write-integers", +))] // NOTE: // Fallback radixes use 1 for the value to avoid infinite loops, diff --git a/lexical-util/tests/algorithm_tests.rs b/lexical-util/tests/algorithm_tests.rs index 7b77d92b..e1c9b20e 100644 --- a/lexical-util/tests/algorithm_tests.rs +++ b/lexical-util/tests/algorithm_tests.rs @@ -1,8 +1,8 @@ -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] use lexical_util::algorithm; #[test] -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] fn copy_to_dest_test() { let src = b"12345"; let mut dst = [b'0'; 16]; @@ -12,7 +12,7 @@ fn copy_to_dest_test() { } #[test] -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] fn ltrim_char_test() { let w = "0001"; let x = "1010"; diff --git a/lexical-util/tests/digit_tests.rs b/lexical-util/tests/digit_tests.rs index b46aaa8a..9b7bdfd0 100644 --- a/lexical-util/tests/digit_tests.rs +++ b/lexical-util/tests/digit_tests.rs @@ -1,15 +1,20 @@ -#![cfg(any(feature = "parse", feature = "write"))] +#![cfg(any( + feature = "parse-floats", + feature = "parse-integers", + feature = "write-floats", + feature = "write-integers", +))] use lexical_util::digit; -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] fn char_to_digit(c: u8, radix: u32, expected: Option) { assert_eq!(digit::char_to_digit_const(c, radix), expected); assert_eq!(digit::char_to_digit(c, radix), expected); } #[test] -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] fn char_to_digit_test() { char_to_digit(b'0', 2, Some(0)); char_to_digit(b'1', 2, Some(1)); @@ -67,14 +72,14 @@ fn char_to_digit_test() { char_to_digit(0x7A, 16, None); } -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] fn char_is_digit(c: u8, radix: u32, expected: bool) { assert_eq!(digit::char_is_digit_const(c, radix), expected); assert_eq!(digit::char_is_digit(c, radix), expected); } #[test] -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] fn char_is_digit_const_test() { char_is_digit(b'0', 2, true); char_is_digit(b'1', 2, true); @@ -95,14 +100,14 @@ fn char_is_digit_const_test() { char_is_digit(b'Z', 16, false); } -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] fn digit_to_char(digit: u32, radix: u32, expected: u8) { assert_eq!(digit::digit_to_char_const(digit, radix), expected); assert_eq!(digit::digit_to_char(digit), expected); } #[test] -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] fn digit_to_char_const_test() { digit_to_char(9, 10, b'9'); digit_to_char(10, 36, b'A'); diff --git a/lexical-util/tests/iterator_tests.rs b/lexical-util/tests/iterator_tests.rs index 1cf0c9de..9550a78d 100644 --- a/lexical-util/tests/iterator_tests.rs +++ b/lexical-util/tests/iterator_tests.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "parse")] +#![cfg(any(feature = "parse-floats", feature = "parse-integers"))] use lexical_util::iterator::{AsBytes, Bytes, DigitsIter, Iter}; diff --git a/lexical-util/tests/num_tests.rs b/lexical-util/tests/num_tests.rs index 9d732f75..8849deb6 100644 --- a/lexical-util/tests/num_tests.rs +++ b/lexical-util/tests/num_tests.rs @@ -172,7 +172,7 @@ fn ceil_divmod_test() { assert_eq!(36usize.ceil_divmod(7), (6, -6)); } -#[cfg(feature = "floats")] +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] fn check_float(mut x: T) { // Copy, partialeq, partialord let _ = x; @@ -209,7 +209,7 @@ fn check_float(mut x: T) { } #[test] -#[cfg(feature = "floats")] +#[cfg(any(feature = "parse-floats", feature = "write-floats"))] fn float_test() { use lexical_util::num::Float; diff --git a/lexical-util/tests/skip_tests.rs b/lexical-util/tests/skip_tests.rs index 158e3421..9955daac 100644 --- a/lexical-util/tests/skip_tests.rs +++ b/lexical-util/tests/skip_tests.rs @@ -1,4 +1,4 @@ -#![cfg(all(feature = "format", feature = "parse"))] +#![cfg(all(feature = "format", any(feature = "parse-floats", feature = "parse-integers")))] use core::num; diff --git a/lexical-write-float/src/options.rs b/lexical-write-float/src/options.rs index b0d71cf3..dc925bb7 100644 --- a/lexical-write-float/src/options.rs +++ b/lexical-write-float/src/options.rs @@ -1176,7 +1176,6 @@ impl Options { /// #[inline(always)] #[cfg(feature = "power-of-two")] - #[cfg_attr(docsrs, doc(cfg(feature = "power-of-two")))] // FIXME: When we release a major version, validate the radix. pub const fn from_radix(radix: u8) -> Self { // Need to determine the correct exponent character ('e' or '^'), @@ -1459,8 +1458,6 @@ impl Options { self.negative_exponent_break } - // TODO: HERE - /// Get the rounding mode for writing digits with precision control. /// /// For example, writing `1.23456` with 5 significant digits with diff --git a/lexical/Cargo.toml b/lexical/Cargo.toml index aa4ebcb3..36efc126 100644 --- a/lexical/Cargo.toml +++ b/lexical/Cargo.toml @@ -29,13 +29,13 @@ default = ["std", "write-integers", "write-floats", "parse-integers", "parse-flo # Use the standard library. std = ["lexical-core/std"] # Add support for writing integers. -write-integers = ["lexical-core/write-integers", "write", "integers"] +write-integers = ["lexical-core/write-integers"] # Add support for writing floats. -write-floats = ["lexical-core/write-floats", "write", "floats"] +write-floats = ["lexical-core/write-floats"] # Add support for parsing integers. -parse-integers = ["lexical-core/parse-integers", "parse", "integers"] +parse-integers = ["lexical-core/parse-integers"] # Add support for parsing floats. -parse-floats = ["lexical-core/parse-floats", "parse", "floats"] +parse-floats = ["lexical-core/parse-floats"] # Add support for parsing power-of-two float strings. power-of-two = ["lexical-core/power-of-two"] # Add support for parsing non-decimal float strings. @@ -52,18 +52,6 @@ f16 = ["lexical-core/f16"] # Internal only features. These are not meant to be used directly. # Enable the lint checks. lint = ["lexical-core/lint"] -# Add support for writing numbers. -# Library users should use `write-integers` and `write-floats` instead. -write = ["lexical-core/write"] -# Add support for parsing numbers. -# Library users should use `parse-integers` and `parse-floats` instead. -parse = ["lexical-core/parse"] -# Add support for conversions to or from integers. -# Library users should use `write-integers` and `parse-integers` instead. -integers = ["lexical-core/integers"] -# Add support for conversions to or from floats. -# Library users should use `write-floats` and `parse-floats` instead. -floats = ["lexical-core/floats"] # UNSUPPORTED # ----------- diff --git a/lexical/src/lib.rs b/lexical/src/lib.rs index 10cd065a..64642dec 100644 --- a/lexical/src/lib.rs +++ b/lexical/src/lib.rs @@ -77,9 +77,12 @@ //! [`core-write`]: core::fmt::Display::fmt //! //! -#![cfg_attr(feature = "write", doc = "- [`to_string`]: Write a number to string.")] #![cfg_attr( - feature = "parse", + any(feature = "write-floats", feature = "write-integers"), + doc = "- [`to_string`]: Write a number to string." +)] +#![cfg_attr( + any(feature = "parse-floats", feature = "parse-integers"), doc = " - [`parse`]: Parse a number from string validating the complete string is a number. - [`parse_partial`]: Parse a number from string returning the number and the number @@ -116,25 +119,25 @@ //! //! #![cfg_attr( - all(feature = "write", feature = "floats"), + feature = "write-floats", doc = "[`nan_string`]: WriteFloatOptionsBuilder::nan_string" )] #![cfg_attr( - all(not(feature = "write"), feature = "parse", feature = "floats"), + all(not(feature = "write-floats"), feature = "parse-floats"), doc = "[`nan_string`]: ParseFloatOptionsBuilder::nan_string" )] #![cfg_attr( - any(not(feature = "floats"), all(not(feature = "write"), not(feature = "parse"))), + all(not(feature = "write-floats"), not(feature = "parse-floats")), doc = "[`nan_string`]: https://docs.rs/lexical-core/latest/lexical_core/struct.WriteFloatOptionsBuilder.html#method.nan_string" )] //! //! #![cfg_attr( - feature = "write", + any(feature = "write-floats", feature = "write-integers"), doc = "- [`to_string_with_options`]: Write a number to string using custom formatting options." )] #![cfg_attr( - feature = "parse", + any(feature = "parse-floats", feature = "parse-integers"), doc = " - [`parse_with_options`]: Parse a number from string using custom formatting options, validating the complete string is a number. @@ -555,21 +558,21 @@ //! //! #![cfg_attr( - feature = "write", + any(feature = "write-floats", feature = "write-integers"), doc = " [`to_string`]: crate::to_string [`to_string_with_options`]: crate::to_string_with_options " )] #![cfg_attr( - not(feature = "write"), + not(any(feature = "write-floats", feature = "write-integers")), doc = " [`to_string`]: https://docs.rs/lexical/latest/lexical/fn.to_string.html [`to_string_with_options`]: https://docs.rs/lexical/latest/lexical/fn.to_string_with_options.html " )] #![cfg_attr( - feature = "parse", + any(feature = "parse-floats", feature = "parse-integers"), doc = " [`parse`]: crate::parse [`parse_partial`]: crate::parse_partial @@ -578,7 +581,7 @@ " )] #![cfg_attr( - not(feature = "parse"), + not(any(feature = "parse-floats", feature = "parse-integers")), doc = " [`parse`]: https://docs.rs/lexical/latest/lexical/fn.parse.html [`parse_partial`]: https://docs.rs/lexical/latest/lexical/fn.parse_partial.html @@ -630,32 +633,12 @@ )] #![cfg_attr(rustfmt, rustfmt_skip)] // reason = "this simplifies our imports" -// Ensure our features are properly enabled. This means no parse without -// parse support, etc. -#[cfg(all(feature = "parse", not(any(feature = "parse-integers", feature = "parse-floats"))))] -compile_error!( - "Do not use the `parse` feature directly. Use `parse-integers` and/or `parse-floats` instead." -); - -#[cfg(all(feature = "write", not(any(feature = "write-integers", feature = "write-floats"))))] -compile_error!( - "Do not use the `write` feature directly. Use `write-integers` and/or `write-floats` instead." -); - -#[cfg(all(feature = "integers", not(any(feature = "write-integers", feature = "parse-integers"))))] -compile_error!("Do not use the `integers` feature directly. Use `write-integers` and/or `parse-integers` instead."); - -#[cfg(all(feature = "floats", not(any(feature = "write-floats", feature = "parse-floats"))))] -compile_error!( - "Do not use the `floats` feature directly. Use `write-floats` and/or `parse-floats` instead." -); - // Need an allocator for String/Vec. -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] #[macro_use(vec)] extern crate alloc; -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] use alloc::string::String; // Re-exports @@ -677,12 +660,10 @@ pub use lexical_core::{bf16, f16}; // PARSE -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub use lexical_core::ParseOptions; -#[cfg(feature = "parse")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "parse-floats", feature = "parse-integers"))))] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub use lexical_core::{FromLexical, FromLexicalWithOptions}; #[cfg(feature = "parse-floats")] @@ -693,16 +674,13 @@ pub use lexical_core::{parse_integer_options, ParseIntegerOptions, ParseIntegerO // WRITE -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub use lexical_core::WriteOptions; -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub use lexical_core::{ToLexical, ToLexicalWithOptions}; -#[cfg(feature = "write")] -#[cfg_attr(docsrs, doc(cfg(any(feature = "write-floats", feature = "write-integers"))))] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub use lexical_core::{FormattedSize, BUFFER_SIZE}; #[cfg(feature = "write-floats")] @@ -746,7 +724,7 @@ pub use lexical_core::{write_integer_options, WriteIntegerOptions, WriteIntegerO /// assert_eq!(lexical::to_string(0.0), "0.0"); /// ``` #[inline] -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] pub fn to_string(n: N) -> String { let mut buf = vec![0u8; N::FORMATTED_SIZE_DECIMAL]; let len = lexical_core::write(n, buf.as_mut_slice()).len(); @@ -776,7 +754,7 @@ pub fn to_string(n: N) -> String { /// assert_eq!(lexical::to_string_with_options::<_, FORMAT>(123.456, &OPTIONS), "123.456"); /// ``` #[inline] -#[cfg(feature = "write")] +#[cfg(any(feature = "write-floats", feature = "write-integers"))] #[allow(deprecated)] // reason = "allow the user of `buffer_size`" pub fn to_string_with_options( n: N, @@ -830,7 +808,7 @@ pub fn to_string_with_options( /// # assert_eq!(lexical::parse::(b"5.002868148396374"), Ok(5.002868148396374)); /// ``` #[inline] -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub fn parse>(bytes: Bytes) -> Result { N::from_lexical(bytes.as_ref()) } @@ -865,7 +843,7 @@ pub fn parse>(bytes: Bytes) -> Result { /// # assert_eq!(lexical::parse_partial::(b"5.002868148396374"), Ok((5.002868148396374, 17))); /// ``` #[inline] -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub fn parse_partial>(bytes: Bytes) -> Result<(N, usize)> { N::from_lexical_partial(bytes.as_ref()) } @@ -898,7 +876,7 @@ pub fn parse_partial>(bytes: Bytes) -> Result /// assert_eq!(lexical::parse_with_options::("1,2345^4", &OPTIONS), Ok(12345.0)); /// ``` #[inline] -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub fn parse_with_options, const FORMAT: u128>( bytes: Bytes, options: &N::Options, @@ -938,7 +916,7 @@ pub fn parse_with_options, const F /// assert_eq!(lexical::parse_partial_with_options::("1,2345^4", &OPTIONS), Ok((12345.0, 8))); /// ``` #[inline] -#[cfg(feature = "parse")] +#[cfg(any(feature = "parse-floats", feature = "parse-integers"))] pub fn parse_partial_with_options< N: FromLexicalWithOptions, Bytes: AsRef<[u8]>,