Build Status

Scroll - cast some magic

``` ____ ()==( (@==() '___'| | | | ἀρετή | )_____| ()==( (@==() '--------------'

```

Documentation

https://docs.rs/scroll

Usage

Add to your Cargo.toml

toml [dependencies] scroll = "0.5.0"

Overview

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:

```rust 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.preadwith(0, scroll::BE).unwrap(); // or a u16 - specify the type either on the variable or with the beloved turbofish let i2 = b.preadwith::(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 = b.pread(0);

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

// here is an example of parsing a uleb128 custom datatype, which // uses the ctx::DefaultCtx let leb128bytes: [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 = leb128bytes.pread::(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"helloworld\0more words"; let helloworld: &str = hello.pread(0).unwrap(); asserteq!("helloworld", hello_world);

// ... and this parses the string if its space separated! let spaces: &[u8] = b"hello world some junk"; let world: &str = spaces.preadwith(6, ctx::SPACE).unwrap(); asserteq!("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::<YourDatatype> on arrays of bytes.

```rust 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 tryfromctx (src: &'a [u8], (offset, DataCtx {size, endian}): (usize, DataCtx)) -> Result { let name = src.pread_slice::(offset, size)?; let id = src.pread(offset+size, endian)?; Ok(Data { name: name, id: id }) } }

let bytes = scroll::Buffer::new(b"UserName\x01\x02\x03\x04"); let data = bytes.pread::(0, DataCtx { size: 8, endian: BE }).unwrap(); asserteq!(data.id, 0x01020304); asserteq!(data.name.tostring(), "UserName".tostring()); ```

Please see the official documentation, or a simple example for more.

Contributing

There are several open issues right now which I'd like clarified/closed before releasing on crates.io. Keep in mind, the primary use case is an immutable byte parser/reader, which Pread implements, and which I want backwards compability at this point.

In fact, if you look at the tests, most of them actually are just testing the APIs remain unbroken (still compiling), which is very easy to do with something this generic.

However, I believe there are some really interesting ideas to pursue, particularly in terms of the more generic contexts that scroll allows.

Any ideas, thoughts, or contributions are welcome!