use std::alloc::Layout;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicPtr, AtomicU8, Ordering};
use std::{alloc, mem, ptr};
use super::{probe, State};
#[repr(transparent)]
pub struct RawTable<T>(u8, PhantomData<T>);
#[repr(C)]
struct TableLayout<T> {
mask: usize,
limit: usize,
state: State<T>,
meta: [AtomicU8; 0],
entries: [AtomicPtr<T>; 0],
}
#[repr(C)]
pub struct Table<T> {
pub mask: usize,
pub limit: usize,
pub raw: *mut RawTable<T>,
}
impl<T> Copy for Table<T> {}
impl<T> Clone for Table<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Table<T> {
pub fn alloc(len: usize) -> Table<T> {
assert!(len.is_power_of_two());
let len = len.max(mem::align_of::<AtomicPtr<T>>());
let mask = len - 1;
let limit = probe::limit(len);
let layout = Table::<T>::layout(len);
let ptr = unsafe { alloc::alloc_zeroed(layout) };
if ptr.is_null() {
alloc::handle_alloc_error(layout);
}
unsafe {
ptr.cast::<TableLayout<T>>().write(TableLayout {
mask,
limit,
meta: [],
entries: [],
state: State::default(),
});
ptr.add(mem::size_of::<TableLayout<T>>())
.cast::<u8>()
.write_bytes(super::meta::EMPTY, len);
}
Table {
mask,
limit,
raw: ptr.cast::<RawTable<T>>(),
}
}
#[inline]
pub unsafe fn from_raw(raw: *mut RawTable<T>) -> Table<T> {
if raw.is_null() {
return Table {
raw,
mask: 0,
limit: 0,
};
}
let layout = unsafe { &*raw.cast::<TableLayout<T>>() };
Table {
raw,
mask: layout.mask,
limit: layout.limit,
}
}
#[inline]
pub unsafe fn meta(&self, i: usize) -> &AtomicU8 {
debug_assert!(i < self.len());
unsafe {
let meta = self.raw.add(mem::size_of::<TableLayout<T>>());
&*meta.cast::<AtomicU8>().add(i)
}
}
#[inline]
pub unsafe fn entry(&self, i: usize) -> &AtomicPtr<T> {
debug_assert!(i < self.len());
unsafe {
let meta = self.raw.add(mem::size_of::<TableLayout<T>>());
let entries = meta.add(self.len()).cast::<AtomicPtr<T>>();
&*entries.add(i)
}
}
#[inline]
pub fn len(&self) -> usize {
self.mask + 1
}
#[inline]
pub fn state(&self) -> &State<T> {
unsafe { &(*self.raw.cast::<TableLayout<T>>()).state }
}
#[inline]
pub fn state_mut(&mut self) -> &mut State<T> {
unsafe { &mut (*self.raw.cast::<TableLayout<T>>()).state }
}
#[inline]
pub fn next_table(&self) -> Option<Self> {
let next = self.state().next.load(Ordering::Acquire);
if !next.is_null() {
return unsafe { Some(Table::from_raw(next)) };
}
None
}
pub unsafe fn dealloc(table: Table<T>) {
let layout = Self::layout(table.len());
unsafe {
ptr::drop_in_place(table.raw.cast::<TableLayout<T>>());
alloc::dealloc(table.raw.cast::<u8>(), layout);
};
}
fn layout(len: usize) -> Layout {
let size = mem::size_of::<TableLayout<T>>()
+ (mem::size_of::<u8>() * len) + (mem::size_of::<AtomicPtr<T>>() * len); Layout::from_size_align(size, mem::align_of::<TableLayout<T>>()).unwrap()
}
}
#[test]
fn layout() {
unsafe {
let table: Table<u8> = Table::alloc(4);
let table: Table<u8> = Table::from_raw(table.raw);
assert_eq!(table.mask, 7);
assert_eq!(table.len(), 8);
Table::dealloc(table);
}
}