Skip to content

Commit 2f03a86

Browse files
authored
Fix float parsing bug (#458)
Signed-off-by: Heinz N. Gies <heinz@licenser.net>
1 parent 0662a83 commit 2f03a86

7 files changed

Lines changed: 36 additions & 4 deletions

File tree

‎.github/workflows/tests.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ jobs:
125125
targets: wasm32-wasip1
126126

127127
- name: Install wasmtime
128-
run: cargo install wasmtime-cli
128+
run: cargo install wasmtime-cli --version ^38
129129

130130
- name: Run tests
131131
run: |

‎src/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ mod numberparse;
4444
mod safer_unchecked;
4545
mod stringparse;
4646

47+
#[allow(unused_imports)]
4748
use macros::static_cast_u64;
4849
use safer_unchecked::GetSaferUnchecked;
4950
use stage2::StackState;

‎src/numberparse/approx.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::{
44
};
55
use crate::StaticNode;
66
use crate::charutils::is_structural_or_whitespace;
7+
#[allow(unused_imports)]
78
use crate::macros::{static_cast_i64, unlikely};
89
use crate::safer_unchecked::GetSaferUnchecked;
910
use crate::{Deserializer, ErrorType, Result};

‎src/numberparse/correct.rs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use super::{is_made_of_eight_digits_fast, parse_eight_digits_unrolled};
99
use crate::StaticNode;
1010
use crate::charutils::is_structural_or_whitespace;
1111
use crate::error::Error;
12+
#[allow(unused_imports)]
1213
use crate::macros::{static_cast_i64, unlikely};
1314
use crate::safer_unchecked::GetSaferUnchecked;
1415
use crate::{Deserializer, ErrorType, Result};
@@ -165,11 +166,11 @@ impl Deserializer<'_> {
165166
}
166167
if is_float {
167168
if unlikely!(digit_count >= 19) {
168-
let start_digits = get!(buf, start) as usize;
169+
let orig_start = start;
169170
while get!(buf, start) == b'0' || get!(buf, start) == b'.' {
170171
start += 1;
171172
}
172-
digit_count = digit_count.wrapping_sub(start.wrapping_sub(start_digits));
173+
digit_count = digit_count.wrapping_sub(start.wrapping_sub(orig_start));
173174
if digit_count >= 19 {
174175
return f64_from_parts_slow(
175176
unsafe { buf.get_kinda_unchecked(start_idx..idx) },

‎src/stage2.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(dead_code)]
22
use crate::charutils::is_not_structural_or_whitespace;
3+
#[allow(unused_imports)]
34
use crate::macros::unlikely;
45
use crate::safer_unchecked::GetSaferUnchecked;
56
use crate::value::tape::Node;

‎src/tests/serde.rs‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,35 @@ fn float3() {
605605
assert_eq!(v_simd, v_serde);
606606
}
607607

608+
// https://github.com/simd-lite/simd-json/issues/455
609+
// Float parsing was corrupted when a long decimal number extended >= 13 bytes
610+
// past a 64-byte SIMD chunk boundary.
611+
#[test]
612+
fn float_across_chunk_boundary() {
613+
// "3077999.0000000000000000" (24 bytes) starts at byte offset 56;
614+
// 8 bytes are in chunk 0, the remaining 16 spill into chunk 1.
615+
let json: &[u8] =
616+
br#"{"asks":[{"price":3077990.0,"amount":0.111111},{"price":3077999.0000000000000000,"amount":0.5}]}"#;
617+
assert_eq!(&json[56..80], b"3077999.0000000000000000");
618+
let mut d = json.to_vec();
619+
let v_serde: serde_json::Value = serde_json::from_slice(json).expect("serde_json");
620+
let v_simd: serde_json::Value = from_slice(&mut d).expect("simd_json");
621+
assert_eq!(v_simd, v_serde);
622+
623+
// Sweep the number start offset within the 64-byte chunk to cover every
624+
// possible boundary split of the 24-byte number.
625+
for pad in 0..64 {
626+
let json = format!(
627+
r#"{{"pad":"{}","price":3077999.0000000000000000,"amount":0.5}}"#,
628+
"x".repeat(pad)
629+
);
630+
let mut d = json.clone().into_bytes();
631+
let v_serde: serde_json::Value = serde_json::from_str(&json).expect("serde_json");
632+
let v_simd: serde_json::Value = from_slice(&mut d).expect("simd_json");
633+
assert_eq!(v_simd, v_serde, "mismatch at pad {pad}");
634+
}
635+
}
636+
608637
#[test]
609638
fn map0() {
610639
let mut d = String::from(r#"{"snot": "badger"}"#);

‎src/value/tape.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ impl<'input> Node<'input> {
195195
mod test {
196196
#![allow(clippy::cognitive_complexity)]
197197
use super::StaticNode as Value;
198-
use super::*;
199198
use crate::prelude::*;
200199

201200
#[test]

0 commit comments

Comments
 (0)