# structview
`structview` is a Rust library for casting references to binary data into
references to higher-level data structures, such as structs, unions, and arrays.
The implemented approach is similar to a common pattern used when parsing binary
data formats in C, where `char *`s representing the raw data are directly cast
to, for example, struct pointers. This technique has the benefits of being
simple and highly efficient. Unfortunately, it is also unsafe, as issues with
alignment and integer endianess are usually ignored. `structview` avoids these
issues by providing a safe interface to its users.
The intended use-case for this crate is parsing of binary data formats,
particularly if one is only interested in the value of certain fields, not all
of them. In these cases `structview` can be used to efficiently find the fields
of interest without having to potentially perform byteorder conversion for all
the irrelevant ones. If all fields need to be parsed anyway, it is probably more
straightforward to use the [`byteorder` crate](https://crates.io/crates/byteorder)
directly.
## Example
The following example demonstrates viewing a slice of binary data as a simple
struct:
```rust
use structview::{u32_le, View};
#[derive(Clone, Copy, View)]
#[repr(C)]
struct Animal {
name: [u8; 4],
number_of_heads: u8,
number_of_legs: u32_le,
}
fn main() -> Result<(), structview::Error> {
let data = [0x43, 0x61, 0x74, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00];
let animal = Animal::view(&data)?;
assert_eq!(animal.name, *b"Cat\x00");
assert_eq!(animal.number_of_heads, 1);
assert_eq!(animal.number_of_legs.to_int(), 4);
Ok(())
}
```
As the example shows, `structview` makes reinterpreting raw data both safe and
convenient by providing [an (automatically derivable) trait `View`](#view-trait),
as well as [types for safely viewing integer fields](#integer-views).
## Requirements
Requires Rust version **1.38.0** or newer.
## The `View` Trait
By implementing the `View` trait, a type promises it is safe to be cast from raw
binary data. The trait adds several view methods to implementing types, which
enable producing references to instances of these types from byte slices:
```rust
pub unsafe trait View: Copy {
fn view(data: &[u8]) -> Result<&Self, structview::Error> { ... }
fn view_slice(data: &[u8]) -> Result<&[Self], Error> { ... }
fn view_boxed_slice(data: Box<[u8]>) -> Result, Error> { ... }
}
```
All view methods check the length of the given `data` and return
`Error::NotEnoughData` if there are too few bytes. Additionally, the two
slice view methods require that `mem::size_of::() > 0` and will panic
otherwise.
Implementing `View` is unsafe as one must ensure that:
- every possible raw byte value constitutes valid data for the implementing type
- the implementing type is 1-byte aligned
- the compiler doesn't change the order of the implementing type's fields (in
case of a compound type)
`structview` already implements `View` for the 1-byte integer types (`i8` and
`u8`), arrays of `View` types, and the provided integer views (`u32_le` and
friends). Based on these primitives, users can create views for their own
structs and unions.
Manually implementing the `View` trait is not recommended. Instead, it should be
automatically derived as demonstrated in the example above. The derive ensures
safety be enforcing that implementing structs and unions are `repr(C)` and
contain only `View` fields. This is sufficient to satisfy the safety
requirements mentioned above.
## Integer Views
While the single-byte integers `i8` and `u8` can be safely cast from raw data,
wider integers can not: Their alignment is incompatible with the 1-byte
alignment of byte slices and their (application-defined) endianess might be
incompatible with the system's native byteorder.
`structview` solves this by casting these wider integer types to special integer
views instead: wrappers around `u8` arrays of appropriate sizes that provide
methods to parse the raw data into the actual integers.
The `u32_le` type used in the example is one of these integer views. It is
actually an alias for `U32`, a type generic over a `ByteOrder`
supplied by the [`byteorder` crate](https://crates.io/crates/byteorder). The
following table lists all provided integer views:
| bit-width | generic | little-endian | big-endian |
| :-------: | :-------: | :-----------: | :--------: |
| 16 | `I16` | `i16_le` | `i16_be` |
| | `U16` | `u16_le` | `u16_be` |
| 32 | `I32` | `i32_le` | `i32_be` |
| | `U32` | `u32_le` | `u32_be` |
| 64 | `I64` | `i64_le` | `i64_be` |
| | `U64` | `u64_le` | `u64_be` |
Each integer view provides a `to_int` method that parses and returns the
respective integer value. Each integer view also implements the [`From`
conversion trait](https://doc.rust-lang.org/std/convert/trait.From.html) for its
integer type.
## Use in `no_std` contexts
`structview` has a feature, `std`, that is enabled by default. To use
the crate in `no_std` contexts, disable the default features in the
`Cargo.toml`:
```toml
[dependencies.structview]
version = "1"
default-features = false
```
If `std` is disabled:
- `structview::Error` does not impl `std::error::Error`
- `View::view_boxed_slice` is not available
## License
This project is licensed under the MIT license ([LICENSE](LICENSE) or
http://opensource.org/licenses/MIT).
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in structview by you, shall be licensed as above, without any
additional terms or conditions.