use std::mem::MaybeUninit;
use crate::errors::PrimitiveError;
pub mod bincode_io;
pub mod containers;
pub mod prims;
pub unsafe trait InPlaceCodec: Sized {
const ENCODED_SIZE: usize;
fn write_le_bytes(&self, out: &mut [MaybeUninit<u8>]);
fn read_le_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError>;
const PACK: usize = 1;
const PACK_BYTES: usize = Self::PACK * Self::ENCODED_SIZE;
fn write_pack(items: &[Self], out: &mut [MaybeUninit<u8>]) {
for (chunk, item) in out.chunks_exact_mut(Self::ENCODED_SIZE).zip(items) {
item.write_le_bytes(chunk);
}
}
fn read_pack(bytes: &[u8], out: &mut [MaybeUninit<Self>]) -> Result<(), PrimitiveError> {
for (chunk, slot) in bytes.chunks_exact(Self::ENCODED_SIZE).zip(out.iter_mut()) {
slot.write(Self::read_le_bytes(chunk)?);
}
Ok(())
}
fn to_inplace_bytes(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(Self::ENCODED_SIZE);
let spare = &mut out.spare_capacity_mut()[..Self::ENCODED_SIZE];
self.write_le_bytes(spare);
unsafe { out.set_len(Self::ENCODED_SIZE) };
out
}
fn from_inplace_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError> {
if bytes.len() != Self::ENCODED_SIZE {
return Err(PrimitiveError::InvalidSize(Self::ENCODED_SIZE, bytes.len()));
}
Self::read_le_bytes(bytes)
}
}
const fn tail_is_unpacked<T: InPlaceCodec>(rem: usize) -> bool {
rem * T::ENCODED_SIZE < T::PACK_BYTES
}
const fn tail_size<T: InPlaceCodec>(rem: usize) -> usize {
if rem == 0 {
0
} else if tail_is_unpacked::<T>(rem) {
rem * T::ENCODED_SIZE
} else {
T::PACK_BYTES
}
}
pub(crate) const fn packed_size<T: InPlaceCodec>(len: usize) -> usize {
(len / T::PACK) * T::PACK_BYTES + tail_size::<T>(len % T::PACK)
}
pub(crate) fn write_packed_le_bytes<T: InPlaceCodec>(items: &[T], out: &mut [MaybeUninit<u8>]) {
if T::PACK == 1 {
for (chunk, elem) in out.chunks_exact_mut(T::ENCODED_SIZE).zip(items) {
elem.write_le_bytes(chunk);
}
return;
}
let n_full = items.len() / T::PACK;
let rem = items.len() % T::PACK;
let (packed, last) = out.split_at_mut(n_full * T::PACK_BYTES);
for (chunk, group) in packed
.chunks_exact_mut(T::PACK_BYTES)
.zip(items.chunks_exact(T::PACK))
{
T::write_pack(group, chunk);
}
if rem == 0 {
return;
}
let tail_items = &items[n_full * T::PACK..];
if tail_is_unpacked::<T>(rem) {
for (chunk, item) in last.chunks_exact_mut(T::ENCODED_SIZE).zip(tail_items) {
item.write_le_bytes(chunk);
}
return;
}
let mut padded: Vec<T> = tail_items
.iter()
.map(|item| unsafe { std::ptr::read(item) })
.collect();
padded.resize_with(T::PACK, || unsafe { std::ptr::read(&tail_items[rem - 1]) });
T::write_pack(&padded, last);
}
pub(crate) fn read_packed_le_bytes<T: InPlaceCodec>(
bytes: &[u8],
data: &mut [MaybeUninit<T>],
) -> Result<(), PrimitiveError> {
let mut guard = SliceDropGuard::<T>::new(data.as_mut_ptr());
if T::PACK == 1 {
for (chunk, slot) in bytes.chunks_exact(T::ENCODED_SIZE).zip(data.iter_mut()) {
slot.write(T::read_le_bytes(chunk)?);
guard.inc_len();
}
} else {
let n_full = data.len() / T::PACK;
let rem = data.len() % T::PACK;
let (packed, last) = bytes.split_at(n_full * T::PACK_BYTES);
let mut written = 0usize;
for chunk in packed.chunks_exact(T::PACK_BYTES) {
T::read_pack(chunk, &mut data[written..written + T::PACK])?;
written += T::PACK;
guard.add_len(T::PACK);
}
if rem > 0 {
if tail_is_unpacked::<T>(rem) {
for chunk in last.chunks_exact(T::ENCODED_SIZE) {
data[written].write(T::read_le_bytes(chunk)?);
written += 1;
guard.inc_len();
}
} else {
let mut padded = Box::<[T]>::new_uninit_slice(T::PACK);
T::read_pack(last, &mut padded)?;
let padded = unsafe { padded.assume_init() };
for item in Vec::from(padded).into_iter().take(rem) {
data[written].write(item);
written += 1;
guard.inc_len();
}
}
}
}
std::mem::forget(guard);
Ok(())
}
struct SliceDropGuard<T> {
ptr: *mut MaybeUninit<T>,
initialized_len: usize,
}
impl<T> SliceDropGuard<T> {
fn new(ptr: *mut MaybeUninit<T>) -> Self {
Self {
ptr,
initialized_len: 0,
}
}
#[inline(always)]
#[allow(clippy::arithmetic_side_effects)]
fn inc_len(&mut self) {
self.initialized_len += 1;
}
#[inline(always)]
#[allow(clippy::arithmetic_side_effects)]
fn add_len(&mut self, n: usize) {
self.initialized_len += n;
}
}
impl<T> Drop for SliceDropGuard<T> {
#[inline(always)]
fn drop(&mut self) {
unsafe {
std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(
self.ptr.cast::<T>(),
self.initialized_len,
));
}
}
}