From f5111f9476a0c2a2b9b315cfe84a3a23a1b9c1ae Mon Sep 17 00:00:00 2001 From: Ty Overby Date: Tue, 17 Feb 2015 12:52:15 -0800 Subject: [PATCH] Add RefBox. RefBox is a structure for encoding references, and decoding them into a box. This makes it easy to nest structures that otherwise would not be able to be nested and encoded. --- src/lib.rs | 4 ++- src/refbox.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/test.rs | 56 ++++++++++++++++++++++++++++++-- 3 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 src/refbox.rs diff --git a/src/lib.rs b/src/lib.rs index 0cb0f7a..b12c24b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ #![crate_type = "rlib"] #![crate_type = "dylib"] -#![allow(unstable)] +#![feature(io, hash, core)] #![doc(html_logo_url = "./icon.png")] @@ -11,12 +11,14 @@ extern crate "rustc-serialize" as rustc_serialize; use std::old_io::{Buffer, MemWriter}; use rustc_serialize::{Encodable, Decodable}; +pub use refbox::RefBox; pub use writer::{EncoderWriter, EncodingResult, EncodingError}; pub use reader::{DecoderReader, DecodingResult, DecodingError}; use writer::SizeChecker; mod writer; mod reader; +mod refbox; #[cfg(test)] mod test; ///! `bincode` is a crate for encoding and decoding using a tiny binary diff --git a/src/refbox.rs b/src/refbox.rs new file mode 100644 index 0000000..1f70dae --- /dev/null +++ b/src/refbox.rs @@ -0,0 +1,90 @@ +use std::boxed::Box; +use std::ops::Deref; + +use rustc_serialize::{Encodable, Encoder}; +use rustc_serialize::{Decodable, Decoder}; + +pub struct RefBox<'a, T: 'a> { + inner: RefBoxInner<'a, T> +} + +#[derive(Debug, Hash)] +enum RefBoxInner<'a, T: 'a> { + Ref(&'a T), + Box(Box) +} + +impl <'a, T> RefBox<'a, T> { + pub fn new(v: &'a T) -> RefBox<'a, T> { + RefBox { + inner: RefBoxInner::Ref(v) + } + } +} + +impl RefBox<'static, T> { + pub fn take(self) -> Box { + match self.inner { + RefBoxInner::Box(b) => b, + _ => unreachable!() + } + } + pub fn try_take(self) -> Result, RefBox<'static, T>> { + match self.inner { + RefBoxInner::Box(b) => Ok(b), + o => Err(RefBox{ inner: o}) + } + } +} +impl <'a, T: Encodable> Encodable for RefBox<'a, T> { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + self.inner.encode(s) + } +} +impl Decodable for RefBox<'static, T> { + fn decode(d: &mut D) -> Result, D::Error> { + let inner = try!(Decodable::decode(d)); + Ok(RefBox{inner: inner}) + } +} + +impl <'a, T: Encodable> Encodable for RefBoxInner<'a, T> { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_enum("RefBox", |s| { + s.emit_enum_variant("Box", 1, 1, |s| { + s.emit_enum_variant_arg(0, |s| { + match self { + &RefBoxInner::Ref(ref r) => r.encode(s), + &RefBoxInner::Box(ref b) => b.encode(s) + } + }) + }) + }) + } +} + +impl Decodable for RefBoxInner<'static, T> { + fn decode(d: &mut D) -> Result, D::Error> { + d.read_enum("RefBox", |d| { + d.read_enum_variant(&["Ref", "Box"], |d, i| { + assert!(i == 1); + d.read_enum_variant_arg(0, |d| { + let decoded = try!(Decodable::decode(d)); + Ok(RefBoxInner::Box(Box::new(decoded))) + }) + }) + }) + } +} + +impl <'a, T> Deref for RefBox<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + match &self.inner { + &RefBoxInner::Ref(ref t) => t, + &RefBoxInner::Box(ref b) => b.deref() + } + } +} + diff --git a/src/test.rs b/src/test.rs index 0dc33ef..9acd1a8 100644 --- a/src/test.rs +++ b/src/test.rs @@ -2,6 +2,7 @@ extern crate "rustc-serialize" as serialize; use std::fmt::Debug; use std::collections::HashMap; +use std::ops::Deref; use rustc_serialize::{ Encoder, @@ -16,17 +17,32 @@ use super::{ decode_from, encoded_size, DecodingError, - DecodingResult + DecodingResult, + RefBox, }; + use super::SizeLimit::{Infinite, Bounded}; fn the_same(element: V) -where V: Encodable + Decodable + PartialEq + Debug { +where V: Encodable + Decodable + PartialEq + Debug + 'static { + + // Make sure that the bahavior is correct when wrapping with a RefBox. + fn ref_box_correct(v: &V) -> bool + where V: Encodable + Decodable + PartialEq + Debug + 'static { + let rf = RefBox::new(v); + + let encoded = encode(&rf, Infinite).unwrap(); + let decoded: RefBox<'static, V> = decode(&encoded[]).unwrap(); + + decoded.take().deref() == v + } + let size = encoded_size(&element); let encoded = encode(&element, Infinite).unwrap(); let decoded = decode(&encoded[]).unwrap(); assert!(element == decoded); assert!(size == encoded.len() as u64); + assert!(ref_box_correct(&element)) } #[test] @@ -246,3 +262,39 @@ fn test_encoded_size() { fn encode_box() { the_same(Box::new(5)); } + +#[test] +fn test_refbox() { + let large_object = vec![1u32,2,3,4,5,6]; + let mut large_map = HashMap::new(); + large_map.insert(1, 2); + + + #[derive(RustcEncodable, RustcDecodable)] + enum Message<'a> { + M1(RefBox<'a, Vec>), + M2(RefBox<'a, HashMap>) + } + + // Test 1 + { + let encoded = encode(&Message::M1(RefBox::new(&large_object)), Infinite).unwrap(); + let decoded: Message<'static> = decode(&encoded[]).unwrap(); + + match decoded { + Message::M1(b) => assert!(b.take().deref() == &large_object), + _ => assert!(false) + } + } + + // Test 2 + { + let encoded = encode(&Message::M2(RefBox::new(&large_map)), Infinite).unwrap(); + let decoded: Message<'static> = decode(&encoded[]).unwrap(); + + match decoded { + Message::M2(b) => assert!(b.take().deref() == &large_map), + _ => assert!(false) + } + } +}