lexical_util/lib.rs
1//! Shared utilities for lexical conversion routines.
2//!
3//! These are not meant to be used publicly for any numeric
4//! conversion routines, but provide optimized math routines,
5//! format packed struct definitions, and custom iterators
6//! for all workspaces.
7//!
8//! # Features
9//!
10//! * `std` - Use the standard library.
11//! * `power-of-two` - Add support for parsing power-of-two integer strings.
12//! * `radix` - Add support for strings of any radix.
13//! * `write-integers` - Add support for writing integers.
14//! * `write-floats` - Add support for writing floats.
15//! * `parse-integers` - Add support for parsing integers.
16//! * `parse-floats` - Add support for parsing floats.
17//! * `compact` - Reduce code size at the cost of performance.
18//!
19//! # Note
20//!
21//! None of this is considered a public API: any of the implementation
22//! details may change release-to-release without major or minor version
23//! changes. Use internal implementation details at your own risk.
24//!
25//! lexical-util mainly exists as an implementation detail for
26//! lexical-core, although its API is stable. If you would like to use
27//! a high-level API that writes to and parses from `String` and `&str`,
28//! respectively, please look at [lexical](https://crates.io/crates/lexical)
29//! instead. If you would like an API that supports multiple numeric
30//! conversions, please look at [lexical-core](https://crates.io/crates/lexical-core)
31//! instead.
32//!
33//! # Version Support
34//!
35//! The minimum, standard, required version is 1.63.0, for const generic
36//! support. Older versions of lexical support older Rust versions.
37//!
38//! # Safety Guarantees
39//!
40//! The only major sources of unsafe code are wrapped in the `iterator.rs`,
41//! `skip.rs`, and `noskip.rs`. These are fully encapsulated into standalone
42//! traits to clearly define safety invariants and localize any unsafety to
43//! 1 or 2 lines of code.
44//!
45//! The core, unsafe trait is `DigitsIter` and `Iter`, both which expect
46//! to be backed by a contiguous block of memory (a slice) but may skip
47//! bytes internally. To guarantee safety, for non-skip iterators you
48//! must implement [DigitsIter::is_consumed][is_consumed] correctly.
49//!
50//! This must correctly determine if there are any elements left in the
51//! iterator. If the buffer is contiguous, this can just be `index ==
52//! self.len()`, but for a non-contiguous iterator it must skip any digits to
53//! advance to the element next to be returned or the iterator itself will be
54//! unsafe. **ALL** other safety invariants depend on this being implemented
55//! correctly.
56//!
57//! To see if the cursor is at the end of the buffer, use
58//! [is_buffer_empty][is_buffer_empty].
59//!
60//! Any iterators must be peekable: you must be able to read and return the next
61//! value without advancing the iterator past that point. For iterators that
62//! skip bytes, this means advancing to the next element to be returned and
63//! returning that value.
64//!
65//! For examples of how to safely implement skip iterators, you can do something
66//! like:
67//!
68//! ```rust,ignore
69//! impl<_> DigitsIter<_> for MyIter {
70//! fn peek(&mut self) -> Option<u8> {
71//! loop {
72//! let value = self.bytes.get(self.index)?;
73//! if value != &b'.' {
74//! return value;
75//! }
76//! self.index += 1;
77//! }
78//! }
79//! }
80//! ```
81//!
82//! Then, [next](core::iter::Iterator::next) will be implemented in terms
83//! of [peek], incrementing the position in the cursor just after the value.
84//! The next iteration of peek will step to the correct byte to return.
85//!
86//! ```rust,ignore
87//! impl<_> Iterator for MyIter {
88//! type Item = &'a u8;
89//!
90//! fn next(&mut self) -> Option<Self::Item> {
91//! let value = self.peek()?;
92//! self.index += 1;
93//! Some(value)
94//! }
95//! }
96//! ```
97//!
98//! [is_buffer_empty]: <https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/iterator.rs#76>
99//! [is_consumed]: <https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/iterator.rs#L276>
100//! [peek]: <https://github.com/Alexhuszagh/rust-lexical/blob/8fe1d9a/lexical-util/src/iterator.rs#L284>
101
102// FIXME: Implement clippy/allow reasons once we drop support for 1.80.0 and below
103// Clippy reasons were stabilized in 1.81.0.
104
105// We want to have the same safety guarantees as Rust core,
106// so we allow unused unsafe to clearly document safety guarantees.
107#![allow(unused_unsafe)]
108#![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))]
109#![cfg_attr(not(feature = "std"), no_std)]
110#![deny(
111 clippy::doc_markdown,
112 clippy::unnecessary_safety_comment,
113 clippy::semicolon_if_nothing_returned,
114 clippy::unwrap_used,
115 clippy::as_underscore,
116 clippy::doc_markdown
117)]
118#![allow(
119 // used when concepts are logically separate
120 clippy::match_same_arms,
121 // loss of precision is intentional
122 clippy::integer_division,
123 // mathematical names use 1-character identifiers
124 clippy::min_ident_chars,
125 // these are not cryptographically secure contexts
126 clippy::integer_division_remainder_used,
127 // this can be intentional
128 clippy::module_name_repetitions,
129 // this is intentional: already passing a pointer and need performance
130 clippy::needless_pass_by_value,
131 // we use this for inline formatting for unsafe blocks
132 clippy::semicolon_inside_block,
133)]
134
135// Ensure our features are properly enabled. This means no parse without
136// parse support, etc.
137#[cfg(all(feature = "parse", not(any(feature = "parse-integers", feature = "parse-floats"))))]
138compile_error!(
139 "Do not use the `parse` feature directly. Use `parse-integers` and/or `parse-floats` instead."
140);
141
142#[cfg(all(feature = "write", not(any(feature = "write-integers", feature = "write-floats"))))]
143compile_error!(
144 "Do not use the `write` feature directly. Use `write-integers` and/or `write-floats` instead."
145);
146
147#[cfg(all(feature = "integers", not(any(feature = "write-integers", feature = "parse-integers"))))]
148compile_error!("Do not use the `integers` feature directly. Use `write-integers` and/or `parse-integers` instead.");
149
150#[cfg(all(feature = "floats", not(any(feature = "write-floats", feature = "parse-floats"))))]
151compile_error!(
152 "Do not use the `floats` feature directly. Use `write-floats` and/or `parse-floats` instead."
153);
154
155pub mod algorithm;
156pub mod ascii;
157pub mod assert;
158pub mod bf16;
159pub mod constants;
160pub mod digit;
161pub mod div128;
162pub mod error;
163pub mod extended_float;
164pub mod f16;
165pub mod format;
166pub mod iterator;
167pub mod mul;
168pub mod num;
169pub mod options;
170pub mod result;
171pub mod step;
172
173mod api;
174mod feature_format;
175mod format_builder;
176mod format_flags;
177mod noskip;
178mod not_feature_format;
179mod numtypes;
180mod skip;