Repository: pyfisch/cbor
Pull request #103 (2019-03-18 09:43:08)

Implement ser/de for 128 bit numbers
Serde can represent negative numbers up
to -2^64 while an i64's minimum value
is -2^63-1. For this reason these
numbers are now stored as i128.

For symmetry u128 is added.
Note that no numbers beyond +/- 2^64
can be expressed in CBOR and will raise
an error.

pyfisch: Additionally this prepares serde-cbor for an improved `Value` type.

Patch:
diff --git a/src/de.rs b/src/de.rs
index 4c13d36..47a47d0 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -547,7 +547,7 @@ where
             0x3b => {
                 let value = self.parse_u64()?;
                 if value > i64::max_value() as u64 {
-                    return Err(self.error(ErrorCode::NumberOutOfRange));
+                    return visitor.visit_i128(-1 - value as i128);
                 }
                 visitor.visit_i64(-1 - value as i64)
             }
@@ -790,7 +790,7 @@ where
     }
 
     serde::forward_to_deserialize_any! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string unit
+        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string unit
         unit_struct seq tuple tuple_struct map struct identifier ignored_any
         bytes byte_buf
     }
diff --git a/src/error.rs b/src/error.rs
index d1571c6..185211e 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -136,8 +136,7 @@ impl Error {
             ErrorCode::EofWhileParsingValue
             | ErrorCode::EofWhileParsingArray
             | ErrorCode::EofWhileParsingMap => Category::Eof,
-            ErrorCode::NumberOutOfRange
-            | ErrorCode::LengthOutOfRange
+            ErrorCode::LengthOutOfRange
             | ErrorCode::InvalidUtf8
             | ErrorCode::UnassignedCode
             | ErrorCode::UnexpectedCode
@@ -274,7 +273,6 @@ pub(crate) enum ErrorCode {
     EofWhileParsingValue,
     EofWhileParsingArray,
     EofWhileParsingMap,
-    NumberOutOfRange,
     LengthOutOfRange,
     InvalidUtf8,
     UnassignedCode,
@@ -300,7 +298,6 @@ impl fmt::Display for ErrorCode {
             ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
             ErrorCode::EofWhileParsingArray => f.write_str("EOF while parsing an array"),
             ErrorCode::EofWhileParsingMap => f.write_str("EOF while parsing a map"),
-            ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
             ErrorCode::LengthOutOfRange => f.write_str("length out of range"),
             ErrorCode::InvalidUtf8 => f.write_str("invalid UTF-8"),
             ErrorCode::UnassignedCode => f.write_str("unassigned type"),
diff --git a/src/ser.rs b/src/ser.rs
index ebe1b8c..3e4e2e5 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -400,6 +400,21 @@ where
         }
     }
 
+    #[inline]
+    fn serialize_i128(self, value: i128) -> Result<()> {
+        if value < 0 {
+            if -(value + 1) > u64::max_value() as i128 {
+                return Err(Error::message("The number can't be stored in CBOR"));
+            }
+            self.write_u64(1, -(value + 1) as u64)
+        } else {
+            if value > u64::max_value() as i128 {
+                return Err(Error::message("The number can't be stored in CBOR"));
+            }
+            self.write_u64(0, value as u64)
+        }
+    }
+
     #[inline]
     fn serialize_u8(self, value: u8) -> Result<()> {
         self.write_u8(0, value)
@@ -420,6 +435,14 @@ where
         self.write_u64(0, value)
     }
 
+    #[inline]
+    fn serialize_u128(self, value: u128) -> Result<()> {
+        if value > u64::max_value() as u128 {
+            return Err(Error::message("The number can't be stored in CBOR"));
+        }
+        self.write_u64(0, value as u64)
+    }
+
     #[inline]
     #[allow(clippy::float_cmp)]
     fn serialize_f32(self, value: f32) -> Result<()> {


Test patch:
diff --git a/tests/std_types.rs b/tests/std_types.rs
index 5f2bf1d..48c067e 100644
--- a/tests/std_types.rs
+++ b/tests/std_types.rs
@@ -53,8 +53,12 @@ mod std_tests {
             fn $name() {
                 let expr: $ty = $expr;
                 let mut serialized = to_binary($s);
-                assert_eq!(to_vec(&expr).unwrap(), serialized, "serialization differs");
-                let parsed: $ty = from_slice(&serialized[..]).unwrap();
+                assert_eq!(
+                    to_vec(&expr).expect("ser1 works"),
+                    serialized,
+                    "serialization differs"
+                );
+                let parsed: $ty = from_slice(&serialized[..]).expect("de1 works");
                 assert_eq!(parsed, expr, "parsed result differs");
                 let packed = &to_vec_packed(&expr).expect("serializing packed")[..];
                 let parsed_from_packed: $ty = from_slice(packed).expect("parsing packed");
@@ -173,4 +177,12 @@ mod std_tests {
         Color::Alpha(234567, 60),
         "8365416c7068611a00039447183c"
     );
+    testcase!(test_i128_a, i128, -1i128, "20");
+    testcase!(
+        test_i128_b,
+        i128,
+        -18446744073709551616i128,
+        "3BFFFFFFFFFFFFFFFF"
+    );
+    testcase!(test_u128, u128, 17, "11");
 }