From a8680ce1d8f601754eea211c56c37d021fb0a3b3 Mon Sep 17 00:00:00 2001 From: Victor Koenders Date: Tue, 12 Oct 2021 18:18:43 +0200 Subject: [PATCH] Started working on documentation --- src/config.rs | 87 +++++++++++++++++++++++++++++ src/error.rs | 19 ++++++- src/lib.rs | 151 ++++++++++++++++++++++++++++++-------------------- 3 files changed, 195 insertions(+), 62 deletions(-) diff --git a/src/config.rs b/src/config.rs index ad549c3..6531ab6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,16 +1,99 @@ +//! The config module is used to change the behavior of bincode's encoding and decoding logic. +//! +//! *Important* make sure you use the same config for encoding and decoding, or else bincode will not work properly. +//! +//! To use a config, first create a type of [struct@Default]. This type will implement trait [Config] for further configuration. +//! +//! ``` +//! use bincode::config::{Config, Default}; +//! let config = Default +//! // pick one of: +//! .with_big_endian() +//! .with_little_endian() +//! // pick one of: +//! .with_variable_int_encoding() +//! .with_fixed_int_encoding(); +//! ``` +//! +//! See [Config] for more information on the configuration options. + pub(crate) use self::internal::*; use core::marker::PhantomData; +/// The config trait that is implemented by all types returned by this function, as well as [struct@Default]. +/// +/// The following methods are mutually exclusive and will overwrite each other. The last call to one of these methods determines the behavior of the configuration: +/// +/// - [with_little_endian] and [with_big_endian] +/// - [with_fixed_int_encoding] and [with_variable_int_encoding] +/// +/// +/// [with_little_endian]: #method.with_little_endian +/// [with_big_endian]: #method.with_big_endian +/// [with_fixed_int_encoding]: #method.with_fixed_int_encoding +/// [with_variable_int_encoding]: #method.with_variable_int_encoding pub trait Config: InternalConfig + Copy + Clone + Sized { + /// Makes bincode encode all integer types in big endian. fn with_big_endian(self) -> BigEndian { BigEndian { _pd: PhantomData } } + + /// Makes bincode encode all integer types in little endian. fn with_little_endian(self) -> LittleEndian { LittleEndian { _pd: PhantomData } } + + /// Makes bincode encode all integer types with a variable integer encoding. + /// + /// Encoding an unsigned integer v (of any type excepting u8) works as follows: + /// + /// 1. If `u < 251`, encode it as a single byte with that value. + /// 2. If `251 <= u < 2**16`, encode it as a literal byte 251, followed by a u16 with value `u`. + /// 3. If `2**16 <= u < 2**32`, encode it as a literal byte 252, followed by a u32 with value `u`. + /// 4. If `2**32 <= u < 2**64`, encode it as a literal byte 253, followed by a u64 with value `u`. + /// 5. If `2**64 <= u < 2**128`, encode it as a literal byte 254, followed by a + /// u128 with value `u`. + /// + /// Then, for signed integers, we first convert to unsigned using the zigzag algorithm, + /// and then encode them as we do for unsigned integers generally. The reason we use this + /// algorithm is that it encodes those values which are close to zero in less bytes; the + /// obvious algorithm, where we encode the cast values, gives a very large encoding for all + /// negative values. + /// + /// The zigzag algorithm is defined as follows: + /// + /// ```ignore + /// fn zigzag(v: Signed) -> Unsigned { + /// match v { + /// 0 => 0, + /// v if v < 0 => |v| * 2 - 1 + /// v if v > 0 => v * 2 + /// } + /// } + /// ``` + /// + /// And works such that: + /// + /// ```ignore + /// assert_eq!(zigzag(0), 0); + /// assert_eq!(zigzag(-1), 1); + /// assert_eq!(zigzag(1), 2); + /// assert_eq!(zigzag(-2), 3); + /// assert_eq!(zigzag(2), 4); + /// assert_eq!(zigzag(i64::min_value()), u64::max_value()); + /// ``` + /// + /// Note that u256 and the like are unsupported by this format; if and when they are added to the + /// language, they may be supported via the extension point given by the 255 byte. fn with_variable_int_encoding(self) -> Varint { Varint { _pd: PhantomData } } + + /// Fixed-size integer encoding. + /// + /// * Fixed size integers are encoded directly + /// * Enum discriminants are encoded as u32 + /// * Lengths and usize are encoded as u64 fn with_fixed_int_encoding(self) -> Fixint { Fixint { _pd: PhantomData } } @@ -28,6 +111,7 @@ impl InternalConfig for Default { const ALLOW_TRAILING: bool = true; } +#[doc(hidden)] #[derive(Copy, Clone)] pub struct BigEndian { _pd: PhantomData, @@ -40,6 +124,7 @@ impl InternalConfig for BigEndian { const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; } +#[doc(hidden)] #[derive(Copy, Clone)] pub struct LittleEndian { _pd: PhantomData, @@ -52,6 +137,7 @@ impl InternalConfig for LittleEndian { const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; } +#[doc(hidden)] #[derive(Copy, Clone)] pub struct Fixint { _pd: PhantomData, @@ -64,6 +150,7 @@ impl InternalConfig for Fixint { const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; } +#[doc(hidden)] #[derive(Copy, Clone)] pub struct Varint { _pd: PhantomData, diff --git a/src/error.rs b/src/error.rs index b21c6df..7ff8824 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,20 +1,29 @@ +//! Errors that can be encounting by Encoding and Decoding. + +/// Errors that can be encountered by encoding a type #[non_exhaustive] #[derive(Debug)] pub enum EncodeError { - InvalidIntEncoding, + /// The writer ran out of storage. UnexpectedEnd, + /// The targetted writer encountered an `std::io::Error` #[cfg(feature = "std")] Io { + /// The encountered error error: std::io::Error, + /// The amount of bytes that were written before the error occured index: usize, }, } +/// Errors that can be encounted by decoding a type #[non_exhaustive] #[derive(Debug)] pub enum DecodeError { + /// The reader reached its end but more bytes were expected. UnexpectedEnd, + /// Invalid type was found. The decoder tried to read type `expected`, but found type `found` instead. InvalidIntegerType { /// The type that was being read from the reader @@ -22,12 +31,20 @@ pub enum DecodeError { /// The type that was encoded in the data found: IntegerType, }, + + /// Invalid enum variant was found. The decoder tried to decode variant index `found`, but the variant index should be between `min` and `max`. UnexpectedVariant { + /// The min index of the enum. Usually this is `0`. min: u32, + + /// the max index of the enum. max: u32, + + // The index of the enum that the decoder encountered found: u32, }, + /// The decoder tried to decode a `str`, but an utf8 error was encountered. Utf8(core::str::Utf8Error), } diff --git a/src/lib.rs b/src/lib.rs index 71e4cb9..2701499 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,61 +1,90 @@ -#![no_std] - -//! Bincode is a crate for encoding and decoding using a tiny binary -//! serialization strategy. Using it, you can easily go from having -//! an object in memory, quickly serialize it to bytes, and then -//! deserialize it back just as fast! - -#![doc(html_root_url = "https://docs.rs/bincode/2.0.0-dev")] -#![crate_name = "bincode"] -#![crate_type = "rlib"] -#![crate_type = "dylib"] - -#[cfg(feature = "alloc")] -extern crate alloc; -#[cfg(any(feature = "std", test))] -extern crate std; - -mod features; -pub(crate) mod varint; - -pub use features::*; - -pub mod config; -pub mod de; -pub mod enc; -pub mod error; - -use config::Config; - -pub fn encode_into_slice( - val: E, - dst: &mut [u8], -) -> Result { - encode_into_slice_with_config(val, dst, config::Default) -} - -pub fn encode_into_slice_with_config( - val: E, - dst: &mut [u8], - _config: C, -) -> Result { - let writer = enc::write::SliceWriter::new(dst); - let mut encoder = enc::Encoder::<_, C>::new(writer); - val.encode(&mut encoder)?; - Ok(encoder.into_writer().bytes_written()) -} - -pub fn decode<'__de, D: de::BorrowDecodable<'__de>>( - src: &'__de [u8], -) -> Result { - decode_with_config(src, config::Default) -} - -pub fn decode_with_config<'__de, D: de::BorrowDecodable<'__de>, C: Config>( - src: &'__de [u8], - _config: C, -) -> Result { - let reader = de::read::SliceReader::new(src); - let mut decoder = de::Decoder::<_, C>::new(reader, _config); - D::borrow_decode(&mut decoder) -} +#![no_std] + +//! Bincode is a crate for encoding and decoding using a tiny binary +//! serialization strategy. Using it, you can easily go from having +//! an object in memory, quickly serialize it to bytes, and then +//! deserialize it back just as fast! +//! +//! # Serde +//! +//! Starting from bincode 2, serde is now an optional dependency. If you want to use serde, please enable the `serde` feature. See [Features](#features) for more information. +//! +//! # Features +//! +//! |Name |Default?|Supported types for Encodeable/Decodeable|Enabled methods |Other| +//! |------|--------|-----------------------------------------|-----------------------------------------------------------------|-----| +//! |std | Yes ||`decode_from[_with_config]` and `encode_into_write[_with_config]`| +//! |alloc | Yes |`Vec`, `HashMap`, `BinaryHeap`, `BTreeMap`, `BTreeSet`, `LinkedList`, `VecDeque`, `Box`, `Arc`, `Rc`, `Cow`|`encode_to_vec[_with_config]`| +//! |derive| Yes |||Enables the `Encodeable` and `Decodeable` derive macro| +//! |serde | No ||`serde_decode_from[_with_config]`, `serde_encode_into[_with_config]`|Also enables `_to_vec` when `alloc` is enabled| + +#![doc(html_root_url = "https://docs.rs/bincode/2.0.0-dev")] +#![crate_name = "bincode"] +#![crate_type = "rlib"] +#![crate_type = "dylib"] + +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(any(feature = "std", test))] +extern crate std; + +mod features; +pub(crate) mod varint; + +pub use features::*; + +pub mod config; +pub mod de; +pub mod enc; +pub mod error; + +use config::Config; + +/// Encode the given value into the given slice. Returns the amount of bytes that have been written. +/// +/// Will take the [Default] configuration. See the [config] module for more information. +/// +/// [Default]: config/struct.Default.html +pub fn encode_into_slice( + val: E, + dst: &mut [u8], +) -> Result { + encode_into_slice_with_config(val, dst, config::Default) +} + +/// Encode the given value into the given slice. Returns the amount of bytes that have been written. +/// +/// See the [config] module for more information on configurations. +pub fn encode_into_slice_with_config( + val: E, + dst: &mut [u8], + _config: C, +) -> Result { + let writer = enc::write::SliceWriter::new(dst); + let mut encoder = enc::Encoder::<_, C>::new(writer); + val.encode(&mut encoder)?; + Ok(encoder.into_writer().bytes_written()) +} + +/// Attempt to decode a given type `D` from the given slice. +/// +/// Will take the [Default] configuration. See the [config] module for more information. +/// +/// [Default]: config/struct.Default.html +pub fn decode<'__de, D: de::BorrowDecodable<'__de>>( + src: &'__de [u8], +) -> Result { + decode_with_config(src, config::Default) +} + +/// Attempt to decode a given type `D` from the given slice. +/// +/// See the [config] module for more information on configurations. +pub fn decode_with_config<'__de, D: de::BorrowDecodable<'__de>, C: Config>( + src: &'__de [u8], + _config: C, +) -> Result { + let reader = de::read::SliceReader::new(src); + let mut decoder = de::Decoder::<_, C>::new(reader, _config); + D::borrow_decode(&mut decoder) +}