Repository: pyfisch/cbor
Pull request #101 (2019-03-18 00:49:44)

Support deserializing from mixed slices.
When trying to parse fido2 (ctap2) responses from token,
slices are packed together, cbor encoded data may be followed
by other type of data.

This patch introduces a new `byte_offset` method that would
return the position of the reader.

Based on suggestions of @dtolnay. Superseed #100.

pyfisch: LGTM. Can you please rebase the changes and add a simple test?
baloo: @pyfisch rebased and tested
baloo: @pyfisch any chance you could merge this?
pyfisch: LGTM. Will merge with the comment addressed.

If you have time it would be great if you could add an example to the crate level documentation in `lib.rs` (right before the section "limitations")

Patch:
diff --git a/src/de.rs b/src/de.rs
index 47a47d0..c0b8319 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -15,6 +15,7 @@ use crate::error::{Error, ErrorCode, Result};
 use crate::read::EitherLifetime;
 #[cfg(feature = "unsealed_read_write")]
 pub use crate::read::EitherLifetime;
+use crate::read::Offset;
 #[cfg(feature = "std")]
 pub use crate::read::{IoRead, SliceRead};
 pub use crate::read::{MutSliceRead, Read, SliceReadFixed};
@@ -547,7 +548,7 @@ where
             0x3b => {
                 let value = self.parse_u64()?;
                 if value > i64::max_value() as u64 {
-                    return visitor.visit_i128(-1 - value as i128);
+                    return Err(self.error(ErrorCode::NumberOutOfRange));
                 }
                 visitor.visit_i64(-1 - value as i64)
             }
@@ -790,12 +791,23 @@ where
     }
 
     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
     }
 }
 
+impl<R> Deserializer<R>
+where
+    R: Offset,
+{
+    /// Return the current offset in the reader
+    #[inline]
+    pub fn byte_offset(&self) -> usize {
+        self.read.byte_offset()
+    }
+}
+
 trait MakeError {
     fn error(&self, code: ErrorCode) -> Error;
 }
diff --git a/src/error.rs b/src/error.rs
index 185211e..d1571c6 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -136,7 +136,8 @@ impl Error {
             ErrorCode::EofWhileParsingValue
             | ErrorCode::EofWhileParsingArray
             | ErrorCode::EofWhileParsingMap => Category::Eof,
-            ErrorCode::LengthOutOfRange
+            ErrorCode::NumberOutOfRange
+            | ErrorCode::LengthOutOfRange
             | ErrorCode::InvalidUtf8
             | ErrorCode::UnassignedCode
             | ErrorCode::UnexpectedCode
@@ -273,6 +274,7 @@ pub(crate) enum ErrorCode {
     EofWhileParsingValue,
     EofWhileParsingArray,
     EofWhileParsingMap,
+    NumberOutOfRange,
     LengthOutOfRange,
     InvalidUtf8,
     UnassignedCode,
@@ -298,6 +300,7 @@ 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/lib.rs b/src/lib.rs
index b65bb6a..2c7f337 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -145,6 +145,25 @@
 //! # }
 //! ```
 //!
+//! Deserializing data in the middle of a slice
+//! ```
+//! # extern crate serde_cbor;
+//! use serde_cbor::Deserializer;
+//!
+//! # fn main() {
+//! let data: Vec<u8> = vec![
+//!     0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x66, 0x66, 0x6f, 0x6f, 0x62,
+//!     0x61, 0x72,
+//! ];
+//! let mut deserializer = Deserializer::from_slice(&data);
+//! let value: &str = serde::de::Deserialize::deserialize(&mut deserializer)
+//!     .unwrap();
+//! let rest = &data[deserializer.byte_offset()..];
+//! assert_eq!(value, "foobar");
+//! assert_eq!(rest, &[0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72]);
+//! # }
+//! ```
+//!
 //! # Limitations
 //!
 //! While Serde CBOR strives to support all features of Serde and CBOR
diff --git a/src/read.rs b/src/read.rs
index dad7b92..3af9b3c 100644
--- a/src/read.rs
+++ b/src/read.rs
@@ -113,6 +113,11 @@ pub trait Read<'de> {
     fn offset(&self) -> u64;
 }
 
+/// Represents a reader that can return its current position
+pub trait Offset {
+    fn byte_offset(&self) -> usize;
+}
+
 /// Represents a buffer with one of two lifetimes.
 pub enum EitherLifetime<'short, 'long> {
     /// The short lifetime
@@ -306,6 +311,14 @@ impl<'a> SliceRead<'a> {
     }
 }
 
+#[cfg(feature = "std")]
+impl<'a> Offset for SliceRead<'a> {
+    #[inline]
+    fn byte_offset(&self) -> usize {
+        self.index
+    }
+}
+
 #[cfg(all(feature = "std", not(feature = "unsealed_read_write")))]
 impl<'a> private::Sealed for SliceRead<'a> {}
 
diff --git a/src/ser.rs b/src/ser.rs
index 3e4e2e5..ebe1b8c 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -400,21 +400,6 @@ 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)
@@ -435,14 +420,6 @@ 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/de.rs b/tests/de.rs
index eb2655d..aadf85b 100644
--- a/tests/de.rs
+++ b/tests/de.rs
@@ -48,6 +48,7 @@ mod std_tests {
     use serde_bytes::ByteBuf;
     use std::collections::BTreeMap;
 
+    use serde::de as serde_de;
     use serde_cbor::{de, error, from_reader, to_vec, Deserializer, ObjectKey, Value};
 
     #[test]
@@ -396,4 +397,28 @@ mod std_tests {
             serde_cbor::error::Category::Syntax
         );
     }
+
+    fn from_slice_stream<'a, T>(slice: &'a [u8]) -> error::Result<(&'a [u8], T)>
+    where
+        T: serde_de::Deserialize<'a>,
+    {
+        let mut deserializer = Deserializer::from_slice(slice);
+        let value = serde_de::Deserialize::deserialize(&mut deserializer)?;
+        let rest = &slice[deserializer.byte_offset()..];
+
+        Ok((rest, value))
+    }
+
+    #[test]
+    fn test_slice_offset() {
+        let v: Vec<u8> = vec![
+            0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72,
+        ];
+        let (rest, value): (&[u8], String) = from_slice_stream(&v[..]).unwrap();
+        assert_eq!(value, "foobar");
+        assert_eq!(rest, &[0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72]);
+        let (rest, value): (&[u8], String) = from_slice_stream(rest).unwrap();
+        assert_eq!(value, "foobar");
+        assert_eq!(rest, &[]);
+    }
 }
diff --git a/tests/std_types.rs b/tests/std_types.rs
index 48c067e..5f2bf1d 100644
--- a/tests/std_types.rs
+++ b/tests/std_types.rs
@@ -53,12 +53,8 @@ mod std_tests {
             fn $name() {
                 let expr: $ty = $expr;
                 let mut serialized = to_binary($s);
-                assert_eq!(
-                    to_vec(&expr).expect("ser1 works"),
-                    serialized,
-                    "serialization differs"
-                );
-                let parsed: $ty = from_slice(&serialized[..]).expect("de1 works");
+                assert_eq!(to_vec(&expr).unwrap(), serialized, "serialization differs");
+                let parsed: $ty = from_slice(&serialized[..]).unwrap();
                 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");
@@ -177,12 +173,4 @@ 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");
 }