about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDan Burkert <dan@danburkert.com>2014-09-27 14:19:19 -0700
committerDan Burkert <dan@danburkert.com>2014-11-01 10:54:34 -0700
commitca6b082c0573f9d6f6c81403ac7ea4b5b78260d6 (patch)
tree89fc51a5a629a766949f6445f56e04b020f05d27
parent0547a407aa03b9f1c03843aead617a2e8c5d1147 (diff)
libserialize: tuple-arity should be provided to `Decoder::read_tuple`
Currently `Decoder` implementations are not provided the tuple arity as
a parameter to `read_tuple`. This forces all encoder/decoder combos to
serialize the arity along with the elements. Tuple-arity is always known
statically at the decode site, because it is part of the type of the
tuple, so it could instead be provided as an argument to `read_tuple`,
as it is to `read_struct`.

The upside to this is that serialized tuples could become smaller in
encoder/decoder implementations which choose not to serialize type
(arity) information. For example, @TyOverby's
[binary-encode](https://github.com/TyOverby/binary-encode) format is
currently forced to serialize the tuple-arity along with every tuple,
despite the information being statically known at the decode site.

A downside to this change is that the tuple-arity of serialized tuples
can no longer be automatically checked during deserialization. However,
for formats which do serialize the tuple-arity, either explicitly (rbml)
or implicitly (json), this check can be added to the `read_tuple` method.

The signature of `Deserialize::read_tuple` and
`Deserialize::read_tuple_struct` are changed, and thus binary
backwards-compatibility is broken. This change does *not* force
serialization formats to change, and thus does not break decoding values
serialized prior to this change.

[breaking-change]
-rw-r--r--src/librbml/lib.rs15
-rw-r--r--src/libserialize/json.rs34
-rw-r--r--src/libserialize/serialize.rs17
3 files changed, 52 insertions, 14 deletions
diff --git a/src/librbml/lib.rs b/src/librbml/lib.rs
index 95b6d9a17dd..9bb5777ec9a 100644
--- a/src/librbml/lib.rs
+++ b/src/librbml/lib.rs
@@ -558,9 +558,15 @@ pub mod reader {
         }
 
         fn read_tuple<T>(&mut self,
-                         f: |&mut Decoder<'doc>, uint| -> DecodeResult<T>) -> DecodeResult<T> {
+                         tuple_len: uint,
+                         f: |&mut Decoder<'doc>| -> DecodeResult<T>) -> DecodeResult<T> {
             debug!("read_tuple()");
-            self.read_seq(f)
+            self.read_seq(|d, len| {
+                assert!(len == tuple_len,
+                        "expected tuple of length `{}`, found tuple \
+                         of length `{}`", tuple_len, len);
+                f(d)
+            })
         }
 
         fn read_tuple_arg<T>(&mut self, idx: uint, f: |&mut Decoder<'doc>| -> DecodeResult<T>)
@@ -571,10 +577,11 @@ pub mod reader {
 
         fn read_tuple_struct<T>(&mut self,
                                 name: &str,
-                                f: |&mut Decoder<'doc>, uint| -> DecodeResult<T>)
+                                len: uint,
+                                f: |&mut Decoder<'doc>| -> DecodeResult<T>)
                                 -> DecodeResult<T> {
             debug!("read_tuple_struct(name={})", name);
-            self.read_tuple(f)
+            self.read_tuple(len, f)
         }
 
         fn read_tuple_struct_arg<T>(&mut self,
diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs
index 84b5cdd3cc8..e5847ce58db 100644
--- a/src/libserialize/json.rs
+++ b/src/libserialize/json.rs
@@ -2153,9 +2153,14 @@ impl ::Decoder<DecoderError> for Decoder {
         Ok(value)
     }
 
-    fn read_tuple<T>(&mut self, f: |&mut Decoder, uint| -> DecodeResult<T>) -> DecodeResult<T> {
+    fn read_tuple<T>(&mut self, tuple_len: uint, f: |&mut Decoder| -> DecodeResult<T>) -> DecodeResult<T> {
         debug!("read_tuple()");
-        self.read_seq(f)
+        self.read_seq(|d, len| {
+            assert!(len == tuple_len,
+                    "expected tuple of length `{}`, found tuple \
+                         of length `{}`", tuple_len, len);
+            f(d)
+        })
     }
 
     fn read_tuple_arg<T>(&mut self,
@@ -2167,10 +2172,11 @@ impl ::Decoder<DecoderError> for Decoder {
 
     fn read_tuple_struct<T>(&mut self,
                             name: &str,
-                            f: |&mut Decoder, uint| -> DecodeResult<T>)
+                            len: uint,
+                            f: |&mut Decoder| -> DecodeResult<T>)
                             -> DecodeResult<T> {
         debug!("read_tuple_struct(name={})", name);
-        self.read_tuple(f)
+        self.read_tuple(len, f)
     }
 
     fn read_tuple_struct_arg<T>(&mut self,
@@ -2873,6 +2879,26 @@ mod tests {
     }
 
     #[test]
+    fn test_decode_tuple() {
+        let t: (uint, uint, uint) = super::decode("[1, 2, 3]").unwrap();
+        assert_eq!(t, (1u, 2, 3))
+
+        let t: (uint, string::String) = super::decode("[1, \"two\"]").unwrap();
+        assert_eq!(t, (1u, "two".to_string()));
+    }
+
+    #[test]
+    fn test_decode_tuple_malformed_types() {
+        assert!(super::decode::<(uint, string::String)>("[1, 2]").is_err());
+    }
+
+    #[test]
+    #[should_fail]
+    fn test_decode_tuple_malformed_length() {
+        let _ = super::decode::<(uint, uint)>("[1, 2, 3]");
+    }
+
+    #[test]
     fn test_read_object() {
         assert_eq!(from_str("{"),       Err(SyntaxError(EOFWhileParsingObject, 1, 2)));
         assert_eq!(from_str("{ "),      Err(SyntaxError(EOFWhileParsingObject, 1, 3)));
diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs
index 1cd1738e369..b7c37defbfa 100644
--- a/src/libserialize/serialize.rs
+++ b/src/libserialize/serialize.rs
@@ -142,12 +142,13 @@ pub trait Decoder<E> {
                             f: |&mut Self| -> Result<T, E>)
                             -> Result<T, E>;
 
-    fn read_tuple<T>(&mut self, f: |&mut Self, uint| -> Result<T, E>) -> Result<T, E>;
+    fn read_tuple<T>(&mut self, len: uint, f: |&mut Self| -> Result<T, E>) -> Result<T, E>;
     fn read_tuple_arg<T>(&mut self, a_idx: uint, f: |&mut Self| -> Result<T, E>) -> Result<T, E>;
 
     fn read_tuple_struct<T>(&mut self,
                             s_name: &str,
-                            f: |&mut Self, uint| -> Result<T, E>)
+                            len: uint,
+                            f: |&mut Self| -> Result<T, E>)
                             -> Result<T, E>;
     fn read_tuple_struct_arg<T>(&mut self,
                                 a_idx: uint,
@@ -465,20 +466,24 @@ impl<E, D:Decoder<E>,T:Decodable<D, E>> Decodable<D, E> for Option<T> {
 
 macro_rules! peel(($name:ident, $($other:ident,)*) => (tuple!($($other,)*)))
 
+/// Evaluates to the number of identifiers passed to it, for example: `count_idents!(a, b, c) == 3
+macro_rules! count_idents {
+    () => { 0u };
+    ($_i:ident $(, $rest:ident)*) => { 1 + count_idents!($($rest),*) }
+}
+
 macro_rules! tuple (
     () => ();
     ( $($name:ident,)+ ) => (
         impl<E, D:Decoder<E>,$($name:Decodable<D, E>),*> Decodable<D,E> for ($($name,)*) {
             #[allow(non_snake_case)]
             fn decode(d: &mut D) -> Result<($($name,)*), E> {
-                d.read_tuple(|d, amt| {
+                let len: uint = count_idents!($($name),*);
+                d.read_tuple(len, |d| {
                     let mut i = 0;
                     let ret = ($(try!(d.read_tuple_arg({ i+=1; i-1 }, |d| -> Result<$name,E> {
                         Decodable::decode(d)
                     })),)*);
-                    assert!(amt == i,
-                            "expected tuple of length `{}`, found tuple \
-                             of length `{}`", i, amt);
                     return Ok(ret);
                 })
             }