use std::cmp::Ordering;
use std::ptr as StdPtr;
use std::sync::atomic::{AtomicPtr, Ordering as AtomicOrdering};
use seize::{Guard, LocalGuard};
use crate::TreePermutation;
use crate::suffix::{InlineSuffixBag, SuffixBag, SuffixBagCell};
#[inline(always)]
pub(super) fn get_suffix<'a>(
inline: &'a InlineSuffixBag,
external: &'a AtomicPtr<SuffixBagCell>,
slot: usize,
) -> Option<&'a [u8]> {
if let Some(suffix) = inline.get(slot) {
return Some(suffix);
}
let ext_ptr: *mut SuffixBagCell = external.load(AtomicOrdering::Acquire);
if ext_ptr.is_null() {
None
} else {
unsafe { (*ext_ptr).as_ref() }.get(slot)
}
}
#[inline(always)]
#[allow(
dead_code,
reason = "SuffixStore trait plumbing, not all impls call through here"
)]
pub(super) fn suffix_equals(
inline: &InlineSuffixBag,
external: &AtomicPtr<SuffixBagCell>,
slot: usize,
suffix: &[u8],
) -> bool {
get_suffix(inline, external, slot) == Some(suffix)
}
#[inline(always)]
#[allow(
dead_code,
reason = "SuffixStore trait plumbing, not all impls call through here"
)]
pub(super) fn suffix_compare(
inline: &InlineSuffixBag,
external: &AtomicPtr<SuffixBagCell>,
slot: usize,
suffix: &[u8],
) -> Option<Ordering> {
get_suffix(inline, external, slot).map(|stored: &[u8]| stored.cmp(suffix))
}
#[inline(always)]
pub(super) fn has_external(external: &AtomicPtr<SuffixBagCell>) -> bool {
!external.load(AtomicOrdering::Acquire).is_null()
}
#[inline(always)]
pub(super) unsafe fn assign_suffix(
inline: &InlineSuffixBag,
external: &AtomicPtr<SuffixBagCell>,
slot: usize,
suffix: &[u8],
perm: &impl TreePermutation,
) -> *mut u8 {
if suffix.is_empty() {
return StdPtr::null_mut();
}
if inline.try_assign(slot, suffix) {
return StdPtr::null_mut();
}
let ext_ptr: *mut SuffixBagCell = external.load(AtomicOrdering::Relaxed);
if !ext_ptr.is_null() {
let bag: &mut SuffixBag = unsafe { (*ext_ptr).as_mut() };
if bag.try_assign_append_only(slot, suffix) {
inline.clear(slot);
return StdPtr::null_mut();
}
}
unsafe { drain_and_rebuild(inline, external, slot, suffix, perm) }
}
#[inline(always)]
pub(super) unsafe fn assign_suffix_init(
inline: &InlineSuffixBag,
external: &AtomicPtr<SuffixBagCell>,
slot: usize,
suffix: &[u8],
guard: &LocalGuard<'_>,
) {
if suffix.is_empty() {
return;
}
if inline.try_assign(slot, suffix) {
return;
}
let ext_ptr: *mut SuffixBagCell = external.load(AtomicOrdering::Relaxed);
if !ext_ptr.is_null() {
let bag: &mut SuffixBag = unsafe { (*ext_ptr).as_mut() };
bag.assign(slot, suffix);
inline.clear(slot);
return;
}
unsafe { drain_and_rebuild_init(inline, external, slot, suffix, guard) }
}
#[inline(always)]
pub(super) unsafe fn assign_suffix_prealloc(
inline: &InlineSuffixBag,
external: &AtomicPtr<SuffixBagCell>,
slot: usize,
suffix: &[u8],
perm: &impl TreePermutation,
prealloc: Vec<u8>,
) -> *mut u8 {
if inline.try_assign(slot, suffix) {
return StdPtr::null_mut();
}
let ext_ptr: *mut SuffixBagCell = external.load(AtomicOrdering::Relaxed);
if !ext_ptr.is_null() {
let bag: &mut SuffixBag = unsafe { (*ext_ptr).as_mut() };
if bag.try_assign_append_only(slot, suffix) {
inline.clear(slot);
return StdPtr::null_mut();
}
}
unsafe { drain_and_rebuild_prealloc(inline, external, slot, suffix, perm, prealloc) }
}
#[inline(always)]
pub(super) unsafe fn clear_suffix(
inline: &InlineSuffixBag,
external: &AtomicPtr<SuffixBagCell>,
slot: usize,
) {
inline.clear(slot);
let ext_ptr: *mut SuffixBagCell = external.load(AtomicOrdering::Acquire);
if !ext_ptr.is_null() {
let bag: &mut SuffixBag = unsafe { (*ext_ptr).as_mut() };
bag.clear(slot);
}
}
#[inline(always)]
pub(super) unsafe fn ensure_external_bag(
external: &AtomicPtr<SuffixBagCell>,
) -> *mut SuffixBagCell {
let ptr: *mut SuffixBagCell = external.load(AtomicOrdering::Acquire);
if !ptr.is_null() {
return ptr;
}
let new_cell: Box<SuffixBagCell> = Box::default();
let new_ptr: *mut SuffixBagCell = Box::into_raw(new_cell);
external.store(new_ptr, AtomicOrdering::Release);
new_ptr
}
#[cold]
#[inline(never)]
unsafe fn drain_and_rebuild(
inline: &InlineSuffixBag,
external_slot: &AtomicPtr<SuffixBagCell>,
slot: usize,
suffix: &[u8],
perm: &impl TreePermutation,
) -> *mut u8 {
let new_bag: SuffixBag = inline.drain_to_external(perm, slot, suffix);
unsafe { install_drained_bag(new_bag, external_slot, slot, perm) }
}
#[cold]
#[inline(never)]
unsafe fn drain_and_rebuild_prealloc(
inline: &InlineSuffixBag,
external_slot: &AtomicPtr<SuffixBagCell>,
slot: usize,
suffix: &[u8],
perm: &impl TreePermutation,
prealloc: Vec<u8>,
) -> *mut u8 {
let new_bag: SuffixBag = inline.drain_to_external_with_vec(perm, slot, suffix, prealloc);
unsafe { install_drained_bag(new_bag, external_slot, slot, perm) }
}
#[inline(always)]
unsafe fn install_drained_bag(
mut new_bag: SuffixBag,
external_slot: &AtomicPtr<SuffixBagCell>,
slot: usize,
perm: &impl TreePermutation,
) -> *mut u8 {
let old_external: *mut SuffixBagCell = external_slot.load(AtomicOrdering::Relaxed);
merge_old_external_perm(&mut new_bag, old_external, slot, perm);
let new_cell: SuffixBagCell = SuffixBagCell::from_bag(new_bag);
let new_ptr: *mut SuffixBagCell = Box::into_raw(Box::new(new_cell));
external_slot.store(new_ptr, AtomicOrdering::Release);
old_external.cast::<u8>()
}
#[cold]
#[inline(never)]
unsafe fn drain_and_rebuild_init(
inline: &InlineSuffixBag,
external_slot: &AtomicPtr<SuffixBagCell>,
slot: usize,
suffix: &[u8],
guard: &LocalGuard<'_>,
) {
let mut new_bag: SuffixBag = inline.drain_to_external_init(slot, suffix);
let old_external: *mut SuffixBagCell = external_slot.load(AtomicOrdering::Relaxed);
merge_old_external_init(&mut new_bag, old_external, slot);
let new_cell: SuffixBagCell = SuffixBagCell::from_bag(new_bag);
let new_ptr: *mut SuffixBagCell = Box::into_raw(Box::new(new_cell));
external_slot.store(new_ptr, AtomicOrdering::Release);
if !old_external.is_null() {
unsafe {
guard.defer_retire(old_external, |ptr, _| {
drop(Box::from_raw(ptr));
});
}
}
}
fn merge_old_external_perm(
new_bag: &mut SuffixBag,
old_external: *mut SuffixBagCell,
skip_slot: usize,
perm: &impl TreePermutation,
) {
if old_external.is_null() {
return;
}
let old_ref: &SuffixBag = unsafe { (*old_external).as_ref() };
let size: usize = perm.size();
for i in 0..size {
let phys: usize = perm.get(i);
if phys == skip_slot {
continue;
}
if !new_bag.has_suffix(phys)
&& let Some(s) = old_ref.get(phys)
{
new_bag.assign(phys, s);
}
}
}
fn merge_old_external_init(new_bag: &mut SuffixBag, old_external: *mut SuffixBagCell, slot: usize) {
if old_external.is_null() {
return;
}
let old_ref: &SuffixBag = unsafe { (*old_external).as_ref() };
for s in 0..slot {
if !new_bag.has_suffix(s)
&& let Some(ext_suffix) = old_ref.get(s)
{
new_bag.assign(s, ext_suffix);
}
}
}