extern crate alloc;
use super::MIN_CAPACITY;
use super::node::{Node, ptr};
use crate::sync::AtomicIsize;
use core::sync::atomic::Ordering;
use kovan::{Atomic, CachePadded, RetiredNode, alloc_node, drop_node, free_node, pin};
#[repr(C)]
pub(super) struct TableHeader {
mask: usize,
capacity: usize,
proxy: usize,
count: CachePadded<AtomicIsize>,
}
pub(super) struct TableRef<K: 'static, V: 'static> {
header: *mut TableHeader,
_marker: core::marker::PhantomData<(K, V)>,
}
impl<K, V> Clone for TableRef<K, V> {
fn clone(&self) -> Self {
*self
}
}
impl<K, V> Copy for TableRef<K, V> {}
impl<K: 'static, V: 'static> TableRef<K, V> {
#[inline(always)]
pub(super) fn from_raw(header: *mut TableHeader) -> Self {
Self {
header,
_marker: core::marker::PhantomData,
}
}
fn layout(capacity: usize) -> (core::alloc::Layout, usize) {
let header = core::alloc::Layout::new::<TableHeader>();
let buckets = core::alloc::Layout::array::<Atomic<Node<K, V>>>(capacity)
.expect("bucket array layout overflow");
let (layout, offset) = header.extend(buckets).expect("table layout overflow");
(layout.pad_to_align(), offset)
}
pub(super) fn alloc(capacity: usize) -> Self {
let capacity = capacity.next_power_of_two().max(MIN_CAPACITY);
let (layout, _) = Self::layout(capacity);
let header = unsafe { alloc::alloc::alloc_zeroed(layout) as *mut TableHeader };
if header.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
unsafe {
(*header).mask = capacity - 1;
(*header).capacity = capacity;
core::ptr::write(
core::ptr::addr_of_mut!((*header).count),
CachePadded::new(AtomicIsize::new(0)),
);
}
let table = Self::from_raw(header);
#[cfg(feature = "shuttle")]
for i in 0..capacity {
unsafe { core::ptr::write(table.bucket_mut(i), Atomic::null()) };
}
let proxy = alloc_node(TableProxy {
retired: RetiredNode::new(),
table,
});
unsafe { (*header).proxy = proxy as usize };
table
}
#[inline(always)]
pub(super) fn take_proxy(self) -> *mut TableProxy<K, V> {
let proxy = unsafe { (*self.header).proxy };
assert_ne!(proxy, 0, "kovan-map: table proxy already taken");
unsafe { (*self.header).proxy = 0 };
proxy as *mut TableProxy<K, V>
}
#[inline(always)]
unsafe fn drop_unused_proxy(self) {
let proxy = unsafe { (*self.header).proxy };
if proxy != 0 {
unsafe {
(*self.header).proxy = 0;
free_node(proxy as *mut TableProxy<K, V>);
}
}
}
pub(super) unsafe fn free_array_only(self) {
unsafe { self.drop_unused_proxy() };
let capacity = unsafe { (*self.header).capacity };
unsafe { self.drop_words(capacity) };
let (layout, _) = Self::layout(capacity);
unsafe { alloc::alloc::dealloc(self.header as *mut u8, layout) };
}
pub(super) unsafe fn free(self) {
unsafe { self.drop_unused_proxy() };
let capacity = unsafe { (*self.header).capacity };
let guard = pin();
for i in 0..capacity {
let mut current = ptr(self.bucket(i).load(Ordering::Relaxed, &guard).as_raw());
while !current.is_null() {
unsafe {
let next = ptr((*current).next.load(Ordering::Relaxed, &guard).as_raw());
drop_node(current);
current = next;
}
}
}
drop(guard);
unsafe { self.drop_words(capacity) };
let (layout, _) = Self::layout(capacity);
unsafe { alloc::alloc::dealloc(self.header as *mut u8, layout) };
}
#[inline(always)]
unsafe fn drop_words(self, _capacity: usize) {
#[cfg(feature = "shuttle")]
unsafe {
core::ptr::drop_in_place(core::ptr::addr_of_mut!((*self.header).count));
for i in 0.._capacity {
core::ptr::drop_in_place(self.bucket_mut(i));
}
}
}
#[inline(always)]
pub(super) fn as_raw(self) -> *mut TableHeader {
self.header
}
#[inline(always)]
pub(super) fn capacity(self) -> usize {
unsafe { (*self.header).capacity }
}
#[inline(always)]
pub(super) fn count(self) -> &'static AtomicIsize {
unsafe { &(*self.header).count }
}
#[inline(always)]
fn buckets(self) -> *mut Atomic<Node<K, V>> {
let (_, offset) = Self::layout_offset();
unsafe { (self.header as *mut u8).add(offset) as *mut Atomic<Node<K, V>> }
}
#[inline(always)]
fn layout_offset() -> ((), usize) {
let header = core::alloc::Layout::new::<TableHeader>();
let one = core::alloc::Layout::new::<Atomic<Node<K, V>>>();
let (_, offset) = header.extend(one).expect("layout");
((), offset)
}
#[inline(always)]
pub(super) fn bucket_index(self, hash: u64) -> usize {
(hash as usize) & unsafe { (*self.header).mask }
}
#[inline(always)]
pub(super) fn bucket(self, idx: usize) -> &'static Atomic<Node<K, V>> {
unsafe { &*self.buckets().add(idx) }
}
#[cfg(feature = "shuttle")]
#[inline(always)]
fn bucket_mut(self, idx: usize) -> *mut Atomic<Node<K, V>> {
unsafe { self.buckets().add(idx) }
}
}
#[repr(C)]
pub(super) struct TableProxy<K: 'static, V: 'static> {
retired: RetiredNode,
table: TableRef<K, V>,
}
unsafe impl<K: Send, V: Send> Send for TableProxy<K, V> {}
unsafe impl<K: Send + Sync, V: Send + Sync> Sync for TableProxy<K, V> {}
impl<K, V> Drop for TableProxy<K, V> {
fn drop(&mut self) {
unsafe { self.table.free() };
}
}