#![allow(unsafe_code)]
pub mod mmap;
use core::marker::PhantomData;
use bincode_next::config::{Configuration, Fixint, LittleEndian, NoLimit};
use bincode_next::de::read::BorrowReader;
use bincode_next::de::{BorrowDecode, BorrowDecoder, Decode};
use bincode_next::enc::write::Writer;
use bincode_next::enc::{Encode, Encoder};
use bincode_next::error::{DecodeError, EncodeError};
pub use self::mmap::MmapBuffer;
extern crate alloc;
use alloc::boxed::Box;
use alloc::vec::Vec;
type ZeroCopyConfig = Configuration<LittleEndian, Fixint, NoLimit>;
#[must_use]
pub const fn zerocopy_config() -> ZeroCopyConfig {
bincode_next::config::standard()
.with_fixed_int_encoding()
.with_little_endian()
.with_no_limit()
}
pub unsafe trait Pod: Copy + 'static {}
unsafe impl Pod for u8 {}
unsafe impl Pod for u16 {}
unsafe impl Pod for u32 {}
unsafe impl Pod for u64 {}
unsafe impl Pod for i8 {}
unsafe impl Pod for i16 {}
unsafe impl Pod for i32 {}
unsafe impl Pod for i64 {}
unsafe impl Pod for f32 {}
unsafe impl Pod for f64 {}
pub struct AlignedBytes {
storage: Box<[u64]>,
len: usize,
}
pub const fn verify_pod_layout<T: Pod>() {
assert!(
core::mem::align_of::<T>() <= 8,
"Pod type alignment exceeds 8 bytes — not safe for zerocopy wire format"
);
assert!(
core::mem::size_of::<T>() > 0,
"Pod type must not be zero-sized"
);
}
impl AlignedBytes {
#[must_use]
pub fn from_slice(bytes: &[u8]) -> Self {
let n_u64 = bytes.len().div_ceil(core::mem::size_of::<u64>());
let mut storage: Vec<u64> = alloc::vec![0u64; n_u64];
unsafe {
core::ptr::copy_nonoverlapping(
bytes.as_ptr(),
storage.as_mut_ptr().cast::<u8>(),
bytes.len(),
);
}
Self {
storage: storage.into_boxed_slice(),
len: bytes.len(),
}
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.storage.as_ptr().cast::<u8>(), self.len) }
}
#[must_use]
pub fn from_vec_if_aligned(v: Vec<u8>) -> Option<Self> {
let ptr = v.as_ptr() as usize;
if ptr & 7 != 0 {
return None; }
let _len = v.len();
drop(v);
None }
#[must_use]
pub fn from_vec(v: Vec<u8>) -> Self {
Self::from_slice(&v)
}
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
pub struct AlignedBytesWriter {
storage: Vec<u64>,
byte_len: usize,
}
impl AlignedBytesWriter {
#[must_use]
pub fn with_capacity(cap: usize) -> Self {
let u64_cap = cap.div_ceil(core::mem::size_of::<u64>());
Self {
storage: Vec::with_capacity(u64_cap),
byte_len: 0,
}
}
#[must_use]
pub fn finish(self) -> AlignedBytes {
AlignedBytes {
storage: self.storage.into_boxed_slice(),
len: self.byte_len,
}
}
}
impl Writer for AlignedBytesWriter {
fn write(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
let needed_bytes = self.byte_len + bytes.len();
let needed_u64s = needed_bytes.div_ceil(core::mem::size_of::<u64>());
if needed_u64s > self.storage.len() {
self.storage.resize(needed_u64s, 0u64);
}
unsafe {
core::ptr::copy_nonoverlapping(
bytes.as_ptr(),
self.storage.as_mut_ptr().cast::<u8>().add(self.byte_len),
bytes.len(),
);
}
self.byte_len += bytes.len();
Ok(())
}
}
pub fn encode_zerocopy_direct<E: Encode>(value: E) -> Result<AlignedBytes, EncodeError> {
let mut writer = AlignedBytesWriter::with_capacity(256);
bincode_next::encode_into_writer(value, &mut writer, zerocopy_config())?;
Ok(writer.finish())
}
pub fn encode_zerocopy<E: Encode>(value: E) -> Result<AlignedBytes, EncodeError> {
let vec = bincode_next::encode_to_vec(value, zerocopy_config())?;
Ok(AlignedBytes::from_slice(&vec))
}
pub fn decode_zerocopy<'a, T>(bytes: &'a AlignedBytes) -> Result<T, DecodeError>
where
T: BorrowDecode<'a, ()>,
{
let (value, _read) =
bincode_next::borrow_decode_from_slice(bytes.as_bytes(), zerocopy_config())?;
Ok(value)
}
pub fn decode_zerocopy_raw<'a, T>(bytes: &'a [u8]) -> Result<T, DecodeError>
where
T: BorrowDecode<'a, ()>,
{
let (value, _read) = bincode_next::borrow_decode_from_slice(bytes, zerocopy_config())?;
Ok(value)
}
#[derive(Debug, Clone, Copy)]
pub struct BorrowedSlice<'a, T: Pod> {
inner: &'a [T],
}
impl<'a, T: Pod> BorrowedSlice<'a, T> {
#[must_use]
pub const fn new(slice: &'a [T]) -> Self {
Self { inner: slice }
}
#[must_use]
pub const fn as_slice(&self) -> &'a [T] {
self.inner
}
#[must_use]
pub const fn len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
impl<'a, T: Pod> From<&'a [T]> for BorrowedSlice<'a, T> {
fn from(s: &'a [T]) -> Self {
Self::new(s)
}
}
impl<T: Pod> Encode for BorrowedSlice<'_, T> {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
let len = self.inner.len() as u64;
len.encode(encoder)?;
let byte_len = self
.inner
.len()
.checked_mul(core::mem::size_of::<T>())
.ok_or_else(|| {
EncodeError::OtherString(alloc::string::String::from(
"BorrowedSlice: byte length overflowed usize",
))
})?;
let bytes: &[u8] =
unsafe { core::slice::from_raw_parts(self.inner.as_ptr().cast::<u8>(), byte_len) };
encoder.writer().write(bytes)
}
}
#[doc(hidden)]
#[cold]
#[track_caller]
#[inline(never)]
const fn decode_error_misaligned() -> DecodeError {
DecodeError::Other("BorrowedSlice: misaligned input buffer")
}
impl<'de, Context, T: Pod> BorrowDecode<'de, Context> for BorrowedSlice<'de, T> {
fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>(
decoder: &mut D,
) -> Result<Self, DecodeError> {
let len = u64::decode(decoder)?;
let len_usize: usize = len
.try_into()
.map_err(|_| DecodeError::OutsideUsizeRange(len))?;
let byte_len = len_usize
.checked_mul(core::mem::size_of::<T>())
.ok_or(DecodeError::OutsideUsizeRange(len))?;
decoder.claim_bytes_read(byte_len)?;
let bytes: &'de [u8] = decoder.borrow_reader().take_bytes(byte_len)?;
let align = core::mem::align_of::<T>();
if (bytes.as_ptr() as usize) & (align - 1) != 0 {
return Err(decode_error_misaligned());
}
let inner: &'de [T] =
unsafe { core::slice::from_raw_parts(bytes.as_ptr().cast::<T>(), len_usize) };
Ok(Self { inner })
}
}
#[derive(Debug, Clone, Copy)]
pub struct BorrowedArena<'a, T: Pod> {
nodes: BorrowedSlice<'a, T>,
_phantom: PhantomData<&'a T>,
}
impl<'a, T: Pod> BorrowedArena<'a, T> {
#[must_use]
pub const fn new(nodes: BorrowedSlice<'a, T>) -> Self {
Self {
nodes,
_phantom: PhantomData,
}
}
#[must_use]
pub const fn from_slice(slice: &'a [T]) -> Self {
Self::new(BorrowedSlice::new(slice))
}
#[must_use]
pub fn get(&self, i: usize) -> Option<&'a T> {
self.nodes.as_slice().get(i)
}
#[must_use]
pub const fn as_slice(&self) -> &'a [T] {
self.nodes.as_slice()
}
#[must_use]
pub const fn len(&self) -> usize {
self.nodes.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
}
impl<T: Pod> Encode for BorrowedArena<'_, T> {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
self.nodes.encode(encoder)
}
}
impl<'de, Context, T: Pod> BorrowDecode<'de, Context> for BorrowedArena<'de, T> {
fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>(
decoder: &mut D,
) -> Result<Self, DecodeError> {
let nodes = BorrowedSlice::<T>::borrow_decode(decoder)?;
Ok(Self::new(nodes))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verify_pod_layout_primitives() {
verify_pod_layout::<u8>();
verify_pod_layout::<u32>();
verify_pod_layout::<u64>();
verify_pod_layout::<f64>();
verify_pod_layout::<i64>();
}
#[test]
fn encode_zerocopy_direct_matches_encode_zerocopy() {
let data: Vec<u32> = (0u32..64).collect();
let view = BorrowedSlice::new(data.as_slice());
let buf_indirect = encode_zerocopy(view).expect("indirect encode");
let buf_direct = encode_zerocopy_direct(view).expect("direct encode");
let decoded_indirect: BorrowedSlice<'_, u32> =
decode_zerocopy(&buf_indirect).expect("decode indirect");
let decoded_direct: BorrowedSlice<'_, u32> =
decode_zerocopy(&buf_direct).expect("decode direct");
assert_eq!(decoded_indirect.as_slice(), decoded_direct.as_slice());
assert_eq!(decoded_direct.as_slice(), data.as_slice());
assert_eq!(buf_direct.as_bytes().as_ptr() as usize & 7, 0);
}
#[test]
fn aligned_bytes_alignment_holds() {
let buf = AlignedBytes::from_slice(&[1, 2, 3, 4, 5]);
let ptr = buf.as_bytes().as_ptr() as usize;
assert_eq!(ptr & 0b111, 0, "AlignedBytes start must be 8-byte aligned");
assert_eq!(buf.as_bytes(), &[1, 2, 3, 4, 5]);
assert_eq!(buf.len(), 5);
}
#[test]
fn borrowed_slice_roundtrips_u32() {
let data: Vec<u32> = (0u32..1024).collect();
let view = BorrowedSlice::new(data.as_slice());
let buf = encode_zerocopy(view).expect("encode");
let decoded: BorrowedSlice<'_, u32> = decode_zerocopy(&buf).expect("decode");
assert_eq!(decoded.len(), data.len());
assert_eq!(decoded.as_slice(), data.as_slice());
}
#[test]
fn borrowed_arena_roundtrips_f64() {
let data: Vec<f64> = (0..256).map(|i| (i as f64) * 1.5).collect();
let arena = BorrowedArena::from_slice(data.as_slice());
let buf = encode_zerocopy(arena).expect("encode");
let decoded: BorrowedArena<'_, f64> = decode_zerocopy(&buf).expect("decode");
assert_eq!(decoded.len(), 256);
assert_eq!(decoded.as_slice(), data.as_slice());
}
#[test]
fn empty_slice_roundtrips() {
let data: Vec<u64> = Vec::new();
let view = BorrowedSlice::new(data.as_slice());
let buf = encode_zerocopy(view).expect("encode");
let decoded: BorrowedSlice<'_, u64> = decode_zerocopy(&buf).expect("decode");
assert!(decoded.is_empty());
}
#[test]
fn decoded_slice_points_into_buffer() {
let data: Vec<u32> = (0u32..64).collect();
let view = BorrowedSlice::new(data.as_slice());
let buf = encode_zerocopy(view).expect("encode");
let decoded: BorrowedSlice<'_, u32> = decode_zerocopy(&buf).expect("decode");
let buf_start = buf.as_bytes().as_ptr() as usize;
let buf_end = buf_start + buf.as_bytes().len();
let decoded_start = decoded.as_slice().as_ptr() as usize;
assert!(
decoded_start >= buf_start && decoded_start < buf_end,
"decoded slice does not point into the input buffer (zero-copy violated)"
);
}
}