Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Be smarter about integer deserialization #157

Merged
merged 2 commits into from Apr 14, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 14 additions & 5 deletions src/de/mod.rs
Expand Up @@ -6,7 +6,7 @@ use serde::de::{self, DeserializeSeed, Deserializer as SerdeError, Visitor};
use std::{borrow::Cow, io, str};

use self::id::IdDeserializer;
use crate::parse::{Bytes, Extensions, ParsedStr};
use crate::parse::{AnyNum, Bytes, Extensions, ParsedStr};

mod error;
mod id;
Expand Down Expand Up @@ -148,10 +148,19 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
b'[' => self.deserialize_seq(visitor),
b'{' => self.deserialize_map(visitor),
b'0'..=b'9' | b'+' | b'-' => {
if self.bytes.next_bytes_is_float() {
self.deserialize_f64(visitor)
} else {
self.deserialize_i64(visitor)
let any_num: AnyNum = self.bytes.any_num()?;

match any_num {
AnyNum::F32(x) => visitor.visit_f32(x),
AnyNum::F64(x) => visitor.visit_f64(x),
AnyNum::I8(x) => visitor.visit_i8(x),
AnyNum::U8(x) => visitor.visit_u8(x),
AnyNum::I16(x) => visitor.visit_i16(x),
AnyNum::U16(x) => visitor.visit_u16(x),
AnyNum::I32(x) => visitor.visit_i32(x),
AnyNum::U32(x) => visitor.visit_u32(x),
AnyNum::I64(x) => visitor.visit_i64(x),
AnyNum::U64(x) => visitor.visit_u64(x),
}
}
b'.' => self.deserialize_f64(visitor),
Expand Down
15 changes: 15 additions & 0 deletions src/de/tests.rs
Expand Up @@ -305,3 +305,18 @@ fn test_numbers() {
from_str("[1_234, 12_345, 1_2_3_4_5_6, 1_234_567, 5_55_55_5]"),
);
}

fn de_any_number(s: &str) -> AnyNum {
let mut bytes = Bytes::new(s.as_bytes()).unwrap();

bytes.any_num().unwrap()
}

#[test]
fn test_any_number_precision() {
assert_eq!(de_any_number("1"), AnyNum::U8(1));
assert_eq!(de_any_number("+1"), AnyNum::I8(1));
assert_eq!(de_any_number("-1"), AnyNum::I8(-1));
assert_eq!(de_any_number("-1.0"), AnyNum::F32(-1.0));
torkleyy marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(de_any_number("0.3"), AnyNum::F64(0.3));
}
87 changes: 87 additions & 0 deletions src/parse.rs
Expand Up @@ -15,6 +15,20 @@ const IDENT_FIRST: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy
const IDENT_CHAR: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789";
const WHITE_SPACE: &[u8] = b"\n\t\r ";

#[derive(Clone, Debug, PartialEq)]
pub enum AnyNum {
F32(f32),
F64(f64),
I8(i8),
U8(u8),
I16(i16),
U16(u16),
I32(i32),
U32(u32),
I64(i64),
U64(u64),
}

#[derive(Clone, Copy, Debug)]
pub struct Bytes<'a> {
/// Bits set according to `Extension` enum.
Expand Down Expand Up @@ -70,6 +84,79 @@ impl<'a> Bytes<'a> {
Ok(())
}

pub fn any_num(&mut self) -> Result<AnyNum> {
fn any_float(f: f64) -> Result<AnyNum> {
if f == f as f32 as f64 {
Ok(AnyNum::F32(f as f32))
} else {
Ok(AnyNum::F64(f))
}
}

let bytes_backup = self.bytes;

let first_byte = self.peek_or_eof()?;
let is_signed = first_byte == b'-' || first_byte == b'+';
let is_float = self.next_bytes_is_float();

if is_float {
let f = self.float::<f64>()?;

any_float(f)
} else {
let max_u8 = std::u8::MAX as u64;
let max_u16 = std::u16::MAX as u64;
let max_u32 = std::u32::MAX as u64;

let min_i8 = std::i8::MIN as i64;
let max_i8 = std::i8::MAX as i64;
let min_i16 = std::i16::MIN as i64;
let max_i16 = std::i16::MAX as i64;
let min_i32 = std::i32::MIN as i64;
let max_i32 = std::i32::MAX as i64;

if is_signed {
match self.signed_integer::<i64>() {
Ok(x) => {
if x >= min_i8 && x <= max_i8 {
Ok(AnyNum::I8(x as i8))
} else if x >= min_i16 && x <= max_i16 {
Ok(AnyNum::I16(x as i16))
} else if x >= min_i32 && x <= max_i32 {
Ok(AnyNum::I32(x as i32))
} else {
Ok(AnyNum::I64(x))
}
}
Err(_) => {
self.bytes = bytes_backup;

any_float(self.float::<f64>()?)
}
}
} else {
match self.unsigned_integer::<u64>() {
Ok(x) => {
if x <= max_u8 {
Ok(AnyNum::U8(x as u8))
} else if x <= max_u16 {
Ok(AnyNum::U16(x as u16))
} else if x <= max_u32 {
Ok(AnyNum::U32(x as u32))
} else {
Ok(AnyNum::U64(x))
}
}
Err(_) => {
self.bytes = bytes_backup;

any_float(self.float::<f64>()?)
}
}
}
}
}

pub fn bool(&mut self) -> Result<bool> {
if self.consume("true") {
Ok(true)
Expand Down
12 changes: 12 additions & 0 deletions tests/large_number.rs
@@ -0,0 +1,12 @@
use ron::value::Number;

#[test]
fn test_large_number() {
use ron::value::Value;
let test_var = Value::Number(Number::new(10000000000000000000000.0f64));
let test_ser = ron::ser::to_string(&test_var).unwrap();
println!("test_ser: {}", test_ser);
torkleyy marked this conversation as resolved.
Show resolved Hide resolved
let test_deser = ron::de::from_str::<Value>(&test_ser);

assert_eq!(test_deser.unwrap(), Value::Number(Number::new(10000000000000000000000.0)));
}