diff options
Diffstat (limited to '')
26 files changed, 4408 insertions, 0 deletions
diff --git a/third_party/rust/scroll/.cargo-checksum.json b/third_party/rust/scroll/.cargo-checksum.json new file mode 100644 index 0000000000..406f97faa2 --- /dev/null +++ b/third_party/rust/scroll/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"de2bbf4669561405d402322f4cc2604218d4986b73b75b41708b9505aebcb02c","Cargo.lock":"d6a215b7466d37e08551c56949e77be4ee488f989bdef3e507713c729bbda0e6","Cargo.toml":"c240c5768d23ea9611ef57308f08b8ee4372ede6c04f0783dc9fd1710e664c19","LICENSE":"6e24b7455f0b9afefdf4f3efd59a56ce76a3020c2dc4371937e281fc5e587fd7","README.md":"e4fe9aabcd87d85a5ec93241eeefc0d69aa0d98fbd67da2fe1849e4cbddac3ce","benches/bench.rs":"12ae02c383c91f1b0e11e9201eb8a9d44dadfb2b5987e7e71b0ef7c6589af1ca","examples/data_ctx.rs":"79684fc44d499d0b13a173184793837fbaba70d2f74f075e796eb37a1803ce3d","src/ctx.rs":"8f58672c5f3bc09b8f09c76f1d423431cbff786af75f5b39a0cef23b820d48c6","src/endian.rs":"5b717eb5ed0dc2b536779316b020df4e6489c05b13b4fd9b5f5e683aca1b2c28","src/error.rs":"a6a0ec9a6237d23febd608637c0e3926d147511e7983195366bc5a11f12d9093","src/greater.rs":"29d9736f9d35a0f92ca054c7a36878ade0a77b4e8ee27441c34cd81c6bdb68e6","src/leb128.rs":"e343f4e104ca6d8660a3dded30934b83bad4c04d8888ce2cbebfa562f5ac115d","src/lesser.rs":"d3028781977e60d67003512e45666935deab9a03c76a3ba9316a5dbdddf432eb","src/lib.rs":"49d02fa761bb2a771d1857ffd150aa4b6f55b4f03aee1a7a23d8181c76a55fd6","src/pread.rs":"64afdcf2c2785f1f23d065ec5e565d78569086dfd9ece0a3d2553b05aee5df9b","src/pwrite.rs":"05e3129ec666790a61f5b5f894ad863103e213eb798243cfe5f2cbb54d042ba1","tests/api.rs":"1bef345e020a6a4e590350ea4f6069c5836941656379e252bfbdaee6edbbc0de"},"package":"04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"}
\ No newline at end of file diff --git a/third_party/rust/scroll/CHANGELOG.md b/third_party/rust/scroll/CHANGELOG.md new file mode 100644 index 0000000000..bae87ee590 --- /dev/null +++ b/third_party/rust/scroll/CHANGELOG.md @@ -0,0 +1,17 @@ +# 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 diff --git a/third_party/rust/scroll/Cargo.lock b/third_party/rust/scroll/Cargo.lock new file mode 100644 index 0000000000..baf29fe049 --- /dev/null +++ b/third_party/rust/scroll/Cargo.lock @@ -0,0 +1,205 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "byteorder" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const_fn" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" + +[[package]] +name = "crossbeam-channel" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" +dependencies = [ + "cfg-if", + "const_fn", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" + +[[package]] +name = "memoffset" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scroll" +version = "0.11.0" +dependencies = [ + "byteorder", + "rayon", + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" diff --git a/third_party/rust/scroll/Cargo.toml b/third_party/rust/scroll/Cargo.toml new file mode 100644 index 0000000000..548be72db9 --- /dev/null +++ b/third_party/rust/scroll/Cargo.toml @@ -0,0 +1,36 @@ +# 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 are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "scroll" +version = "0.11.0" +authors = ["m4b <m4b.github.io@gmail.com>", "Ted Mielczarek <ted@mielczarek.org>"] +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" +resolver = "2" +[dependencies.scroll_derive] +version = "0.11" +optional = true +[dev-dependencies.byteorder] +version = "1" + +[dev-dependencies.rayon] +version = "1" + +[features] +default = ["std"] +derive = ["scroll_derive"] +std = [] diff --git a/third_party/rust/scroll/LICENSE b/third_party/rust/scroll/LICENSE new file mode 100644 index 0000000000..28e9a1f370 --- /dev/null +++ b/third_party/rust/scroll/LICENSE @@ -0,0 +1,21 @@ +The 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. diff --git a/third_party/rust/scroll/README.md b/third_party/rust/scroll/README.md new file mode 100644 index 0000000000..717fe6a234 --- /dev/null +++ b/third_party/rust/scroll/README.md @@ -0,0 +1,193 @@ + [![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 main() -> 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::<u32>(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::<u16>(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<i64> = 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(()) +} +``` + +### Deriving `Pread` and `Pwrite` + +Scroll implements a custom derive that can provide `Pread` and `Pwrite` implementations for your structs. + +```rust +use scroll::{Pread, Pwrite, BE}; + +#[derive(Pread, Pwrite)] +struct Data { + one: u32, + two: u16, + three: u8, +} + +fn main() -> 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(()) +} +``` + +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 main() -> 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::<usize>()?; + // ..ditto + let bar = bytes.ioread::<u32>()?; + Ok(()) +} +``` + +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 main() -> 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(()) +} +``` + +# 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::<YourDatatype>` 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 main() -> Result<(), scroll::Error> { + let bytes = b"UserName\x00\x01\x02\x03\x04"; + let data = bytes.pread_with::<Data>(0, BE)?; + assert_eq!(data.id, 0x01020304); + assert_eq!(data.name.to_string(), "UserName".to_string()); + Ok(()) +} +``` + +Please see the official documentation, or a simple [example](examples/data_ctx.rs) for more. + +# Contributing + +Any ideas, thoughts, or contributions are welcome! diff --git a/third_party/rust/scroll/benches/bench.rs b/third_party/rust/scroll/benches/bench.rs new file mode 100644 index 0000000000..0787dbe14b --- /dev/null +++ b/third_party/rust/scroll/benches/bench.rs @@ -0,0 +1,157 @@ +#![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<u16, _> = 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<u16, _> = 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<u16, _> = 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<u16, _> = 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<u16, _> = black_box(val.read_u16::<byteorder::LittleEndian>()); + } + }); + 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; +} diff --git a/third_party/rust/scroll/examples/data_ctx.rs b/third_party/rust/scroll/examples/data_ctx.rs new file mode 100644 index 0000000000..667f4b18f0 --- /dev/null +++ b/third_party/rust/scroll/examples/data_ctx.rs @@ -0,0 +1,24 @@ +use 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::<Data>(0, BE).unwrap(); + assert_eq!(data.id, 0x01020304); + assert_eq!(data.name.to_string(), "UserName".to_string()); + println!("Data: {:?}", &data); +} diff --git a/third_party/rust/scroll/src/ctx.rs b/third_party/rust/scroll/src/ctx.rs new file mode 100644 index 0000000000..1f982b82fa --- /dev/null +++ b/third_party/rust/scroll/src/ctx.rs @@ -0,0 +1,897 @@ +//! Generic context-aware conversion traits, for automatic _downstream_ extension of `Pread`, et. al +//! +//! The context traits are arguably the center piece of the scroll crate. In simple terms they +//! define how to actually read and write, respectively, a data type from a container, being able to +//! take context into account. +//! +//! ### Reading +//! +//! Types implementing [TryFromCtx](trait.TryFromCtx.html) and it's infallible cousin [FromCtx](trait.FromCtx.html) +//! allow a user of [Pread::pread](../trait.Pread.html#method.pread) or respectively +//! [Cread::cread](../trait.Cread.html#method.cread) and +//! [IOread::ioread](../trait.IOread.html#method.ioread) to read that data type from a data source one +//! of the `*read` traits has been implemented for. +//! +//! Implementations of `TryFromCtx` specify a source (called `This`) and an `Error` type for failed +//! reads. The source defines the kind of container the type can be read from, and defaults to +//! `[u8]` for any type that implements `AsRef<[u8]>`. +//! +//! `FromCtx` is slightly more restricted; it requires the implementer to use `[u8]` as source and +//! never fail, and thus does not have an `Error` type. +//! +//! Types chosen here are of relevance to `Pread` implementations; of course only a container which +//! can produce a source of the type `This` can be used to read a `TryFromCtx` requiring it and the +//! `Error` type returned in `Err` of `Pread::pread`'s Result. +//! +//! ### Writing +//! +//! [TryIntoCtx](trait.TryIntoCtx.html) and the infallible [IntoCtx](trait.IntoCtx.html) work +//! similarly to the above traits, allowing [Pwrite::pwrite](../trait.Pwrite.html#method.pwrite) or +//! respectively [Cwrite::cwrite](../trait.Cwrite.html#method.cwrite) and +//! [IOwrite::iowrite](../trait.IOwrite.html#method.iowrite) to write data into a byte sink for +//! which one of the `*write` traits has been implemented for. +//! +//! `IntoCtx` is similarly restricted as `FromCtx` is to `TryFromCtx`. And equally the types chosen +//! affect usable `Pwrite` implementation. +//! +//! ### Context +//! +//! Each of the traits passes along a `Ctx` to the marshalling logic. This context type contains +//! any additional information that may be required to successfully parse or write the data: +//! Examples would be endianness to use, field lengths of a serialized struct, or delimiters to use +//! when reading/writing `&str`. The context type can be any type but must derive +//! [Copy](https://doc.rust-lang.org/std/marker/trait.Copy.html). In addition if you want to use +//! the `*read`-methods instead of the `*read_with` ones you must also implement +//! [default::Default](https://doc.rust-lang.org/std/default/trait.Default.html). +//! +//! # Example +//! +//! Let's expand on the [previous example](../index.html#complex-use-cases). +//! +//! ```rust +//! use scroll::{self, ctx, Pread, Endian}; +//! use scroll::ctx::StrCtx; +//! +//! #[derive(Copy, Clone, PartialEq, Eq)] +//! enum FieldSize { +//! U32, +//! U64 +//! } +//! +//! // Our custom context type. As said above it has to derive Copy. +//! #[derive(Copy, Clone)] +//! struct Context { +//! fieldsize: FieldSize, +//! endianess: Endian, +//! } +//! +//! // Our custom data type +//! struct Data<'b> { +//! // These u64 are encoded either as 32-bit or 64-bit wide ints. Which one it is is defined in +//! // the Context. +//! // Also, let's imagine they have a strict relationship: A < B < C otherwise the struct is +//! // invalid. +//! field_a: u64, +//! field_b: u64, +//! field_c: u64, +//! +//! // Both of these are marshalled with a prefixed length. +//! name: &'b str, +//! value: &'b [u8], +//! } +//! +//! #[derive(Debug)] +//! enum Error { +//! // We'll return this custom error if the field* relationship doesn't hold +//! BadFieldMatchup, +//! Scroll(scroll::Error), +//! } +//! +//! impl<'a> ctx::TryFromCtx<'a, Context> for Data<'a> { +//! type Error = Error; +//! +//! // Using the explicit lifetime specification again you ensure that read data doesn't outlife +//! // its source buffer without having to resort to copying. +//! fn try_from_ctx (src: &'a [u8], ctx: Context) +//! // the `usize` returned here is the amount of bytes read. +//! -> Result<(Self, usize), Self::Error> +//! { +//! // The offset counter; gread and gread_with increment a given counter automatically so we +//! // don't have to manually care. +//! let offset = &mut 0; +//! +//! let field_a; +//! let field_b; +//! let field_c; +//! +//! // Switch the amount of bytes read depending on the parsing context +//! if ctx.fieldsize == FieldSize::U32 { +//! field_a = src.gread_with::<u32>(offset, ctx.endianess)? as u64; +//! field_b = src.gread_with::<u32>(offset, ctx.endianess)? as u64; +//! field_c = src.gread_with::<u32>(offset, ctx.endianess)? as u64; +//! } else { +//! field_a = src.gread_with::<u64>(offset, ctx.endianess)?; +//! field_b = src.gread_with::<u64>(offset, ctx.endianess)?; +//! field_c = src.gread_with::<u64>(offset, ctx.endianess)?; +//! } +//! +//! // You can use type ascribition or turbofish operators, whichever you prefer. +//! let namelen = src.gread_with::<u16>(offset, ctx.endianess)? as usize; +//! let name: &str = src.gread_with(offset, scroll::ctx::StrCtx::Length(namelen))?; +//! +//! let vallen = src.gread_with::<u16>(offset, ctx.endianess)? as usize; +//! let value = &src[*offset..(*offset+vallen)]; +//! +//! // Let's sanity check those fields, shall we? +//! if ! (field_a < field_b && field_b < field_c) { +//! return Err(Error::BadFieldMatchup); +//! } +//! +//! Ok((Data { field_a, field_b, field_c, name, value }, *offset)) +//! } +//! } +//! +//! // In lieu of a complex byte buffer we hearken back to the venerable &[u8]; do note however +//! // that the implementation of TryFromCtx did not specify such. In fact any type that implements +//! // Pread can now read `Data` as it implements TryFromCtx. +//! let bytes = b"\x00\x02\x03\x04\x01\x02\x03\x04\xde\xad\xbe\xef\x00\x08UserName\x00\x02\xCA\xFE"; +//! +//! // We define an appropiate context, and get going +//! let contextA = Context { +//! fieldsize: FieldSize::U32, +//! endianess: Endian::Big, +//! }; +//! let data: Data = bytes.pread_with(0, contextA).unwrap(); +//! +//! assert_eq!(data.field_a, 0x00020304); +//! assert_eq!(data.field_b, 0x01020304); +//! assert_eq!(data.field_c, 0xdeadbeef); +//! assert_eq!(data.name, "UserName"); +//! assert_eq!(data.value, [0xCA, 0xFE]); +//! +//! // Here we have a context with a different FieldSize, changing parsing information at runtime. +//! let contextB = Context { +//! fieldsize: FieldSize::U64, +//! endianess: Endian::Big, +//! }; +//! +//! // Which will of course error with a malformed input for the context +//! let err: Result<Data, Error> = bytes.pread_with(0, contextB); +//! assert!(err.is_err()); +//! +//! let bytes_long = [0x00,0x00,0x00,0x00,0x00,0x02,0x03,0x04,0x00,0x00,0x00,0x00,0x01,0x02,0x03, +//! 0x04,0x00,0x00,0x00,0x00,0xde,0xad,0xbe,0xef,0x00,0x08,0x55,0x73,0x65,0x72, +//! 0x4e,0x61,0x6d,0x65,0x00,0x02,0xCA,0xFE]; +//! +//! let data: Data = bytes_long.pread_with(0, contextB).unwrap(); +//! +//! assert_eq!(data.field_a, 0x00020304); +//! assert_eq!(data.field_b, 0x01020304); +//! assert_eq!(data.field_c, 0xdeadbeef); +//! assert_eq!(data.name, "UserName"); +//! assert_eq!(data.value, [0xCA, 0xFE]); +//! +//! // Ergonomic conversion, not relevant really. +//! use std::convert::From; +//! impl From<scroll::Error> for Error { +//! fn from(error: scroll::Error) -> Error { +//! Error::Scroll(error) +//! } +//! } +//! ``` + +use core::mem::size_of; +use core::mem::transmute; +use core::ptr::copy_nonoverlapping; +use core::result; +use core::str; + +#[cfg(feature = "std")] +use std::ffi::{CStr, CString}; + +use crate::endian::Endian; +use crate::error; + +/// A trait for measuring how large something is; for a byte sequence, it will be its length. +pub trait MeasureWith<Ctx> { + /// How large is `Self`, given the `ctx`? + fn measure_with(&self, ctx: &Ctx) -> usize; +} + +impl<Ctx> MeasureWith<Ctx> for [u8] { + #[inline] + fn measure_with(&self, _ctx: &Ctx) -> usize { + self.len() + } +} + +impl<Ctx, T: AsRef<[u8]>> MeasureWith<Ctx> 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<Ctx: Copy = (), This: ?Sized = [u8]> { + fn from_ctx(this: &This, ctx: Ctx) -> Self; +} + +/// Tries to read `Self` from `This` using the context `Ctx` +/// +/// # 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 this trait +/// +/// ```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::<Foo>(0, scroll::LE).unwrap(); +/// assert_eq!(Foo(0xadde), foo); +/// +/// let foo2 = bytes.pread_with::<Foo>(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 transformed from a scroll error +/// #[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<scroll::Error> 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<Foo, ExternalError> = bytes.pread(0); +/// ``` +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<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { + fn into_ctx(self, _: &mut This, ctx: Ctx); +} + +/// Tries to write `Self` into `This` using the context `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` +/// impl ctx::TryIntoCtx<Endian> 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 type, see `leb128.rs` +/// fn try_into_ctx(self, this: &mut [u8], le: Endian) -> Result<usize, Self::Error> { +/// 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 TryIntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { + type Error; + fn try_into_ctx(self, _: &mut This, ctx: Ctx) -> Result<usize, Self::Error>; +} + +/// 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<Ctx = ()> { + fn size_with(ctx: &Ctx) -> usize; +} + +#[rustfmt::skip] +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<Endian> 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<Endian> for &'a $typ { + #[inline] + fn into_ctx(self, dst: &mut [u8], le: Endian) { + (*self).into_ctx(dst, le) + } + } + impl TryIntoCtx<Endian> for $typ + where + $typ: IntoCtx<Endian>, + { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { + if $size > dst.len() { + Err(error::Error::TooBig { + size: $size, + len: dst.len(), + }) + } else { + <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le); + Ok($size) + } + } + } + impl<'a> TryIntoCtx<Endian> for &'a $typ { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { + (*self).try_into_ctx(dst, le) + } + } + }; +} + +macro_rules! from_ctx_impl { + ($typ:tt, $size:expr) => { + impl<'a> FromCtx<Endian> 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<Endian>, + { + 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<Endian, T> 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<Endian, T>, + 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<Endian> for $typ { + #[inline] + fn from_ctx(src: &[u8], le: Endian) -> Self { + assert!(src.len() >= ::core::mem::size_of::<Self>()); + 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<Endian>, + { + 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<Endian> 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<Endian> for &'a $typ { + #[inline] + fn into_ctx(self, dst: &mut [u8], le: Endian) { + (*self).into_ctx(dst, le) + } + } + impl TryIntoCtx<Endian> for $typ + where + $typ: IntoCtx<Endian>, + { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { + if $size > dst.len() { + Err(error::Error::TooBig { + size: $size, + len: dst.len(), + }) + } else { + <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le); + Ok($size) + } + } + } + impl<'a> TryIntoCtx<Endian> for &'a $typ { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { + (*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<usize> { + 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<usize> { + 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<Endian> 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); + +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)) + } + } +} + +#[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<usize> { + 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<usize> { + 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); + } +} diff --git a/third_party/rust/scroll/src/endian.rs b/third_party/rust/scroll/src/endian.rs new file mode 100644 index 0000000000..06d7a1dc1c --- /dev/null +++ b/third_party/rust/scroll/src/endian.rs @@ -0,0 +1,51 @@ +#[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<bool> 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, + } + } +} diff --git a/third_party/rust/scroll/src/error.rs b/third_party/rust/scroll/src/error.rs new file mode 100644 index 0000000000..7740254774 --- /dev/null +++ b/third_party/rust/scroll/src/error.rs @@ -0,0 +1,84 @@ +use core::fmt::{self, Display}; +use core::result; + +#[cfg(feature = "std")] +use std::error; +#[cfg(feature = "std")] +use std::io; + +#[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<io::Error> 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<T> = result::Result<T, Error>; diff --git a/third_party/rust/scroll/src/greater.rs b/third_party/rust/scroll/src/greater.rs new file mode 100644 index 0000000000..353aab7c4c --- /dev/null +++ b/third_party/rust/scroll/src/greater.rs @@ -0,0 +1,169 @@ +use 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](ctx/trait.FromCtx.html) then you can `cread::<YourType>(offset)`. +/// +/// # Example +/// +/// ```rust +/// use scroll::{ctx, Cread, LE}; +/// +/// #[repr(packed)] +/// struct Bar { +/// foo: i32, +/// bar: u32, +/// } +/// +/// impl ctx::FromCtx<scroll::Endian> 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::<Bar>(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<Ctx, I = usize>: Index<I> + Index<RangeFrom<I>> +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::<i64>(0, BE); + /// let bar = bytes.cread_with::<u32>(8, LE); + /// assert_eq!(foo, MAX); + /// assert_eq!(bar, 0xdeadbeef); + /// ``` + #[inline] + fn cread_with<N: FromCtx<Ctx, <Self as Index<RangeFrom<I>>>::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::<i64>(0); + /// let bar = bytes.cread::<u32>(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<N: FromCtx<Ctx, <Self as Index<RangeFrom<I>>>::Output>>(&self, offset: I) -> N + where + Ctx: Default, + { + let ctx = Ctx::default(); + N::from_ctx(&self[offset..], ctx) + } +} + +impl<Ctx: Copy, I, R: ?Sized + Index<I> + Index<RangeFrom<I>>> Cread<Ctx, I> 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](ctx/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<scroll::Endian> 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>(bar, 0); +/// ``` +pub trait Cwrite<Ctx: Copy, I = usize>: Index<I> + IndexMut<RangeFrom<I>> { + /// 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::<i64>(42, 0); + /// bytes.cwrite::<u32>(0xdeadbeef, 8); + /// + /// assert_eq!(bytes.cread::<i64>(0), 42); + /// assert_eq!(bytes.cread::<u32>(8), 0xdeadbeef); + #[inline] + fn cwrite<N: IntoCtx<Ctx, <Self as Index<RangeFrom<I>>>::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::<i64>(42, 0, LE); + /// bytes.cwrite_with::<u32>(0xdeadbeef, 8, BE); + /// assert_eq!(bytes.cread_with::<i64>(0, LE), 42); + /// assert_eq!(bytes.cread_with::<u32>(8, LE), 0xefbeadde); + #[inline] + fn cwrite_with<N: IntoCtx<Ctx, <Self as Index<RangeFrom<I>>>::Output>>( + &mut self, + n: N, + offset: I, + ctx: Ctx, + ) { + n.into_ctx(self.index_mut(offset..), ctx) + } +} + +impl<Ctx: Copy, I, W: ?Sized + Index<I> + IndexMut<RangeFrom<I>>> Cwrite<Ctx, I> for W {} diff --git a/third_party/rust/scroll/src/leb128.rs b/third_party/rust/scroll/src/leb128.rs new file mode 100644 index 0000000000..43f50b95f1 --- /dev/null +++ b/third_party/rust/scroll/src/leb128.rs @@ -0,0 +1,249 @@ +use crate::ctx::TryFromCtx; +use crate::error; +use crate::Pread; +use core::convert::{AsRef, From}; +use core::result; +use core::u8; + +#[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<u64> { + let tmp = bytes.pread::<Uleb128>(*offset)?; + *offset += tmp.size(); + Ok(tmp.into()) + } +} + +impl AsRef<u64> for Uleb128 { + fn as_ref(&self) -> &u64 { + &self.value + } +} + +impl From<Uleb128> 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<i64> { + let tmp = bytes.pread::<Sleb128>(*offset)?; + *offset += tmp.size(); + Ok(tmp.into()) + } +} + +impl AsRef<i64> for Sleb128 { + fn as_ref(&self) -> &i64 { + &self.value + } +} + +impl From<Sleb128> 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::super::LE; + use super::{Sleb128, Uleb128}; + + 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::<Uleb128>(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::<Uleb128>(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::<Uleb128>(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::<Uleb128>(0).expect("Should read Uleb128"); + assert_eq!(130u64, num.into()); + assert_eq!( + 386, + bytes.pread_with::<u16>(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::<Uleb128>(0).is_err()); + } + + #[test] + fn sleb128() { + use super::super::Pread; + let bytes = [0x7fu8 | CONTINUATION_BIT, 0x7e]; + let num: i64 = bytes + .pread::<Sleb128>(0) + .expect("Should read Sleb128") + .into(); + assert_eq!(-129, num); + } +} diff --git a/third_party/rust/scroll/src/lesser.rs b/third_party/rust/scroll/src/lesser.rs new file mode 100644 index 0000000000..46ef4c5b11 --- /dev/null +++ b/third_party/rust/scroll/src/lesser.rs @@ -0,0 +1,178 @@ +use crate::ctx::{FromCtx, IntoCtx, SizeWith}; +use std::io::{Read, Result, Write}; + +/// An extension trait to `std::io::Read` streams; mainly targeted at reading primitive types with +/// a known size. +/// +/// Requires types to implement [`FromCtx`](ctx/trait.FromCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html). +/// +/// **NB** You should probably add `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`. +/// +/// Warning: Currently ioread/write uses a small 256-byte buffer and can not read/write larger types +/// +/// # Example +/// ```rust +/// use std::io::Cursor; +/// use scroll::{self, ctx, LE, Pread, IOread}; +/// +/// #[repr(packed)] +/// struct Foo { +/// foo: i64, +/// bar: u32, +/// } +/// +/// impl ctx::FromCtx<scroll::Endian> for Foo { +/// fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { +/// Foo { foo: bytes.pread_with::<i64>(0, ctx).unwrap(), bar: bytes.pread_with::<u32>(8, ctx).unwrap() } +/// } +/// } +/// +/// impl ctx::SizeWith<scroll::Endian> for Foo { +/// // our parsing context doesn't influence our size +/// fn size_with(_: &scroll::Endian) -> usize { +/// ::std::mem::size_of::<Foo>() +/// } +/// } +/// +/// 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::<i64>(LE).unwrap(); +/// let bar = bytes.ioread_with::<u32>(LE).unwrap(); +/// assert_eq!(foo, 0xb0b); +/// assert_eq!(bar, 0xbeef); +/// let error = bytes.ioread_with::<f64>(LE); +/// assert!(error.is_err()); +/// let mut bytes = Cursor::new(bytes_); +/// let foo_ = bytes.ioread_with::<Foo>(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<Ctx: Copy>: 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::<u16>().unwrap(); + /// + /// #[cfg(target_endian = "little")] + /// assert_eq!(0xbeef, beef); + /// #[cfg(target_endian = "big")] + /// assert_eq!(0xefbe, beef); + /// ``` + #[inline] + fn ioread<N: FromCtx<Ctx> + SizeWith<Ctx>>(&mut self) -> Result<N> + 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::<u16>(LE).unwrap(); + /// assert_eq!(0xbeef, beef); + /// let b0 = bytes.ioread::<u8>().unwrap(); + /// assert_eq!(0xb0, b0); + /// let b0 = bytes.ioread::<u8>().unwrap(); + /// assert_eq!(0xb0, b0); + /// let feeddead = bytes.ioread_with::<u32>(BE).unwrap(); + /// assert_eq!(0xfeeddead, feeddead); + /// ``` + #[inline] + fn ioread_with<N: FromCtx<Ctx> + SizeWith<Ctx>>(&mut self, ctx: Ctx) -> Result<N> { + 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<Ctx: Copy, R: Read + ?Sized> IOread<Ctx> 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::<YourType>` call, implement [`IntoCtx`](ctx/trait.IntoCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html) for `YourType`. +pub trait IOwrite<Ctx: Copy>: 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<N: SizeWith<Ctx> + IntoCtx<Ctx>>(&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<N: SizeWith<Ctx> + IntoCtx<Ctx>>(&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<Ctx: Copy, W: Write + ?Sized> IOwrite<Ctx> for W {} diff --git a/third_party/rust/scroll/src/lib.rs b/third_party/rust/scroll/src/lib.rs new file mode 100644 index 0000000000..dcb58e7564 --- /dev/null +++ b/third_party/rust/scroll/src/lib.rs @@ -0,0 +1,637 @@ +//! # Scroll +//! +//! ```text, no_run +//! _______________ +//! ()==( (@==() +//! '______________'| +//! | | +//! | ἀρετή | +//! __)_____________| +//! ()==( (@==() +//! '--------------' +//! +//! ``` +//! +//! Scroll is a library for easily and efficiently reading/writing types from data containers like +//! byte arrays. +//! +//! ## Easily: +//! +//! Scroll sets down a number of traits: +//! +//! [FromCtx](ctx/trait.FromCtx.html), [IntoCtx](ctx/trait.IntoCtx.html), +//! [TryFromCtx](ctx/trait.TryFromCtx.html) and [TryIntoCtx](ctx/trait.TryIntoCtx.html) — further +//! explained in the [ctx module](ctx/index.html); to be implemented on custom types to allow +//! reading, writing, and potentially fallible reading/writing respectively. +//! +//! [Pread](trait.Pread.html) and [Pwrite](trait.Pwrite.html) which are implemented on data +//! containers such as byte arrays to define how to read or respectively write types implementing +//! the *Ctx traits above. +//! In addition scroll also defines [IOread](trait.IOread.html) and +//! [IOwrite](trait.IOwrite.html) with additional constraits that then allow reading and writing +//! from `std::io` [Read](https://doc.rust-lang.org/nightly/std/io/trait.Read.html) and +//! [Write](https://doc.rust-lang.org/nightly/std/io/trait.Write.html). +//! +//! +//! In most cases you can use [scroll_derive](https://docs.rs/scroll_derive) to derive sensible +//! defaults for `Pread`, `Pwrite`, their IO counterpart and `SizeWith`. More complex situations +//! call for manual implementation of those traits; refer to [the ctx module](ctx/index.html) for +//! details. +//! +//! +//! ## Efficiently: +//! +//! Reading Slices — including [&str](https://doc.rust-lang.org/std/primitive.str.html) — supports +//! zero-copy. Scroll is designed with a `no_std` context in mind; every dependency on `std` is +//! cfg-gated and errors need not allocate. +//! +//! Reads by default take only immutable references wherever possible, allowing for trivial +//! parallelization. +//! +//! # Examples +//! +//! Let's start with a simple example +//! +//! ```rust +//! use scroll::{ctx, Pread}; +//! +//! // Let's first define some data, cfg-gated so our assertions later on hold. +//! #[cfg(target_endian = "little")] +//! let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef]; +//! #[cfg(target_endian = "big")] +//! let bytes: [u8; 4] = [0xef, 0xbe, 0xad, 0xde]; +//! +//! // We can read a u32 from the array `bytes` at offset 0. +//! // This will use a default context for the type being parsed; +//! // in the case of u32 this defines to use the host's endianess. +//! let number = bytes.pread::<u32>(0).unwrap(); +//! assert_eq!(number, 0xefbeadde); +//! +//! +//! // Similarly we can also read a single byte at offset 2 +//! // This time using type ascription instead of the turbofish (::<>) operator. +//! let byte: u8 = bytes.pread(2).unwrap(); +//! #[cfg(target_endian = "little")] +//! assert_eq!(byte, 0xbe); +//! #[cfg(target_endian = "big")] +//! assert_eq!(byte, 0xad); +//! +//! +//! // If required we can also provide a specific parsing context; e.g. if we want to explicitly +//! // define the endianess to use: +//! let be_number: u32 = bytes.pread_with(0, scroll::BE).unwrap(); +//! #[cfg(target_endian = "little")] +//! assert_eq!(be_number, 0xdeadbeef); +//! #[cfg(target_endian = "big")] +//! assert_eq!(be_number, 0xefbeadde); +//! +//! let be_number16 = bytes.pread_with::<u16>(1, scroll::BE).unwrap(); +//! #[cfg(target_endian = "little")] +//! assert_eq!(be_number16, 0xadbe); +//! #[cfg(target_endian = "big")] +//! assert_eq!(be_number16, 0xbead); +//! +//! +//! // Reads may fail; in this example due to a too large read for the given container. +//! // Scroll's error type does not by default allocate to work in environments like no_std. +//! let byte_err: scroll::Result<i64> = bytes.pread(0); +//! assert!(byte_err.is_err()); +//! +//! +//! // We can parse out custom datatypes, or types with lifetimes, as long as they implement +//! // the conversion traits `TryFromCtx/FromCtx`. +//! // Here we use the default context for &str which parses are C-style '\0'-delimited string. +//! let hello: &[u8] = b"hello world\0more words"; +//! let hello_world: &str = hello.pread(0).unwrap(); +//! assert_eq!("hello world", hello_world); +//! +//! // We can again provide a custom context; for example to parse Space-delimited strings. +//! // As you can see while we still call `pread` changing the context can influence the output — +//! // instead of splitting at '\0' we split at spaces +//! let hello2: &[u8] = b"hello world\0more words"; +//! let world: &str = hello2.pread_with(6, ctx::StrCtx::Delimiter(ctx::SPACE)).unwrap(); +//! assert_eq!("world\0more", world); +//! ``` +//! +//! ## `std::io` API +//! +//! Scroll also allows reading from `std::io`. For this the types to read need to implement +//! [FromCtx](ctx/trait.FromCtx.html) and [SizeWith](ctx/trait.SizeWith.html). +//! +//! ```rust +//! use std::io::Cursor; +//! use scroll::{IOread, ctx, Endian}; +//! let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; +//! let mut cursor = Cursor::new(bytes); +//! +//! // IOread uses std::io::Read methods, thus the Cursor will be incremented on these reads: +//! let prev = cursor.position(); +//! +//! let integer = cursor.ioread_with::<u64>(Endian::Little).unwrap(); +//! +//! let after = cursor.position(); +//! +//! assert!(prev < after); +//! +//! // SizeWith allows us to define a context-sensitive size of a read type: +//! // Contexts can have different instantiations; e.g. the `Endian` context can be either Little or +//! // Big. This is useful if for example the context contains the word-size of fields to be +//! // read/written, e.g. switching between ELF32 or ELF64 at runtime. +//! let size = <u64 as ctx::SizeWith<Endian>>::size_with(&Endian::Little) as u64; +//! assert_eq!(prev + size, after); +//! ``` +//! +//! In the same vein as IOread we can use IOwrite to write a type to anything implementing +//! `std::io::Write`: +//! +//! ```rust +//! use std::io::Cursor; +//! use scroll::{IOwrite}; +//! +//! let mut bytes = [0x0u8; 5]; +//! let mut cursor = Cursor::new(&mut bytes[..]); +//! +//! // This of course once again increments the cursor position +//! cursor.iowrite_with(0xdeadbeef as u32, scroll::BE).unwrap(); +//! +//! assert_eq!(cursor.into_inner(), [0xde, 0xad, 0xbe, 0xef, 0x0]); +//! ``` +//! +//! ## Complex use cases +//! +//! Scoll is designed to be highly adaptable while providing a strong abstraction between the types +//! being read/written and the data container containing them. +//! +//! In this example we'll define a custom Data and allow it to be read from an arbitrary byte +//! buffer. +//! +//! ```rust +//! use scroll::{self, ctx, Pread, Endian}; +//! use scroll::ctx::StrCtx; +//! +//! // Our custom context type. In a more complex situation you could for example store details on +//! // how to write or read your type, field-sizes or other information. +//! // In this simple example we could also do without using a custom context in the first place. +//! #[derive(Copy, Clone)] +//! struct Context(Endian); +//! +//! // Our custom data type +//! struct Data<'zerocopy> { +//! // This is only a reference to the actual data; we make use of scroll's zero-copy capability +//! name: &'zerocopy str, +//! id: u32, +//! } +//! +//! // To allow for safe zero-copying scroll allows to specify lifetimes explicitly: +//! // The context +//! impl<'a> ctx::TryFromCtx<'a, Context> for Data<'a> { +//! // If necessary you can set a custom error type here, which will be returned by Pread/Pwrite +//! type Error = scroll::Error; +//! +//! // Using the explicit lifetime specification again you ensure that read data doesn't outlife +//! // its source buffer without having to resort to copying. +//! fn try_from_ctx (src: &'a [u8], ctx: Context) +//! // the `usize` returned here is the amount of bytes read. +//! -> Result<(Self, usize), Self::Error> +//! { +//! let offset = &mut 0; +//! +//! let id = src.gread_with(offset, ctx.0)?; +//! +//! // In a more serious application you would validate data here of course. +//! let namelen: u16 = src.gread_with(offset, ctx.0)?; +//! let name = src.gread_with::<&str>(offset, StrCtx::Length(namelen as usize))?; +//! +//! Ok((Data { name: name, id: id }, *offset)) +//! } +//! } +//! +//! // In lieu of a complex byte buffer we hearken back to a simple &[u8]; the default source +//! // of TryFromCtx. However, any type that implements Pread to produce a &[u8] can now read +//! // `Data` thanks to it's implementation of TryFromCtx. +//! let bytes = b"\x01\x02\x03\x04\x00\x08UserName"; +//! let data: Data = bytes.pread_with(0, Context(Endian::Big)).unwrap(); +//! +//! assert_eq!(data.id, 0x01020304); +//! assert_eq!(data.name.to_string(), "UserName".to_string()); +//! ``` +//! +//! For further explanation of the traits and how to implement them manually refer to +//! [Pread](trait.Pread.html) and [TryFromCtx](ctx/trait.TryFromCtx.html). + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "derive")] +#[allow(unused_imports)] +pub use scroll_derive::{IOread, IOwrite, Pread, Pwrite, SizeWith}; + +#[cfg(feature = "std")] +extern crate core; + +pub mod ctx; +mod endian; +mod error; +mod greater; +mod leb128; +#[cfg(feature = "std")] +mod lesser; +mod pread; +mod pwrite; + +pub use crate::endian::*; +pub use crate::error::*; +pub use crate::greater::*; +pub use crate::leb128::*; +#[cfg(feature = "std")] +pub use crate::lesser::*; +pub use crate::pread::*; +pub use crate::pwrite::*; + +#[doc(hidden)] +pub mod export { + pub use ::core::mem; + pub use ::core::result; +} + +#[allow(unused)] +macro_rules! doc_comment { + ($x:expr) => { + #[doc = $x] + #[doc(hidden)] + mod readme_tests {} + }; +} + +#[cfg(feature = "derive")] +doc_comment!(include_str!("../README.md")); + +#[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::{Pread, Pwrite, 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::ctx::StrCtx; + use super::Pread; + 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::ctx::*; + use super::Pread; + 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::ctx::*; + use super::Pread; + 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::ctx::*; + use super::{Pread, Pwrite}; + 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<super::Error> 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<super::Endian> for Foo { + type Error = ExternalError; + fn try_into_ctx(self, this: &mut [u8], le: super::Endian) -> Result<usize, Self::Error> { + 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::{Pread, Pwrite, BE, LE}; + 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::ctx::StrCtx; + use super::Pread; + 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 + ///////////////////////////////////////////////////////////////// +} diff --git a/third_party/rust/scroll/src/pread.rs b/third_party/rust/scroll/src/pread.rs new file mode 100644 index 0000000000..72ba877054 --- /dev/null +++ b/third_party/rust/scroll/src/pread.rs @@ -0,0 +1,178 @@ +use core::result; + +use crate::ctx::TryFromCtx; +use crate::error; + +/// A very generic, contextual pread interface in Rust. +/// +/// Like [Pwrite](trait.Pwrite.html) — but for reading! +/// +/// Implementing `Pread` on a data store allows you to then read almost arbitrarily complex types +/// efficiently. +/// +/// To this end the Pread trait works in conjuction with the [TryFromCtx](ctx/trait.TryFromCtx.html); +/// The `TryFromCtx` trait implemented on a type defines how to convert data to an object of that +/// type, the Pread trait implemented on a data store defines how to extract said data from that +/// store. +/// +/// It should be noted though that in this context, data does not necessarily mean `&[u8]` — +/// `Pread` and `TryFromCtx` are generic over what 'data' means and could be implemented instead +/// over chunks of memory or any other indexable type — but scroll does come with a set of powerful +/// blanket implementations for data being a continous block of byte-addressable memory. +/// +/// Pread provides two main groups of functions: pread and gread. +/// +/// `pread` is the basic function that simply extracts a given type from a given data store - either +/// using a provided Context in the case of [pread_with](trait.Pread.html#method.pread_with) or +/// with the default context for the given type in the case of [pread](trait.Pread.html#method.pread) +/// +/// `gread` does in addition to that update the offset it's currently at, allowing for a cursored +/// read — `gread_inout` expands on that and reads a number of continous types from the data store. +/// gread again comes with `_with` variants to allow using a specific context. +/// +/// Since pread and friends are very generic functions their types are rather complex, but very +/// much understandable; `TryFromCtx` is generic over `Ctx` ([described +/// here](ctx/index.html#context)), `Output` and `Error`. The Error type is hopefully +/// self-explanatory, however the `Output` type is rather important; it defines what Pread extracts +/// from the data store and has to match up with what `TryFromCtx` expects as input to convert into +/// the resulting type. scroll defaults to `&[u8]` here. +/// +/// Unless you need to implement your own data store — that is either can't convert to `&[u8]` or +/// have a data that does not expose a `&[u8]` — you will probably want to implement +/// [TryFromCtx](ctx/trait.TryFromCtx.html) on your Rust types to be extracted. +/// +pub trait Pread<Ctx: Copy, E> { + #[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::<u8>(0).unwrap(); + fn pread<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>( + &'a self, + offset: usize, + ) -> result::Result<N, E> + where + 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, Self, Error = E>>( + &'a self, + offset: usize, + ctx: Ctx, + ) -> result::Result<N, E> { + let mut ignored = offset; + self.gread_with(&mut ignored, ctx) + } + + #[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::<u8>(offset).unwrap(); + /// assert_eq!(*offset, 1); + fn gread<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>( + &'a self, + offset: &mut usize, + ) -> result::Result<N, E> + where + Ctx: Default, + { + 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); + fn gread_with<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>( + &'a self, + offset: &mut usize, + ctx: Ctx, + ) -> result::Result<N, E>; + + /// Tries 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<u8> = 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: TryFromCtx<'a, Ctx, Self, Error = E>>( + &'a self, + offset: &mut usize, + inout: &mut [N], + ) -> result::Result<(), E> + where + Ctx: Default, + { + for i in inout.iter_mut() { + *i = self.gread(offset)?; + } + Ok(()) + } + + /// Tries 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<u8> = 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: TryFromCtx<'a, Ctx, Self, Error = E>>( + &'a self, + offset: &mut usize, + inout: &mut [N], + ctx: Ctx, + ) -> result::Result<(), E> { + for i in inout.iter_mut() { + *i = self.gread_with(offset, ctx)?; + } + Ok(()) + } +} + +impl<Ctx: Copy, E: From<error::Error>> Pread<Ctx, E> for [u8] { + fn gread_with<'a, N: TryFromCtx<'a, Ctx, Self, Error = E>>( + &'a self, + offset: &mut usize, + ctx: Ctx, + ) -> result::Result<N, E> { + let start = *offset; + if start >= self.len() { + return Err(error::Error::BadOffset(start).into()); + } + N::try_from_ctx(&self[start..], ctx).map(|(n, size)| { + *offset += size; + n + }) + } +} diff --git a/third_party/rust/scroll/src/pwrite.rs b/third_party/rust/scroll/src/pwrite.rs new file mode 100644 index 0000000000..ab6d96157d --- /dev/null +++ b/third_party/rust/scroll/src/pwrite.rs @@ -0,0 +1,96 @@ +use core::result; + +use crate::ctx::TryIntoCtx; +use crate::error; + +/// A very generic, contextual pwrite interface in Rust. +/// +/// Like [Pread](trait.Pread.html) — but for writing! +/// +/// Implementing `Pwrite` on a data store allows you to then write almost arbitrarily complex types +/// efficiently. +/// +/// To this end the Pwrite trait works in conjuction with the [TryIntoCtx](ctx/trait.TryIntoCtx.html); +/// The `TryIntoCtx` trait implemented on a type defines how to convert said type into data that +/// an implementation of Pwrite can … well … write. +/// +/// As with [Pread](trait.Pread.html) 'data' does not necessarily mean `&[u8]` but can be any +/// indexable type. In fact much of the documentation of `Pread` applies to `Pwrite` as well just +/// with 'read' switched for 'write' and 'From' switched with 'Into' so if you haven't yet you +/// should read the documentation of `Pread` first. +/// +/// Unless you need to implement your own data store — that is either can't convert to `&[u8]` or +/// have a data that does not expose a `&mut [u8]` — you will probably want to implement +/// [TryIntoCtx](ctx/trait.TryIntoCtx.html) on your Rust types to be written. +/// +pub trait Pwrite<Ctx: Copy, E> { + #[inline] + fn pwrite<N: TryIntoCtx<Ctx, Self, Error = E>>( + &mut self, + n: N, + offset: usize, + ) -> result::Result<usize, E> + 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::<u32>(0xbeefbeef, 0, LE).unwrap(); + /// assert_eq!(bytes.pread_with::<u32>(0, LE).unwrap(), 0xbeefbeef); + fn pwrite_with<N: TryIntoCtx<Ctx, Self, Error = E>>( + &mut self, + n: N, + offset: usize, + ctx: Ctx, + ) -> result::Result<usize, E>; + + /// Write `n` into `self` at `offset`, with a default `Ctx`. Updates the offset. + #[inline] + fn gwrite<N: TryIntoCtx<Ctx, Self, Error = E>>( + &mut self, + n: N, + offset: &mut usize, + ) -> result::Result<usize, E> + 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<N: TryIntoCtx<Ctx, Self, Error = E>>( + &mut self, + n: N, + offset: &mut usize, + ctx: Ctx, + ) -> result::Result<usize, E> { + let o = *offset; + self.pwrite_with(n, o, ctx).map(|size| { + *offset += size; + size + }) + } +} + +impl<Ctx: Copy, E: From<error::Error>> Pwrite<Ctx, E> for [u8] { + fn pwrite_with<N: TryIntoCtx<Ctx, Self, Error = E>>( + &mut self, + n: N, + offset: usize, + ctx: Ctx, + ) -> result::Result<usize, E> { + if offset >= self.len() { + return Err(error::Error::BadOffset(offset).into()); + } + let dst = &mut self[offset..]; + n.try_into_ctx(dst, ctx) + } +} diff --git a/third_party/rust/scroll/tests/api.rs b/third_party/rust/scroll/tests/api.rs new file mode 100644 index 0000000000..e10726f22a --- /dev/null +++ b/third_party/rust/scroll/tests/api.rs @@ -0,0 +1,292 @@ +// 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(unaligned_references)] + +// #[macro_use] extern crate scroll_derive; + +use scroll::ctx::SizeWith; +use scroll::{ctx, Cread, Pread, Result}; +use std::ops::{Deref, DerefMut}; + +#[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::<Section>())) + } +} + +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<Vec<Section<'a>>> { + 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::<Section<'a>>(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<Segment<'a>>, +} + +impl<'a> Deref for Segments<'a> { + type Target = Vec<Segment<'a>>; + 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<Vec<Vec<Section<'a>>>> { + 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<scroll::Endian> for Foo { + fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { + Foo { + foo: bytes.cread_with::<i64>(0, ctx), + bar: bytes.cread_with::<u32>(8, ctx), + } + } +} + +impl scroll::ctx::SizeWith<scroll::Endian> for Foo { + fn size_with(_: &scroll::Endian) -> usize { + ::std::mem::size_of::<Foo>() + } +} + +#[test] +fn ioread_api() { + use scroll::{IOread, LE}; + use std::io::Cursor; + 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::<i64>(LE).unwrap(); + let bar = bytes.ioread_with::<u32>(LE).unwrap(); + assert_eq!(foo, 1); + assert_eq!(bar, 0xbeef); + let error = bytes.ioread_with::<f64>(LE); + assert!(error.is_err()); + let mut bytes = Cursor::new(bytes_); + let foo_ = bytes.ioread_with::<Foo>(LE).unwrap(); + assert_eq!({ foo_.foo }, foo); + assert_eq!({ foo_.bar }, bar); +} + +#[repr(packed)] +struct Bar { + foo: i32, + bar: u32, +} + +impl scroll::ctx::FromCtx<scroll::Endian> 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::{Cread, LE}; + let bytes = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0x00, 0x00, + ]; + let foo = bytes.cread_with::<u64>(0, LE); + let bar = bytes.cread_with::<u32>(8, LE); + assert_eq!(foo, 1); + assert_eq!(bar, 0xbeef); +} + +#[test] +fn cread_api_customtype() { + use scroll::{Cread, LE}; + let bytes = [0xff, 0xff, 0xff, 0xff, 0xef, 0xbe, 0xad, 0xde]; + let bar = &bytes[..].cread_with::<Bar>(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::<i64>(1_000_000); +} + +#[test] +fn cwrite_api() { + use scroll::Cread; + use scroll::Cwrite; + let mut bytes = [0x0; 16]; + bytes.cwrite::<u64>(42, 0); + bytes.cwrite::<u32>(0xdeadbeef, 8); + assert_eq!(bytes.cread::<u64>(0), 42); + assert_eq!(bytes.cread::<u32>(8), 0xdeadbeef); +} + +impl scroll::ctx::IntoCtx<scroll::Endian> 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::{Cread, Cwrite}; + let bar = Bar { + foo: -1, + bar: 0xdeadbeef, + }; + let mut bytes = [0x0; 16]; + let _ = &bytes[..].cwrite::<Bar>(bar, 0); + let bar = bytes.cread::<Bar>(0); + assert_eq!({ bar.foo }, -1); + assert_eq!({ bar.bar }, 0xdeadbeef); +} diff --git a/third_party/rust/scroll_derive/.cargo-checksum.json b/third_party/rust/scroll_derive/.cargo-checksum.json new file mode 100644 index 0000000000..a86b234ae3 --- /dev/null +++ b/third_party/rust/scroll_derive/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"1c75a1216efdf3c7b4726138eed43fadf9325750c8d01d2358b4cf4ad742f8c1","Cargo.toml":"36cd9b38e6f4ed4bd807208da809d3803af3b134264fd5c90a29a6f064b3b4d9","LICENSE":"afb11426e09da40a1ae4f8fa17ddcc6b6a52d14df04c29bc5bcd06eb8730624d","README.md":"f89c7768454b0d2b9db816afe05db3a4cea1125bef87f08ed3eefd65e9e2b180","examples/main.rs":"dc2f7f6ba45dcba4e6fe7c8ac100df0c101cb091ddd34f7dfc6599e58cc9e9a7","src/lib.rs":"a9cabe3c0b373f352357745b817f188ab841e9445056014dee9cc83c4d167483","tests/tests.rs":"ab4e6955d2e3bedd003b53b8f3423a6fc48424e37218ca989bf7e0debdf3c3f9"},"package":"bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e"}
\ No newline at end of file diff --git a/third_party/rust/scroll_derive/Cargo.lock b/third_party/rust/scroll_derive/Cargo.lock new file mode 100644 index 0000000000..49119a6c7b --- /dev/null +++ b/third_party/rust/scroll_derive/Cargo.lock @@ -0,0 +1,52 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "scroll" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" + +[[package]] +name = "scroll_derive" +version = "0.11.0" +dependencies = [ + "proc-macro2", + "quote", + "scroll", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" diff --git a/third_party/rust/scroll_derive/Cargo.toml b/third_party/rust/scroll_derive/Cargo.toml new file mode 100644 index 0000000000..98041f2cd0 --- /dev/null +++ b/third_party/rust/scroll_derive/Cargo.toml @@ -0,0 +1,35 @@ +# 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 are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "scroll_derive" +version = "0.11.0" +authors = ["m4b <m4b.github.io@gmail.com>", "Ted Mielczarek <ted@mielczarek.org>", "Systemcluster <me@systemcluster.me>"] +description = "A macros 1.1 derive implementation for Pread and Pwrite traits from the scroll crate" +documentation = "https://docs.rs/scroll_derive" +readme = "README.md" +keywords = ["derive", "macros", "pread", "pwrite", "bytes"] +license = "MIT" +repository = "https://github.com/m4b/scroll" + +[lib] +proc-macro = true +[dependencies.proc-macro2] +version = "1" + +[dependencies.quote] +version = "1" + +[dependencies.syn] +version = "1" +[dev-dependencies.scroll] +version = "0.10" diff --git a/third_party/rust/scroll_derive/LICENSE b/third_party/rust/scroll_derive/LICENSE new file mode 100644 index 0000000000..8864d4a396 --- /dev/null +++ b/third_party/rust/scroll_derive/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 + +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. diff --git a/third_party/rust/scroll_derive/README.md b/third_party/rust/scroll_derive/README.md new file mode 100644 index 0000000000..a7f7e85f0e --- /dev/null +++ b/third_party/rust/scroll_derive/README.md @@ -0,0 +1,35 @@ +# scroll_derive +Macros 1.1 implementing #[derive(Pread, Pwrite)] for https://github.com/m4b/scroll + +Add derive annotations to your POD seamlessly and easily: + +```rust +extern crate scroll; +#[macro_use] +extern crate scroll_derive; + +#[derive(Debug, PartialEq, Pread, Pwrite, IOread, IOwrite, SizeWith)] +#[repr(C)] +struct Data { + id: u32, + timestamp: f64, + arr: [u16; 2], +} + +use scroll::{Pread, Pwrite, Cread, LE}; + +fn main (){ + let bytes = [0xefu8, 0xbe, 0xad, 0xde, 0, 0, 0, 0, 0, 0, 224, 63, 0xad, 0xde, 0xef, 0xbe]; + let data: Data = bytes.pread_with(0, LE).unwrap(); + println!("data: {:?}", &data); + assert_eq!(data.id, 0xdeadbeefu32); + let mut bytes2 = vec![0; ::std::mem::size_of::<Data>()]; + bytes2.pwrite_with(data, 0, LE).unwrap(); + let data: Data = bytes.pread_with(0, LE).unwrap(); + let data2: Data = bytes2.pread_with(0, LE).unwrap(); + assert_eq!(data, data2); + + let data: Data = bytes.cread_with(0, LE); + assert_eq!(data, data2); +} +``` diff --git a/third_party/rust/scroll_derive/examples/main.rs b/third_party/rust/scroll_derive/examples/main.rs new file mode 100644 index 0000000000..faec85300b --- /dev/null +++ b/third_party/rust/scroll_derive/examples/main.rs @@ -0,0 +1,28 @@ +use scroll_derive::{IOread, IOwrite, Pread, Pwrite, SizeWith}; + +#[derive(Debug, PartialEq, Pread, Pwrite, IOread, IOwrite, SizeWith)] +#[repr(C)] +struct Data { + id: u32, + timestamp: f64, + arr: [u16; 2], +} + +use scroll::{Cread, Pread, Pwrite, LE}; + +fn main() { + let bytes = [ + 0xefu8, 0xbe, 0xad, 0xde, 0, 0, 0, 0, 0, 0, 224, 63, 0xad, 0xde, 0xef, 0xbe, + ]; + let data: Data = bytes.pread_with(0, LE).unwrap(); + println!("data: {:?}", &data); + assert_eq!(data.id, 0xdeadbeefu32); + let mut bytes2 = vec![0; ::std::mem::size_of::<Data>()]; + bytes2.pwrite_with(data, 0, LE).unwrap(); + let data: Data = bytes.pread_with(0, LE).unwrap(); + let data2: Data = bytes2.pread_with(0, LE).unwrap(); + assert_eq!(data, data2); + + let data: Data = bytes.cread_with(0, LE); + assert_eq!(data, data2); +} diff --git a/third_party/rust/scroll_derive/src/lib.rs b/third_party/rust/scroll_derive/src/lib.rs new file mode 100644 index 0000000000..a2ba6692df --- /dev/null +++ b/third_party/rust/scroll_derive/src/lib.rs @@ -0,0 +1,538 @@ +#![recursion_limit = "1024"] + +extern crate proc_macro; +use proc_macro2; +use quote::quote; + +use proc_macro::TokenStream; + +fn impl_field(ident: &proc_macro2::TokenStream, ty: &syn::Type) -> proc_macro2::TokenStream { + match *ty { + syn::Type::Array(ref array) => match array.len { + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Int(ref int), + .. + }) => { + let size = int.base10_parse::<usize>().unwrap(); + quote! { + #ident: { let mut __tmp: #ty = [0u8.into(); #size]; src.gread_inout_with(offset, &mut __tmp, ctx)?; __tmp } + } + } + _ => panic!("Pread derive with bad array constexpr"), + }, + syn::Type::Group(ref group) => impl_field(ident, &group.elem), + _ => { + quote! { + #ident: src.gread_with::<#ty>(offset, ctx)? + } + } + } +} + +fn impl_struct( + name: &syn::Ident, + fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, + generics: &syn::Generics, +) -> proc_macro2::TokenStream { + let items: Vec<_> = fields + .iter() + .enumerate() + .map(|(i, f)| { + let ident = &f.ident.as_ref().map(|i| quote! {#i}).unwrap_or({ + let t = proc_macro2::Literal::usize_unsuffixed(i); + quote! {#t} + }); + let ty = &f.ty; + impl_field(ident, ty) + }) + .collect(); + + let gl = &generics.lt_token; + let gp = &generics.params; + let gg = &generics.gt_token; + let gn = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { #ident } + } + p => quote! { #p }, + }); + let gn = quote! { #gl #( #gn ),* #gg }; + let gw = if !gp.is_empty() { + let gi = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { + #ident : ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> + ::std::convert::From<u8> + ::std::marker::Copy, + ::scroll::Error : ::std::convert::From<< #ident as ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian>>::Error>, + < #ident as ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian>>::Error : ::std::convert::From<scroll::Error> + } + }, + p => quote! { #p } + }); + quote! { #( #gi ),* , } + } else { + quote! {} + }; + + quote! { + impl<'a, #gp > ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> for #name #gn where #gw #name #gn : 'a { + type Error = ::scroll::Error; + #[inline] + fn try_from_ctx(src: &'a [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<(Self, usize), Self::Error> { + use ::scroll::Pread; + let offset = &mut 0; + let data = Self { #(#items,)* }; + Ok((data, *offset)) + } + } + } +} + +fn impl_try_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { + let name = &ast.ident; + let generics = &ast.generics; + match ast.data { + syn::Data::Struct(ref data) => match data.fields { + syn::Fields::Named(ref fields) => impl_struct(name, &fields.named, generics), + syn::Fields::Unnamed(ref fields) => impl_struct(name, &fields.unnamed, generics), + _ => { + panic!("Pread can not be derived for unit structs") + } + }, + _ => panic!("Pread can only be derived for structs"), + } +} + +#[proc_macro_derive(Pread)] +pub fn derive_pread(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(input).unwrap(); + let gen = impl_try_from_ctx(&ast); + gen.into() +} + +fn impl_pwrite_field(ident: &proc_macro2::TokenStream, ty: &syn::Type) -> proc_macro2::TokenStream { + match ty { + syn::Type::Array(ref array) => match array.len { + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Int(ref int), + .. + }) => { + let size = int.base10_parse::<usize>().unwrap(); + quote! { + for i in 0..#size { + dst.gwrite_with(&self.#ident[i], offset, ctx)?; + } + } + } + _ => panic!("Pwrite derive with bad array constexpr"), + }, + syn::Type::Group(group) => impl_pwrite_field(ident, &group.elem), + _ => { + quote! { + dst.gwrite_with(&self.#ident, offset, ctx)? + } + } + } +} + +fn impl_try_into_ctx( + name: &syn::Ident, + fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, + generics: &syn::Generics, +) -> proc_macro2::TokenStream { + let items: Vec<_> = fields + .iter() + .enumerate() + .map(|(i, f)| { + let ident = &f.ident.as_ref().map(|i| quote! {#i}).unwrap_or({ + let t = proc_macro2::Literal::usize_unsuffixed(i); + quote! {#t} + }); + let ty = &f.ty; + impl_pwrite_field(ident, ty) + }) + .collect(); + + let gl = &generics.lt_token; + let gp = &generics.params; + let gg = &generics.gt_token; + let gn = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { #ident } + } + p => quote! { #p }, + }); + let gn = quote! { #gl #( #gn ),* #gg }; + let gwref = if !gp.is_empty() { + let gi = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { + &'a #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>, + ::scroll::Error: ::std::convert::From<<&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>, + <&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error> + } + }, + p => quote! { #p } + }); + quote! { where #( #gi ),* } + } else { + quote! {} + }; + let gw = if !gp.is_empty() { + let gi = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { + #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>, + ::scroll::Error: ::std::convert::From<<#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>, + <#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error> + } + }, + p => quote! { #p } + }); + quote! { where Self: ::std::marker::Copy, #( #gi ),* } + } else { + quote! {} + }; + + quote! { + impl<'a, #gp > ::scroll::ctx::TryIntoCtx<::scroll::Endian> for &'a #name #gn #gwref { + type Error = ::scroll::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> { + use ::scroll::Pwrite; + let offset = &mut 0; + #(#items;)*; + Ok(*offset) + } + } + + impl #gl #gp #gg ::scroll::ctx::TryIntoCtx<::scroll::Endian> for #name #gn #gw { + type Error = ::scroll::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> { + (&self).try_into_ctx(dst, ctx) + } + } + } +} + +fn impl_pwrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { + let name = &ast.ident; + let generics = &ast.generics; + match ast.data { + syn::Data::Struct(ref data) => match data.fields { + syn::Fields::Named(ref fields) => impl_try_into_ctx(name, &fields.named, generics), + syn::Fields::Unnamed(ref fields) => impl_try_into_ctx(name, &fields.unnamed, generics), + _ => { + panic!("Pwrite can not be derived for unit structs") + } + }, + _ => panic!("Pwrite can only be derived for structs"), + } +} + +#[proc_macro_derive(Pwrite)] +pub fn derive_pwrite(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(input).unwrap(); + let gen = impl_pwrite(&ast); + gen.into() +} + +fn size_with( + name: &syn::Ident, + fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, + generics: &syn::Generics, +) -> proc_macro2::TokenStream { + let items: Vec<_> = fields + .iter() + .map(|f| { + let ty = &f.ty; + match *ty { + syn::Type::Array(ref array) => { + let elem = &array.elem; + match array.len { + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Int(ref int), + .. + }) => { + let size = int.base10_parse::<usize>().unwrap(); + quote! { + (#size * <#elem>::size_with(ctx)) + } + } + _ => panic!("Pread derive with bad array constexpr"), + } + } + _ => { + quote! { + <#ty>::size_with(ctx) + } + } + } + }) + .collect(); + + let gl = &generics.lt_token; + let gp = &generics.params; + let gg = &generics.gt_token; + let gn = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { #ident } + } + p => quote! { #p }, + }); + let gn = quote! { #gl #( #gn ),* #gg }; + let gw = if !gp.is_empty() { + let gi = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { + #ident : ::scroll::ctx::SizeWith<::scroll::Endian> + } + } + p => quote! { #p }, + }); + quote! { where #( #gi ),* } + } else { + quote! {} + }; + + quote! { + impl #gl #gp #gg ::scroll::ctx::SizeWith<::scroll::Endian> for #name #gn #gw { + #[inline] + fn size_with(ctx: &::scroll::Endian) -> usize { + 0 #(+ #items)* + } + } + } +} + +fn impl_size_with(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { + let name = &ast.ident; + let generics = &ast.generics; + match ast.data { + syn::Data::Struct(ref data) => match data.fields { + syn::Fields::Named(ref fields) => size_with(name, &fields.named, generics), + syn::Fields::Unnamed(ref fields) => size_with(name, &fields.unnamed, generics), + _ => { + panic!("SizeWith can not be derived for unit structs") + } + }, + _ => panic!("SizeWith can only be derived for structs"), + } +} + +#[proc_macro_derive(SizeWith)] +pub fn derive_sizewith(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(input).unwrap(); + let gen = impl_size_with(&ast); + gen.into() +} + +fn impl_cread_struct( + name: &syn::Ident, + fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, + generics: &syn::Generics, +) -> proc_macro2::TokenStream { + let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| { + let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}}); + let ty = &f.ty; + match *ty { + syn::Type::Array(ref array) => { + let arrty = &array.elem; + match array.len { + syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(ref int), ..}) => { + let size = int.base10_parse::<usize>().unwrap(); + let incr = quote! { ::scroll::export::mem::size_of::<#arrty>() }; + quote! { + #ident: { + let mut __tmp: #ty = [0u8.into(); #size]; + for i in 0..__tmp.len() { + __tmp[i] = src.cread_with(*offset, ctx); + *offset += #incr; + } + __tmp + } + } + }, + _ => panic!("IOread derive with bad array constexpr") + } + }, + _ => { + let size = quote! { ::scroll::export::mem::size_of::<#ty>() }; + quote! { + #ident: { let res = src.cread_with::<#ty>(*offset, ctx); *offset += #size; res } + } + } + } + }).collect(); + + let gl = &generics.lt_token; + let gp = &generics.params; + let gg = &generics.gt_token; + let gn = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { #ident } + } + p => quote! { #p }, + }); + let gn = quote! { #gl #( #gn ),* #gg }; + let gw = if !gp.is_empty() { + let gi = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { + #ident : ::scroll::ctx::FromCtx<::scroll::Endian> + ::std::convert::From<u8> + ::std::marker::Copy + } + }, + p => quote! { #p } + }); + quote! { where #( #gi ),* , } + } else { + quote! {} + }; + + quote! { + impl #gl #gp #gg ::scroll::ctx::FromCtx<::scroll::Endian> for #name #gn #gw { + #[inline] + fn from_ctx(src: &[u8], ctx: ::scroll::Endian) -> Self { + use ::scroll::Cread; + let offset = &mut 0; + let data = Self { #(#items,)* }; + data + } + } + } +} + +fn impl_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { + let name = &ast.ident; + let generics = &ast.generics; + match ast.data { + syn::Data::Struct(ref data) => match data.fields { + syn::Fields::Named(ref fields) => impl_cread_struct(name, &fields.named, generics), + syn::Fields::Unnamed(ref fields) => impl_cread_struct(name, &fields.unnamed, generics), + _ => { + panic!("IOread can not be derived for unit structs") + } + }, + _ => panic!("IOread can only be derived for structs"), + } +} + +#[proc_macro_derive(IOread)] +pub fn derive_ioread(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(input).unwrap(); + let gen = impl_from_ctx(&ast); + gen.into() +} + +fn impl_into_ctx( + name: &syn::Ident, + fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>, + generics: &syn::Generics, +) -> proc_macro2::TokenStream { + let items: Vec<_> = fields + .iter() + .enumerate() + .map(|(i, f)| { + let ident = &f.ident.as_ref().map(|i| quote! {#i}).unwrap_or({ + let t = proc_macro2::Literal::usize_unsuffixed(i); + quote! {#t} + }); + let ty = &f.ty; + let size = quote! { ::scroll::export::mem::size_of::<#ty>() }; + match *ty { + syn::Type::Array(ref array) => { + let arrty = &array.elem; + quote! { + let size = ::scroll::export::mem::size_of::<#arrty>(); + for i in 0..self.#ident.len() { + dst.cwrite_with(self.#ident[i], *offset, ctx); + *offset += size; + } + } + } + _ => { + quote! { + dst.cwrite_with(self.#ident, *offset, ctx); + *offset += #size; + } + } + } + }) + .collect(); + + let gl = &generics.lt_token; + let gp = &generics.params; + let gg = &generics.gt_token; + let gn = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { #ident } + } + p => quote! { #p }, + }); + let gw = if !gp.is_empty() { + let gi = gp.iter().map(|param: &syn::GenericParam| match param { + syn::GenericParam::Type(ref t) => { + let ident = &t.ident; + quote! { + #ident : ::scroll::ctx::IntoCtx<::scroll::Endian> + ::std::marker::Copy + } + } + p => quote! { #p }, + }); + quote! { where #( #gi ),* } + } else { + quote! {} + }; + let gn = quote! { #gl #( #gn ),* #gg }; + + quote! { + impl<'a, #gp > ::scroll::ctx::IntoCtx<::scroll::Endian> for &'a #name #gn #gw { + #[inline] + fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) { + use ::scroll::Cwrite; + let offset = &mut 0; + #(#items;)*; + () + } + } + + impl #gl #gp #gg ::scroll::ctx::IntoCtx<::scroll::Endian> for #name #gn #gw { + #[inline] + fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) { + (&self).into_ctx(dst, ctx) + } + } + } +} + +fn impl_iowrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { + let name = &ast.ident; + let generics = &ast.generics; + match ast.data { + syn::Data::Struct(ref data) => match data.fields { + syn::Fields::Named(ref fields) => impl_into_ctx(name, &fields.named, generics), + syn::Fields::Unnamed(ref fields) => impl_into_ctx(name, &fields.unnamed, generics), + _ => { + panic!("IOwrite can not be derived for unit structs") + } + }, + _ => panic!("IOwrite can only be derived for structs"), + } +} + +#[proc_macro_derive(IOwrite)] +pub fn derive_iowrite(input: TokenStream) -> TokenStream { + let ast: syn::DeriveInput = syn::parse(input).unwrap(); + let gen = impl_iowrite(&ast); + gen.into() +} diff --git a/third_party/rust/scroll_derive/tests/tests.rs b/third_party/rust/scroll_derive/tests/tests.rs new file mode 100644 index 0000000000..532ac065e0 --- /dev/null +++ b/third_party/rust/scroll_derive/tests/tests.rs @@ -0,0 +1,213 @@ +use scroll::{Cread, Cwrite, Pread, Pwrite, LE}; +use scroll_derive::{IOread, IOwrite, Pread, Pwrite, SizeWith}; + +use scroll::ctx::SizeWith; + +macro_rules! test { + ( struct $name:ident { $( $field:ident: $t:ty, )* } ) => { + // check we can exist inside a macro_rules + // https://github.com/m4b/scroll/pull/75 + #[derive(Pread, Pwrite)] + pub struct $name { + $( pub $field: $t, )* + } + }; +} + +test! { + struct Test { + field: [u16; 40], + } +} + +#[derive(Debug, PartialEq, Pread, Pwrite)] +struct Data { + id: u32, + timestamp: f64, +} + +#[test] +fn test_data() { + let bytes = [0xefu8, 0xbe, 0xad, 0xde, 0, 0, 0, 0, 0, 0, 224, 63]; + let data: Data = bytes.pread_with(0, LE).unwrap(); + println!("data: {:?}", &data); + assert_eq!(data.id, 0xdeadbeefu32); + assert_eq!(data.timestamp, 0.5f64); + let mut bytes2 = vec![0; ::std::mem::size_of::<Data>()]; + bytes2.pwrite_with(data, 0, LE).unwrap(); + let data: Data = bytes.pread_with(0, LE).unwrap(); + let data2: Data = bytes2.pread_with(0, LE).unwrap(); + assert_eq!(data, data2); +} + +#[derive(Debug, PartialEq, Pread, Pwrite)] +struct Data2 { + name: [u8; 32], +} + +#[test] +fn test_array() { + let bytes = [0u8; 64]; + let data: Data2 = bytes.pread_with(0, LE).unwrap(); + println!("data: {:?}", &data); +} + +#[derive(Debug, PartialEq, Pread, Pwrite, SizeWith)] +struct Data3 { + name: u32, +} + +#[test] +fn test_sizewith() { + let bytes = [0u8; 64]; + let data: Data3 = bytes.gread_with(&mut 0, LE).unwrap(); + println!("data: {:?}", &data); +} + +#[derive(Debug, PartialEq, IOread, IOwrite, SizeWith)] +struct Data4 { + name: u32, + j: u16, + arr: [u8; 2], +} + +#[test] +fn test_ioread() { + let bytes = [0, 1, 2, 3, 0xde, 0xed, 0xbe, 0xaf]; + let data: Data4 = bytes.cread_with(0, LE); + println!("data: {:?}", &data); + assert_eq!(data.name, 50462976); + assert_eq!(data.j, 0xedde); + assert_eq!(data.arr, [0xbe, 0xaf]); +} + +#[test] +fn test_iowrite() { + let bytes = [0, 1, 2, 3, 0xde, 0xed, 0xbe, 0xaf]; + let data: Data4 = bytes.cread_with(0, LE); + println!("data: {:?}", &data); + assert_eq!(data.name, 50462976); + assert_eq!(data.j, 0xedde); + assert_eq!(data.arr, [0xbe, 0xaf]); + + let mut bytes_null = [0u8; 8]; + bytes_null.cwrite_with(&data, 0, LE); + println!("bytes_null: {:?}", &bytes_null); + println!("bytes : {:?}", &bytes); + assert_eq!(bytes_null, bytes); + + let mut bytes_null = [0u8; 8]; + bytes_null.cwrite_with(data, 0, LE); + println!("bytes_null: {:?}", &bytes_null); + println!("bytes : {:?}", &bytes); + assert_eq!(bytes_null, bytes); +} + +#[derive(Debug, PartialEq, Pread, SizeWith)] +#[repr(C)] +struct Data5 { + name: u32, + j: u16, + arr1: [u8; 2], + arr2: [u16; 2], +} + +#[test] +fn test_pread_arrays() { + let bytes = [0, 1, 2, 3, 0, 0, 0xde, 0xed, 0xad, 0xde, 0xef, 0xbe]; + let data: Data5 = bytes.pread_with(0, LE).unwrap(); + println!("data: {:?}", &data); + assert_eq!(data.name, 50462976); + assert_eq!(data.arr1, [0xde, 0xed]); + assert_eq!(data.arr2, [0xdead, 0xbeef]); + let offset = &mut 0; + let data: Data5 = bytes.gread_with(offset, LE).unwrap(); + println!("data: {:?}", &data); + assert_eq!(data.name, 50462976); + assert_eq!(data.arr1, [0xde, 0xed]); + assert_eq!(data.arr2, [0xdead, 0xbeef]); + assert_eq!(*offset, ::std::mem::size_of::<Data5>()); +} + +#[derive(Debug, PartialEq, Pread, SizeWith)] +#[repr(C)] +struct Data6 { + id: u32, + name: [u8; 5], +} + +#[test] +fn test_array_copy() { + let bytes = [0xde, 0xed, 0xef, 0xbe, 0x68, 0x65, 0x6c, 0x6c, 0x0]; + let data: Data6 = bytes.pread_with(0, LE).unwrap(); + let name: &str = data.name.pread(0).unwrap(); + println!("data: {:?}", &data); + println!("data.name: {:?}", name); + assert_eq!(data.id, 0xbeefedde); + assert_eq!(name, "hell"); +} + +#[derive(Debug, PartialEq, Eq, Pread, Pwrite, SizeWith)] +struct Data7A { + pub y: u64, + pub x: u32, +} + +#[derive(Debug, PartialEq, Eq, Pread, Pwrite, SizeWith)] +struct Data7B { + pub a: Data7A, +} + +#[test] +fn test_nested_struct() { + let b = Data7B { + a: Data7A { y: 1, x: 2 }, + }; + let size = Data7B::size_with(&LE); + assert_eq!(size, 12); + let mut bytes = vec![0; size]; + let written = bytes.pwrite_with(&b, 0, LE).unwrap(); + assert_eq!(written, size); + let mut read = 0; + let b2: Data7B = bytes.gread_with(&mut read, LE).unwrap(); + assert_eq!(read, size); + assert_eq!(b, b2); +} + +#[derive(Debug, PartialEq, Eq, Pread, Pwrite, IOread, IOwrite, SizeWith)] +#[repr(C)] +struct Data8<T, Y> { + ids: [T; 3], + xyz: Y, +} + +#[test] +fn test_generics() { + let mut bytes = [0xde, 0xed, 0xef, 0x10, 0x10]; + let data: Data8<u8, u16> = bytes.pread_with(0, LE).unwrap(); + assert_eq!(data.ids, [0xde, 0xed, 0xef]); + assert_eq!(data.xyz, 0x1010); + let data: Data8<u8, u16> = bytes.cread_with(0, LE); + assert_eq!(data.ids, [0xde, 0xed, 0xef]); + assert_eq!(data.xyz, 0x1010); + let size = Data8::<u8, u16>::size_with(&LE); + let written = bytes.pwrite_with(&data, 0, LE).unwrap(); + assert_eq!(written, size); +} + +#[derive(Debug, PartialEq, Eq, Pread, Pwrite, IOread, IOwrite, SizeWith)] +struct Data9(u8, u16); + +#[test] +fn test_newtype() { + let mut bytes = [0xde, 0x10, 0x10]; + let data: Data9 = bytes.pread_with(0, LE).unwrap(); + assert_eq!(data.0, 0xde); + assert_eq!(data.1, 0x1010); + let data: Data9 = bytes.cread_with(0, LE); + assert_eq!(data.0, 0xde); + assert_eq!(data.1, 0x1010); + let size = Data9::size_with(&LE); + let written = bytes.pwrite_with(&data, 0, LE).unwrap(); + assert_eq!(written, size); +} |