Repository: pyfisch/cbor
Pull request #133 (2019-06-25 16:01:51)

Deserializer options
see #119 


Patch:
diff --git a/README.md b/README.md
index afeba30..ddb767f 100644
--- a/README.md
+++ b/README.md
@@ -36,24 +36,24 @@ struct Mascot {
     year_of_birth: u32,
 }
 
-fn main() -> Result<(), Box<dyn Error>> {
+fn main() -> Result<(), Box<Error>> {
     let ferris = Mascot {
         name: "Ferris".to_owned(),
         species: "crab".to_owned(),
         year_of_birth: 2015,
     };
 
-    let ferris_file = File::create("examples/ferris.cbor")?;
+    let mut 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)?;
+    serde_cbor::to_writer(&mut ferris_file, &ferris)?;
 
-    let tux_file = File::open("examples/tux.cbor")?;
+    let mut 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)?;
+    let tux: Mascot = serde_cbor::from_reader(&mut tux_file)?;
 
     println!("{:?}", tux);
     // prints: Mascot { name: "Tux", species: "penguin", year_of_birth: 1996 }
diff --git a/examples/readme.rs b/examples/readme.rs
index 7689394..d9ea455 100644
--- a/examples/readme.rs
+++ b/examples/readme.rs
@@ -13,24 +13,24 @@ struct Mascot {
     year_of_birth: u32,
 }
 
-fn main() -> Result<(), Box<dyn Error>> {
+fn main() -> Result<(), Box<Error>> {
     let ferris = Mascot {
         name: "Ferris".to_owned(),
         species: "crab".to_owned(),
         year_of_birth: 2015,
     };
 
-    let ferris_file = File::create("examples/ferris.cbor")?;
+    let mut 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)?;
+    serde_cbor::to_writer(&mut ferris_file, &ferris)?;
 
-    let tux_file = File::open("examples/tux.cbor")?;
+    let mut 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)?;
+    let tux: Mascot = serde_cbor::from_reader(&mut tux_file)?;
 
     println!("{:?}", tux);
     // prints: Mascot { name: "Tux", species: "penguin", year_of_birth: 1996 }
diff --git a/src/de.rs b/src/de.rs
index 76908b3..d7babaf 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -54,6 +54,7 @@ where
 
 // When the "std" feature is enabled there should be little to no need to ever use this function,
 // as `from_slice` covers all use cases (at the expense of being less efficient).
+#[cfg_attr(feature = "std", doc(hidden))]
 /// Decode a value from CBOR data in a mutable slice.
 ///
 /// This can be used in analogy to `from_slice`. Unlike `from_slice`, this will use the slice's
@@ -72,6 +73,7 @@ where
 // When the "std" feature is enabled there should be little to no need to ever use this function,
 // as `from_slice` covers all use cases and is much more reliable (at the expense of being less
 // efficient).
+#[cfg_attr(feature = "std", doc(hidden))]
 /// Decode a value from CBOR data using a scratch buffer.
 ///
 /// Users should generally prefer to use `from_slice` or `from_mut_slice` over this function,
@@ -126,6 +128,10 @@ where
 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 +186,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 +442,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 +464,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 +501,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 +800,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 +832,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 +950,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 +969,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 +1006,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 +1021,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)),
@@ -1111,27 +1196,6 @@ where
 ///
 /// A stream deserializer can be created from any CBOR deserializer using the
 /// `Deserializer::into_iter` method.
-///
-/// ```
-/// # extern crate serde_cbor;
-/// use serde_cbor::de::Deserializer;
-/// use serde_cbor::value::Value;
-///
-/// # fn main() {
-/// let data: Vec<u8> = vec![
-///     0x01, 0x66, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72,
-/// ];
-/// let mut it = Deserializer::from_slice(&data[..]).into_iter::<Value>();
-/// assert_eq!(
-///     Value::Integer(1),
-///     it.next().unwrap().unwrap()
-/// );
-/// assert_eq!(
-///     Value::Text("foobar".to_string()),
-///     it.next().unwrap().unwrap()
-/// );
-/// # }
-/// ```
 pub struct StreamDeserializer<'de, R, T> {
     de: Deserializer<R>,
     output: PhantomData<T>,
diff --git a/src/error.rs b/src/error.rs
index 9f7c0c1..0667afa 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -130,6 +130,8 @@ impl Error {
             ErrorCode::Message => Category::Data,
             #[cfg(feature = "std")]
             ErrorCode::Io(_) => Category::Io,
+            #[cfg(not(feature = "std"))]
+            ErrorCode::Io => Category::Io,
             ErrorCode::ScratchTooSmall => Category::Io,
             ErrorCode::EofWhileParsingValue
             | ErrorCode::EofWhileParsingArray
@@ -141,7 +143,9 @@ impl Error {
             | ErrorCode::TrailingData
             | ErrorCode::ArrayTooShort
             | ErrorCode::ArrayTooLong
-            | ErrorCode::RecursionLimitExceeded => Category::Syntax,
+            | ErrorCode::RecursionLimitExceeded
+            | ErrorCode::WrongEnumFormat
+            | ErrorCode::WrongStructFormat => Category::Syntax,
         }
     }
 
@@ -265,6 +269,8 @@ pub(crate) enum ErrorCode {
     Message,
     #[cfg(feature = "std")]
     Io(io::Error),
+    #[cfg(not(feature = "std"))]
+    Io,
     ScratchTooSmall,
     EofWhileParsingValue,
     EofWhileParsingArray,
@@ -277,6 +283,8 @@ pub(crate) enum ErrorCode {
     ArrayTooShort,
     ArrayTooLong,
     RecursionLimitExceeded,
+    WrongEnumFormat,
+    WrongStructFormat,
 }
 
 impl fmt::Display for ErrorCode {
@@ -288,6 +296,8 @@ impl fmt::Display for ErrorCode {
             ErrorCode::Message => f.write_str("Unknown error"),
             #[cfg(feature = "std")]
             ErrorCode::Io(ref err) => fmt::Display::fmt(err, f),
+            #[cfg(not(feature = "std"))]
+            ErrorCode::Io => f.write_str("Unknown I/O error"),
             ErrorCode::ScratchTooSmall => f.write_str("Scratch buffer too small"),
             ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
             ErrorCode::EofWhileParsingArray => f.write_str("EOF while parsing an array"),
@@ -300,6 +310,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 db9eb21..b47f5ad 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,58 +1,72 @@
 //! CBOR and serialization.
 //!
-//! # Usage
-//!
-//! Serde CBOR supports Rust 1.31 and up. Add this to your `Cargo.toml`:
-//! ```toml
-//! [dependencies]
-//! serde_cbor = "0.9"
-//! ```
-//!
-//! 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(())
+//! # 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"
+//!     ]
 //! }
 //! ```
 //!
-//! There are a lot of options available to customize the format.
-//! To operate on untyped CBOR values have a look at the `Value` type.
+//! 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"
+//! ```
+//! 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.
 //!
 //! # Type-based Serialization and Deserialization
 //! Serde provides a mechanism for low boilerplate serialization & deserialization of values to and
@@ -132,102 +146,6 @@
 //! # }
 //! ```
 //!
-//! # `no-std` support
-//!
-//! Serde CBOR supports building in a `no_std` context, use the following lines
-//! in your `Cargo.toml` dependencies:
-//! ``` toml
-//! [dependencies]
-//! serde = { version = "1.0", default-features = false }
-//! serde_cbor = { version = "0.10", default-features = false }
-//! ```
-//!
-//! *Note*: to use derive macros in serde you will need to declare `serde`
-//! dependency like so:
-//! ``` toml
-//! serde = { version = "1.0", default-features = false, features = ["derive"] }
-//! ```
-//!
-//! Serialize an object.
-//! ``` rust
-//! # #[macro_use] extern crate serde_derive;
-//! # fn main() -> Result<(), serde_cbor::Error> {
-//! use serde::Serialize;
-//! use serde_cbor::Serializer;
-//! use serde_cbor::ser::SliceWrite;
-//!
-//! #[derive(Serialize)]
-//! struct User {
-//!     user_id: u32,
-//!     password_hash: [u8; 4],
-//! }
-//!
-//! let mut buf = [0u8; 100];
-//! let writer = SliceWrite::new(&mut buf[..]);
-//! let mut ser = Serializer::new(writer);
-//! let user = User {
-//!     user_id: 42,
-//!     password_hash: [1, 2, 3, 4],
-//! };
-//! user.serialize(&mut ser)?;
-//! let writer = ser.into_inner();
-//! let size = writer.bytes_written();
-//! let expected = [
-//!     0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
-//!     0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
-//!     0x68, 0x84, 0x1, 0x2, 0x3, 0x4
-//! ];
-//! assert_eq!(&buf[..size], expected);
-//! # Ok(())
-//! # }
-//! ```
-//!
-//! Deserialize an object.
-//! ``` rust
-//! # #[macro_use] extern crate serde_derive;
-//! # fn main() -> Result<(), serde_cbor::Error> {
-//! #[derive(Debug, PartialEq, Deserialize)]
-//! struct User {
-//!     user_id: u32,
-//!     password_hash: [u8; 4],
-//! }
-//!
-//! let value = [
-//!     0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
-//!     0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
-//!     0x68, 0x84, 0x1, 0x2, 0x3, 0x4
-//! ];
-//!
-//! // from_slice_with_scratch will not alter input data, use it whenever you
-//! // borrow from somewhere else.
-//! // You will have to size your scratch according to the input data you
-//! // expect.
-//! use serde_cbor::de::from_slice_with_scratch;
-//! let mut scratch = [0u8; 32];
-//! let user: User = from_slice_with_scratch(&value[..], &mut scratch)?;
-//! assert_eq!(user, User {
-//!     user_id: 42,
-//!     password_hash: [1, 2, 3, 4],
-//! });
-//!
-//! let mut value = [
-//!     0xa2, 0x67, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x6d,
-//!     0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x68, 0x61, 0x73,
-//!     0x68, 0x84, 0x1, 0x2, 0x3, 0x4
-//! ];
-//!
-//! // from_mut_slice will move data around the input slice, you may only use it
-//! // on data you may own or can modify.
-//! use serde_cbor::de::from_mut_slice;
-//! let user: User = from_mut_slice(&mut value[..])?;
-//! assert_eq!(user, User {
-//!     user_id: 42,
-//!     password_hash: [1, 2, 3, 4],
-//! });
-//! # Ok(())
-//! # }
-//! ```
-//!
 //! # Limitations
 //!
 //! While Serde CBOR strives to support all features of Serde and CBOR
diff --git a/src/ser.rs b/src/ser.rs
index 031177c..59b5a09 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -35,7 +35,7 @@ where
 
 /// Serializes a value to a writer.
 #[cfg(feature = "std")]
-pub fn to_writer<W, T>(writer: W, value: &T) -> Result<()>
+pub fn to_writer<W, T>(writer: &mut W, value: &T) -> Result<()>
 where
     W: io::Write,
     T: ser::Serialize,
diff --git a/src/write.rs b/src/write.rs
index aa8d3df..f6e1579 100644
--- a/src/write.rs
+++ b/src/write.rs
@@ -67,18 +67,18 @@ 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.
-pub struct IoWrite<W>(W);
+pub struct IoWrite<'a, W>(&'a mut W);
 
 #[cfg(feature = "std")]
-impl<W: io::Write> IoWrite<W> {
+impl<'a, W: io::Write> IoWrite<'a, W> {
     /// Wraps an `io::Write` writer to make it compatible with [`Write`](trait.Write.html)
-    pub fn new(w: W) -> IoWrite<W> {
+    pub fn new(w: &'a mut W) -> IoWrite<'a, W> {
         IoWrite(w)
     }
 }
 
 #[cfg(feature = "std")]
-impl<W: io::Write> Write for IoWrite<W> {
+impl<'a, W: io::Write> Write for IoWrite<'a, W> {
     type Error = io::Error;
 
     fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
@@ -87,7 +87,7 @@ impl<W: io::Write> Write for IoWrite<W> {
 }
 
 #[cfg(all(feature = "std", not(feature = "unsealed_read_write")))]
-impl<W> private::Sealed for IoWrite<W> where W: io::Write {}
+impl<'a, W> private::Sealed for IoWrite<'a, W> where W: io::Write {}
 
 // TODO this should be possible with just alloc
 #[cfg(feature = "std")]


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);
+    }
+
 }