Skip to content

Commit

Permalink
Merge pull request #114 from serde-rs/large
Browse files Browse the repository at this point in the history
Error on large numbers instead of parsing infinity
  • Loading branch information
dtolnay committed Jul 12, 2016
2 parents 02be472 + 09340cb commit b61d90d
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 22 deletions.
69 changes: 49 additions & 20 deletions json/src/de.rs
Expand Up @@ -404,12 +404,7 @@ impl<R: Read> DeserializerImpl<R> {
let digit = (c - b'0') as i32;

if overflow!(exp * 10 + digit, i32::MAX) {
// Really big exponent, just ignore the rest.
while let b'0' ... b'9' = try!(self.peek_or_null()) {
self.eat_char();
}
exp = i32::MAX;
break;
return self.parse_exponent_overflow(pos, significand, pos_exp, visitor);
}

exp = exp * 10 + digit;
Expand All @@ -424,29 +419,63 @@ impl<R: Read> DeserializerImpl<R> {
self.visit_f64_from_parts(pos, significand, final_exp, visitor)
}

// This cold code should not be inlined into the middle of the hot
// exponent-parsing loop above.
#[cold]
#[inline(never)]
// https://github.com/Manishearth/rust-clippy/issues/1086
#[cfg_attr(feature = "clippy", allow(if_same_then_else))]
fn parse_exponent_overflow<V>(&mut self,
pos: bool,
significand: u64,
pos_exp: bool,
mut visitor: V) -> Result<V::Value>
where V: de::Visitor,
{
// Error instead of +/- infinity.
if significand != 0 && pos_exp {
return Err(self.error(ErrorCode::NumberOutOfRange));
}

while let b'0' ... b'9' = try!(self.peek_or_null()) {
self.eat_char();
}
visitor.visit_f64(if pos { 0.0 } else { -0.0 })
}

fn visit_f64_from_parts<V>(&mut self,
pos: bool,
significand: u64,
exponent: i32,
mut exponent: i32,
mut visitor: V) -> Result<V::Value>
where V: de::Visitor,
{
let f = match POW10.get(exponent.abs() as usize) {
Some(pow) => {
if exponent >= 0 {
significand as f64 * pow
} else {
significand as f64 / pow
let mut f = significand as f64;
loop {
match POW10.get(exponent.abs() as usize) {
Some(&pow) => {
if exponent >= 0 {
f *= pow;
if f.is_infinite() {
return Err(self.error(ErrorCode::NumberOutOfRange));
}
} else {
f /= pow;
}
break;
}
}
None => {
if exponent >= 0 && significand != 0 {
f64::INFINITY
} else {
0.0
None => {
if f == 0.0 {
break;
}
if exponent >= 0 {
return Err(self.error(ErrorCode::NumberOutOfRange));
}
f /= 1e308;
exponent += 308;
}
}
};
}
visitor.visit_f64(if pos { f } else { -f })
}

Expand Down
4 changes: 4 additions & 0 deletions json/src/error.rs
Expand Up @@ -68,6 +68,9 @@ pub enum ErrorCode {
/// Invalid number.
InvalidNumber,

/// Number is bigger than the maximum value of its type.
NumberOutOfRange,

/// Invalid unicode code point.
InvalidUnicodeCodePoint,

Expand Down Expand Up @@ -105,6 +108,7 @@ impl fmt::Display for ErrorCode {
ErrorCode::ExpectedSomeValue => "expected value".fmt(f),
ErrorCode::InvalidEscape => "invalid escape".fmt(f),
ErrorCode::InvalidNumber => "invalid number".fmt(f),
ErrorCode::NumberOutOfRange => "number out of range".fmt(f),
ErrorCode::InvalidUnicodeCodePoint => "invalid unicode code point".fmt(f),
ErrorCode::KeyMustBeAString => "key must be a string".fmt(f),
ErrorCode::LoneLeadingSurrogateInHexEscape => "lone leading surrogate in hex escape".fmt(f),
Expand Down
51 changes: 49 additions & 2 deletions json_tests/tests/test_json.rs
Expand Up @@ -788,6 +788,29 @@ fn test_parse_number_errors() {
("1e", Error::Syntax(ErrorCode::InvalidNumber, 1, 2)),
("1e+", Error::Syntax(ErrorCode::InvalidNumber, 1, 3)),
("1a", Error::Syntax(ErrorCode::TrailingCharacters, 1, 2)),
("100e777777777777777777777777777", Error::Syntax(ErrorCode::NumberOutOfRange, 1, 14)),
("-100e777777777777777777777777777", Error::Syntax(ErrorCode::NumberOutOfRange, 1, 15)),
("1000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000", // 1e309
Error::Syntax(ErrorCode::NumberOutOfRange, 1, 310)),
("1000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
.0e9", // 1e309
Error::Syntax(ErrorCode::NumberOutOfRange, 1, 305)),
("1000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
e9", // 1e309
Error::Syntax(ErrorCode::NumberOutOfRange, 1, 303)),
]);
}

Expand Down Expand Up @@ -819,6 +842,7 @@ fn test_parse_negative_zero() {
"-0e2",
"-0.0e2",
"-1e-400",
"-1e-4000000000000000000000000000000000000000000000000",
] {
assert_eq!(0, from_str::<u32>(negative_zero).unwrap());
assert!(from_str::<f64>(negative_zero).unwrap().is_sign_negative(),
Expand Down Expand Up @@ -850,11 +874,34 @@ fn test_parse_f64() {
(&format!("{:?}", (u64::MAX as f64) + 1.0), (u64::MAX as f64) + 1.0),
(&format!("{:?}", f64::EPSILON), f64::EPSILON),
("0.0000000000000000000000000000000000000000000000000123e50", 1.23),
("100e777777777777777777777777777", f64::INFINITY),
("-100e777777777777777777777777777", f64::NEG_INFINITY),
("100e-777777777777777777777777777", 0.0),
("1010101010101010101010101010101010101010", 10101010101010101010e20),
("0.1010101010101010101010101010101010101010", 0.1010101010101010101),
("0e1000000000000000000000000000000000000000000000", 0.0),
("1000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
00000000", 1e308),
("1000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
.0e8", 1e308),
("1000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
e8", 1e308),
("1000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000000000000000000000000000000000000000000000\
000000000000000000e-10", 1e308),
]);
}

Expand Down

0 comments on commit b61d90d

Please sign in to comment.