Skip to content

Commit

Permalink
no longer accept '-' or '+' as valid numeric strings (#52)
Browse files Browse the repository at this point in the history
* no longer accept '-' or '+' as valid numeric strings

* unwrap error

Co-authored-by: Samuel Colvin <s@muelcolvin.com>

* Update tests/main.rs

---------

Co-authored-by: Samuel Colvin <s@muelcolvin.com>
  • Loading branch information
davidhewitt and samuelcolvin authored Nov 6, 2023
1 parent fd0c948 commit 6e68609
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 36 deletions.
58 changes: 24 additions & 34 deletions src/numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,28 @@
///
/// This is around 2x faster than using `str::parse::<i64>()`
pub fn int_parse_str(s: &str) -> Option<i64> {
fast_parse_int(s.bytes())
int_parse_bytes(s.as_bytes())
}

/// Parse bytes as an int.
pub fn int_parse_bytes(s: &[u8]) -> Option<i64> {
fast_parse_int(s.iter().copied())
}
let (neg, first_digit, digits) = match s {
[b'-', first, digits @ ..] => (true, first, digits),
[b'+', first, digits @ ..] | [first, digits @ ..] => (false, first, digits),
_ => return None,
};

pub fn fast_parse_int<I: Iterator>(mut bytes: I) -> Option<i64>
where
I: Iterator<Item = u8>,
{
let mut result: i64 = 0;
let neg = match bytes.next() {
Some(b'-') => true,
Some(b'+') => false,
Some(c) if c.is_ascii_digit() => {
result = (c & 0x0f) as i64;
false
}
let mut result = match first_digit {
b'0' => 0,
b'1'..=b'9' => (first_digit & 0x0f) as i64,
_ => return None,
};

for digit in bytes {
for digit in digits {
result = result.checked_mul(10)?;
match digit {
b'0'..=b'9' => {
result = result.checked_mul(10)?;
result = result.checked_add((digit & 0x0f) as i64)?
}
b'0' => {}
b'1'..=b'9' => result = result.checked_add((digit & 0x0f) as i64)?,
_ => return None,
}
}
Expand Down Expand Up @@ -58,30 +51,27 @@ impl IntFloat {
///
/// This is around 2x faster than using `str::parse::<f64>()`
pub fn float_parse_str(s: &str) -> IntFloat {
fast_parse_float(s.bytes())
float_parse_bytes(s.as_bytes())
}

/// Parse bytes as an float.
pub fn float_parse_bytes(s: &[u8]) -> IntFloat {
fast_parse_float(s.iter().copied())
}
let (neg, first_digit, digits) = match s {
[b'-', first, digits @ ..] => (true, first, digits),
[b'+', first, digits @ ..] | [first, digits @ ..] => (false, first, digits),
_ => return IntFloat::Err,
};

pub fn fast_parse_float<I: Iterator>(mut bytes: I) -> IntFloat
where
I: Iterator<Item = u8>,
{
let mut int_part: i64 = 0;
let neg = match bytes.next() {
Some(b'-') => true,
Some(c) if c.is_ascii_digit() => {
int_part = (c & 0x0f) as i64;
false
}
let mut int_part = match first_digit {
b'0' => 0,
b'1'..=b'9' => (first_digit & 0x0f) as i64,
_ => return IntFloat::Err,
};

let mut found_dot = false;

let mut bytes = digits.iter().copied();

for digit in bytes.by_ref() {
match digit {
b'0'..=b'9' => {
Expand Down
25 changes: 23 additions & 2 deletions tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use chrono::{Datelike, FixedOffset as ChronoFixedOffset, NaiveDate, NaiveDateTim
use strum::EnumMessage;

use speedate::{
float_parse_str, int_parse_str, Date, DateTime, Duration, MicrosecondsPrecisionOverflowBehavior, ParseError, Time,
TimeConfig, TimeConfigBuilder,
float_parse_bytes, float_parse_str, int_parse_bytes, int_parse_str, Date, DateTime, Duration, IntFloat,
MicrosecondsPrecisionOverflowBehavior, ParseError, Time, TimeConfig, TimeConfigBuilder,
};

/// macro for expected values
Expand Down Expand Up @@ -1348,3 +1348,24 @@ fn test_time_config_builder() {
);
assert_eq!(TimeConfigBuilder::new().build(), TimeConfig::builder().build());
}

#[test]
fn date_dash_err() {
let error = Date::parse_str("-").unwrap_err();
assert_eq!(error, ParseError::TooShort);
assert_eq!(error.to_string(), "too_short");
assert_eq!(error.get_documentation(), Some("input is too short"));
}

#[test]
fn number_dash_err() {
assert!(int_parse_str("-").is_none());
assert!(int_parse_str("+").is_none());
assert!(int_parse_bytes(b"-").is_none());
assert!(int_parse_bytes(b"+").is_none());

assert!(matches!(float_parse_str("-"), IntFloat::Err));
assert!(matches!(float_parse_str("+"), IntFloat::Err));
assert!(matches!(float_parse_bytes(b"-"), IntFloat::Err));
assert!(matches!(float_parse_bytes(b"+"), IntFloat::Err));
}

0 comments on commit 6e68609

Please sign in to comment.