1 //! Fast lexical conversion routines with a C FFI for a no_std environment. 2 //! 3 //! # Getting Started 4 //! 5 //! lexical-core is a low-level, partially FFI-compatible API for 6 //! number-to-string and string-to-number conversions, without requiring 7 //! a system allocator. If you would like to use a convenient, high-level 8 //! API, please look at [lexical](https://crates.io/crates/lexical) instead. 9 //! 10 //! # Getting Started 11 //! 12 //! ```rust 13 //! extern crate lexical_core; 14 //! 15 //! // String to number using slices 16 //! // The argument is the byte string parsed. 17 //! let f = lexical_core::atof64_slice(b"3.5"); // 3.5 18 //! let i = lexical_core::atoi32_slice(b"15"); // 15 19 //! 20 //! // String to number using pointer ranges, for FFI-compatible code. 21 //! // The first argument is a pointer to the start of the parsed byte array, 22 //! // and the second argument is a pointer to 1-past-the-end. It will process 23 //! // bytes in the range [first, last). 24 //! unsafe { 25 //! let bytes = b"3.5"; 26 //! let first = bytes.as_ptr(); 27 //! let last = first.add(bytes.len()); 28 //! let f = lexical_core::atof64_range(first, last); 29 //! } 30 //! 31 //! // If and only if the `radix` feature is enabled, you may use the radix 32 //! // overloads to parse non-decimal floats and strings. 33 //! ##[cfg(feature = "radix")] 34 //! let f = lexical_core::atof32_radix_slice(2, b"11.1"); // 3.5 35 //! ##[cfg(feature = "radix")] 36 //! let i = lexical_core::atoi32_radix_slice(2, b"1111"); // 15 37 //! 38 //! // The ato*_slice and ato*_range parsers are not checked, they do not 39 //! // validate that the input data is entirely correct, and discard trailing 40 //! // bytes that are found. The explicit behavior is to wrap on overflow, and 41 //! // to discard invalid digits. 42 //! let i = lexical_core::atoi8_slice(b"256"); // 0, wraps from 256 43 //! let i = lexical_core::atoi8_slice(b"1a5"); // 1, discards "a5" 44 //! 45 //! // You should prefer the checked parsers, whenever possible. These detect 46 //! // numeric overflow, and no invalid trailing digits are present. 47 //! // The error code for success is 0, all errors are less than 0. 48 //! 49 //! // Ideally, everything works great. 50 //! let res = lexical_core::try_atoi8_slice(b"15"); 51 //! assert_eq!(res.error.code, lexical_core::ErrorCode::Success); 52 //! assert_eq!(res.value, 15); 53 //! 54 //! // However, it detects numeric overflow, setting `res.error.code` 55 //! // to the appropriate value. 56 //! let res = lexical_core::try_atoi8_slice(b"256"); 57 //! assert_eq!(res.error.code, lexical_core::ErrorCode::Overflow); 58 //! 59 //! // Errors occurring prematurely terminating the parser due to invalid 60 //! // digits return the index in the buffer where the invalid digit was 61 //! // seen. This may useful in contexts like serde, which require numerical 62 //! // parsers from complex data without having to extract a substring 63 //! // containing only numeric data ahead of time. If the error is set 64 //! // to a `InvalidDigit`, the value is guaranteed to be accurate up until 65 //! // that point. For example, if the trailing data is whitespace, 66 //! // the value from an invalid digit may be perfectly valid in some contexts. 67 //! let res = lexical_core::try_atoi8_slice(b"15 45"); 68 //! assert_eq!(res.error.code, lexical_core::ErrorCode::InvalidDigit); 69 //! assert_eq!(res.error.index, 2); 70 //! assert_eq!(res.value, 15); 71 //! 72 //! // Number to string using slices. 73 //! // The first argument is the value, the second argument is the radix, 74 //! // and the third argument is the buffer to write to. 75 //! // The function returns a subslice of the original buffer, and will 76 //! // always start at the same position (`buf.as_ptr() == slc.as_ptr()`). 77 //! let mut buf = [b'0'; lexical_core::MAX_I64_SIZE]; 78 //! let slc = lexical_core::i64toa_slice(15, &mut buf); 79 //! assert_eq!(slc, b"15"); 80 //! 81 //! // If an insufficiently long buffer is passed, the serializer will panic. 82 //! // PANICS 83 //! let mut buf = [b'0'; 1]; 84 //! //let slc = lexical_core::i64toa_slice(15, &mut buf); 85 //! 86 //! // In order to guarantee the buffer is long enough, always ensure there 87 //! // are at least `MAX_XX_SIZE`, where XX is the type name in upperase, 88 //! // IE, for `isize`, `MAX_ISIZE_SIZE`. 89 //! let mut buf = [b'0'; lexical_core::MAX_F64_SIZE]; 90 //! let slc = lexical_core::f64toa_slice(15.1, &mut buf); 91 //! assert_eq!(slc, b"15.1"); 92 //! ``` 93 94 // FEATURES 95 96 // Require intrinsics in a no_std context. 97 #![cfg_attr(not(feature = "std"), no_std)] 98 #![cfg_attr(all(not(feature = "std"), feature = "correct", feature = "radix"), feature(alloc))] 99 #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] 100 #![cfg_attr(all(not(test), not(feature = "std")), feature(lang_items))] 101 102 // DEPENDENCIES 103 104 #[macro_use] 105 extern crate cfg_if; 106 107 #[cfg(feature = "correct")] 108 #[allow(unused_imports)] // Not used before 1.26. 109 #[macro_use] 110 extern crate static_assertions; 111 112 // Testing assertions for floating-point equality. 113 #[cfg(test)] 114 #[macro_use] 115 extern crate approx; 116 117 // Test against randomly-generated data. 118 #[cfg(test)] 119 #[macro_use] 120 extern crate quickcheck; 121 122 // Test against randomly-generated guided data. 123 #[cfg(test)] 124 #[macro_use] 125 extern crate proptest; 126 127 // Use vec if there is a system allocator, which we require only if 128 // we're using the correct and radix features. 129 #[cfg(all(not(feature = "std"), feature = "correct", feature = "radix"))] 130 #[cfg_attr(test, macro_use)] 131 extern crate alloc; 132 133 // Use stackvector for atof. 134 #[cfg(feature = "correct")] 135 #[macro_use] 136 extern crate stackvector; 137 138 // Ensure only one back-end is enabled. 139 #[cfg(all(feature = "grisu3", feature = "ryu"))] 140 compile_error!("Lexical only accepts one of the following backends: `grisu3` or `ryu`."); 141 142 // Import the back-end, if applicable. 143 cfg_if! { 144 if #[cfg(feature = "grisu3")] { 145 extern crate dtoa; 146 } else if #[cfg(feature = "ryu")] { 147 extern crate ryu; 148 }} // cfg_if 149 150 /// Facade around the core features for name mangling. 151 pub(crate) mod lib { 152 #[cfg(feature = "std")] 153 pub(crate) use std::*; 154 155 #[cfg(not(feature = "std"))] 156 pub(crate) use core::*; 157 158 cfg_if! { 159 if #[cfg(all(feature = "correct", feature = "radix"))] { 160 #[cfg(feature = "std")] 161 pub(crate) use std::vec::Vec; 162 163 #[cfg(not(feature = "std"))] 164 pub(crate) use alloc::vec::Vec; 165 }} // cfg_if 166 167 } // lib 168 169 // PANIC 170 171 // Need to define a panic handler when we're not testing (panic handler 172 // then becomes "unwind" but there is no_std). This causes us to fail 173 // with doctests, so ensure `--tests` is passed to `cargo test` whenever 174 // we are in a `no_std` context. 175 cfg_if! { 176 if #[cfg(all(not(test), not(feature = "std")))] { 177 use lib::intrinsics; 178 use lib::panic::PanicInfo; 179 180 #[panic_handler] 181 fn panic(_: &PanicInfo) -> ! { 182 unsafe { 183 intrinsics::abort(); 184 } 185 } 186 187 #[lang = "eh_personality"] 188 extern fn eh_personality() {} 189 }} // cfg_if 190 191 // API 192 193 // Hide implementation details 194 #[macro_use] 195 mod util; 196 197 mod atof; 198 mod atoi; 199 mod float; 200 mod ftoa; 201 mod itoa; 202 203 // Publicly re-export the low-level string-to-float functions. 204 pub use atof::*; 205 206 // Publicly re-export the low-level string-to-integer functions. 207 pub use atoi::*; 208 209 // Publicly re-export the low-level float-to-string functions. 210 pub use ftoa::*; 211 212 // Publicly re-export the low-level integer-to-string functions. 213 pub use itoa::*; 214 215 // Re-export configuration and utilities globally. 216 pub use util::*; 217