bytes-cast 0.3.0

Safely re-interpreting &[u8] bytes as custom structs without copying, for efficiently reading structured binary data.
Documentation
//! Utilities for safely re-interpreting `&[u8]` bytes as custom structs
//! and back without copying, for efficiently reading structured binary data.
//!
//! # Example
//!
//! Reading bytes:
//!
//! ```
//! use bytes_cast::{BytesCast, unaligned};
//!
//! #[derive(BytesCast)]
//! #[repr(C)]
//! struct Foo {
//!     bar: [u8; 2],
//!     baz: unaligned::U32Be,
//! }
//!
//! let input = &[1_u8, 2, 3, 4, 5, 6, 7, 8];
//!
//! let (foo, rest) = Foo::from_bytes(input).unwrap();
//! assert_eq!(foo.bar, [1_u8, 2]);
//! assert_eq!(foo.baz.get(), 0x0304_0506_u32);
//! assert_eq!(rest, &[7_u8, 8]);
//!
//! assert!(<[Foo; 2]>::from_bytes(input).is_err()); // input is too short
//!
//! let (values, rest) = unaligned::U16Le::slice_from_bytes(input, 2).unwrap();
//! assert_eq!(values.len(), 2);
//! assert_eq!(values[0].get(), 0x02_01_u16);
//! assert_eq!(values[1].get(), 0x04_03_u16);
//! assert_eq!(rest, &[5_u8, 6, 7, 8]);
//!
//! assert!(unaligned::U16Le::slice_from_bytes(input, 5).is_err()); // input is too short
//! ```
//!
//! Writing bytes:
//!
//! ```
//! # use bytes_cast::{BytesCast, unaligned};
//! # #[derive(BytesCast)]
//! # #[repr(C)]
//! # struct Foo {
//! #     bar: [u8; 2],
//! #     baz: unaligned::U32Be,
//! # }
//!
//! let foo = Foo { bar: [1, 2], baz: 0x0304_0506.into() };
//! assert_eq!(foo.as_bytes(), &[1_u8, 2, 3, 4, 5, 6]);
//!
//! let slice: &[unaligned::U16Le] = &[0x02_01.into(), 0x04_03.into()];
//! assert_eq!(slice.as_bytes(), &[1_u8, 2, 3, 4]);
//! ```

#![no_std]

pub use bytes_cast_derive::BytesCast;
use core::fmt;
use core::mem;
use core::slice;

pub mod unaligned;

#[cfg(doctest)]
mod compile_fail_tests;

/// Marks a type as safe to interpret from and to bytes without copying.
///
/// # Safety
///
/// For a type to implement this trait:
///
/// * All initialized bit patterns must be valid. (This excludes `bool`, enums, etc.)
/// * There must not be an alignment requirement. (`align_of() == 1`)
/// * There must be no padding or otherwise uninitialized bytes
///
/// # Deriving
///
/// Instead of writing `unsafe impl` blocks this trait should be derived.
/// `#[derive(BytesCast)]` on a type definition invokes a procedural macro
/// that implements the trait after checking that the type:
///
/// * Is a `struct`
/// * Is not generic
/// * Has a `#[repr(C)]` or `#[repr(transparent)]` attribute
/// * Has `align_of() == 1`
/// * Only has fields whose respective type implement `BytesCast`.
///
/// Failing any of these checks causes a compile-time error.
/// This excludes some types that could implement `BytesCast` without memory safety
/// issue:
///
/// * By choice: disabling field reordering with `repr` is not about memory
///   safety but making memory layout / field offsets predictable.
/// * By necessity: generics would make `align_of` potentially depend on type
///   parameters and not possible to statically check at the struct definition
///   site.
pub unsafe trait BytesCast {
    /// Interpret the start of the given slice of bytes as reference to this
    /// type.
    ///
    /// If the given input is large enough, returns a tuple of the new
    /// reference and the remaining of the bytes.
    #[inline]
    fn from_bytes(bytes: &[u8]) -> Result<(&Self, &[u8]), FromBytesError>
    where
        Self: Sized,
    {
        let expected_len = mem::size_of::<Self>();
        remaining_bytes(bytes, expected_len).map(|rest| {
            // Safety: this cast and dereference are made sound by the length
            // check done in `remaining_bytes` together with the
            // invariants of `BytesCast`.
            let this = unsafe { &*bytes.as_ptr().cast::<Self>() };
            (this, rest)
        })
    }

    /// Interpret the start of the given slice of bytes as slice of this type.
    ///
    /// If the given input is large enough, returns a tuple of the new
    /// slice and the remaining of the bytes.
    #[inline]
    fn slice_from_bytes(bytes: &[u8], slice_len: usize) -> Result<(&[Self], &[u8]), FromBytesError>
    where
        Self: Sized,
    {
        let expected_byte_len =
            mem::size_of::<Self>()
                .checked_mul(slice_len)
                .ok_or(FromBytesError {
                    input_len: bytes.len(),
                    expected_len: None,
                })?;
        remaining_bytes(bytes, expected_byte_len).map(|rest| {
            // Safety: this cast and call are made sound by the length check
            // done in `remaining_bytes` together with the invariants of
            // `BytesCast`.
            let this = unsafe { slice::from_raw_parts(bytes.as_ptr().cast::<Self>(), slice_len) };
            (this, rest)
        })
    }

    /// Interpret this value as the bytes of its memory representation.
    #[inline]
    fn as_bytes(&self) -> &[u8] {
        let ptr: *const Self = self;
        let bytes_ptr = ptr.cast::<u8>();
        let bytes_len = mem::size_of_val(self);
        // Safety: the invariants of `BytesCast` make this sound by definition:
        unsafe { slice::from_raw_parts(bytes_ptr, bytes_len) }
    }
}

/// If the given slice is long enough, return the the remaining bytes after the
/// given length.
#[inline]
fn remaining_bytes(bytes: &[u8], expected_byte_len: usize) -> Result<&[u8], FromBytesError> {
    bytes.get(expected_byte_len..).ok_or(FromBytesError {
        input_len: bytes.len(),
        expected_len: Some(expected_byte_len),
    })
}

/// The error type for [`BytesCast::from_bytes`] and
/// [`BytesCast::slice_from_bytes`].
pub struct FromBytesError {
    expected_len: Option<usize>,
    input_len: usize,
}

impl fmt::Display for FromBytesError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(expected_len) = self.expected_len {
            write!(
                f,
                "Expected at least {} bytes, got {}",
                expected_len, self.input_len
            )
        } else {
            write!(f, "Expected byte size overflowed in slice_from_bytes")
        }
    }
}

impl fmt::Debug for FromBytesError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

unsafe impl<T: ?Sized> BytesCast for core::marker::PhantomData<T> {}
unsafe impl<T: BytesCast> BytesCast for [T] {}
unsafe impl BytesCast for () {}
unsafe impl BytesCast for u8 {}

// NOTE: We don’t implement BytesCast for tuples with 2 or more fields
// because they are subject to field reordering.
// Like with default-`repr` structs this is not a memory safety issue but still
// a footgun. Single-field tuples don’t have that problem but are much less
// useful in the first place.

// FIXME: Use const generics when we require Rust 1.51+
macro_rules! array_impls {
    ($($N: expr)+) => {
        $(
            unsafe impl<T: BytesCast> BytesCast for [T; $N] {}
        )+
    };
}

array_impls!(
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
);