Crate packtool[−][src]
Expand description
packtool
is a packing library. Useful to define how serializing
and deserializing data from a type level definition.
Example
Unit types
unit types can be packed. What this means is that the object is known to have the same constant value. That way it is possible to define values that are expected to be found and to be the same.
All Packed
unit structures must have a #[packed(value = ...)]
attribute. The value can be set to any literal except: bool
, float
.
use packtool::{Packed, View}; /// a unit that is always the utf8 string `"my protocol"` /// and takes 11 bytes in the packed structure #[derive(Packed)] #[packed(value = "my protocol")] pub struct ProtocolPrefix; /// a unit that is always `4` and takes 1 byte long #[derive(Packed)] #[packed(value = 0b0000_0100u8)] pub struct OtherUnit(); /// a unit that is always `0xcafe` and takes 4 bytes /// in the packed structure #[derive(Packed)] #[packed(value = 0xcafeu32)] pub struct LastButNotLeast {} const SLICE: &[u8] = b"my protocol"; let view: View<'_, ProtocolPrefix> = View::try_from_slice(SLICE)?;
Here we are expecting the ProtocolPrefix
to always have the
same value in the packed representation. When serializing the
ProtocolPrefix
, the value
will be set with these 11
characters.
Enumeration
Only enumerations without fields are allowed for now.
use packtool::{Packed, View}; #[derive(Packed)] #[repr(u8)] pub enum Version { V1 = 1, V2 = 2, } let view: View<'_, Version> = View::try_from_slice(SLICE)?; assert!(matches!(view.unpack(), Version::V1));
the repr(...)
is necessary in order to set a size to the enum.
use packtool::Packed; #[derive(Packed)] pub enum Color { Red = 1, Green = 2, Blue = -1 }
combining packed objects
It is possible to compose packed objects in named or tuple structures.
use packtool::Packed; #[derive(Packed)] #[packed(value = "packcoin")] pub struct Tag; /// 1 byte that will be used to store a version number #[derive(Packed)] #[repr(u8)] pub enum Version { V1 = 1, V2 = 2, } /// 8 bytes that will be used to store a block number #[derive(Packed)] pub struct BlockNumber(u32, u32); /// 9 bytes packed header #[derive(Packed)] pub struct Header { tag: Tag, version: Version, block_number: BlockNumber }
Each of the packed objects have a view accessor for each fields:
- for named fields, the name of the accessor is the name of the field
- for tuples, the name of the accessor is the index of the field preceded by an underscore (
_
):_0
,_1
etc.
let tag: View<'_, Tag> = Header::tag(header); let block_number: View<'_, BlockNumber> = Header::block_number(header); let epoch: View<'_, u32> = BlockNumber::_0(block_number); let slot: u32 = BlockNumber::_1(block_number).unpack();
You can rename the accessor with the attribute accessor
:
#[derive(Packed)] pub struct BlockNumber( #[packed(accessor = "epoch")] u32, #[packed(accessor = "slot")] u32 ); let epoch = BlockNumber::epoch(block_number); // instead of _0 let slot = BlockNumber::slot(block_number).unpack(); // instead of _1
It is also possible to prevent the accessor to be created. You can set
the accessor with a literal boolean to say if you want the accessor or
not. true
will simply means the default case (use the index of the field
or use the name for the name of the accessor):
#[derive(Packed)] pub struct Hash( #[packed(accessor = true)] [u8; 32] ); let bytes = Hash::_0(hash);
However if you set it to false
there will be no accessor created for you:
#[derive(Packed)] pub struct Hash( #[packed(accessor = false)] [u8; 32] ); let bytes = Hash::_0(hash);
Macros
helper method to create an [Error
] is the assumption
fails.
Structs
a owned slice of memory containing the Packed
view of a slice in memory as a packed structure of type T
Enums
error associated to unpacking or creating [View
] of [Packed
] types.
Traits
trait to define how a fixed size Packed object is serialized into a byte slice representation.