Skip to main content

Crate dusk_bytes

Crate dusk_bytes 

Source
Expand description

§dusk-bytes

dusk-bytes is a small, no_std friendly crate that helps you implement fixed-size (de)serialization for your types using const generics.

A type that can be represented by exactly N bytes implements Serializable<N>. From there, the crate provides convenience traits to:

  • deserialize from slices and byte readers (DeserializableSlice).
  • parse hex strings (ParseHexStr).
  • parse hex literals at compile time (hex()).
  • and format types as hex (Hex / HexDebug).

This crate is used as the foundation for a number of Dusk types where a compact, allocation-free byte representation is desirable.

§Features

  • #![no_std] (no alloc required).
  • Const-generic byte sizes (e.g. Serializable<32>).
  • Default helpers that work with custom error types via the BadLength and InvalidChar traits.
  • Built-in Serializable implementations for common integer primitives (little-endian).

§Quick start

Implement Serializable<N> for your type:

use dusk_bytes::{DeserializableSlice, ParseHexStr, Serializable};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct Point {
    x: u16,
    y: u16,
}

impl Serializable<4> for Point {
    type Error = dusk_bytes::Error;

    fn from_bytes(buf: &[u8; 4]) -> Result<Self, Self::Error> {
        let x = u16::from_le_bytes([buf[0], buf[1]]);
        let y = u16::from_le_bytes([buf[2], buf[3]]);
        Ok(Self { x, y })
    }

    fn to_bytes(&self) -> [u8; 4] {
        let mut out = [0u8; 4];
        out[0..2].copy_from_slice(&self.x.to_le_bytes());
        out[2..4].copy_from_slice(&self.y.to_le_bytes());
        out
    }
}

// `DeserializableSlice` is auto-implemented for any `Serializable` type.
let p = Point::from_slice(&[1, 0, 2, 0]).unwrap();
assert_eq!(p, Point { x: 1, y: 2 });

// `ParseHexStr` is also auto-implemented.
let p2 = Point::from_hex_str("01000200").unwrap();
assert_eq!(p2, p);

§Hex parsing

§Runtime: ParseHexStr::from_hex_str

from_hex_str parses the first N * 2 characters of a string slice (two hex characters per byte).

  • If the string is shorter than N * 2, it returns a BadLength error.
  • If a non-hex character is found, it returns an InvalidChar error.
  • If the string is longer, extra characters are ignored.

§Compile-time: hex()

hex() is a const fn that parses an ASCII hex byte string like b"deadbeef" into a byte array.

use dusk_bytes::hex;

const MAGIC: [u8; 4] = hex(b"deadbeef");
assert_eq!(MAGIC, [0xde, 0xad, 0xbe, 0xef]);

The input byte string must have an even length (two hex digits per output byte). Invalid characters cause a compile-time panic during const evaluation.

§Hex formatting: Hex and HexDebug

dusk-bytes re-exports two derive macros from the companion derive-hex crate:

  • #[derive(Hex)] implements core::fmt::LowerHex and core::fmt::UpperHex.
  • #[derive(HexDebug)] additionally implements core::fmt::Debug and formats the value as hex for {:x?} / {:X?}.

Both derives expect your type to expose a to_bytes() method (which the Serializable trait already provides).

use dusk_bytes::HexDebug;

#[derive(Copy, Clone, HexDebug)]
struct IdPrefix([u8; 4]);

impl IdPrefix {
    pub fn to_bytes(&self) -> [u8; 4] {
        self.0
    }
}

let p = IdPrefix([0xde, 0xad, 0xbe, 0xef]);
assert_eq!(format!("{:x}", p), "deadbeef");
assert_eq!(format!("{:#x}", p), "0xdeadbeef");
assert_eq!(format!("{:x?}", p), "deadbeef");

§Readers and writers

For embedded / no_std environments, the crate provides minimal Read / Write traits (inspired by std::io) and implements them for:

  • &[u8] (reader)
  • &mut [u8] (writer)
use dusk_bytes::{DeserializableSlice, Read, Serializable, Write};

let value: u32 = 0x01020304;

let mut buf = [0u8; 4];
{
    let mut w = &mut buf[..];
    w.write(&value.to_bytes()).unwrap();
}

let mut r = &buf[..];
let parsed = u32::from_reader(&mut r).unwrap();
assert_eq!(parsed, value);
assert!(r.is_empty());

§Error handling

The crate provides a small default Error enum that is used by the built-in primitive implementations.

If you want to keep your own error type, implement:

Those traits are used by the default implementations of DeserializableSlice and ParseHexStr.

§License

Licensed under the Mozilla Public License 2.0 (MPL-2.0).

Enums§

Error
Dusk Bytes operation error variants

Traits§

BadLength
Trait to be implemented for the associated Error used in [DeserializableSlice::from_slice]. The function is called if the slice given is smaller than the mandatory size for the struct.
DeserializableSlice
An optional trait used to implement [from_slice] on top of types that uses Serializable trait. The default implementation makes use of Serializable trait to provide the necessary deserialization functionality without additional code from the consumer.
InvalidChar
Trait to be implemented for the associated Error used in [ParseHexStr::from_hex_str]. The function is called if an invalid character is found in the string slice.
ParseHexStr
An optional trait used to parse a string slice for types that implements the Serializable trait. The default implementation makes use of Serializable trait to provide the necessary parsing functionality without additional code from the consumer.
Read
Implementors of the Read trait are called ‘readers’.
Serializable
The core trait used to implement [from_bytes] and [to_bytes]
Write
Implementors of the Write trait are sometimes called ‘writers’.

Functions§

hex
A constant funtion to parse a bytes string representing hexadecimals (e.g. b"fe12c6" ) into bytes (e.g [0xfe, 0x12, 0xc6]). If a smaller destination buffer is provided, the value will be truncated (e.g [0xfe, 0x12]); if a bigger destination buffer is provided, it will be padded with zeroes (e.g. `[0xfe, 0x12, 0xc6, 0x0, 0x0])

Derive Macros§

Hex
HexDebug