Skip to content

Commit d25ea20

Browse files
committed
Be smarter about integer deserialization
Now, deserialize_any will try to find the best representation possible
1 parent eb0ff58 commit d25ea20

File tree

4 files changed

+128
-5
lines changed

4 files changed

+128
-5
lines changed

src/de/mod.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use serde::de::{self, DeserializeSeed, Deserializer as SerdeError, Visitor};
66
use std::{borrow::Cow, io, str};
77

88
use self::id::IdDeserializer;
9-
use crate::parse::{Bytes, Extensions, ParsedStr};
9+
use crate::parse::{AnyNum, Bytes, Extensions, ParsedStr};
1010

1111
mod error;
1212
mod id;
@@ -148,10 +148,19 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
148148
b'[' => self.deserialize_seq(visitor),
149149
b'{' => self.deserialize_map(visitor),
150150
b'0'..=b'9' | b'+' | b'-' => {
151-
if self.bytes.next_bytes_is_float() {
152-
self.deserialize_f64(visitor)
153-
} else {
154-
self.deserialize_i64(visitor)
151+
let any_num: AnyNum = self.bytes.any_num()?;
152+
153+
match any_num {
154+
AnyNum::F32(x) => visitor.visit_f32(x),
155+
AnyNum::F64(x) => visitor.visit_f64(x),
156+
AnyNum::I8(x) => visitor.visit_i8(x),
157+
AnyNum::U8(x) => visitor.visit_u8(x),
158+
AnyNum::I16(x) => visitor.visit_i16(x),
159+
AnyNum::U16(x) => visitor.visit_u16(x),
160+
AnyNum::I32(x) => visitor.visit_i32(x),
161+
AnyNum::U32(x) => visitor.visit_u32(x),
162+
AnyNum::I64(x) => visitor.visit_i64(x),
163+
AnyNum::U64(x) => visitor.visit_u64(x),
155164
}
156165
}
157166
b'.' => self.deserialize_f64(visitor),

src/de/tests.rs

+15
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,18 @@ fn test_numbers() {
305305
from_str("[1_234, 12_345, 1_2_3_4_5_6, 1_234_567, 5_55_55_5]"),
306306
);
307307
}
308+
309+
fn de_any_number(s: &str) -> AnyNum {
310+
let mut bytes = Bytes::new(s.as_bytes()).unwrap();
311+
312+
bytes.any_num().unwrap()
313+
}
314+
315+
#[test]
316+
fn test_any_number_precision() {
317+
assert_eq!(de_any_number("1"), AnyNum::U8(1));
318+
assert_eq!(de_any_number("+1"), AnyNum::I8(1));
319+
assert_eq!(de_any_number("-1"), AnyNum::I8(-1));
320+
assert_eq!(de_any_number("-1.0"), AnyNum::F32(-1.0));
321+
assert_eq!(de_any_number("0.3"), AnyNum::F64(0.3));
322+
}

src/parse.rs

+87
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ const IDENT_FIRST: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy
1515
const IDENT_CHAR: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789";
1616
const WHITE_SPACE: &[u8] = b"\n\t\r ";
1717

18+
#[derive(Clone, Debug, PartialEq)]
19+
pub enum AnyNum {
20+
F32(f32),
21+
F64(f64),
22+
I8(i8),
23+
U8(u8),
24+
I16(i16),
25+
U16(u16),
26+
I32(i32),
27+
U32(u32),
28+
I64(i64),
29+
U64(u64),
30+
}
31+
1832
#[derive(Clone, Copy, Debug)]
1933
pub struct Bytes<'a> {
2034
/// Bits set according to `Extension` enum.
@@ -70,6 +84,79 @@ impl<'a> Bytes<'a> {
7084
Ok(())
7185
}
7286

87+
pub fn any_num(&mut self) -> Result<AnyNum> {
88+
fn any_float(f: f64) -> Result<AnyNum> {
89+
if f == f as f32 as f64 {
90+
Ok(AnyNum::F32(f as f32))
91+
} else {
92+
Ok(AnyNum::F64(f))
93+
}
94+
}
95+
96+
let bytes_backup = self.bytes;
97+
98+
let first_byte = self.peek_or_eof()?;
99+
let is_signed = first_byte == b'-' || first_byte == b'+';
100+
let is_float = self.next_bytes_is_float();
101+
102+
if is_float {
103+
let f = self.float::<f64>()?;
104+
105+
any_float(f)
106+
} else {
107+
let max_u8 = std::u8::MAX as u64;
108+
let max_u16 = std::u16::MAX as u64;
109+
let max_u32 = std::u32::MAX as u64;
110+
111+
let min_i8 = std::i8::MIN as i64;
112+
let max_i8 = std::i8::MAX as i64;
113+
let min_i16 = std::i16::MIN as i64;
114+
let max_i16 = std::i16::MAX as i64;
115+
let min_i32 = std::i32::MIN as i64;
116+
let max_i32 = std::i32::MAX as i64;
117+
118+
if is_signed {
119+
match self.signed_integer::<i64>() {
120+
Ok(x) => {
121+
if x >= min_i8 && x <= max_i8 {
122+
Ok(AnyNum::I8(x as i8))
123+
} else if x >= min_i16 && x <= max_i16 {
124+
Ok(AnyNum::I16(x as i16))
125+
} else if x >= min_i32 && x <= max_i32 {
126+
Ok(AnyNum::I32(x as i32))
127+
} else {
128+
Ok(AnyNum::I64(x))
129+
}
130+
}
131+
Err(_) => {
132+
self.bytes = bytes_backup;
133+
134+
any_float(self.float::<f64>()?)
135+
}
136+
}
137+
} else {
138+
match self.unsigned_integer::<u64>() {
139+
Ok(x) => {
140+
if x <= max_u8 {
141+
Ok(AnyNum::U8(x as u8))
142+
} else if x <= max_u16 {
143+
Ok(AnyNum::U16(x as u16))
144+
} else if x <= max_u32 {
145+
Ok(AnyNum::U32(x as u32))
146+
} else {
147+
Ok(AnyNum::U64(x))
148+
}
149+
}
150+
Err(_) => {
151+
self.bytes = bytes_backup;
152+
153+
any_float(self.float::<f64>()?)
154+
}
155+
}
156+
}
157+
}
158+
}
159+
73160
pub fn bool(&mut self) -> Result<bool> {
74161
if self.consume("true") {
75162
Ok(true)

tests/large_number.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use ron::value::Number;
2+
3+
#[test]
4+
fn test_large_number() {
5+
use ron::value::Value;
6+
let test_var = Value::Number(Number::new(10000000000000000000000.0f64));
7+
let test_ser = ron::ser::to_string(&test_var).unwrap();
8+
println!("test_ser: {}", test_ser);
9+
let test_deser = ron::de::from_str::<Value>(&test_ser);
10+
11+
assert_eq!(test_deser.unwrap(), Value::Number(Number::new(10000000000000000000000.0)));
12+
}

0 commit comments

Comments
 (0)