use std::ptr;
#[must_use]
pub const fn layout_size(capacity: u16, element_size: u32) -> usize {
let cap = capacity as usize;
let lookup = 2 * cap * size_of::<u16>();
let generation_size = cap * size_of::<u16>();
let before_vals = HEADER_SIZE + lookup + generation_size;
let pad = (8 - (before_vals % 8)) % 8;
let vals = cap * element_size as usize;
before_vals + pad + vals
}
#[must_use]
pub const fn alignment() -> usize {
8
}
const SLOT_OFFSET: usize = HEADER_SIZE;
const HEADER_SIZE: usize = 8;
pub unsafe fn init(base: *mut u8, capacity: u16, element_size: u32) {
unsafe {
ptr::write(base.cast::<u16>(), capacity);
ptr::write(base.add(2).cast::<u16>(), 0);
ptr::write(base.add(4).cast::<u32>(), element_size);
let cap = capacity as usize;
let id_offset = SLOT_OFFSET + cap * size_of::<u16>();
let generation_offset = id_offset + cap * size_of::<u16>();
for i in 0..cap {
ptr::write(
base.add(SLOT_OFFSET).cast::<u16>().add(i),
(cap - 1 - i) as u16,
);
}
for i in 0..cap {
ptr::write(base.add(id_offset).cast::<u16>().add(i), u16::MAX);
}
for i in 0..cap {
ptr::write(base.add(generation_offset).cast::<u16>().add(i), 0);
}
}
}
pub unsafe fn allocate(base: *mut u8) -> Option<(u16, u16)> {
unsafe {
let capacity = *base.cast::<u16>() as usize;
let count_ptr = base.add(2).cast::<u16>();
let count = *count_ptr as usize;
if count >= capacity {
return None;
}
let id_offset = SLOT_OFFSET + capacity * size_of::<u16>();
let generation_offset = id_offset + capacity * size_of::<u16>();
let id = *base.add(SLOT_OFFSET).cast::<u16>().add(count);
ptr::write(count_ptr, (count as u16).wrapping_add(1));
ptr::write(
base.add(id_offset).cast::<u16>().add(id as usize),
count as u16,
);
let generation_ptr = base.add(generation_offset).cast::<u16>();
let new_gen = generation_ptr.add(id as usize).read().wrapping_add(1);
ptr::write(generation_ptr.add(id as usize), new_gen);
Some((id, new_gen))
}
}
#[must_use]
pub const fn values_offset(base: *const u8) -> usize {
let capacity = unsafe { *base.cast::<u16>() } as usize;
let id_offset = SLOT_OFFSET + capacity * size_of::<u16>();
let generation_offset = id_offset + capacity * size_of::<u16>();
let before_values = generation_offset + capacity * size_of::<u16>();
let padding = (8 - (before_values % 8)) % 8;
before_values + padding
}
#[inline]
pub unsafe fn insert(base: *mut u8, id: u16, src: *const u8) -> bool {
unsafe {
let capacity = *base.cast::<u16>() as usize;
#[cfg(debug_assertions)]
{
if id as usize >= capacity {
return false;
}
let id_offset = SLOT_OFFSET + capacity * size_of::<u16>();
let slot = *base.add(id_offset).cast::<u16>().add(id as usize);
let count = *base.add(2).cast::<u16>() as usize;
if slot == u16::MAX || slot as usize >= count {
return false;
}
let slot_to_id_ptr = base.add(SLOT_OFFSET).cast::<u16>();
if *slot_to_id_ptr.add(slot as usize) != id {
return false;
}
}
let element_size = *base.add(4).cast::<u32>() as usize;
let off = values_offset(base) + id as usize * element_size;
ptr::copy_nonoverlapping(src, base.add(off), element_size);
true
}
}
pub unsafe fn remove(base: *mut u8, id: u16, generation: u16) -> bool {
unsafe {
let capacity = *base.cast::<u16>() as usize;
#[cfg(debug_assertions)]
{
if id as usize >= capacity {
return false;
}
if !is_alive(base, id, generation) {
return false;
}
}
let id_offset = SLOT_OFFSET + capacity * size_of::<u16>();
let generation_offset = id_offset + capacity * size_of::<u16>();
let count_ptr = base.add(2).cast::<u16>();
let count = (*count_ptr) as usize;
let last = count - 1;
ptr::write(count_ptr, last as u16);
let slot_ptr = base.add(SLOT_OFFSET).cast::<u16>();
let slot_idx = *base.add(id_offset).cast::<u16>().add(id as usize) as usize;
let last_id = *slot_ptr.add(last);
ptr::write(slot_ptr.add(slot_idx), last_id);
ptr::write(
base.add(id_offset).cast::<u16>().add(last_id as usize),
slot_idx as u16,
);
ptr::write(slot_ptr.add(last), id);
ptr::write(base.add(id_offset).cast::<u16>().add(id as usize), u16::MAX);
let gen_ptr = base.add(generation_offset).cast::<u16>();
ptr::write(gen_ptr.add(id as usize), generation.wrapping_add(1));
true
}
}
pub unsafe fn is_alive(base: *mut u8, id: u16, generation: u16) -> bool {
unsafe {
let capacity = *base.cast::<u16>() as usize;
#[cfg(debug_assertions)]
{
if id as usize >= capacity {
return false;
}
}
let count = *base.add(2).cast::<u16>() as usize;
let id_offset = SLOT_OFFSET + capacity * size_of::<u16>();
let generation_offset = id_offset + capacity * size_of::<u16>();
let slot = *base.add(id_offset).cast::<u16>().add(id as usize) as usize;
let current_generation = *base.add(generation_offset).cast::<u16>().add(id as usize);
slot < count
&& current_generation == generation
&& *base.add(SLOT_OFFSET).cast::<u16>().add(slot) == id
}
}
pub const unsafe fn slot_to_id_ptr(base: *mut u8) -> *mut u16 {
unsafe { base.add(SLOT_OFFSET).cast::<u16>() }
}
#[must_use]
pub const unsafe fn generation_ptr_const(base: *const u8) -> *const u16 {
unsafe {
let capacity = *base.cast::<u16>() as usize;
let id_offset = SLOT_OFFSET + capacity * size_of::<u16>();
let generation_offset = id_offset + capacity * size_of::<u16>();
base.add(generation_offset).cast::<u16>()
}
}
#[must_use]
pub const unsafe fn slot_to_id_ptr_const(base: *const u8) -> *const u16 {
unsafe { base.add(SLOT_OFFSET).cast::<u16>() }
}
pub unsafe fn id_to_slot_ptr(base: *mut u8) -> *mut u16 {
unsafe {
let capacity = *base.cast::<u16>() as usize;
base.add(SLOT_OFFSET + capacity * size_of::<u16>())
.cast::<u16>()
}
}
#[must_use]
pub const unsafe fn id_to_slot_ptr_const(base: *const u8) -> *const u16 {
unsafe {
let capacity = *base.cast::<u16>() as usize;
base.add(SLOT_OFFSET + capacity * size_of::<u16>())
.cast::<u16>()
}
}
#[must_use]
pub const unsafe fn element_count(base: *const u8) -> u16 {
unsafe { *base.add(2).cast::<u16>() }
}
#[must_use]
pub const unsafe fn element_size(base: *const u8) -> u32 {
unsafe { *base.add(4).cast::<u32>() }
}
pub unsafe fn insert_if_alive(base: *mut u8, id: u16, generation: u16, src: *const u8) -> bool {
unsafe {
if is_alive(base, id, generation) {
insert(base, id, src)
} else {
false
}
}
}
pub unsafe fn get_value_ptr(base: *mut u8, id: u16, generation: u16) -> Option<*mut u8> {
unsafe {
if !is_alive(base, id, generation) {
return None;
}
let capacity = *base.cast::<u16>() as usize;
if id as usize >= capacity {
return None;
}
let element_size = *base.add(4).cast::<u32>() as usize;
let off = values_offset(base) + id as usize * element_size;
Some(base.add(off))
}
}