Crate byte [] [src]

A low-level, zero-copy, panic-free, binary serializer and deserializer

Usage

First, add the following to your Cargo.toml:

[dependencies]
byte = "0.2"

Next, add this to your crate root:

extern crate byte;

Byte is no_std library; it can directly be used in a #![no_std] situation or crate.

Overview

Byte is mainly used to encode and decode binary data with standard or protocol, such as network TCP packages and hardware communication packages. It's similar to crate nom but more ligthweight and specialized for operating binary in low-level and hardware programing.

Byte delivers two core traits TryRead and TryWrite. Types implement these traits can be serialize into or deserialize from byte slices. Byte slices [u8] derives methods read() and write() to serialize, deserialize and handle offset.

Small and general is kept in mind in this library. For example, Byte can take byte slice from MMap to read binary file, or take heap-allocated byte buffer from Bytes.

Example

Byte consumes byte slice continuously. The first parameter of read is offset, instructing the position to begin, which will be increaed by size the operation consumed. Reading a types usually requires some context, such as the endian of number, in such situations, read_with is used and we can pass the context as the second parameter.

use byte::*;
 
let bytes: &[u8] = &[0xde, 0xad, 0xbe, 0xef];
 
let offset = &mut 0;
let num = bytes.read_with::<u32>(offset, BE).unwrap();
assert_eq!(num, 0xdeadbeef);
assert_eq!(*offset, 4);
use byte::*;
use byte::ctx::{Str, NULL};
 
let bytes: &[u8] = b"hello, world!\0dump";
 
let offset = &mut 0;
let str = bytes.read_with::<&str>(offset, Str::Delimiter(NULL)).unwrap();
assert_eq!(str, "hello, world!");
assert_eq!(*offset, 14);

Byte supports language primitives by default.

  • &str (with context Str)
  • &[u8] (with context Byte)
  • u8, i8, u64, f64 ... (with context Endian)
  • bool

&str and &[u8] are references to the byte slice so there is no copy when read() and it has the same lifetime as the byte slice.

Define custom serializable type

In this example, we defined a custom type Header, which have a varibal-length name and a bool field. We implement TryRead and TryWrite to enable this type to be serialzed and deserialized.

Below is an example of communication protocol

Protocol Byte Representation

|       | Length of name (Big Endian) |                Name              | Enabled |
| ----- | --------------------------- | ---- | ---- | ---- | ---- | ---- | ------- |
| Byte  | 0            | 5            | 'H'  | 'E'  | 'L'  | 'L'  | 'O'  | 0       |

Below is the code to realize a reader and writer to it.

Note that the bytes passed in is splitted by offset and should be read at head. Type Result is an alias as core::result::Result<(T, size), byte::Error>, where the size is the number of bytes read or write consumed and it will be used to increase the offset.

use byte::*;
use byte::ctx::*;

struct Header<'a> {
    name: &'a str,
    enabled: bool,
}

impl<'a> TryRead<'a, Endian> for Header<'a> {
    fn try_read(bytes: &'a [u8], endian: Endian) -> Result<(Self, usize)> {
        let offset = &mut 0;

        let name_len = bytes.read_with::<u16>(offset, endian)? as usize;
        let header = Header {
            name: bytes.read_with::<&str>(offset, Str::Len(name_len))?,
            enabled: bytes.read::<bool>(offset)?,
        };

        Ok((header, *offset))
    }
}

impl<'a> TryWrite<Endian> for Header<'a> {
    fn try_write(self, bytes: &mut [u8], endian: Endian) -> Result<usize> {
        let offset = &mut 0;

        bytes.write_with::<u16>(offset, self.name.len() as u16, endian)?;
        bytes.write::<&str>(offset, self.name)?;
        bytes.write::<bool>(offset, self.enabled)?;

        Ok(*offset)
    }
}

Usage

Be careful when using this code, it's not being tested!
let bytes = [0, 5, b"H"[0], b"E"[0], b"L"[0], b"L"[0], b"O"[0], 0];

let header: Header = bytes.read_with(&mut 0, BE).unwrap();
assert_eq!(header.name, "HELLO");
assert_eq!(header.enabled, false);

let mut write = [0u8; 8];
write.write_with(&mut 0, header, BE).unwrap();
assert_eq!(write, bytes);

Modules

ctx

Context for primitives

Structs

Iter

Iterator that read values of same type from bytes.

Enums

Error

The error type for serializing and deserializing.

Constants

BE

Big Endian byte order

LE

Little Endian byte order

Traits

BytesExt

Extension methods for byte slices.

TryRead

A data structure that can be deserialized. Types implement this trait can be read() from byte slice.

TryWrite

A data structure that can be serialized. Types implement this trait can be write() into byte slice.

Functions

check_len

A shorthand function to check whether the given length exceeded the length of slice; return Err(Error::Incomplete) if not.

Type Definitions

Result

A specialized Result type for Byte