use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::slice::from_mut;
use crate::encode::EncodeError;
use crate::{Chunk, Encode, Slot, CHUNK_SIZE};
pub trait Encoder {
fn bytes_written(&self) -> usize;
fn reserve(&mut self, len: usize);
fn write(&mut self, bytes: &[u8]);
fn rewrite(&mut self, pos: usize, bytes: &[u8]);
#[doc(hidden)]
fn __internal_handle_count(&self) -> usize;
}
impl<T: Encoder> Encoder for &mut T {
fn bytes_written(&self) -> usize {
T::bytes_written(self)
}
fn reserve(&mut self, len: usize) {
T::reserve(self, len)
}
fn write(&mut self, bytes: &[u8]) {
T::write(self, bytes)
}
fn rewrite(&mut self, pos: usize, bytes: &[u8]) {
T::rewrite(self, pos, bytes)
}
fn __internal_handle_count(&self) -> usize {
T::__internal_handle_count(self)
}
}
impl Encoder for Vec<Chunk> {
fn bytes_written(&self) -> usize {
self.len() * CHUNK_SIZE
}
fn reserve(&mut self, len: usize) {
let count = len.div_ceil(CHUNK_SIZE);
self.reserve(count);
let ptr = unsafe { self.as_mut_ptr().add(self.len()) };
unsafe {
ptr.write_bytes(0, count);
}
unsafe {
self.set_len(self.len() + count);
}
}
fn write(&mut self, bytes: &[u8]) {
let count = bytes.len().div_ceil(CHUNK_SIZE);
self.reserve(count);
let ptr = unsafe { self.as_mut_ptr().add(self.len()).cast::<u8>() };
unsafe {
ptr.copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
}
let trailing = count * CHUNK_SIZE - bytes.len();
unsafe {
ptr.add(bytes.len()).write_bytes(0, trailing);
}
unsafe {
self.set_len(self.len() + count);
}
}
fn rewrite(&mut self, pos: usize, bytes: &[u8]) {
assert!(pos + bytes.len() <= self.bytes_written());
let ptr = unsafe { self.as_mut_ptr().cast::<u8>().add(pos) };
unsafe {
ptr.copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
}
}
fn __internal_handle_count(&self) -> usize {
0
}
}
pub trait EncoderExt {
fn preallocate<T>(&mut self, len: usize) -> Preallocated<'_, Self, T>;
fn encode_next_slice<T: Encode<Self>>(&mut self, values: &mut [T]) -> Result<(), EncodeError>;
fn encode_next<T: Encode<Self>>(&mut self, value: &mut T) -> Result<(), EncodeError>;
}
impl<E: Encoder + ?Sized> EncoderExt for E {
fn preallocate<T>(&mut self, len: usize) -> Preallocated<'_, Self, T> {
let pos = self.bytes_written();
let len_bytes = len * size_of::<T>();
self.reserve(len_bytes);
Preallocated { encoder: self, pos, end: pos + len_bytes, _phantom: PhantomData }
}
fn encode_next_slice<T: Encode<Self>>(&mut self, values: &mut [T]) -> Result<(), EncodeError> {
let mut slots = self.preallocate::<T::Encoded<'_>>(values.len());
let mut backing = MaybeUninit::<T::Encoded<'_>>::uninit();
for value in values {
let mut slot = Slot::new(&mut backing);
value.encode(slots.encoder, slot.as_mut())?;
slots.write_next(slot);
}
Ok(())
}
fn encode_next<T: Encode<Self>>(&mut self, value: &mut T) -> Result<(), EncodeError> {
self.encode_next_slice(from_mut(value))
}
}
pub struct Preallocated<'a, E: ?Sized, T> {
pub encoder: &'a mut E,
pos: usize,
end: usize,
_phantom: PhantomData<T>,
}
impl<E: Encoder + ?Sized, T> Preallocated<'_, E, T> {
pub fn write_next(&mut self, slot: Slot<'_, T>) {
assert_ne!(self.pos, self.end, "attemped to write more slots than preallocated",);
self.encoder.rewrite(self.pos, slot.as_bytes());
self.pos += size_of::<T>();
}
}