mod box_values;
#[cfg(not(feature = "sidecar-suffix"))]
mod embedded_suffix;
mod inline_values;
mod sidecar_suffix;
mod suffix_ops;
#[cfg(test)]
mod tests;
pub use box_values::BoxValueArray;
#[expect(
clippy::redundant_pub_crate,
reason = "pub(crate) is intentional: not part of public API"
)]
pub(crate) use box_values::atomic_read_value;
#[cfg(not(feature = "sidecar-suffix"))]
pub use embedded_suffix::EmbeddedSuffix;
pub use inline_values::InlineValueArray;
pub use sidecar_suffix::SidecarSuffix;
use std::cmp::Ordering;
use std::fmt::{self as StdFmt, Debug, Display, Formatter};
use std::hash::Hash;
use std::marker::PhantomData;
use std::mem::{MaybeUninit, align_of, needs_drop, size_of};
use std::ops::Deref;
use std::ptr::NonNull;
use seize::{Guard, LocalGuard};
use crate::inline::bits::InlineBits;
use crate::ordering::READ_ORD;
use crate::prefetch::prefetch_read;
use crate::suffix::SuffixBagCell;
#[repr(transparent)]
pub struct ValuePtr<V>(NonNull<V>);
impl<V> ValuePtr<V> {
#[inline(always)]
pub(crate) const unsafe fn from_raw(ptr: *mut V) -> Self {
Self(unsafe { NonNull::new_unchecked(ptr) })
}
#[inline(always)]
pub(crate) const fn as_ptr(self) -> *mut V {
self.0.as_ptr()
}
}
impl<V> Copy for ValuePtr<V> {}
impl<V> Clone for ValuePtr<V> {
#[inline(always)]
fn clone(&self) -> Self {
*self
}
}
impl<V> Deref for ValuePtr<V> {
type Target = V;
#[inline(always)]
fn deref(&self) -> &V {
unsafe { self.0.as_ref() }
}
}
unsafe impl<V: Send + Sync> Send for ValuePtr<V> {}
unsafe impl<V: Send + Sync> Sync for ValuePtr<V> {}
impl<V: Debug> Debug for ValuePtr<V> {
fn fmt(&self, f: &mut Formatter<'_>) -> StdFmt::Result {
Debug::fmt(&**self, f)
}
}
impl<V: PartialEq> PartialEq for ValuePtr<V> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<V: Eq> Eq for ValuePtr<V> {}
impl<V: Display> Display for ValuePtr<V> {
fn fmt(&self, f: &mut Formatter<'_>) -> StdFmt::Result {
Display::fmt(&**self, f)
}
}
impl<V: Hash> Hash for ValuePtr<V> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<V: PartialOrd> PartialOrd for ValuePtr<V> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<V: Ord> Ord for ValuePtr<V> {
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<V> AsRef<V> for ValuePtr<V> {
#[inline(always)]
fn as_ref(&self) -> &V {
self
}
}
pub enum ValueRef<'a, V> {
Borrowed(&'a V),
Owned(V),
}
impl<V> Deref for ValueRef<'_, V> {
type Target = V;
#[inline(always)]
fn deref(&self) -> &V {
match self {
ValueRef::Borrowed(r) => r,
ValueRef::Owned(v) => v,
}
}
}
impl<V: Debug> Debug for ValueRef<'_, V> {
fn fmt(&self, f: &mut Formatter<'_>) -> StdFmt::Result {
Debug::fmt(&**self, f)
}
}
impl<V: Display> Display for ValueRef<'_, V> {
fn fmt(&self, f: &mut Formatter<'_>) -> StdFmt::Result {
Display::fmt(&**self, f)
}
}
impl<V: PartialEq> PartialEq for ValueRef<'_, V> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<V: Eq> Eq for ValueRef<'_, V> {}
impl<V: PartialEq> PartialEq<V> for ValueRef<'_, V> {
#[inline(always)]
fn eq(&self, other: &V) -> bool {
**self == *other
}
}
impl<V: Clone> ValueRef<'_, V> {
#[inline(always)]
pub fn into_owned(self) -> V {
match self {
ValueRef::Borrowed(r) => r.clone(),
ValueRef::Owned(v) => v,
}
}
}
impl<V: Copy> ValueRef<'_, V> {
#[inline(always)]
pub fn copied(&self) -> V {
**self
}
}
mod sealed {
use crate::inline::bits::InlineBits;
use super::{BoxPolicy, BoxValueArray, InlinePolicy, InlineValueArray, SidecarSuffix};
#[cfg(not(feature = "sidecar-suffix"))]
use super::EmbeddedSuffix;
pub trait Sealed {}
impl<V: Send + Sync + 'static> Sealed for BoxPolicy<V> {}
impl<V: InlineBits> Sealed for InlinePolicy<V> {}
pub trait ValueArraySealed {}
impl<V: Send + Sync + 'static> ValueArraySealed for BoxValueArray<V> {}
impl<V: InlineBits> ValueArraySealed for InlineValueArray<V> {}
pub trait SuffixStoreSealed {}
impl SuffixStoreSealed for SidecarSuffix {}
#[cfg(not(feature = "sidecar-suffix"))]
impl SuffixStoreSealed for EmbeddedSuffix {}
pub trait RefPolicySealed {}
impl<V: Send + Sync + 'static> RefPolicySealed for BoxPolicy<V> {}
}
#[derive(Debug, PartialEq, Eq)]
pub enum RetireHandle {
Ptr(*mut u8),
Noop,
}
unsafe impl Send for RetireHandle {}
pub enum SlotKind<O> {
Empty,
Value(O),
Layer(*mut u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SlotState {
Empty,
Value,
Layer(*mut u8),
}
#[allow(dead_code, reason = "Sealed trait")]
pub trait ValueArray<O>: sealed::ValueArraySealed + Send + Sync + Sized + 'static {
fn new() -> Self;
fn is_empty(&self, slot: usize) -> bool;
fn is_empty_relaxed(&self, slot: usize) -> bool;
fn is_layer(&self, slot: usize) -> bool;
fn load(&self, slot: usize) -> Option<O>;
fn store(&self, slot: usize, output: &O);
fn store_relaxed(&self, slot: usize, output: &O);
fn update_in_place(&self, slot: usize, output: &O) -> RetireHandle;
fn update_in_place_relaxed(&self, slot: usize, output: &O) -> RetireHandle;
fn take(&self, slot: usize) -> Option<O>;
fn load_raw(&self, slot: usize) -> *mut u8;
fn load_raw_relaxed(&self, slot: usize) -> *mut u8;
fn load_layer(&self, slot: usize) -> *mut u8;
fn store_layer(&self, slot: usize, ptr: *mut u8);
fn load_relaxed(&self, slot: usize) -> Option<O>;
fn clear(&self, slot: usize);
fn clear_relaxed(&self, slot: usize);
fn move_slot(&self, dst: &Self, src_slot: usize, dst_slot: usize);
fn move_slot_relaxed(&self, dst: &Self, src_slot: usize, dst_slot: usize);
unsafe fn cleanup(&self, slot: usize);
}
#[allow(dead_code, reason = "Sealed trait")]
pub trait SuffixStore: sealed::SuffixStoreSealed + Send + Sync + Sized + 'static {
fn new() -> Self;
fn get(&self, slot: usize) -> Option<&[u8]>;
fn suffix_equals(&self, slot: usize, suffix: &[u8]) -> bool;
fn suffix_compare(&self, slot: usize, suffix: &[u8]) -> Option<Ordering>;
fn has_external(&self) -> bool;
fn prefetch(&self);
unsafe fn assign(
&self,
slot: usize,
suffix: &[u8],
perm: &impl crate::TreePermutation,
guard: &LocalGuard<'_>,
) -> *mut u8;
unsafe fn assign_init(&self, slot: usize, suffix: &[u8], guard: &LocalGuard<'_>);
unsafe fn assign_prealloc(
&self,
slot: usize,
suffix: &[u8],
perm: &impl crate::TreePermutation,
guard: &LocalGuard<'_>,
prealloc: Vec<u8>,
) -> *mut u8;
unsafe fn ensure_external(&self) -> *mut SuffixBagCell;
unsafe fn clear(&self, slot: usize, guard: &LocalGuard<'_>);
unsafe fn retire_bag_ptr(ptr: *mut u8, guard: &LocalGuard<'_>);
unsafe fn drop_storage(&mut self);
unsafe fn init_at_zero(ptr: *mut Self);
}
pub trait LeafPolicy: sealed::Sealed + Send + Sync + Sized + 'static {
type Value: Send + Sync;
type Output: Clone + Send + Sync;
type Values: ValueArray<Self::Output>;
type Suffix: SuffixStore;
const NEEDS_RETIREMENT: bool;
const SUPPORTS_REF: bool;
const CAN_WRITE_THROUGH: bool = false;
fn is_value_empty(values: &Self::Values, slot: usize) -> bool;
fn is_value_empty_relaxed(values: &Self::Values, slot: usize) -> bool;
fn prefetch_value(values: &Self::Values, slot: usize);
unsafe fn load_value_ptr(values: &Self::Values, slot: usize) -> *const Self::Value;
fn take_value_for_layer(values: &Self::Values, slot: usize) -> RetireHandle;
fn into_output(value: Self::Value) -> Self::Output;
#[inline(always)]
fn clone_output(output: &Self::Output) -> Self::Output {
output.clone()
}
fn clone_value_from_output(output: &Self::Output) -> Self::Value
where
Self::Value: Clone;
unsafe fn output_from_retire_ptr(ptr: *mut u8) -> Self::Output;
unsafe fn retire_handle(handle: RetireHandle, guard: &LocalGuard<'_>);
unsafe fn retire_output(output: Self::Output, guard: &LocalGuard<'_>);
unsafe fn write_through_update(
_values: &Self::Values,
_slot: usize,
_new_value: &Self::Value,
) -> Self::Value {
unreachable!("write_through_update called on policy with CAN_WRITE_THROUGH=false")
}
unsafe fn load_value_ref<'g>(
values: &Self::Values,
slot: usize,
) -> Option<ValueRef<'g, Self::Value>>;
}
pub trait RefPolicy: sealed::RefPolicySealed + LeafPolicy {
fn output_as_ref(output: &Self::Output) -> &Self::Value;
unsafe fn output_as_ref_sound<'a>(
output: &'a Self::Output,
scratch: &'a mut MaybeUninit<Self::Value>,
) -> &'a Self::Value;
}
pub struct BoxPolicy<V>(PhantomData<V>);
impl<V: Send + Sync + 'static> LeafPolicy for BoxPolicy<V> {
type Value = V;
type Output = ValuePtr<V>;
type Values = BoxValueArray<V>;
type Suffix = SidecarSuffix;
const NEEDS_RETIREMENT: bool = true;
const SUPPORTS_REF: bool = true;
const CAN_WRITE_THROUGH: bool = !needs_drop::<V>()
&& size_of::<V>() <= 8
&& size_of::<V>().is_power_of_two()
&& align_of::<V>() >= size_of::<V>();
#[inline(always)]
fn is_value_empty(values: &BoxValueArray<V>, slot: usize) -> bool {
values.is_empty(slot)
}
#[inline(always)]
fn is_value_empty_relaxed(values: &BoxValueArray<V>, slot: usize) -> bool {
values.is_empty_relaxed(slot)
}
#[inline(always)]
fn prefetch_value(values: &BoxValueArray<V>, slot: usize) {
let ptr: *mut u8 = values.load_raw_relaxed(slot);
if !ptr.is_null() {
prefetch_read(ptr.cast::<V>());
}
}
#[inline(always)]
unsafe fn load_value_ptr(values: &BoxValueArray<V>, slot: usize) -> *const V {
let ptr: *mut u8 = values.load_raw(slot);
if ptr.is_null() {
std::ptr::null()
} else {
ptr.cast::<V>()
}
}
#[inline(always)]
fn take_value_for_layer(values: &BoxValueArray<V>, slot: usize) -> RetireHandle {
values.clear_relaxed(slot);
RetireHandle::Noop
}
#[inline(always)]
fn into_output(value: V) -> ValuePtr<V> {
let ptr: *mut V = Box::into_raw(Box::new(value));
unsafe { ValuePtr::from_raw(ptr) }
}
#[inline(always)]
fn clone_value_from_output(output: &ValuePtr<V>) -> V
where
V: Clone,
{
if Self::CAN_WRITE_THROUGH {
unsafe { atomic_read_value::<V>(output.as_ptr().cast(), READ_ORD) }
} else {
(**output).clone()
}
}
#[inline(always)]
unsafe fn output_from_retire_ptr(ptr: *mut u8) -> ValuePtr<V> {
debug_assert!(!ptr.is_null());
unsafe { ValuePtr::from_raw(ptr.cast::<V>()) }
}
#[inline(always)]
unsafe fn retire_handle(handle: RetireHandle, guard: &LocalGuard<'_>) {
if let RetireHandle::Ptr(ptr) = handle {
debug_assert!(!ptr.is_null());
unsafe {
guard.defer_retire(ptr.cast::<V>(), |ptr: *mut V, _| {
drop(Box::from_raw(ptr));
});
}
}
}
#[inline(always)]
unsafe fn retire_output(output: ValuePtr<V>, guard: &LocalGuard<'_>) {
let ptr: *mut V = output.as_ptr();
unsafe {
guard.defer_retire(ptr, |ptr: *mut V, _| {
drop(Box::from_raw(ptr));
});
}
}
#[inline(always)]
unsafe fn write_through_update(values: &BoxValueArray<V>, slot: usize, new_value: &V) -> V {
debug_assert!(
Self::CAN_WRITE_THROUGH,
"write_through_update called but CAN_WRITE_THROUGH is false"
);
unsafe { values.write_through_update(slot, new_value) }
}
#[inline(always)]
unsafe fn load_value_ref<'g>(
values: &BoxValueArray<V>,
slot: usize,
) -> Option<ValueRef<'g, V>> {
let ptr: *mut u8 = values.load_raw(slot);
if ptr.is_null() {
None
} else if Self::CAN_WRITE_THROUGH {
Some(ValueRef::Owned(unsafe {
atomic_read_value::<V>(ptr, READ_ORD)
}))
} else {
Some(ValueRef::Borrowed(unsafe { &*ptr.cast::<V>() }))
}
}
}
impl<V: Send + Sync + 'static> RefPolicy for BoxPolicy<V> {
#[inline(always)]
fn output_as_ref(output: &ValuePtr<V>) -> &V {
output
}
#[inline(always)]
unsafe fn output_as_ref_sound<'a>(
output: &'a ValuePtr<V>,
scratch: &'a mut MaybeUninit<V>,
) -> &'a V {
if Self::CAN_WRITE_THROUGH {
*scratch = MaybeUninit::new(unsafe {
atomic_read_value::<V>(output.as_ptr().cast(), READ_ORD)
});
unsafe { scratch.assume_init_ref() }
} else {
Self::output_as_ref(output)
}
}
}
pub struct InlinePolicy<V: InlineBits>(PhantomData<V>);
impl<V: InlineBits> LeafPolicy for InlinePolicy<V> {
type Value = V;
type Output = V;
type Values = InlineValueArray<V>;
#[cfg(not(feature = "sidecar-suffix"))]
type Suffix = EmbeddedSuffix;
#[cfg(feature = "sidecar-suffix")]
type Suffix = SidecarSuffix;
const NEEDS_RETIREMENT: bool = false;
const SUPPORTS_REF: bool = false;
#[inline(always)]
fn is_value_empty(values: &InlineValueArray<V>, slot: usize) -> bool {
values.is_empty(slot)
}
#[inline(always)]
fn is_value_empty_relaxed(values: &InlineValueArray<V>, slot: usize) -> bool {
values.is_empty_relaxed(slot)
}
#[inline(always)]
fn prefetch_value(_values: &InlineValueArray<V>, _slot: usize) {}
#[inline(always)]
unsafe fn load_value_ptr(_values: &InlineValueArray<V>, _slot: usize) -> *const V {
unreachable!("inline values do not support pointer-based access")
}
#[inline(always)]
fn take_value_for_layer(values: &InlineValueArray<V>, slot: usize) -> RetireHandle {
values.clear_relaxed(slot);
RetireHandle::Noop
}
#[inline(always)]
fn into_output(value: V) -> V {
value
}
#[inline(always)]
fn clone_value_from_output(output: &V) -> V
where
V: Clone,
{
*output }
#[inline(always)]
unsafe fn output_from_retire_ptr(_ptr: *mut u8) -> V {
unreachable!("inline values have no retired pointer")
}
#[inline(always)]
unsafe fn retire_handle(_handle: RetireHandle, _guard: &LocalGuard<'_>) {}
#[inline(always)]
unsafe fn retire_output(_output: V, _guard: &LocalGuard<'_>) {}
#[inline(always)]
unsafe fn load_value_ref<'g>(
_values: &InlineValueArray<V>,
_slot: usize,
) -> Option<ValueRef<'g, V>> {
unreachable!("inline values do not support reference returns")
}
}