1 [![Build Status](https://travis-ci.org/m4b/scroll.svg?branch=master)](https://travis-ci.org/m4b/scroll) 2## Scroll - cast some magic 3 4```text 5 _______________ 6 ()==( (@==() 7 '______________'| 8 | | 9 | ἀρετή | 10 __)_____________| 11 ()==( (@==() 12 '--------------' 13 14``` 15 16### Documentation 17 18https://docs.rs/scroll 19 20### Usage 21 22Add to your `Cargo.toml` 23 24```toml, no_test 25[dependencies] 26scroll = "0.10" 27``` 28 29### Overview 30 31Scroll 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. 32 33Because self is immutable, _**all** reads can be performed in parallel_ and hence are trivially parallelizable. 34 35A simple example demonstrates its flexibility: 36 37```rust 38use scroll::{ctx, Pread, LE}; 39 40fn main() -> Result<(), scroll::Error> { 41 let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef]; 42 43 // reads a u32 out of `b` with the endianness of the host machine, at offset 0, turbofish-style 44 let number: u32 = bytes.pread::<u32>(0)?; 45 // ...or a byte, with type ascription on the binding. 46 let byte: u8 = bytes.pread(0)?; 47 48 //If the type is known another way by the compiler, say reading into a struct field, we can omit the turbofish, and type ascription altogether! 49 50 // If we want, we can explicitly add a endianness to read with by calling `pread_with`. 51 // The following reads a u32 out of `b` with Big Endian byte order, at offset 0 52 let be_number: u32 = bytes.pread_with(0, scroll::BE)?; 53 // or a u16 - specify the type either on the variable or with the beloved turbofish 54 let be_number2 = bytes.pread_with::<u16>(2, scroll::BE)?; 55 56 // Scroll has core friendly errors (no allocation). This will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound 57 let byte: scroll::Result<i64> = bytes.pread(0); 58 59 // Scroll is extensible: as long as the type implements `TryWithCtx`, then you can read your type out of the byte array! 60 61 // We can parse out custom datatypes, or types with lifetimes 62 // if they implement the conversion trait `TryFromCtx`; here we parse a C-style \0 delimited &str (safely) 63 let hello: &[u8] = b"hello_world\0more words"; 64 let hello_world: &str = hello.pread(0)?; 65 assert_eq!("hello_world", hello_world); 66 67 // ... and this parses the string if its space separated! 68 use scroll::ctx::*; 69 let spaces: &[u8] = b"hello world some junk"; 70 let world: &str = spaces.pread_with(6, StrCtx::Delimiter(SPACE))?; 71 assert_eq!("world", world); 72 Ok(()) 73} 74``` 75 76### Deriving `Pread` and `Pwrite` 77 78Scroll implements a custom derive that can provide `Pread` and `Pwrite` implementations for your structs. 79 80```rust 81use scroll::{Pread, Pwrite, BE}; 82 83#[derive(Pread, Pwrite)] 84struct Data { 85 one: u32, 86 two: u16, 87 three: u8, 88} 89 90fn main() -> Result<(), scroll::Error> { 91 let bytes: [u8; 7] = [0xde, 0xad, 0xbe, 0xef, 0xfa, 0xce, 0xff]; 92 // Read a single `Data` at offset zero in big-endian byte order. 93 let data: Data = bytes.pread_with(0, BE)?; 94 assert_eq!(data.one, 0xdeadbeef); 95 assert_eq!(data.two, 0xface); 96 assert_eq!(data.three, 0xff); 97 98 // Write it back to a buffer 99 let mut out: [u8; 7] = [0; 7]; 100 out.pwrite_with(data, 0, BE)?; 101 assert_eq!(bytes, out); 102 Ok(()) 103} 104``` 105 106This feature is **not** enabled by default, you must enable the `derive` feature in Cargo.toml to use it: 107 108```toml, no_test 109[dependencies] 110scroll = { version = "0.10", features = ["derive"] } 111``` 112 113# `std::io` API 114 115Scroll can also read/write simple types from a `std::io::Read` or `std::io::Write` implementor. The built-in numeric types are taken care of for you. If you want to read a custom type, you need to implement the `FromCtx` (_how_ to parse) and `SizeWith` (_how_ big the parsed thing will be) traits. You must compile with default features. For example: 116 117```rust 118use std::io::Cursor; 119use scroll::IOread; 120 121fn main() -> Result<(), scroll::Error> { 122 let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; 123 let mut bytes = Cursor::new(bytes_); 124 125 // this will bump the cursor's Seek 126 let foo = bytes.ioread::<usize>()?; 127 // ..ditto 128 let bar = bytes.ioread::<u32>()?; 129 Ok(()) 130} 131``` 132 133Similarly, we can write to anything that implements `std::io::Write` quite naturally: 134 135```rust 136use scroll::{IOwrite, LE, BE}; 137use std::io::{Write, Cursor}; 138 139fn main() -> Result<(), scroll::Error> { 140 let mut bytes = [0x0u8; 10]; 141 let mut cursor = Cursor::new(&mut bytes[..]); 142 cursor.write_all(b"hello")?; 143 cursor.iowrite_with(0xdeadbeef as u32, BE)?; 144 assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]); 145 Ok(()) 146} 147``` 148 149# Advanced Uses 150 151Scroll 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` and/or `Pwrite` traits. 152 153For example, suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary 154byte buffer. In order to do this, we need to provide a [TryFromCtx](trait.TryFromCtx.html) impl for our datatype. 155 156In particular, if we do this for the `[u8]` target, using the convention `(usize, YourCtx)`, you will automatically get access to 157calling `pread_with::<YourDatatype>` on arrays of bytes. 158 159```rust 160use scroll::{ctx, Pread, BE, Endian}; 161 162struct Data<'a> { 163 name: &'a str, 164 id: u32, 165} 166 167// note the lifetime specified here 168impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { 169 type Error = scroll::Error; 170 // and the lifetime annotation on `&'a [u8]` here 171 fn try_from_ctx (src: &'a [u8], endian: Endian) 172 -> Result<(Self, usize), Self::Error> { 173 let offset = &mut 0; 174 let name = src.gread::<&str>(offset)?; 175 let id = src.gread_with(offset, endian)?; 176 Ok((Data { name: name, id: id }, *offset)) 177 } 178} 179 180fn main() -> Result<(), scroll::Error> { 181 let bytes = b"UserName\x00\x01\x02\x03\x04"; 182 let data = bytes.pread_with::<Data>(0, BE)?; 183 assert_eq!(data.id, 0x01020304); 184 assert_eq!(data.name.to_string(), "UserName".to_string()); 185 Ok(()) 186} 187``` 188 189Please see the official documentation, or a simple [example](examples/data_ctx.rs) for more. 190 191# Contributing 192 193Any ideas, thoughts, or contributions are welcome! 194