diff --git a/src/de.rs b/src/de.rs index e5ef4dff..de18c6e5 100644 --- a/src/de.rs +++ b/src/de.rs @@ -280,6 +280,33 @@ where Ok(BigEndian::read_u64(&buf)) } + fn parse_bigint(&mut self, signed: bool, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + let desc = match self.peek()? { + Some(desc) => desc, + None => return Err(self.error(ErrorCode::EofWhileParsingValue)), + }; + let ty = desc >> 5; + if ty != 2 { + return self.parse_value(visitor); + } + self.consume(); + let len = desc & 0x1f; + if len > 16 { + return Err(self.error(ErrorCode::LengthOutOfRange)); + } + let mut buf = [0; 16]; + self.read.read_into(&mut buf[usize::from(16 - len)..])?; + let num = BigEndian::read_u128(&buf); + if !signed { + visitor.visit_u128(num) + } else { + visitor.visit_i128(-1 - num as i128) + } + } + fn parse_bytes(&mut self, len: usize, visitor: V) -> Result where V: de::Visitor<'de>, @@ -743,8 +770,7 @@ where } 0xfc..=0xfe => Err(self.error(ErrorCode::UnassignedCode)), 0xff => Err(self.error(ErrorCode::UnexpectedCode)), - - _ => unreachable!(), + _ => unreachable!(), // Remove this once minimum supported rustc version is 1.33.0. } } } @@ -848,13 +874,35 @@ where } } + #[inline] + fn deserialize_i128(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + self.deserialize_u128(visitor) + } + + #[inline] + fn deserialize_u128(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + match self.peek()? { + Some(tag @ 0xc2..=0xc3) => { + self.consume(); + self.parse_bigint(tag == 0xc3 /* signed */, visitor) + } + _ => self.parse_value(visitor), + } + } + #[inline] fn is_human_readable(&self) -> bool { false } serde::forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string unit + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string unit unit_struct seq tuple tuple_struct map struct identifier ignored_any bytes byte_buf } diff --git a/src/ser.rs b/src/ser.rs index d827728c..4be8b5d4 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -177,6 +177,23 @@ where } } + #[inline] + fn write_u128(&mut self, tag: u8, value: u128) -> Result<()> { + let len = 16 - (value.leading_zeros() >> 3); + if len <= 8 { + self.write_u64(tag - 2, value as u64) + } else { + let mut buf = [0u8; 2 + 16]; + BigEndian::write_u128(&mut buf[2..], value); + let hdr_offset = 16 - len as usize; + buf[hdr_offset] = 6 << 5 | tag; + buf[hdr_offset + 1] = 2 << 5 | len as u8; + self.writer + .write_all(&buf[hdr_offset..]) + .map_err(|e| e.into()) + } + } + #[inline] fn serialize_collection<'a>( &'a mut self, @@ -263,15 +280,9 @@ where #[inline] fn serialize_i128(self, value: i128) -> Result<()> { if value < 0 { - if -(value + 1) > i128::from(u64::max_value()) { - return Err(Error::message("The number can't be stored in CBOR")); - } - self.write_u64(1, -(value + 1) as u64) + self.write_u128(3, -(value + 1) as u128) } else { - if value > i128::from(u64::max_value()) { - return Err(Error::message("The number can't be stored in CBOR")); - } - self.write_u64(0, value as u64) + self.write_u128(2, value as u128) } } @@ -297,10 +308,7 @@ where #[inline] fn serialize_u128(self, value: u128) -> Result<()> { - if value > u128::from(u64::max_value()) { - return Err(Error::message("The number can't be stored in CBOR")); - } - self.write_u64(0, value as u64) + self.write_u128(2, value) } #[inline] diff --git a/src/value/de.rs b/src/value/de.rs index 2905f3b7..01847111 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -67,6 +67,14 @@ impl<'de> de::Deserialize<'de> for Value { Ok(Value::Integer(v.into())) } + #[inline] + fn visit_u128(self, v: u128) -> Result + where + E: de::Error, + { + Ok(Value::Unsigned(v)) + } + #[inline] fn visit_i128(self, v: i128) -> Result where diff --git a/src/value/mod.rs b/src/value/mod.rs index 3e83c461..d412e6f6 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -25,13 +25,10 @@ pub enum Value { Null, /// Represents a boolean value. Bool(bool), - /// Integer CBOR numbers. - /// - /// The biggest value that can be represented is 2^64 - 1. - /// While the smallest value is -2^64. - /// Values outside this range can't be serialized - /// and will cause an error. + /// Signed integer CBOR numbers. Integer(i128), + /// Unsigned integer CBOR numbers. + Unsigned(u128), /// Represents a floating point value. Float(f64), /// Represents a byte string. @@ -85,6 +82,9 @@ impl Ord for Value { } match (self, other) { (Integer(a), Integer(b)) => a.abs().cmp(&b.abs()), + (Unsigned(a), Unsigned(b)) => a.cmp(&b), + (Integer(a), Unsigned(b)) => (a.abs() as u128).cmp(&b), + (Unsigned(a), Integer(b)) => a.cmp(&(b.abs() as u128)), (Bytes(a), Bytes(b)) if a.len() != b.len() => a.len().cmp(&b.len()), (Text(a), Text(b)) if a.len() != b.len() => a.len().cmp(&b.len()), (Array(a), Array(b)) if a.len() != b.len() => a.len().cmp(&b.len()), @@ -115,12 +115,12 @@ impl_from!(Value::Integer, i8); impl_from!(Value::Integer, i16); impl_from!(Value::Integer, i32); impl_from!(Value::Integer, i64); -// i128 omitted because not all numbers fit in CBOR serialization -impl_from!(Value::Integer, u8); -impl_from!(Value::Integer, u16); -impl_from!(Value::Integer, u32); -impl_from!(Value::Integer, u64); -// u128 omitted because not all numbers fit in CBOR serialization +impl_from!(Value::Integer, i128); +impl_from!(Value::Unsigned, u8); +impl_from!(Value::Unsigned, u16); +impl_from!(Value::Unsigned, u32); +impl_from!(Value::Unsigned, u64); +impl_from!(Value::Unsigned, u128); impl_from!(Value::Float, f32); impl_from!(Value::Float, f64); impl_from!(Value::Bytes, Vec); @@ -136,12 +136,21 @@ impl Value { Null => 7, Bool(_) => 7, Integer(v) => { - if *v >= 0 { + if v.abs() > i128::from(u64::max_value()) { + 6 + } else if *v >= 0 { 0 } else { 1 } } + Unsigned(v) => { + if *v > u128::from(u64::max_value()) { + 6 + } else { + 0 + } + } Float(_) => 7, Bytes(_) => 2, Text(_) => 3, diff --git a/src/value/ser.rs b/src/value/ser.rs index e51dea89..f1b9cfe6 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -21,6 +21,7 @@ impl serde::Serialize for Value { { match *self { Value::Integer(v) => serializer.serialize_i128(v), + Value::Unsigned(v) => serializer.serialize_u128(v), Value::Bytes(ref v) => serializer.serialize_bytes(&v), Value::Text(ref v) => serializer.serialize_str(&v), Value::Array(ref v) => v.serialize(serializer), @@ -78,22 +79,27 @@ impl serde::Serializer for Serializer { #[inline] fn serialize_u8(self, value: u8) -> Result { - self.serialize_u64(u64::from(value)) + self.serialize_u128(u128::from(value)) } #[inline] fn serialize_u16(self, value: u16) -> Result { - self.serialize_u64(u64::from(value)) + self.serialize_u128(u128::from(value)) } #[inline] fn serialize_u32(self, value: u32) -> Result { - self.serialize_u64(u64::from(value)) + self.serialize_u128(u128::from(value)) } #[inline] fn serialize_u64(self, value: u64) -> Result { - Ok(Value::Integer(value.into())) + self.serialize_u128(u128::from(value)) + } + + #[inline] + fn serialize_u128(self, value: u128) -> Result { + Ok(Value::Unsigned(value)) } #[inline] diff --git a/tests/std_types.rs b/tests/std_types.rs index 7a7ded48..b35e6ce7 100644 --- a/tests/std_types.rs +++ b/tests/std_types.rs @@ -182,5 +182,23 @@ mod std_tests { -18446744073709551616i128, "3BFFFFFFFFFFFFFFFF" ); - testcase!(test_u128, u128, 17, "11"); + testcase!( + test_i128_c, + i128, + -18446744073709551617i128, + "C349010000000000000000" + ); + testcase!(test_u128_a, u128, 17, "11"); + testcase!( + test_u128_b, + u128, + u128::max_value(), + "C250FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + ); + testcase!( + test_u128_c, + u128, + u128::from(u64::max_value()), + "1BFFFFFFFFFFFFFFFF" + ); }