lexical_write_integer/
lib.rs

1//! Fast lexical integer-to-string conversion routines.
2//!
3//! The default implementations use power reduction to unroll
4//! 4 loops at a time to minimize the number of required divisions,
5//! leading to massive performance gains. In addition, decimal
6//! strings pre-calculate the number of digits, avoiding temporary buffers.
7//!
8//! A compact, fallback algorithm uses a naive, simple algorithm,
9//! where each loop generates a single digit. This comes at a performance
10//! penalty, but produces smaller binaries.
11//!
12//! # Features
13//!
14//! * `std` - Use the standard library.
15//! * `power-of-two` - Add support for writing power-of-two integer strings.
16//! * `radix` - Add support for strings of any radix.
17//! * `compact` - Reduce code size at the cost of performance.
18//! * `safe` - Ensure only memory-safe indexing is used.
19//!
20//! # Note
21//!
22//! Only documented functionality is considered part of the public API:
23//! any of the modules, internal functions, or structs may change
24//! release-to-release without major or minor version changes. Use
25//! internal implementation details at your own risk.
26//!
27//! lexical-write-integer mainly exists as an implementation detail for
28//! lexical-core, although its API is stable. If you would like to use
29//! a high-level API that writes to and parses from `String` and `&str`,
30//! respectively, please look at [lexical](https://crates.io/crates/lexical)
31//! instead. If you would like an API that supports multiple numeric
32//! conversions, please look at [lexical-core](https://crates.io/crates/lexical-core)
33//! instead.
34//!
35//! # Version Support
36//!
37//! The minimum, standard, required version is 1.63.0, for const generic
38//! support. Older versions of lexical support older Rust versions.
39//!
40//! # Design
41//!
42//! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Algorithm.md)
43//! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-integer/docs/Benchmarks.md)
44//! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks)
45//!
46//! # Safety
47//!
48//! This module uses a some more unsafe code for moderately acceptable
49//! performance. The compact decimal serializer has no non-local safety
50//! invariants, which since it's focused on code size rather than performance,
51//! this trade-off is acceptable and it uses a temporary, over-allocated buffer
52//! as an intermediate.
53//!
54//! The decimal writer relies on pre-computed tables and an exact calculation
55//! of the digit count ([`digit_count`]) to avoid any overhead. Avoid
56//! intermediary copies is **CRITICAL** for fast performance so the entire
57//! buffer must be known but assigned to use algorithms the compiler cannot
58//! easily verify. This is because we use multi-digit optimizations with our
59//! pre-computed tables, so we cannot just iterate over the slice and assign
60//! iteratively. Using checked indexing can lead to 30%+ decreases in
61//! performance. However, with careful analysis and factoring of the code, it's
62//! fairly easy to demonstrate the safety as long as the caller ensures at least
63//! the required number of digits are provided.
64//!
65//! Our algorithms work like this, carving off the lower digits and writing them
66//! to the back of the buffer.
67//!
68//! ```rust,ignore
69//! let mut value = 12345u32;
70//! let buffer = [0u8; 32];
71//! let digits = value.digit_count();
72//! let bytes = buffer[..digits];
73//!
74//! let radix = 10;
75//! let radix2 = radix * radix;
76//! let radix4 = radix2 * radix2
77//! let mut index = bytes.len();
78//! while value >= 10000 {
79//!     let r = value % radix4;
80//!     value /= radix4;
81//!     let r1 = 2 * (r / radix2) as usize;
82//!     let r2 = 2 * (r % radix2) as usize;
83//!
84//!     // write 5, then 4
85//!     index -= 1;
86//!     bytes[index] = table[r2 + 1];
87//!     index -= 1;
88//!     bytes[index] = table[r2];
89//!
90//!     // write 3 then 2
91//!     index -= 1;
92//!     bytes[index] = table[r1 + 1];
93//!     index -= 1;
94//!     bytes[index] = table[r1];
95//! }
96//!
97//! // oontinue with radix^2 and then a single digit.
98//! ```
99//!
100//! We can efficiently determine at compile time if the pre-computed
101//! tables are large enough so there are no non-local safety considerations
102//! there. The current logic call stack is:
103//! 1. [`to_lexical`]
104//! 2. [decimal][dec], compact, or radix (gets the correct tables and calls
105//!    algorithm)
106//! 3. [algorithm]
107//!
108//! [decimal][dec], compact, and radix therefore **MUST** be safe and do type
109//! check of the bounds to avoid too much exposure to unsafety. Only
110//! [`algorithm`] should have any unsafety associated with it. That is, as long
111//! as the direct caller has ensure the proper buffer is allocated, there are
112//! non-local safety invariants.
113//!
114//! [`digit_count`]: crate::decimal::DigitCount
115//! [`to_lexical`]: crate::ToLexical::to_lexical
116//! [dec]: crate::decimal::Decimal::decimal
117//! [`algorithm`]: crate::algorithm::algorithm
118
119// We want to have the same safety guarantees as Rust core,
120// so we allow unused unsafe to clearly document safety guarantees.
121#![allow(unused_unsafe)]
122#![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))]
123#![cfg_attr(not(feature = "std"), no_std)]
124#![deny(
125    clippy::doc_markdown,
126    clippy::unnecessary_safety_comment,
127    clippy::semicolon_if_nothing_returned,
128    clippy::unwrap_used,
129    clippy::as_underscore,
130    clippy::doc_markdown
131)]
132#![allow(
133    // used when concepts are logically separate
134    clippy::match_same_arms,
135    // loss of precision is intentional
136    clippy::integer_division,
137    // mathematical names use 1-character identifiers
138    clippy::min_ident_chars,
139    // these are not cryptographically secure contexts
140    clippy::integer_division_remainder_used,
141    // this can be intentional
142    clippy::module_name_repetitions,
143    // this is intentional: already passing a pointer and need performance
144    clippy::needless_pass_by_value,
145    // we use this for inline formatting for unsafe blocks
146    clippy::semicolon_inside_block,
147)]
148
149pub mod algorithm;
150pub mod compact;
151pub mod decimal;
152pub mod digit_count;
153pub mod jeaiii;
154pub mod options;
155pub mod radix;
156pub mod table;
157pub mod write;
158
159mod api;
160mod table_binary;
161mod table_decimal;
162mod table_radix;
163
164// Re-exports
165pub use lexical_util::constants::{FormattedSize, BUFFER_SIZE};
166pub use lexical_util::format::{self, NumberFormatBuilder};
167pub use lexical_util::options::WriteOptions;
168
169pub use self::api::{ToLexical, ToLexicalWithOptions};
170#[doc(inline)]
171pub use self::options::{Options, OptionsBuilder};