Skip to content

Commit 84c76f9

Browse files
committed
0.8.0 fix u64 overflowing #36
1 parent 03ed2c6 commit 84c76f9

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

src/parser.rs

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ macro_rules! sequence {
5050

5151
macro_rules! read_num {
5252
($tok:ident, $num:ident, $then:expr) => {
53-
while let Some(ch) = $tok.next_byte() {
53+
while let Some(ch) = $tok.checked_next_byte() {
5454
match ch {
5555
b'0' ... b'9' => {
5656
let $num = ch - b'0';
@@ -213,22 +213,42 @@ impl<'a> Tokenizer<'a> {
213213

214214
fn read_number(&mut self, first: u8, is_negative: bool) -> JsonResult<f64> {
215215
let mut num = (first - b'0') as u64;
216+
let mut digits = 0u8;
216217

217-
read_num!(self, digit, num = num * 10 + digit as u64);
218+
// Cap on how many iterations we do while reading to u64
219+
// in order to avoid an overflow.
220+
while digits < 18 {
221+
digits += 1;
218222

219-
match self.peek_byte() {
220-
Some(b'.') | Some(b'e') | Some(b'E') => {},
221-
_ => {
222-
return if is_negative {
223-
Ok(-(num as f64))
224-
} else {
225-
Ok(num as f64)
226-
};
223+
if let Some(ch) = self.next_byte() {
224+
match ch {
225+
b'0' ... b'9' => {
226+
num = num * 10 + (ch - b'0') as u64;
227+
},
228+
b'.' | b'e' | b'E' => {
229+
self.left_over = Some(ch);
230+
break;
231+
}
232+
ch => {
233+
self.left_over = Some(ch);
234+
return Ok(
235+
if is_negative { -(num as f64) } else { num as f64 }
236+
);
237+
}
238+
}
239+
} else {
240+
return Ok(
241+
if is_negative { -(num as f64) } else { num as f64 }
242+
);
227243
}
228244
}
229245

230246
let mut num = num as f64;
231247

248+
// Attempt to continue reading digits that would overflow
249+
// u64 into freshly converted f64
250+
read_num!(self, digit, num = num * 10.0 + digit as f64);
251+
232252
if let Some(b'.') = self.peek_byte() {
233253
self.left_over = None;
234254
let mut precision = -1;
@@ -251,22 +271,14 @@ impl<'a> Tokenizer<'a> {
251271
},
252272
};
253273

254-
while let Some(ch) = self.checked_next_byte() {
255-
match ch {
256-
b'0' ... b'9' => e = e * 10 + (ch - b'0') as i32,
257-
ch => {
258-
self.left_over = Some(ch);
259-
break;
260-
}
261-
}
262-
}
274+
read_num!(self, digit, e = e * 10 + digit as i32);
263275

264276
num *= 10f64.powi(e * sign);
265277
},
266278
byte => self.left_over = byte
267279
}
268280

269-
Ok(if is_negative { num * -1.0 } else { num })
281+
Ok(if is_negative { -num } else { num })
270282
}
271283

272284
fn next(&mut self) -> JsonResult<Token> {

tests/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,11 @@ fn parse_number_with_negative_e() {
322322
assert_eq!(parse("5E-2").unwrap(), 0.05);
323323
}
324324

325+
#[test]
326+
fn parse_large_number() {
327+
assert_eq!(parse("18446744073709551616").unwrap(), 18446744073709552000f64);
328+
}
329+
325330
#[test]
326331
fn parse_array() {
327332
assert_eq!(parse(r#"[10, "foo", true, null]"#).unwrap(), array![

0 commit comments

Comments
 (0)