Skip to content

Commit feb364a

Browse files
committed
- Fix integer overflow on large exponents.
- Require at least one digit after a period when parsing number fractions.
1 parent f158831 commit feb364a

File tree

4 files changed

+59
-15
lines changed

4 files changed

+59
-15
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "json"
3-
version = "0.10.2"
3+
version = "0.10.3"
44
authors = ["Maciej Hirsz <maciej.hirsz@gmail.com>"]
55
description = "JSON implementation in Rust"
66
repository = "https://github.com/maciejhirsz/json-rust"

src/number.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ impl ops::Neg for Number {
488488
// }
489489

490490
#[inline]
491-
fn decimal_power(e: u16) -> u64 {
491+
fn decimal_power(mut e: u16) -> u64 {
492492
static CACHED: [u64; 20] = [
493493
1,
494494
10,
@@ -515,6 +515,12 @@ fn decimal_power(e: u16) -> u64 {
515515
if e < 20 {
516516
CACHED[e as usize]
517517
} else {
518-
10u64.pow(e as u32)
518+
let mut pow = 1u64;
519+
while e >= 20 {
520+
pow = pow.saturating_mul(CACHED[(e % 20) as usize]);
521+
e /= 20;
522+
}
523+
524+
pow
519525
}
520526
}

src/parser.rs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,7 @@ macro_rules! expect_number {
237237
match ch {
238238
b'0' ... b'9' => {
239239
$parser.bump();
240-
// Avoid multiplication with bitshifts and addition
241-
num = (num << 1) + (num << 3) + (ch - b'0') as u64;
240+
num = num * 10 + (ch - b'0') as u64;
242241
},
243242
_ => {
244243
let mut e = 0;
@@ -288,9 +287,31 @@ macro_rules! expect_fraction {
288287
($parser:ident, $num:ident, $e:ident) => ({
289288
let result: Number;
290289

290+
let ch = expect_byte!($parser);
291+
292+
match ch {
293+
b'0' ... b'9' => {
294+
if $num < MAX_PRECISION {
295+
$num = $num * 10 + (ch - b'0') as u64;
296+
$e -= 1;
297+
} else {
298+
match $num.checked_mul(10).and_then(|num| {
299+
num.checked_add((ch - b'0') as u64)
300+
}) {
301+
Some(result) => {
302+
$num = result;
303+
$e -= 1;
304+
},
305+
None => {}
306+
}
307+
}
308+
},
309+
_ => return $parser.unexpected_character(ch)
310+
}
311+
291312
loop {
292313
if $parser.is_eof() {
293-
result = Number::from_parts(true, $num, $e as i16);
314+
result = Number::from_parts(true, $num, $e);
294315
break;
295316
}
296317
let ch = $parser.read_byte();
@@ -299,7 +320,7 @@ macro_rules! expect_fraction {
299320
b'0' ... b'9' => {
300321
$parser.bump();
301322
if $num < MAX_PRECISION {
302-
$num = ($num << 3) + ($num << 1) + (ch - b'0') as u64;
323+
$num = $num * 10 + (ch - b'0') as u64;
303324
$e -= 1;
304325
} else {
305326
match $num.checked_mul(10).and_then(|num| {
@@ -319,7 +340,7 @@ macro_rules! expect_fraction {
319340
break;
320341
}
321342
_ => {
322-
result = Number::from_parts(true, $num, $e as i16);
343+
result = Number::from_parts(true, $num, $e);
323344
break;
324345
}
325346
}
@@ -614,10 +635,10 @@ impl<'a> Parser<'a> {
614635
// the exponent. Note that no digits are actually read here, as we already
615636
// exceeded the precision range of f64 anyway.
616637
fn read_big_number(&mut self, mut num: u64) -> Result<Number> {
617-
let mut e = 0i32;
638+
let mut e = 0i16;
618639
loop {
619640
if self.is_eof() {
620-
return Ok(Number::from_parts(true, num, e as i16));
641+
return Ok(Number::from_parts(true, num, e));
621642
}
622643
let ch = self.read_byte();
623644
match ch {
@@ -642,12 +663,12 @@ impl<'a> Parser<'a> {
642663
}
643664
}
644665

645-
Ok(Number::from_parts(true, num, e as i16))
666+
Ok(Number::from_parts(true, num, e))
646667
}
647668

648669
// Called in the rare case that a number with `e` notation has been
649670
// encountered. This is pretty straight forward, I guess.
650-
fn expect_exponent(&mut self, num: u64, big_e: i32) -> Result<Number> {
671+
fn expect_exponent(&mut self, num: u64, big_e: i16) -> Result<Number> {
651672
let mut ch = expect_byte!(self);
652673
let sign = match ch {
653674
b'-' => {
@@ -662,7 +683,7 @@ impl<'a> Parser<'a> {
662683
};
663684

664685
let mut e = match ch {
665-
b'0' ... b'9' => (ch - b'0') as i32,
686+
b'0' ... b'9' => (ch - b'0') as i16,
666687
_ => return self.unexpected_character(ch),
667688
};
668689

@@ -674,13 +695,13 @@ impl<'a> Parser<'a> {
674695
match ch {
675696
b'0' ... b'9' => {
676697
self.bump();
677-
e = (e << 3) + (e << 1) + (ch - b'0') as i32;
698+
e = e.saturating_mul(10).saturating_add((ch - b'0') as i16);
678699
},
679700
_ => break
680701
}
681702
}
682703

683-
Ok(Number::from_parts(true, num, (big_e + (e * sign)) as i16))
704+
Ok(Number::from_parts(true, num, (big_e.saturating_add(e * sign))))
684705
}
685706

686707
// Given how compilcated reading numbers and strings is, reading objects

tests/parse.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ fn parse_number() {
2424
assert_eq!(parse("3.141592653589793").unwrap(), 3.141592653589793);
2525
}
2626

27+
#[test]
28+
fn uncode_identifier() {
29+
assert!(parse("[C3A9] <=> [é]").is_err());
30+
}
31+
32+
#[test]
33+
fn parse_period_requires_digit() {
34+
assert!(parse("[1.]").is_err());
35+
}
36+
2737
#[test]
2838
fn parse_small_number() {
2939
assert_eq!(parse("0.05").unwrap(), 0.05);
@@ -40,6 +50,13 @@ fn parse_very_long_float() {
4050
assert_eq!(parsed, Number::from_parts(true, 2225073858507201136, -326));
4151
}
4252

53+
#[test]
54+
fn parse_very_long_exponent() {
55+
let parsed = parse("1e999999999999999999999999999999999999999999999999999999999999").unwrap();
56+
57+
assert_eq!(parsed, Number::from_parts(true, 1, 32767));
58+
}
59+
4360
#[test]
4461
fn parse_integer() {
4562
assert_eq!(parse("42").unwrap(), 42);

0 commit comments

Comments
 (0)