Skip to content

Commit

Permalink
Merge branch 'main' into duration
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Dec 13, 2023
2 parents c6293b5 + ce08d32 commit 8155ef4
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 37 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "speedate"
authors = ["Samuel Colvin <s@muelcolvin.com>"]
version = "0.12.0"
version = "0.13.0"
edition = "2021"
description = "Fast and simple datetime, date, time and duration parsing"
readme = "README.md"
Expand Down
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 @@ -1382,3 +1382,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 8155ef4

Please sign in to comment.