use crate::scan::{EntryRec, NameRange, PackedMeta, Span};
use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicU64, Ordering};
#[inline]
unsafe fn assume_init<T>(s: &[MaybeUninit<T>]) -> &[T] {
unsafe { &*(std::ptr::from_ref(s) as *const [T]) }
}
static NEXT_EPOCH: AtomicU64 = AtomicU64::new(1);
#[derive(Debug)]
pub struct Win<T> {
off: usize,
cap: usize,
len: usize,
spilled: bool,
spill: Vec<T>,
}
impl<T> Default for Win<T> {
fn default() -> Self {
Self {
off: 0,
cap: 0,
len: 0,
spilled: false,
spill: Vec::new(),
}
}
}
impl<T> Win<T> {
fn clear(&mut self) {
self.len = 0;
self.spilled = false;
self.spill.clear();
}
fn spill_bytes(&self) -> usize {
self.spill.capacity() * size_of::<T>()
}
}
pub struct Table<'w, T: Copy> {
win: &'w mut [MaybeUninit<T>],
len: usize,
state: &'w mut Win<T>,
}
impl<'w, T: Copy> Table<'w, T> {
fn new(state: &'w mut Win<T>, window: &'w mut [MaybeUninit<T>]) -> Self {
state.clear();
Self {
win: window,
len: 0,
state,
}
}
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub fn push(&mut self, v: T) {
if let Some(slot) = self.win.get_mut(self.len) {
slot.write(v);
self.len += 1;
} else {
self.push_spilled(v);
}
}
#[cold]
#[inline(never)]
fn push_spilled(&mut self, v: T) {
if !self.state.spilled {
let win = std::mem::take(&mut self.win);
let written = unsafe { assume_init(&win[..self.len]) };
self.state.spill.extend_from_slice(written);
self.state.spilled = true;
}
self.state.spill.push(v);
self.len += 1;
}
}
impl<T: Copy> Drop for Table<'_, T> {
fn drop(&mut self) {
self.state.len = self.len;
}
}
macro_rules! tables {
($($name:ident: $t:ty),* $(,)?) => {
pub struct Index<'w> {
$(pub $name: Table<'w, $t>,)*
}
#[derive(Debug, Default)]
pub struct SegTabs {
epoch: u64,
$($name: Win<$t>,)*
}
impl SegTabs {
pub fn clear(&mut self) {
$(self.$name.clear();)*
}
pub fn spills(&self) -> usize {
0 $(+ usize::from(self.$name.spilled))*
}
pub fn spill_bytes(&self) -> usize {
0 $(+ self.$name.spill_bytes())*
}
pub fn open<'w>(&'w mut self, wins: Wins<'w>) -> Index<'w> {
let ok = self.epoch != 0 && self.epoch == wins.epoch;
Index {
$($name: {
let w = &mut self.$name;
let window = if ok && wins.base.$name == w.off && wins.$name.len() >= w.cap {
&mut wins.$name[..w.cap]
} else {
&mut []
};
Table::new(w, window)
},)*
}
}
}
pub struct Wins<'w> {
epoch: u64,
base: Offsets,
$($name: &'w mut [MaybeUninit<$t>],)*
}
#[derive(Clone, Copy, Default)]
struct Offsets {
$($name: usize,)*
}
impl<'w> Wins<'w> {
pub fn none() -> Self {
Self {
epoch: 0,
base: Offsets::default(),
$($name: &mut [],)*
}
}
pub fn split_at(self, at: &SegTabs) -> (Self, Self) {
let mut left = Self::none();
let mut right = Self::none();
left.epoch = self.epoch;
right.epoch = self.epoch;
left.base = self.base;
$({
let k = at.$name.off.saturating_sub(self.base.$name).min(self.$name.len());
let (l, r) = self.$name.split_at_mut(k);
left.$name = l;
right.$name = r;
right.base.$name = self.base.$name + k;
})*
(left, right)
}
}
#[derive(Default)]
pub struct Slabs {
epoch: u64,
cursor: Offsets,
$($name: Slab<$t>,)*
}
impl Slabs {
pub fn begin(&mut self, want: [usize; 7]) {
let [$($name),*] = want;
if self.epoch == 0 || $(self.$name.0.len() < $name)||* {
$(if self.$name.0.len() < $name {
self.$name = Slab::with_len($name);
})*
self.epoch = NEXT_EPOCH.fetch_add(1, Ordering::Relaxed);
}
self.cursor = Offsets::default();
}
pub fn place(&mut self, tabs: &mut SegTabs, want: [usize; 7]) {
let [$($name),*] = want;
tabs.epoch = self.epoch;
$({
let w = &mut tabs.$name;
let off = self.cursor.$name;
w.off = off;
w.cap = $name.min(self.$name.0.len().saturating_sub(off));
w.clear();
self.cursor.$name = off + w.cap;
})*
}
pub fn wins(&mut self) -> Wins<'_> {
Wins {
epoch: self.epoch,
base: Offsets::default(),
$($name: &mut self.$name.0[..],)*
}
}
pub fn wins_for(&mut self, tabs: &SegTabs) -> Wins<'_> {
if tabs.epoch != self.epoch {
return Wins::none();
}
let mut base = Offsets::default();
$(base.$name = tabs.$name.off;)*
Wins {
epoch: self.epoch,
base,
$($name: {
let w = &tabs.$name;
self.$name.0.get_mut(w.off..w.off + w.cap).unwrap_or(&mut [])
},)*
}
}
pub fn capacity_bytes(&self) -> usize {
0 $(+ self.$name.0.capacity() * size_of::<$t>())*
}
$(
#[inline]
pub fn $name<'a>(&'a self, tabs: &'a SegTabs) -> &'a [$t] {
self.$name.get(self.epoch, tabs.epoch, &tabs.$name)
}
)*
}
};
}
tables! {
entries: EntryRec,
descs: NameRange,
deps: NameRange,
peers: NameRange,
bins: NameRange,
dmeta: (Span, PackedMeta),
pmeta: (Span, PackedMeta),
}
pub struct Slab<T>(Vec<MaybeUninit<T>>);
impl<T> Default for Slab<T> {
fn default() -> Self {
Self(Vec::new())
}
}
impl<T> Slab<T> {
fn with_len(n: usize) -> Self {
let mut v = Vec::new();
if v.try_reserve_exact(n).is_ok() {
v.resize_with(n, MaybeUninit::uninit);
}
Self(v)
}
#[inline]
fn get<'a>(&'a self, epoch: u64, tabs_epoch: u64, w: &'a Win<T>) -> &'a [T] {
if w.spilled {
return &w.spill;
}
if epoch == 0 || tabs_epoch != epoch {
return &[];
}
match self.0.get(w.off..w.off.saturating_add(w.len)) {
Some(s) => unsafe { assume_init(s) },
None => &[],
}
}
}
pub const fn wanted(bytes: usize) -> [usize; 7] {
let entries = bytes / 192 + 16;
[
entries,
entries * 2 + bytes / 256 + 64,
bytes / 32 + 16,
bytes / 256 + 16,
bytes / 256 + 16,
bytes / 256 + 16,
bytes / 256 + 16,
]
}
#[cfg(test)]
mod tests {
use super::*;
fn rec(i: u32) -> NameRange {
NameRange {
name: Span { start: i, len: 1 },
range: Span { start: i, len: 2 },
}
}
fn names(t: &[NameRange]) -> Vec<u32> {
t.iter().map(|r| r.name.start).collect()
}
#[test]
fn windows_fill_spill_and_read_back() {
let mut slabs = Slabs::default();
let mut segs: Vec<SegTabs> = (0..3).map(|_| SegTabs::default()).collect();
slabs.begin([0, 0, 6, 0, 0, 0, 0]);
for s in &mut segs {
slabs.place(s, [0, 0, 2, 0, 0, 0, 0]);
}
{
let (a, rest) = slabs.wins().split_at(&segs[1]);
let (b, c) = rest.split_at(&segs[2]);
let [s0, s1, s2] = &mut segs[..] else {
unreachable!()
};
let (mut i0, mut i1, mut i2) = (s0.open(a), s1.open(b), s2.open(c));
i0.deps.push(rec(1));
for i in 10..14 {
i1.deps.push(rec(i)); }
i2.deps.push(rec(20));
i2.deps.push(rec(21));
}
assert_eq!(names(slabs.deps(&segs[0])), [1]);
assert_eq!(names(slabs.deps(&segs[1])), [10, 11, 12, 13]);
assert_eq!(names(slabs.deps(&segs[2])), [20, 21]);
assert_eq!(segs.iter().map(SegTabs::spills).sum::<usize>(), 1);
{
let wins = slabs.wins_for(&segs[1]);
segs[1].open(wins).deps.push(rec(30));
}
assert_eq!(names(slabs.deps(&segs[1])), [30]);
assert_eq!(segs[1].spills(), 0);
slabs.begin([0, 0, 100, 0, 0, 0, 0]);
let mut fresh = SegTabs::default();
slabs.place(&mut fresh, [0, 0, 100, 0, 0, 0, 0]);
assert!(slabs.deps(&segs[0]).is_empty());
assert!(slabs.deps(&fresh).is_empty());
}
#[test]
fn foreign_or_mismatched_windows_are_not_used() {
let mut a = Slabs::default();
let mut b = Slabs::default();
a.begin([0, 0, 4, 0, 0, 0, 0]);
b.begin([0, 0, 4, 0, 0, 0, 0]);
let mut t = SegTabs::default();
a.place(&mut t, [0, 0, 4, 0, 0, 0, 0]);
t.open(b.wins()).deps.push(rec(7));
assert_eq!(t.spills(), 1);
assert_eq!(names(a.deps(&t)), [7]);
a.place(&mut t, [0, 0, 4, 0, 0, 0, 0]);
assert_eq!(t.spills(), 0);
assert!(b.deps(&t).is_empty());
}
#[test]
fn exhausted_or_failed_slabs_give_empty_windows() {
let mut slabs = Slabs::default();
slabs.begin([0, 0, 3, 0, 0, 0, 0]);
let (mut first, mut second) = (SegTabs::default(), SegTabs::default());
slabs.place(&mut first, [0, 0, 3, 0, 0, 0, 0]);
slabs.place(&mut second, [0, 0, 3, 0, 0, 0, 0]);
{
let (w1, w2) = slabs.wins().split_at(&second);
let (mut i1, mut i2) = (first.open(w1), second.open(w2));
i1.deps.push(rec(1));
i2.deps.push(rec(2));
}
assert_eq!((first.spills(), second.spills()), (0, 1));
assert_eq!(names(slabs.deps(&second)), [2]);
let mut big = Slabs::default();
big.begin([0, 0, usize::MAX / 2, 0, 0, 0, 0]);
let mut t = SegTabs::default();
big.place(&mut t, [0, 0, 10, 0, 0, 0, 0]);
{
let w = big.wins_for(&t);
t.open(w).deps.push(rec(3));
}
assert_eq!(names(big.deps(&t)), [3]);
}
}