Skip to content

Commit

Permalink
chore: add tests for digits parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
CrazyboyQCD committed Feb 8, 2025
1 parent c9cf38f commit 51974b7
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 22 deletions.
27 changes: 26 additions & 1 deletion core/engine/src/builtins/date/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{js_string, run_test_actions, JsNativeErrorKind, TestAction};
use crate::{
builtins::date::utils::fast_atoi::{process_4, process_8},
js_string, run_test_actions, JsNativeErrorKind, TestAction,
};
use boa_macros::js_str;
use indoc::indoc;
use time::{macros::format_description, OffsetDateTime};
Expand Down Expand Up @@ -72,6 +75,28 @@ fn timestamp_from_utc(
t.unix_timestamp() * 1000 + i64::from(t.millisecond())
}

#[test]
fn parse_ascii_digits() {
let parse_8_ascii_digits = |val: &[u8; 8], len: usize| -> u64 {
let val = u64::from_le_bytes(*val);
process_8(val, len)
};
assert_eq!(12_345_678, parse_8_ascii_digits(b"12345678", 8));
assert_eq!(123_456, parse_8_ascii_digits(b"123456xx", 6));
assert_eq!(123, parse_8_ascii_digits(b"123xxxxx", 3));
assert_eq!(123, parse_8_ascii_digits(b"000123xx", 6));
assert_eq!(0, parse_8_ascii_digits(b"00000000", 8));
let parse_4_ascii_digits = |val: &[u8; 4], len: usize| -> u64 {
let val = u32::from_le_bytes(*val);
u64::from(process_4(val, len))
};
assert_eq!(1234, parse_4_ascii_digits(b"1234", 4));
assert_eq!(12, parse_4_ascii_digits(b"12xx", 2));
assert_eq!(3, parse_4_ascii_digits(b"003x", 3));
assert_eq!(23, parse_4_ascii_digits(b"023x", 3));
assert_eq!(0, parse_4_ascii_digits(b"0000", 8));
}

#[test]
fn date_this_time_value() {
run_test_actions([TestAction::assert_native_error(
Expand Down
67 changes: 46 additions & 21 deletions core/engine/src/builtins/date/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,27 @@ struct DateParser<'a> {
offset: i64,
}

// Copied from https://github.com/RoDmitry/atoi_simd/blob/master/src/fallback.rs,
// which is based on https://rust-malaysia.github.io/code/2020/07/11/faster-integer-parsing.html.
#[doc(hidden)]
#[allow(clippy::inline_always)]
pub(in crate::builtins::date) mod fast_atoi {
#[inline(always)]
pub(in crate::builtins::date) const fn process_8(mut val: u64, len: usize) -> u64 {
val <<= 64_usize.saturating_sub(len << 3); // << 3 - same as mult by 8
val = (val & 0x0F0F_0F0F_0F0F_0F0F).wrapping_mul(0xA01) >> 8;
val = (val & 0x00FF_00FF_00FF_00FF).wrapping_mul(0x64_0001) >> 16;
(val & 0x0000_FFFF_0000_FFFF).wrapping_mul(0x2710_0000_0001) >> 32
}

#[inline(always)]
pub(in crate::builtins::date) const fn process_4(mut val: u32, len: usize) -> u32 {
val <<= 32_usize.saturating_sub(len << 3); // << 3 - same as mult by 8
val = (val & 0x0F0F_0F0F).wrapping_mul(0xA01) >> 8;
(val & 0x00FF_00FF).wrapping_mul(0x64_0001) >> 16
}
}

impl<'a> DateParser<'a> {
fn new(s: &'a str, hooks: &'a dyn HostHooks) -> Self {
Self {
Expand Down Expand Up @@ -843,31 +864,35 @@ impl<'a> DateParser<'a> {
Some(digits)
}

#[allow(clippy::items_after_statements)]
#[allow(clippy::inline_always)]
fn parse_n_ascii_digits<const N: usize>(&mut self) -> Option<u64> {
assert!(N <= 8, "parse_n_ascii_digits parses no more than 8 digits");

let digits = self.next_n_ascii_digits::<N>()?;
if N <= 4 {
let mut res = 0;
for digit in digits {
res = res * 10 + u64::from(digit & 0xF);
if N == 0 {
return None;
}
let ascii_digits = self.next_n_ascii_digits::<N>()?;
match N {
1..4 => {
// When N is small, process digits naively.
let mut res = 0;
for digit in ascii_digits {
res = res * 10 + u64::from(digit & 0xF);
}
Some(res)
}
Some(res)
} else {
// Copied from https://github.com/RoDmitry/atoi_simd/blob/master/src/fallback.rs
#[inline(always)]
fn process_8(mut val: u64, len: usize) -> u64 {
val <<= 64_usize.saturating_sub(len << 3); // << 3 - same as mult by 8
val = (val & 0x0F0F_0F0F_0F0F_0F0F).wrapping_mul(0xA01) >> 8;
val = (val & 0x00FF_00FF_00FF_00FF).wrapping_mul(0x64_0001) >> 16;
(val & 0x0000_FFFF_0000_FFFF).wrapping_mul(0x2710_0000_0001) >> 32
4 => {
// Process digits as an u32 block.
let mut src = [0; 4];
src[..N].copy_from_slice(&ascii_digits);
let val = u32::from_le_bytes(src);
Some(u64::from(fast_atoi::process_4(val, N)))
}
_ => {
// Process digits as an u64 block.
let mut src = [0; 8];
src[..N].copy_from_slice(&ascii_digits);
let val = u64::from_le_bytes(src);
Some(fast_atoi::process_8(val, N))
}
let mut src = [0; 8];
src[..N].copy_from_slice(&digits);
let val = u64::from_le_bytes(src);
Some(process_8(val, N))
}
}

Expand Down

0 comments on commit 51974b7

Please sign in to comment.