Skip to content

Commit

Permalink
Merge #157
Browse files Browse the repository at this point in the history
157: Be smarter about integer deserialization r=torkleyy a=torkleyy

Now, `deserialize_any` will try to find the best representation possible.

This is not a very pretty implementation, but it should work well.

Fixes #100 
Fixes #155 

Co-authored-by: Thomas Schaller <torkleyy@gmail.com>
  • Loading branch information
bors[bot] and torkleyy committed Apr 14, 2019
2 parents eb0ff58 + 44fcaed commit 66fc37f
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 7 deletions.
19 changes: 14 additions & 5 deletions src/de/mod.rs
Original file line number Diff line number Diff line change
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
19 changes: 17 additions & 2 deletions src/de/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,6 @@ y: 2.0 // 2!
}

fn err<T>(kind: ParseError, line: usize, col: usize) -> Result<T> {
use crate::parse::Position;

Err(Error::Parser(kind, Position { line, col }))
}

Expand Down Expand Up @@ -305,3 +303,20 @@ 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));
assert_eq!(de_any_number("1."), AnyNum::F32(1.));
assert_eq!(de_any_number("-1."), AnyNum::F32(-1.));
assert_eq!(de_any_number("0.3"), AnyNum::F64(0.3));
}
87 changes: 87 additions & 0 deletions src/parse.rs
Original file line number Diff line number Diff line change
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
11 changes: 11 additions & 0 deletions tests/large_number.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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();
let test_deser = ron::de::from_str::<Value>(&test_ser);

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

0 comments on commit 66fc37f

Please sign in to comment.