diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d84425e..391cf07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -134,7 +134,7 @@ jobs: ubuntu-latest-${{ matrix.rust }}-${{ env.cache-name }}- ubuntu-latest-${{ matrix.rust }}- ubuntu-latest- - + - name: Check uses: actions-rs/cargo@v1 with: @@ -312,7 +312,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: doc - args: --no-deps --document-private-items + args: --no-deps --document-private-items --all-features missing-files: runs-on: ubuntu-latest diff --git a/packable/packable/README.md b/packable/packable/README.md index a11b717..8fa5ba5 100644 --- a/packable/packable/README.md +++ b/packable/packable/README.md @@ -1,62 +1,59 @@ -# packable +Packable is a binary serialization and deserialization framework. -A crate that provides a [`Packable`] trait to serialize and deserialize types. +## Design -For more information about the design of this crate please read the [`Packable`], [`unpacker`], [`packer`], -[`UnpackError`](error::UnpackError) and [`UnpackErrorExt`](error::UnpackErrorExt) documentation. +Values of a type can be serialized and deserialized if the type implements the +`Packable` trait. The serialization strategy used for each type is up to the +user. However, `Packable` can also be derived, this provides a consistent +serialization strategy. -## Motivation +For more information about the design of this crate please read the `Packable`, +`unpacker`, `packer`, `UnpackError` and `UnpackErrorExt` documentation. -This crate was written as a `no_std` replacement for the -[`Packable` serialization framework](https://github.com/iotaledger/bee/blob/c08f1bac7170fe5cb650ddc347ac4d483bb9036a/bee-common/bee-common/src/packable.rs) -used in the Bee node implementation for Chrysalis Part 2. +## `no_std` compatibility -### The old `Packable` trait +Packable is `no_std` compatible. This is achieved by introducing the `Packer` +and `Unpacker` traits to abstract away any IO operation without relying on +`std::io`. This has the additional benefit of allowing us to pack and unpack +values from different kinds of buffers. -The need for a serialization API existed before Coordicide. Efforts to satisfy this need culminated with the -introduction of the `Packable` trait in the `bee-common` crate during the Chrysalis part 2 period. -Most of the design decisions behind this crate were done to simplify the serialization of the -[IOTA protocol messages](https://github.com/iotaledger/protocol-rfcs/pull/0017). -The proposed trait was the following: +## Types that implement `Packable` -```rust -use std::io::{Read, Write}; +The `Packable` trait is implemented for every sized integer type by encoding +the value as an array of bytes in little-endian order. -pub trait Packable { - type Error: std::fmt::Debug; +Booleans are packed following Rust's data layout, meaning that `true` is packed +as a `1` byte and `false` as a `0` byte. However, boolean unpacking is less +strict and unpacks any non-zero byte as `true`. - fn packed_len(&self) -> usize; +Types such as `Box<[T]>`, `[T; N]` and `Option` implement `Packable` if `T` +implements `Packable`. - fn pack(&self, writer: &mut W) -> Result<(), Self::Error>; +This crate also provides bounded integers under the `bounded` module which have +additional syntactical checks to guarantee that the deserialized values are +in-bounds. It is also possible to serialize and deserialize sequences of values +by using the types provided in the `prefix` module, which represent linear +sequences of values with a length prefix. - fn unpack_inner(reader: &mut R) -> Result - where - Self: Sized; -} -``` -The main issue with this trait is that it cannot be used in a `no_std` environment because it depends explicitly on -the [`std::io`] API, whose transition to the [`core`] crate has not been decided yet. -Another issue is that the `Error` type is used to represent three different kinds of errors: +Check the `Packable` `impl` section for further information. -- Writing errors: Raised when there are issues while writing bytes. -- Reading errors: Raised when there are issues while reading bytes. -- Deserialization errors: Raised when the bytes being used to create a value are invalid for the data layout of such -value. +## Features -## Replacing [`std::io`] +### `std` -We introduced the [`Packer`](packer::Packer) and [`Unpacker`](unpacker::Unpacker) taits to abstract away any IO -operation without relying on [`std::io`]. This has the additional benefit of allowing us to pack and unpack values -from different kinds of buffers. +This feature implements `Error` for all the error types provided by this crate. -## Types that implement [`Packable`] +### `io` -The [`Packable`] trait is implemented for every integer type by encoding the value as an array of bytes in -little-endian order. Booleans are packed following Rust's data layout, meaning that `true` is packed as a `1` byte -and `false` as a `0` byte. However, boolean unpacking is less strict and unpacks any non-zero byte as `true`. -Additional implementations of [`Packable`] are provided for [`Vec`](std::vec::Vec), `Box<[T]>`, `[T; N]` and -[`Option`] if T implements [`Packable`]. +This feature provides the types `IoPacker` and `IoUnpacker` which allow packing +and unpacking from values whose types implement `Write` and `Read` +respectively. -Check the [`Packable`] `impl` section for further information. +### `usize` + +This feature implements `Packable` for `usize`, `isize`, `Vec`, `Box<[T]>` +and `String`. This is done serializing and deserializing pointer sized integers +as 64-bit integers. This feature will not work for targets with a pointer width +larger than 64. License: Apache-2.0 diff --git a/packable/packable/src/lib.rs b/packable/packable/src/lib.rs index 5278c80..07d8acb 100644 --- a/packable/packable/src/lib.rs +++ b/packable/packable/src/lib.rs @@ -1,64 +1,63 @@ // Copyright 2021-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -//! A crate that provides a [`Packable`] trait to serialize and deserialize types. +//! Packable is a binary serialization and deserialization framework. //! -//! For more information about the design of this crate please read the [`Packable`], [`unpacker`], [`packer`], -//! [`UnpackError`](error::UnpackError) and [`UnpackErrorExt`](error::UnpackErrorExt) documentation. +//! # Design //! -//! # Motivation +//! Values of a type can be serialized and deserialized if the type implements the [`Packable`] +//! trait. The serialization strategy used for each type is up to the user. However, [`Packable`] +//! can also be derived, this provides a consistent serialization strategy. //! -//! This crate was written as a `no_std` replacement for the -//! [`Packable` serialization framework](https://github.com/iotaledger/bee/blob/c08f1bac7170fe5cb650ddc347ac4d483bb9036a/bee-common/bee-common/src/packable.rs) -//! used in the Bee node implementation for Chrysalis Part 2. +//! For more information about the design of this crate please read the [`Packable`], [`unpacker`], +//! [`packer`], [`UnpackError`](error::UnpackError) and [`UnpackErrorExt`](error::UnpackErrorExt) +//! documentation. //! -//! ## The old `Packable` trait +//! # `no_std` compatibility //! -//! The need for a serialization API existed before Coordicide. Efforts to satisfy this need culminated with the -//! introduction of the `Packable` trait in the `bee-common` crate during the Chrysalis part 2 period. -//! Most of the design decisions behind this crate were done to simplify the serialization of the -//! [IOTA protocol messages](https://github.com/iotaledger/protocol-rfcs/pull/0017). -//! The proposed trait was the following: +//! Packable is `no_std` compatible. This is achieved by introducing the [`Packer`](packer::Packer) +//! and [`Unpacker`](unpacker::Unpacker) traits to abstract away any IO operation without relying on +//! [`std::io`]. This has the additional benefit of allowing us to pack and unpack values from +//! different kinds of buffers. //! -//! ``` -//! use std::io::{Read, Write}; +//! # Types that implement [`Packable`] //! -//! pub trait Packable { -//! type Error: std::fmt::Debug; +//! The [`Packable`] trait is implemented for every sized integer type by encoding the value as an +//! array of bytes in little-endian order. //! -//! fn packed_len(&self) -> usize; +//! Booleans are packed following Rust's data layout, meaning that `true` is packed as a `1` byte +//! and `false` as a `0` byte. However, boolean unpacking is less strict and unpacks any non-zero +//! byte as `true`. //! -//! fn pack(&self, writer: &mut W) -> Result<(), Self::Error>; +//! Types such as `Box<[T]>`, `[T; N]` and [`Option`] implement [`Packable`] if `T` implements +//! [`Packable`]. //! -//! fn unpack_inner(reader: &mut R) -> Result -//! where -//! Self: Sized; -//! } -//! ``` -//! The main issue with this trait is that it cannot be used in a `no_std` environment because it depends explicitly on -//! the [`std::io`] API, whose transition to the [`core`] crate has not been decided yet. -//! Another issue is that the `Error` type is used to represent three different kinds of errors: +//! This crate also provides bounded integers under the [`mod@bounded`] module which have additional +//! syntactical checks to guarantee that the deserialized values are in-bounds. It is also possible +//! to serialize and deserialize sequences of values by using the types provided in the [`prefix`] +//! module, which represent linear sequences of values with a length prefix. //! -//! - Writing errors: Raised when there are issues while writing bytes. -//! - Reading errors: Raised when there are issues while reading bytes. -//! - Deserialization errors: Raised when the bytes being used to create a value are invalid for the data layout of such -//! value. +//! Check the [`Packable`] `impl` section for further information. //! -//! # Replacing [`std::io`] +//! # Features //! -//! We introduced the [`Packer`](packer::Packer) and [`Unpacker`](unpacker::Unpacker) taits to abstract away any IO -//! operation without relying on [`std::io`]. This has the additional benefit of allowing us to pack and unpack values -//! from different kinds of buffers. +//! ## `std` //! -//! # Types that implement [`Packable`] +//! This feature implements [`Error`](std::error::Error) for all the error types provided by this +//! crate. //! -//! The [`Packable`] trait is implemented for every integer type by encoding the value as an array of bytes in -//! little-endian order. Booleans are packed following Rust's data layout, meaning that `true` is packed as a `1` byte -//! and `false` as a `0` byte. However, boolean unpacking is less strict and unpacks any non-zero byte as `true`. -//! Additional implementations of [`Packable`] are provided for [`Vec`](std::vec::Vec), `Box<[T]>`, `[T; N]` and -//! [`Option`] if T implements [`Packable`]. +//! ## `io` //! -//! Check the [`Packable`] `impl` section for further information. +//! This feature provides the types [`IoPacker`](packer::IoPacker) and +//! [`IoUnpacker`](unpacker::IoUnpacker) which allow packing and unpacking from values whose types +//! implement [`Write`](std::io::Write) and [`Read`](std::io::Read) respectively. +//! +//! ## `usize` +//! +//! This feature implements [`Packable`] for [`usize`], [`isize`], [`Vec`](std::vec::Vec), +//! `Box<[T]>` and [`String`](std::string::String). This is done serializing and deserializing +//! pointer sized integers as 64-bit integers. This feature will not work for targets with a +//! pointer width larger than 64. #![cfg_attr(not(feature = "std"), no_std)] #![deny(missing_docs)] diff --git a/packable/packable/src/packable/mod.rs b/packable/packable/src/packable/mod.rs index 2ed62c4..dac3749 100644 --- a/packable/packable/src/packable/mod.rs +++ b/packable/packable/src/packable/mod.rs @@ -108,23 +108,21 @@ use core::{convert::AsRef, fmt::Debug}; /// The derive implementation can be tweaked using `#[packable(...)]` attributes. /// /// ## Tags for enums -/// /// A very common pattern when implementing `Packable` for enums consists in introducing a prefix /// value to differentiate each variant of the enumeration when unpacking, this prefix value is /// known as a `tag`. The type of the `tag` is specified with the `#[packable(tag_type = ...)]` -/// attribute and it can only be one of `u8`, `u16`, `u32` or `u64`. The `tag` value used for each -/// variant is specified with the `#[packable(tag = ...)]` attribute and can only contain integer -/// literal without any type prefixes (e.g. `42` is valid but `42u8` is not). +/// attribute and it can only be one of `[u8]`, `[u16]`, `[u32]` or `[u64]`. The `tag` value used +/// for each variant is specified with the `#[packable(tag = ...)]` attribute and can only contain +/// integer literal without any type prefixes (e.g. `42` is valid but `42u8` is not). /// -/// In the example above, the `tag` type is `u8`, the `Nothing` variant has a `tag` value of `0` +/// In the example above, the `tag` type is `[u8]`, the `Nothing` variant has a `tag` value of `0` /// and the `Just` variant has a `tag` value of `1`. This means that the packed version of -/// `Maybe::Nothing` is `[0u8]` and the packed version of `Maybe::Just(7)` is `[1u8, 0u8, 0u8, 0u8, -/// 7u8]`. +/// `Maybe::Nothing` is `[0]` and the packed version of `Maybe::Just(7)` is `[1, 0, 0, 0, 7]`. /// /// The `tag_type` and `tag` attributes are mandatory for enums unless the enum has a -/// `#[repr(...)]` attribute identifiern which case the `repr` type will be used as the `tag_type` and each -/// variant discriminant will be used as the `tag`. The `tag_type` and `tag` attributes take -/// precedence over the `repr` attribute. +/// `#[repr(...)]` attribute identifier, in which case the `repr` type will be used as the +/// `tag_type` and each variant discriminant will be used as the `tag`. The `tag_type` and `tag` +/// attributes take precedence over the `repr` attribute. /// /// ## The `UnpackError` associated type /// @@ -132,11 +130,13 @@ use core::{convert::AsRef, fmt::Debug}; /// specify the `UnpackError` associated type. The macro also provides sensible defaults for cases /// when the attribute is not used. /// -/// For structs, the default `UnpackError` type is the `UnpackError` of any of the fields or -/// `core::convert::Infallible` in case the struct has no fields. +/// For structs, the default [`UnpackError`](Packable::UnpackError) type is the +/// [`UnpackError`](Packable::UnpackError) of any of the fields type or +/// [`Infallible`](core::convert::Infallible) in case the struct has no fields. /// -/// For enums, the default `UnpackError` type is `UnknownTagError` where `T` is the type -/// specified according to the `tag_type` or `repr` attributes. +/// For enums, the default [`UnpackError`](Packable::UnpackError) type is +/// [`UnknownTagError`](crate::error::UnknownTagError) where `T` is the type specified according +/// to the `tag_type` or `repr` attributes. /// /// Following the example above, `Maybe::UnpackError` is `UnknownTagError` because no /// `unpack_error` attribute was specified.