binary-layout 3.1.3

The binary-layout library allows type-safe, inplace, zero-copy access to structured binary data. You define a custom data layout and give it a slice of binary data, and it will allow you to read and write the fields defined in the layout from the binary data without having to copy any of the data. It's similar to transmuting to/from a `#[repr(packed)]` struct, but much safer.
Documentation
use core::convert::TryFrom;

use super::super::{Field, StorageIntoFieldView, StorageToFieldView};
use super::PrimitiveField;
use crate::endianness::Endianness;
use crate::utils::data::Data;

/// This trait is implemented for fields with "slice access",
/// i.e. fields that are read/write directly without a copy
/// by returning a borrowed slice to the underlying data.
pub trait FieldSliceAccess<'a>: Field {
    /// The type of slice returned from calls requesting read access
    type SliceType: 'a;
    /// The type of slice returned from calls requesting write access
    type MutSliceType: 'a;

    /// Borrow the data in the byte array with read access using the [Field] API.
    ///
    /// # Example:
    /// ```
    /// use binary_layout::prelude::*;
    ///
    /// define_layout!(my_layout, LittleEndian, {
    ///     //... other fields ...
    ///     tail_data: [u8],
    /// });
    ///
    /// fn func(storage_data: &[u8]) {
    ///     let tail_data: &[u8] = my_layout::tail_data::data(storage_data);
    /// }
    /// ```
    fn data(storage: &'a [u8]) -> Self::SliceType;

    /// Borrow the data in the byte array with write access using the [Field] API.
    ///
    /// # Example:
    /// ```
    /// use binary_layout::prelude::*;
    ///
    /// define_layout!(my_layout, LittleEndian, {
    ///     //... other fields ...
    ///     tail_data: [u8],
    /// });
    ///
    /// fn func(storage_data: &mut [u8]) {
    ///     let tail_data: &mut [u8] = my_layout::tail_data::data_mut(storage_data);
    /// }
    /// ```
    fn data_mut(storage: &'a mut [u8]) -> Self::MutSliceType;
}

/// Field type `[u8]`:
/// This field represents an [open ended byte array](crate#open-ended-byte-arrays-u8).
/// In this impl, we define accessors for such fields.
impl<'a, E: Endianness, const OFFSET_: usize> FieldSliceAccess<'a>
    for PrimitiveField<[u8], E, OFFSET_>
{
    type SliceType = &'a [u8];
    type MutSliceType = &'a mut [u8];

    /// Borrow the data in the byte array with read access using the [Field] API.
    ///
    /// # Example:
    /// ```
    /// use binary_layout::prelude::*;
    ///
    /// define_layout!(my_layout, LittleEndian, {
    ///     //... other fields ...
    ///     tail_data: [u8],
    /// });
    ///
    /// fn func(storage_data: &[u8]) {
    ///     let tail_data: &[u8] = my_layout::tail_data::data(storage_data);
    /// }
    /// ```
    #[inline(always)]
    fn data(storage: &'a [u8]) -> &'a [u8] {
        &storage[Self::OFFSET..]
    }

    /// Borrow the data in the byte array with write access using the [Field] API.
    ///
    /// # Example:
    /// ```
    /// use binary_layout::prelude::*;
    ///
    /// define_layout!(my_layout, LittleEndian, {
    ///     //... other fields ...
    ///     tail_data: [u8],
    /// });
    ///
    /// fn func(storage_data: &mut [u8]) {
    ///     let tail_data: &mut [u8] = my_layout::tail_data::data_mut(storage_data);
    /// }
    /// ```
    #[inline(always)]
    fn data_mut(storage: &'a mut [u8]) -> &'a mut [u8] {
        &mut storage[Self::OFFSET..]
    }
}
impl<E: Endianness, const OFFSET_: usize> Field for PrimitiveField<[u8], E, OFFSET_> {
    /// See [Field::Endian]
    type Endian = E;
    /// See [Field::OFFSET]
    const OFFSET: usize = OFFSET_;
    /// See [Field::SIZE]
    const SIZE: Option<usize> = None;
}
impl<'a, E: Endianness, const OFFSET_: usize> StorageToFieldView<&'a [u8]>
    for PrimitiveField<[u8], E, OFFSET_>
{
    type View = &'a [u8];

    #[inline(always)]
    fn view(storage: &'a [u8]) -> Self::View {
        &storage[Self::OFFSET..]
    }
}

impl<'a, E: Endianness, const OFFSET_: usize> StorageToFieldView<&'a mut [u8]>
    for PrimitiveField<[u8], E, OFFSET_>
{
    type View = &'a mut [u8];

    #[inline(always)]
    fn view(storage: &'a mut [u8]) -> Self::View {
        &mut storage[Self::OFFSET..]
    }
}

impl<S: AsRef<[u8]>, E: Endianness, const OFFSET_: usize> StorageIntoFieldView<S>
    for PrimitiveField<[u8], E, OFFSET_>
{
    type View = Data<S>;

    #[inline(always)]
    fn into_view(storage: S) -> Self::View {
        Data::from(storage).into_subregion(Self::OFFSET..)
    }
}

/// Field type `[u8; N]`:
/// This field represents a [fixed size byte array](crate#fixed-size-byte-arrays-u8-n).
/// In this impl, we define accessors for such fields.
impl<'a, E: Endianness, const N: usize, const OFFSET_: usize> FieldSliceAccess<'a>
    for PrimitiveField<[u8; N], E, OFFSET_>
{
    type SliceType = &'a [u8; N];
    type MutSliceType = &'a mut [u8; N];

    /// Borrow the data in the byte array with read access using the [Field] API.
    /// See also [FieldSliceAccess::data].
    ///
    /// # Example:
    /// ```
    /// use binary_layout::prelude::*;
    ///
    /// define_layout!(my_layout, LittleEndian, {
    ///     //... other fields ...
    ///     some_field: [u8; 5],
    ///     //... other fields
    /// });
    ///
    /// fn func(storage_data: &[u8]) {
    ///     let some_field: &[u8; 5] = my_layout::some_field::data(storage_data);
    /// }
    /// ```
    #[inline(always)]
    fn data(storage: &'a [u8]) -> &'a [u8; N] {
        // TODO Can this be done without try_from? https://stackoverflow.com/questions/71216771/create-array-reference-to-sub-slice
        <&[u8; N]>::try_from(&storage[Self::OFFSET..(Self::OFFSET + N)]).unwrap()
    }

    /// Borrow the data in the byte array with write access using the [Field] API.
    /// See also [FieldSliceAccess::data_mut]
    ///
    /// # Example:
    /// ```
    /// use binary_layout::prelude::*;
    ///
    /// define_layout!(my_layout, LittleEndian, {
    ///     //... other fields ...
    ///     some_field: [u8; 5],
    ///     //... other fields
    /// });
    ///
    /// fn func(storage_data: &mut [u8]) {
    ///     let some_field: &mut [u8; 5] = my_layout::some_field::data_mut(storage_data);
    /// }
    /// ```
    #[inline(always)]
    fn data_mut(storage: &'a mut [u8]) -> &'a mut [u8; N] {
        // TODO Can this be done without try_from? https://stackoverflow.com/questions/71216771/create-array-reference-to-sub-slice
        <&mut [u8; N]>::try_from(&mut storage[Self::OFFSET..(Self::OFFSET + N)]).unwrap()
    }
}
impl<E: Endianness, const N: usize, const OFFSET_: usize> Field
    for PrimitiveField<[u8; N], E, OFFSET_>
{
    /// See [Field::Endian]
    type Endian = E;
    /// See [Field::OFFSET]
    const OFFSET: usize = OFFSET_;
    /// See [Field::SIZE]
    const SIZE: Option<usize> = Some(N);
}
impl<'a, E: Endianness, const N: usize, const OFFSET_: usize> StorageToFieldView<&'a [u8]>
    for PrimitiveField<[u8; N], E, OFFSET_>
{
    type View = &'a [u8; N];

    #[inline(always)]
    fn view(storage: &'a [u8]) -> Self::View {
        Self::View::try_from(&storage[Self::OFFSET..(Self::OFFSET + N)]).unwrap()
    }
}

impl<'a, E: Endianness, const N: usize, const OFFSET_: usize> StorageToFieldView<&'a mut [u8]>
    for PrimitiveField<[u8; N], E, OFFSET_>
{
    type View = &'a mut [u8; N];

    #[inline(always)]
    fn view(storage: &'a mut [u8]) -> Self::View {
        Self::View::try_from(&mut storage[Self::OFFSET..(Self::OFFSET + N)]).unwrap()
    }
}

impl<S: AsRef<[u8]>, E: Endianness, const N: usize, const OFFSET_: usize> StorageIntoFieldView<S>
    for PrimitiveField<[u8; N], E, OFFSET_>
{
    type View = Data<S>;

    #[inline(always)]
    fn into_view(storage: S) -> Self::View {
        Data::from(storage).into_subregion(Self::OFFSET..(Self::OFFSET + N))
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::float_cmp)]
    use crate::prelude::*;
    use crate::PrimitiveField;

    #[test]
    fn test_slice() {
        let mut storage = vec![0; 1024];

        type Field1 = PrimitiveField<[u8], LittleEndian, 5>;
        type Field2 = PrimitiveField<[u8], BigEndian, 7>;

        Field1::data_mut(&mut storage)[..5].copy_from_slice(&[10, 20, 30, 40, 50]);
        Field2::data_mut(&mut storage)[..5].copy_from_slice(&[60, 70, 80, 90, 100]);

        assert_eq!(&[10, 20, 60, 70, 80], &Field1::data(&storage)[..5]);
        assert_eq!(&[60, 70, 80, 90, 100], &Field2::data(&storage)[..5]);

        // Check types are correct
        let _a: &[u8] = Field1::data(&storage);
        let _b: &mut [u8] = Field1::data_mut(&mut storage);
    }

    #[test]
    fn test_array() {
        let mut storage = vec![0; 1024];

        type Field1 = PrimitiveField<[u8; 2], LittleEndian, 5>;
        type Field2 = PrimitiveField<[u8; 5], BigEndian, 6>;

        Field1::data_mut(&mut storage).copy_from_slice(&[10, 20]);
        Field2::data_mut(&mut storage).copy_from_slice(&[60, 70, 80, 90, 100]);

        assert_eq!(&[10, 60], Field1::data(&storage));
        assert_eq!(&[60, 70, 80, 90, 100], Field2::data(&storage));

        assert_eq!(Some(2), PrimitiveField::<[u8; 2], LittleEndian, 5>::SIZE);
        assert_eq!(Some(5), PrimitiveField::<[u8; 5], BigEndian, 5>::SIZE);

        // Check types are correct
        let _a: &[u8; 2] = Field1::data(&storage);
        let _b: &mut [u8; 2] = Field1::data_mut(&mut storage);
    }
}