Fix parsing min, max, and epsilon f64 values

This commit is contained in:
Erick Tryzelaar 2015-08-04 18:00:24 -07:00
parent ed6777e59f
commit fd84aec485
3 changed files with 122 additions and 60 deletions

View File

@ -110,10 +110,10 @@ impl<Iter> Deserializer<Iter>
}
b'-' => {
try!(self.bump());
self.parse_number(false, visitor)
self.parse_integer(false, visitor)
}
b'0' ... b'9' => {
self.parse_number(true, visitor)
self.parse_integer(true, visitor)
}
b'"' => {
try!(self.parse_string());
@ -151,11 +151,114 @@ impl<Iter> Deserializer<Iter>
Ok(())
}
fn parse_number<V>(&mut self, pos: bool, mut visitor: V) -> Result<V::Value>
fn parse_integer<V>(&mut self, pos: bool, visitor: V) -> Result<V::Value>
where V: de::Visitor,
{
let res = try!(self.parse_integer());
match self.ch_or_null() {
b'0' => {
try!(self.bump());
// There can be only one leading '0'.
match self.ch_or_null() {
b'0' ... b'9' => {
Err(self.error(ErrorCode::InvalidNumber))
}
_ => {
self.parse_number(pos, 0, visitor)
}
}
},
c @ b'1' ... b'9' => {
try!(self.bump());
let mut res: u64 = (c as u64) - ('0' as u64);
loop {
match self.ch_or_null() {
c @ b'0' ... b'9' => {
try!(self.bump());
let digit = (c as u64) - ('0' as u64);
// We need to be careful with overflow. If we can, try to keep the
// number as a `u64` until we grow too large. At that point, switch to
// parsing the value as a `f64`.
match res.checked_mul(10) {
Some(res_) => {
res = res_;
match res.checked_add(digit) {
Some(res_) => { res = res_; }
None => {
return self.parse_float(
pos,
(res as f64) + (digit as f64),
visitor);
}
}
}
None => {
return self.parse_float(
pos,
(res as f64) * 10.0 + (digit as f64),
visitor);
}
}
}
_ => {
return self.parse_number(pos, res, visitor);
}
}
}
}
_ => {
Err(self.error(ErrorCode::InvalidNumber))
}
}
}
fn parse_float<V>(&mut self,
pos: bool,
mut res: f64,
mut visitor: V) -> Result<V::Value>
where V: de::Visitor,
{
loop {
match self.ch_or_null() {
c @ b'0' ... b'9' => {
try!(self.bump());
let digit = (c as u64) - ('0' as u64);
res *= 10.0;
res += digit as f64;
}
_ => {
match self.ch_or_null() {
b'.' => {
return self.parse_decimal(pos, res, visitor);
}
b'e' | b'E' => {
return self.parse_exponent(pos, res, visitor);
}
_ => {
if !pos {
res = -res;
}
return visitor.visit_f64(res);
}
}
}
}
}
}
fn parse_number<V>(&mut self,
pos: bool,
res: u64,
mut visitor: V) -> Result<V::Value>
where V: de::Visitor,
{
match self.ch_or_null() {
b'.' => {
self.parse_decimal(pos, res as f64, visitor)
@ -167,53 +270,19 @@ impl<Iter> Deserializer<Iter>
if pos {
visitor.visit_u64(res)
} else {
let res = (res as i64).wrapping_neg();
let res_i64 = (res as i64).wrapping_neg();
// Make sure we didn't underflow.
if res > 0 {
Err(self.error(ErrorCode::InvalidNumber))
// Convert into a float if we underflow.
if res_i64 > 0 {
visitor.visit_f64(-(res as f64))
} else {
visitor.visit_i64(res)
visitor.visit_i64(res_i64)
}
}
}
}
}
fn parse_integer(&mut self) -> Result<u64> {
let mut accum: u64 = 0;
match self.ch_or_null() {
b'0' => {
try!(self.bump());
// There can be only one leading '0'.
match self.ch_or_null() {
b'0' ... b'9' => {
return Err(self.error(ErrorCode::InvalidNumber));
}
_ => ()
}
},
b'1' ... b'9' => {
while !self.eof() {
match self.ch_or_null() {
c @ b'0' ... b'9' => {
accum = try_or_invalid!(self, accum.checked_mul(10));
accum = try_or_invalid!(self, accum.checked_add((c as u64) - ('0' as u64)));
try!(self.bump());
}
_ => break,
}
}
}
_ => { return Err(self.error(ErrorCode::InvalidNumber)); }
}
Ok(accum)
}
fn parse_decimal<V>(&mut self,
pos: bool,
mut res: f64,

View File

@ -465,12 +465,7 @@ fn fmt_f32_or_null<W>(wr: &mut W, value: f32) -> io::Result<()>
match value.classify() {
FpCategory::Nan | FpCategory::Infinite => wr.write_all(b"null"),
_ => {
let s = format!("{:?}", value);
try!(wr.write_all(s.as_bytes()));
if !s.contains('.') {
try!(wr.write_all(b".0"))
}
Ok(())
write!(wr, "{:?}", value)
}
}
}
@ -481,12 +476,7 @@ fn fmt_f64_or_null<W>(wr: &mut W, value: f64) -> io::Result<()>
match value.classify() {
FpCategory::Nan | FpCategory::Infinite => wr.write_all(b"null"),
_ => {
let s = format!("{:?}", value);
try!(wr.write_all(s.as_bytes()));
if !s.contains('.') {
try!(wr.write_all(b".0"))
}
Ok(())
write!(wr, "{:?}", value)
}
}
}

View File

@ -1,4 +1,5 @@
use std::collections::BTreeMap;
use std::f64;
use std::fmt::Debug;
use std::i64;
use std::marker::PhantomData;
@ -108,12 +109,12 @@ fn test_write_i64() {
#[test]
fn test_write_f64() {
let min_string = f64::MIN.to_string();
let max_string = f64::MAX.to_string();
let epsilon_string = f64::EPSILON.to_string();
let min_string = format!("{:?}", f64::MIN);
let max_string = format!("{:?}", f64::MAX);
let epsilon_string = format!("{:?}", f64::EPSILON);
let tests = &[
(3.0, "3.0"),
(3.0, "3"),
(3.1, "3.1"),
(-1.5, "-1.5"),
(0.5, "0.5"),
@ -727,7 +728,6 @@ fn test_parse_number_errors() {
("1e", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 2)),
("1e+", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 3)),
("1a", Error::SyntaxError(ErrorCode::TrailingCharacters, 1, 2)),
("777777777777777777777777777", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 20)),
("1e777777777777777777777777777", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 23)),
]);
}
@ -773,6 +773,9 @@ fn test_parse_f64() {
("0.00e00", 0.0),
("0.00e+00", 0.0),
("0.00e-00", 0.0),
(&format!("{:?}", (i64::MIN as f64) - 1.0), (i64::MIN as f64) - 1.0),
(&format!("{:?}", (u64::MAX as f64) + 1.0), (u64::MAX as f64) + 1.0),
(&format!("{:?}", f64::EPSILON), f64::EPSILON),
]);
}