Skip to content

Commit

Permalink
Dummy commit, DO NOT MERGE.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexhuszagh committed Jan 12, 2025
1 parent f53fae0 commit 1532839
Show file tree
Hide file tree
Showing 18 changed files with 773 additions and 106 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `build_checked` to our `Options` API (#204).
- Added `has_digit_separator` to `NumberFormat` (#204).
- Re-export `NumberFormat` to our other crates (#204).
- Add `Options::from_radix` for all options for similar APIs for each (#208).
- Added `Options::from_radix` for all options for similar APIs for each (#208).
- Support for requiring both integer and fraction digits with exponents, that is, `1.e5` and `.1e5`, as opposed to just requiring `1e5` (#215).
- Added `supports_parsing_integers`, `supports_parsing_floats`, `supports_writing_integers`, and `supports_writing_floats` for our number formats (#215).
- Added `required_base_prefix` and `required_base_suffix` for our number formats (#215).

### Changed

Expand Down
28 changes: 19 additions & 9 deletions lexical-parse-float/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,25 +536,31 @@ pub fn parse_number<'a, const FORMAT: u128, const IS_PARTIAL: bool>(
// INTEGER

// Check to see if we have a valid base prefix.
// NOTE: `lz_prefix` is if we had a leading zero when
// checking for a base prefix: it is not if the prefix
// exists or not.
#[allow(unused_variables)]
let mut is_prefix = false;
let mut lz_prefix = false;
#[cfg(feature = "format")]
{
let base_prefix = format.base_prefix();
let mut has_prefix = false;
let mut iter = byte.integer_iter();
if base_prefix != 0 && iter.read_if_value_cased(b'0').is_some() {
// Check to see if the next character is the base prefix.
// We must have a format like `0x`, `0d`, `0o`.
// NOTE: The check for empty integer digits happens below so
// we don't need a redundant check here.
is_prefix = true;
if iter.read_if_value(base_prefix, format.case_sensitive_base_prefix()).is_some()
&& iter.is_buffer_empty()
&& format.required_integer_digits()
{
lz_prefix = true;
let has_prefix =
iter.read_if_value(base_prefix, format.case_sensitive_base_prefix()).is_some();
if has_prefix && iter.is_buffer_empty() && format.required_integer_digits() {
return Err(Error::EmptyInteger(iter.cursor()));
}
}
if format.required_base_prefix() && !has_prefix {
return Err(Error::MissingBasePrefix(iter.cursor()));
}
}

// Parse our integral digits.
Expand Down Expand Up @@ -600,7 +606,7 @@ pub fn parse_number<'a, const FORMAT: u128, const IS_PARTIAL: bool>(

// Check if integer leading zeros are disabled.
#[cfg(feature = "format")]
if !is_prefix && format.no_float_leading_zeros() {
if !lz_prefix && format.no_float_leading_zeros() {
if integer_digits.len() > 1 && integer_digits.first() == Some(&b'0') {
return Err(Error::InvalidLeadingZeros(start.cursor()));
}
Expand Down Expand Up @@ -741,12 +747,16 @@ pub fn parse_number<'a, const FORMAT: u128, const IS_PARTIAL: bool>(
// that the first character **is not** a digit separator.
#[allow(unused_variables)]
let base_suffix = format.base_suffix();
#[cfg(feature = "format")]
#[cfg(all(feature = "format", feature = "power-of-two"))]
if base_suffix != 0 {
if byte.first_is(base_suffix, format.case_sensitive_base_suffix()) {
let is_suffix = byte.first_is(base_suffix, format.case_sensitive_base_suffix());
if is_suffix {
// SAFETY: safe since `byte.len() >= 1`.
unsafe { byte.step_unchecked() };
}
if format.required_base_suffix() && !is_suffix {
return Err(Error::MissingBaseSuffix(byte.cursor()));
}
}

// CHECK OVERFLOW
Expand Down
38 changes: 38 additions & 0 deletions lexical-parse-float/tests/api_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1307,3 +1307,41 @@ fn supported_test() {
let value = f64::from_lexical_partial_with_options::<FORMAT>(float.as_bytes(), &OPTIONS);
assert_eq!(value, Ok((12345.0, 7)));
}


#[test]
#[cfg(all(feature = "format", feature = "power-of-two"))]
fn require_base_prefix_test() {
use core::num;

const PREFIX: u128 = NumberFormatBuilder::new()
.base_prefix(num::NonZeroU8::new(b'd'))
.required_base_prefix(true)
.build_strict();
const OPTIONS: Options = Options::new();

// TODO: Failing with a missing base prefix
let value = f64::from_lexical_with_options::<PREFIX>(b"0d12345", &OPTIONS);
assert_eq!(value, Ok(12345.0));
let value = f64::from_lexical_with_options::<PREFIX>(b"12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBasePrefix(0)));

let value = f64::from_lexical_with_options::<PREFIX>(b"-0d12345", &OPTIONS);
assert_eq!(value, Ok(-12345.0));
let value = f64::from_lexical_with_options::<PREFIX>(b"-12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBasePrefix(1)));

const SUFFIX: u128 = NumberFormatBuilder::rebuild(PREFIX)
.base_suffix(num::NonZeroU8::new(b'z'))
.required_base_suffix(true)
.build_strict();
let value = f64::from_lexical_with_options::<SUFFIX>(b"0d12345z", &OPTIONS);
assert_eq!(value, Ok(12345.0));
let value = f64::from_lexical_with_options::<SUFFIX>(b"0d12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBaseSuffix(5)));

let value = f64::from_lexical_with_options::<SUFFIX>(b"-0d12345z", &OPTIONS);
assert_eq!(value, Ok(-12345.0));
let value = f64::from_lexical_with_options::<SUFFIX>(b"-0d12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBasePrefix(1)));
}
12 changes: 10 additions & 2 deletions lexical-parse-integer/src/algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ macro_rules! fmt_invalid_digit {
// correctness.
debug_assert!($iter.is_contiguous() || $is_end);

let base_suffix = NumberFormat::<FORMAT>::BASE_SUFFIX;
let uncased_base_suffix = NumberFormat::<FORMAT>::CASE_SENSITIVE_BASE_SUFFIX;
let format = NumberFormat::<FORMAT> {};
let base_suffix = format.base_suffix();
let uncased_base_suffix = format.case_sensitive_base_suffix();
// Need to check for a base suffix, if so, return a valid value.
// We can't have a base suffix at the first value (need at least
// 1 digit).
Expand Down Expand Up @@ -411,6 +412,7 @@ macro_rules! parse_1digit_unchecked {
while let Some(&c) = $iter.next() {
let digit = match char_to_digit_const(c, radix) {
Some(v) => v,
// TODO: These both incorrectly handle required base suffixes
None => fmt_invalid_digit!($value, $iter, c, $start_index, $invalid_digit, $is_end),
};
// multiply first since compilers are good at optimizing things out and will do
Expand Down Expand Up @@ -449,6 +451,7 @@ macro_rules! parse_1digit_checked {
while let Some(&c) = $iter.next() {
let digit = match char_to_digit_const(c, radix) {
Some(v) => v,
// TODO: These both incorrectly handle required base suffixes
None => fmt_invalid_digit!($value, $iter, c, $start_index, $invalid_digit, true),
};
// multiply first since compilers are good at optimizing things out and will do
Expand Down Expand Up @@ -650,6 +653,9 @@ macro_rules! algorithm {
}
}
}
if cfg!(feature = "power-of-two") && format.required_base_prefix() && !is_prefix {
return Err(Error::MissingBasePrefix(iter.cursor()));
}

// If we have a format that doesn't accept leading zeros,
// check if the next value is invalid. It's invalid if the
Expand Down Expand Up @@ -694,6 +700,8 @@ macro_rules! algorithm {
parse_digits_checked!(value, iter, checked_add, wrapping_add, start_index, $invalid_digit, Overflow, $no_multi_digit, overflow_digits);
}

// TODO: Need to handle a required based suffix

$into_ok!(value, iter.buffer_length(), iter.current_count())
}};
}
Expand Down
46 changes: 46 additions & 0 deletions lexical-parse-integer/tests/api_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,49 @@ fn supported_test() {
let value = u64::from_lexical_partial_with_options::<FORMAT>(integer.as_bytes(), &OPTIONS);
assert_eq!(value, Ok((12345, 5)));
}

#[test]
#[cfg(all(feature = "format", feature = "power-of-two"))]
fn require_base_prefix_test() {
use core::num;

const PREFIX: u128 = NumberFormatBuilder::new()
.base_prefix(num::NonZeroU8::new(b'd'))
.required_base_prefix(true)
.build_strict();
const OPTIONS: Options = Options::new();

let value = i64::from_lexical_with_options::<PREFIX>(b"0d12345", &OPTIONS);
assert_eq!(value, Ok(12345));
let value = i64::from_lexical_with_options::<PREFIX>(b"12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBasePrefix(0)));

let value = i64::from_lexical_with_options::<PREFIX>(b"-0d12345", &OPTIONS);
assert_eq!(value, Ok(-12345));
let value = i64::from_lexical_with_options::<PREFIX>(b"-12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBasePrefix(1)));

let value = u64::from_lexical_with_options::<PREFIX>(b"0d12345", &OPTIONS);
assert_eq!(value, Ok(12345));
let value = u64::from_lexical_with_options::<PREFIX>(b"12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBasePrefix(0)));

const SUFFIX: u128 = NumberFormatBuilder::rebuild(PREFIX)
.base_suffix(num::NonZeroU8::new(b'z'))
.required_base_suffix(true)
.build_strict();
let value = i64::from_lexical_with_options::<SUFFIX>(b"0d12345z", &OPTIONS);
assert_eq!(value, Ok(12345));
let value = i64::from_lexical_with_options::<SUFFIX>(b"0d12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBaseSuffix(5)));

let value = i64::from_lexical_with_options::<SUFFIX>(b"-0d12345z", &OPTIONS);
assert_eq!(value, Ok(-12345));
let value = i64::from_lexical_with_options::<SUFFIX>(b"-0d12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBasePrefix(1)));

let value = u64::from_lexical_with_options::<SUFFIX>(b"0d12345z", &OPTIONS);
assert_eq!(value, Ok(12345));
let value = u64::from_lexical_with_options::<SUFFIX>(b"0d12345", &OPTIONS);
assert_eq!(value, Err(Error::MissingBasePrefix(0)));
}
14 changes: 13 additions & 1 deletion lexical-util/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ pub enum Error {
InvalidPositiveSign(usize),
/// Invalid negative sign for an unsigned type was found.
InvalidNegativeSign(usize),
/// Missing a required base prefix when parsing the number.
MissingBasePrefix(usize),
/// Missing a required base suffix when parsing the number.
MissingBaseSuffix(usize),

// NUMBER FORMAT ERRORS
/// Invalid radix for the mantissa (significant) digits.
Expand Down Expand Up @@ -167,6 +171,8 @@ impl Error {
Self::MissingSign(_) => "'missing required `+/-` sign for integer'",
Self::InvalidPositiveSign(_) => "'invalid `+` sign for an integer was found'",
Self::InvalidNegativeSign(_) => "'invalid `-` sign for an unsigned type was found'",
Self::MissingBasePrefix(_) => "'missing a required base prefix when parsing the number'",
Self::MissingBaseSuffix(_) => "'missing a required base suffix when parsing the number'",

// NUMBER FORMAT ERRORS
Self::InvalidMantissaRadix => "'invalid radix for mantissa digits'",
Expand All @@ -186,7 +192,7 @@ impl Error {
Self::InvalidConsecutiveFractionDigitSeparator => "'enabled consecutive digit separators in the fraction without setting a valid location'",
Self::InvalidConsecutiveExponentDigitSeparator => "'enabled consecutive digit separators in the exponent without setting a valid location'",
Self::InvalidFlags => "'invalid flags enabled without the format feature'",
Self::Unsupported => "the desired operation is unsupported for this format",
Self::Unsupported => "'the desired operation is unsupported for this format'",

// OPTION ERRORS
Self::InvalidNanString => "'NaN string must started with `n`'",
Expand Down Expand Up @@ -233,6 +239,8 @@ impl Error {
Self::MissingSign(index) => Some(index),
Self::InvalidPositiveSign(index) => Some(index),
Self::InvalidNegativeSign(index) => Some(index),
Self::MissingBasePrefix(index) => Some(index),
Self::MissingBaseSuffix(index) => Some(index),

// NUMBER FORMAT ERRORS
Self::InvalidMantissaRadix => None,
Expand Down Expand Up @@ -292,6 +300,8 @@ impl Error {
is_error_type!(is_missing_sign, MissingSign(_));
is_error_type!(is_invalid_positive_sign, InvalidPositiveSign(_));
is_error_type!(is_invalid_negative_sign, InvalidNegativeSign(_));
is_error_type!(is_missing_base_prefix, MissingBasePrefix(_));
is_error_type!(is_missing_base_suffix, MissingBaseSuffix(_));
is_error_type!(is_invalid_mantissa_radix, InvalidMantissaRadix);
is_error_type!(is_invalid_exponent_base, InvalidExponentBase);
is_error_type!(is_invalid_exponent_radix, InvalidExponentRadix);
Expand Down Expand Up @@ -391,6 +401,8 @@ impl fmt::Display for Error {
Self::MissingSign(index) => write_parse_error!(formatter, description, index),
Self::InvalidPositiveSign(index) => write_parse_error!(formatter, description, index),
Self::InvalidNegativeSign(index) => write_parse_error!(formatter, description, index),
Self::MissingBasePrefix(index) => write_parse_error!(formatter, description, index),
Self::MissingBaseSuffix(index) => write_parse_error!(formatter, description, index),

// NUMBER FORMAT ERRORS
Self::InvalidMantissaRadix => format_message!(formatter, description),
Expand Down
Loading

0 comments on commit 1532839

Please sign in to comment.