Merge branch 'master' of github.com:TyOverby/bincode

This commit is contained in:
TyOverby 2015-01-16 13:21:46 -08:00
commit aeaa4aafd4
6 changed files with 171 additions and 41 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bincode" name = "bincode"
version = "0.0.5" version = "0.0.6"
authors = ["Ty Overby <ty@pre-alpha.com>", "Francesco Mazzoli <f@mazzo.li>"] authors = ["Ty Overby <ty@pre-alpha.com>", "Francesco Mazzoli <f@mazzo.li>"]
repository = "https://github.com/TyOverby/bincode" repository = "https://github.com/TyOverby/bincode"

View File

@ -24,7 +24,7 @@ fn main() {
// 8 bytes for the length of the vector, 4 bytes per float. // 8 bytes for the length of the vector, 4 bytes per float.
assert_eq!(encoded.len(), 8 + 4 * 4); assert_eq!(encoded.len(), 8 + 4 * 4);
let decoded: World = bincode::decode(encoded.as_slice()).unwrap(); let decoded: World = bincode::decode(&encoded[]).unwrap();
assert!(world == decoded); assert!(world == decoded);
} }

View File

@ -17,10 +17,76 @@ mod writer;
mod reader; mod reader;
#[cfg(test)] mod test; #[cfg(test)] mod test;
#[derive(Clone, Copy)] ///! `bincode` is a crate for encoding and decoding using a tiny binary
///! serialization strategy.
///!
///! There are simple functions for encoding to `Vec<u8>` and decoding from
///! `&[u8]`, but the meat of the library is the `encode_into` and `decode_from`
///! functions which respectively allow encoding into a `std::io::Writer`
///! and decoding from a `std::io::Buffer`.
///!
///! ### Using Basic Functions
///!
///! ```rust
///! extern crate bincode;
///! fn main() {
///! // The object that we will serialize.
///! let target = Some("hello world".to_string());
///! // The maximum size of the encoded message.
///! let limit = bincode::SizeLimit::Bounded(20);
///!
///! let encoded: Vec<u8> = bincode::encode(&target, limit).unwrap();
///! let decoded: Option<String> = bincode::decode(&encoded[]).unwrap();
///! assert_eq!(target, decoded);
///! }
///! ```
///!
///! ### Using Into/From Functions
///!
///! ```rust
///! extern crate bincode;
///! use std::io::pipe::PipeStream;
///! use std::io::BufferedReader;
///! fn main() {
///! // The pipes that we will be using to send values across.
///! let streams = PipeStream::pair().unwrap();
///! let (mut reader, mut writer) = (BufferedReader::new(streams.reader),
///! streams.writer);
///! // The object that we will send across.
///! let target = Some(5u32);
///! // The max-size of the encoded bytes.
///! let limit = bincode::SizeLimit::Bounded(10);
///!
///! // Do the actual encoding and decoding.
///! bincode::encode_into(&target, &mut writer, limit);
///! let out: Option<u32> = bincode::decode_from(&mut reader, limit).unwrap();
///! assert_eq!(target, out);
///! }
///! ```
///!
/// A limit on the size of bytes to be read or written.
///
/// Size limits are an incredibly important part of both encoding and decoding.
///
/// In order to prevent DOS attacks on a decoder, it is important to limit the
/// amount of bytes that a single encoded message can be; otherwise, if you
/// are decoding bytes right off of a TCP stream for example, it would be
/// possible for an attacker to flood your server with a 3TB vec, causing the
/// decoder to run out of memory and crash your application!
/// Because of this, you can provide a maximum-number-of-bytes that can be read
/// during decoding, and the decoder will explicitly fail if it has to read
/// any more than that.
///
/// On the other side, you want to make sure that you aren't encoding a message
/// that is larger than your decoder expects. By supplying a size limit to an
/// encoding function, the encoder will verify that the structure can be encoded
/// within that limit. This verification occurs before any bytes are written to
/// the Writer, so recovering from an the error is possible.
#[derive(Clone, Copy, Show, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum SizeLimit { pub enum SizeLimit {
Infinite, Infinite,
UpperBound(u64) Bounded(u64)
} }
/// Encodes an encodable object into a `Vec` of bytes. /// Encodes an encodable object into a `Vec` of bytes.
@ -36,6 +102,9 @@ pub fn encode<T: Encodable>(t: &T, size_limit: SizeLimit) -> EncodingResult<Vec<
} }
/// Decodes a slice of bytes into an object. /// Decodes a slice of bytes into an object.
///
/// This method does not have a size-limit because if you already have the bytes
/// in memory, then you don't gain anything by having a limiter.
pub fn decode<T: Decodable>(b: &[u8]) -> DecodingResult<T> { pub fn decode<T: Decodable>(b: &[u8]) -> DecodingResult<T> {
let mut b = b; let mut b = b;
decode_from(&mut b, SizeLimit::Infinite) decode_from(&mut b, SizeLimit::Infinite)
@ -45,10 +114,14 @@ pub fn decode<T: Decodable>(b: &[u8]) -> DecodingResult<T> {
/// ///
/// If the encoding would take more bytes than allowed by `size_limit`, an error /// If the encoding would take more bytes than allowed by `size_limit`, an error
/// is returned and *no bytes* will be written into the `Writer`. /// is returned and *no bytes* will be written into the `Writer`.
///
/// If this returns an `EncodingError` (other than SizeLimit), assume that the
/// writer is in an invalid state, as writing could bail out in the middle of
/// encoding.
pub fn encode_into<T: Encodable, W: Writer>(t: &T, w: &mut W, size_limit: SizeLimit) -> EncodingResult<()> { pub fn encode_into<T: Encodable, W: Writer>(t: &T, w: &mut W, size_limit: SizeLimit) -> EncodingResult<()> {
try!(match size_limit { try!(match size_limit {
SizeLimit::Infinite => Ok(()), SizeLimit::Infinite => Ok(()),
SizeLimit::UpperBound(x) => { SizeLimit::Bounded(x) => {
let mut size_checker = SizeChecker::new(x); let mut size_checker = SizeChecker::new(x);
t.encode(&mut size_checker) t.encode(&mut size_checker)
} }
@ -57,11 +130,15 @@ pub fn encode_into<T: Encodable, W: Writer>(t: &T, w: &mut W, size_limit: SizeLi
t.encode(&mut writer::EncoderWriter::new(w, size_limit)) t.encode(&mut writer::EncoderWriter::new(w, size_limit))
} }
/// Decoes an object directly from a Buffered Reader. /// Decoes an object directly from a `Buffer`ed Reader.
/// ///
/// If the provided `SizeLimit` is reached, the decode will bail immediately. /// If the provided `SizeLimit` is reached, the decode will bail immediately.
/// A SizeLimit can help prevent an attacker from flooding your server with /// A SizeLimit can help prevent an attacker from flooding your server with
/// a neverending stream of values that runs your server out of memory. /// a neverending stream of values that runs your server out of memory.
///
/// If this returns an `DecodingError`, assume that the buffer that you passed
/// in is in an invalid state, as the error could be returned during any point
/// in the reading.
pub fn decode_from<R: Buffer, T: Decodable>(r: &mut R, size_limit: SizeLimit) -> pub fn decode_from<R: Buffer, T: Decodable>(r: &mut R, size_limit: SizeLimit) ->
DecodingResult<T> { DecodingResult<T> {
Decodable::decode(&mut reader::DecoderReader::new(r, size_limit)) Decodable::decode(&mut reader::DecoderReader::new(r, size_limit))

View File

@ -1,24 +1,61 @@
use std::io::{Buffer, Reader, IoError}; use std::io::{Buffer, Reader, IoError};
use std::num::{cast, NumCast}; use std::num::{cast, NumCast};
use std::error::{Error, FromError}; use std::error::{Error, FromError};
use std::fmt;
use rustc_serialize::Decoder; use rustc_serialize::Decoder;
use super::SizeLimit; use super::SizeLimit;
#[derive(PartialEq, Clone, Show)] #[derive(Eq, PartialEq, Clone, Show)]
pub struct InvalidBytes { pub struct InvalidEncoding {
desc: &'static str, desc: &'static str,
detail: Option<String>, detail: Option<String>,
} }
#[derive(PartialEq, Clone, Show)] impl fmt::String for InvalidEncoding {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
InvalidEncoding { detail: None, desc } =>
write!(fmt, "{}", desc),
InvalidEncoding { detail: Some(ref detail), desc } =>
write!(fmt, "{} ({})", desc, detail)
}
}
}
/// An error that can be produced during decoding.
///
/// If decoding from a Buffer, assume that the buffer has been left
/// in an invalid state.
#[derive(Eq, PartialEq, Clone, Show)]
pub enum DecodingError { pub enum DecodingError {
/// If the error stems from the reader that is being used
/// during decoding, that error will be stored and returned here.
IoError(IoError), IoError(IoError),
InvalidBytes(InvalidBytes), /// If the bytes in the reader are not decodable because of an invalid
/// encoding, this error will be returned. This error is only possible
/// if a stream is corrupted. A stream produced from `encode` or `encode_into`
/// should **never** produce an InvalidEncoding error.
InvalidEncoding(InvalidEncoding),
/// If decoding a message takes more than the provided size limit, this
/// error is returned.
SizeLimit SizeLimit
} }
impl fmt::String for DecodingError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
DecodingError::IoError(ref ioerr) =>
write!(fmt, "IoError: {}", ioerr),
DecodingError::InvalidEncoding(ref ib) =>
write!(fmt, "InvalidEncoding: {}", ib),
DecodingError::SizeLimit =>
write!(fmt, "SizeLimit")
}
}
}
pub type DecodingResult<T> = Result<T, DecodingError>; pub type DecodingResult<T> = Result<T, DecodingError>;
fn wrap_io(err: IoError) -> DecodingError { fn wrap_io(err: IoError) -> DecodingError {
@ -29,7 +66,7 @@ impl Error for DecodingError {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
DecodingError::IoError(ref err) => err.description(), DecodingError::IoError(ref err) => err.description(),
DecodingError::InvalidBytes(ref ib) => ib.desc, DecodingError::InvalidEncoding(ref ib) => ib.desc,
DecodingError::SizeLimit => "the size limit for decoding has been reached" DecodingError::SizeLimit => "the size limit for decoding has been reached"
} }
} }
@ -37,7 +74,7 @@ impl Error for DecodingError {
fn detail(&self) -> Option<String> { fn detail(&self) -> Option<String> {
match *self { match *self {
DecodingError::IoError(ref err) => err.detail(), DecodingError::IoError(ref err) => err.detail(),
DecodingError::InvalidBytes(ref ib) => ib.detail.clone(), DecodingError::InvalidEncoding(ref ib) => ib.detail.clone(),
DecodingError::SizeLimit => None DecodingError::SizeLimit => None
} }
} }
@ -49,13 +86,17 @@ impl FromError<IoError> for DecodingError {
} }
} }
/// A Decoder that reads bytes from a buffer.
///
/// This struct should rarely be used.
/// In most cases, prefer the `decode_from` function.
pub struct DecoderReader<'a, R: 'a> { pub struct DecoderReader<'a, R: 'a> {
reader: &'a mut R, reader: &'a mut R,
size_limit: SizeLimit, size_limit: SizeLimit,
read: u64 read: u64
} }
impl<'a, R: Reader+Buffer> DecoderReader<'a, R> { impl<'a, R: Buffer> DecoderReader<'a, R> {
pub fn new(r: &'a mut R, size_limit: SizeLimit) -> DecoderReader<'a, R> { pub fn new(r: &'a mut R, size_limit: SizeLimit) -> DecoderReader<'a, R> {
DecoderReader { DecoderReader {
reader: r, reader: r,
@ -71,8 +112,8 @@ impl <'a, A> DecoderReader<'a, A> {
self.read += cast(count).unwrap(); self.read += cast(count).unwrap();
match self.size_limit { match self.size_limit {
SizeLimit::Infinite => Ok(()), SizeLimit::Infinite => Ok(()),
SizeLimit::UpperBound(x) if self.read <= x => Ok(()), SizeLimit::Bounded(x) if self.read <= x => Ok(()),
SizeLimit::UpperBound(_) => Err(DecodingError::SizeLimit) SizeLimit::Bounded(_) => Err(DecodingError::SizeLimit)
} }
} }
@ -82,7 +123,7 @@ impl <'a, A> DecoderReader<'a, A> {
} }
} }
impl<'a, R: Reader+Buffer> Decoder for DecoderReader<'a, R> { impl<'a, R: Buffer> Decoder for DecoderReader<'a, R> {
type Error = DecodingError; type Error = DecodingError;
fn read_nil(&mut self) -> DecodingResult<()> { fn read_nil(&mut self) -> DecodingResult<()> {
@ -131,7 +172,7 @@ impl<'a, R: Reader+Buffer> Decoder for DecoderReader<'a, R> {
match x { match x {
1 => Ok(true), 1 => Ok(true),
0 => Ok(false), 0 => Ok(false),
_ => Err(DecodingError::InvalidBytes(InvalidBytes{ _ => Err(DecodingError::InvalidEncoding(InvalidEncoding{
desc: "invalid u8 when decoding bool", desc: "invalid u8 when decoding bool",
detail: Some(format!("Expected 0 or 1, got {}", x)) detail: Some(format!("Expected 0 or 1, got {}", x))
})), })),
@ -158,7 +199,7 @@ impl<'a, R: Reader+Buffer> Decoder for DecoderReader<'a, R> {
let vector = try!(self.reader.read_exact(len)); let vector = try!(self.reader.read_exact(len));
match String::from_utf8(vector) { match String::from_utf8(vector) {
Ok(s) => Ok(s), Ok(s) => Ok(s),
Err(err) => Err(DecodingError::InvalidBytes(InvalidBytes { Err(err) => Err(DecodingError::InvalidEncoding(InvalidEncoding {
desc: "error while decoding utf8 string", desc: "error while decoding utf8 string",
detail: Some(format!("Decoding error: {}", err)) detail: Some(format!("Decoding error: {}", err))
})), })),
@ -173,7 +214,7 @@ impl<'a, R: Reader+Buffer> Decoder for DecoderReader<'a, R> {
let id = try!(self.read_u32()); let id = try!(self.read_u32());
let id = id as usize; let id = id as usize;
if id >= names.len() { if id >= names.len() {
Err(DecodingError::InvalidBytes(InvalidBytes { Err(DecodingError::InvalidEncoding(InvalidEncoding {
desc: "out of bounds tag when reading enum variant", desc: "out of bounds tag when reading enum variant",
detail: Some(format!("Expected tag < {}, got {}", names.len(), id)) detail: Some(format!("Expected tag < {}, got {}", names.len(), id))
})) }))
@ -231,7 +272,7 @@ impl<'a, R: Reader+Buffer> Decoder for DecoderReader<'a, R> {
match x { match x {
1 => f(self, true), 1 => f(self, true),
0 => f(self, false), 0 => f(self, false),
_ => Err(DecodingError::InvalidBytes(InvalidBytes { _ => Err(DecodingError::InvalidEncoding(InvalidEncoding {
desc: "invalid tag when decoding Option", desc: "invalid tag when decoding Option",
detail: Some(format!("Expected 0 or 1, got {}", x)) detail: Some(format!("Expected 0 or 1, got {}", x))
})), })),
@ -260,7 +301,7 @@ impl<'a, R: Reader+Buffer> Decoder for DecoderReader<'a, R> {
f(self) f(self)
} }
fn error(&mut self, err: &str) -> DecodingError { fn error(&mut self, err: &str) -> DecodingError {
DecodingError::InvalidBytes(InvalidBytes { DecodingError::InvalidEncoding(InvalidEncoding {
desc: "user-induced error", desc: "user-induced error",
detail: Some(err.to_string()), detail: Some(err.to_string()),
}) })

View File

@ -18,7 +18,7 @@ use super::{
DecodingError, DecodingError,
DecodingResult DecodingResult
}; };
use super::SizeLimit::{Infinite, UpperBound}; use super::SizeLimit::{Infinite, Bounded};
fn the_same<'a, V>(element: V) where V: Encodable, V: Decodable, V: PartialEq, V: Show { fn the_same<'a, V>(element: V) where V: Encodable, V: Decodable, V: PartialEq, V: Show {
assert!(element == decode(encode(&element, Infinite).unwrap().as_slice()).unwrap()); assert!(element == decode(encode(&element, Infinite).unwrap().as_slice()).unwrap());
@ -169,39 +169,40 @@ fn unicode() {
the_same("aåååååååa".to_string()); the_same("aåååååååa".to_string());
} }
fn is_invalid_bytes<T>(res: DecodingResult<T>) {
match res {
Ok(_) => panic!("Expecting error"),
Err(DecodingError::IoError(_)) => panic!("Expecting InvalidBytes"),
Err(DecodingError::SizeLimit) => panic!("Expecting InvalidBytes"),
Err(DecodingError::InvalidBytes(_)) => {},
}
}
#[test] #[test]
fn decoding_errors() { fn decoding_errors() {
is_invalid_bytes(decode::<bool>(vec![0xA].as_slice())); fn is_invalid_encoding<T>(res: DecodingResult<T>) {
is_invalid_bytes(decode::<String>(vec![0, 0, 0, 0, 0, 0, 0, 1, 0xFF].as_slice())); match res {
Ok(_) => panic!("Expecting error"),
Err(DecodingError::IoError(_)) => panic!("Expecting InvalidEncoding"),
Err(DecodingError::SizeLimit) => panic!("Expecting InvalidEncoding"),
Err(DecodingError::InvalidEncoding(_)) => {},
}
}
is_invalid_encoding(decode::<bool>(vec![0xA].as_slice()));
is_invalid_encoding(decode::<String>(vec![0, 0, 0, 0, 0, 0, 0, 1, 0xFF].as_slice()));
// Out-of-bounds variant // Out-of-bounds variant
#[derive(RustcEncodable, RustcDecodable)] #[derive(RustcEncodable, RustcDecodable)]
enum Test { enum Test {
One, One,
Two, Two,
}; };
is_invalid_bytes(decode::<Test>(vec![0, 0, 0, 5].as_slice())); is_invalid_encoding(decode::<Test>(vec![0, 0, 0, 5].as_slice()));
is_invalid_bytes(decode::<Option<u8>>(vec![5, 0].as_slice())); is_invalid_encoding(decode::<Option<u8>>(vec![5, 0].as_slice()));
} }
#[test] #[test]
fn too_big_decode() { fn too_big_decode() {
let encoded = vec![0,0,0,3]; let encoded = vec![0,0,0,3];
let mut encoded_ref = encoded.as_slice(); let mut encoded_ref = encoded.as_slice();
let decoded: Result<u32, _> = decode_from(&mut encoded_ref, UpperBound(3)); let decoded: Result<u32, _> = decode_from(&mut encoded_ref, Bounded(3));
assert!(decoded.is_err()); assert!(decoded.is_err());
let encoded = vec![0,0,0,3]; let encoded = vec![0,0,0,3];
let mut encoded_ref = encoded.as_slice(); let mut encoded_ref = encoded.as_slice();
let decoded: Result<u32, _> = decode_from(&mut encoded_ref, UpperBound(4)); let decoded: Result<u32, _> = decode_from(&mut encoded_ref, Bounded(4));
assert!(decoded.is_ok()); assert!(decoded.is_ok());
} }
@ -209,17 +210,17 @@ fn too_big_decode() {
fn too_big_char_decode() { fn too_big_char_decode() {
let encoded = vec![0x41]; let encoded = vec![0x41];
let mut encoded_ref = encoded.as_slice(); let mut encoded_ref = encoded.as_slice();
let decoded: Result<char, _> = decode_from(&mut encoded_ref, UpperBound(1)); let decoded: Result<char, _> = decode_from(&mut encoded_ref, Bounded(1));
assert_eq!(decoded, Ok('A')); assert_eq!(decoded, Ok('A'));
} }
#[test] #[test]
fn too_big_encode() { fn too_big_encode() {
assert!(encode(&0u32, UpperBound(3)).is_err()); assert!(encode(&0u32, Bounded(3)).is_err());
assert!(encode(&0u32, UpperBound(4)).is_ok()); assert!(encode(&0u32, Bounded(4)).is_ok());
assert!(encode(&"abcde", UpperBound(4)).is_err()); assert!(encode(&"abcde", Bounded(4)).is_err());
assert!(encode(&"abcde", UpperBound(5)).is_ok()); assert!(encode(&"abcde", Bounded(5)).is_ok());
} }
#[test] #[test]

View File

@ -8,12 +8,23 @@ use super::SizeLimit;
pub type EncodingResult<T> = Result<T, EncodingError>; pub type EncodingResult<T> = Result<T, EncodingError>;
/// An error that can be produced during encoding.
#[derive(Show)] #[derive(Show)]
pub enum EncodingError { pub enum EncodingError {
/// An error originating from the underlying `Writer`.
IoError(IoError), IoError(IoError),
/// An object could not be encoded with the given size limit.
///
/// This error is returned before any bytes are written to the
/// output `Writer`.
SizeLimit SizeLimit
} }
/// An Encoder that encodes values directly into a Writer.
///
/// This struct should not be used often.
/// For most cases, prefer the `encode_into` function.
pub struct EncoderWriter<'a, W: 'a> { pub struct EncoderWriter<'a, W: 'a> {
writer: &'a mut W, writer: &'a mut W,
_size_limit: SizeLimit _size_limit: SizeLimit