test-assembler-0.1.6/.cargo_vcs_info.json0000644000000001120000000000000137630ustar { "git": { "sha1": "26bab9b2eecc55c6893b37cd157d6beccdfe4e0e" } } test-assembler-0.1.6/.coveralls.yml000064400000000000000000000001070000000000000153300ustar 00000000000000service_name: travis-pro repo_token: D73lm00BvXkngu3XgVTz0pgGpVTuswcO5 test-assembler-0.1.6/.github/workflows/rust.yml000064400000000000000000000026200000000000000176540ustar 00000000000000on: [push, pull_request] name: Continuous integration jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: check test: name: Test Suite runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: test fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add rustfmt - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add clippy - uses: actions-rs/cargo@v1 with: command: clippy args: -- -D warnings test-assembler-0.1.6/.gitignore000064400000000000000000000000250000000000000145240ustar 00000000000000target Cargo.lock *~ test-assembler-0.1.6/Cargo.toml0000644000000016250000000000000117730ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "test-assembler" version = "0.1.6" authors = ["Ted Mielczarek "] description = "A set of types for building complex binary streams." homepage = "https://github.com/luser/rust-test-assembler" license = "MIT" repository = "https://github.com/luser/rust-test-assembler" [dependencies.byteorder] version = "1" [dev-dependencies.doc-comment] version = "0.3.3" test-assembler-0.1.6/Cargo.toml.orig000064400000000000000000000005720000000000000154320ustar 00000000000000[package] name = "test-assembler" description = "A set of types for building complex binary streams." version = "0.1.6" authors = ["Ted Mielczarek "] license = "MIT" homepage = "https://github.com/luser/rust-test-assembler" repository = "https://github.com/luser/rust-test-assembler" [dependencies] byteorder = "1" [dev-dependencies] doc-comment = "0.3.3" test-assembler-0.1.6/LICENSE000064400000000000000000000020670000000000000135510ustar 00000000000000Copyright (c) 2015 Ted Mielczarek 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. test-assembler-0.1.6/README.md000064400000000000000000000071660000000000000140300ustar 00000000000000[![Continuous integration](https://github.com/luser/rust-test-assembler/actions/workflows/rust.yml/badge.svg)](https://github.com/luser/rust-test-assembler/actions/workflows/rust.yml) [![crates.io](https://img.shields.io/crates/v/test-assembler.svg)](https://crates.io/crates/test-assembler) [![](https://docs.rs/test-assembler/badge.svg)](https://docs.rs/test-assembler) # Overview This Rust crate implements a set of types for building complex binary streams, primarily for writing tests of things that ingest binary data. It's designed to allow you to create input data with specific properties that might be otherwise difficult to produce. For example, testing data in big-endian byte ordering on little-endian systems, testing your error-handling code by producing inputs that are malformed in certain ways, or testing code that parses a piece of a larger overall format without having to generate the whole thing. You use this crate primarily by creating a `Section`, which provides APIs to build a sequence of bytes piece-by-piece. In addition to using fixed data inputs, you can also insert the contents of `Label`s, which are placeholders for values that are not yet known. These can be used to insert data calculated from elsewhere, such as offsets into the `Section` being built. When you're finished, you can call `Section::get_contents()` to produce a `Vec` of the contents. # Example ``` rust use test_assembler::*; // Create some `Label`s, which can be used to fill in values that aren't yet known, // or marked to provide offsets into data in a `Section`. let l1 = Label::new(); let l2 = Label::new(); // `Section`s have an associated default endianness. let s = Section::with_endian(Endian::Little); // `start` is a `Label` whose value is the beginning of the `Section`'s data. let start = s.start(); let s = s.append_bytes(&[0x01, 0x02, 0x03]) // Append a 32-bit word with the section's default endianness // Methods on `Section` chain to make writing repeated entries simple! .D32(0xabcd0102) // L1 will now have the value of the current offset into the section. .mark(&l1) // Append the value of the `Label` l2 as a 16-bit word in big-endian .B16(&l2) // Append 10 bytes of 0xff. .append_repeated(0xff, 10); // Labels that have been added to Sections need to have a fixed value before // you call `Section::get_contents`. Calling `Section::mark` will do that, // or you can give them a constant value. l2.set_const(0x1234); let bytes = s.get_contents().unwrap(); // By default the `start` Label doesn't have a const value, but you can do math // on Labels that have relative offsets! let offset = &l1 - &start; assert_eq!(7, offset); let i = offset as usize; // Label values that appended have their value inserted when `Section::get_contents` // is called, so they can depend on things that aren't known at insertion time. assert_eq!(&[0x12, 0x34], &bytes[i..i + 2]); ``` For examples of real-world usage, you can take a look at [various tests in the `gimli` crate](https://github.com/gimli-rs/gimli/blob/13c526510d0b0f5fab4ddb9a9abc0318cb142754/src/read/str.rs#L287), [the stack unwinding tests in the `minidump` crate](https://github.com/luser/rust-minidump/blob/330e4956f283d297ce09e7d122e9770b6d763336/minidump-processor/src/stackwalker/x86_unittest.rs#L75), or [the synth_minidump tests in the `minidump` crate](https://github.com/luser/rust-minidump/blob/master/minidump/src/synth_minidump.rs), which also demonstrate building test fixtures using this crate to build more complex structured data. # License This software is provided under the MIT license. See [LICENSE](LICENSE). test-assembler-0.1.6/src/lib.rs000064400000000000000000001123370000000000000144510ustar 00000000000000// Copyright 2015 Ted Mielczarek. See the COPYRIGHT // file at the top-level directory of this distribution. //! A set of types for building complex binary streams. //! //! For testing code that consumes binary data, it is necessary to //! be able to generate test inputs consisting of various valid and //! invalid permutations. The `Section` struct defined in this crate //! allows for easily building a stream of bytes in any desired //! endianness. //! //! This crate defines two useful structs: [`Section`] and //! [`Label`]. A `Section` is simply a stream of bytes which can //! be written to, and a `Label` is a placeholder for a value that can be //! computed based on other values, but can be written to a `Section` without //! knowing its value at that time. //! //! This crate is based on [Jim Blandy's fantastic C++ implementation][1] in Google Breakpad. //! //! [1]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/common/test_assembler.h //! //! # Examples //! //! `Section` provides many convenient methods for appending data of specific endianness and byte width. There are methods for appending 8, 16, 32, and 64-bit integers using the `Section`'s default endianness, or explicitly using big or little-endian byte ordering. These methods all have shorthand names of the form: //! //! `[endianness][bits]` //! //! Where `endianness` is one of: //! * `D`: The `Section`'s default endianness //! * `L`: little-endian //! * `B`: big-endian //! And `bits` is any of 8, 16, 32, or 64 to write an integer from 1 to 8 bytes in length. //! //! So, for example, to write a little-endian 32-bit integer, call [`L32`](struct.Section.html#method.L32). //! //! The `Section` methods for appending data all consume and return the `Section` //! so that they can be chained: //! //! ``` //! use test_assembler::Section; //! //! let mut section = Section::new(); //! assert_eq!(section.D8(0xFF).L16(0xABCD).B32(0x12345678) //! .get_contents().unwrap(), //! &[0xFF, 0xCD, 0xAB, 0x12, 0x34, 0x56, 0x78]); //! ``` //! //! `Label`s can be appended to a section as placeholders for values that //! are not yet known using the same methods: //! //! ``` //! use test_assembler::*; //! //! let l = Label::new(); //! let mut s = Section::with_endian(Endian::Little); //! s = s.D32(&l); //! // Now assign a value to l. //! l.set_const(0x12345678); //! assert_eq!(s.get_contents().unwrap(), //! &[0x78, 0x56, 0x34, 0x12]); //! ``` //! //! `Label`s can also be set to the current length of the `Section` by calling //! [`mark`][mark], which is useful for calculating an offset into the data: //! //! [mark]: struct.Section.html#method.mark //! //! ``` //! use test_assembler::*; //! //! let l = Label::new(); //! let s = Section::with_endian(Endian::Little); //! let start = s.start(); //! s.append_repeated(0, 10) //! .mark(&l) //! .append_repeated(0, 10); //! assert_eq!(&l - &start, 10); //! ``` #![allow(non_snake_case)] extern crate byteorder; #[cfg(doctest)] extern crate doc_comment; #[cfg(doctest)] doc_comment::doctest!("../README.md"); use std::borrow::Borrow; use std::cell::RefCell; use std::fmt; use std::io::Cursor; use std::io::Seek; use std::io::SeekFrom; use std::io::Write; use std::ops::{Add, Deref, Sub}; use std::rc::Rc; use byteorder::{BigEndian, LittleEndian, WriteBytesExt}; /// Possible byte orders #[derive(Clone, Copy, Debug, PartialEq)] pub enum Endian { Big, Little, } /// The default endianness for this system. #[cfg(target_endian = "little")] pub const DEFAULT_ENDIAN: Endian = Endian::Little; /// The default endianness for this system. #[cfg(target_endian = "big")] pub const DEFAULT_ENDIAN: Endian = Endian::Big; /// Potential values of a `Binding`. enum BindingValue { /// A known constant value. Constant(u64), /// Equal to some other binding plus a constant. From(Rc, i64), /// Free to take on any value. Unconstrained, } impl fmt::Debug for BindingValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { BindingValue::Constant(v) => write!(f, "Constant({})", v), BindingValue::From(ref b, v) => write!(f, "From({:?}, {})", b, v), BindingValue::Unconstrained => write!(f, "Unconstrained"), } } } /// A label's value, or if that is not yet known, how the value is related to other labels' values. struct Binding { value: RefCell, } impl fmt::Debug for Binding { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Binding {{ {:?} }}", self.value.borrow()) } } /// These methods need to work with Rc, so they go on a trait implemented for that type. trait BindingOffset { /// Get the base `Binding` that this Binding references, as well /// as the sum of all offsets along the chain. fn get_base_and_offset(&self) -> (Rc, i64); /// Return the offset between two bindings, if they are related. fn offset(&self, other: &Rc) -> Option; } impl BindingOffset for Rc { fn get_base_and_offset(&self) -> (Rc, i64) { match *self.value.borrow().deref() { BindingValue::From(ref b, offset) => { let (base, base_offset) = b.get_base_and_offset(); (base, base_offset + offset) } // If it's not From another binding, just return self. _ => (self.clone(), 0), } } fn offset(&self, other: &Rc) -> Option { let (base, offset) = self.get_base_and_offset(); let (other_base, other_offset) = other.get_base_and_offset(); let base_ptr = base.deref() as *const Binding; let other_base_ptr = other_base.deref() as *const Binding; if base_ptr == other_base_ptr { // If the two bindings have the same base, then the offset is // the difference of their offsets. Some(offset - other_offset) } else { // Otherwise they are unrelated. None } } } impl Binding { /// Create a new Binding with an unconstrained value. pub fn unconstrained() -> Binding { Binding { value: RefCell::new(BindingValue::Unconstrained), } } /// Create a new Binding whose value is taken from another Binding. pub fn from(other: Rc, offset: i64) -> Binding { Binding { value: RefCell::new(BindingValue::From(other, offset)), } } /// Create a new Binding with a constant value. pub fn constant(val: u64) -> Binding { Binding { value: RefCell::new(BindingValue::Constant(val)), } } /// Set this `Binding`s value to `val`. pub fn set_const(&self, val: u64) { let mut v = self.value.borrow_mut(); *v = BindingValue::Constant(val); } /// Set this `Binding`s value equal to `other`. pub fn set(&self, other: Rc) { let mut v = self.value.borrow_mut(); let (base, offset) = other.get_base_and_offset(); *v = BindingValue::From(base, offset); } /// Get the constant value of the `Binding`, if known. pub fn value(&self) -> Option { match *self.value.borrow() { // If this Binding is a constant, this is easy. BindingValue::Constant(c) => Some(c), // If this Binding is based on another Binding, ask it for its // value. BindingValue::From(ref base, addend) => base.value().map(|v| v + addend as u64), // If this Binding is unconstrained then its value is not known. _ => None, } } } #[doc(hidden)] pub struct RealLabel { binding: Rc, } impl fmt::Debug for RealLabel { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.binding) } } /// Methods for creating a `Label` (or a `RealLabel`, but don't do that). pub trait LabelMaker { /// Create an undefined label. fn new() -> Self; /// Create a label with a constant value `val`. fn from_const(val: u64) -> Self; /// Create a label whose value is equal to `other`. fn from_label(other: &Self) -> Self; /// Create a label whose value is equal to `other` plus `offset`. fn from_label_offset(other: &Self, offset: i64) -> Self; } impl RealLabel { /// Get the constant value of the `RealLabel`, if known. pub fn value(&self) -> Option { self.binding.value() } /// Get the relative offset from another label, if possible. pub fn offset(&self, other: &RealLabel) -> Option { // Let the Binding calculate the offset. self.binding.offset(&other.binding) } /// Set this `RealLabel`s value to `val`. pub fn set_const(&self, val: u64) { self.binding.set_const(val); } /// Set this `RealLabel`s value equal to `other`. pub fn set(&self, other: &RealLabel) { //TODO: could use some sanity checks here? self.binding.set(other.binding.clone()) } } impl LabelMaker for RealLabel { fn new() -> RealLabel { RealLabel { binding: Rc::new(Binding::unconstrained()), } } fn from_const(val: u64) -> RealLabel { RealLabel { binding: Rc::new(Binding::constant(val)), } } fn from_label(other: &RealLabel) -> RealLabel { RealLabel { binding: other.binding.clone(), } } fn from_label_offset(other: &RealLabel, offset: i64) -> RealLabel { RealLabel { binding: Rc::new(Binding::from(other.binding.clone(), offset)), } } } /// A `Label` represents a value not yet known that is stored in a `Section`. /// /// As long as all the labels a section refers to are defined /// by the time its contents are retrieved as bytes, undefined /// labels can be used freely in that section's construction. /// A label can be in one of three states: /// /// * undefined, /// * defined as the sum of some other label and a constant, or /// * a constant. /// /// A label's value never changes, but it can accumulate constraints. /// Adding labels and integers is permitted, and yields a label. /// Subtracting a constant from a label is permitted, and also yields a /// label. Subtracting two labels that have some relationship to each /// other is permitted, and yields a constant. /// /// A common usage of `Label` is to use [`Section::mark`] to make a `Label` /// contain an offset into the `Section` without having to hard-code it. /// The `Section` methods for inserting fixed-width integer values (such as /// [`Section::L32`]) all accept `&Label` for the purpose of writing a value /// that is not yet available. /// /// # Examples /// /// `Label`s can be set to point to other `Label`s, potentially with an offset. /// /// ``` /// use test_assembler::*; /// /// let l1 = Label::new(); /// // l2 is l1's value (which is currently undefined) + 10 /// let l2 = &l1 + 10; /// // Now give l1 a value. /// l1.set_const(1); /// // l2's value is derived from l1. /// assert_eq!(l2.value().unwrap(), 11); /// ``` #[derive(Clone)] pub struct Label(pub Rc); impl fmt::Debug for Label { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Label {{ {:?} }}", self.0) } } impl Deref for Label { type Target = RealLabel; fn deref(&self) -> &RealLabel { let &Label(ref inner) = self; inner.deref() } } impl LabelMaker for Label { fn new() -> Label { Label(Rc::new(RealLabel::new())) } fn from_const(val: u64) -> Label { Label(Rc::new(RealLabel::from_const(val))) } fn from_label(other: &Label) -> Label { let &Label(ref inner) = other; Label(Rc::new(RealLabel::from_label(inner.borrow()))) } fn from_label_offset(other: &Label, offset: i64) -> Label { let &Label(ref inner) = other; Label(Rc::new(RealLabel::from_label_offset( inner.borrow(), offset, ))) } } /// Add a constant to a `Label`, producing a new `Label`. /// /// The new `Label` references the existing `Label`. impl<'a> Add for &'a Label { type Output = Label; fn add(self, rhs: i64) -> Label { Label::from_label_offset(self, rhs) } } /// Subtract a constant from a `Label`, producing a new `Label`. /// /// The new `Label` references the existing `Label`. impl<'a> Sub for &'a Label { type Output = Label; fn sub(self, rhs: i64) -> Label { Label::from_label_offset(self, -rhs) } } /// Subtract a `Label` from another `Label`, returning an `i64`. /// /// If the labels are unrelated this will panic. impl<'a> Sub<&'a Label> for &'a Label { type Output = i64; fn sub(self, rhs: &'a Label) -> i64 { self.offset(rhs).unwrap() } } /// A marker trait for number types. pub trait Num {} impl Num for u8 {} impl Num for u16 {} impl Num for u32 {} impl Num for u64 {} /// An enum to hold `Label`s or `Num`s. pub enum LabelOrNum { Label(Label), Num(T), } /// A trait to allow passing numbers or Labels. pub trait ToLabelOrNum<'a, T: Num> { fn to_labelornum(self) -> LabelOrNum; } impl<'a, T: Num> ToLabelOrNum<'a, T> for Label { fn to_labelornum(self) -> LabelOrNum { LabelOrNum::Label(self) } } impl<'a, T: Num> ToLabelOrNum<'a, T> for &'a Label { fn to_labelornum(self) -> LabelOrNum { LabelOrNum::Label(self.clone()) } } impl<'a, T: Num> ToLabelOrNum<'a, T> for T { fn to_labelornum(self) -> LabelOrNum { LabelOrNum::Num(self) } } /// A reference to a `Label` that has been appended to a `Section`. #[derive(Clone)] struct Reference { /// The `Label` being referenced. label: Label, /// The offset of the reference within the `Section`. offset: u64, /// The endianness with which the `Label` should be stored. endian: Endian, /// The size with which the `Label` should be stored. size: usize, } /// A section is a sequence of bytes, constructed by appending bytes to the end. /// /// Sections have a convenient and flexible set of member /// functions for appending data in various formats: big-endian and /// little-endian signed and unsigned values of different sizes, and /// raw blocks of bytes. /// /// There are methods for appending each of `u8`, `u16`, `u32`, and `u64` /// in each of big, little, or the `Section`'s default endianness. /// The method names are {D,L,B}{8,16,32,64} for each variant, so /// [`D16`][D16] writes a `u16` in the `Section`'s default endianness, and /// [`L64`][L64] writes a `u64` in little-endian byte order. /// /// Each of these methods also accepts a [`Label`][Label] to write /// non-constant values. /// /// See [the module-level documentation][module] for examples. /// /// [module]: index.html /// [D16]: #method.D16 /// [L64]: #method.L64 /// [Label]: struct.Label.html pub struct Section { /// The current endianness of the writer. /// /// This sets the behavior of the D appending functions. pub endian: Endian, /// The contents of the section. contents: Cursor>, /// References to `Label`s that were added to this `Section`. references: Vec, /// A label representing the start of this `Section`. start: Label, /// A label representing the final size of this `Section`. final_size: Label, } impl Section { /// Construct a `Section` with platform-default endianness. pub fn new() -> Section { Section::with_endian(DEFAULT_ENDIAN) } /// Construct a `Section` with `endian` endianness. pub fn with_endian(endian: Endian) -> Section { Section { endian, contents: Cursor::new(vec![]), references: vec![], start: Label::new(), final_size: Label::new(), } } /// Return the current size of the section. pub fn size(&self) -> u64 { self.contents.get_ref().len() as u64 } /// Return a `Label` that will resolve to the final size of the section. /// /// This label is undefined until `get_contents` is called. pub fn final_size(&self) -> Label { self.final_size.clone() } /// Get the contents of this section as a slice of bytes. /// /// Consumes the section. If there were still undefined labels, /// return `None`. pub fn get_contents(self) -> Option> { // Patch all labels into the section's contents. let mut section = self; section.final_size.set_const(section.size()); let references: Vec = section.references.to_vec(); let mut ok = true; section = references.iter().cloned().fold(section, |s, r| { if let Some(val) = r.label.value() { s.store_label_value(val, r.offset, r.endian, r.size) } else { ok = false; s } }); if ok { Some(section.contents.into_inner()) } else { None } } /// Return a label representing the start of the section. /// /// It is up to the user whether this label represents the section's /// position in an object file, the section's address in memory, or /// what have you; some applications may need both, in which case /// this simple-minded interface won't be enough. This class only /// provides a single start label, for use with the `here` and `mark` /// methods. pub fn start(&self) -> Label { self.start.clone() } /// A label representing the point at which the next appended item will appear in the section, relative to `start`. pub fn here(&self) -> Label { &self.start + self.size() as i64 } /// Set the value of `start` to `value`. pub fn set_start_const(self, value: u64) -> Section { self.start.set_const(value); self } /// Set `label` to [`here`](#method.here), and return this section. pub fn mark(self, label: &Label) -> Section { label.set(&self.here()); self } /// Append `data` to the end of this section, and return this section. pub fn append_bytes(mut self, data: &[u8]) -> Section { self.contents.write_all(data).unwrap(); self } /// Append `section`'s contents to the end of this section. /// /// Any `Label`s that were appended to `section` will not be /// resolved until this section is finalized. /// Return this section. pub fn append_section>(mut self, section: S) -> Section { let Section { contents, references, final_size, .. } = section.into(); final_size.set_const(contents.get_ref().len() as u64); let current = self.size(); self.contents.write_all(&contents.into_inner()).unwrap(); self.references.extend(references.into_iter().map(|mut r| { r.offset += current; r })); self } /// Append `count` copies of `byte` to the end of this section. /// /// Return this section. pub fn append_repeated(mut self, byte: u8, count: usize) -> Section { for _ in 0..count { self.contents.write_u8(byte).unwrap(); } self } /// Jump to the next location aligned on an `alignment`-byte boundary, relative to the start of the section. /// /// Fill the gap with zeroes. `alignment` must be a power of two. /// Return this section. pub fn align(self, alignment: u64) -> Section { // `alignment` must be a power of two. assert!(((alignment - 1) & alignment) == 0); let new_size = (self.size() + alignment - 1) & !(alignment - 1); let add = new_size - self.size(); self.append_repeated(0, add as usize) } fn store_label_value(mut self, val: u64, offset: u64, endian: Endian, size: usize) -> Section { let current = self.size(); if offset != current { self.contents.seek(SeekFrom::Start(offset)).unwrap(); } match endian { Endian::Little => match size { 1 => self.L8(val as u8), 2 => self.L16(val as u16), 4 => self.L32(val as u32), 8 => self.L64(val), _ => unreachable!("Unhandled label size!"), }, Endian::Big => match size { 1 => self.B8(val as u8), 2 => self.B16(val as u16), 4 => self.B32(val as u32), 8 => self.B64(val), _ => unreachable!("Unhandled label size!"), }, } } /// Append `label` to the Section with `endian` endianness, taking `size` bytes. /// /// Panics if `size` is not an intrinsic integer size: 1, 2, 4, 8 bytes. fn append_label(mut self, label: &Label, endian: Endian, size: usize) -> Section { let current = self.size(); // For labels with a known value, don't bother with a reference. if let Some(val) = label.value() { self.store_label_value(val, current, endian, size) } else { // label isn't yet known, need to store a reference. self.references.push(Reference { label: label.clone(), offset: current, endian, size, }); // Reserve space for the label. self.append_repeated(0, size) } } /// Append `byte` with the Section's default endianness. /// /// `byte` may be a `Label` or a `u8`. /// Return this section. pub fn D8<'a, T: ToLabelOrNum<'a, u8>>(mut self, byte: T) -> Section { let endian = self.endian; match byte.to_labelornum() { LabelOrNum::Num(n) => { self.contents.write_u8(n).unwrap(); self } LabelOrNum::Label(l) => self.append_label(&l, endian, 1), } } /// Append `byte` as little-endian (identical to `D8`). /// /// Return this section. pub fn L8<'a, T: ToLabelOrNum<'a, u8>>(self, byte: T) -> Section { self.D8(byte) } /// Append `byte` as big-endian (identical to `D8`). /// /// Return this section. pub fn B8<'a, T: ToLabelOrNum<'a, u8>>(self, byte: T) -> Section { self.D8(byte) } /// Append `word` with the Section's default endianness. /// /// `word` may be a `Label` or a `u16`. /// Return this section. pub fn D16<'a, T: ToLabelOrNum<'a, u16>>(self, word: T) -> Section { match self.endian { Endian::Little => self.L16(word), Endian::Big => self.B16(word), } } /// Append `word` as little-endian. /// /// `word` may be a `Label` or a `u16`. /// Return this section. pub fn L16<'a, T: ToLabelOrNum<'a, u16>>(mut self, word: T) -> Section { match word.to_labelornum() { LabelOrNum::Num(n) => { self.contents.write_u16::(n).unwrap(); self } LabelOrNum::Label(l) => self.append_label(&l, Endian::Little, 2), } } /// Append `word` as big-endian. /// /// `word` may be a `Label` or a `u16`. /// Return this section. pub fn B16<'a, T: ToLabelOrNum<'a, u16>>(mut self, word: T) -> Section { match word.to_labelornum() { LabelOrNum::Num(n) => { self.contents.write_u16::(n).unwrap(); self } LabelOrNum::Label(l) => self.append_label(&l, Endian::Big, 2), } } /// Append `dword` with the Section's default endianness. /// /// `dword` may be a `Label` or a `u32`. /// Return this section. pub fn D32<'a, T: ToLabelOrNum<'a, u32>>(self, dword: T) -> Section { match self.endian { Endian::Little => self.L32(dword), Endian::Big => self.B32(dword), } } /// Append `dword` as little-endian. /// /// `dword` may be a `Label` or a `u32`. /// Return this section. pub fn L32<'a, T: ToLabelOrNum<'a, u32>>(mut self, dword: T) -> Section { match dword.to_labelornum() { LabelOrNum::Num(n) => { self.contents.write_u32::(n).unwrap(); self } LabelOrNum::Label(l) => self.append_label(&l, Endian::Little, 4), } } /// Append `dword` as big-endian. /// /// `dword` may be a `Label` or a `u32`. /// Return this section. pub fn B32<'a, T: ToLabelOrNum<'a, u32>>(mut self, dword: T) -> Section { match dword.to_labelornum() { LabelOrNum::Num(n) => { self.contents.write_u32::(n).unwrap(); self } LabelOrNum::Label(l) => self.append_label(&l, Endian::Big, 4), } } /// Append `qword` with the Section's default endianness. /// /// `qword` may be a `Label` or a `u32`. /// Return this section. pub fn D64<'a, T: ToLabelOrNum<'a, u64>>(self, qword: T) -> Section { match self.endian { Endian::Little => self.L64(qword), Endian::Big => self.B64(qword), } } /// Append `qword` as little-endian. /// /// `qword` may be a `Label` or a `u32`. /// Return this section. pub fn L64<'a, T: ToLabelOrNum<'a, u64>>(mut self, qword: T) -> Section { match qword.to_labelornum() { LabelOrNum::Num(n) => { self.contents.write_u64::(n).unwrap(); self } LabelOrNum::Label(l) => self.append_label(&l, Endian::Little, 8), } } /// Append `qword` as big-endian. /// /// `qword` may be a `Label` or a `u32`. /// Return this section. pub fn B64<'a, T: ToLabelOrNum<'a, u64>>(mut self, qword: T) -> Section { match qword.to_labelornum() { LabelOrNum::Num(n) => { self.contents.write_u64::(n).unwrap(); self } LabelOrNum::Label(l) => self.append_label(&l, Endian::Big, 8), } } } impl Default for Section { fn default() -> Self { Section::new() } } #[test] fn binding_offset() { let b_u = Rc::new(Binding::unconstrained()); let b_c = Rc::new(Binding::constant(1)); let b_f1 = Rc::new(Binding::from(b_u.clone(), 0)); // b_f1 is equal to b_u, so the offset should be zero. assert_eq!(b_f1.offset(&b_u).unwrap(), 0); // b_f1 and b_c are unrelated assert!(b_f1.offset(&b_c).is_none()); // as are b_u and b_c. assert!(b_u.offset(&b_c).is_none()); assert!(b_c.offset(&b_u).is_none()); let b_f2 = Rc::new(Binding::from(b_c.clone(), 10)); // b_f2 is a non-zero offset from b_c. assert_eq!(b_f2.offset(&b_c).unwrap(), 10); // We need to go deeper. let b_f3 = Rc::new(Binding::from(b_f1.clone(), 10)); assert_eq!(b_f3.offset(&b_u).unwrap(), 10); let b_f4 = Rc::new(Binding::from(b_f3.clone(), 10)); assert_eq!(b_f4.offset(&b_u).unwrap(), 20); // Make another chain of bindings so we can test that the leaves of // both chains compare properly. // let b_f5 = Rc::new(Binding::from(b_u.clone(), 10)); let b_f6 = Rc::new(Binding::from(b_f5.clone(), 10)); assert_eq!(b_f6.offset(&b_f5).unwrap(), 10); assert_eq!(b_f6.offset(&b_u).unwrap(), 20); // Now for the kicker, b_f6 and b_f4 should be the same value, since // they're both b_u + 20. assert_eq!(b_f6.offset(&b_f4).unwrap(), 0); // and some additional checks. assert_eq!(b_f6.offset(&b_f3).unwrap(), 10); assert_eq!(b_f3.offset(&b_f6).unwrap(), -10); } #[test] fn binding_value() { let b_u = Rc::new(Binding::unconstrained()); let b_c = Rc::new(Binding::constant(1)); let b_f1 = Rc::new(Binding::from(b_u.clone(), 0)); assert!(b_u.value().is_none()); assert_eq!(b_c.value().unwrap(), 1); assert!(b_f1.value().is_none()); let b_f2 = Rc::new(Binding::from(b_c.clone(), 10)); assert_eq!(b_f2.value().unwrap(), 11); // We need to go deeper. let b_f3 = Rc::new(Binding::from(b_f1.clone(), 10)); assert!(b_f3.value().is_none()); let b_f4 = Rc::new(Binding::from(b_f3.clone(), 10)); assert!(b_f4.value().is_none()); let b_f5 = Rc::new(Binding::from(b_f2.clone(), 10)); assert_eq!(b_f5.value().unwrap(), 21); } #[test] fn label_unconstrained() { let l = Label::new(); assert!(l.value().is_none()); } #[test] fn label_const() { let l = Label::from_const(10); assert_eq!(l.value().unwrap(), 10); } #[test] fn label_label() { let l1 = Label::new(); let l2 = Label::from_label(&l1); assert!(l2.value().is_none()); // The offset should work both ways. assert_eq!(l2.offset(&l1).unwrap(), 0); assert_eq!(l1.offset(&l2).unwrap(), 0); } #[test] fn label_label_offset() { let l1 = Label::new(); let l2 = Label::from_label_offset(&l1, 10); assert!(l2.value().is_none()); assert_eq!(l2.offset(&l1).unwrap(), 10); assert_eq!(l1.offset(&l2).unwrap(), -10); let l3 = Label::from_label_offset(&l2, 10); assert_eq!(l3.offset(&l2).unwrap(), 10); assert_eq!(l2.offset(&l3).unwrap(), -10); assert_eq!(l3.offset(&l1).unwrap(), 20); assert_eq!(l1.offset(&l3).unwrap(), -20); let l4 = Label::from_label_offset(&l3, 10); assert_eq!(l4.offset(&l1).unwrap(), 30); // Check that chains of label offsets work properly. let l5 = Label::from_label_offset(&l1, 10); // l5 and l2 are both l1 + 10. assert_eq!(l5.offset(&l2).unwrap(), 0); assert_eq!(l5.offset(&l3).unwrap(), -10); assert_eq!(l3.offset(&l5).unwrap(), 10); } #[test] fn label_offset_unrelated() { let l1 = Label::new(); let l2 = Label::new(); assert!(l2.offset(&l1).is_none()); assert!(l1.offset(&l2).is_none()); } #[test] fn label_add() { let l1 = Label::new(); let l2 = &l1 + 10; assert_eq!(l2.offset(&l1).unwrap(), 10); } #[test] fn label_sub() { let l1 = Label::new(); let l2 = &l1 - 10; assert_eq!(l2.offset(&l1).unwrap(), -10); } #[test] fn label_sub_label() { let l1 = Label::new(); let l2 = &l1 + 10; assert_eq!(&l2 - &l1, 10); } #[test] fn label_set_const() { let l = Label::new(); let val = 0x12345678; l.set_const(val); assert_eq!(l.value().unwrap(), val); } #[test] fn label_set() { let val = 0x12345678; let l1 = Label::from_const(val); let l2 = Label::new(); l2.set(&l1); assert_eq!(l2.value().unwrap(), val); // Check that setting the first value's label *after* the call to set works. let l3 = Label::new(); let l4 = Label::new(); l4.set(&l3); l3.set_const(val); assert_eq!(l4.value().unwrap(), val); } #[test] fn section_construction() { let s = Section::new(); assert_eq!(s.endian, DEFAULT_ENDIAN); let s2 = Section::with_endian(Endian::Little); assert_eq!(s2.endian, Endian::Little); } #[test] fn section_append_bytes() { let s = Section::new(); let b1 = [0, 1, 2, 3, 4]; let b2 = [0xf, 0xe, 0xd, 0xc, 0xb]; assert_eq!( s.append_bytes(&b1) .append_bytes(&b2) .get_contents() .unwrap(), &[0, 1, 2, 3, 4, 0xf, 0xe, 0xd, 0xc, 0xb] ); } #[test] fn section_final_size() { let s = Section::new(); let size = s.final_size(); s.append_repeated(0, 20).get_contents().unwrap(); assert_eq!(size.value().unwrap(), 20); } #[test] fn section_append_section_simple() { assert_eq!( Section::new() .D8(0xab) .append_section(Section::new().D8(0xcd)) .D8(0xef) .get_contents() .unwrap(), &[0xab, 0xcd, 0xef] ); } #[test] fn section_append_section_labels() { let mut s = Section::new(); let l1 = Label::from_const(0x12); let l2 = Label::new(); s = s.D8(0xab); { s = s.append_section(Section::new().D8(0xcd).D8(&l1).D8(&l2)); } s = s.D8(0xef); l2.set_const(0x34); assert_eq!(s.get_contents().unwrap(), &[0xab, 0xcd, 0x12, 0x34, 0xef]); } #[test] fn section_append_section_final_size() { let s = Section::new().D8(0xcd); assert_eq!( Section::new() .D8(0xab) .D8(s.final_size()) .append_section(s) .D8(0xef) .get_contents() .unwrap(), &[0xab, 1, 0xcd, 0xef] ); } #[test] fn section_append_repeated() { let s = Section::new(); assert_eq!( s.append_repeated(0xff, 5).get_contents().unwrap(), &[0xff, 0xff, 0xff, 0xff, 0xff] ); } #[test] fn section_align() { let s = Section::new(); assert_eq!( s.D8(1).align(8).D8(1).get_contents().unwrap(), &[1, 0, 0, 0, 0, 0, 0, 0, 1] ); } #[test] fn section_test_8() { let s = Section::new(); assert_eq!( s.D8(0x12).L8(0x12).B8(0x12).get_contents().unwrap(), &[0x12, 0x12, 0x12] ); } #[test] fn section_test_16() { let s = Section::with_endian(Endian::Little); assert_eq!( s.D16(0xABCD) .L16(0xABCD) .B16(0xABCD) .get_contents() .unwrap(), &[0xCD, 0xAB, 0xCD, 0xAB, 0xAB, 0xCD] ); } #[test] fn section_test_32() { let s = Section::with_endian(Endian::Little); assert_eq!( s.D32(0xABCD1234) .L32(0xABCD1234) .B32(0xABCD1234) .get_contents() .unwrap(), &[0x34, 0x12, 0xCD, 0xAB, 0x34, 0x12, 0xCD, 0xAB, 0xAB, 0xCD, 0x12, 0x34] ); } #[test] fn section_test_64() { let s = Section::with_endian(Endian::Little); assert_eq!( s.D64(0x12345678ABCDEFFF) .L64(0x12345678ABCDEFFF) .B64(0x12345678ABCDEFFF) .get_contents() .unwrap(), &[ 0xFF, 0xEF, 0xCD, 0xAB, 0x78, 0x56, 0x34, 0x12, 0xFF, 0xEF, 0xCD, 0xAB, 0x78, 0x56, 0x34, 0x12, 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0xFF ] ); } #[test] fn section_d8l_const_label() { let l = Label::from_const(10); let s = Section::with_endian(Endian::Little); assert_eq!( s.D8(&l).L8(&l).B8(&l).get_contents().unwrap(), &[10, 10, 10] ); } #[test] fn section_d16l_const_label() { let l = Label::from_const(0xABCD); let s = Section::with_endian(Endian::Little); assert_eq!( s.D16(&l).L16(&l).B16(&l).get_contents().unwrap(), &[0xCD, 0xAB, 0xCD, 0xAB, 0xAB, 0xCD] ); } #[test] fn section_d32l_const_label() { let l = Label::from_const(0xABCD1234); let s = Section::with_endian(Endian::Little); assert_eq!( s.D32(&l).L32(&l).B32(&l).get_contents().unwrap(), &[0x34, 0x12, 0xCD, 0xAB, 0x34, 0x12, 0xCD, 0xAB, 0xAB, 0xCD, 0x12, 0x34] ); } #[test] fn section_d64l_const_label() { let l = Label::from_const(0xABCD12345678F00D); let s = Section::with_endian(Endian::Little); assert_eq!( s.D64(&l).L64(&l).B64(&l).get_contents().unwrap(), &[ 0x0D, 0xF0, 0x78, 0x56, 0x34, 0x12, 0xCD, 0xAB, 0x0D, 0xF0, 0x78, 0x56, 0x34, 0x12, 0xCD, 0xAB, 0xAB, 0xCD, 0x12, 0x34, 0x56, 0x78, 0xF0, 0x0D ] ); } #[test] fn section_get_contents_label_no_value() { // Trying to get_contents on a Section when a Label that was added // has no value should return None. let l = Label::new(); let s = Section::with_endian(Endian::Little); assert!(s.D8(&l).get_contents().is_none()); } #[test] fn section_label_assign_late() { let l = Label::new(); let mut s = Section::with_endian(Endian::Little); s = s.D8(&l).L8(&l).B8(&l); // Now assign a value to l. l.set_const(10); assert_eq!(s.get_contents().unwrap(), &[10, 10, 10]); } #[test] fn section_start_here() { let mut s = Section::with_endian(Endian::Little); s = s.append_repeated(0, 10); let start = s.start(); let mut here = s.here(); assert_eq!(here.offset(&start).unwrap(), 10); s = s.append_repeated(0, 10); here = s.here(); assert_eq!(here.offset(&start).unwrap(), 20); } #[test] fn section_start_mark() { let s = Section::with_endian(Endian::Little); let start = s.start(); let marked = Label::new(); s.append_repeated(0, 10) .mark(&marked) .append_repeated(0, 10); assert_eq!(marked.offset(&start).unwrap(), 10); } #[test] fn section_additional_methods_trait() { trait ExtraSection { fn add_a_thing(self) -> Section; } impl ExtraSection for Section { fn add_a_thing(self) -> Section { self.B8(0x12).B16(0x3456).B32(0x7890abcd) } } assert_eq!( Section::new() .D8(0) .add_a_thing() .D8(1) .get_contents() .unwrap(), &[0, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 1] ); } #[test] fn test_simple_labels() { let start = Label::new(); let end = Label::new(); let _section = Section::new().mark(&start).mark(&end); assert_eq!(start.offset(&end), Some(0)); } #[test] fn test_set_start_const() { let l = Label::new(); Section::new() .set_start_const(0) .append_repeated(0, 10) .mark(&l) .get_contents() .unwrap(); assert_eq!(l.value().unwrap(), 10); } #[test] fn section_bigendian_defaults() { let s = Section::with_endian(Endian::Big); assert_eq!( s.D8(0x12) .D16(0x1234) .D32(0x12345678) .D64(0x12345678ABCDEFFF) .get_contents() .unwrap(), &[ 0x12, 0x12, 0x34, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0xFF ] ); }