Replace Page data Vec with boxed fixed array

This commit is contained in:
Lukas Wirth 2024-12-13 15:47:09 +01:00
parent 5cd2b63ef0
commit a861b14abe

View file

@ -1,7 +1,9 @@
use std::{
any::{Any, TypeId},
cell::UnsafeCell,
mem::MaybeUninit,
panic::RefUnwindSafe,
ptr, slice,
};
use append_only_vec::AppendOnlyVec;
@ -60,9 +62,8 @@ pub(crate) struct Page<T: Slot> {
/// that the data is initialized).
allocation_lock: Mutex<()>,
/// Vector with data. This is always created with the capacity/length of `PAGE_LEN`
/// and uninitialized data. As we initialize new entries, we increment `allocated`.
data: Vec<UnsafeCell<T>>,
/// The potentially uninitialized data of this page. As we initialize new entries, we increment `allocated`.
data: Box<[UnsafeCell<MaybeUninit<T>>; PAGE_LEN]>,
}
pub(crate) trait Slot: Any + Send + Sync {
@ -169,15 +170,11 @@ impl Table {
impl<T: Slot> Page<T> {
#[allow(clippy::uninit_vec)]
fn new(ingredient: IngredientIndex) -> Self {
let mut data = Vec::with_capacity(PAGE_LEN);
unsafe {
data.set_len(PAGE_LEN);
}
Self {
ingredient,
allocated: Default::default(),
allocation_lock: Default::default(),
data,
data: Box::new([const { UnsafeCell::new(MaybeUninit::uninit()) }; PAGE_LEN]),
}
}
@ -196,7 +193,7 @@ impl<T: Slot> Page<T> {
/// If slot is out of bounds
pub(crate) fn get(&self, slot: SlotIndex) -> &T {
self.check_bounds(slot);
unsafe { &*self.data[slot.0].get() }
unsafe { (*self.data[slot.0].get()).assume_init_ref() }
}
/// Returns a raw pointer to the given slot.
@ -211,7 +208,7 @@ impl<T: Slot> Page<T> {
/// properly with calls to [`get`](`Self::get`) and [`get_mut`](`Self::get_mut`).
pub(crate) fn get_raw(&self, slot: SlotIndex) -> *mut T {
self.check_bounds(slot);
self.data[slot.0].get()
self.data[slot.0].get().cast()
}
pub(crate) fn allocate<V>(&self, page: PageIndex, value: V) -> Result<Id, V>
@ -226,7 +223,7 @@ impl<T: Slot> Page<T> {
// Initialize entry `index`
let data = &self.data[index];
unsafe { std::ptr::write(data.get(), value()) };
unsafe { (*data.get()).write(value()) };
// Update the length (this must be done after initialization!)
self.allocated.store(index + 1);
@ -252,14 +249,13 @@ impl<T: Slot> TablePage for Page<T> {
impl<T: Slot> Drop for Page<T> {
fn drop(&mut self) {
// Free `self.data` and the data within: to do this, we swap it out with an empty vector
// and then convert it from a `Vec<UnsafeCell<T>>` with partially uninitialized values
// to a `Vec<T>` with the correct length. This way the `Vec` drop impl can do its job.
let mut data = std::mem::take(&mut self.data);
// Execute destructors for all initialized elements
let len = self.allocated.load();
// SAFETY: self.data is initialized for T's up to len
unsafe {
data.set_len(len);
drop(std::mem::transmute::<Vec<UnsafeCell<T>>, Vec<T>>(data));
// FIXME: Should be ptr::from_raw_parts_mut but that is unstable
let to_drop = slice::from_raw_parts_mut(self.data.as_mut_ptr().cast::<T>(), len);
ptr::drop_in_place(to_drop)
}
}
}