1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! bytevec: A Rust serialization library that uses byte vectors
//! ============================================================
//! 
//! bytevec takes advantage of Rust's concise and stable type system to
//! serialize data objects to a byte vector (`Vec<u8>`) and back.
//! 
//! What does it do?
//! ----------------
//! Rust has a very powerful type system with predictable sizes for most
//! types, starting with the primitive types, so it's fairly easy to convert
//! any type to a collection of bytes and convert it back. This library intends
//! to give the user the means of converting a given type instance to a byte vector
//! and store it or send it through the wire to another device, with the possibility
//! of getting the value back from the byte vector anytime using the library traits.
//! 
//! Of course, Rust isn't magical enough to implement the traits to serialize
//! the functions automatically, as every type has its quirks. This library
//! uses two traits to give a type the functionality it needs to do that: 
//! `ByteEncodable` and `ByteDecodable`.
//! 
//! ###The `ByteEncodable` trait
//! A type that implements this trait is able to use the `encode` method that 
//! yields a `Vec<u8>` byte sequence. Seems prone to failure right? Of course it is,
//! internally it uses `unsafe` blocks to extract the bytes of a given type, so 
//! it can be pretty unsafe. That's why it always checks for any possible error and
//! returns the vector wrapped around a `BVEncodeResult` instance. If everything
//! goes `Ok`, we will be able to get a byte vector value that represents the 
//! original data structure.
//! 
//! bytevec doesn't actually do a 1:1 conversion of the bytes of the original
//! type instance, as not every Rust type is stored on the stack. For any type
//! that wraps a heap stored value, it will give a representation of the 
//! underlying value.
//! 
//! bytevec implements `ByteEncodable` out of the box for the following types:
//! 
//! - The integral types: `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`
//! 
//! - The floating point types: `f32` and `f64`
//! 
//! - `char`, `str` and `String`
//! 
//! - [`Vec`](http://doc.rust-lang.org/stable/std/vec/struct.Vec.html)
//! 
//! - [`&[T]`](http://doc.rust-lang.org/stable/std/primitive.slice.html)
//! 
//! - [`HashMap`](http://doc.rust-lang.org/stable/std/collections/struct.HashMap.html)
//! 
//! - [`HashSet`](http://doc.rust-lang.org/stable/std/collections/struct.HashSet.html)
//! 
//! - Tuples with up to 12 elements
//! 
//! - Custom `struct`s
//! 
//! For collections and other structures, automatic implementation of bytevec
//! requires that all of its underlying elements implement the `ByteEncodable`
//! trait.
//! 
//! ###The bytevec serialization format
//! bytevec doesn't follow any particular serialization format. It follows simple
//! rules when translating some type value to bytes:
//! 
//! - For a primitive type such as the integral types, floating points
//! or char that have fixed size, it will just grab the bytes and put them 
//! on a `u8` buffer of the same length as the size of the type through 
//! [`std::mem::transmute`][1]. These types are converted to and from little endian on
//! serialization and deserialization respectively.
//! 
//! - String and str don't store their byte count, it's up to their container (if any)
//! to store the size of the byte buffer of the string.
//! 
//! - Complex data structures such as `struct`s, tuples and collections need to store
//! the sizes of their underlying data fields. These sizes are stored as values of a generic
//! integral type parameter `Size` that should be provided in every call of the methods of the
//! `ByteEncodable` and `ByteDecodable` traits. This type parameter is propagated to the
//! serialization and deserialization operations of the contained data fields. The type parameter
//! `Size` is constrained by the `BVSize` trait. Currently the types that implement this trait
//! are `u8`, `u16`, `u32` and `u64`. Users should select the type for the `Size` type parameter
//! according to the expected size of the byte buffer. If the expected size exceeds the 
//! 2<sup>32</sup> byte length limit of `u32`, use `u64` instead.
//! 
//! - For structures with defined fields such as a custom `struct` or a tuple,
//! it will store the size of each field on a sequence of `Size` values at the start
//! of the slice segment for the structure, followed by the actual bytes of 
//! the values of the fields.
//! 
//! - For any collection with variable length, it will first store the length
//! (in elements, not byte count) on a `Size` value, followed by the byte count
//! (yes, of `Size`) of each element, and then the actual values of the elements.
//! All of this done in order, order is important, the same order of serialization
//! is the order of deserialization.
//! 
//! - All serializable values can be nested, so any structure that implements 
//! `ByteEncodable` containing a `Vec`, `String`, or another structure that also implements
//! `ByteEncodable` will be serialized along all its fields.
//! 
//! ###The `ByteDecodable` trait
//! Given a byte vector retrieved from memory, a file, or maybe a TCP connection,
//! the user will be able to pass the vector to the `decode` method of
//! a type that implements the `ByteDecodable` trait. `decode` will do a few checks 
//! on the byte vector and if the required sizes matches, it will yield a type instance wrapped 
//! in a `BVDecodeResult`. If the size doesn't match, or if some other conversion problem 
//! arises, it will yield a `ByteVecError` detailing the failure.
//! 
//! Almost all of the out of the box implementations of `ByteEncodable` also
//! implement `ByteDecodable`, but some of them, particularly the slices and 
//! the tuple references don't make sense when deserialized, as they can't
//! point to the original data they were referencing. This is usually a problem
//! that requires some tweaking, but bytevec allows data conversion from byte
//! buffers that were originally referenced data to a new instance of an owned data type,
//! as long as the size requirements are the same. This way, slice data can
//! be assigned to a `Vec` instance for example, as long as they share the same 
//! type of the underlying elements.
//! 
//! The `ByteDecodable` trait also provides the `decode_max` method, which like `decode`, it
//! accepts the byte buffer to deserialize, but additionally, this method also accepts
//! a `limit` argument. This parameter is compared to the length of the `u8` buffer and
//! if the buffer length is greater than it, it will return a `BadSizeDecodeError`,
//! otherwise it will return the result of `decode` on the byte buffer.
//! 
//! ###Example: Serialization and deserialization of a slice
//! 
//! ```rust
//! # #[macro_use]
//! # extern crate bytevec;
//! #
//! # use bytevec::{ByteEncodable, ByteDecodable};
//! # fn main() {
//! let slice = &["Rust", "Is", "Awesome!"];
//! let bytes = slice.encode::<u32>().unwrap();
//! let vec = <Vec<String>>::decode::<u32>(&bytes).unwrap();
//! assert_eq!(vec, slice);
//! # }
//! ```
//! [1]: http://doc.rust-lang.org/stable/std/mem/fn.transmute.html

#[macro_use]
mod macros;
mod traits;
pub mod errors;
mod impls;

pub use traits::{ByteEncodable, ByteDecodable};
pub type BVEncodeResult<T> = Result<T, errors::ByteVecError>;
pub type BVDecodeResult<T> = Result<T, errors::ByteVecError>;
pub use impls::BVSize;