use crate::format::wal::{Op, OwnedEdge, OwnedSeg};
use compact_str::CompactString;
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::OnceLock;
const BASE_CAP: usize = 64;
const CHUNKS: usize = 20;
type Cell = OnceLock<(u64, Op)>;
pub(crate) struct Arena {
chunks: [OnceLock<Box<[Cell]>>; CHUNKS],
frontier: AtomicUsize,
}
impl std::fmt::Debug for Arena {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Arena").field("len", &self.len()).finish()
}
}
fn locate(i: usize) -> (usize, usize) {
let x = i / BASE_CAP + 1;
let k = (usize::BITS - 1 - x.leading_zeros()) as usize;
let before = BASE_CAP * ((1 << k) - 1);
(k, i - before)
}
impl Arena {
pub(crate) fn new() -> Self {
Self { chunks: std::array::from_fn(|_| OnceLock::new()), frontier: AtomicUsize::new(0) }
}
pub(crate) fn len(&self) -> usize {
self.frontier.load(Ordering::Acquire)
}
fn cell(&self, i: usize) -> Option<&Cell> {
let (k, off) = locate(i);
let chunk = self.chunks.get(k)?;
let chunk = chunk.get_or_init(|| {
(0..BASE_CAP << k).map(|_| OnceLock::new()).collect::<Vec<_>>().into_boxed_slice()
});
Some(&chunk[off])
}
pub(crate) fn publish(&self, seq: u64, op: Op) -> bool {
let i = self.frontier.load(Ordering::Relaxed);
let Some(cell) = self.cell(i) else { return false };
let ok = cell.set((seq, op)).is_ok();
debug_assert!(ok, "arena cell double-initialized");
self.frontier.store(i + 1, Ordering::Release);
true
}
pub(crate) fn last_seq(&self) -> u64 {
match self.len() {
0 => 0,
n => self.cell(n - 1).and_then(OnceLock::get).map_or(0, |e| e.0),
}
}
pub(crate) fn committed(&self, upto: usize) -> impl Iterator<Item = &(u64, Op)> + '_ {
(0..upto).map(move |i| {
self.cell(i).and_then(OnceLock::get).expect("cell below frontier is initialized")
})
}
}
#[derive(Debug, Default)]
pub(crate) struct OverlayNode {
pub(crate) core: Option<CoreFields>,
pub(crate) segs: Option<Vec<OwnedSeg>>,
pub(crate) edges: Option<Vec<OwnedEdge>>,
pub(crate) attrs: Option<Vec<(u8, CompactString)>>,
pub(crate) touch: Option<TouchFields>,
pub(crate) carried: Option<(u16, u16, u64)>,
pub(crate) pinned: Option<bool>,
pub(crate) removed: bool,
pub(crate) severed: bool,
}
#[derive(Debug, Clone)]
pub(crate) struct CoreFields {
pub(crate) content_hash: u64,
pub(crate) fetched_at_ms: u64,
pub(crate) flags: u16,
pub(crate) url: CompactString,
pub(crate) title: Option<CompactString>,
pub(crate) snippet: Option<CompactString>,
pub(crate) etag: Option<CompactString>,
}
#[derive(Debug, Clone)]
pub(crate) struct TouchFields {
pub(crate) checked_at_ms: u64,
pub(crate) content_hash: Option<u64>,
pub(crate) etag: Option<CompactString>,
pub(crate) interval_s: u32,
pub(crate) checks: u16,
pub(crate) changes: u16,
pub(crate) last_change_ms: u64,
pub(crate) tombstone: bool,
}
#[derive(Debug, Default)]
pub(crate) struct OverlayView {
pub(crate) map: HashMap<u64, OverlayNode>,
}
impl OverlayView {
pub(crate) fn build<'a>(ops: impl Iterator<Item = &'a (u64, Op)>) -> Self {
let mut map: HashMap<u64, OverlayNode> = HashMap::new();
for (_, op) in ops {
let entry = map.entry(op.key().0).or_default();
match op {
Op::UpsertNode {
content_hash,
fetched_at_ms,
flags,
url,
title,
snippet,
etag,
..
} => {
const PINNED: u16 = crate::format::base::nflags::PINNED;
let upsert_pin = *flags & PINNED != 0;
let prior_core_pin = entry.core.as_ref().is_some_and(|c| c.flags & PINNED != 0);
let folded_pin = match entry.pinned {
Some(prev) => Some(upsert_pin || prev),
None if upsert_pin || prior_core_pin => Some(true),
None => None,
};
entry.removed = false;
entry.carried = entry
.touch
.as_ref()
.map(|t| (t.checks, t.changes, t.last_change_ms))
.or(entry.carried);
entry.touch = None;
entry.core = Some(CoreFields {
content_hash: *content_hash,
fetched_at_ms: *fetched_at_ms,
flags: *flags,
url: url.clone(),
title: title.clone(),
snippet: snippet.clone(),
etag: etag.clone(),
});
entry.pinned = folded_pin;
}
Op::SetSegments { segs, .. } => entry.segs = Some(segs.clone()),
Op::SetEdges { edges, .. } => entry.edges = Some(edges.clone()),
Op::SetAttrs { attrs, .. } => entry.attrs = Some(attrs.clone()),
Op::Touch {
checked_at_ms,
content_hash,
etag,
interval_s,
checks,
changes,
last_change_ms,
tombstone,
..
} => {
entry.touch = Some(TouchFields {
checked_at_ms: *checked_at_ms,
content_hash: *content_hash,
etag: etag.clone(),
interval_s: *interval_s,
checks: *checks,
changes: *changes,
last_change_ms: *last_change_ms,
tombstone: *tombstone,
});
}
Op::Remove { .. } => {
*entry = OverlayNode { removed: true, severed: true, ..OverlayNode::default() };
}
Op::SetPinned { pinned, .. } => entry.pinned = Some(*pinned),
}
}
Self { map }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::key::UrlKey;
#[test]
fn locate_walks_chunk_boundaries() {
assert_eq!(locate(0), (0, 0));
assert_eq!(locate(63), (0, 63));
assert_eq!(locate(64), (1, 0));
assert_eq!(locate(191), (1, 127));
assert_eq!(locate(192), (2, 0));
}
#[test]
fn publish_then_read_below_frontier() {
let a = Arena::new();
for i in 0..200u64 {
assert!(a.publish(i + 1, Op::Remove { key: UrlKey(i) }));
}
assert_eq!(a.len(), 200);
let seen: Vec<u64> = a.committed(200).map(|(s, _)| *s).collect();
assert_eq!(seen, (1..=200).collect::<Vec<_>>());
}
#[test]
fn view_folds_last_writer_wins_and_remove_clears() {
let key = UrlKey(5);
let ops = [
(1, Op::SetPinned { key, pinned: true }),
(2, Op::Remove { key }),
(
3,
Op::UpsertNode {
key,
content_hash: 9,
fetched_at_ms: 10,
flags: 0,
url: "https://x.dev/a".into(),
title: None,
snippet: None,
etag: None,
},
),
];
let view = OverlayView::build(ops.iter());
let n = &view.map[&5];
assert!(!n.removed, "upsert resurrects");
assert!(n.pinned.is_none(), "remove cleared the earlier pin");
assert_eq!(n.core.as_ref().unwrap().content_hash, 9);
}
}