#![no_std]
extern crate alloc;
use core::{mem, ptr, usize};
use core::borrow::{Borrow, BorrowMut};
use core::marker::PhantomData;
use alloc::alloc::{alloc, dealloc, handle_alloc_error, Layout};
use dioptre::{Fields, Field};
pub use soak_derive::Columns;
pub unsafe trait Columns: Fields {
type Pointers: BorrowMut<[ptr::NonNull<u8>]>;
fn dangling() -> Self::Pointers;
}
pub struct RawTable<T: Columns> {
pointers: T::Pointers,
capacity: usize,
_marker: PhantomData<T>,
}
impl<T: Columns> Default for RawTable<T> {
fn default() -> Self {
let pointers = T::dangling();
let capacity = if mem::size_of::<T>() == 0 { usize::MAX } else { 0 };
RawTable { pointers, capacity, _marker: PhantomData }
}
}
impl<T: Columns> RawTable<T> {
pub fn with_capacity(capacity: usize) -> Self {
unsafe {
let align = T::ALIGNS.iter().cloned().max().unwrap_or(1);
let mask = align - 1;
let size = T::SIZES.iter().try_fold(0, move |sum, &size| {
let array_size = usize::checked_mul(capacity, size)?;
let aligned_size = usize::checked_add(array_size, mask)? & !mask;
Some(usize::checked_add(sum, aligned_size)?)
}).expect("capacity overflow");
let layout = Layout::from_size_align_unchecked(size, align);
let data = if size == 0 { align as *mut u8 } else { alloc(layout) };
if data == ptr::null_mut() {
handle_alloc_error(layout);
}
let mut pointers = T::dangling();
let mut offset = 0;
let dst = pointers.borrow_mut().iter_mut();
for (pointer, size) in Iterator::zip(dst, T::SIZES.iter()) {
*pointer = ptr::NonNull::new_unchecked(data.add(offset));
offset += (capacity * size + mask) & !mask;
}
let capacity = if mem::size_of::<T>() == 0 { usize::MAX } else { capacity };
RawTable { pointers, capacity, _marker: PhantomData }
}
}
pub fn ptr<F>(&mut self, field: Field<T, F>) -> *mut F {
self.pointers.borrow()[field.index()].as_ptr() as *mut F
}
pub fn capacity(&self) -> usize { self.capacity }
pub fn reserve_exact(&mut self, used: usize, extra: usize) {
unsafe {
if self.capacity - used >= extra {
return;
}
let capacity = usize::checked_add(used, extra).expect("capacity overflow");
let table = Self::with_capacity(capacity);
let src = self.pointers.borrow().iter();
let dst = table.pointers.borrow().iter();
for ((src, dst), size) in Iterator::zip(Iterator::zip(src, dst), T::SIZES.iter()) {
ptr::copy_nonoverlapping(src.as_ptr(), dst.as_ptr(), capacity * size);
}
mem::replace(self, table);
}
}
}
impl<T: Columns> Drop for RawTable<T> {
fn drop(&mut self) {
unsafe {
let align = *T::ALIGNS.iter().max().unwrap_or(&1);
let mask = align - 1;
let capacity = self.capacity;
let size = T::SIZES.iter().map(move |&size| (capacity * size + mask) & !mask).sum();
let layout = Layout::from_size_align_unchecked(size, align);
if size > 0 { dealloc(self.pointers.borrow()[0].as_ptr(), layout); }
}
}
}