Repository: pyfisch/cbor (rust)

Issue:
Detecting invalid cbor when deserializing multiple objects
I am deserializing multiple objects from a slice (like in https://github.com/pyfisch/cbor/issues/20).

I am using the EOF error to detect when I reached the last object, but I have no way to distinguish between an actual EOF and an invalid cbor object.

It would be great to have an `Iter` implementation that does the right thing or that the thrown error had not the current offset in the stream but the offset where the deserialization began.

Pull request description:
Add a StreamDeserializer
Closes #68

Patch:
diff --git a/src/de.rs b/src/de.rs
index c1bfeb8..57a625d 100644
--- a/src/de.rs
+++ b/src/de.rs
@@ -6,6 +6,7 @@ use std::io;
 use std::str;
 use std::f32;
 use std::result;
+use std::marker::PhantomData;
 
 use error::{Error, Result, ErrorCode};
 use read::Reference;
@@ -84,6 +85,18 @@ where
         }
     }
 
+    /// Turn a CBOR deserializer into an iterator over values of type T.
+    pub fn into_iter<T>(self) -> StreamDeserializer<'de, R, T>
+    where
+        T: de::Deserialize<'de>,
+    {
+        StreamDeserializer {
+            de: self,
+            output: PhantomData,
+            lifetime: PhantomData,
+        }
+    }
+
     fn next(&mut self) -> Result<Option<u8>> {
         self.read.next().map_err(Error::io)
     }
@@ -967,3 +980,50 @@ where
         de.deserialize_any(self.visitor)
     }
 }
+
+/// Iterator that deserializes a stream into multiple CBOR values.
+///
+/// A stream deserializer can be created from any CBOR deserializer using the
+/// `Deserializer::into_iter` method.
+pub struct StreamDeserializer<'de, R, T> {
+    de: Deserializer<R>,
+    output: PhantomData<T>,
+    lifetime: PhantomData<&'de ()>,
+}
+
+impl<'de, R, T> StreamDeserializer<'de, R, T>
+where
+    R: Read<'de>,
+    T: de::Deserialize<'de>,
+{
+    /// Create a new CBOR stream deserializer from one of the possible
+    /// serde_cbor input sources.
+    ///
+    /// Typically it is more convenient to use one of these methods instead:
+    ///
+    /// * `Deserializer::from_bytes(...).into_iter()`
+    /// * `Deserializer::from_reader(...).into_iter()`
+    pub fn new(read: R) -> StreamDeserializer<'de, R, T> {
+        StreamDeserializer {
+            de: Deserializer::new(read),
+            output: PhantomData,
+            lifetime: PhantomData,
+        }
+    }
+}
+
+impl<'de, R, T> Iterator for StreamDeserializer<'de, R, T>
+where
+    R: Read<'de>,
+    T: de::Deserialize<'de>,
+{
+    type Item = Result<T>;
+
+    fn next(&mut self) -> Option<Result<T>> {
+        match self.de.peek() {
+            Ok(Some(_)) => Some(T::deserialize(&mut self.de)),
+            Ok(None) => None,
+            Err(e) => Some(Err(e)),
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 33d7cc8..947a75b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -159,7 +159,7 @@ pub mod ser;
 pub mod value;
 
 #[doc(inline)]
-pub use de::{from_slice, from_reader, Deserializer};
+pub use de::{from_slice, from_reader, Deserializer, StreamDeserializer};
 #[doc(inline)]
 pub use ser::{to_writer, to_vec, Serializer};
 #[doc(inline)]


Test patch:
diff --git a/tests/de.rs b/tests/de.rs
index 5224403..e28bfa3 100644
--- a/tests/de.rs
+++ b/tests/de.rs
@@ -4,7 +4,7 @@ extern crate serde_bytes;
 use serde_bytes::ByteBuf;
 use std::collections::BTreeMap;
 
-use serde_cbor::{to_vec, Value, ObjectKey, error, de, from_reader};
+use serde_cbor::{to_vec, Value, ObjectKey, error, de, Deserializer, from_reader};
 
 #[test]
 fn test_string1() {
@@ -216,6 +216,23 @@ fn test_object_determinism_roundtrip() {
     }
 }
 
+#[test]
+fn stream_deserializer() {
+    let slice = b"\x01\x66foobar";
+    let mut it = Deserializer::from_slice(slice).into_iter::<Value>();
+    assert_eq!(Value::U64(1), it.next().unwrap().unwrap());
+    assert_eq!(Value::String("foobar".to_string()), it.next().unwrap().unwrap());
+    assert!(it.next().is_none());
+}
+
+#[test]
+fn stream_deserializer_eof() {
+    let slice = b"\x01\x66foob";
+    let mut it = Deserializer::from_slice(slice).into_iter::<Value>();
+    assert_eq!(Value::U64(1), it.next().unwrap().unwrap());
+    assert!(it.next().unwrap().unwrap_err().is_eof());
+}
+
 #[test]
 fn test_large_bytes() {
     let expected = (0..2 * 1024 * 1024).map(|i| (i * 7) as u8).collect::<Vec<_>>();