scroll-0.10.1/.gitignore010064400017500001750000000000471355762526200133160ustar0000000000000000target Cargo.lock *~ *.idea scroll.iml scroll-0.10.1/.travis.yml010064400017500001750000000003321355762526200134340ustar0000000000000000language: rust rust: - stable - beta - nightly matrix: allow_failures: - rust: nightly script: - cargo build --verbose --features=derive - RUST_BACKTRACE=1 cargo test --all --verbose --features=derive scroll-0.10.1/CHANGELOG.md010064400017500001750000000012721355762526200131400ustar0000000000000000# Changelog All notable changes to this project will be documented in this file. Before 1.0, this project does not adhere to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [0.10.0] - unreleased ### Added - scroll is now 2018 compliant, thanks @lzutao: https://github.com/m4b/scroll/pull/49 - scroll_derive now lives in scroll repo itself ### Removed - BREAKING: removed units/size generics in SizeWith, thanks @willglynn: https://github.com/m4b/scroll/pull/45 ## [0.9.1] - 2018-9-22 ### Added - pread primitive references: https://github.com/m4b/scroll/pull/35 - u128/i128 support: https://github.com/m4b/scroll/pull/32 - CStr support: https://github.com/m4b/scroll/pull/30 scroll-0.10.1/Cargo.toml.orig010064400017500001750000000012051355762526200142120ustar0000000000000000[package] name = "scroll" version = "0.10.1" authors = ["m4b ", "Ted Mielczarek "] readme = "README.md" edition = "2018" keywords = ["bytes", "endian", "immutable", "pread", "pwrite"] repository = "https://github.com/m4b/scroll" license = "MIT" documentation = "https://docs.rs/scroll" description = "A suite of powerful, extensible, generic, endian-aware Read/Write traits for byte buffers" [dependencies] scroll_derive = { version = "0.10", optional = true, path = "scroll_derive" } [features] default = ["std"] std = [] derive = ["scroll_derive"] [dev-dependencies] rayon = "1" byteorder = "1" scroll-0.10.1/Cargo.toml0000644000000022250000000000000104470ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "scroll" version = "0.10.1" authors = ["m4b ", "Ted Mielczarek "] description = "A suite of powerful, extensible, generic, endian-aware Read/Write traits for byte buffers" documentation = "https://docs.rs/scroll" readme = "README.md" keywords = ["bytes", "endian", "immutable", "pread", "pwrite"] license = "MIT" repository = "https://github.com/m4b/scroll" [dependencies.scroll_derive] version = "0.10" optional = true [dev-dependencies.byteorder] version = "1" [dev-dependencies.rayon] version = "1" [features] default = ["std"] derive = ["scroll_derive"] std = [] scroll-0.10.1/LICENSE010064400017500001750000000020561355762526200123350ustar0000000000000000The MIT License (MIT) Copyright (c) m4b 2016 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. scroll-0.10.1/README.md010064400017500001750000000153011355762526200126040ustar0000000000000000 [![Build Status](https://travis-ci.org/m4b/scroll.svg?branch=master)](https://travis-ci.org/m4b/scroll) ## Scroll - cast some magic ```text _______________ ()==( (@==() '______________'| | | | ἀρετή | __)_____________| ()==( (@==() '--------------' ``` ### Documentation https://docs.rs/scroll ### Usage Add to your `Cargo.toml` ```toml, no_test [dependencies] scroll = "0.10" ``` ### 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, LE}; fn parse() -> Result<(), scroll::Error> { let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef]; // reads a u32 out of `b` with the endianness of the host machine, at offset 0, turbofish-style let number: u32 = bytes.pread::(0)?; // ...or a byte, with type ascription on the binding. let byte: u8 = bytes.pread(0)?; //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! // If we want, we can explicitly add a endianness to read with by calling `pread_with`. // The following reads a u32 out of `b` with Big Endian byte order, at offset 0 let be_number: u32 = bytes.pread_with(0, scroll::BE)?; // or a u16 - specify the type either on the variable or with the beloved turbofish let be_number2 = bytes.pread_with::(2, scroll::BE)?; // Scroll has core friendly errors (no allocation). This will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound let byte: scroll::Result = bytes.pread(0); // Scroll is extensible: as long as the type implements `TryWithCtx`, then you can read your type out of the byte array! // We can 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)?; assert_eq!("hello_world", hello_world); // ... and this parses the string if its space separated! use scroll::ctx::*; let spaces: &[u8] = b"hello world some junk"; let world: &str = spaces.pread_with(6, StrCtx::Delimiter(SPACE))?; assert_eq!("world", world); Ok(()) } fn main() { parse().unwrap(); } ``` ### Deriving `Pread` and `Pwrite` Scroll implements a custom derive that can provide `Pread` and `Pwrite` implementations for your structs. ```no_test #[macro_use] extern crate scroll_derive; use scroll::{Pread, Pwrite, BE}; #[derive(Pread, Pwrite)] struct Data { one: u32, two: u16, three: u8, } fn parse() -> Result<(), scroll::Error> { let bytes: [u8; 7] = [0xde, 0xad, 0xbe, 0xef, 0xfa, 0xce, 0xff]; // Read a single `Data` at offset zero in big-endian byte order. let data: Data = bytes.pread_with(0, BE)?; assert_eq!(data.one, 0xdeadbeef); assert_eq!(data.two, 0xface); assert_eq!(data.three, 0xff); // Write it back to a buffer let mut out: [u8; 7] = [0; 7]; out.pwrite_with(data, 0, BE)?; assert_eq!(bytes, out); Ok(()) } fn main() { parse().unwrap(); } ``` This feature is **not** enabled by default, you must enable the `derive` feature in Cargo.toml to use it: ```toml, no_test [dependencies] scroll = { version = "0.10", features = ["derive"] } ``` # `std::io` API Scroll 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: ```rust use std::io::Cursor; use scroll::IOread; fn parse_io() -> Result<(), scroll::Error> { let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; let mut bytes = Cursor::new(bytes_); // this will bump the cursor's Seek let foo = bytes.ioread::()?; // ..ditto let bar = bytes.ioread::()?; Ok(()) } fn main() { parse_io().unwrap(); } ``` Similarly, we can write to anything that implements `std::io::Write` quite naturally: ```rust use scroll::{IOwrite, LE, BE}; use std::io::{Write, Cursor}; fn write_io() -> Result<(), scroll::Error> { let mut bytes = [0x0u8; 10]; let mut cursor = Cursor::new(&mut bytes[..]); cursor.write_all(b"hello")?; cursor.iowrite_with(0xdeadbeef as u32, BE)?; assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]); Ok(()) } fn main() { write_io().unwrap(); } ``` # 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` and/or `Pwrite` 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](trait.TryFromCtx.html) 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::` on arrays of bytes. ```rust use scroll::{ctx, Pread, BE, Endian}; struct Data<'a> { name: &'a str, id: u32, } // note the lifetime specified here impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { type Error = scroll::Error; // and the lifetime annotation on `&'a [u8]` here fn try_from_ctx (src: &'a [u8], endian: Endian) -> Result<(Self, usize), Self::Error> { let offset = &mut 0; let name = src.gread::<&str>(offset)?; let id = src.gread_with(offset, endian)?; Ok((Data { name: name, id: id }, *offset)) } } fn parse_data() -> Result<(), scroll::Error> { let bytes = b"UserName\x00\x01\x02\x03\x04"; let data = bytes.pread_with::(0, BE)?; assert_eq!(data.id, 0x01020304); assert_eq!(data.name.to_string(), "UserName".to_string()); Ok(()) } fn main() { parse_data().unwrap(); } ``` Please see the official documentation, or a simple [example](examples/data_ctx.rs) for more. # Contributing Any ideas, thoughts, or contributions are welcome! scroll-0.10.1/benches/bench.rs010064400017500001750000000076531355762526200143740ustar0000000000000000#![feature(test)] extern crate test; use scroll::{Cread, Pread, LE}; use test::black_box; #[bench] fn bench_parallel_cread_with(b: &mut test::Bencher) { use rayon::prelude::*; let vec = vec![0u8; 1_000_000]; let nums = vec![0usize; 500_000]; b.iter(|| { let data = black_box(&vec[..]); nums.par_iter().for_each(| offset | { let _: u16 = black_box(data.cread_with(*offset, LE)); }); }); b.bytes = vec.len() as u64; } #[bench] fn bench_cread_vec(b: &mut test::Bencher) { let vec = vec![0u8; 1_000_000]; b.iter(|| { let data = black_box(&vec[..]); for val in data.chunks(2) { let _: u16 = black_box(val.cread_with(0, LE)); } }); b.bytes = vec.len() as u64; } #[bench] fn bench_cread(b: &mut test::Bencher) { const NITER: i32 = 100_000; b.iter(|| { for _ in 1..NITER { let data = black_box([1, 2]); let _: u16 = black_box(data.cread(0)); } }); b.bytes = 2 * NITER as u64; } #[bench] fn bench_pread_ctx_vec(b: &mut test::Bencher) { let vec = vec![0u8; 1_000_000]; b.iter(|| { let data = black_box(&vec[..]); for val in data.chunks(2) { let _: Result = black_box(val.pread(0)); } }); b.bytes = vec.len() as u64; } #[bench] fn bench_pread_with_unwrap(b: &mut test::Bencher) { const NITER: i32 = 100_000; b.iter(|| { for _ in 1..NITER { let data: &[u8] = &black_box([1, 2]); let _: u16 = black_box(data.pread_with(0, LE).unwrap()); } }); b.bytes = 2 * NITER as u64; } #[bench] fn bench_pread_vec(b: &mut test::Bencher) { let vec = vec![0u8; 1_000_000]; b.iter(|| { let data = black_box(&vec[..]); for val in data.chunks(2) { let _: Result = black_box(val.pread_with(0, LE)); } }); b.bytes = vec.len() as u64; } #[bench] fn bench_pread_unwrap(b: &mut test::Bencher) { const NITER: i32 = 100_000; b.iter(|| { for _ in 1..NITER { let data = black_box([1, 2]); let _: u16 = black_box(data.pread(0)).unwrap(); } }); b.bytes = 2 * NITER as u64; } #[bench] fn bench_gread_vec(b: &mut test::Bencher) { let vec = vec![0u8; 1_000_000]; b.iter(|| { let data = black_box(&vec[..]); for val in data.chunks(2) { let mut offset = 0; let _: Result = black_box(val.gread(&mut offset)); } }); b.bytes = vec.len() as u64; } #[bench] fn bench_gread_unwrap(b: &mut test::Bencher) { const NITER: i32 = 100_000; b.iter(|| { for _ in 1..NITER { let data = black_box([1, 2]); let mut offset = 0; let _: u16 = black_box(data.gread_with(&mut offset, LE).unwrap()); } }); b.bytes = 2 * NITER as u64; } #[bench] fn bench_parallel_pread_with(b: &mut test::Bencher) { use rayon::prelude::*; let vec = vec![0u8; 1_000_000]; let nums = vec![0usize; 500_000]; b.iter(|| { let data = black_box(&vec[..]); nums.par_iter().for_each(| offset | { let _: Result = black_box(data.pread_with(*offset, LE)); }); }); b.bytes = vec.len() as u64; } #[bench] fn bench_byteorder_vec(b: &mut test::Bencher) { use byteorder::ReadBytesExt; let vec = vec![0u8; 1_000_000]; b.iter(|| { let data = black_box(&vec[..]); for mut val in data.chunks(2) { let _: Result = black_box(val.read_u16::()); } }); b.bytes = vec.len() as u64; } #[bench] fn bench_byteorder(b: &mut test::Bencher) { use byteorder::ByteOrder; const NITER: i32 = 100_000; b.iter(|| { for _ in 1..NITER { let data = black_box([1, 2]); let _: u16 = black_box(byteorder::LittleEndian::read_u16(&data)); } }); b.bytes = 2 * NITER as u64; } scroll-0.10.1/examples/data_ctx.rs010064400017500001750000000013271355762526200153030ustar0000000000000000use scroll::{ctx, Endian, Pread, BE}; #[derive(Debug)] struct Data<'a> { name: &'a str, id: u32, } impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { type Error = scroll::Error; fn try_from_ctx (src: &'a [u8], endian: Endian) -> Result<(Self, usize), Self::Error> { let name = src.pread::<&'a str>(0)?; let id = src.pread_with(name.len()+1, endian)?; Ok((Data { name: name, id: id }, name.len()+4)) } } fn main() { let bytes = b"UserName\x00\x01\x02\x03\x04"; let data = bytes.pread_with::(0, BE).unwrap(); assert_eq!(data.id, 0x01020304); assert_eq!(data.name.to_string(), "UserName".to_string()); println!("Data: {:?}", &data); } scroll-0.10.1/src/ctx.rs010064400017500001750000000507511355762526200132700ustar0000000000000000//! Generic context-aware conversion traits, for automatic _downstream_ extension of `Pread`, et. al //! //! # Discussion //! //! Implementors of `TryFromCtx` automatically grant any client user of `pread, pwrite, gread, gwrite` the ability to parse their structure out of the source it has been implemented for, typically `&[u8]`. //! //! The implementor only needs to specify the error type, and the type of their size, and then implement the parsing/marshalling logic given a byte sequence, starting at the offset `pread`, et. al was called at, with the context you have implemented it for. //! //! Returning the size allows dynamic content (e.g., `&str`s) to be parsed alongside fixed size content (e.g., `u64`). The parsing context is any information you the implementor need to correctly parse out your datatype - this could be the endianness of the type, more offsets, or other complex data. The only requirement is that your `Ctx` be `Copy`, and hence encourages lightweight contexts (but this isn't required of course). //! //! //! # 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, with a "parsing contex", `YourCtx`, you will automatically get access to //! calling `pread_with::(offset, your_ctx)` on arrays of bytes. //! //! In the example below, we implement `TryFromCtx` using the `Endian` parsing context provided by `scroll`, which is used to specifying the endianness at which numbers should be parsed, but you could provide anything, as long as it implements `Copy`. //! //! ```rust //! use scroll::{self, ctx, Endian, Pread, BE}; //! //! struct Data<'a> { //! name: &'a str, //! id: u32, //! } //! //! impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { //! type Error = scroll::Error; //! fn try_from_ctx (src: &'a [u8], ctx: Endian) //! -> Result<(Self, usize), Self::Error> { //! let name = src.pread::<&str>(0)?; //! let id = src.pread_with(name.len() + 1, ctx)?; //! Ok((Data { name: name, id: id }, name.len() + 1 + 4)) //! } //! } //! //! let bytes = b"UserName\x00\x01\x02\x03\x04"; //! let data = bytes.pread_with::(0, BE).unwrap(); //! assert_eq!(data.id, 0x01020304); //! assert_eq!(data.name.to_string(), "UserName".to_string()); //! //! ``` use core::ptr::copy_nonoverlapping; use core::mem::transmute; use core::mem::size_of; use core::str; use core::result; #[cfg(feature = "std")] use std::ffi::{CStr, CString}; use crate::error; use crate::endian::Endian; /// A trait for measuring how large something is; for a byte sequence, it will be its length. pub trait MeasureWith { /// How large is `Self`, given the `ctx`? fn measure_with(&self, ctx: &Ctx) -> usize; } impl MeasureWith for [u8] { #[inline] fn measure_with(&self, _ctx: &Ctx) -> usize { self.len() } } impl> MeasureWith for T { #[inline] fn measure_with(&self, _ctx: &Ctx) -> usize { self.as_ref().len() } } /// The parsing context for converting a byte sequence to a `&str` /// /// `StrCtx` specifies what byte delimiter to use, and defaults to C-style null terminators. Be careful. #[derive(Debug, Copy, Clone)] pub enum StrCtx { Delimiter(u8), DelimiterUntil(u8, usize), Length(usize), } /// A C-style, null terminator based delimiter pub const NULL: u8 = 0; /// A space-based delimiter pub const SPACE: u8 = 0x20; /// A newline-based delimiter pub const RET: u8 = 0x0a; /// A tab-based delimiter pub const TAB: u8 = 0x09; impl Default for StrCtx { #[inline] fn default() -> Self { StrCtx::Delimiter(NULL) } } impl StrCtx { pub fn len(&self) -> usize { match *self { StrCtx::Delimiter(_) | StrCtx::DelimiterUntil(_, _) => 1, StrCtx::Length(_) => 0, } } pub fn is_empty(&self) -> bool { if let StrCtx::Length(_) = *self { true } else { false } } } /// Reads `Self` from `This` using the context `Ctx`; must _not_ fail pub trait FromCtx { fn from_ctx(this: &This, ctx: Ctx) -> Self; } /// Tries to read `Self` from `This` using the context `Ctx` pub trait TryFromCtx<'a, Ctx: Copy = (), This: ?Sized = [u8]> where Self: 'a + Sized { type Error; fn try_from_ctx(from: &'a This, ctx: Ctx) -> Result<(Self, usize), Self::Error>; } /// Writes `Self` into `This` using the context `Ctx` pub trait IntoCtx: Sized { fn into_ctx(self, _: &mut This, ctx: Ctx); } /// Tries to write `Self` into `This` using the context `Ctx` pub trait TryIntoCtx: Sized { type Error; fn try_into_ctx(self, _: &mut This, ctx: Ctx) -> Result; } /// Gets the size of `Self` with a `Ctx`, and in `Self::Units`. Implementors can then call `Gread` related functions /// /// The rationale behind this trait is to: /// /// 1. Prevent `gread` from being used, and the offset being modified based on simply the sizeof the value, which can be a misnomer, e.g., for Leb128, etc. /// 2. Allow a context based size, which is useful for 32/64 bit variants for various containers, etc. pub trait SizeWith { fn size_with(ctx: &Ctx) -> usize; } macro_rules! signed_to_unsigned { (i8) => {u8 }; (u8) => {u8 }; (i16) => {u16}; (u16) => {u16}; (i32) => {u32}; (u32) => {u32}; (i64) => {u64}; (u64) => {u64}; (i128) => {u128}; (u128) => {u128}; (f32) => {u32}; (f64) => {u64}; } macro_rules! write_into { ($typ:ty, $size:expr, $n:expr, $dst:expr, $endian:expr) => ({ unsafe { assert!($dst.len() >= $size); let bytes = transmute::<$typ, [u8; $size]>(if $endian.is_little() { $n.to_le() } else { $n.to_be() }); copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size); } }); } macro_rules! into_ctx_impl { ($typ:tt, $size:expr) => { impl IntoCtx for $typ { #[inline] fn into_ctx(self, dst: &mut [u8], le: Endian) { assert!(dst.len() >= $size); write_into!($typ, $size, self, dst, le); } } impl<'a> IntoCtx for &'a $typ { #[inline] fn into_ctx(self, dst: &mut [u8], le: Endian) { (*self).into_ctx(dst, le) } } impl TryIntoCtx for $typ where $typ: IntoCtx { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { if $size > dst.len () { Err(error::Error::TooBig{size: $size, len: dst.len()}) } else { <$typ as IntoCtx>::into_ctx(self, dst, le); Ok($size) } } } impl<'a> TryIntoCtx for &'a $typ { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { (*self).try_into_ctx(dst, le) } } } } macro_rules! from_ctx_impl { ($typ:tt, $size:expr) => { impl<'a> FromCtx for $typ { #[inline] fn from_ctx(src: &[u8], le: Endian) -> Self { assert!(src.len() >= $size); let mut data: signed_to_unsigned!($typ) = 0; unsafe { copy_nonoverlapping( src.as_ptr(), &mut data as *mut signed_to_unsigned!($typ) as *mut u8, $size); } (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ } } impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { if $size > src.len () { Err(error::Error::TooBig{size: $size, len: src.len()}) } else { Ok((FromCtx::from_ctx(&src, le), $size)) } } } // as ref impl<'a, T> FromCtx for $typ where T: AsRef<[u8]> { #[inline] fn from_ctx(src: &T, le: Endian) -> Self { let src = src.as_ref(); assert!(src.len() >= $size); let mut data: signed_to_unsigned!($typ) = 0; unsafe { copy_nonoverlapping( src.as_ptr(), &mut data as *mut signed_to_unsigned!($typ) as *mut u8, $size); } (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ } } impl<'a, T> TryFromCtx<'a, Endian, T> for $typ where $typ: FromCtx, T: AsRef<[u8]> { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a T, le: Endian) -> result::Result<(Self, usize), Self::Error> { let src = src.as_ref(); Self::try_from_ctx(src, le) } } }; } macro_rules! ctx_impl { ($typ:tt, $size:expr) => { from_ctx_impl!($typ, $size); }; } ctx_impl!(u8, 1); ctx_impl!(i8, 1); ctx_impl!(u16, 2); ctx_impl!(i16, 2); ctx_impl!(u32, 4); ctx_impl!(i32, 4); ctx_impl!(u64, 8); ctx_impl!(i64, 8); ctx_impl!(u128, 16); ctx_impl!(i128, 16); macro_rules! from_ctx_float_impl { ($typ:tt, $size:expr) => { impl<'a> FromCtx for $typ { #[inline] fn from_ctx(src: &[u8], le: Endian) -> Self { assert!(src.len() >= ::core::mem::size_of::()); let mut data: signed_to_unsigned!($typ) = 0; unsafe { copy_nonoverlapping( src.as_ptr(), &mut data as *mut signed_to_unsigned!($typ) as *mut u8, $size); transmute(if le.is_little() { data.to_le() } else { data.to_be() }) } } } impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { if $size > src.len () { Err(error::Error::TooBig{size: $size, len: src.len()}) } else { Ok((FromCtx::from_ctx(src, le), $size)) } } } } } from_ctx_float_impl!(f32, 4); from_ctx_float_impl!(f64, 8); into_ctx_impl!(u8, 1); into_ctx_impl!(i8, 1); into_ctx_impl!(u16, 2); into_ctx_impl!(i16, 2); into_ctx_impl!(u32, 4); into_ctx_impl!(i32, 4); into_ctx_impl!(u64, 8); into_ctx_impl!(i64, 8); into_ctx_impl!(u128, 16); into_ctx_impl!(i128, 16); macro_rules! into_ctx_float_impl { ($typ:tt, $size:expr) => { impl IntoCtx for $typ { #[inline] fn into_ctx(self, dst: &mut [u8], le: Endian) { assert!(dst.len() >= $size); write_into!(signed_to_unsigned!($typ), $size, transmute::<$typ, signed_to_unsigned!($typ)>(self), dst, le); } } impl<'a> IntoCtx for &'a $typ { #[inline] fn into_ctx(self, dst: &mut [u8], le: Endian) { (*self).into_ctx(dst, le) } } impl TryIntoCtx for $typ where $typ: IntoCtx { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { if $size > dst.len () { Err(error::Error::TooBig{size: $size, len: dst.len()}) } else { <$typ as IntoCtx>::into_ctx(self, dst, le); Ok($size) } } } impl<'a> TryIntoCtx for &'a $typ { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { (*self).try_into_ctx(dst, le) } } } } into_ctx_float_impl!(f32, 4); into_ctx_float_impl!(f64, 8); impl<'a> TryFromCtx<'a, StrCtx> for &'a str { type Error = error::Error; #[inline] /// Read a `&str` from `src` using `delimiter` fn try_from_ctx(src: &'a [u8], ctx: StrCtx) -> Result<(Self, usize), Self::Error> { let len = match ctx { StrCtx::Length(len) => len, StrCtx::Delimiter(delimiter) => src.iter().take_while(|c| **c != delimiter).count(), StrCtx::DelimiterUntil(delimiter, len) => { if len > src.len() { return Err(error::Error::TooBig{size: len, len: src.len()}); }; src .iter() .take_while(|c| **c != delimiter) .take(len) .count() } }; if len > src.len() { return Err(error::Error::TooBig{size: len, len: src.len()}); }; match str::from_utf8(&src[..len]) { Ok(res) => Ok((res, len + ctx.len())), Err(_) => Err(error::Error::BadInput{size: src.len(), msg: "invalid utf8"}) } } } impl<'a, T> TryFromCtx<'a, StrCtx, T> for &'a str where T: AsRef<[u8]> { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a T, ctx: StrCtx) -> result::Result<(Self, usize), Self::Error> { let src = src.as_ref(); TryFromCtx::try_from_ctx(src, ctx) } } impl<'a> TryIntoCtx for &'a [u8] { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result { let src_len = self.len() as isize; let dst_len = dst.len() as isize; // if src_len < 0 || dst_len < 0 || offset < 0 { // return Err(error::Error::BadOffset(format!("requested operation has negative casts: src len: {} dst len: {} offset: {}", src_len, dst_len, offset)).into()) // } if src_len > dst_len { Err(error::Error::TooBig{ size: self.len(), len: dst.len()}) } else { unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), src_len as usize) }; Ok(self.len()) } } } // TODO: make TryIntoCtx use StrCtx for awesomeness impl<'a> TryIntoCtx for &'a str { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result { let bytes = self.as_bytes(); TryIntoCtx::try_into_ctx(bytes, dst, ()) } } // TODO: we can make this compile time without size_of call, but compiler probably does that anyway macro_rules! sizeof_impl { ($ty:ty) => { impl SizeWith for $ty { #[inline] fn size_with(_ctx: &Endian) -> usize { size_of::<$ty>() } } } } sizeof_impl!(u8); sizeof_impl!(i8); sizeof_impl!(u16); sizeof_impl!(i16); sizeof_impl!(u32); sizeof_impl!(i32); sizeof_impl!(u64); sizeof_impl!(i64); sizeof_impl!(u128); sizeof_impl!(i128); sizeof_impl!(f32); sizeof_impl!(f64); sizeof_impl!(usize); sizeof_impl!(isize); impl FromCtx for usize { #[inline] fn from_ctx(src: &[u8], le: Endian) -> Self { let size = ::core::mem::size_of::(); assert!(src.len() >= size); let mut data: usize = 0; unsafe { copy_nonoverlapping( src.as_ptr(), &mut data as *mut usize as *mut u8, size); if le.is_little() { data.to_le() } else { data.to_be() } } } } impl<'a> TryFromCtx<'a, Endian> for usize where usize: FromCtx { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { let size = ::core::mem::size_of::(); if size > src.len () { Err(error::Error::TooBig{size, len: src.len()}) } else { Ok((FromCtx::from_ctx(src, le), size)) } } } impl<'a> TryFromCtx<'a, usize> for &'a[u8] { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a [u8], size: usize) -> result::Result<(Self, usize), Self::Error> { if size > src.len () { Err(error::Error::TooBig{size, len: src.len()}) } else { Ok((&src[..size], size)) } } } impl IntoCtx for usize { #[inline] fn into_ctx(self, dst: &mut [u8], le: Endian) { let size = ::core::mem::size_of::(); assert!(dst.len() >= size); let mut data = if le.is_little() { self.to_le() } else { self.to_be() }; let data = &mut data as *mut usize as *mut u8; unsafe { copy_nonoverlapping(data, dst.as_mut_ptr(), size); } } } impl TryIntoCtx for usize where usize: IntoCtx { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { let size = ::core::mem::size_of::(); if size > dst.len() { Err(error::Error::TooBig{size, len: dst.len()}) } else { >::into_ctx(self, dst, le); Ok(size) } } } #[cfg(feature = "std")] impl<'a> TryFromCtx<'a> for &'a CStr { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { let null_byte = match src.iter().position(|b| *b == 0) { Some(ix) => ix, None => return Err(error::Error::BadInput { size: 0, msg: "The input doesn't contain a null byte", }) }; let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(&src[..=null_byte]) }; Ok((cstr, null_byte+1)) } } #[cfg(feature = "std")] impl<'a> TryFromCtx<'a> for CString { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { let (raw, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(src, _ctx)?; Ok((raw.to_owned(), bytes_read)) } } #[cfg(feature = "std")] impl<'a> TryIntoCtx for &'a CStr { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result { let data = self.to_bytes_with_nul(); if dst.len() < data.len() { Err(error::Error::TooBig { size: dst.len(), len: data.len(), }) } else { unsafe { copy_nonoverlapping(data.as_ptr(), dst.as_mut_ptr(), data.len()); } Ok(data.len()) } } } #[cfg(feature = "std")] impl TryIntoCtx for CString { type Error = error::Error; #[inline] fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result { self.as_c_str().try_into_ctx(dst, ()) } } // example of marshalling to bytes, let's wait until const is an option // impl FromCtx for [u8; 10] { // fn from_ctx(bytes: &[u8], _ctx: Endian) -> Self { // let mut dst: Self = [0; 10]; // assert!(bytes.len() >= dst.len()); // unsafe { // copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr(), dst.len()); // } // dst // } // } #[cfg(test)] mod tests { use super::*; #[test] #[cfg(feature = "std")] fn parse_a_cstr() { let src = CString::new("Hello World").unwrap(); let as_bytes = src.as_bytes_with_nul(); let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(as_bytes, ()).unwrap(); assert_eq!(bytes_read, as_bytes.len()); assert_eq!(got, src.as_c_str()); } #[test] #[cfg(feature = "std")] fn round_trip_a_c_str() { let src = CString::new("Hello World").unwrap(); let src = src.as_c_str(); let as_bytes = src.to_bytes_with_nul(); let mut buffer = vec![0; as_bytes.len()]; let bytes_written = src.try_into_ctx(&mut buffer, ()).unwrap(); assert_eq!(bytes_written, as_bytes.len()); let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(&buffer, ()).unwrap(); assert_eq!(bytes_read, as_bytes.len()); assert_eq!(got, src); } } scroll-0.10.1/src/endian.rs010064400017500001750000000017611355762526200137250ustar0000000000000000#[derive(PartialEq, Eq, Copy, Debug, Clone)] /// The endianness (byte order) of a stream of bytes pub enum Endian { Little, Big, } /// Little Endian byte order context pub const LE: Endian = Endian::Little; /// Big Endian byte order context pub const BE: Endian = Endian::Big; /// Network byte order context pub const NETWORK: Endian = Endian::Big; #[cfg(target_endian = "little")] /// The machine's native byte order pub const NATIVE: Endian = LE; #[cfg(target_endian = "big")] /// The machine's native byte order pub const NATIVE: Endian = BE; impl Default for Endian { #[inline] fn default() -> Self { NATIVE } } impl From for Endian { #[inline] fn from(little_endian: bool) -> Self { if little_endian { LE } else { BE } } } impl Endian { #[inline] pub fn network() -> Endian { NETWORK } #[inline] pub fn is_little(&self) -> bool { match *self { LE => true, _ => false, } } } scroll-0.10.1/src/error.rs010064400017500001750000000040621355762526200136150ustar0000000000000000use core::fmt::{self, Display}; use core::result; #[cfg(feature = "std")] use std::io; #[cfg(feature = "std")] use std::error; #[derive(Debug)] /// A custom Scroll error pub enum Error { /// The type you tried to read was too big TooBig { size: usize, len: usize }, /// The requested offset to read/write at is invalid BadOffset(usize), BadInput{ size: usize, msg: &'static str }, #[cfg(feature = "std")] /// A custom Scroll error for reporting messages to clients Custom(String), #[cfg(feature = "std")] /// Returned when IO based errors are encountered IO(io::Error), } #[cfg(feature = "std")] impl error::Error for Error { fn description(&self) -> &str { match *self { Error::TooBig{ .. } => { "TooBig" } Error::BadOffset(_) => { "BadOffset" } Error::BadInput{ .. } => { "BadInput" } Error::Custom(_) => { "Custom" } Error::IO(_) => { "IO" } } } fn cause(&self) -> Option<&dyn error::Error> { match *self { Error::TooBig{ .. } => { None } Error::BadOffset(_) => { None } Error::BadInput{ .. } => { None } Error::Custom(_) => { None } Error::IO(ref io) => { io.source() } } } } #[cfg(feature = "std")] impl From for Error { fn from(err: io::Error) -> Error { Error::IO(err) } } impl Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Error::TooBig{ ref size, ref len } => { write! (fmt, "type is too big ({}) for {}", size, len) }, Error::BadOffset(ref offset) => { write! (fmt, "bad offset {}", offset) }, Error::BadInput{ ref msg, ref size } => { write! (fmt, "bad input {} ({})", msg, size) }, #[cfg(feature = "std")] Error::Custom(ref msg) => { write! (fmt, "{}", msg) }, #[cfg(feature = "std")] Error::IO(ref err) => { write!(fmt, "{}", err) }, } } } pub type Result = result::Result; scroll-0.10.1/src/greater.rs010064400017500001750000000123031355762526200141120ustar0000000000000000use core::ops::{Index, IndexMut, RangeFrom}; use crate::ctx::{FromCtx, IntoCtx}; /// Core-read - core, no_std friendly trait for reading basic traits from byte buffers. Cannot fail unless the buffer is too small, in which case an assert fires and the program panics. /// /// If your type implements [FromCtx](trait.FromCtx.html) then you can `cread::(offset)`. /// /// # Example /// /// ```rust /// use scroll::{ctx, Cread, LE}; /// /// #[repr(packed)] /// struct Bar { /// foo: i32, /// bar: u32, /// } /// /// impl ctx::FromCtx for Bar { /// fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { /// use scroll::Cread; /// Bar { foo: bytes.cread_with(0, ctx), bar: bytes.cread_with(4, ctx) } /// } /// } /// /// let bytes = [0xff, 0xff, 0xff, 0xff, 0xef,0xbe,0xad,0xde,]; /// let bar = bytes.cread_with::(0, LE); /// // Remember that you need to copy out fields from packed structs /// // with a `{}` block instead of borrowing them directly /// // ref: https://github.com/rust-lang/rust/issues/46043 /// assert_eq!({bar.foo}, -1); /// assert_eq!({bar.bar}, 0xdeadbeef); /// ``` pub trait Cread : Index + Index> where Ctx: Copy, { /// Reads a value from `Self` at `offset` with `ctx`. Cannot fail. /// If the buffer is too small for the value requested, this will panic. /// /// # Example /// /// ```rust /// use scroll::{Cread, BE, LE}; /// use std::i64::MAX; /// /// let bytes = [0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xef,0xbe,0xad,0xde,]; /// let foo = bytes.cread_with::(0, BE); /// let bar = bytes.cread_with::(8, LE); /// assert_eq!(foo, MAX); /// assert_eq!(bar, 0xdeadbeef); /// ``` #[inline] fn cread_with>>::Output>>(&self, offset: I, ctx: Ctx) -> N { N::from_ctx(&self[offset..], ctx) } /// Reads a value implementing `FromCtx` from `Self` at `offset`, /// with the **target machine**'s endianness. /// For the primitive types, this will be the **target machine**'s endianness. /// /// # Example /// /// ```rust /// use scroll::Cread; /// /// let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; /// let foo = bytes.cread::(0); /// let bar = bytes.cread::(8); /// #[cfg(target_endian = "little")] /// assert_eq!(foo, 1); /// #[cfg(target_endian = "big")] /// assert_eq!(foo, 0x100_0000_0000_0000); /// /// #[cfg(target_endian = "little")] /// assert_eq!(bar, 0xbeef); /// #[cfg(target_endian = "big")] /// assert_eq!(bar, 0xefbe0000); /// ``` #[inline] fn cread>>::Output>>(&self, offset: I) -> N where Ctx: Default { let ctx = Ctx::default(); N::from_ctx(&self[offset..], ctx) } } impl + Index>> Cread for R {} /// Core-write - core, no_std friendly trait for writing basic types into byte buffers. Cannot fail unless the buffer is too small, in which case an assert fires and the program panics. /// Similar to [Cread](trait.Cread.html), if your type implements [IntoCtx](trait.IntoCtx.html) then you can `cwrite(your_type, offset)`. /// /// # Example /// /// ```rust /// use scroll::{ctx, Cwrite}; /// /// #[repr(packed)] /// struct Bar { /// foo: i32, /// bar: u32, /// } /// /// impl ctx::IntoCtx for Bar { /// fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) { /// use scroll::Cwrite; /// bytes.cwrite_with(self.foo, 0, ctx); /// bytes.cwrite_with(self.bar, 4, ctx); /// } /// } /// /// let bar = Bar { foo: -1, bar: 0xdeadbeef }; /// let mut bytes = [0x0; 16]; /// bytes.cwrite::(bar, 0); /// ``` pub trait Cwrite: Index + IndexMut> { /// Writes `n` into `Self` at `offset`; uses default context. /// For the primitive types, this will be the **target machine**'s endianness. /// /// # Example /// /// ``` /// use scroll::{Cwrite, Cread}; /// let mut bytes = [0x0; 16]; /// bytes.cwrite::(42, 0); /// bytes.cwrite::(0xdeadbeef, 8); /// /// assert_eq!(bytes.cread::(0), 42); /// assert_eq!(bytes.cread::(8), 0xdeadbeef); #[inline] fn cwrite>>::Output>>(&mut self, n: N, offset: I) where Ctx: Default { let ctx = Ctx::default(); n.into_ctx(self.index_mut(offset..), ctx) } /// Writes `n` into `Self` at `offset` with `ctx` /// /// # Example /// /// ``` /// use scroll::{Cwrite, Cread, LE, BE}; /// let mut bytes = [0x0; 0x10]; /// bytes.cwrite_with::(42, 0, LE); /// bytes.cwrite_with::(0xdeadbeef, 8, BE); /// assert_eq!(bytes.cread_with::(0, LE), 42); /// assert_eq!(bytes.cread_with::(8, LE), 0xefbeadde); #[inline] fn cwrite_with>>::Output>>(&mut self, n: N, offset: I, ctx: Ctx) { n.into_ctx(self.index_mut(offset..), ctx) } } impl + IndexMut>> Cwrite for W {} scroll-0.10.1/src/leb128.rs010064400017500001750000000134371355762526200134670ustar0000000000000000use core::u8; use core::convert::{From, AsRef}; use core::result; use crate::Pread; use crate::ctx::TryFromCtx; use crate::error; #[derive(Debug, PartialEq, Copy, Clone)] /// An unsigned leb128 integer pub struct Uleb128 { value: u64, count: usize, } impl Uleb128 { #[inline] /// Return how many bytes this Uleb128 takes up in memory pub fn size(&self) -> usize { self.count } #[inline] /// Read a variable length u64 from `bytes` at `offset` pub fn read(bytes: &[u8], offset: &mut usize) -> error::Result { let tmp = bytes.pread::(*offset)?; *offset += tmp.size(); Ok(tmp.into()) } } impl AsRef for Uleb128 { fn as_ref(&self) -> &u64 { &self.value } } impl From for u64 { #[inline] fn from(uleb128: Uleb128) -> u64 { uleb128.value } } #[derive(Debug, PartialEq, Copy, Clone)] /// An signed leb128 integer pub struct Sleb128 { value: i64, count: usize, } impl Sleb128 { #[inline] /// Return how many bytes this Sleb128 takes up in memory pub fn size(&self) -> usize { self.count } #[inline] /// Read a variable length i64 from `bytes` at `offset` pub fn read(bytes: &[u8], offset: &mut usize) -> error::Result { let tmp = bytes.pread::(*offset)?; *offset += tmp.size(); Ok(tmp.into()) } } impl AsRef for Sleb128 { fn as_ref(&self) -> &i64 { &self.value } } impl From for i64 { #[inline] fn from(sleb128: Sleb128) -> i64 { sleb128.value } } // Below implementation heavily adapted from: https://github.com/fitzgen/leb128 const CONTINUATION_BIT: u8 = 1 << 7; const SIGN_BIT: u8 = 1 << 6; #[inline] fn mask_continuation(byte: u8) -> u8 { byte & !CONTINUATION_BIT } // #[inline] // fn mask_continuation_u64(val: u64) -> u8 { // let byte = val & (u8::MAX as u64); // mask_continuation(byte as u8) // } impl<'a> TryFromCtx<'a> for Uleb128 { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { let mut result = 0; let mut shift = 0; let mut count = 0; loop { let byte: u8 = src.pread(count)?; if shift == 63 && byte != 0x00 && byte != 0x01 { return Err(error::Error::BadInput{ size: src.len(), msg: "failed to parse"}) } let low_bits = u64::from(mask_continuation(byte)); result |= low_bits << shift; count += 1; shift += 7; if byte & CONTINUATION_BIT == 0 { return Ok((Uleb128 { value: result, count }, count)); } } } } impl<'a> TryFromCtx<'a> for Sleb128 { type Error = error::Error; #[inline] fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { let o = 0; let offset = &mut 0; let mut result = 0; let mut shift = 0; let size = 64; let mut byte: u8; loop { byte = src.gread(offset)?; if shift == 63 && byte != 0x00 && byte != 0x7f { return Err(error::Error::BadInput{size: src.len(), msg: "failed to parse"}) } let low_bits = i64::from(mask_continuation(byte)); result |= low_bits << shift; shift += 7; if byte & CONTINUATION_BIT == 0 { break; } } if shift < size && (SIGN_BIT & byte) == SIGN_BIT { // Sign extend the result. result |= !0 << shift; } let count = *offset - o; Ok((Sleb128{ value: result, count }, count)) } } #[cfg(test)] mod tests { use super::{Uleb128, Sleb128}; use super::super::LE; const CONTINUATION_BIT: u8 = 1 << 7; //const SIGN_BIT: u8 = 1 << 6; #[test] fn uleb_size() { use super::super::Pread; let buf = [2u8 | CONTINUATION_BIT, 1]; let bytes = &buf[..]; let num = bytes.pread::(0).unwrap(); println!("num: {:?}", &num); assert_eq!(130u64, num.into()); assert_eq!(num.size(), 2); let buf = [0x00,0x01]; let bytes = &buf[..]; let num = bytes.pread::(0).unwrap(); println!("num: {:?}", &num); assert_eq!(0u64, num.into()); assert_eq!(num.size(), 1); let buf = [0x21]; let bytes = &buf[..]; let num = bytes.pread::(0).unwrap(); println!("num: {:?}", &num); assert_eq!(0x21u64, num.into()); assert_eq!(num.size(), 1); } #[test] fn uleb128() { use super::super::Pread; let buf = [2u8 | CONTINUATION_BIT, 1]; let bytes = &buf[..]; let num = bytes.pread::(0).expect("Should read Uleb128"); assert_eq!(130u64, num.into()); assert_eq!(386, bytes.pread_with::(0, LE).expect("Should read number")); } #[test] fn uleb128_overflow() { use super::super::Pread; let buf = [2u8 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 2 | CONTINUATION_BIT, 1]; let bytes = &buf[..]; assert!(bytes.pread::(0).is_err()); } #[test] fn sleb128() { use super::super::Pread; let bytes = [0x7fu8 | CONTINUATION_BIT, 0x7e]; let num: i64 = bytes.pread::(0).expect("Should read Sleb128").into(); assert_eq!(-129, num); } } scroll-0.10.1/src/lesser.rs010064400017500001750000000151621355762526200137640ustar0000000000000000use std::io::{Result, Read, Write}; use crate::ctx::{FromCtx, IntoCtx, SizeWith}; /// An extension trait to `std::io::Read` streams; this only deserializes simple types, like `u8`, `i32`, `f32`, `usize`, etc. /// /// If you implement [`FromCtx`](trait.FromCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html) for your type, you can then `ioread::()` on a `Read`. Note: [`FromCtx`](trait.FromCtx.html) is only meant for very simple types, and should _never_ fail. /// /// **NB** You should probably add `repr(packed)` or `repr(C)` and be very careful how you implement [`SizeWith`](ctx/trait.SizeWith.html), otherwise you /// will get IO errors failing to fill entire buffer (the size you specified in `SizeWith`), or out of bound errors (depending on your impl) in `from_ctx` /// /// # Example /// ```rust /// use std::io::Cursor; /// use scroll::{self, ctx, LE, Pread, IOread}; /// /// #[repr(packed)] /// struct Foo { /// foo: i64, /// bar: u32, /// } /// /// impl ctx::FromCtx for Foo { /// fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { /// Foo { foo: bytes.pread_with::(0, ctx).unwrap(), bar: bytes.pread_with::(8, ctx).unwrap() } /// } /// } /// /// impl ctx::SizeWith for Foo { /// // our parsing context doesn't influence our size /// fn size_with(_: &scroll::Endian) -> usize { /// ::std::mem::size_of::() /// } /// } /// /// let bytes_ = [0x0b,0x0b,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; /// let mut bytes = Cursor::new(bytes_); /// let foo = bytes.ioread_with::(LE).unwrap(); /// let bar = bytes.ioread_with::(LE).unwrap(); /// assert_eq!(foo, 0xb0b); /// assert_eq!(bar, 0xbeef); /// let error = bytes.ioread_with::(LE); /// assert!(error.is_err()); /// let mut bytes = Cursor::new(bytes_); /// let foo_ = bytes.ioread_with::(LE).unwrap(); /// // Remember that you need to copy out fields from packed structs /// // with a `{}` block instead of borrowing them directly /// // ref: https://github.com/rust-lang/rust/issues/46043 /// assert_eq!({foo_.foo}, foo); /// assert_eq!({foo_.bar}, bar); /// ``` /// pub trait IOread : Read { /// Reads the type `N` from `Self`, with a default parsing context. /// For the primitive numeric types, this will be at the host machine's endianness. /// /// # Example /// ```rust /// use scroll::IOread; /// use std::io::Cursor; /// let bytes = [0xef, 0xbe]; /// let mut bytes = Cursor::new(&bytes[..]); /// let beef = bytes.ioread::().unwrap(); /// /// #[cfg(target_endian = "little")] /// assert_eq!(0xbeef, beef); /// #[cfg(target_endian = "big")] /// assert_eq!(0xefbe, beef); /// ``` #[inline] fn ioread + SizeWith>(&mut self) -> Result where Ctx: Default { let ctx = Ctx::default(); self.ioread_with(ctx) } /// Reads the type `N` from `Self`, with the parsing context `ctx`. /// **NB**: this will panic if the type you're reading has a size greater than 256. Plans are to have this allocate in larger cases. /// /// For the primitive numeric types, this will be at the host machine's endianness. /// /// # Example /// ```rust /// use scroll::{IOread, LE, BE}; /// use std::io::Cursor; /// let bytes = [0xef, 0xbe, 0xb0, 0xb0, 0xfe, 0xed, 0xde, 0xad]; /// let mut bytes = Cursor::new(&bytes[..]); /// let beef = bytes.ioread_with::(LE).unwrap(); /// assert_eq!(0xbeef, beef); /// let b0 = bytes.ioread::().unwrap(); /// assert_eq!(0xb0, b0); /// let b0 = bytes.ioread::().unwrap(); /// assert_eq!(0xb0, b0); /// let feeddead = bytes.ioread_with::(BE).unwrap(); /// assert_eq!(0xfeeddead, feeddead); /// ``` #[inline] fn ioread_with + SizeWith>(&mut self, ctx: Ctx) -> Result { let mut scratch = [0u8; 256]; let size = N::size_with(&ctx); let mut buf = &mut scratch[0..size]; self.read_exact(&mut buf)?; Ok(N::from_ctx(buf, ctx)) } } /// Types that implement `Read` get methods defined in `IOread` /// for free. impl IOread for R {} /// An extension trait to `std::io::Write` streams; this only serializes simple types, like `u8`, `i32`, `f32`, `usize`, etc. /// /// To write custom types with a single `iowrite::` call, implement [`IntoCtx`](trait.IntoCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html) for `YourType`. pub trait IOwrite: Write { /// Writes the type `N` into `Self`, with the parsing context `ctx`. /// **NB**: this will panic if the type you're writing has a size greater than 256. Plans are to have this allocate in larger cases. /// /// For the primitive numeric types, this will be at the host machine's endianness. /// /// # Example /// ```rust /// use scroll::IOwrite; /// use std::io::Cursor; /// /// let mut bytes = [0x0u8; 4]; /// let mut bytes = Cursor::new(&mut bytes[..]); /// bytes.iowrite(0xdeadbeef as u32).unwrap(); /// /// #[cfg(target_endian = "little")] /// assert_eq!(bytes.into_inner(), [0xef, 0xbe, 0xad, 0xde,]); /// #[cfg(target_endian = "big")] /// assert_eq!(bytes.into_inner(), [0xde, 0xad, 0xbe, 0xef,]); /// ``` #[inline] fn iowrite + IntoCtx>(&mut self, n: N) -> Result<()> where Ctx: Default { let ctx = Ctx::default(); self.iowrite_with(n, ctx) } /// Writes the type `N` into `Self`, with the parsing context `ctx`. /// **NB**: this will panic if the type you're writing has a size greater than 256. Plans are to have this allocate in larger cases. /// /// For the primitive numeric types, this will be at the host machine's endianness. /// /// # Example /// ```rust /// use scroll::{IOwrite, LE, BE}; /// use std::io::{Write, Cursor}; /// /// let mut bytes = [0x0u8; 10]; /// let mut cursor = Cursor::new(&mut bytes[..]); /// cursor.write_all(b"hello").unwrap(); /// cursor.iowrite_with(0xdeadbeef as u32, BE).unwrap(); /// assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]); /// ``` #[inline] fn iowrite_with + IntoCtx>(&mut self, n: N, ctx: Ctx) -> Result<()> { let mut buf = [0u8; 256]; let size = N::size_with(&ctx); let buf = &mut buf[0..size]; n.into_ctx(buf, ctx); self.write_all(buf)?; Ok(()) } } /// Types that implement `Write` get methods defined in `IOwrite` /// for free. impl IOwrite for W {} scroll-0.10.1/src/lib.rs010064400017500001750000000473271355762526200132450ustar0000000000000000//! # Scroll //! //! ```text, no_run //! _______________ //! ()==( (@==() //! '______________'| //! | | //! | ἀρετή | //! __)_____________| //! ()==( (@==() //! '--------------' //! //! ``` //! //! Scroll is a library for efficiently and easily reading/writing types from byte arrays. All the builtin types are supported, e.g., `u32`, `i8`, etc., where the type is specified as a type parameter, or type inferred when possible. In addition, it supports zero-copy reading of string slices, or any other kind of slice. The library can be used in a no_std context as well; the [Error](enum.Error.html) type only has the `IO` and `String` variants if the default features are used, and is `no_std` safe when compiled without default features. //! //! There are 3 traits for reading that you can import: //! //! 1. [Pread](trait.Pread.html), for reading (immutable) data at an offset; //! 2. [Gread](trait.Gread.html), for reading data at an offset which automatically gets incremented by the size; //! 3. [IOread](trait.IOread.html), for reading _simple_ data out of a `std::io::Read` based interface, e.g., a stream. (**Note**: only available when compiled with `std`) //! //! Each of these interfaces also have their corresponding writer versions as well, e.g., [Pwrite](trait.Pwrite.html), [Gwrite](trait.Gwrite.html), and [IOwrite](trait.IOwrite.html), respectively. //! //! Most familiar will likely be the `Pread` trait (inspired from the C function), which in our case takes an immutable reference to self, an immutable offset to read at, (and _optionally_ 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. //! //! For most usecases, you can use [scroll_derive](https://docs.rs/scroll_derive) to annotate your types with `derive(Pread, Pwrite, IOread, IOwrite, SizeWith)` to automatically add sensible derive defaults, and you should be ready to roll. For more complex usescases, you can implement the conversion traits yourself, see the [context module](ctx/index.html) for more information. //! //! # Example //! //! A simple example demonstrates its flexibility: //! //! ```rust //! use scroll::{ctx, Pread, LE}; //! let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef]; //! //! // reads a u32 out of `b` with the endianness of the host machine, at offset 0, turbofish-style //! let number: u32 = bytes.pread::(0).unwrap(); //! // ...or a byte, with type ascription on the binding. //! let byte: u8 = bytes.pread(0).unwrap(); //! //! //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! //! //! // If we want, we can explicitly add a endianness to read with by calling `pread_with`. //! // The following reads a u32 out of `b` with Big Endian byte order, at offset 0 //! let be_number: u32 = bytes.pread_with(0, scroll::BE).unwrap(); //! // or a u16 - specify the type either on the variable or with the beloved turbofish //! let be_number2 = bytes.pread_with::(2, scroll::BE).unwrap(); //! //! // Scroll has core friendly errors (no allocation). This will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound //! let byte: scroll::Result = bytes.pread(0); //! //! // Scroll is extensible: as long as the type implements `TryWithCtx`, then you can read your type out of the byte array! //! //! // We can 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! //! use scroll::ctx::*; //! let spaces: &[u8] = b"hello world some junk"; //! let world: &str = spaces.pread_with(6, StrCtx::Delimiter(SPACE)).unwrap(); //! assert_eq!("world", world); //! ``` //! //! # `std::io` API //! //! Scroll 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](trait.FromCtx.html) (_how_ to parse) and [SizeWith](ctx/trait.SizeWith.html) (_how_ big the parsed thing will be) traits. You must compile with default features. For example: //! //! ```rust //! use std::io::Cursor; //! use scroll::IOread; //! let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; //! let mut bytes = Cursor::new(bytes_); //! //! // this will bump the cursor's Seek //! let foo = bytes.ioread::().unwrap(); //! // ..ditto //! let bar = bytes.ioread::().unwrap(); //! ``` //! //! Similarly, we can write to anything that implements `std::io::Write` quite naturally: //! //! ```rust //! use scroll::{IOwrite, LE, BE}; //! use std::io::{Write, Cursor}; //! //! let mut bytes = [0x0u8; 10]; //! let mut cursor = Cursor::new(&mut bytes[..]); //! cursor.write_all(b"hello").unwrap(); //! cursor.iowrite_with(0xdeadbeef as u32, BE).unwrap(); //! assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]); //! ``` //! //! # 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` and/or `Pwrite` 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](trait.TryFromCtx.html) 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::` on arrays of bytes. //! //! ```rust //! use scroll::{self, ctx, Pread, BE, Endian}; //! //! struct Data<'a> { //! name: &'a str, //! id: u32, //! } //! //! // note the lifetime specified here //! impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { //! type Error = scroll::Error; //! // and the lifetime annotation on `&'a [u8]` here //! fn try_from_ctx (src: &'a [u8], endian: Endian) //! -> Result<(Self, usize), Self::Error> { //! let offset = &mut 0; //! let name = src.gread::<&str>(offset)?; //! let id = src.gread_with(offset, endian)?; //! Ok((Data { name: name, id: id }, *offset)) //! } //! } //! //! let bytes = b"UserName\x00\x01\x02\x03\x04"; //! let data = bytes.pread_with::(0, BE).unwrap(); //! assert_eq!(data.id, 0x01020304); //! assert_eq!(data.name.to_string(), "UserName".to_string()); //! ``` //! //! Please see the [Pread documentation examples](trait.Pread.html#implementing-your-own-reader) #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "derive")] #[allow(unused_imports)] pub use scroll_derive::{Pread, Pwrite, SizeWith, IOread, IOwrite}; #[cfg(feature = "std")] extern crate core; pub mod ctx; mod pread; mod pwrite; mod greater; mod error; mod endian; mod leb128; #[cfg(feature = "std")] mod lesser; pub use crate::endian::*; pub use crate::pread::*; pub use crate::pwrite::*; pub use crate::greater::*; pub use crate::error::*; pub use crate::leb128::*; #[cfg(feature = "std")] pub use crate::lesser::*; #[doc(hidden)] pub mod export { pub use ::core::result; pub use ::core::mem; } #[cfg(test)] mod tests { #[allow(overflowing_literals)] use super::{LE}; #[test] fn test_measure_with_bytes() { use super::ctx::MeasureWith; let bytes: [u8; 4] = [0xef, 0xbe, 0xad, 0xde]; assert_eq!(bytes.measure_with(&()), 4); } #[test] fn test_measurable() { use super::ctx::SizeWith; assert_eq!(8, u64::size_with(&LE)); } ////////////////////////////////////////////////////////////// // begin pread_with ////////////////////////////////////////////////////////////// macro_rules! pwrite_test { ($write:ident, $read:ident, $deadbeef:expr) => { #[test] fn $write() { use super::{Pwrite, Pread, BE}; let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; let b = &mut bytes[..]; b.pwrite_with::<$read>($deadbeef, 0, LE).unwrap(); assert_eq!(b.pread_with::<$read>(0, LE).unwrap(), $deadbeef); b.pwrite_with::<$read>($deadbeef, 0, BE).unwrap(); assert_eq!(b.pread_with::<$read>(0, BE).unwrap(), $deadbeef); } } } pwrite_test!(pwrite_and_pread_roundtrip_u16, u16, 0xbeef); pwrite_test!(pwrite_and_pread_roundtrip_i16, i16, 0x7eef); pwrite_test!(pwrite_and_pread_roundtrip_u32, u32, 0xbeefbeef); pwrite_test!(pwrite_and_pread_roundtrip_i32, i32, 0x7eefbeef); pwrite_test!(pwrite_and_pread_roundtrip_u64, u64, 0xbeefbeef7eef7eef); pwrite_test!(pwrite_and_pread_roundtrip_i64, i64, 0x7eefbeef7eef7eef); #[test] fn pread_with_be() { use super::{Pread}; let bytes: [u8; 2] = [0x7e, 0xef]; let b = &bytes[..]; let byte: u16 = b.pread_with(0, super::BE).unwrap(); assert_eq!(0x7eef, byte); let bytes: [u8; 2] = [0xde, 0xad]; let dead: u16 = bytes.pread_with(0, super::BE).unwrap(); assert_eq!(0xdead, dead); } #[test] fn pread() { use super::{Pread}; let bytes: [u8; 2] = [0x7e, 0xef]; let b = &bytes[..]; let byte: u16 = b.pread(0).unwrap(); #[cfg(target_endian = "little")] assert_eq!(0xef7e, byte); #[cfg(target_endian = "big")] assert_eq!(0x7eef, byte); } #[test] fn pread_slice() { use super::{Pread}; use super::ctx::StrCtx; let bytes: [u8; 2] = [0x7e, 0xef]; let b = &bytes[..]; let iserr: Result<&str, _> = b.pread_with(0, StrCtx::Length(3)); assert!(iserr.is_err()); // let bytes2: &[u8] = b.pread_with(0, 2).unwrap(); // assert_eq!(bytes2.len(), bytes[..].len()); // for i in 0..bytes2.len() { // assert_eq!(bytes2[i], bytes[i]) // } } #[test] fn pread_str() { use super::Pread; use super::ctx::*; let bytes: [u8; 2] = [0x2e, 0x0]; let b = &bytes[..]; let s: &str = b.pread(0).unwrap(); println!("str: {}", s); assert_eq!(s.len(), bytes[..].len() - 1); let bytes: &[u8] = b"hello, world!\0some_other_things"; let hello_world: &str = bytes.pread_with(0, StrCtx::Delimiter(NULL)).unwrap(); println!("{:?}", &hello_world); assert_eq!(hello_world.len(), 13); let hello: &str = bytes.pread_with(0, StrCtx::Delimiter(SPACE)).unwrap(); println!("{:?}", &hello); assert_eq!(hello.len(), 6); // this could result in underflow so we just try it let _error = bytes.pread_with::<&str>(6, StrCtx::Delimiter(SPACE)); let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE)); println!("{:?}", &error); assert!(error.is_ok()); } #[test] fn pread_str_weird() { use super::Pread; use super::ctx::*; let bytes: &[u8] = b""; let hello_world = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL)); println!("1 {:?}", &hello_world); assert_eq!(hello_world.is_err(), true); let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE)); println!("2 {:?}", &error); assert!(error.is_err()); let bytes: &[u8] = b"\0"; let null = bytes.pread::<&str>(0).unwrap(); println!("3 {:?}", &null); assert_eq!(null.len(), 0); } #[test] fn pwrite_str_and_bytes() { use super::{Pread, Pwrite}; use super::ctx::*; let astring: &str = "lol hello_world lal\0ala imabytes"; let mut buffer = [0u8; 33]; buffer.pwrite(astring, 0).unwrap(); { let hello_world = buffer.pread_with::<&str>(4, StrCtx::Delimiter(SPACE)).unwrap(); assert_eq!(hello_world, "hello_world"); } let bytes: &[u8] = b"more\0bytes"; buffer.pwrite(bytes, 0).unwrap(); let more = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL)).unwrap(); assert_eq!(more, "more"); let bytes = bytes.pread_with::<&str>(more.len() + 1, StrCtx::Delimiter(NULL)).unwrap(); assert_eq!(bytes, "bytes"); } use std::error; use std::fmt::{self, Display}; #[derive(Debug)] pub struct ExternalError {} impl Display for ExternalError { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "ExternalError") } } impl error::Error for ExternalError { fn description(&self) -> &str { "ExternalError" } fn cause(&self) -> Option<&dyn error::Error> { None} } impl From for ExternalError { fn from(err: super::Error) -> Self { //use super::Error::*; match err { _ => ExternalError{}, } } } #[derive(Debug, PartialEq, Eq)] pub struct Foo(u16); impl super::ctx::TryIntoCtx for Foo { type Error = ExternalError; fn try_into_ctx(self, this: &mut [u8], le: super::Endian) -> Result { use super::Pwrite; if this.len() < 2 { return Err((ExternalError {}).into()) } this.pwrite_with(self.0, 0, le)?; Ok(2) } } impl<'a> super::ctx::TryFromCtx<'a, super::Endian> for Foo { type Error = ExternalError; fn try_from_ctx(this: &'a [u8], le: super::Endian) -> Result<(Self, usize), Self::Error> { use super::Pread; if this.len() > 2 { return Err((ExternalError {}).into()) } let n = this.pread_with(0, le)?; Ok((Foo(n), 2)) } } #[test] fn pread_with_iter_bytes() { use super::{Pread}; let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; let bytes_to = &mut bytes_to[..]; let bytes_from = &bytes_from[..]; for i in 0..bytes_from.len() { bytes_to[i] = bytes_from.pread(i).unwrap(); } assert_eq!(bytes_to, bytes_from); } ////////////////////////////////////////////////////////////// // end pread_with ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // begin gread_with ////////////////////////////////////////////////////////////// macro_rules! g_test { ($read:ident, $deadbeef:expr, $typ:ty) => { #[test] fn $read() { use super::Pread; let bytes: [u8; 8] = [0xf, 0xe, 0xe, 0xb, 0xd, 0xa, 0xe, 0xd]; let mut offset = 0; let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap(); assert_eq!(deadbeef, $deadbeef as $typ); assert_eq!(offset, ::std::mem::size_of::<$typ>()); } } } g_test!(simple_gread_u16, 0xe0f, u16); g_test!(simple_gread_u32, 0xb0e0e0f, u32); g_test!(simple_gread_u64, 0xd0e0a0d0b0e0e0f, u64); g_test!(simple_gread_i64, 940700423303335439, i64); macro_rules! simple_float_test { ($read:ident, $deadbeef:expr, $typ:ty) => { #[test] fn $read() { use super::Pread; let bytes: [u8; 8] = [0u8, 0, 0, 0, 0, 0, 224, 63]; let mut offset = 0; let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap(); assert_eq!(deadbeef, $deadbeef as $typ); assert_eq!(offset, ::std::mem::size_of::<$typ>()); } }; } simple_float_test!(gread_f32, 0.0, f32); simple_float_test!(gread_f64, 0.5, f64); macro_rules! g_read_write_test { ($read:ident, $val:expr, $typ:ty) => { #[test] fn $read() { use super::{LE, BE, Pread, Pwrite}; let mut buffer = [0u8; 16]; let offset = &mut 0; buffer.gwrite_with($val.clone(), offset, LE).unwrap(); let o2 = &mut 0; let val: $typ = buffer.gread_with(o2, LE).unwrap(); assert_eq!(val, $val); assert_eq!(*offset, ::std::mem::size_of::<$typ>()); assert_eq!(*o2, ::std::mem::size_of::<$typ>()); assert_eq!(*o2, *offset); buffer.gwrite_with($val.clone(), offset, BE).unwrap(); let val: $typ = buffer.gread_with(o2, BE).unwrap(); assert_eq!(val, $val); } }; } g_read_write_test!(gread_gwrite_f64_1, 0.25f64, f64); g_read_write_test!(gread_gwrite_f64_2, 0.5f64, f64); g_read_write_test!(gread_gwrite_f64_3, 0.064, f64); g_read_write_test!(gread_gwrite_f32_1, 0.25f32, f32); g_read_write_test!(gread_gwrite_f32_2, 0.5f32, f32); g_read_write_test!(gread_gwrite_f32_3, 0.0f32, f32); g_read_write_test!(gread_gwrite_i64_1, 0i64, i64); g_read_write_test!(gread_gwrite_i64_2, -1213213211111i64, i64); g_read_write_test!(gread_gwrite_i64_3, -3000i64, i64); g_read_write_test!(gread_gwrite_i32_1, 0i32, i32); g_read_write_test!(gread_gwrite_i32_2, -1213213232, i32); g_read_write_test!(gread_gwrite_i32_3, -3000i32, i32); // useful for ferreting out problems with impls #[test] fn gread_with_iter_bytes() { use super::{Pread}; let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; let bytes_to = &mut bytes_to[..]; let bytes_from = &bytes_from[..]; let mut offset = &mut 0; for i in 0..bytes_from.len() { bytes_to[i] = bytes_from.gread(&mut offset).unwrap(); } assert_eq!(bytes_to, bytes_from); assert_eq!(*offset, bytes_to.len()); } #[test] fn gread_inout() { use super::{Pread}; let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; let bytes = &bytes_from[..]; let offset = &mut 0; bytes.gread_inout(offset, &mut bytes_to[..]).unwrap(); assert_eq!(bytes_to, bytes_from); assert_eq!(*offset, bytes_to.len()); } #[test] fn gread_with_byte() { use super::{Pread}; let bytes: [u8; 1] = [0x7f]; let b = &bytes[..]; let offset = &mut 0; let byte: u8 = b.gread(offset).unwrap(); assert_eq!(0x7f, byte); assert_eq!(*offset, 1); } #[test] fn gread_slice() { use super::{Pread}; use super::ctx::{StrCtx}; let bytes: [u8; 2] = [0x7e, 0xef]; let b = &bytes[..]; let offset = &mut 0; let res = b.gread_with::<&str>(offset, StrCtx::Length(3)); assert!(res.is_err()); *offset = 0; let astring: [u8; 3] = [0x45, 042, 0x44]; let string = astring.gread_with::<&str>(offset, StrCtx::Length(2)); match &string { &Ok(_) => {}, &Err(ref err) => {println!("{}", &err); panic!();} } assert_eq!(string.unwrap(), "E*"); *offset = 0; let bytes2: &[u8] = b.gread_with(offset, 2).unwrap(); assert_eq!(*offset, 2); assert_eq!(bytes2.len(), bytes[..].len()); for i in 0..bytes2.len() { assert_eq!(bytes2[i], bytes[i]) } } ///////////////////////////////////////////////////////////////// // end gread_with ///////////////////////////////////////////////////////////////// } scroll-0.10.1/src/pread.rs010064400017500001750000000176271355762526200135720ustar0000000000000000use core::result; use core::ops::{Index, RangeFrom}; use crate::ctx::{TryFromCtx, MeasureWith}; use crate::error; /// A very generic, contextual pread interface in Rust. Allows completely parallelized reads, as `Self` is immutable /// /// Don't be scared! The `Pread` definition _is_ terrifying, but it is definitely tractable. Essentially, `E` is the error, `Ctx` the parsing context, `I` is the indexing type, `TryCtx` is the "offset + ctx" Context given to the `TryFromCtx` trait bounds, and `SliceCtx` is the "offset + size + ctx" context given to the `TryRefFromCtx` trait bound. /// /// # Implementing Your Own Reader /// If you want to implement your own reader for a type `Foo` from some kind of buffer (say `[u8]`), then you need to implement [TryFromCtx](trait.TryFromCtx.html) /// /// ```rust /// use scroll::{self, ctx, Pread}; /// #[derive(Debug, PartialEq, Eq)] /// pub struct Foo(u16); /// /// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { /// type Error = scroll::Error; /// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { /// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } /// let n = this.pread_with(0, le)?; /// Ok((Foo(n), 2)) /// } /// } /// /// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; /// let foo = bytes.pread_with::(0, scroll::LE).unwrap(); /// assert_eq!(Foo(0xadde), foo); /// /// let foo2 = bytes.pread_with::(0, scroll::BE).unwrap(); /// assert_eq!(Foo(0xdeadu16), foo2); /// ``` /// /// # Advanced: Using Your Own Error in `TryFromCtx` /// ```rust /// use scroll::{self, ctx, Pread}; /// use std::error; /// use std::fmt::{self, Display}; /// // make some kind of normal error which also can transform a scroll error ideally (quick_error, error_chain allow this automatically nowadays) /// #[derive(Debug)] /// pub struct ExternalError {} /// /// impl Display for ExternalError { /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { /// write!(fmt, "ExternalError") /// } /// } /// /// impl error::Error for ExternalError { /// fn description(&self) -> &str { /// "ExternalError" /// } /// fn cause(&self) -> Option<&error::Error> { None} /// } /// /// impl From for ExternalError { /// fn from(err: scroll::Error) -> Self { /// match err { /// _ => ExternalError{}, /// } /// } /// } /// #[derive(Debug, PartialEq, Eq)] /// pub struct Foo(u16); /// /// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { /// type Error = ExternalError; /// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { /// if this.len() <= 2 { return Err((ExternalError {}).into()) } /// let offset = &mut 0; /// let n = this.gread_with(offset, le)?; /// Ok((Foo(n), *offset)) /// } /// } /// /// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; /// let foo: Result = bytes.pread(0); /// ``` pub trait Pread : Index + Index> + MeasureWith where Ctx: Copy, E: From, { #[inline] /// Reads a value from `self` at `offset` with a default `Ctx`. For the primitive numeric values, this will read at the machine's endianness. /// # Example /// ```rust /// use scroll::Pread; /// let bytes = [0x7fu8; 0x01]; /// let byte = bytes.pread::(0).unwrap(); fn pread<'a, N: TryFromCtx<'a, Ctx, >>::Output, Error = E>>(&'a self, offset: usize) -> result::Result where >>::Output: 'a, Ctx: Default { self.pread_with(offset, Ctx::default()) } #[inline] /// Reads a value from `self` at `offset` with the given `ctx` /// # Example /// ```rust /// use scroll::Pread; /// let bytes: [u8; 2] = [0xde, 0xad]; /// let dead: u16 = bytes.pread_with(0, scroll::BE).unwrap(); /// assert_eq!(dead, 0xdeadu16); fn pread_with<'a, N: TryFromCtx<'a, Ctx, >>::Output, Error = E>>(&'a self, offset: usize, ctx: Ctx) -> result::Result where >>::Output: 'a { let len = self.measure_with(&ctx); if offset >= len { return Err(error::Error::BadOffset(offset).into()) } N::try_from_ctx(&self[offset..], ctx).and_then(|(n, _)| Ok(n)) } #[inline] /// Reads a value from `self` at `offset` with a default `Ctx`. For the primitive numeric values, this will read at the machine's endianness. Updates the offset /// # Example /// ```rust /// use scroll::Pread; /// let offset = &mut 0; /// let bytes = [0x7fu8; 0x01]; /// let byte = bytes.gread::(offset).unwrap(); /// assert_eq!(*offset, 1); fn gread<'a, N: TryFromCtx<'a, Ctx, >>::Output, Error = E>>(&'a self, offset: &mut usize) -> result::Result where Ctx: Default, >>::Output: 'a { let ctx = Ctx::default(); self.gread_with(offset, ctx) } /// Reads a value from `self` at `offset` with the given `ctx`, and updates the offset. /// # Example /// ```rust /// use scroll::Pread; /// let offset = &mut 0; /// let bytes: [u8; 2] = [0xde, 0xad]; /// let dead: u16 = bytes.gread_with(offset, scroll::BE).unwrap(); /// assert_eq!(dead, 0xdeadu16); /// assert_eq!(*offset, 2); #[inline] fn gread_with<'a, N: TryFromCtx<'a, Ctx, >>::Output, Error = E>> (&'a self, offset: &mut usize, ctx: Ctx) -> result::Result where >>::Output: 'a { let o = *offset; // self.pread_with(o, ctx).and_then(|(n, size)| { // *offset += size; // Ok(n) // }) let len = self.measure_with(&ctx); if o >= len { return Err(error::Error::BadOffset(o).into()) } N::try_from_ctx(&self[o..], ctx).and_then(|(n, size)| { *offset += size; Ok(n) }) } /// Trys to write `inout.len()` `N`s into `inout` from `Self` starting at `offset`, using the default context for `N`, and updates the offset. /// # Example /// ```rust /// use scroll::Pread; /// let mut bytes: Vec = vec![0, 0]; /// let offset = &mut 0; /// let bytes_from: [u8; 2] = [0x48, 0x49]; /// bytes_from.gread_inout(offset, &mut bytes).unwrap(); /// assert_eq!(&bytes, &bytes_from); /// assert_eq!(*offset, 2); #[inline] fn gread_inout<'a, N>(&'a self, offset: &mut usize, inout: &mut [N]) -> result::Result<(), E> where N: TryFromCtx<'a, Ctx, >>::Output, Error = E>, Ctx: Default, >>::Output: 'a { for i in inout.iter_mut() { *i = self.gread(offset)?; } Ok(()) } /// Trys to write `inout.len()` `N`s into `inout` from `Self` starting at `offset`, using the context `ctx` /// # Example /// ```rust /// use scroll::{ctx, LE, Pread}; /// let mut bytes: Vec = vec![0, 0]; /// let offset = &mut 0; /// let bytes_from: [u8; 2] = [0x48, 0x49]; /// bytes_from.gread_inout_with(offset, &mut bytes, LE).unwrap(); /// assert_eq!(&bytes, &bytes_from); /// assert_eq!(*offset, 2); #[inline] fn gread_inout_with<'a, N>(&'a self, offset: &mut usize, inout: &mut [N], ctx: Ctx) -> result::Result<(), E> where N: TryFromCtx<'a, Ctx, >>::Output, Error = E>, >>::Output: 'a { for i in inout.iter_mut() { *i = self.gread_with(offset, ctx)?; } Ok(()) } } impl, R: ?Sized + Index + Index> + MeasureWith> Pread for R {} scroll-0.10.1/src/pwrite.rs010064400017500001750000000063621355762526200140030ustar0000000000000000use core::result; use core::ops::{Index, IndexMut, RangeFrom}; use crate::ctx::{TryIntoCtx, MeasureWith}; use crate::error; /// Writes into `Self` at an offset of type `I` using a `Ctx` /// /// To implement writing into an arbitrary byte buffer, implement `TryIntoCtx` /// # Example /// ```rust /// use scroll::{self, ctx, LE, Endian, Pwrite}; /// #[derive(Debug, PartialEq, Eq)] /// pub struct Foo(u16); /// /// // this will use the default `DefaultCtx = scroll::Endian` and `I = usize`... /// impl ctx::TryIntoCtx for Foo { /// // you can use your own error here too, but you will then need to specify it in fn generic parameters /// type Error = scroll::Error; /// // you can write using your own context too... see `leb128.rs` /// fn try_into_ctx(self, this: &mut [u8], le: Endian) -> Result { /// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } /// this.pwrite_with(self.0, 0, le)?; /// Ok(2) /// } /// } /// // now we can write a `Foo` into some buffer (in this case, a byte buffer, because that's what we implemented it for above) /// /// let mut bytes: [u8; 4] = [0, 0, 0, 0]; /// bytes.pwrite_with(Foo(0x7f), 1, LE).unwrap(); /// pub trait Pwrite : Index + IndexMut> + MeasureWith where Ctx: Copy, E: From, { fn pwrite>>::Output, Error = E>>(&mut self, n: N, offset: usize) -> result::Result where Ctx: Default { self.pwrite_with(n, offset, Ctx::default()) } /// Write `N` at offset `I` with context `Ctx` /// # Example /// ``` /// use scroll::{Pwrite, Pread, LE}; /// let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; /// bytes.pwrite_with::(0xbeefbeef, 0, LE).unwrap(); /// assert_eq!(bytes.pread_with::(0, LE).unwrap(), 0xbeefbeef); fn pwrite_with>>::Output, Error = E>>(&mut self, n: N, offset: usize, ctx: Ctx) -> result::Result { let len = self.measure_with(&ctx); if offset >= len { return Err(error::Error::BadOffset(offset).into()) } let dst = &mut self[offset..]; n.try_into_ctx(dst, ctx) } /// Write `n` into `self` at `offset`, with a default `Ctx`. Updates the offset. #[inline] fn gwrite>>::Output, Error = E>>(&mut self, n: N, offset: &mut usize) -> result::Result where Ctx: Default { let ctx = Ctx::default(); self.gwrite_with(n, offset, ctx) } /// Write `n` into `self` at `offset`, with the `ctx`. Updates the offset. #[inline] fn gwrite_with>>::Output, Error = E>>(&mut self, n: N, offset: &mut usize, ctx: Ctx) -> result::Result { let o = *offset; match self.pwrite_with(n, o, ctx) { Ok(size) => { *offset += size; Ok(size) }, err => err } } } impl, R: ?Sized + Index + IndexMut> + MeasureWith> Pwrite for R {} scroll-0.10.1/tests/api.rs010064400017500001750000000161261355762526200136140ustar0000000000000000// this exists primarily to test various API usages of scroll; e.g., must compile // guard against potential undefined behaviour when borrowing from // packed structs. See https://github.com/rust-lang/rust/issues/46043 #![deny(safe_packed_borrows)] // #[macro_use] extern crate scroll_derive; use std::ops::{Deref, DerefMut}; use scroll::{ctx, Result, Cread, Pread}; use scroll::ctx::SizeWith; #[derive(Default)] pub struct Section<'a> { pub sectname: [u8; 16], pub segname: [u8; 16], pub addr: u64, pub size: u64, pub offset: u32, pub align: u32, pub reloff: u32, pub nreloc: u32, pub flags: u32, pub data: &'a [u8], } impl<'a> Section<'a> { pub fn name(&self) -> Result<&str> { self.sectname.pread::<&str>(0) } pub fn segname(&self) -> Result<&str> { self.segname.pread::<&str>(0) } } impl<'a> ctx::SizeWith for Section<'a> { fn size_with(_ctx: &()) -> usize { 4 } } #[repr(C)] // renable when scroll_derive Pread/Pwrite matches //#[derive(Debug, Clone, Copy, Pread, Pwrite)] #[derive(Debug, Clone, Copy)] pub struct Section32 { pub sectname: [u8; 16], pub segname: [u8; 16], pub addr: u32, pub size: u32, pub offset: u32, pub align: u32, pub reloff: u32, pub nreloc: u32, pub flags: u32, pub reserved1: u32, pub reserved2: u32, } impl<'a> ctx::TryFromCtx<'a, ()> for Section<'a> { type Error = scroll::Error; fn try_from_ctx(_bytes: &'a [u8], _ctx: ()) -> ::std::result::Result<(Self, usize), Self::Error> { let section = Section::default(); Ok((section, ::std::mem::size_of::
())) } } pub struct Segment<'a> { pub cmd: u32, pub cmdsize: u32, pub segname: [u8; 16], pub vmaddr: u64, pub vmsize: u64, pub fileoff: u64, pub filesize: u64, pub maxprot: u32, pub initprot: u32, pub nsects: u32, pub flags: u32, pub data: &'a [u8], offset: usize, raw_data: &'a [u8], } impl<'a> Segment<'a> { pub fn name(&self) -> Result<&str> { Ok(self.segname.pread::<&str>(0)?) } pub fn sections(&self) -> Result>> { let nsects = self.nsects as usize; let mut sections = Vec::with_capacity(nsects); let offset = &mut (self.offset + Self::size_with(&())); let _size = Section::size_with(&()); let raw_data: &'a [u8] = self.raw_data; for _ in 0..nsects { let section = raw_data.gread_with::>(offset, ())?; sections.push(section); //offset += size; } Ok(sections) } } impl<'a> ctx::SizeWith for Segment<'a> { fn size_with(_ctx: &()) -> usize { 4 } } pub struct Segments<'a> { pub segments: Vec>, } impl<'a> Deref for Segments<'a> { type Target = Vec>; fn deref(&self) -> &Self::Target { &self.segments } } impl<'a> DerefMut for Segments<'a> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.segments } } impl<'a> Segments<'a> { pub fn new() -> Self { Segments { segments: Vec::new(), } } pub fn sections(&self) -> Result>>> { let mut sections = Vec::new(); for segment in &self.segments { sections.push(segment.sections()?); } Ok(sections) } } fn lifetime_passthrough_<'a>(segments: &Segments<'a>, section_name: &str) -> Option<&'a [u8]> { let segment_name = "__TEXT"; for segment in &segments.segments { if let Ok(name) = segment.name() { println!("segment.name: {}", name); if name == segment_name { if let Ok(sections) = segment.sections() { for section in sections { let sname = section.name().unwrap(); println!("section.name: {}", sname); if section_name == sname { return Some(section.data); } } } } } } None } #[test] fn lifetime_passthrough() { let segments = Segments::new(); let _res = lifetime_passthrough_(&segments, "__text"); assert!(true) } #[derive(Default)] #[repr(packed)] struct Foo { foo: i64, bar: u32, } impl scroll::ctx::FromCtx for Foo { fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { Foo { foo: bytes.cread_with::(0, ctx), bar: bytes.cread_with::(8, ctx) } } } impl scroll::ctx::SizeWith for Foo { fn size_with(_: &scroll::Endian) -> usize { ::std::mem::size_of::() } } #[test] fn ioread_api() { use std::io::Cursor; use scroll::{LE, IOread}; let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; let mut bytes = Cursor::new(bytes_); let foo = bytes.ioread_with::(LE).unwrap(); let bar = bytes.ioread_with::(LE).unwrap(); assert_eq!(foo, 1); assert_eq!(bar, 0xbeef); let error = bytes.ioread_with::(LE); assert!(error.is_err()); let mut bytes = Cursor::new(bytes_); let foo_ = bytes.ioread_with::(LE).unwrap(); assert_eq!({foo_.foo}, foo); assert_eq!({foo_.bar}, bar); } #[repr(packed)] struct Bar { foo: i32, bar: u32, } impl scroll::ctx::FromCtx for Bar { fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { Bar { foo: bytes.cread_with(0, ctx), bar: bytes.cread_with(4, ctx) } } } #[test] fn cread_api() { use scroll::{LE, Cread}; let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; let foo = bytes.cread_with::(0, LE); let bar = bytes.cread_with::(8, LE); assert_eq!(foo, 1); assert_eq!(bar, 0xbeef); } #[test] fn cread_api_customtype() { use scroll::{LE, Cread}; let bytes = [0xff, 0xff, 0xff, 0xff, 0xef,0xbe,0xad,0xde,]; let bar = &bytes[..].cread_with::(0, LE); assert_eq!({bar.foo}, -1); assert_eq!({bar.bar}, 0xdeadbeef); } #[test] #[should_panic] fn cread_api_badindex() { use scroll::Cread; let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0xad,0xde,]; let _foo = bytes.cread::(1_000_000); } #[test] fn cwrite_api() { use scroll::Cwrite; use scroll::Cread; let mut bytes = [0x0; 16]; bytes.cwrite::(42, 0); bytes.cwrite::(0xdeadbeef, 8); assert_eq!(bytes.cread::(0), 42); assert_eq!(bytes.cread::(8), 0xdeadbeef); } impl scroll::ctx::IntoCtx for Bar { fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) { use scroll::Cwrite; bytes.cwrite_with(self.foo, 0, ctx); bytes.cwrite_with(self.bar, 4, ctx); } } #[test] fn cwrite_api_customtype() { use scroll::{Cwrite, Cread}; let bar = Bar { foo: -1, bar: 0xdeadbeef }; let mut bytes = [0x0; 16]; &bytes[..].cwrite::(bar, 0); let bar = bytes.cread::(0); assert_eq!({bar.foo}, -1); assert_eq!({bar.bar}, 0xdeadbeef); } scroll-0.10.1/tests/readme.rs010064400017500001750000000020151355762526200142700ustar0000000000000000use std::env; use std::env::consts::EXE_EXTENSION; use std::path::Path; use std::process::Command; #[test] fn readme_test() { let rustdoc = Path::new("rustdoc").with_extension(EXE_EXTENSION); let readme = Path::new(file!()).parent().unwrap().parent().unwrap().join("README.md"); let exe = env::current_exe().unwrap(); let depdir = exe.parent().unwrap(); let outdir = depdir.parent().unwrap(); let extern_arg = format!("scroll={}", outdir.join("libscroll.rlib").to_string_lossy()); let mut cmd = Command::new(rustdoc); cmd.args(&["--verbose", "--test", "-L"]) .arg(&outdir) .arg("-L") .arg(&depdir) .arg("--extern") .arg(&extern_arg) .arg(&readme); println!("Running `{:?}`", cmd); let result = cmd.spawn() .expect("Failed to spawn process") .wait() .expect("Failed to run process"); // fixme: i dont know why this is failing, so disabling // assert!(result.success(), "Failed to run rustdoc tests on README.md!"); } scroll-0.10.1/.cargo_vcs_info.json0000644000000001120000000000000124420ustar00{ "git": { "sha1": "f7472de54fcb33f0531158ff92fb4f7469fdb171" } } scroll-0.10.1/Cargo.lock0000644000000234660000000000000104360ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "arrayvec" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "crossbeam-deque" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-queue" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hermit-abi" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" version = "0.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memoffset" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "nodrop" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num_cpus" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro2" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon-core" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "scopeguard" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "scroll" version = "0.10.1" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "scroll_derive" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" "checksum memoffset 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a85c1a8c329f11437034d7313dca647c79096523533a1c79e86f1d0f657c7cc" "checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" "checksum num_cpus 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "155394f924cdddf08149da25bfb932d226b4a593ca7468b08191ff6335941af5" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" "checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"