diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8a3db72a5..3954417eb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -88,6 +88,9 @@ jobs: env: CARGO_TARGET_DIR: target/ - run: | + cargo test --all-targets + cargo test --features integer128 --all-targets + cargo test --features indexmap --all-targets cargo test --all-features --all-targets grcov . -s . --binary-path ./target/debug/ \ -t cobertura -o cobertura.xml --branch \ diff --git a/CHANGELOG.md b/CHANGELOG.md index 03d1a4aac..7f01ca8e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix deserialising deserialising `A('/')` into a `ron::Value` ([#465](https://github.com/ron-rs/ron/pull/465)) - Update the arbitrary fuzzer to check arbitrary serde data types, values, and `ron::ser::PrettyConfig`s ([#465](https://github.com/ron-rs/ron/pull/465)) - Add a benchmark for PRs that runs over the latest fuzzer corpus ([#465](https://github.com/ron-rs/ron/pull/465)) +- Fix issue [#445](https://github.com/ron-rs/ron/issues/445) and allow parsing `+unsigned` as an unsigned int ([#479](https://github.com/ron-rs/ron/pull/479)) +- Breaking: Expand the `value::Number` enum to explicitly encode all possible number types ([#479](https://github.com/ron-rs/ron/pull/479)) ## [0.8.1] - 2023-08-17 diff --git a/docs/grammar.md b/docs/grammar.md index 3ef2b7f7e..5a7685a7e 100644 --- a/docs/grammar.md +++ b/docs/grammar.md @@ -40,7 +40,7 @@ For the extension names see the [`extensions.md`][exts] document. ## Value ```ebnf -value = unsigned | signed | float | string | char | bool | option | list | map | tuple | struct | enum_variant; +value = integer | float | string | char | bool | option | list | map | tuple | struct | enum_variant; ``` ## Numbers @@ -50,7 +50,7 @@ digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; hex_digit = "A" | "a" | "B" | "b" | "C" | "c" | "D" | "d" | "E" | "e" | "F" | "f"; unsigned = (["0", ("b" | "o")], digit, { digit | '_' } | "0x", (digit | hex_digit), { digit | hex_digit | '_' }); -signed = ["+" | "-"], unsigned; +integer = ["+" | "-"], unsigned; float = ["+" | "-"], ("inf" | "NaN" | float_num); float_num = (float_int | float_std | float_frac), [float_exp]; float_int = digit, { digit }; diff --git a/src/de/mod.rs b/src/de/mod.rs index ad15cf20c..1575dc77f 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -17,7 +17,7 @@ use crate::{ error::{Result, SpannedResult}, extensions::Extensions, options::Options, - parse::{AnyNum, Bytes, NewtypeMode, ParsedStr, StructType, TupleMode, BASE64_ENGINE}, + parse::{Bytes, NewtypeMode, ParsedStr, StructType, TupleMode, BASE64_ENGINE}, }; mod id; @@ -294,11 +294,9 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { } else if self.bytes.consume("()") { return visitor.visit_unit(); } else if self.bytes.consume_ident("inf") { - return visitor.visit_f64(std::f64::INFINITY); - } else if self.bytes.consume_ident("-inf") { - return visitor.visit_f64(std::f64::NEG_INFINITY); + return visitor.visit_f32(std::f32::INFINITY); } else if self.bytes.consume_ident("NaN") { - return visitor.visit_f64(std::f64::NAN); + return visitor.visit_f32(std::f32::NAN); } // `identifier` does not change state if it fails @@ -314,27 +312,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { b'(' => self.handle_any_struct(visitor, None), b'[' => self.deserialize_seq(visitor), b'{' => self.deserialize_map(visitor), - b'0'..=b'9' | b'+' | b'-' => { - 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), - #[cfg(feature = "integer128")] - AnyNum::I128(x) => visitor.visit_i128(x), - #[cfg(feature = "integer128")] - AnyNum::U128(x) => visitor.visit_u128(x), - } - } - b'.' => self.deserialize_f64(visitor), + b'0'..=b'9' | b'+' | b'-' | b'.' => self.bytes.any_number()?.visit(visitor), b'"' | b'r' => self.deserialize_string(visitor), b'\'' => self.deserialize_char(visitor), other => Err(Error::UnexpectedByte(other as char)), diff --git a/src/de/tests.rs b/src/de/tests.rs index 2c46eb68f..83e4c5c72 100644 --- a/src/de/tests.rs +++ b/src/de/tests.rs @@ -4,7 +4,8 @@ use serde_derive::Deserialize; use crate::{ de::from_str, error::{Error, Position, SpannedError, SpannedResult}, - parse::{AnyNum, Bytes}, + parse::Bytes, + value::Number, }; #[derive(Debug, PartialEq, Deserialize)] @@ -345,19 +346,86 @@ fn test_numbers() { ); } -fn de_any_number(s: &str) -> AnyNum { +fn check_de_any_number< + T: Copy + PartialEq + std::fmt::Debug + Into + serde::de::DeserializeOwned, +>( + s: &str, + cmp: T, +) { let mut bytes = Bytes::new(s.as_bytes()).unwrap(); + let number = bytes.any_number().unwrap(); - bytes.any_num().unwrap() + assert_eq!(number, Number::new(cmp)); + assert_eq!( + Number::new(super::from_str::(s).unwrap()), + Number::new(cmp) + ); } #[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)); + check_de_any_number("1", 1_u8); + check_de_any_number("+1", 1_u8); + check_de_any_number("-1", -1_i8); + check_de_any_number("-1.0", -1.0_f32); + check_de_any_number("1.", 1.0_f32); + check_de_any_number("-1.", -1.0_f32); + check_de_any_number(".3", 0.3_f64); + check_de_any_number("-.3", -0.3_f64); + check_de_any_number("+.3", 0.3_f64); + check_de_any_number("0.3", 0.3_f64); + check_de_any_number("NaN", f32::NAN); + check_de_any_number("-NaN", -f32::NAN); + check_de_any_number("inf", f32::INFINITY); + check_de_any_number("-inf", f32::NEG_INFINITY); + + macro_rules! test_min { + ($($ty:ty),*) => { + $(check_de_any_number(&format!("{}", <$ty>::MIN), <$ty>::MIN);)* + }; + } + + macro_rules! test_max { + ($($ty:ty),*) => { + $(check_de_any_number(&format!("{}", <$ty>::MAX), <$ty>::MAX);)* + }; + } + + test_min! { i8, i16, i32, i64, f64 } + test_max! { u8, u16, u32, u64, f64 } + #[cfg(feature = "integer128")] + test_min! { i128 } + #[cfg(feature = "integer128")] + test_max! { u128 } +} + +#[test] +fn test_value_special_floats() { + use crate::{from_str, value::Number, Value}; + + assert_eq!( + from_str("NaN"), + Ok(Value::Number(Number::F32(f32::NAN.into()))) + ); + assert_eq!( + from_str("+NaN"), + Ok(Value::Number(Number::F32(f32::NAN.into()))) + ); + assert_eq!( + from_str("-NaN"), + Ok(Value::Number(Number::F32((-f32::NAN).into()))) + ); + + assert_eq!( + from_str("inf"), + Ok(Value::Number(Number::F32(f32::INFINITY.into()))) + ); + assert_eq!( + from_str("+inf"), + Ok(Value::Number(Number::F32(f32::INFINITY.into()))) + ); + assert_eq!( + from_str("-inf"), + Ok(Value::Number(Number::F32(f32::NEG_INFINITY.into()))) + ); } diff --git a/src/de/value.rs b/src/de/value.rs index 5883a37ad..8be45b691 100644 --- a/src/de/value.rs +++ b/src/de/value.rs @@ -49,6 +49,27 @@ impl<'de> Visitor<'de> for ValueVisitor { Ok(Value::Bool(v)) } + fn visit_i8(self, v: i8) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + + fn visit_i16(self, v: i16) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + + fn visit_i32(self, v: i32) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + fn visit_i64(self, v: i64) -> Result where E: Error, @@ -61,7 +82,28 @@ impl<'de> Visitor<'de> for ValueVisitor { where E: Error, { - self.visit_f64(v as f64) + Ok(Value::Number(Number::new(v))) + } + + fn visit_u8(self, v: u8) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + + fn visit_u16(self, v: u16) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + + fn visit_u32(self, v: u32) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) } fn visit_u64(self, v: u64) -> Result @@ -76,7 +118,14 @@ impl<'de> Visitor<'de> for ValueVisitor { where E: Error, { - self.visit_f64(v as f64) + Ok(Value::Number(Number::new(v))) + } + + fn visit_f32(self, v: f32) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) } fn visit_f64(self, v: f64) -> Result @@ -210,9 +259,9 @@ mod tests { assert_eq!( eval("(3, 4.0, 5.0)"), Value::Seq(vec![ - Value::Number(Number::new(3)), - Value::Number(Number::new(4.0)), - Value::Number(Number::new(5.0)), + Value::Number(Number::U8(3)), + Value::Number(Number::F32(4.0.into())), + Value::Number(Number::F32(5.0.into())), ],), ); } @@ -223,9 +272,9 @@ mod tests { eval("(true, 3, 4, 5.0)"), Value::Seq(vec![ Value::Bool(true), - Value::Number(Number::new(3)), - Value::Number(Number::new(4)), - Value::Number(Number::new(5.0)), + Value::Number(Number::U8(3)), + Value::Number(Number::U8(4)), + Value::Number(Number::F32(5.0.into())), ]), ); } @@ -248,9 +297,9 @@ mod tests { assert_eq!( eval("(inf, -inf, NaN)"), Value::Seq(vec![ - Value::Number(Number::new(std::f64::INFINITY)), - Value::Number(Number::new(std::f64::NEG_INFINITY)), - Value::Number(Number::new(std::f64::NAN)), + Value::Number(Number::new(std::f32::INFINITY)), + Value::Number(Number::new(std::f32::NEG_INFINITY)), + Value::Number(Number::new(std::f32::NAN)), ]), ); } @@ -279,11 +328,11 @@ mod tests { vec![ ( Value::String("width".to_owned()), - Value::Number(Number::new(20)), + Value::Number(Number::U8(20)), ), ( Value::String("height".to_owned()), - Value::Number(Number::new(5)), + Value::Number(Number::U8(5)), ), ( Value::String("name".to_owned()), @@ -297,11 +346,11 @@ mod tests { vec![ ( Value::String("width".to_owned()), - Value::Number(Number::new(10.0)), + Value::Number(Number::F32(10.0.into())), ), ( Value::String("height".to_owned()), - Value::Number(Number::new(10.0)), + Value::Number(Number::F32(10.0.into())), ), ( Value::String("name".to_owned()), @@ -313,15 +362,15 @@ mod tests { vec![ ( Value::String("Enemy1".to_owned()), - Value::Number(Number::new(3)), + Value::Number(Number::U8(3)), ), ( Value::String("Enemy2".to_owned()), - Value::Number(Number::new(5)), + Value::Number(Number::U8(5)), ), ( Value::String("Enemy3".to_owned()), - Value::Number(Number::new(7)), + Value::Number(Number::U8(7)), ), ] .into_iter() diff --git a/src/parse.rs b/src/parse.rs index 20d2b788a..c78b5d5fe 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -10,6 +10,7 @@ use base64::engine::general_purpose::{GeneralPurpose, STANDARD}; use crate::{ error::{Error, Position, Result, SpannedError, SpannedResult}, extensions::Extensions, + value::Number, }; pub const BASE64_ENGINE: GeneralPurpose = STANDARD; @@ -89,24 +90,6 @@ const fn is_whitespace_char(c: u8) -> bool { ENCODINGS[c as usize] & WHITESPACE_CHAR != 0 } -#[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), - #[cfg(feature = "integer128")] - I128(i128), - #[cfg(feature = "integer128")] - U128(u128), -} - #[derive(Clone, Copy, Debug)] pub struct Bytes<'a> { /// Bits set according to the [`Extensions`] enum. @@ -249,112 +232,79 @@ impl<'a> Bytes<'a> { res } - pub fn any_num(&mut self) -> Result { - // We are not doing float comparisons here in the traditional sense. - // Instead, this code checks if a f64 fits inside an f32. - #[allow(clippy::float_cmp)] - fn any_float(f: f64) -> Result { - if f == f64::from(f as f32) { - Ok(AnyNum::F32(f as f32)) + pub fn any_number(&mut self) -> Result { + fn any_float(f: f64) -> Result { + // total_cmp ensures that NANs are treated properly + if f.total_cmp(&f64::from(f as f32)).is_eq() { + Ok(Number::F32((f as f32).into())) } else { - Ok(AnyNum::F64(f)) + Ok(Number::F64(f.into())) } } 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 self.next_bytes_is_float() { + return any_float(self.float::()?); + } - if is_float { - let f = self.float::()?; + let is_negative = match self.peek_or_eof()? { + b'+' => { + let _ = self.advance_single(); + false + } + b'-' => { + let _ = self.advance_single(); + true + } + _ => false, + }; - any_float(f) - } else { - let max_u8 = LargeUInt::from(std::u8::MAX); - let max_u16 = LargeUInt::from(std::u16::MAX); - let max_u32 = LargeUInt::from(std::u32::MAX); - #[cfg_attr(not(feature = "integer128"), allow(clippy::useless_conversion))] - let max_u64 = LargeUInt::from(std::u64::MAX); - - let min_i8 = LargeSInt::from(std::i8::MIN); - let max_i8 = LargeSInt::from(std::i8::MAX); - let min_i16 = LargeSInt::from(std::i16::MIN); - let max_i16 = LargeSInt::from(std::i16::MAX); - let min_i32 = LargeSInt::from(std::i32::MIN); - let max_i32 = LargeSInt::from(std::i32::MAX); - #[cfg_attr(not(feature = "integer128"), allow(clippy::useless_conversion))] - let min_i64 = LargeSInt::from(std::i64::MIN); - #[cfg_attr(not(feature = "integer128"), allow(clippy::useless_conversion))] - let max_i64 = LargeSInt::from(std::i64::MAX); - - if is_signed { - match self.signed_integer::() { - 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 if x >= min_i64 && x <= max_i64 { - #[cfg_attr( - not(feature = "integer128"), - allow(clippy::unnecessary_cast) - )] - Ok(AnyNum::I64(x as i64)) - } else { - #[cfg(feature = "integer128")] - { - Ok(AnyNum::I128(x)) - } - #[cfg(not(feature = "integer128"))] - { - Ok(AnyNum::I64(x)) - } - } + if is_negative { + if let Ok(x) = self.any_integer::(-1) { + return if let Ok(x) = i8::try_from(x) { + Ok(Number::I8(x)) + } else if let Ok(x) = i16::try_from(x) { + Ok(Number::I16(x)) + } else if let Ok(x) = i32::try_from(x) { + Ok(Number::I32(x)) + } else { + #[cfg(not(feature = "integer128"))] + { + Ok(Number::I64(x)) } - Err(_) => { - self.bytes = bytes_backup; - - any_float(self.float::()?) + #[cfg(feature = "integer128")] + if let Ok(x) = i64::try_from(x) { + Ok(Number::I64(x)) + } else { + Ok(Number::I128(x)) } - } + }; + } + } else if let Ok(x) = self.any_integer::(1) { + return if let Ok(x) = u8::try_from(x) { + Ok(Number::U8(x)) + } else if let Ok(x) = u16::try_from(x) { + Ok(Number::U16(x)) + } else if let Ok(x) = u32::try_from(x) { + Ok(Number::U32(x)) } else { - match self.unsigned_integer::() { - 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 if x <= max_u64 { - #[cfg_attr( - not(feature = "integer128"), - allow(clippy::unnecessary_cast) - )] - Ok(AnyNum::U64(x as u64)) - } else { - #[cfg(feature = "integer128")] - { - Ok(AnyNum::U128(x)) - } - #[cfg(not(feature = "integer128"))] - { - Ok(AnyNum::U64(x)) - } - } - } - Err(_) => { - self.bytes = bytes_backup; - - any_float(self.float::()?) - } + #[cfg(not(feature = "integer128"))] + { + Ok(Number::U64(x)) } - } + #[cfg(feature = "integer128")] + if let Ok(x) = u64::try_from(x) { + Ok(Number::U64(x)) + } else { + Ok(Number::U128(x)) + } + }; } + + // Fall-back to parse an out-of-range integer as a float + self.bytes = bytes_backup; + any_float(self.float::()?) } pub fn bool(&mut self) -> Result { @@ -900,6 +850,8 @@ impl<'a> Bytes<'a> { } pub fn unsigned_integer(&mut self) -> Result { + // Allow an optional leading `+` before the unsigned integer + self.consume("+"); self.any_integer(1) } diff --git a/src/ser/tests.rs b/src/ser/tests.rs index fce7f0071..e326feb4f 100644 --- a/src/ser/tests.rs +++ b/src/ser/tests.rs @@ -1,5 +1,7 @@ use serde_derive::Serialize; +use crate::Number; + use super::to_string; #[derive(Serialize)] @@ -145,3 +147,44 @@ fn rename() { assert_eq!(to_string(&Foo::D2).unwrap(), "r#2d"); assert_eq!(to_string(&Foo::TriangleList).unwrap(), "r#triangle-list"); } + +#[test] +fn test_any_number_precision() { + check_ser_any_number(1_u8); + check_ser_any_number(-1_i8); + check_ser_any_number(1_f32); + check_ser_any_number(-1_f32); + check_ser_any_number(0.3_f64); + check_ser_any_number(f32::NAN); + check_ser_any_number(-f32::NAN); + check_ser_any_number(f32::INFINITY); + check_ser_any_number(f32::NEG_INFINITY); + + macro_rules! test_min_max { + ($ty:ty) => { + check_ser_any_number(<$ty>::MIN); + check_ser_any_number(<$ty>::MAX); + }; + ($($ty:ty),*) => { + $(test_min_max! { $ty })* + }; + } + + test_min_max! { i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 } + #[cfg(feature = "integer128")] + test_min_max! { i128, u128 } +} + +fn check_ser_any_number + std::fmt::Display + serde::Serialize>(n: T) { + let mut fmt = format!("{}", n); + if !fmt.contains('.') + && std::any::type_name::().contains('f') + && !fmt.contains("NaN") + && !fmt.contains("inf") + { + fmt.push_str(".0"); + } + + assert_eq!(super::to_string(&n.into()).unwrap(), fmt); + assert_eq!(super::to_string(&n).unwrap(), fmt); +} diff --git a/src/ser/value.rs b/src/ser/value.rs index 1a4775898..e39ae9417 100644 --- a/src/ser/value.rs +++ b/src/ser/value.rs @@ -1,6 +1,6 @@ use serde::ser::{Serialize, Serializer}; -use crate::value::{Number, Value}; +use crate::value::Value; impl Serialize for Value { fn serialize(&self, serializer: S) -> Result @@ -11,8 +11,7 @@ impl Serialize for Value { Value::Bool(b) => serializer.serialize_bool(b), Value::Char(c) => serializer.serialize_char(c), Value::Map(ref m) => Serialize::serialize(m, serializer), - Value::Number(Number::Float(ref f)) => serializer.serialize_f64(f.get()), - Value::Number(Number::Integer(i)) => serializer.serialize_i64(i), + Value::Number(ref number) => Serialize::serialize(number, serializer), Value::Option(Some(ref o)) => serializer.serialize_some(o.as_ref()), Value::Option(None) => serializer.serialize_none(), Value::String(ref s) => serializer.serialize_str(s), diff --git a/src/value/map.rs b/src/value/map.rs new file mode 100644 index 000000000..0a3e78d26 --- /dev/null +++ b/src/value/map.rs @@ -0,0 +1,159 @@ +use std::{ + cmp::{Eq, Ordering}, + hash::{Hash, Hasher}, + iter::FromIterator, + ops::{Index, IndexMut}, +}; + +use serde_derive::{Deserialize, Serialize}; + +use super::Value; + +/// A [`Value`] to [`Value`] map. +/// +/// This structure either uses a [BTreeMap](std::collections::BTreeMap) or the +/// [IndexMap](indexmap::IndexMap) internally. +/// The latter can be used by enabling the `indexmap` feature. This can be used +/// to preserve the order of the parsed map. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(transparent)] +pub struct Map(MapInner); + +#[cfg(not(feature = "indexmap"))] +type MapInner = std::collections::BTreeMap; +#[cfg(feature = "indexmap")] +type MapInner = indexmap::IndexMap; + +impl Map { + /// Creates a new, empty [`Map`]. + pub fn new() -> Map { + Default::default() + } + + /// Returns the number of elements in the map. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns `true` if `self.len() == 0`, `false` otherwise. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Immutably looks up an element by its `key`. + pub fn get(&self, key: &Value) -> Option<&Value> { + self.0.get(key) + } + + /// Mutably looks up an element by its `key`. + pub fn get_mut(&mut self, key: &Value) -> Option<&mut Value> { + self.0.get_mut(key) + } + + /// Inserts a new element, returning the previous element with this `key` if + /// there was any. + pub fn insert(&mut self, key: Value, value: Value) -> Option { + self.0.insert(key, value) + } + + /// Removes an element by its `key`. + pub fn remove(&mut self, key: &Value) -> Option { + self.0.remove(key) + } + + /// Iterate all key-value pairs. + pub fn iter(&self) -> impl Iterator + DoubleEndedIterator { + self.0.iter() + } + + /// Iterate all key-value pairs mutably. + pub fn iter_mut(&mut self) -> impl Iterator + DoubleEndedIterator { + self.0.iter_mut() + } + + /// Iterate all keys. + pub fn keys(&self) -> impl Iterator + DoubleEndedIterator { + self.0.keys() + } + + /// Iterate all values. + pub fn values(&self) -> impl Iterator + DoubleEndedIterator { + self.0.values() + } + + /// Iterate all values mutably. + pub fn values_mut(&mut self) -> impl Iterator + DoubleEndedIterator { + self.0.values_mut() + } + + /// Retains only the elements specified by the `keep` predicate. + /// + /// In other words, remove all pairs `(k, v)` for which `keep(&k, &mut v)` + /// returns `false`. + /// + /// The elements are visited in iteration order. + pub fn retain(&mut self, keep: F) + where + F: FnMut(&Value, &mut Value) -> bool, + { + self.0.retain(keep); + } +} + +impl Index<&Value> for Map { + type Output = Value; + + fn index(&self, index: &Value) -> &Self::Output { + &self.0[index] + } +} + +impl IndexMut<&Value> for Map { + fn index_mut(&mut self, index: &Value) -> &mut Self::Output { + self.0.get_mut(index).expect("no entry found for key") + } +} + +impl IntoIterator for Map { + type Item = (Value, Value); + + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl FromIterator<(Value, Value)> for Map { + fn from_iter>(iter: T) -> Self { + Map(MapInner::from_iter(iter)) + } +} + +/// Note: equality is only given if both values and order of values match +impl PartialEq for Map { + fn eq(&self, other: &Map) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +/// Note: equality is only given if both values and order of values match +impl Eq for Map {} + +impl PartialOrd for Map { + fn partial_cmp(&self, other: &Map) -> Option { + self.iter().partial_cmp(other.iter()) + } +} + +impl Ord for Map { + fn cmp(&self, other: &Map) -> Ordering { + self.iter().cmp(other.iter()) + } +} + +impl Hash for Map { + fn hash(&self, state: &mut H) { + self.iter().for_each(|x| x.hash(state)); + } +} diff --git a/src/value/mod.rs b/src/value/mod.rs index 6c13ddfbf..c0b5d2e3f 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,346 +1,22 @@ //! Value module. -use std::{ - cmp::{Eq, Ordering}, - hash::{Hash, Hasher}, - iter::FromIterator, - ops::{Index, IndexMut}, -}; +use std::{cmp::Eq, hash::Hash}; use serde::{ de::{DeserializeOwned, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor}, forward_to_deserialize_any, }; -use serde_derive::{Deserialize, Serialize}; use crate::{de::Error, error::Result}; +mod map; +mod number; pub(crate) mod raw; +pub use map::Map; +pub use number::{Number, F32, F64}; pub use raw::RawValue; -/// A [`Value`] to [`Value`] map. -/// -/// This structure either uses a [BTreeMap](std::collections::BTreeMap) or the -/// [IndexMap](indexmap::IndexMap) internally. -/// The latter can be used by enabling the `indexmap` feature. This can be used -/// to preserve the order of the parsed map. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(transparent)] -pub struct Map(MapInner); - -impl Map { - /// Creates a new, empty [`Map`]. - pub fn new() -> Map { - Default::default() - } - - /// Returns the number of elements in the map. - pub fn len(&self) -> usize { - self.0.len() - } - - /// Returns `true` if `self.len() == 0`, `false` otherwise. - pub fn is_empty(&self) -> bool { - self.0.len() == 0 - } - - /// Inserts a new element, returning the previous element with this `key` if - /// there was any. - pub fn insert(&mut self, key: Value, value: Value) -> Option { - self.0.insert(key, value) - } - - /// Removes an element by its `key`. - pub fn remove(&mut self, key: &Value) -> Option { - self.0.remove(key) - } - - /// Iterate all key-value pairs. - pub fn iter(&self) -> impl Iterator + DoubleEndedIterator { - self.0.iter() - } - - /// Iterate all key-value pairs mutably. - pub fn iter_mut(&mut self) -> impl Iterator + DoubleEndedIterator { - self.0.iter_mut() - } - - /// Iterate all keys. - pub fn keys(&self) -> impl Iterator + DoubleEndedIterator { - self.0.keys() - } - - /// Iterate all values. - pub fn values(&self) -> impl Iterator + DoubleEndedIterator { - self.0.values() - } - - /// Iterate all values mutably. - pub fn values_mut(&mut self) -> impl Iterator + DoubleEndedIterator { - self.0.values_mut() - } - - /// Retains only the elements specified by the `keep` predicate. - /// - /// In other words, remove all pairs `(k, v)` for which `keep(&k, &mut v)` - /// returns `false`. - /// - /// The elements are visited in iteration order. - pub fn retain(&mut self, keep: F) - where - F: FnMut(&Value, &mut Value) -> bool, - { - self.0.retain(keep); - } -} - -impl FromIterator<(Value, Value)> for Map { - fn from_iter>(iter: T) -> Self { - Map(MapInner::from_iter(iter)) - } -} - -impl IntoIterator for Map { - type Item = (Value, Value); - - type IntoIter = ::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -/// Note: equality is only given if both values and order of values match -impl Eq for Map {} - -impl Hash for Map { - fn hash(&self, state: &mut H) { - self.iter().for_each(|x| x.hash(state)); - } -} - -impl Index<&Value> for Map { - type Output = Value; - - fn index(&self, index: &Value) -> &Self::Output { - &self.0[index] - } -} - -impl IndexMut<&Value> for Map { - fn index_mut(&mut self, index: &Value) -> &mut Self::Output { - self.0.get_mut(index).expect("no entry found for key") - } -} - -impl Ord for Map { - fn cmp(&self, other: &Map) -> Ordering { - self.iter().cmp(other.iter()) - } -} - -/// Note: equality is only given if both values and order of values match -impl PartialEq for Map { - fn eq(&self, other: &Map) -> bool { - self.iter().zip(other.iter()).all(|(a, b)| a == b) - } -} - -impl PartialOrd for Map { - fn partial_cmp(&self, other: &Map) -> Option { - Some(self.cmp(other)) - } -} - -#[cfg(not(feature = "indexmap"))] -type MapInner = std::collections::BTreeMap; -#[cfg(feature = "indexmap")] -type MapInner = indexmap::IndexMap; - -/// A wrapper for a number, which can be either [`f64`] or [`i64`]. -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)] -pub enum Number { - Integer(i64), - Float(Float), -} - -/// A wrapper for [`f64`], which guarantees that the inner value -/// is finite and thus implements [`Eq`], [`Hash`] and [`Ord`]. -#[derive(Copy, Clone, Debug)] -pub struct Float(f64); - -impl Float { - /// Construct a new [`Float`]. - pub fn new(v: f64) -> Self { - Float(v) - } - - /// Returns the wrapped float. - pub fn get(self) -> f64 { - self.0 - } -} - -impl Number { - /// Construct a new number. - pub fn new(v: impl Into) -> Self { - v.into() - } - - /// Returns the [`f64`] representation of the [`Number`] regardless of - /// whether the number is stored as a float or integer. - /// - /// # Example - /// - /// ``` - /// # use ron::value::Number; - /// let i = Number::new(5); - /// let f = Number::new(2.0); - /// assert_eq!(i.into_f64(), 5.0); - /// assert_eq!(f.into_f64(), 2.0); - /// ``` - pub fn into_f64(self) -> f64 { - self.map_to(|i| i as f64, |f| f) - } - - /// If the [`Number`] is a float, return it. Otherwise return [`None`]. - /// - /// # Example - /// - /// ``` - /// # use ron::value::Number; - /// let i = Number::new(5); - /// let f = Number::new(2.0); - /// assert_eq!(i.as_f64(), None); - /// assert_eq!(f.as_f64(), Some(2.0)); - /// ``` - pub fn as_f64(self) -> Option { - self.map_to(|_| None, Some) - } - - /// If the [`Number`] is an integer, return it. Otherwise return [`None`]. - /// - /// # Example - /// - /// ``` - /// # use ron::value::Number; - /// let i = Number::new(5); - /// let f = Number::new(2.0); - /// assert_eq!(i.as_i64(), Some(5)); - /// assert_eq!(f.as_i64(), None); - /// ``` - pub fn as_i64(self) -> Option { - self.map_to(Some, |_| None) - } - - /// Map this number to a single type using the appropriate closure. - /// - /// # Example - /// - /// ``` - /// # use ron::value::Number; - /// let i = Number::new(5); - /// let f = Number::new(2.0); - /// assert!(i.map_to(|i| i > 3, |f| f > 3.0)); - /// assert!(!f.map_to(|i| i > 3, |f| f > 3.0)); - /// ``` - pub fn map_to( - self, - integer_fn: impl FnOnce(i64) -> T, - float_fn: impl FnOnce(f64) -> T, - ) -> T { - match self { - Number::Integer(i) => integer_fn(i), - Number::Float(Float(f)) => float_fn(f), - } - } -} - -impl From for Number { - fn from(f: f64) -> Number { - Number::Float(Float(f)) - } -} - -impl From for Number { - fn from(i: i64) -> Number { - Number::Integer(i) - } -} - -impl From for Number { - fn from(i: i32) -> Number { - Number::Integer(i64::from(i)) - } -} - -/// The following [`Number`] conversion checks if the integer fits losslessly -/// into an [`i64`], before constructing a [`Number::Integer`] variant. -/// If not, the conversion defaults to [`Number::Float`]. - -impl From for Number { - fn from(i: u64) -> Number { - if i <= std::i64::MAX as u64 { - Number::Integer(i as i64) - } else { - Number::new(i as f64) - } - } -} - -/// Partial equality comparison -/// In order to be able to use [`Number`] as a mapping key, floating values -/// use [`f64::total_ord`] for a total order comparison. -/// -/// See the [`Ord`] implementation. -impl PartialEq for Float { - fn eq(&self, other: &Self) -> bool { - self.cmp(other) == Ordering::Equal - } -} - -/// Equality comparison -/// In order to be able to use [`Float`] as a mapping key, floating values -/// use [`f64::total_ord`] for a total order comparison. -/// -/// See the [`Ord`] implementation. -impl Eq for Float {} - -impl Hash for Float { - fn hash(&self, state: &mut H) { - state.write_u64(self.0.to_bits()); - } -} - -/// Partial ordering comparison -/// In order to be able to use [`Number`] as a mapping key, floating values -/// use [`f64::total_ord`] for a total order comparison. -/// -/// See the [`Ord`] implementation. -impl PartialOrd for Float { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// Ordering comparison -/// In order to be able to use [`Float`] as a mapping key, floating values -/// use [`f64::total_ord`] for a total order comparison. -/// See the [`PartialEq`] implementation. -/// -/// ``` -/// use ron::value::Number; -/// assert!(Number::new(std::f64::NAN) > Number::new(std::f64::INFINITY)); -/// assert!(Number::new(-std::f64::NAN) < Number::new(std::f64::NEG_INFINITY)); -/// assert_eq!(Number::new(std::f64::NAN), Number::new(std::f64::NAN)); -/// ``` -impl Ord for Float { - fn cmp(&self, other: &Self) -> Ordering { - self.0.total_cmp(&other.0) - } -} - #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Value { Bool(bool), @@ -369,11 +45,16 @@ impl<'de> Deserializer<'de> for Value { type Error = Error; forward_to_deserialize_any! { - bool f32 f64 char str string bytes + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } + #[cfg(feature = "integer128")] + forward_to_deserialize_any! { + i128 u128 + } + fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, @@ -401,8 +82,7 @@ impl<'de> Deserializer<'de> for Value { }) } } - Value::Number(Number::Float(ref f)) => visitor.visit_f64(f.get()), - Value::Number(Number::Integer(i)) => visitor.visit_i64(i), + Value::Number(number) => number.visit(visitor), Value::Option(Some(o)) => visitor.visit_some(*o), Value::Option(None) => visitor.visit_none(), Value::String(s) => visitor.visit_string(s), @@ -410,7 +90,7 @@ impl<'de> Deserializer<'de> for Value { let old_len = seq.len(); seq.reverse(); - let value = visitor.visit_seq(Seq { seq: &mut seq })?; + let value = visitor.visit_seq(SeqAccessor { seq: &mut seq })?; if seq.is_empty() { Ok(value) @@ -424,67 +104,27 @@ impl<'de> Deserializer<'de> for Value { Value::Unit => visitor.visit_unit(), } } +} - fn deserialize_i8(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_i64(visitor) - } - - fn deserialize_i16(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_i64(visitor) - } - - fn deserialize_i32(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_i64(visitor) - } - - fn deserialize_i64(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - Value::Number(Number::Integer(i)) => visitor.visit_i64(i), - v => Err(Error::Message(format!("Expected a number, got {:?}", v))), - } - } - - fn deserialize_u8(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_u64(visitor) - } +struct SeqAccessor<'a> { + seq: &'a mut Vec, +} - fn deserialize_u16(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_u64(visitor) - } +impl<'a, 'de> SeqAccess<'de> for SeqAccessor<'a> { + type Error = Error; - fn deserialize_u32(self, visitor: V) -> Result + fn next_element_seed(&mut self, seed: T) -> Result> where - V: Visitor<'de>, + T: DeserializeSeed<'de>, { - self.deserialize_u64(visitor) + // The `Vec` is reversed, so we can pop to get the originally first element + self.seq + .pop() + .map_or(Ok(None), |v| seed.deserialize(v).map(Some)) } - fn deserialize_u64(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - Value::Number(Number::Integer(i)) => visitor.visit_u64(i as u64), - v => Err(Error::Message(format!("Expected a number, got {:?}", v))), - } + fn size_hint(&self) -> Option { + Some(self.seq.len()) } } @@ -525,28 +165,6 @@ impl<'a, 'de> MapAccess<'de> for MapAccessor<'a> { } } -struct Seq<'a> { - seq: &'a mut Vec, -} - -impl<'a, 'de> SeqAccess<'de> for Seq<'a> { - type Error = Error; - - fn next_element_seed(&mut self, seed: T) -> Result> - where - T: DeserializeSeed<'de>, - { - // The `Vec` is reversed, so we can pop to get the originally first element - self.seq - .pop() - .map_or(Ok(None), |v| seed.deserialize(v).map(Some)) - } - - fn size_hint(&self) -> Option { - Some(self.seq.len()) - } -} - #[cfg(test)] mod tests { use std::{collections::BTreeMap, fmt::Debug}; diff --git a/src/value/number.rs b/src/value/number.rs new file mode 100644 index 000000000..370b3abe6 --- /dev/null +++ b/src/value/number.rs @@ -0,0 +1,274 @@ +use std::{ + cmp::{Eq, Ordering}, + hash::{Hash, Hasher}, +}; + +use serde::{de::Visitor, Serialize, Serializer}; + +/// A wrapper for any numeric primitive type in Rust +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)] +pub enum Number { + I8(i8), + I16(i16), + I32(i32), + I64(i64), + #[cfg(feature = "integer128")] + I128(i128), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + #[cfg(feature = "integer128")] + U128(u128), + F32(F32), + F64(F64), +} + +impl Serialize for Number { + fn serialize(&self, serializer: S) -> Result { + match self { + Self::I8(v) => serializer.serialize_i8(*v), + Self::I16(v) => serializer.serialize_i16(*v), + Self::I32(v) => serializer.serialize_i32(*v), + Self::I64(v) => serializer.serialize_i64(*v), + #[cfg(feature = "integer128")] + Self::I128(v) => serializer.serialize_i128(*v), + Self::U8(v) => serializer.serialize_u8(*v), + Self::U16(v) => serializer.serialize_u16(*v), + Self::U32(v) => serializer.serialize_u32(*v), + Self::U64(v) => serializer.serialize_u64(*v), + #[cfg(feature = "integer128")] + Self::U128(v) => serializer.serialize_u128(*v), + Self::F32(v) => serializer.serialize_f32(v.get()), + Self::F64(v) => serializer.serialize_f64(v.get()), + } + } +} + +impl Number { + pub fn visit<'de, V: Visitor<'de>, E: serde::de::Error>( + &self, + visitor: V, + ) -> Result { + match self { + Self::I8(v) => visitor.visit_i8(*v), + Self::I16(v) => visitor.visit_i16(*v), + Self::I32(v) => visitor.visit_i32(*v), + Self::I64(v) => visitor.visit_i64(*v), + #[cfg(feature = "integer128")] + Self::I128(v) => visitor.visit_i128(*v), + Self::U8(v) => visitor.visit_u8(*v), + Self::U16(v) => visitor.visit_u16(*v), + Self::U32(v) => visitor.visit_u32(*v), + Self::U64(v) => visitor.visit_u64(*v), + #[cfg(feature = "integer128")] + Self::U128(v) => visitor.visit_u128(*v), + Self::F32(v) => visitor.visit_f32(v.get()), + Self::F64(v) => visitor.visit_f64(v.get()), + } + } +} + +macro_rules! float_ty { + ($ty:ident($float:ty)) => { + #[doc = concat!( + "A wrapper for [`", stringify!($float), "`], which implements [`Eq`], ", + "[`Hash`] and [`Ord`] using [`", stringify!($float), "::total_cmp`] ", + "for a total order comparison", + )] + #[derive(Copy, Clone, Debug)] + pub struct $ty(pub $float); + + impl $ty { + #[doc = concat!("Construct a new [`", stringify!($ty), "`].")] + pub fn new(v: $float) -> Self { + Self(v) + } + + #[doc = concat!("Returns the wrapped [`", stringify!($float), "`].")] + pub fn get(self) -> $float { + self.0 + } + } + + impl From<$float> for $ty { + fn from(v: $float) -> Self { + Self(v) + } + } + + /// Partial equality comparison + /// + #[doc = concat!( + "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", + "floating values use [`", stringify!($float), "::total_cmp`] for a total ", + "order comparison.", + )] + /// + /// See the [`Ord`] implementation. + impl PartialEq for $ty { + fn eq(&self, other: &Self) -> bool { + self.cmp(other).is_eq() + } + } + + /// Equality comparison + /// + #[doc = concat!( + "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", + "floating values use [`", stringify!($float), "::total_cmp`] for a total ", + "order comparison.", + )] + /// + /// See the [`Ord`] implementation. + impl Eq for $ty {} + + impl Hash for $ty { + fn hash(&self, state: &mut H) { + self.0.to_bits().hash(state); + } + } + + /// Partial ordering comparison + /// + #[doc = concat!( + "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", + "floating values use [`", stringify!($float), "::total_cmp`] for a total ", + "order comparison.", + )] + /// + /// See the [`Ord`] implementation. + impl PartialOrd for $ty { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + /// Ordering comparison + /// + #[doc = concat!( + "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", + "floating values use [`", stringify!($float), "::total_cmp`] for a total ", + "order comparison.", + )] + /// + /// ``` + #[doc = concat!("use ron::value::", stringify!($ty), ";")] + #[doc = concat!( + "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) > ", + stringify!($ty), "::new(", stringify!($float), "::INFINITY));", + )] + #[doc = concat!( + "assert!(", stringify!($ty), "::new(-", stringify!($float), "::NAN) < ", + stringify!($ty), "::new(", stringify!($float), "::NEG_INFINITY));", + )] + #[doc = concat!( + "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) == ", + stringify!($ty), "::new(", stringify!($float), "::NAN));", + )] + /// ``` + impl Ord for $ty { + fn cmp(&self, other: &Self) -> Ordering { + self.0.total_cmp(&other.0) + } + } + }; +} + +float_ty! { F32(f32) } +float_ty! { F64(f64) } + +impl Number { + /// Construct a new number. + pub fn new(v: impl Into) -> Self { + v.into() + } + + /// Returns the [`f64`] representation of the [`Number`] regardless of + /// whether the number is stored as a float or integer. + /// + /// # Example + /// + /// ``` + /// # use ron::value::Number; + /// let i = Number::new(5); + /// let f = Number::new(2.0); + /// assert_eq!(i.into_f64(), 5.0); + /// assert_eq!(f.into_f64(), 2.0); + /// ``` + pub fn into_f64(self) -> f64 { + match self { + Number::I8(v) => f64::from(v), + Number::I16(v) => f64::from(v), + Number::I32(v) => f64::from(v), + Number::I64(v) => v as f64, + #[cfg(feature = "integer128")] + Number::I128(v) => v as f64, + Number::U8(v) => f64::from(v), + Number::U16(v) => f64::from(v), + Number::U32(v) => f64::from(v), + Number::U64(v) => v as f64, + #[cfg(feature = "integer128")] + Number::U128(v) => v as f64, + Number::F32(v) => f64::from(v.get()), + Number::F64(v) => v.get(), + } + } +} + +macro_rules! number_from_impl { + (Number::$variant:ident($wrap:ident($ty:ty))) => { + impl From<$ty> for Number { + fn from(v: $ty) -> Number { + Number::$variant($wrap(v)) + } + } + }; + (Number::$variant:ident($ty:ty)) => { + impl From<$ty> for Number { + fn from(v: $ty) -> Number { + Number::$variant(v) + } + } + }; +} + +number_from_impl! { Number::I8(i8) } +number_from_impl! { Number::I16(i16) } +number_from_impl! { Number::I32(i32) } +number_from_impl! { Number::I64(i64) } +#[cfg(feature = "integer128")] +number_from_impl! { Number::I128(i128) } +number_from_impl! { Number::U8(u8) } +number_from_impl! { Number::U16(u16) } +number_from_impl! { Number::U32(u32) } +number_from_impl! { Number::U64(u64) } +#[cfg(feature = "integer128")] +number_from_impl! { Number::U128(u128) } +number_from_impl! { Number::F32(F32(f32)) } +number_from_impl! { Number::F64(F64(f64)) } + +#[cfg(test)] +mod tests { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + use super::*; + + fn hash(v: &T) -> u64 { + let mut state = DefaultHasher::new(); + v.hash(&mut state); + state.finish() + } + + #[test] + fn test_nan() { + assert_eq!(F32(f32::NAN), F32(f32::NAN)); + assert_eq!(F32(-f32::NAN), F32(-f32::NAN)); + assert_ne!(F32(f32::NAN), F32(-f32::NAN)); + + assert_eq!(hash(&F32(f32::NAN)), hash(&F32(f32::NAN))); + assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN))); + assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN))); + } +} diff --git a/tests/115_minimal_flattening.rs b/tests/115_minimal_flattening.rs index 49758d649..d52642a55 100644 --- a/tests/115_minimal_flattening.rs +++ b/tests/115_minimal_flattening.rs @@ -174,7 +174,7 @@ fn test_flatten_rest() { let mut map = HashMap::new(); map.insert( String::from("third"), - ron::Value::Number(ron::value::Number::from(3)), + ron::Value::Number(ron::value::Number::U8(3)), ); map }, @@ -202,7 +202,7 @@ fn test_flatten_rest() { let mut map = HashMap::new(); map.insert( String::from("third"), - ron::Value::Number(ron::value::Number::from(3)), + ron::Value::Number(ron::value::Number::U8(3)), ); map }, diff --git a/tests/238_array.rs b/tests/238_array.rs index 8429f804c..ec339b271 100644 --- a/tests/238_array.rs +++ b/tests/238_array.rs @@ -17,9 +17,9 @@ fn test_array() { assert_eq!( value, Value::Seq(vec![ - Value::Number(Number::from(1)), - Value::Number(Number::from(2)), - Value::Number(Number::from(3)), + Value::Number(Number::U8(1)), + Value::Number(Number::U8(2)), + Value::Number(Number::U8(3)), ]) ); @@ -43,9 +43,9 @@ fn test_array() { assert_eq!( value, Value::Seq(vec![ - Value::Number(Number::from(1)), - Value::Number(Number::from(2)), - Value::Number(Number::from(3)), + Value::Number(Number::U8(1)), + Value::Number(Number::U8(2)), + Value::Number(Number::U8(3)), ]) ); } diff --git a/tests/337_value_float_roundtrip.rs b/tests/337_value_float_roundtrip.rs index 66873eaea..a5add4647 100644 --- a/tests/337_value_float_roundtrip.rs +++ b/tests/337_value_float_roundtrip.rs @@ -2,7 +2,7 @@ fn roundtrip_value_float_with_decimals() { let v: ron::Value = ron::from_str("1.0").unwrap(); - assert_eq!(v, ron::Value::Number(1.0_f64.into())); + assert_eq!(v, ron::Value::Number(1.0_f32.into())); let ser = ron::ser::to_string(&v).unwrap(); @@ -15,7 +15,7 @@ fn roundtrip_value_float_with_decimals() { #[allow(clippy::float_cmp)] fn roundtrip_value_float_into() { let v: ron::Value = ron::from_str("1.0").unwrap(); - assert_eq!(v, ron::Value::Number(1.0_f64.into())); + assert_eq!(v, ron::Value::Number(1.0_f32.into())); let ser = ron::ser::to_string(&v).unwrap(); diff --git a/tests/465_r_name_value.rs b/tests/465_r_name_value.rs index 3623b6395..d6a63bc50 100644 --- a/tests/465_r_name_value.rs +++ b/tests/465_r_name_value.rs @@ -4,14 +4,16 @@ fn value_deserialises_r_name() { assert_eq!(ron::from_str("r()"), Ok(ron::Value::Seq(vec![]))); assert_eq!( ron::from_str("r(42)"), - Ok(ron::Value::Seq(vec![ron::Value::Number(42.into())])) + Ok(ron::Value::Seq(vec![ron::Value::Number( + ron::value::Number::U8(42) + )])) ); assert_eq!( ron::from_str("r(a:42)"), Ok(ron::Value::Map( [( ron::Value::String(String::from("a")), - ron::Value::Number(42.into()) + ron::Value::Number(ron::value::Number::U8(42)) )] .into_iter() .collect() diff --git a/tests/465_unwrap_some_newtype_variant_value.rs b/tests/465_unwrap_some_newtype_variant_value.rs index 826c72420..52c2e09d2 100644 --- a/tests/465_unwrap_some_newtype_variant_value.rs +++ b/tests/465_unwrap_some_newtype_variant_value.rs @@ -12,7 +12,7 @@ fn deserialise_value_with_unwrap_some_newtype_variant() { Ok(ron::Value::Option(Some(Box::new(ron::Value::Map( [( ron::Value::String(String::from("a")), - ron::Value::Number(42.into()) + ron::Value::Number(ron::value::Number::U8(42)) )] .into_iter() .collect() @@ -21,14 +21,14 @@ fn deserialise_value_with_unwrap_some_newtype_variant() { assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42, true)"), Ok(ron::Value::Option(Some(Box::new(ron::Value::Seq(vec![ - ron::Value::Number(42.into()), + ron::Value::Number(ron::value::Number::U8(42)), ron::Value::Bool(true) ]))))), ); assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42,)"), Ok(ron::Value::Option(Some(Box::new(ron::Value::Seq(vec![ - ron::Value::Number(42.into()) + ron::Value::Number(ron::value::Number::U8(42)) ]))))), ); assert_eq!( @@ -38,7 +38,7 @@ fn deserialise_value_with_unwrap_some_newtype_variant() { assert_eq!( ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42)"), Ok(ron::Value::Option(Some(Box::new(ron::Value::Number( - 42.into() + ron::value::Number::U8(42) ))))), ); } diff --git a/tests/large_number.rs b/tests/large_number.rs index c8ae49f0b..8a7bc39d6 100644 --- a/tests/large_number.rs +++ b/tests/large_number.rs @@ -14,15 +14,16 @@ fn test_large_number() { #[test] fn test_large_integer_to_float() { - use ron::value::Float; - let test_var = std::i64::MAX as u64 + 1; - let expected = test_var as f64; // Is exactly representable by f64 - let test_ser = ron::ser::to_string(&test_var).unwrap(); - assert_eq!(test_ser, test_var.to_string()); + use ron::value::Number; + let test_var = std::u64::MAX as u128 + 1; + let test_ser = test_var.to_string(); let test_deser = ron::de::from_str::(&test_ser); + #[cfg(not(feature = "integer128"))] assert_eq!( test_deser.unwrap(), - Value::Number(Number::Float(Float::new(expected))), + Value::Number(Number::F32((test_var as f32).into())), // f64 representation matches f32 ); + #[cfg(feature = "integer128")] + assert_eq!(test_deser.unwrap(), Value::Number(Number::U128(test_var)),); } diff --git a/tests/value.rs b/tests/value.rs index 65838ab08..fdc3b22aa 100644 --- a/tests/value.rs +++ b/tests/value.rs @@ -20,14 +20,14 @@ fn char() { #[test] fn map() { let mut map = Map::new(); - map.insert(Value::Char('a'), Value::Number(Number::new(1))); - map.insert(Value::Char('b'), Value::Number(Number::new(2f64))); + map.insert(Value::Char('a'), Value::Number(Number::U8(1))); + map.insert(Value::Char('b'), Value::Number(Number::new(2f32))); assert_eq!("{ 'a': 1, 'b': 2.0 }".parse(), Ok(Value::Map(map))); } #[test] fn number() { - assert_eq!("42".parse(), Ok(Value::Number(Number::new(42)))); + assert_eq!("42".parse(), Ok(Value::Number(Number::U8(42)))); assert_eq!( "3.141592653589793".parse(), Ok(Value::Number(Number::new(f64::consts::PI))) @@ -67,8 +67,8 @@ fn string() { #[test] fn seq() { let seq = vec![ - Value::Number(Number::new(1)), - Value::Number(Number::new(2f64)), + Value::Number(Number::U8(1)), + Value::Number(Number::new(2f32)), ]; assert_eq!("[1, 2.0]".parse(), Ok(Value::Seq(seq)));