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.
This commit is contained in:
Ty Overby 2015-02-17 12:52:15 -08:00
parent b15b85797e
commit f5111f9476
3 changed files with 147 additions and 3 deletions

View File

@ -2,7 +2,7 @@
#![crate_type = "rlib"] #![crate_type = "rlib"]
#![crate_type = "dylib"] #![crate_type = "dylib"]
#![allow(unstable)] #![feature(io, hash, core)]
#![doc(html_logo_url = "./icon.png")] #![doc(html_logo_url = "./icon.png")]
@ -11,12 +11,14 @@ extern crate "rustc-serialize" as rustc_serialize;
use std::old_io::{Buffer, MemWriter}; use std::old_io::{Buffer, MemWriter};
use rustc_serialize::{Encodable, Decodable}; use rustc_serialize::{Encodable, Decodable};
pub use refbox::RefBox;
pub use writer::{EncoderWriter, EncodingResult, EncodingError}; pub use writer::{EncoderWriter, EncodingResult, EncodingError};
pub use reader::{DecoderReader, DecodingResult, DecodingError}; pub use reader::{DecoderReader, DecodingResult, DecodingError};
use writer::SizeChecker; use writer::SizeChecker;
mod writer; mod writer;
mod reader; mod reader;
mod refbox;
#[cfg(test)] mod test; #[cfg(test)] mod test;
///! `bincode` is a crate for encoding and decoding using a tiny binary ///! `bincode` is a crate for encoding and decoding using a tiny binary

90
src/refbox.rs Normal file
View File

@ -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<T>)
}
impl <'a, T> RefBox<'a, T> {
pub fn new(v: &'a T) -> RefBox<'a, T> {
RefBox {
inner: RefBoxInner::Ref(v)
}
}
}
impl <T> RefBox<'static, T> {
pub fn take(self) -> Box<T> {
match self.inner {
RefBoxInner::Box(b) => b,
_ => unreachable!()
}
}
pub fn try_take(self) -> Result<Box<T>, 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<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
self.inner.encode(s)
}
}
impl <T: Decodable> Decodable for RefBox<'static, T> {
fn decode<D: Decoder>(d: &mut D) -> Result<RefBox<'static, T>, D::Error> {
let inner = try!(Decodable::decode(d));
Ok(RefBox{inner: inner})
}
}
impl <'a, T: Encodable> Encodable for RefBoxInner<'a, T> {
fn encode<S: Encoder>(&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 <T: Decodable> Decodable for RefBoxInner<'static, T> {
fn decode<D: Decoder>(d: &mut D) -> Result<RefBoxInner<'static, T>, 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()
}
}
}

View File

@ -2,6 +2,7 @@ extern crate "rustc-serialize" as serialize;
use std::fmt::Debug; use std::fmt::Debug;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Deref;
use rustc_serialize::{ use rustc_serialize::{
Encoder, Encoder,
@ -16,17 +17,32 @@ use super::{
decode_from, decode_from,
encoded_size, encoded_size,
DecodingError, DecodingError,
DecodingResult DecodingResult,
RefBox,
}; };
use super::SizeLimit::{Infinite, Bounded}; use super::SizeLimit::{Infinite, Bounded};
fn the_same<V>(element: V) fn the_same<V>(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: &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 size = encoded_size(&element);
let encoded = encode(&element, Infinite).unwrap(); let encoded = encode(&element, Infinite).unwrap();
let decoded = decode(&encoded[]).unwrap(); let decoded = decode(&encoded[]).unwrap();
assert!(element == decoded); assert!(element == decoded);
assert!(size == encoded.len() as u64); assert!(size == encoded.len() as u64);
assert!(ref_box_correct(&element))
} }
#[test] #[test]
@ -246,3 +262,39 @@ fn test_encoded_size() {
fn encode_box() { fn encode_box() {
the_same(Box::new(5)); 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<u32>>),
M2(RefBox<'a, HashMap<u32, u32>>)
}
// 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)
}
}
}