use std::mem::MaybeUninit;
use std::{fmt, io};
use crate::mdb::ffi;
pub struct ReservedSpace<'a> {
bytes: &'a mut [MaybeUninit<u8>],
written: usize,
write_head: usize,
}
impl ReservedSpace<'_> {
pub(crate) unsafe fn from_val<'a>(val: ffi::MDB_val) -> ReservedSpace<'a> {
let len = val.mv_size;
let ptr = val.mv_data;
ReservedSpace {
bytes: std::slice::from_raw_parts_mut(ptr.cast(), len),
written: 0,
write_head: 0,
}
}
#[inline]
pub fn size(&self) -> usize {
self.bytes.len()
}
#[inline]
pub fn remaining(&self) -> usize {
self.bytes.len() - self.write_head
}
#[inline]
pub fn written_mut(&mut self) -> &mut [u8] {
let ptr = self.bytes.as_mut_ptr();
let len = self.written;
unsafe { std::slice::from_raw_parts_mut(ptr.cast(), len) }
}
#[inline]
pub fn fill_zeroes(&mut self) {
self.bytes[self.write_head..].fill(MaybeUninit::new(0));
self.written = self.bytes.len();
self.write_head = self.bytes.len();
}
#[inline]
pub fn as_uninit_mut(&mut self) -> &mut [MaybeUninit<u8>] {
self.bytes
}
#[inline]
pub unsafe fn assume_written(&mut self, len: usize) {
debug_assert!(len <= self.bytes.len());
self.written = len;
self.write_head = len;
}
}
impl io::Write for ReservedSpace<'_> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.write_all(buf)?;
Ok(buf.len())
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
let remaining = unsafe { self.bytes.get_unchecked_mut(self.write_head..) };
if buf.len() > remaining.len() {
return Err(io::Error::from(io::ErrorKind::WriteZero));
}
unsafe {
let buf_uninit = std::slice::from_raw_parts(buf.as_ptr().cast(), buf.len());
remaining.as_mut_ptr().copy_from_nonoverlapping(buf_uninit.as_ptr(), buf.len());
}
self.write_head += buf.len();
self.written = usize::max(self.written, self.write_head);
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl io::Seek for ReservedSpace<'_> {
#[inline]
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
let (base, offset) = match pos {
io::SeekFrom::Start(start) => (start, 0),
io::SeekFrom::End(offset) => (self.written as u64, offset),
io::SeekFrom::Current(offset) => (self.write_head as u64, offset),
};
let Some(new_pos) = base.checked_add_signed(offset) else {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"cannot seek before start of reserved space",
));
};
if new_pos > self.written as u64 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"cannot seek past end of reserved space",
));
}
self.write_head = new_pos as usize;
Ok(new_pos)
}
#[inline]
fn rewind(&mut self) -> io::Result<()> {
self.write_head = 0;
Ok(())
}
#[inline]
fn stream_position(&mut self) -> io::Result<u64> {
Ok(self.write_head as u64)
}
}
impl fmt::Debug for ReservedSpace<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ReservedSpace").finish()
}
}