musli-zerocopy 0.1.4

Refreshingly simple zero copy primitives by Müsli.
Documentation
#![allow(clippy::assertions_on_constants)]

use core::array;

use anyhow::Result;

use crate::mem::PackedMaybeUninit;
use crate::{Ref, ZeroCopy};

use super::{OwnedBuf, SliceMut};

#[derive(Debug, PartialEq, ZeroCopy)]
#[zero_copy(crate)]
#[repr(C)]
struct Inner {
    first: u8,
    second: u64,
}

#[test]
fn allocate_array_needing_padding() -> Result<()> {
    #[derive(Debug, PartialEq, ZeroCopy)]
    #[zero_copy(crate)]
    #[repr(C)]
    struct Element {
        first: u8,
        second: u32,
        third: [Inner; 2],
    }

    const _: () = assert!(Element::PADDED);

    let mut buf = OwnedBuf::new();

    let array = buf.store_uninit::<[Element; 10]>()?;

    let values = array::from_fn(|index| Element {
        first: index as u8,
        second: 0x01020304u32,
        third: [
            Inner {
                first: index as u8,
                second: 0x05060708090a0b0cu64,
            },
            Inner {
                first: index as u8,
                second: 0x05060708090a0b0cu64,
            },
        ],
    });

    buf.load_uninit_mut(array)?.write(&values);
    let array = array.assume_init();

    assert_eq!(buf.load(array)?, &values);
    Ok(())
}

#[test]
fn allocate_array_not_needing_padding() -> Result<()> {
    #[derive(Debug, PartialEq, ZeroCopy)]
    #[zero_copy(crate)]
    #[repr(C)]
    struct Element {
        first: u32,
        second: u32,
        third: u32,
    }

    const _: () = assert!(!Element::PADDED);

    let mut buf = OwnedBuf::new();

    let array = buf.store_uninit::<[Element; 10]>()?;

    let values = array::from_fn(|index| Element {
        first: index as u32,
        second: 0x01020304u32,
        third: 0x05060708u32,
    });

    buf.load_uninit_mut(array)?.write(&values);
    let array = array.assume_init();

    assert_eq!(buf.load(array)?, &values);
    Ok(())
}

#[test]
fn test_unaligned_write() -> Result<()> {
    #[derive(ZeroCopy)]
    #[repr(C)]
    #[zero_copy(crate)]
    struct Custom {
        string: Ref<str>,
    }

    let mut buf = OwnedBuf::with_capacity_and_alignment::<u8>(128)?;
    buf.extend_from_slice(&[1])?;

    let reference: Ref<PackedMaybeUninit<Custom>> = buf.store_uninit::<Custom>()?;

    let string = buf.store_unsized("Hello World!")?;

    buf.load_uninit_mut(reference)?.write(&Custom { string });

    let reference = reference.assume_init();

    assert_eq!(reference.offset(), 4);

    buf.align_in_place()?;

    let custom = buf.load(reference)?;
    assert_eq!(buf.load(custom.string)?, "Hello World!");
    Ok(())
}

#[test]
fn inner_padding() -> Result<()> {
    #[derive(Debug, PartialEq, Clone, Copy, ZeroCopy)]
    #[repr(C, align(8))]
    #[zero_copy(crate)]
    struct Inner {
        field: u8,
    }

    #[derive(Debug, PartialEq, Clone, Copy, ZeroCopy)]
    #[repr(C, align(16))]
    #[zero_copy(crate)]
    struct Inner2 {
        field: u32,
    }

    #[derive(ZeroCopy)]
    #[repr(C)]
    #[zero_copy(crate)]
    struct Custom {
        inner: Inner,
        inner2: Inner2,
    }

    const _: () = assert!(Custom::PADDED);
    const _: () = assert!(Inner::PADDED);
    const _: () = assert!(Inner2::PADDED);

    let inner = Inner { field: 10 };
    let inner2 = Inner2 { field: 20 };
    let custom = Custom { inner, inner2 };

    let mut buf = OwnedBuf::with_capacity_and_alignment::<u8>(128)?;
    buf.extend_from_slice(&[1])?;

    let reference: Ref<PackedMaybeUninit<Custom>> = buf.store_uninit::<Custom>()?;

    buf.load_uninit_mut(reference)?.write(&custom);

    let reference = reference.assume_init();

    assert_eq!(reference.offset(), 16);

    buf.align_in_place()?;

    let custom = buf.load(reference)?;
    assert_eq!(&custom.inner, &inner);
    assert_eq!(&custom.inner2, &inner2);
    Ok(())
}

#[test]
fn test_packing() {
    #[derive(ZeroCopy)]
    #[repr(C, packed)]
    #[zero_copy(crate)]
    struct Packed {
        inner: u8,
        inner2: u32,
    }

    const _: () = assert!(!Packed::PADDED);

    #[derive(ZeroCopy)]
    #[repr(C, packed(1))]
    #[zero_copy(crate)]
    struct Packed1 {
        inner: u8,
        inner2: u32,
    }

    const _: () = assert!(!Packed1::PADDED);
}

/// A reference whose start is in bounds but whose end is not must be rejected,
/// since writing through it would store past the end of the slice.
#[test]
fn slice_mut_load_uninit_mut_rejects_value_past_end() {
    let mut bytes = [0u8; 4];
    let mut buf = SliceMut::new(&mut bytes);

    let reference = Ref::<PackedMaybeUninit<u64>>::new(0u32);
    assert!(buf.load_uninit_mut(reference).is_err());
}

/// A reference taken from a larger, foreign buffer must not allow writing past
/// the end of a smaller one.
#[test]
fn slice_mut_load_uninit_mut_rejects_foreign_ref() -> Result<()> {
    let mut large = [0u8; 64];
    let mut large = SliceMut::new(&mut large);
    large.store(&0u32)?;
    let foreign = large.store_uninit::<[u32; 2]>()?;
    assert_eq!(foreign.offset(), 4);

    // The start of the foreign reference is in bounds of the smaller buffer,
    // but its end is past the end of the underlying slice.
    let mut small = [0u8; 8];
    let mut small = SliceMut::new(&mut small);
    small.store(&0u32)?;

    assert!(small.load_uninit_mut(foreign).is_err());
    Ok(())
}

/// A value which exactly fits the initialized region is still accepted.
#[test]
fn slice_mut_load_uninit_mut_exact_fit() -> Result<()> {
    let mut bytes = [0u8; 8];

    {
        let mut buf = SliceMut::new(&mut bytes);
        let reference = buf.store_uninit::<u64>()?;
        buf.load_uninit_mut(reference)?
            .write(&0x0102030405060708u64);
    }

    assert_eq!(bytes, 0x0102030405060708u64.to_ne_bytes());
    Ok(())
}