Repository: pyfisch/cbor
Pull request #131 (2019-06-23 15:40:12)

Derive Debug for all types


Patch:
diff --git a/src/de.rs b/src/de.rs
index 76908b3..56420f2 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -123,9 +123,14 @@ where
 }
 
 /// A Serde `Deserialize`r of CBOR data.
+#[derive(Debug)]
 pub struct Deserializer<R> {
     read: R,
     remaining_depth: u8,
+    accept_named: bool,
+    accept_packed: bool,
+    accept_standard_enums: bool,
+    accept_legacy_enums: bool,
 }
 
 #[cfg(feature = "std")]
@@ -180,9 +185,37 @@ where
         Deserializer {
             read,
             remaining_depth: 128,
+            accept_named: true,
+            accept_packed: true,
+            accept_standard_enums: true,
+            accept_legacy_enums: true,
         }
     }
 
+    /// Don't accept named variants and fields.
+    pub fn disable_named_format(mut self) -> Self {
+        self.accept_named = false;
+        self
+    }
+
+    /// Don't accept numbered variants and fields.
+    pub fn disable_packed_format(mut self) -> Self {
+        self.accept_packed = false;
+        self
+    }
+
+    /// Don't accept the new enum format used by `serde_cbor` versions >= v0.10.
+    pub fn disable_standard_enums(mut self) -> Self {
+        self.accept_standard_enums = false;
+        self
+    }
+
+    /// Don't accept the old enum format used by `serde_cbor` versions <= v0.9.
+    pub fn disable_legacy_enums(mut self) -> Self {
+        self.accept_legacy_enums = false;
+        self
+    }
+
     /// This method should be called after a value has been deserialized to ensure there is no
     /// trailing data in the input source.
     pub fn end(&mut self) -> Result<()> {
@@ -408,8 +441,15 @@ where
     where
         V: de::Visitor<'de>,
     {
+        let accept_packed = self.accept_packed;
+        let accept_named = self.accept_named;
         self.recursion_checked(|de| {
-            let value = visitor.visit_map(MapAccess { de, len: &mut len })?;
+            let value = visitor.visit_map(MapAccess {
+                de,
+                len: &mut len,
+                accept_named,
+                accept_packed,
+            })?;
 
             if len != 0 {
                 Err(de.error(ErrorCode::TrailingData))
@@ -423,8 +463,14 @@ where
     where
         V: de::Visitor<'de>,
     {
+        let accept_named = self.accept_named;
+        let accept_packed = self.accept_packed;
         self.recursion_checked(|de| {
-            let value = visitor.visit_map(IndefiniteMapAccess { de })?;
+            let value = visitor.visit_map(IndefiniteMapAccess {
+                de,
+                accept_packed,
+                accept_named,
+            })?;
             match de.next()? {
                 Some(0xff) => Ok(value),
                 Some(_) => Err(de.error(ErrorCode::TrailingData)),
@@ -454,10 +500,17 @@ where
     where
         V: de::Visitor<'de>,
     {
+        let accept_named = self.accept_named;
+        let accept_packed = self.accept_packed;
         self.recursion_checked(|de| {
             let mut len = 1;
             let value = visitor.visit_enum(VariantAccessMap {
-                map: MapAccess { de, len: &mut len },
+                map: MapAccess {
+                    de,
+                    len: &mut len,
+                    accept_packed,
+                    accept_named,
+                },
             })?;
 
             if len != 0 {
@@ -746,6 +799,9 @@ where
     {
         match self.peek()? {
             Some(byte @ 0x80..=0x9f) => {
+                if !self.accept_legacy_enums {
+                    return Err(self.error(ErrorCode::WrongEnumFormat));
+                }
                 self.consume();
                 match byte {
                     0x80..=0x97 => self.parse_enum(byte as usize - 0x80, visitor),
@@ -775,11 +831,19 @@ where
                 }
             }
             Some(0xa1) => {
+                if !self.accept_standard_enums {
+                    return Err(self.error(ErrorCode::WrongEnumFormat));
+                }
                 self.consume();
                 self.parse_enum_map(visitor)
             }
             None => Err(self.error(ErrorCode::EofWhileParsingValue)),
-            _ => visitor.visit_enum(UnitVariantAccess { de: self }),
+            _ => {
+                if !self.accept_standard_enums && !self.accept_legacy_enums {
+                    return Err(self.error(ErrorCode::WrongEnumFormat));
+                }
+                visitor.visit_enum(UnitVariantAccess { de: self })
+            }
         }
     }
 
@@ -885,6 +949,8 @@ where
 struct MapAccess<'a, R> {
     de: &'a mut Deserializer<R>,
     len: &'a mut usize,
+    accept_named: bool,
+    accept_packed: bool,
 }
 
 impl<'de, 'a, R> de::MapAccess<'de> for MapAccess<'a, R>
@@ -902,6 +968,16 @@ where
         }
         *self.len -= 1;
 
+        match self.de.peek()? {
+            Some(_byte @ 0x00..=0x1b) if !self.accept_packed => {
+                return Err(self.de.error(ErrorCode::WrongStructFormat));
+            }
+            Some(_byte @ 0x60..=0x7f) if !self.accept_named => {
+                return Err(self.de.error(ErrorCode::WrongStructFormat));
+            }
+            _ => {}
+        };
+
         let value = seed.deserialize(&mut *self.de)?;
         Ok(Some(value))
     }
@@ -929,6 +1005,8 @@ where
 
 struct IndefiniteMapAccess<'a, R> {
     de: &'a mut Deserializer<R>,
+    accept_packed: bool,
+    accept_named: bool,
 }
 
 impl<'de, 'a, R> de::MapAccess<'de> for IndefiniteMapAccess<'a, R>
@@ -942,6 +1020,12 @@ where
         K: de::DeserializeSeed<'de>,
     {
         match self.de.peek()? {
+            Some(_byte @ 0x00..=0x1b) if !self.accept_packed => {
+                return Err(self.de.error(ErrorCode::WrongStructFormat))
+            }
+            Some(_byte @ 0x60..=0x7f) if !self.accept_named => {
+                return Err(self.de.error(ErrorCode::WrongStructFormat))
+            }
             Some(0xff) => return Ok(None),
             Some(_) => {}
             None => return Err(self.de.error(ErrorCode::EofWhileParsingMap)),
@@ -1132,6 +1216,7 @@ where
 /// );
 /// # }
 /// ```
+#[derive(Debug)]
 pub struct StreamDeserializer<'de, R, T> {
     de: Deserializer<R>,
     output: PhantomData<T>,
diff --git a/src/error.rs b/src/error.rs
index 9f7c0c1..c388fa5 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -141,7 +141,9 @@ impl Error {
             | ErrorCode::TrailingData
             | ErrorCode::ArrayTooShort
             | ErrorCode::ArrayTooLong
-            | ErrorCode::RecursionLimitExceeded => Category::Syntax,
+            | ErrorCode::RecursionLimitExceeded
+            | ErrorCode::WrongEnumFormat
+            | ErrorCode::WrongStructFormat => Category::Syntax,
         }
     }
 
@@ -277,6 +279,8 @@ pub(crate) enum ErrorCode {
     ArrayTooShort,
     ArrayTooLong,
     RecursionLimitExceeded,
+    WrongEnumFormat,
+    WrongStructFormat,
 }
 
 impl fmt::Display for ErrorCode {
@@ -300,6 +304,8 @@ impl fmt::Display for ErrorCode {
             ErrorCode::ArrayTooShort => f.write_str("array too short"),
             ErrorCode::ArrayTooLong => f.write_str("array too long"),
             ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
+            ErrorCode::WrongEnumFormat => f.write_str("wrong enum format"),
+            ErrorCode::WrongStructFormat => f.write_str("wrong struct format"),
         }
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 408f629..db9eb21 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,72 +1,58 @@
 //! CBOR and serialization.
 //!
-//! # What is CBOR?
-//! [CBOR](http://cbor.io) is a way to encode data in a space-efficient and fast binary format.
-//! CBORs data-model is a superset of the JSONs.
-//!
-//! A simple object describing a person in diagnostic notation (it is actually JSON plus some
-//! annotations) looks like
-//!
-//! ```json
-//! {
-//!     "FirstName": "John",
-//!     "LastName": "Doe",
-//!     "Age": 43,
-//!     "Address": {
-//!         "Street": "Downing Street 10",
-//!         "City": "London",
-//!         "Country": "Great Britain"
-//!     },
-//!     "PhoneNumbers": [
-//!         "+44 1234567",
-//!         "+44 2345678"
-//!     ]
-//! }
+//! # Usage
+//!
+//! Serde CBOR supports Rust 1.31 and up. Add this to your `Cargo.toml`:
+//! ```toml
+//! [dependencies]
+//! serde_cbor = "0.9"
 //! ```
 //!
-//! The CBOR encoded object with comments in hexadecimal notation looks like
-//!
-//! ```cbor
-//! a5                                      # map(5)
-//!    69                                   # text(9)
-//!       46697273744e616d65                # "FirstName"
-//!    64                                   # text(4)
-//!       4a6f686e                          # "John"
-//!    68                                   # text(8)
-//!       4c6173744e616d65                  # "LastName"
-//!    63                                   # text(3)
-//!       446f65                            # "Doe"
-//!    63                                   # text(3)
-//!       416765                            # "Age"
-//!    18 2b                                # unsigned(43)
-//!    67                                   # text(7)
-//!       41646472657373                    # "Address"
-//!    a3                                   # map(3)
-//!       66                                # text(6)
-//!          537472656574                   # "Street"
-//!       71                                # text(17)
-//!          446f776e696e6720537472656574203130 # "Downing Street 10"
-//!       64                                # text(4)
-//!          43697479                       # "City"
-//!       66                                # text(6)
-//!          4c6f6e646f6e                   # "London"
-//!       67                                # text(7)
-//!          436f756e747279                 # "Country"
-//!       6d                                # text(13)
-//!          4772656174204272697461696e     # "Great Britain"
-//!    6c                                   # text(12)
-//!       50686f6e654e756d62657273          # "PhoneNumbers"
-//!    82                                   # array(2)
-//!       6b                                # text(11)
-//!          2b34342031323334353637         # "+44 1234567"
-//!       6b                                # text(11)
-//!          2b34342032333435363738         # "+44 2345678"
+//! Storing and loading Rust types is easy and requires only
+//! minimal modifications to the program code.
+//!
+//! ```rust
+//! use serde_derive::{Deserialize, Serialize};
+//! use std::error::Error;
+//! use std::fs::File;
+//!
+//! // Types annotated with `Serialize` can be stored as CBOR.
+//! // To be able to load them again add `Deserialize`.
+//! #[derive(Debug, Serialize, Deserialize)]
+//! struct Mascot {
+//!     name: String,
+//!     species: String,
+//!     year_of_birth: u32,
+//! }
+//!
+//! fn main() -> Result<(), Box<dyn Error>> {
+//!     let ferris = Mascot {
+//!         name: "Ferris".to_owned(),
+//!         species: "crab".to_owned(),
+//!         year_of_birth: 2015,
+//!     };
+//!
+//!     let ferris_file = File::create("examples/ferris.cbor")?;
+//!     // Write Ferris to the given file.
+//!     // Instead of a file you can use any type that implements `io::Write`
+//!     // like a HTTP body, database connection etc.
+//!     serde_cbor::to_writer(ferris_file, &ferris)?;
+//!
+//!     let tux_file = File::open("examples/tux.cbor")?;
+//!     // Load Tux from a file.
+//!     // Serde CBOR performs roundtrip serialization meaning that
+//!     // the data will not change in any way.
+//!     let tux: Mascot = serde_cbor::from_reader(tux_file)?;
+//!
+//!     println!("{:?}", tux);
+//!     // prints: Mascot { name: "Tux", species: "penguin", year_of_birth: 1996 }
+//!
+//!     Ok(())
+//! }
 //! ```
-//! While the JSON encoding is 174 bytes long the CBOR representation is only 141 bytes long.
-//! This is 19% shorter! Sometimes compression will even better, but never CBOR will be longer
-//! than the corresponding JSON. More importantly CBOR supports binary data, custom data types,
-//! annotations for dates, times and expected encoding and is faster to serialize and deserialize.
-//! It can even be used on embedded devices.
+//!
+//! There are a lot of options available to customize the format.
+//! To operate on untyped CBOR values have a look at the `Value` type.
 //!
 //! # Type-based Serialization and Deserialization
 //! Serde provides a mechanism for low boilerplate serialization & deserialization of values to and
diff --git a/src/read.rs b/src/read.rs
index 3af9b3c..89bbd7f 100644
--- a/src/read.rs
+++ b/src/read.rs
@@ -133,6 +133,7 @@ mod private {
 
 /// CBOR input source that reads from a std::io input stream.
 #[cfg(feature = "std")]
+#[derive(Debug)]
 pub struct IoRead<R>
 where
     R: io::Read,
@@ -261,6 +262,7 @@ where
 }
 
 #[cfg(feature = "std")]
+#[derive(Debug)]
 struct OffsetReader<R> {
     reader: R,
     offset: u64,
@@ -283,6 +285,7 @@ where
 
 /// A CBOR input source that reads from a slice of bytes.
 #[cfg(feature = "std")]
+#[derive(Debug)]
 pub struct SliceRead<'a> {
     slice: &'a [u8],
     scratch: Vec<u8>,
@@ -391,6 +394,7 @@ impl<'a> Read<'a> for SliceRead<'a> {
 ///
 /// [`SliceRead`](struct.SliceRead.html) and [`MutSliceRead`](struct.MutSliceRead.html) are usually
 /// preferred over this, as they can handle indefinite length items.
+#[derive(Debug)]
 pub struct SliceReadFixed<'a, 'b> {
     slice: &'a [u8],
     scratch: &'b mut [u8],
@@ -497,6 +501,7 @@ impl<'a, 'b> Read<'a> for SliceReadFixed<'a, 'b> {
 
 /// A CBOR input source that reads from a slice of bytes, and can move data around internally to
 /// reassemble indefinite strings without the need of an allocated scratch buffer.
+#[derive(Debug)]
 pub struct MutSliceRead<'a> {
     /// A complete view of the reader's data. It is promised that bytes before buffer_end are not
     /// mutated any more.
diff --git a/src/ser.rs b/src/ser.rs
index 031177c..5d13f96 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -44,6 +44,7 @@ where
 }
 
 /// A structure for serializing Rust values to CBOR.
+#[derive(Debug)]
 pub struct Serializer<W> {
     writer: W,
     packed: bool,
diff --git a/src/write.rs b/src/write.rs
index aa8d3df..5393401 100644
--- a/src/write.rs
+++ b/src/write.rs
@@ -67,6 +67,7 @@ impl<W> private::Sealed for &mut W where W: Write {}
 /// A wrapper for types that implement
 /// [`std::io::Write`](https://doc.rust-lang.org/std/io/trait.Write.html) to implement the local
 /// [`Write`](trait.Write.html) trait.
+#[derive(Debug)]
 pub struct IoWrite<W>(W);
 
 #[cfg(feature = "std")]
@@ -103,6 +104,7 @@ impl Write for Vec<u8> {
 impl private::Sealed for Vec<u8> {}
 
 #[cfg(not(feature = "std"))]
+#[derive(Debug)]
 pub struct FmtWrite<'a, W: Write>(&'a mut W);
 
 #[cfg(not(feature = "std"))]
@@ -126,6 +128,7 @@ impl<'a, W> private::Sealed for FmtWrite<'a, W> where W: Write {}
 /// Implements [`Write`](trait.Write.html) for mutable byte slices (`&mut [u8]`).
 ///
 /// Returns an error if the value to serialize is too large to fit in the slice.
+#[derive(Debug)]
 pub struct SliceWrite<'a> {
     slice: &'a mut [u8],
     index: usize,


Test patch:
diff --git a/tests/de.rs b/tests/de.rs
index 6d5d6b8..8eed7cd 100644
--- a/tests/de.rs
+++ b/tests/de.rs
@@ -402,4 +402,328 @@ mod std_tests {
         assert_eq!(value, "foobar");
         assert_eq!(rest, &[]);
     }
+
+    #[derive(Debug, Copy, Clone)]
+    struct Options {
+        standard: bool,
+        legacy: bool,
+        packed: bool,
+        named: bool,
+    }
+
+    impl Default for Options {
+        fn default() -> Self {
+            Options {
+                standard: true,
+                legacy: true,
+                packed: true,
+                named: true,
+            }
+        }
+    }
+
+    impl Options {
+        fn no_standard(self) -> Self {
+            Options {
+                standard: false,
+                ..self
+            }
+        }
+
+        fn no_legacy(self) -> Self {
+            Options {
+                legacy: false,
+                ..self
+            }
+        }
+
+        fn no_packed(self) -> Self {
+            Options {
+                packed: false,
+                ..self
+            }
+        }
+
+        fn no_named(self) -> Self {
+            Options {
+                named: false,
+                ..self
+            }
+        }
+    }
+
+    fn from_slice_stream_options<'a, T>(
+        slice: &'a [u8],
+        options: Options,
+    ) -> error::Result<(&'a [u8], T)>
+    where
+        T: serde_de::Deserialize<'a>,
+    {
+        let deserializer = Deserializer::from_slice(slice);
+        let deserializer = if !options.packed {
+            deserializer.disable_packed_format()
+        } else {
+            deserializer
+        };
+        let deserializer = if !options.named {
+            deserializer.disable_named_format()
+        } else {
+            deserializer
+        };
+        let deserializer = if !options.standard {
+            deserializer.disable_standard_enums()
+        } else {
+            deserializer
+        };
+        let mut deserializer = if !options.legacy {
+            deserializer.disable_legacy_enums()
+        } else {
+            deserializer
+        };
+        let value = serde_de::Deserialize::deserialize(&mut deserializer)?;
+        let rest = &slice[deserializer.byte_offset()..];
+
+        Ok((rest, value))
+    }
+
+    #[test]
+    fn test_deserializer_enums() {
+        #[derive(Debug, PartialEq, Deserialize)]
+        enum Enum {
+            Unit,
+            NewType(i32),
+            Tuple(String, bool),
+            Struct { x: i32, y: i32 },
+        }
+
+        // This is the format used in serde >= 0.10
+        //
+        // Serialization of Enum::NewType(10)
+        let v: Vec<u8> = vec![
+            0xa1, // map 1pair
+            0x67, 0x4e, 0x65, 0x77, 0x54, 0x79, 0x70, 0x65, // utf8 string: NewType
+            0x1a, // u32
+            0x00, 0x00, 0x00, 0x0a, // 10 (dec)
+        ];
+        let (_rest, value): (&[u8], Enum) = from_slice_stream(&v[..]).unwrap();
+        assert_eq!(value, Enum::NewType(10));
+        let (_rest, value): (&[u8], Enum) =
+            from_slice_stream_options(&v[..], Options::default().no_legacy()).unwrap();
+        assert_eq!(value, Enum::NewType(10));
+        let value: error::Result<(&[u8], Enum)> =
+            from_slice_stream_options(&v[..], Options::default().no_standard());
+        assert_eq!(
+            value.unwrap_err().classify(),
+            serde_cbor::error::Category::Syntax
+        );
+        let value: error::Result<(&[u8], Enum)> =
+            from_slice_stream_options(&v[..], Options::default().no_standard().no_legacy());
+        assert_eq!(
+            value.unwrap_err().classify(),
+            serde_cbor::error::Category::Syntax
+        );
+        // Serialization of Enum::Unit
+        let v: Vec<u8> = vec![
+            0x64, 0x55, 0x6e, 0x69, 0x74, // utf8 string: Unit
+        ];
+        let (_rest, value): (&[u8], Enum) = from_slice_stream(&v[..]).unwrap();
+        assert_eq!(value, Enum::Unit);
+        let (_rest, value): (&[u8], Enum) =
+            from_slice_stream_options(&v[..], Options::default().no_legacy()).unwrap();
+        assert_eq!(value, Enum::Unit);
+        let (_rest, value): (&[u8], Enum) =
+            from_slice_stream_options(&v[..], Options::default().no_standard()).unwrap();
+        assert_eq!(value, Enum::Unit);
+        let value: error::Result<(&[u8], Enum)> =
+            from_slice_stream_options(&v[..], Options::default().no_legacy().no_standard());
+        assert_eq!(
+            value.unwrap_err().classify(),
+            serde_cbor::error::Category::Syntax
+        );
+
+        // This is the format used in serde <= 0.9
+        let v: Vec<u8> = vec![
+            0x82, // array 2 items
+            0x67, 0x4e, 0x65, 0x77, 0x54, 0x79, 0x70, 0x65, // utf8 string: NewType
+            0x1a, // u32
+            0x00, 0x00, 0x00, 0x0a, // 10 (dec)
+        ];
+        let (_rest, value): (&[u8], Enum) = from_slice_stream(&v[..]).unwrap();
+        assert_eq!(value, Enum::NewType(10));
+        let value: error::Result<(&[u8], Enum)> =
+            from_slice_stream_options(&v[..], Options::default().no_legacy());
+        assert_eq!(
+            value.unwrap_err().classify(),
+            serde_cbor::error::Category::Syntax
+        );
+        let value: error::Result<(&[u8], Enum)> =
+            from_slice_stream_options(&v[..], Options::default().no_standard());
+        assert_eq!(value.unwrap().1, Enum::NewType(10));
+        let value: error::Result<(&[u8], Enum)> =
+            from_slice_stream_options(&v[..], Options::default().no_standard().no_legacy());
+        assert_eq!(
+            value.unwrap_err().classify(),
+            serde_cbor::error::Category::Syntax
+        );
+    }
+
+    #[test]
+    fn test_packed_deserialization() {
+        #[derive(Debug, PartialEq, Deserialize)]
+        struct User {
+            user_id: u32,
+            password_hash: [u8; 4],
+        }
+
+        // unpacked
+        let v: Vec<u8> = vec![
+            0xa2, // map 2pair
+            0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, // utf8 string: user_id
+            0x0a, // integer: 10
+            // utf8 string: password_hash
+            0x6d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68,
+            0x84, 0x01, 0x02, 0x03, 0x04, // 4 byte array [1, 2, 3, 4]
+        ];
+
+        let (_rest, value): (&[u8], User) = from_slice_stream(&v[..]).unwrap();
+        assert_eq!(
+            value,
+            User {
+                user_id: 10,
+                password_hash: [1, 2, 3, 4],
+            }
+        );
+        let (_rest, value): (&[u8], User) =
+            from_slice_stream_options(&v[..], Options::default().no_packed()).unwrap();
+        assert_eq!(
+            value,
+            User {
+                user_id: 10,
+                password_hash: [1, 2, 3, 4],
+            }
+        );
+        let value: error::Result<(&[u8], User)> =
+            from_slice_stream_options(&v[..], Options::default().no_named());
+        assert_eq!(
+            value.unwrap_err().classify(),
+            serde_cbor::error::Category::Syntax
+        );
+
+        // unpacked - indefinite length
+        let v: Vec<u8> = vec![
+            0xbf, // map to be followed by a break
+            0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, // utf8 string: user_id
+            0x0a, // integer: 10
+            // utf8 string: password_hash
+            0x6d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68,
+            0x84, 0x01, 0x02, 0x03, 0x04, // 4 byte array [1, 2, 3, 4]
+            0xff, // break
+        ];
+
+        let (_rest, value): (&[u8], User) = from_slice_stream(&v[..]).unwrap();
+        assert_eq!(
+            value,
+            User {
+                user_id: 10,
+                password_hash: [1, 2, 3, 4],
+            }
+        );
+        let (_rest, value): (&[u8], User) =
+            from_slice_stream_options(&v[..], Options::default().no_packed()).unwrap();
+        assert_eq!(
+            value,
+            User {
+                user_id: 10,
+                password_hash: [1, 2, 3, 4],
+            }
+        );
+        let value: error::Result<(&[u8], User)> =
+            from_slice_stream_options(&v[..], Options::default().no_named());
+        assert_eq!(
+            value.unwrap_err().classify(),
+            serde_cbor::error::Category::Syntax
+        );
+
+        // packed
+        let v: Vec<u8> = vec![
+            0xa2, // map 2pair
+            0x00, // index 0
+            0x0a, // integer: 10
+            0x01, // index 1
+            0x84, 0x01, 0x02, 0x03, 0x04, // 4 byte array [1, 2, 3, 4]
+        ];
+
+        let (_rest, value): (&[u8], User) = from_slice_stream(&v[..]).unwrap();
+        assert_eq!(
+            value,
+            User {
+                user_id: 10,
+                password_hash: [1, 2, 3, 4],
+            }
+        );
+        let (_rest, value): (&[u8], User) =
+            from_slice_stream_options(&v[..], Options::default().no_named()).unwrap();
+        assert_eq!(
+            value,
+            User {
+                user_id: 10,
+                password_hash: [1, 2, 3, 4],
+            }
+        );
+        let value: error::Result<(&[u8], User)> =
+            from_slice_stream_options(&v[..], Options::default().no_packed());
+        assert_eq!(
+            value.unwrap_err().classify(),
+            serde_cbor::error::Category::Syntax
+        );
+
+        // packed - indefinite length
+        let v: Vec<u8> = vec![
+            0xbf, // map, to be followed by a break
+            0x00, // index 0
+            0x0a, // integer: 10
+            0x01, // index 1
+            0x84, 0x01, 0x02, 0x03, 0x04, // 4 byte array [1, 2, 3, 4]
+            0xff, // break
+        ];
+
+        let (_rest, value): (&[u8], User) = from_slice_stream(&v[..]).unwrap();
+        assert_eq!(
+            value,
+            User {
+                user_id: 10,
+                password_hash: [1, 2, 3, 4],
+            }
+        );
+        let (_rest, value): (&[u8], User) =
+            from_slice_stream_options(&v[..], Options::default().no_named()).unwrap();
+        assert_eq!(
+            value,
+            User {
+                user_id: 10,
+                password_hash: [1, 2, 3, 4],
+            }
+        );
+        let value: error::Result<(&[u8], User)> =
+            from_slice_stream_options(&v[..], Options::default().no_packed());
+        assert_eq!(
+            value.unwrap_err().classify(),
+            serde_cbor::error::Category::Syntax
+        );
+    }
+
+    use serde_cbor::{de::from_slice, ser::to_vec_packed};
+    use std::net::{IpAddr, Ipv4Addr};
+    #[test]
+    fn test_ipaddr_deserialization() {
+        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
+        let buf = to_vec_packed(&ip).unwrap();
+        let deserialized_ip = from_slice::<IpAddr>(&buf).unwrap();
+        assert_eq!(ip, deserialized_ip);
+
+        let buf = to_vec(&ip).unwrap();
+        let deserialized_ip = from_slice::<IpAddr>(&buf).unwrap();
+        assert_eq!(ip, deserialized_ip);
+    }
+
 }