use fjall::WriteTransaction;
use std::cell::{Cell, RefCell};
use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
pub(crate) type OverlayValue = Box<[u8]>;
#[derive(Default)]
pub(crate) struct PartitionOverlay {
pub(crate) entries: BTreeMap<Vec<u8>, Option<OverlayValue>>,
size_delta: Option<i64>,
}
impl PartitionOverlay {
#[inline]
fn mark_dirty(&mut self) {
self.size_delta = None;
}
#[inline]
pub(crate) fn cached_size_delta(&self) -> Option<i64> {
self.size_delta
}
#[inline]
pub(crate) fn cache_size_delta(&mut self, delta: i64) {
self.size_delta = Some(delta);
}
}
#[derive(Default)]
struct TxOverlay {
partitions: HashMap<Arc<str>, PartitionOverlay>,
}
thread_local! {
static ACTIVE_TX: Cell<*mut ()> = const { Cell::new(std::ptr::null_mut()) };
static ACTIVE_OVERLAY: RefCell<Vec<TxOverlay>> = const { RefCell::new(Vec::new()) };
}
struct ScopeGuard {
prev: *mut (),
}
impl Drop for ScopeGuard {
fn drop(&mut self) {
ACTIVE_TX.with(|cell| cell.set(self.prev));
ACTIVE_OVERLAY.with(|stack| {
stack.borrow_mut().pop();
});
}
}
pub(crate) fn run_with_active<R>(tx: &mut WriteTransaction<'_>, f: impl FnOnce() -> R) -> R {
let ptr: *mut () = (tx as *mut WriteTransaction<'_>).cast();
let prev = ACTIVE_TX.with(|cell| cell.replace(ptr));
ACTIVE_OVERLAY.with(|stack| stack.borrow_mut().push(TxOverlay::default()));
let _guard = ScopeGuard { prev };
f()
}
#[inline]
pub(crate) fn in_scope() -> bool {
ACTIVE_TX.with(|cell| !cell.get().is_null())
}
pub(crate) fn with_active<R>(f: impl FnOnce(Option<&mut WriteTransaction<'_>>) -> R) -> R {
let raw = ACTIVE_TX.with(|cell| cell.get());
if raw.is_null() {
f(None)
} else {
let tx_ptr: *mut WriteTransaction<'_> = raw.cast();
let tx = unsafe { &mut *tx_ptr };
f(Some(tx))
}
}
pub(crate) fn record_insert(partition: &Arc<str>, key: Vec<u8>, value: OverlayValue) {
ACTIVE_OVERLAY.with(|stack| {
if let Some(active) = stack.borrow_mut().last_mut() {
let overlay = active
.partitions
.entry(partition.clone())
.or_default();
overlay.mark_dirty();
overlay.entries.insert(key, Some(value));
}
});
}
pub(crate) fn record_remove(partition: &Arc<str>, key: Vec<u8>) {
ACTIVE_OVERLAY.with(|stack| {
if let Some(active) = stack.borrow_mut().last_mut() {
let overlay = active
.partitions
.entry(partition.clone())
.or_default();
overlay.mark_dirty();
overlay.entries.insert(key, None);
}
});
}
pub(crate) fn with_partition_overlay<R>(
partition: &Arc<str>,
f: impl FnOnce(Option<&PartitionOverlay>) -> R,
) -> R {
ACTIVE_OVERLAY.with(|stack| {
let borrowed = stack.borrow();
let overlay = borrowed.last().and_then(|active| active.partitions.get(partition));
f(overlay)
})
}
pub(crate) fn with_partition_overlay_mut<R>(
partition: &Arc<str>,
f: impl FnOnce(Option<&mut PartitionOverlay>) -> R,
) -> R {
ACTIVE_OVERLAY.with(|stack| {
let mut borrowed = stack.borrow_mut();
let overlay = borrowed
.last_mut()
.and_then(|active| active.partitions.get_mut(partition));
f(overlay)
})
}