| //! A small Rust library that allows users to interpret arrays of bytes |
| //! as certain kinds of structures safely. |
| //! |
| //! This crate provides an unsafe trait [`Plain`](trait.Plain.html), which the user |
| //! of the crate uses to mark types for which operations of this library are safe. |
| //! See [`Plain`](trait.Plain.html) for the contractual obligation. |
| //! |
| //! Other than that, everything else in this crate is perfectly safe to use as long |
| //! as the `Plain` trait is not implemented on inadmissible types (similar to how |
| //! `Send` and `Sync` in the standard library work). |
| //! |
| //! # Purpose |
| //! |
| //! In low level systems development, it is sometimes necessary to |
| //! interpret locations in memory as data structures. Functions of |
| //! this crate serve to avoid pitfalls associated with that, without |
| //! having to resort to big, full-featured (de)serialization libraries. |
| //! |
| //! On the other hand, this crate contains no provisions when it comes |
| //! to handling differences in encoding and byte ordering between |
| //! platforms. As such, it is entirely unsuitable for processing |
| //! external data such as file contents or network packets. |
| //! |
| //! # Examples |
| //! |
| //! To start using the crate, simply do `extern crate plain;`. |
| //! |
| //! If you want your plain types to have methods from this crate, also include `use plain.Plain;`. |
| //! |
| //! Then it's just a matter of marking the right types and using them. |
| //! |
| //! ``` |
| //! |
| //! extern crate plain; |
| //! use plain::Plain; |
| //! use std::mem; |
| //! |
| //! |
| //! #[repr(C)] |
| //! #[derive(Default)] |
| //! struct ELF64Header { |
| //! pub e_ident: [u8; 16], |
| //! pub e_type: u16, |
| //! pub e_machine: u16, |
| //! pub e_version: u32, |
| //! pub e_entry: u64, |
| //! pub e_phoff: u64, |
| //! pub e_shoff: u64, |
| //! pub e_flags: u32, |
| //! pub e_ehsize: u16, |
| //! pub e_phentsize: u16, |
| //! pub e_phnum: u16, |
| //! pub e_shentsize: u16, |
| //! pub e_shnum: u16, |
| //! pub e_shstrndx: u16, |
| //! } |
| //! |
| //! // SAFE: ELF64Header satisfies all the requirements of `Plain`. |
| //! unsafe impl Plain for ELF64Header {} |
| //! |
| //! impl ELF64Header { |
| //! fn from_bytes(buf: &[u8]) -> &ELF64Header { |
| //! plain::from_bytes(buf).expect("The buffer is either too short or not aligned!") |
| //! } |
| //! |
| //! fn from_mut_bytes(buf: &mut [u8]) -> &mut ELF64Header { |
| //! plain::from_mut_bytes(buf).expect("The buffer is either too short or not aligned!") |
| //! } |
| //! |
| //! fn copy_from_bytes(buf: &[u8]) -> ELF64Header { |
| //! let mut h = ELF64Header::default(); |
| //! h.copy_from_bytes(buf).expect("The buffer is too short!"); |
| //! h |
| //! } |
| //! } |
| //! |
| //! # fn process_elf(elf: &ELF64Header) {} |
| //! |
| //! // Conditional copying for ultimate hackery. |
| //! fn opportunistic_elf_processing(buf: &[u8]) { |
| //! if plain::is_aligned::<ELF64Header>(buf) { |
| //! // No copy necessary. |
| //! let elf_ref = ELF64Header::from_bytes(buf); |
| //! process_elf(elf_ref); |
| //! } else { |
| //! // Not aligned properly, copy to stack first. |
| //! let elf = ELF64Header::copy_from_bytes(buf); |
| //! process_elf(&elf); |
| //! } |
| //! } |
| //! |
| //! #[repr(C)] |
| //! #[derive(Default, Copy, Clone)] |
| //! struct ArrayEntry { |
| //! pub name: [u8; 32], |
| //! pub tag: u32, |
| //! pub score: u32, |
| //! } |
| //! |
| //! // SAFE: ArrayEntry satisfies all the requirements of `Plain`. |
| //! unsafe impl Plain for ArrayEntry {} |
| //! |
| //! fn array_from_bytes(buf: &[u8]) -> &[ArrayEntry] { |
| //! // NOTE: length is not a concern here, |
| //! // since slice_from_bytes() can return empty slice. |
| //! |
| //! match plain::slice_from_bytes(buf) { |
| //! Err(_) => panic!("The buffer is not aligned!"), |
| //! Ok(arr) => arr, |
| //! } |
| //! } |
| //! |
| //! fn array_from_unaligned_bytes(buf: &[u8]) -> Vec<ArrayEntry> { |
| //! let length = buf.len() / mem::size_of::<ArrayEntry>(); |
| //! let mut result = vec![ArrayEntry::default(); length]; |
| //! (&mut result).copy_from_bytes(buf).expect("Cannot fail here."); |
| //! result |
| //! } |
| //! |
| //! # fn main() {} |
| //! |
| //! ``` |
| //! |
| //! # Comparison to [`pod`](https://crates.io/crates/pod) |
| //! |
| //! [`pod`](https://crates.io/crates/pod) is another crate created to help working with plain data. |
| //! The major difference between `pod` and `plain` is scope. |
| //! |
| //! `plain` currently provides only a few functions (+method wrappers) and its implementation |
| //! involves very few lines of unsafe code. It can be used in `no_std` code. Also, it doesn't |
| //! deal with [endianness](https://en.wikipedia.org/wiki/Endianness) in any way, |
| //! so it is only suitable for certain kinds of low-level work. |
| //! |
| //! `pod`, on the other hand, provides a wide arsenal |
| //! of various methods, most of which may be unnecessary for a given use case. |
| //! It has dependencies on `std` as well as other crates, but among other things |
| //! it provides tools to handle endianness properly. |
| //! |
| //! In short, `plain` is much, much _plainer_... |
| #![no_std] |
| |
| mod error; |
| pub use error::Error; |
| |
| mod plain; |
| pub use plain::Plain; |
| |
| mod methods; |
| pub use methods::{as_bytes, as_mut_bytes, copy_from_bytes, from_bytes, from_mut_bytes, is_aligned, |
| slice_from_bytes, slice_from_bytes_len, slice_from_mut_bytes, |
| slice_from_mut_bytes_len}; |
| |
| #[cfg(test)] |
| #[macro_use] |
| extern crate std; |
| |
| #[cfg(test)] |
| mod tests; |