use std::mem::MaybeUninit;
use crate::index::{DataIndex, MAX_DATA_CAPACITY, MAX_DATA_INDEX};
use crate::util::{num_assert_leq, num_assert_lt};
use crate::version::VersionSlot;
const FREE_BIT: u32 = 1 << 31;
const FREE_LIST_END: u32 = (FREE_BIT - 1) | FREE_BIT;
#[inline(always)]
pub(crate) fn populate_free_list(
free_list_start: DataIndex, slots: &mut [MaybeUninit<Slot>], ) -> SlotIndex {
num_assert_leq!(MAX_DATA_CAPACITY as usize, u32::MAX as usize);
if slots.len() > MAX_DATA_CAPACITY as usize {
panic!("capacity may not exceed {}", MAX_DATA_CAPACITY);
}
if slots.len() > 0 {
let start_index = free_list_start.get() as usize;
let last_index = slots.len() - 1;
debug_assert!(start_index <= slots.len());
for i in start_index..(slots.len() - 1) {
unsafe {
let index = DataIndex::new_unchecked(i.wrapping_add(1) as u32);
let slot = Slot::new_free(SlotIndex::new_free(index));
slots.get_unchecked_mut(i).write(slot);
}
}
let last_slot = Slot::new_free(SlotIndex::new_free_end());
unsafe { slots.get_unchecked_mut(last_index).write(last_slot) };
SlotIndex::new_free(free_list_start)
} else {
SlotIndex::new_free_end()
}
}
#[derive(Clone, Copy)]
pub(crate) struct SlotIndex(u32);
impl SlotIndex {
#[inline(always)]
pub(crate) fn new_free_end() -> Self {
Self(FREE_LIST_END)
}
pub(crate) fn new_free(next_free: DataIndex) -> Self {
Self(FREE_BIT | next_free.get())
}
#[inline(always)]
pub(crate) fn is_free(&self) -> bool {
(FREE_BIT & self.0) != 0
}
#[inline(always)]
pub(crate) fn is_free_list_end(&self) -> bool {
self.0 == FREE_LIST_END
}
#[inline(always)]
pub(crate) fn get_data(&self) -> Option<DataIndex> {
debug_assert!(self.is_free() == false);
DataIndex::new_u32(self.0)
}
#[inline(always)]
pub(crate) fn get_next_free(&self) -> Option<DataIndex> {
debug_assert!(self.is_free());
DataIndex::new_u32(!FREE_BIT & self.0)
}
#[inline(always)]
pub(crate) fn assign_data(&mut self, index_data: DataIndex) {
self.0 = index_data.get();
}
}
#[derive(Clone, Copy)]
pub(crate) struct Slot {
index: SlotIndex,
version: VersionSlot,
}
impl Slot {
#[inline(always)]
pub(crate) fn new_free(next_free: SlotIndex) -> Self {
num_assert_lt!(MAX_DATA_INDEX as usize, FREE_BIT as usize);
Self {
index: next_free,
version: VersionSlot::start(),
}
}
#[inline(always)]
pub(crate) fn index(&self) -> SlotIndex {
self.index
}
#[inline(always)]
pub(crate) fn is_free(&self) -> bool {
self.index.is_free()
}
#[inline(always)]
pub(crate) fn version(&self) -> VersionSlot {
self.version
}
#[inline(always)]
pub(crate) fn assign(&mut self, index_data: DataIndex) {
self.index.assign_data(index_data);
}
#[inline(always)]
pub(crate) fn release(&mut self, index_next_free: SlotIndex) {
debug_assert!(self.is_free() == false);
self.index = index_next_free;
self.version = self.version.next();
}
}
#[test]
fn verify_free_list_end_is_invalid_data_index() {
assert!(DataIndex::new_u32(!FREE_BIT & FREE_LIST_END).is_none());
}