byte 0.2.2

A low-level, zero-copy, panic-free, binary serializer and deserializer. (parser and encoder)
Documentation

Byte

build status crates.io docs.rs

A low-level, zero-copy, panic-free, binary serializer and deserializer (parser and encoder)

This crate is inspired by m4b/scroll

Documentation

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. So it's similar to crate nom but more ligthweight and specialized for operating binary in low-level or 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.

All functionality is kept minimum in order to work in more situations. 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, and it must be a mutable referece of usize, which will be increaed by the size operation consumed. Serializing types usually requires some context such as the endian for numbers, in such situations, read_with is used and we can pass context as the second parameter.

Primitives

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);

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] have references to the byte slice so there is no copy when read and it has the same lifetime as the byte slice.

use byte::*;
use byte::ctx::{Str, NULL};

let bytes: &[u8] = b"hello, world!\0more";
let str: &str = bytes.read_with(&mut 0, Str::Delimiter(NULL)).unwrap();
assert_eq!(str, "hello, world!");

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.

Byte Representation

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

Note that the passed-in bytes is implicitly splitted by offset and should be read at head. And the type Result is an alias defind in Byte 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 incread 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(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(offset, self.name.len() as u16, endian)?;
        bytes.write(offset, self.name)?;
        bytes.write(offset, self.enabled)?;

        Ok(*offset)
    }
}

Usage

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);

Contribution

All kinds of contribution are welcomed.

  • Issus. Feel free to open an issue when you find typos, bugs, or have any question.
  • Pull requests. New collection, better implementation, more tests, more documents and typo fixes are all welcomed.

License

Licensed under MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)