use super::entry::{Entry, State};
use core::mem::{self, MaybeUninit};
pub const HEADER_SIZE: usize = mem::size_of::<Entry>();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ValidatedOffset(usize);
#[repr(align(4))]
pub struct Buffer<const N: usize>([MaybeUninit<u8>; N]);
impl<const N: usize> Buffer<N> {
pub const fn new() -> Self {
assert!(N >= HEADER_SIZE, "buffer too small, use N >= 4");
assert!(N % HEADER_SIZE == 0, "memory size has to be divisible by 4");
let mut buffer = [MaybeUninit::uninit(); N];
buffer[0] = MaybeUninit::new(0x00);
buffer[1] = MaybeUninit::new(0x00);
buffer[2] = MaybeUninit::new(0x00);
buffer[3] = MaybeUninit::new(0x00);
Self(buffer)
}
pub fn ensure_initialization(&mut self) {
let buffer = &mut self.0;
let not_yet_initialized = buffer
.iter_mut()
.take(HEADER_SIZE)
.map(|byte| unsafe { byte.assume_init() })
.all(|byte| byte == 0x00);
if not_yet_initialized {
let remaining_size = N - HEADER_SIZE;
let initial_entry = Entry::free(remaining_size).as_raw();
buffer[0] = MaybeUninit::new(initial_entry[0]);
buffer[1] = MaybeUninit::new(initial_entry[1]);
buffer[2] = MaybeUninit::new(initial_entry[2]);
buffer[3] = MaybeUninit::new(initial_entry[3]);
}
}
fn at(&self, offset: usize) -> &MaybeUninit<Entry> {
assert!(offset % mem::align_of::<Entry>() == 0);
assert!(offset + HEADER_SIZE <= self.0.len());
unsafe {
let memory = &self.0[offset..offset + 4];
let memory = memory.as_ptr();
#[allow(clippy::cast_ptr_alignment)] &*(memory
.cast::<[MaybeUninit<u8>; 4]>()
.cast::<MaybeUninit<Entry>>())
}
}
fn at_mut(&mut self, offset: usize) -> &mut MaybeUninit<Entry> {
assert!(offset % mem::align_of::<Entry>() == 0);
assert!(offset + HEADER_SIZE <= self.0.len());
unsafe {
let memory = &mut self.0[offset..offset + 4];
let memory = memory.as_mut_ptr();
#[allow(clippy::cast_ptr_alignment)] &mut *(memory
.cast::<[MaybeUninit<u8>; 4]>()
.cast::<MaybeUninit<Entry>>())
}
}
pub const fn entries(&self) -> EntryIter<N> {
EntryIter::new(self)
}
pub fn memory_of(&self, offset: ValidatedOffset) -> &[MaybeUninit<u8>] {
let size = self[offset].size();
let offset = offset.0 + HEADER_SIZE;
&self.0[offset..offset + size]
}
pub fn memory_of_mut(&mut self, offset: ValidatedOffset) -> &mut [MaybeUninit<u8>] {
let size = self[offset].size();
let offset = offset.0 + HEADER_SIZE;
&mut self.0[offset..offset + size]
}
#[allow(clippy::needless_pass_by_ref_mut)] pub fn following_free_entry(&mut self, offset: ValidatedOffset) -> Option<Entry> {
let iter_starting_at_offset = EntryIter {
buffer: self,
offset: offset.0,
};
iter_starting_at_offset
.map(|offset| self[offset])
.nth(1)
.filter(|entry| entry.state() == State::Free)
}
pub fn mark_as_used(&mut self, offset: ValidatedOffset, size: usize) {
let old_size = self[offset].size();
debug_assert!(old_size >= size);
self[offset] = Entry::used(size);
if let Some(remaining_size) = (old_size - size).checked_sub(HEADER_SIZE) {
self.at_mut(offset.0 + size + HEADER_SIZE)
.write(Entry::free(remaining_size));
}
}
}
impl<const N: usize> core::ops::Index<ValidatedOffset> for Buffer<N> {
type Output = Entry;
fn index(&self, index: ValidatedOffset) -> &Self::Output {
unsafe { self.at(index.0).assume_init_ref() }
}
}
impl<const N: usize> core::ops::IndexMut<ValidatedOffset> for Buffer<N> {
fn index_mut(&mut self, index: ValidatedOffset) -> &mut Self::Output {
unsafe { self.at_mut(index.0).assume_init_mut() }
}
}
pub struct EntryIter<'buffer, const N: usize> {
buffer: &'buffer Buffer<N>,
offset: usize,
}
impl<'buffer, const N: usize> EntryIter<'buffer, N> {
const fn new(buffer: &'buffer Buffer<N>) -> Self {
Self { buffer, offset: 0 }
}
}
impl<'buffer, const N: usize> Iterator for EntryIter<'buffer, N> {
type Item = ValidatedOffset;
fn next(&mut self) -> Option<Self::Item> {
(self.offset + HEADER_SIZE < N).then(|| {
let offset = self.offset;
let entry = unsafe { self.buffer.at(offset).assume_init_ref() };
self.offset += entry.size() + HEADER_SIZE;
ValidatedOffset(offset)
})
}
}
#[cfg(test)]
mod tests {
use super::{Buffer, Entry, ValidatedOffset, HEADER_SIZE};
#[test]
fn validated_offset_debug() {
assert_eq!(format!("{:?}", ValidatedOffset(12)), "ValidatedOffset(12)");
}
#[test]
fn validated_offset_equality() {
assert_eq!(ValidatedOffset(12), ValidatedOffset(12));
assert_ne!(ValidatedOffset(12), ValidatedOffset(24));
assert_eq!(ValidatedOffset(12).clone(), ValidatedOffset(12));
assert_ne!(ValidatedOffset(12).clone(), ValidatedOffset(24));
}
#[test]
fn empty_allocator() {
let mut buffer = Buffer::<32>::new();
buffer.ensure_initialization();
let expected = Entry::free(32 - 4);
let actual = unsafe { buffer.at(0).assume_init() };
assert_eq!(expected, actual);
}
#[test]
fn header_size() {
assert_eq!(HEADER_SIZE, 4);
}
#[test]
#[should_panic(expected = "buffer too small")]
fn too_small_buffer() {
Buffer::<3>::new();
}
#[test]
#[should_panic(expected = "memory size has to be divisible by 4")]
fn invalid_buffer_size() {
Buffer::<13>::new();
}
#[test]
fn entry_iter() {
let mut buffer = Buffer::<32>::new();
buffer.ensure_initialization();
let mut iter = buffer.entries();
assert_eq!(iter.next(), Some(ValidatedOffset(0)));
assert_eq!(iter.next(), None);
let mut buffer = Buffer::<32>::new();
buffer.ensure_initialization();
buffer.at_mut(0).write(Entry::free(4));
buffer.at_mut(8).write(Entry::used(4));
buffer.at_mut(16).write(Entry::free(12));
let mut iter = buffer.entries();
assert_eq!(iter.next(), Some(ValidatedOffset(0)));
assert_eq!(iter.next(), Some(ValidatedOffset(8)));
assert_eq!(iter.next(), Some(ValidatedOffset(16)));
assert_eq!(iter.next(), None);
}
#[test]
fn indexing() {
let mut buffer = Buffer::<32>::new();
buffer.ensure_initialization();
buffer.at_mut(8).write(Entry::used(4));
assert_eq!(buffer[ValidatedOffset(8)], Entry::used(4));
buffer[ValidatedOffset(8)] = Entry::free(12);
assert_eq!(buffer[ValidatedOffset(8)], Entry::free(12));
}
#[test]
#[should_panic]
fn at_out_of_bounds() {
let mut buffer = Buffer::<32>::new();
buffer.ensure_initialization();
buffer.at(64); }
#[test]
#[should_panic]
fn at_mut_out_of_bounds() {
let mut buffer = Buffer::<32>::new();
buffer.ensure_initialization();
buffer.at_mut(64); }
#[test]
#[should_panic]
fn at_unaligned() {
let mut buffer = Buffer::<32>::new();
buffer.ensure_initialization();
buffer.at(2); }
#[test]
#[should_panic]
fn at_mut_unaligned() {
let mut buffer = Buffer::<32>::new();
buffer.ensure_initialization();
buffer.at_mut(2); }
#[test]
fn following_free_entry() {
let mut buffer = Buffer::<24>::new();
buffer.ensure_initialization();
buffer.at_mut(0).write(Entry::used(4));
buffer.at_mut(8).write(Entry::used(4));
buffer.at_mut(16).write(Entry::free(4));
assert_eq!(
buffer.following_free_entry(ValidatedOffset(8)),
Some(Entry::free(4))
);
assert_eq!(buffer.following_free_entry(ValidatedOffset(0)), None);
assert_eq!(buffer.following_free_entry(ValidatedOffset(16)), None);
}
#[test]
fn memory_of() {
use core::ptr;
let mut buffer = Buffer::<20>::new();
buffer.ensure_initialization();
buffer.at_mut(0).write(Entry::used(4));
let expected = &buffer.0[4..8];
let actual = buffer.memory_of(ValidatedOffset(0));
assert_eq!(ptr::addr_of!(expected[0]), ptr::addr_of!(actual[0]));
}
#[test]
fn mark_used_without_split() {
let mut buffer = Buffer::<24>::new();
buffer.ensure_initialization();
buffer.at_mut(0).write(Entry::used(4));
buffer.at_mut(8).write(Entry::free(4));
buffer.at_mut(16).write(Entry::used(4));
buffer.mark_as_used(ValidatedOffset(8), 4);
assert_eq!(buffer[ValidatedOffset(0)], Entry::used(4));
assert_eq!(buffer[ValidatedOffset(8)], Entry::used(4)); assert_eq!(buffer[ValidatedOffset(16)], Entry::used(4));
}
#[test]
fn mark_used_with_split() {
let mut buffer = Buffer::<32>::new();
buffer.ensure_initialization();
buffer.at_mut(0).write(Entry::used(4));
buffer.at_mut(8).write(Entry::free(20));
buffer.mark_as_used(ValidatedOffset(8), 4);
assert_eq!(buffer[ValidatedOffset(0)], Entry::used(4));
assert_eq!(buffer[ValidatedOffset(8)], Entry::used(4)); assert_eq!(buffer[ValidatedOffset(16)], Entry::free(12)); }
}