Crate lexical_util

source ·
Expand description

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 instead. If you would like an API that supports multiple numeric conversions, please look at 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 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.

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:

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 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.

impl<_> Iterator for MyIter {
    type Item = &'a u8;

    fn next(&mut self) -> Option<Self::Item> {
        let value = self.peek()?;
        self.index += 1;
        Some(value)
    }
}

Modules§

  • Simple, shared algorithms for slices and iterators.
  • Utilities for working with ASCII characters.
  • Debugging assertions to check a radix is valid.
  • Pre-defined constants for numeric types.
  • Utilities to process digits.
  • Optimized division algorithms for u128.
  • Error type for numeric parsing functions.
  • Extended precision floating-point type.
  • Public API for the number format packed struct.
  • Specialized iterator traits.
  • Fast multiplication routines.
  • Utilities for Rust numbers.
  • Shared traits for the options API.
  • Result type for numeric parsing functions.
  • The maximum digits that can be held in a u64 for a given radix without overflow.

Macros§