Repository: pyfisch/cbor
Pull request #81 (2018-11-20 08:41:27)

[WIP] Move scratch buffers into readers
This PR describes an idea of how to one might continue after #80, and one answer to the question of whether `scratch_offset` can go away there.

The meat of this PR is 86a51c3762d7daabc214f3621e0d63ed8cd91b4f, 18e254623a39c0c4b0fb69f13181a18f37c9ea70 and 9e123f7cdb4f9492e922c6af72fe314dc3f7b148 (there's a few before because I can't create a PR in github that's based on another PR) which may also make sense to read as a cumulative diff; between them, there is 6fa55802f6a1ae7ce34291ce6aa107a4d6981476 that describes a possible application, and then interfacing and test coverage.

What the patch does is to move the scratch buffer from the deserializer to the the Read trait; each reader can manage a buffer as it seems fit. The Read trait methods change a bit:
* read() now always returns a slice, but while earlier it gave an Enum telling you to either take the 'de long-lived result or to have a short-lived peek into the buffer, it now returns an Enum that always contains a slice but has variable lifetimes.
* For gathering a long slice, the's now the `clear_buffer()` / `read_to_buffer()` / `view_buffer` trio, which exposes functionality previously managed by the decoder's buf (`buf.clear()`, `.read(n, buf)`, `buf.as_ref()`) -- that allows implementing MutSliceRead to manage its own scratch buffer by shuffling data around in its slice. For consistency, `.view_buffer()` returns the same enum as `.read()`. (This allows indefinite strings from a MutSliceRead to go the `visit_borrowed_bytes` route).

The rationale for these changes is twofold:
* They (or at least the first part thereof in 86a51c3762d7daabc214f3621e0d63ed8cd91b4f) allow implementing a MutSliceRead, and thus decoding any CBOR in a no_std (and not even alloc) environment.
* They reduce the `#[cfg(...)]` gating that'd be necessary to make serde_cbor ready for no_std because the individual readers now can take the cfg decisions. (And even there it's fewer places, as the Read trait can stay the same, IoRead needs to be gated anyway, and only SliceRead needs to decide whether it can do `.read_to_buffer()` or not.

pyfisch: Thanks.

Can you rebase it on master. I think will make review easier.
chrysn: Rebased and slightly simplified (in terms of back and forth between function names).

I'd still call this PR "work in progress" because the MutSliceRead implementation is added in the middle of them (which makes sense from a rationale and story perspective), but I'm not sure whether you're OK with having unsafe parts in this library. Other than that, I think it's good for review. (And if you conceptually like the changes but want MutSliceRead to wait for a dedicated unsafe-wrapping crate, I'll happily rebase it out.)
pyfisch: > error[E0433]: failed to resolve: use of undeclared type or module `Reference`
   --> src/read.rs:138:23
    |
138 |             return Ok(Reference::Borrowed(&[]));
    |                       ^^^^^^^^^ use of undeclared type or module `Reference`
chrysn: I should have known better than to rebase and not to check the ensuing trivial changes.

The error is now fixed by a better rebase, and I've incorporated two other small fixes in the patch set right away (`view_buffer` had a needlessly mutable self, and turning the `conver_str` into an associated function was insufficiently motivated).

As for the the `convert_str` rationale, I might add here: Having `read` return `EitherLifetime` is IMO safer w/rt the usage pattern -- it doesn't demand of the caller to follow the `clear` / `read_to_buffer` / `view_buffer` sequence that a caller of `read_to_buffer` needs to follow, and allows for much consistency between `view_buffer` and `read`. The downside of this is that a reader is mutably borrowed while a `read`'s results are being processed, necessitating the `convert_str` to become an associated method. If you'd rather have it another way (with `.read()` returning something more like the old `Reference` and a dedicated `.view_buffer()` if it's a copy), I can rearrange that -- my opinion is that the way in the PR is the slightly clearer one.
chrysn: Addressing the travis failure: Is 1.18 the earliest version that's targeted for support? Then I go check in detail whether it's anything else than the MutSliceRead that fails with those versions.
pyfisch: > Addressing the travis failure: Is 1.18 the earliest version that's targeted for support?

You can update the minimum version to 1.30. It is mainly there so I am notified of changes for old versions of rust.
chrysn: Sorry I missed that the ball was in my court since the last update. Added f1884a95b4e2776b15eb2184dfaf083cb2fd8b3a to bump the "old" version travis tests with; checks pass now.
chrysn: The last commit added atop this changes MutSliceRead to use the [SealingSlice](https://crates.io/crates/sealingslice) abstraction created with this application in mind. I'm fully open on whether or not you'd like to go that route (having an effectively 12-lines-of-code (but much more to test it) dependency rather than having a single "unsafe" in the own code), I would.
chrysn: I'm retracting the last change that'd work on SealingSlice; a [review on that crate](https://gitlab.com/chrysn/sealingslice/issues/1) pointed me towards a way to implement what we need here (not all of SealingSlice, but that overreached) in a safe way (ie. without bespoke unsafe code either in this crate or sealingslice). This changes the internal interface introduced in this PR again slightly, but was left as commits atop the patch series.

If you haven't started reviewing yet, I can squash that API change back into sequence, but so far left the two top-most commits (89cef504ef9d81fa56ca70d3fc663e56e0763da0 and 964c454bb672d6502a520cbea15efafadbc487c8) as individual ones to be better readable as a patch. When those are squashed, `.view_buffer()` will be renamed to `.take_buffer()` or similar to reflect the slightly changed semantics.

Patch:
diff --git a/.travis.yml b/.travis.yml
index e098eed..9bbdb43 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
 language: rust
 rust:
-  - 1.18.0
+  - 1.26.0
   - stable
   - beta
   - nightly
diff --git a/src/de.rs b/src/de.rs
index f96e7ca..1309532 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -10,8 +10,8 @@ use std::result;
 use std::marker::PhantomData;
 
 use error::{Error, Result, ErrorCode};
-use read::Reference;
-pub use read::{Read, IoRead, SliceRead};
+use read::EitherLifetime;
+pub use read::{Read, IoRead, SliceRead, MutSliceRead};
 
 /// Decodes a value from CBOR data in a slice.
 ///
@@ -44,6 +44,21 @@ where
     Ok(value)
 }
 
+/// 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
+/// mutability to rearrange data in it in order to resolve indefinite byte or text strings without
+/// resorting to allocations.
+pub fn from_mut_slice<'a, T>(slice: &'a mut [u8]) -> Result<T>
+where
+    T: de::Deserialize<'a>,
+{
+    let mut deserializer = Deserializer::from_mut_slice(slice);
+    let value = de::Deserialize::deserialize(&mut deserializer)?;
+    deserializer.end()?;
+    Ok(value)
+}
+
 /// Decodes a value from CBOR data in a reader.
 ///
 /// # Examples
@@ -79,7 +94,6 @@ where
 /// A Serde `Deserialize`r of CBOR data.
 pub struct Deserializer<R> {
     read: R,
-    buf: Vec<u8>,
     remaining_depth: u8,
 }
 
@@ -102,6 +116,16 @@ impl<'a> Deserializer<SliceRead<'a>> {
     }
 }
 
+impl<'a> Deserializer<MutSliceRead<'a>> {
+    /// Constructs a `Deserializer` which reads from a mutable slice that doubles as its own
+    /// scratch buffer.
+    ///
+    /// Borrowed strings and byte slices will be provided even for indefinite strings.
+    pub fn from_mut_slice(bytes: &'a mut [u8]) -> Deserializer<MutSliceRead<'a>> {
+        Deserializer::new(MutSliceRead::new(bytes))
+    }
+}
+
 impl<'de, R> Deserializer<R>
 where
     R: Read<'de>,
@@ -112,7 +136,6 @@ where
     pub fn new(read: R) -> Self {
         Deserializer {
             read,
-            buf: Vec::new(),
             remaining_depth: 128,
         }
     }
@@ -184,16 +207,16 @@ where
     where
         V: de::Visitor<'de>,
     {
-        self.buf.clear();
-        match self.read.read(len, &mut self.buf, 0)? {
-            Reference::Borrowed(buf) => visitor.visit_borrowed_bytes(buf),
-            Reference::Copied => visitor.visit_bytes(&self.buf),
+        match self.read.read(len)? {
+            EitherLifetime::Long(buf) => visitor.visit_borrowed_bytes(buf),
+            EitherLifetime::Short(buf) => visitor.visit_bytes(buf),
         }
     }
 
-    fn parse_indefinite_bytes(&mut self) -> Result<&[u8]> {
-        let mut offset = 0;
-        self.buf.clear();
+    fn parse_indefinite_bytes<V>(&mut self, visitor: V) -> Result<V::Value> where
+        V: de::Visitor<'de>,
+    {
+        self.read.clear_buffer();
         loop {
             let byte = self.parse_u8()?;
             let len = match byte {
@@ -212,29 +235,21 @@ where
                 _ => return Err(self.error(ErrorCode::UnexpectedCode)),
             };
 
-            match self.read.read(len, &mut self.buf, offset)? {
-                Reference::Borrowed(buf) => {
-                    let new_len = offset + len;
-                    if new_len > self.buf.len() {
-                        self.buf.resize(new_len, 0);
-                    }
-                    self.buf[offset..].copy_from_slice(buf);
-                }
-                Reference::Copied => {}
-            }
-
-            offset += len;
+            self.read.read_to_buffer(len)?;
         }
 
-        Ok(&self.buf[..offset])
+        match self.read.view_buffer() {
+            EitherLifetime::Long(buf) => visitor.visit_borrowed_bytes(buf),
+            EitherLifetime::Short(buf) => visitor.visit_bytes(buf),
+        }
     }
 
-    fn convert_str<'a>(&self, buf: &'a [u8]) -> Result<&'a str> {
+    fn convert_str<'a>(buf: &'a [u8], buf_end_offset: u64) -> Result<&'a str> {
         match str::from_utf8(buf) {
             Ok(s) => Ok(s),
             Err(e) => {
                 let shift = buf.len() - e.valid_up_to();
-                let offset = self.read.offset() - shift as u64;
+                let offset = buf_end_offset - shift as u64;
                 Err(Error::syntax(ErrorCode::InvalidUtf8, offset))
             }
         }
@@ -244,22 +259,23 @@ where
     where
         V: de::Visitor<'de>,
     {
-        self.buf.clear();
-        match self.read.read(len, &mut self.buf, 0)? {
-            Reference::Borrowed(buf) => {
-                let s = self.convert_str(buf)?;
+        let offset = self.read.offset() + len as u64;
+        match self.read.read(len)? {
+            EitherLifetime::Long(buf) => {
+                let s = Self::convert_str(buf, offset)?;
                 visitor.visit_borrowed_str(s)
             }
-            Reference::Copied => {
-                let s = self.convert_str(&self.buf)?;
+            EitherLifetime::Short(buf) => {
+                let s = Self::convert_str(buf, offset)?;
                 visitor.visit_str(s)
             }
         }
     }
 
-    fn parse_indefinite_str(&mut self) -> Result<&str> {
-        let mut offset = 0;
-        self.buf.clear();
+    fn parse_indefinite_str<V>(&mut self, visitor: V) -> Result<V::Value> where
+        V: de::Visitor<'de>,
+    {
+        self.read.clear_buffer();
         loop {
             let byte = self.parse_u8()?;
             let len = match byte {
@@ -278,21 +294,20 @@ where
                 _ => return Err(self.error(ErrorCode::UnexpectedCode)),
             };
 
-            match self.read.read(len, &mut self.buf, offset)? {
-                Reference::Borrowed(buf) => {
-                    let new_len = offset + len;
-                    if new_len > self.buf.len() {
-                        self.buf.resize(new_len, 0);
-                    }
-                    self.buf[offset..].copy_from_slice(buf);
-                }
-                Reference::Copied => {}
-            }
-
-            offset += len;
+            self.read.read_to_buffer(len)?;
         }
 
-        self.convert_str(&self.buf[..offset])
+        let offset = self.read.offset();
+        match self.read.view_buffer() {
+            EitherLifetime::Long(buf) => {
+                let s = Self::convert_str(buf, offset)?;
+                visitor.visit_borrowed_str(s)
+            }
+            EitherLifetime::Short(buf) => {
+                let s = Self::convert_str(buf, offset)?;
+                visitor.visit_str(s)
+            }
+        }
     }
 
     fn recursion_checked<F, T>(&mut self, f: F) -> Result<T>
@@ -383,6 +398,24 @@ where
         })
     }
 
+    fn parse_enum_map<V>(&mut self, visitor: V) -> Result<V::Value>
+    where
+        V: de::Visitor<'de>,
+    {
+        self.recursion_checked(|de| {
+            let mut len = 1;
+            let value = visitor.visit_enum(VariantAccessMap {
+                map: MapAccess { de, len: &mut len },
+            })?;
+
+            if len != 0 {
+                Err(de.error(ErrorCode::TrailingData))
+            } else {
+                Ok(value)
+            }
+        })
+    }
+
     fn parse_indefinite_enum<V>(&mut self, visitor: V) -> Result<V::Value>
     where
         V: de::Visitor<'de>,
@@ -487,8 +520,7 @@ where
             }
             0x5c...0x5e => Err(self.error(ErrorCode::UnassignedCode)),
             0x5f => {
-                let bytes = self.parse_indefinite_bytes()?;
-                visitor.visit_bytes(bytes)
+                self.parse_indefinite_bytes(visitor)
             }
 
             // Major type 3: a text string
@@ -514,8 +546,7 @@ where
             }
             0x7c...0x7e => Err(self.error(ErrorCode::UnassignedCode)),
             0x7f => {
-                let s = self.parse_indefinite_str()?;
-                visitor.visit_str(s)
+                self.parse_indefinite_str(visitor)
             }
 
             // Major type 4: an array of data items
@@ -693,6 +724,10 @@ where
                     _ => unreachable!(),
                 }
             }
+            Some(0xa1) => {
+                self.consume();
+                self.parse_enum_map(visitor)
+            }
             None => Err(self.error(ErrorCode::EofWhileParsingValue)),
             _ => visitor.visit_enum(UnitVariantAccess { de: self }),
         }
@@ -822,6 +857,15 @@ where
     }
 }
 
+impl<'de, 'a, R> MakeError for MapAccess<'a, R>
+where
+    R: Read<'de>,
+{
+    fn error(&self, code: ErrorCode) -> Error {
+        self.de.error(code)
+    }
+}
+
 struct IndefiniteMapAccess<'a, R: 'a> {
     de: &'a mut Deserializer<R>,
 }
@@ -1049,3 +1093,65 @@ where
         }
     }
 }
+
+struct VariantAccessMap<T> {
+    map: T,
+}
+
+impl<'de, T> de::EnumAccess<'de> for VariantAccessMap<T>
+where
+    T: de::MapAccess<'de, Error = Error> + MakeError,
+{
+    type Error = Error;
+    type Variant = VariantAccessMap<T>;
+
+    fn variant_seed<V>(mut self, seed: V) -> Result<(V::Value, VariantAccessMap<T>)>
+    where
+        V: de::DeserializeSeed<'de>,
+    {
+        let variant = match self.map.next_key_seed(seed) {
+            Ok(Some(variant)) => variant,
+            Ok(None) => return Err(self.map.error(ErrorCode::ArrayTooShort)),
+            Err(e) => return Err(e),
+        };
+        Ok((variant, self))
+    }
+}
+
+impl<'de, T> de::VariantAccess<'de> for VariantAccessMap<T>
+where
+    T: de::MapAccess<'de, Error = Error>
+        + MakeError,
+{
+    type Error = Error;
+
+    fn unit_variant(mut self) -> Result<()> {
+        match self.map.next_value() {
+            Ok(()) => Ok(()),
+            Err(e) => Err(e),
+        }
+    }
+
+    fn newtype_variant_seed<S>(mut self, seed: S) -> Result<S::Value>
+    where
+        S: de::DeserializeSeed<'de>,
+    {
+        self.map.next_value_seed(seed)
+    }
+
+    fn tuple_variant<V>(mut self, _len: usize, visitor: V) -> Result<V::Value>
+    where
+        V: de::Visitor<'de>,
+    {
+        let seed = StructVariantSeed { visitor };
+        self.map.next_value_seed(seed)
+    }
+
+    fn struct_variant<V>(mut self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
+    where
+        V: de::Visitor<'de>,
+    {
+        let seed = StructVariantSeed { visitor };
+        self.map.next_value_seed(seed)
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 837af77..e669ae6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -160,8 +160,8 @@ pub mod ser;
 pub mod value;
 
 #[doc(inline)]
-pub use de::{from_slice, from_reader, Deserializer, StreamDeserializer};
+pub use de::{from_slice, from_mut_slice, from_reader, Deserializer, StreamDeserializer};
 #[doc(inline)]
-pub use ser::{to_writer, to_vec, Serializer};
+pub use ser::{to_writer, to_vec, to_vec_with_options, Serializer, SerializerOptions};
 #[doc(inline)]
 pub use value::{Value, ObjectKey, to_value, from_value};
diff --git a/src/read.rs b/src/read.rs
index 2a837a6..ed004fa 100644
--- a/src/read.rs
+++ b/src/read.rs
@@ -1,5 +1,6 @@
 use std::cmp;
 use std::io::{self, Read as StdRead};
+use std::mem;
 
 use error::{Result, Error, ErrorCode};
 
@@ -13,12 +14,42 @@ pub trait Read<'de>: private::Sealed {
     fn peek(&mut self) -> io::Result<Option<u8>>;
 
     #[doc(hidden)]
-    fn read(
-        &mut self,
+    /// Read n bytes from the input.
+    ///
+    /// Implementations that can are asked to return a slice with a Long lifetime that outlives the
+    /// decoder, but others (eg. ones that need to allocate the data into a temporary buffer) can
+    /// return it with a Short lifetime that just lives for the time of read's mutable borrow of
+    /// the reader.
+    ///
+    /// This may, as a side effect, clear the reader's scratch buffer (as the provided
+    /// implementation does).
+    ///
+    /// A more appropriate lifetime setup for this (that would allow the Deserializer::convert_str
+    /// to stay a function) would be something like `fn read<'a, 'r: 'a>(&'a mut 'r immut self, ...) -> ...
+    /// EitherLifetime<'r, 'de>>`, which borrows self mutably for the duration of the function and
+    /// downgrates that reference to an immutable one that outlives the result (protecting the
+    /// scratch buffer from changes), but alas, that can't be expressed (yet?).
+    fn read<'a>(
+        &'a mut self,
         n: usize,
-        scratch: &mut Vec<u8>,
-        scratch_offset: usize,
-    ) -> Result<Reference<'de>>;
+    ) -> Result<EitherLifetime<'a, 'de>> {
+        self.clear_buffer();
+        self.read_to_buffer(n)?;
+
+        Ok(self.view_buffer())
+    }
+
+    #[doc(hidden)]
+    fn clear_buffer(&mut self);
+
+    #[doc(hidden)]
+    /// Append n bytes from the reader to the reader's scratch buffer (without clearing it)
+    fn read_to_buffer(&mut self, n: usize) -> Result<()>;
+
+    #[doc(hidden)]
+    /// Read out everything accumulated in the reader's scratch buffer. This may, as a side effect,
+    /// clear it.
+    fn view_buffer<'a>(&'a mut self) -> EitherLifetime<'a, 'de>;
 
     #[doc(hidden)]
     fn read_into(&mut self, buf: &mut [u8]) -> Result<()>;
@@ -30,9 +61,9 @@ pub trait Read<'de>: private::Sealed {
     fn offset(&self) -> u64;
 }
 
-pub enum Reference<'b> {
-    Borrowed(&'b [u8]),
-    Copied,
+pub enum EitherLifetime<'short, 'long> {
+    Short(&'short [u8]),
+    Long(&'long [u8]),
 }
 
 mod private {
@@ -45,6 +76,7 @@ where
     R: io::Read,
 {
     reader: OffsetReader<R>,
+    scratch: Vec<u8>,
     ch: Option<u8>,
 }
 
@@ -59,6 +91,7 @@ where
                 reader,
                 offset: 0,
             },
+            scratch: vec![],
             ch: None,
         }
     }
@@ -106,22 +139,21 @@ where
         }
     }
 
-    fn read(
-        &mut self,
-        mut n: usize,
-        scratch: &mut Vec<u8>,
-        scratch_offset: usize,
-    ) -> Result<Reference<'de>> {
-        assert!(scratch.len() == scratch_offset);
-
+    fn read_to_buffer(&mut self, mut n: usize) -> Result<()> {
         // defend against malicious input pretending to be huge strings by limiting growth
-        scratch.reserve(cmp::min(n, 16 * 1024));
+        self.scratch.reserve(cmp::min(n, 16 * 1024));
+
+        if n == 0 {
+            return Ok(())
+        }
 
         if let Some(ch) = self.ch.take() {
-            scratch.push(ch);
+            self.scratch.push(ch);
             n -= 1;
         }
 
+        // n == 0 is OK here and needs no further special treatment
+
         let transfer_result = {
             // Prepare for take() (which consumes its reader) by creating a reference adaptor
             // that'll only live in this block
@@ -129,11 +161,11 @@ where
             // Append the first n bytes of the reader to the scratch vector (or up to
             // an error or EOF indicated by a shorter read)
             let mut taken = reference.take(n as u64);
-            taken.read_to_end(scratch)
+            taken.read_to_end(&mut self.scratch)
         };
 
         match transfer_result {
-            Ok(r) if r == n => Ok(Reference::Copied),
+            Ok(r) if r == n => Ok(()),
             Ok(_) => Err(Error::syntax(
                     ErrorCode::EofWhileParsingValue,
                     self.offset(),
@@ -142,6 +174,14 @@ where
         }
     }
 
+    fn clear_buffer(&mut self) {
+        self.scratch.clear();
+    }
+
+    fn view_buffer<'a>(&'a mut self) -> EitherLifetime<'a, 'de> {
+        EitherLifetime::Short(&self.scratch)
+    }
+
     fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
         self.reader.read_exact(buf).map_err(|e| {
             if e.kind() == io::ErrorKind::UnexpectedEof {
@@ -184,6 +224,7 @@ where
 /// A CBOR input source that reads from a slice of bytes.
 pub struct SliceRead<'a> {
     slice: &'a [u8],
+    scratch: Vec<u8>,
     index: usize,
 }
 
@@ -192,6 +233,7 @@ impl<'a> SliceRead<'a> {
     pub fn new(slice: &'a [u8]) -> SliceRead<'a> {
         SliceRead {
             slice,
+            scratch: vec![],
             index: 0,
         }
     }
@@ -232,12 +274,29 @@ impl<'a> Read<'a> for SliceRead<'a> {
         })
     }
 
+    fn clear_buffer(&mut self) {
+        self.scratch.clear();
+    }
+
+    fn read_to_buffer(&mut self, n: usize) -> Result<()> {
+        let end = self.end(n)?;
+        let slice = &self.slice[self.index..end];
+        self.scratch.extend_from_slice(slice);
+        self.index = end;
+
+        Ok(())
+    }
+
     #[inline]
-    fn read(&mut self, n: usize, _: &mut Vec<u8>, _: usize) -> Result<Reference<'a>> {
+    fn read<'b>(&'b mut self, n: usize) -> Result<EitherLifetime<'b, 'a>> {
         let end = self.end(n)?;
         let slice = &self.slice[self.index..end];
         self.index = end;
-        Ok(Reference::Borrowed(slice))
+        Ok(EitherLifetime::Long(slice))
+    }
+
+    fn view_buffer<'b>(&'b mut self) -> EitherLifetime<'b, 'a> {
+        EitherLifetime::Short(&self.scratch)
     }
 
     #[inline]
@@ -257,3 +316,114 @@ impl<'a> Read<'a> for SliceRead<'a> {
         self.index as u64
     }
 }
+
+/// 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.
+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.
+    slice: &'a mut [u8],
+    /// Read cursor position in slice
+    index: usize,
+    /// Number of bytes already discarded from the slice
+    before: usize,
+    /// End of the buffer area that contains all bytes read_into_buffer. This is always <= index.
+    buffer_end: usize,
+}
+
+impl<'a> MutSliceRead<'a> {
+    /// Creates a CBOR input source to read from a slice of bytes.
+    pub fn new(slice: &'a mut [u8]) -> MutSliceRead<'a> {
+        MutSliceRead {
+            slice,
+            index: 0,
+            before: 0,
+            buffer_end: 0,
+        }
+    }
+
+    fn end(&self, n: usize) -> Result<usize> {
+        match self.index.checked_add(n) {
+            Some(end) if end <= self.slice.len() => Ok(end),
+            _ => {
+                Err(Error::syntax(
+                    ErrorCode::EofWhileParsingValue,
+                    self.slice.len() as u64,
+                ))
+            }
+        }
+    }
+}
+
+impl<'a> private::Sealed for MutSliceRead<'a> {}
+
+impl<'a> Read<'a> for MutSliceRead<'a> {
+    #[inline]
+    fn next(&mut self) -> io::Result<Option<u8>> {
+        // This is duplicated from SliceRead, can that be eased?
+        Ok(if self.index < self.slice.len() {
+            let ch = self.slice[self.index];
+            self.index += 1;
+            Some(ch)
+        } else {
+            None
+        })
+    }
+
+    #[inline]
+    fn peek(&mut self) -> io::Result<Option<u8>> {
+        // This is duplicated from SliceRead, can that be eased?
+        Ok(if self.index < self.slice.len() {
+            Some(self.slice[self.index])
+        } else {
+            None
+        })
+    }
+
+    fn clear_buffer<'b>(&'b mut self) {
+        self.slice = &mut mem::replace(&mut self.slice, &mut [])[self.index..];
+        self.before += self.index;
+        self.index = 0;
+        self.buffer_end = 0;
+    }
+
+    fn read_to_buffer(&mut self, n: usize) -> Result<()> {
+        let end = self.end(n)?;
+        debug_assert!(self.buffer_end <= self.index, "MutSliceRead invariant violated: scratch buffer exceeds index");
+        self.slice[self.buffer_end..end].rotate_left(self.index - self.buffer_end);
+        self.buffer_end += n;
+        self.index = end;
+
+        Ok(())
+    }
+
+    fn view_buffer<'b>(&'b mut self) -> EitherLifetime<'b, 'a> {
+        let (left, right) = mem::replace(&mut self.slice, &mut []).split_at_mut(self.index);
+        self.slice = right;
+        self.before += self.index;
+        self.index = 0;
+
+        let left = &left[..self.buffer_end];
+        self.buffer_end = 0;
+
+        EitherLifetime::Long(left)
+    }
+
+    #[inline]
+    fn read_into(&mut self, buf: &mut [u8]) -> Result<()> {
+        // This is duplicated from SliceRead, can that be eased?
+        let end = self.end(buf.len())?;
+        buf.copy_from_slice(&self.slice[self.index..end]);
+        self.index = end;
+        Ok(())
+    }
+
+    #[inline]
+    fn discard(&mut self) {
+        self.index += 1;
+    }
+
+    fn offset(&self) -> u64 {
+        (self.before + self.index) as u64
+    }
+}
diff --git a/src/ser.rs b/src/ser.rs
index 3329675..57b9a95 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -97,10 +97,83 @@ where
     Ok(vec)
 }
 
+/// Serializes a value to a vector.
+pub fn to_vec_with_options<T>(value: &T, options: &SerializerOptions) -> Result<Vec<u8>>
+where
+    T: ser::Serialize,
+{
+    let mut vec = Vec::new();
+    {
+        let mut ser = Serializer::new_with_options(&mut vec, options);
+        if options.self_describe {
+            ser.self_describe()?;
+        }
+        value.serialize(&mut ser)?;
+    }
+    Ok(vec)
+}
+
+/// Options for a CBOR serializer.
+///
+/// The `enum_as_map` option determines how enums are encoded.
+///
+/// This makes no difference when encoding and decoding enums using
+/// this crate, but it shows up when decoding to a `Value` or decoding
+/// in other languages.
+///
+/// With enum_as_map true, the encoding scheme matches the default encoding
+/// scheme used by `serde_json`.
+///
+/// # Examples
+///
+/// Given the following enum
+/// ```
+/// enum Enum {
+///     Unit,
+///     NewType(i32),
+///     Tuple(String, bool),
+///     Struct{ x: i32, y: i32 },
+/// }
+/// ```
+/// we will give the `Value` with the same encoding for each case using
+/// JSON notation.
+///
+/// ## Default encodings
+///
+/// * `Enum::Unit` encodes as `"Unit"`
+/// * `Enum::NewType(10)` encodes as `["NewType", 10]`
+/// * `Enum::Tuple("x", true)` encodes as `["Tuple", "x", true]`
+/// * `Enum::Struct{ x: 5, y: -5 }` encodes as `["Struct", {"x": 5, "y": -5}]`
+///
+/// ## Encodings with enum_as_map true
+///
+/// * `Enum::Unit` encodes as `"Unit"`
+/// * `Enum::NewType(10)` encodes as `{"NewType": 10}`
+/// * `Enum::Tuple("x", true)` encodes as `{"Tuple": ["x", true]}`
+/// * `Enum::Struct{ x: 5, y: -5 }` encodes as `{"Struct": {"x": 5, "y": -5}}`
+#[derive(Default)]
+pub struct SerializerOptions {
+    /// When set, struct fields and enum variants are identified by their numeric indices rather than names
+    /// to save space.
+    pub packed: bool,
+    /// When set, enums are encoded as maps rather than arrays.
+    pub enum_as_map: bool,
+    /// When set, `to_vec` will prepend the CBOR self-describe tag.
+    pub self_describe: bool,
+}
+
+impl SerializerOptions {
+    /// Serializes a value to a vector.
+    pub fn to_vec<T: ser::Serialize>(&self, value: &T) -> Result<Vec<u8>> {
+        to_vec_with_options(value, self)
+    }
+}
+
 /// A structure for serializing Rust values to CBOR.
 pub struct Serializer<W> {
     writer: W,
     packed: bool,
+    enum_as_map: bool,
 }
 
 impl<W> Serializer<W>
@@ -113,6 +186,7 @@ where
         Serializer {
             writer,
             packed: false,
+            enum_as_map: false,
         }
     }
 
@@ -125,6 +199,17 @@ where
         Serializer {
             writer,
             packed: true,
+            enum_as_map: false,
+        }
+    }
+
+    /// Creates a new CBOR serializer with the specified options.
+    #[inline]
+    pub fn new_with_options(writer: W, options: &SerializerOptions) -> Serializer<W> {
+        Serializer {
+            writer,
+            packed: options.packed,
+            enum_as_map: options.enum_as_map,
         }
     }
 
@@ -133,6 +218,7 @@ where
         let mut s = Serializer {
             writer: buf,
             packed: self.packed,
+            enum_as_map: self.enum_as_map,
         };
         v.serialize(&mut s)?;
         Ok(s.writer)
@@ -406,8 +492,13 @@ where
     where
         T: ?Sized + ser::Serialize,
     {
-        self.writer.write_all(&[4 << 5 | 2]).map_err(Error::io)?;
-        self.serialize_unit_variant(name, variant_index, variant)?;
+        if self.enum_as_map {
+            self.write_u64(5, 1u64)?;
+            variant.serialize(&mut *self)?;
+        } else {
+            self.writer.write_all(&[4 << 5 | 2]).map_err(Error::io)?;
+            self.serialize_unit_variant(name, variant_index, variant)?;
+        }
         value.serialize(self)
     }
 
@@ -439,9 +530,15 @@ where
         variant: &'static str,
         len: usize,
     ) -> Result<&'a mut Serializer<W>> {
-        self.write_u64(4, (len + 1) as u64)?;
-        self.serialize_unit_variant(name, variant_index, variant)?;
-        Ok(self)
+        if self.enum_as_map {
+            self.write_u64(5, 1u64)?;
+            variant.serialize(&mut *self)?;
+            self.serialize_tuple(len)
+        } else {
+            self.write_u64(4, (len + 1) as u64)?;
+            self.serialize_unit_variant(name, variant_index, variant)?;
+            Ok(self)
+        }
     }
 
     #[inline]
@@ -492,7 +589,11 @@ where
         variant: &'static str,
         len: usize,
     ) -> Result<StructSerializer<'a, W>> {
-        self.writer.write_all(&[4 << 5 | 2]).map_err(Error::io)?;
+        if self.enum_as_map {
+            self.write_u64(5, 1u64)?;
+        } else {
+            self.writer.write_all(&[4 << 5 | 2]).map_err(Error::io)?;
+        }
         self.serialize_unit_variant(name, variant_index, variant)?;
         self.serialize_struct(name, len)
     }


Test patch:
diff --git a/tests/de.rs b/tests/de.rs
index a78c97b..86c95f1 100644
--- a/tests/de.rs
+++ b/tests/de.rs
@@ -115,8 +115,12 @@ fn test_indefinite_byte_string() {
 
 #[test]
 fn test_multiple_indefinite_strings() {
+    let input = b"\x82\x7f\x65Mary \x64Had \x62a \x67Little \x60\x64Lamb\xff\x5f\x42\x01\x23\x42\x45\x67\xff";
+    _test_multiple_indefinite_strings(de::from_slice(input));
+    _test_multiple_indefinite_strings(de::from_mut_slice(input.to_vec().as_mut()));
+}
+fn _test_multiple_indefinite_strings(value: error::Result<Value>) {
     // This assures that buffer rewinding in infinite buffers works as intended.
-    let value: error::Result<Value> = de::from_slice(b"\x82\x7f\x65Mary \x64Had \x62a \x67Little \x60\x64Lamb\xff\x5f\x42\x01\x23\x42\x45\x67\xff");
     assert_eq!(value.unwrap(), Value::Array(vec![
         Value::String("Mary Had a Little Lamb".to_owned()),
         Value::Bytes(b"\x01#Eg".to_vec())
@@ -254,6 +258,10 @@ fn stream_deserializer_eof_in_indefinite() {
     for end_of_slice in indices {
         let mut it = Deserializer::from_slice(&slice[..*end_of_slice]).into_iter::<Value>();
         assert!(it.next().unwrap().unwrap_err().is_eof());
+
+        let mut mutcopy = slice[..*end_of_slice].to_vec();
+        let mut it = Deserializer::from_mut_slice(mutcopy.as_mut()).into_iter::<Value>();
+        assert!(it.next().unwrap().unwrap_err().is_eof());
     }
 }
 
diff --git a/tests/enum.rs b/tests/enum.rs
index 52eff8f..8bd6540 100644
--- a/tests/enum.rs
+++ b/tests/enum.rs
@@ -3,7 +3,9 @@ extern crate serde_cbor;
 #[macro_use]
 extern crate serde_derive;
 
-use serde_cbor::{from_slice, to_vec};
+use std::collections::BTreeMap;
+
+use serde_cbor::{from_slice, to_vec, Value, ObjectKey};
 
 #[derive(Debug,Serialize,Deserialize,PartialEq,Eq)]
 enum Enum {
@@ -96,3 +98,86 @@ fn test_variable_length_array() {
     let value: Vec<Foo> = from_slice(slice).unwrap();
     assert_eq!(value, [Foo::Require]);
 }
+
+#[derive(Serialize, Deserialize, PartialEq, Debug)]
+enum Bar {
+    Empty,
+    Number(i32),
+    Flag(String, bool),
+    Point{x: i32, y: i32},
+}
+
+#[test]
+fn test_enum_as_map() {
+    // unit variants serialize like bare strings
+    let empty_s = to_vec(&Bar::Empty).unwrap();
+    let empty_str_s = to_vec(&"Empty").unwrap();
+    assert_eq!(empty_s, empty_str_s);
+
+    // tuple-variants serialize like ["<variant>", values..]
+    let number_s = to_vec(&Bar::Number(42)).unwrap();
+    let number_vec = vec![Value::String("Number".to_string()), Value::I64(42)];
+    let number_vec_s = to_vec(&number_vec).unwrap();
+    assert_eq!(number_s, number_vec_s);
+
+    let flag_s = to_vec(&Bar::Flag("foo".to_string(), true)).unwrap();
+    let flag_vec = vec![Value::String("Flag".to_string()), Value::String("foo".to_string()), Value::Bool(true)];
+    let flag_vec_s = to_vec(&flag_vec).unwrap();
+    assert_eq!(flag_s, flag_vec_s);
+
+    // struct-variants serialize like ["<variant>", {struct..}]
+    let point_s = to_vec(&Bar::Point{ x: 5, y: -5}).unwrap();
+    let mut struct_map = BTreeMap::new();
+    struct_map.insert(ObjectKey::String("x".to_string()), Value::I64(5));
+    struct_map.insert(ObjectKey::String("y".to_string()), Value::I64(-5));
+    let point_vec = vec![Value::String("Point".to_string()), Value::Object(struct_map.clone())];
+    let point_vec_s = to_vec(&point_vec).unwrap();
+    assert_eq!(point_s, point_vec_s);
+
+    // enum_as_map matches serde_json's default serialization for enums.
+    let opts = serde_cbor::SerializerOptions{ enum_as_map: true, ..Default::default() };
+
+    // unit variants still serialize like bare strings
+    let empty_s = opts.to_vec(&Bar::Empty).unwrap();
+    assert_eq!(empty_s, empty_str_s);
+
+    // 1-element tuple variants serialize like {"<variant>": value}
+    let number_s = opts.to_vec(&Bar::Number(42)).unwrap();
+    let mut number_map = BTreeMap::new();
+    number_map.insert("Number", 42);
+    let number_map_s = to_vec(&number_map).unwrap();
+    assert_eq!(number_s, number_map_s);
+
+    // multi-element tuple variants serialize like {"<variant>": [values..]}
+    let flag_s = opts.to_vec(&Bar::Flag("foo".to_string(), true)).unwrap();
+    let mut flag_map = BTreeMap::new();
+    flag_map.insert("Flag", vec![Value::String("foo".to_string()), Value::Bool(true)]);
+    let flag_map_s = to_vec(&flag_map).unwrap();
+    assert_eq!(flag_s, flag_map_s);
+
+    // struct-variants serialize like {"<variant>", {struct..}}
+    let point_s = opts.to_vec(&Bar::Point{ x: 5, y: -5}).unwrap();
+    let mut point_map = BTreeMap::new();
+    point_map.insert("Point", Value::Object(struct_map));
+    let point_map_s = to_vec(&point_map).unwrap();
+    assert_eq!(point_s, point_map_s);
+
+    // deserialization of all encodings should just work
+    let empty_str_ds = from_slice(&empty_str_s).unwrap();
+    assert_eq!(Bar::Empty, empty_str_ds);
+
+    let number_vec_ds = from_slice(&number_vec_s).unwrap();
+    assert_eq!(Bar::Number(42), number_vec_ds);
+    let number_map_ds = from_slice(&number_map_s).unwrap();
+    assert_eq!(Bar::Number(42), number_map_ds);
+
+    let flag_vec_ds = from_slice(&flag_vec_s).unwrap();
+    assert_eq!(Bar::Flag("foo".to_string(), true), flag_vec_ds);
+    let flag_map_ds = from_slice(&flag_map_s).unwrap();
+    assert_eq!(Bar::Flag("foo".to_string(), true), flag_map_ds);
+
+    let point_vec_ds = from_slice(&point_vec_s).unwrap();
+    assert_eq!(Bar::Point{ x: 5, y: -5}, point_vec_ds);
+    let point_map_ds = from_slice(&point_map_s).unwrap();
+    assert_eq!(Bar::Point{ x: 5, y: -5}, point_map_ds);
+}
diff --git a/tests/ser.rs b/tests/ser.rs
index aba7cca..3a23f42 100644
--- a/tests/ser.rs
+++ b/tests/ser.rs
@@ -135,6 +135,10 @@ fn test_self_describing() {
         serializer.serialize_u64(9).unwrap();
     }
     assert_eq!(vec, b"\xd9\xd9\xf7\x09");
+
+    let sd = ser::SerializerOptions{ self_describe: true, ..Default::default() };
+    let vec = sd.to_vec(&9).unwrap();
+    assert_eq!(vec, b"\xd9\xd9\xf7\x09");
 }
 
 #[test]
diff --git a/tests/std_types.rs b/tests/std_types.rs
index fb03e26..b4a133e 100644
--- a/tests/std_types.rs
+++ b/tests/std_types.rs
@@ -7,7 +7,7 @@ use std::u8;
 
 use serde_bytes::ByteBuf;
 
-use serde_cbor::{from_slice, from_reader};
+use serde_cbor::{from_slice, from_mut_slice, from_reader};
 use serde_cbor::ser::{to_vec, to_vec_packed};
 
 fn to_binary(s: &'static str) -> Vec<u8> {
@@ -24,7 +24,7 @@ macro_rules! testcase {
         #[test]
         fn $name() {
             let expr: f64 = $expr;
-            let serialized = to_binary($s);
+            let mut serialized = to_binary($s);
             assert_eq!(to_vec(&expr).unwrap(), serialized);
             let parsed: f64 = from_slice(&serialized[..]).unwrap();
             if !expr.is_nan() {
@@ -39,13 +39,20 @@ macro_rules! testcase {
             } else {
                 assert!(parsed.is_nan())
             }
+
+            let parsed: f64 = from_mut_slice(&mut serialized[..]).unwrap();
+            if !expr.is_nan() {
+                assert_eq!(expr, parsed);
+            } else {
+                assert!(parsed.is_nan())
+            }
         }
     };
     ($name:ident, $ty:ty, $expr:expr, $s:expr) => {
         #[test]
         fn $name() {
             let expr: $ty = $expr;
-            let serialized = to_binary($s);
+            let mut serialized = to_binary($s);
             assert_eq!(to_vec(&expr).unwrap(), serialized, "serialization differs");
             let parsed: $ty = from_slice(&serialized[..]).unwrap();
             assert_eq!(parsed, expr, "parsed result differs");
@@ -57,11 +64,17 @@ macro_rules! testcase {
 
             let parsed: $ty = from_reader(&mut &serialized[..]).unwrap();
             assert_eq!(parsed, expr, "parsed result differs");
-            let packed = to_vec_packed(&expr)
+            let mut packed = to_vec_packed(&expr)
                 .expect("serializing packed");
             let parsed_from_packed: $ty = from_reader(&mut &packed[..])
                 .expect("parsing packed");
             assert_eq!(parsed_from_packed, expr, "packed roundtrip fail");
+
+            let parsed: $ty = from_mut_slice(&mut serialized[..]).unwrap();
+            assert_eq!(parsed, expr, "parsed result differs");
+            let parsed_from_packed: $ty = from_mut_slice(&mut packed[..])
+                .expect("parsing packed");
+            assert_eq!(parsed_from_packed, expr, "packed roundtrip fail");
         }
     }
 }