Crate scroll [] [src]

Scroll

        _______________
   ()==(              (@==()
        '______________'|
          |             |
          |   ἀρετή     |
        __)_____________|
   ()==(               (@==()
        '--------------'

Scroll implements several traits for read/writing generic containers (byte buffers are currently implemented by default). Most familiar will likely be the Pread trait, which at its basic takes an immutable reference to self, an immutable offset to read at, (and a parsing context, more on that later), and then returns the deserialized value.

Because self is immutable, all reads can be performed in parallel and hence are trivially parallelizable.

A simple example demonstrates its flexibility:

use scroll::{ctx, Pread};
let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef];

// we can use the Buffer type that scroll provides, or use it on regular byte slices (or anything that impl's `AsRef<[u8]>`)
//let buffer = scroll::Buffer::new(bytes);
let b = &bytes[..];

// reads a u32 out of `b` with Big Endian byte order, at offset 0
let i: u32 = b.pread_with(0, scroll::BE).unwrap();
// or a u16 - specify the type either on the variable or with the beloved turbofish
let i2 = b.pread_with::<u16>(2, scroll::BE).unwrap();

// We can also skip the ctx by calling `pread`.
// for the primitive numbers, this will default to the host machine endianness (technically it is whatever default `Ctx` the target type is impl'd for)
let byte: u8 = b.pread(0).unwrap();
let i3: u32 = b.pread(0).unwrap();

// this will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound
let byte: scroll::Result<i64> = b.pread(0);

// we can also get str and byte references from the underlying buffer/bytes using `pread_slice`
let slice = b.pread_slice::<str>(0, 2).unwrap();
let byte_slice: &[u8] = b.pread_slice(0, 2).unwrap();

// here is an example of parsing a uleb128 custom datatype, which
// uses the `ctx::DefaultCtx`
let leb128_bytes: [u8; 5] = [0xde | 128, 0xad | 128, 0xbe | 128, 0xef | 128, 0x1];
// parses a uleb128 (variable length encoded integer) from the above bytes
let uleb128: u64 = leb128_bytes.pread::<scroll::Uleb128>(0).unwrap().into();
assert_eq!(uleb128, 0x01def96deu64);

// finally, we can also parse out custom datatypes, or types with lifetimes
// if they implement the conversion trait `TryFromCtx`; here we parse a C-style \0 delimited &str (safely)
let hello: &[u8] = b"hello_world\0more words";
let hello_world: &str = hello.pread(0).unwrap();
assert_eq!("hello_world", hello_world);

// ... and this parses the string if its space separated!
let spaces: &[u8] = b"hello world some junk";
let world: &str = spaces.pread_with(6, ctx::SPACE).unwrap();
assert_eq!("world", world);

Advanced Uses

Scroll is designed to be highly configurable - it allows you to implement various context (Ctx) sensitive traits, which then grants the implementor automatic uses of the Pread/Gread and/or Pwrite/Gwrite traits.

For example, suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary byte buffer. In order to do this, we need to provide a TryFromCtx impl for our datatype.

In particular, if we do this for the [u8] target, using the convention (usize, YourCtx), you will automatically get access to calling pread_with::<YourDatatype> on arrays of bytes.

use scroll::{self, ctx, Pread, BE};
 
struct Data<'a> {
  name: &'a str,
  id: u32,
}
 
// we could use a `(usize, endian::Scroll)` if we wanted
#[derive(Debug, Clone, Copy, Default)]
struct DataCtx { pub size: usize, pub endian: scroll::Endian }
 
// note the lifetime specified here
impl<'a> ctx::TryFromCtx<'a, (usize, DataCtx)> for Data<'a> {
  type Error = scroll::Error;
  // and the lifetime annotation on `&'a [u8]` here
  fn try_from_ctx (src: &'a [u8], (offset, DataCtx {size, endian}): (usize, DataCtx))
    -> Result<Self, Self::Error> {
    let name = src.pread_slice::<str>(offset, size)?;
    let id = src.pread_with(offset+size, endian)?;
    Ok(Data { name: name, id: id })
  }
}
 
let bytes = scroll::Buffer::new(b"UserName\x01\x02\x03\x04");
let data = bytes.pread_with::<Data>(0, DataCtx { size: 8, endian: BE }).unwrap();
assert_eq!(data.id, 0x01020304);
assert_eq!(data.name.to_string(), "UserName".to_string());

Please see the Pread documentation examples

Modules

ctx

Generic context-aware conversion traits, for automatic downstream extension of Pread, et. al

Structs

Buffer

A byte buffer which is versed in both the Greater and Lesser arts

Sleb128

An signed leb128 integer

Uleb128

An unsigned leb128 integer

Enums

Endian

The endianness (byte order) of a stream of bytes

Error

A custom Scroll error

Constants

BE

Big Endian byte order context

LE

Little Endian byte order context

LEB128

This context instructs the underlying Pread implementor to parse as a variable length integer.

NATIVE

The machine's native byte order

NETWORK

Network byte order context

Traits

Gread

The Greater Read (Gread) reads a value at a mutable offset, and increments the offset by the size of the interpreted value.

Gwrite

The Greater Write (Gwrite) writes a value into its mutable insides, at a mutable offset

Measure

Whether Self has a length that is measurable, and in what Self::Units

Pread

A very generic, contextual pread interface in Rust. Allows completely parallelized reads, as Self is immutable

Pwrite

Writes into Self at an offset of type I using a Ctx

TryOffset

Attempt to add an offset for a given N's size, used to compute error values in Gread, or return the N's size in units the same as the offset

Type Definitions

Leb128

A variable length integer parsing Ctx, compatible with the standard integer endian-aware parsing context

Result