lexical_util/
lib.rs

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

// FIXME: Implement clippy/allow reasons once we drop support for 1.80.0 and below
// Clippy reasons were stabilized in 1.81.0.

// We want to have the same safety guarantees as Rust core,
// so we allow unused unsafe to clearly document safety guarantees.
#![allow(unused_unsafe)]
#![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
    clippy::doc_markdown,
    clippy::unnecessary_safety_comment,
    clippy::semicolon_if_nothing_returned,
    clippy::unwrap_used,
    clippy::as_underscore,
    clippy::doc_markdown
)]
#![allow(
    // used when concepts are logically separate
    clippy::match_same_arms,
    // loss of precision is intentional
    clippy::integer_division,
    // mathematical names use 1-character identifiers
    clippy::min_ident_chars,
    // these are not cryptographically secure contexts
    clippy::integer_division_remainder_used,
    // this can be intentional
    clippy::module_name_repetitions,
    // this is intentional: already passing a pointer and need performance
    clippy::needless_pass_by_value,
    // we use this for inline formatting for unsafe blocks
    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;
pub mod bf16;
pub mod constants;
pub mod digit;
pub mod div128;
pub mod error;
pub mod extended_float;
pub mod f16;
pub mod format;
pub mod iterator;
pub mod mul;
pub mod num;
pub mod options;
pub mod result;
pub mod step;

mod api;
mod feature_format;
mod format_builder;
mod format_flags;
mod noskip;
mod not_feature_format;
mod numtypes;
mod skip;