use whasher::{HashMap, new_hash_map};
use wval::{KeyTag, META_VALUE_SIZE, MetaValue, NamespaceDbCodec};
use crate::host::CompactStore;
pub(super) struct MetaDeathScope {
meta_owner: HashMap<Box<[u8]>, u64>,
death_addr: HashMap<u64, u64>,
}
impl MetaDeathScope {
pub(super) fn new() -> Self {
Self {
meta_owner: new_hash_map(),
death_addr: new_hash_map(),
}
}
fn mark_death(&mut self, key_id: u64, addr: u64) {
self.death_addr.entry(key_id).or_insert(addr);
}
pub(super) fn observe<S: CompactStore>(
&mut self,
store: &S,
is_tombstone: bool,
key: &[u8],
val: &[u8],
addr: u64,
) {
if !matches!(NamespaceDbCodec::decode_tag(key), Some(KeyTag::Meta)) {
return;
}
if is_tombstone {
if let Some(&owner) = self.meta_owner.get(key) {
self.mark_death(owner, addr);
}
return;
}
if val.len() < META_VALUE_SIZE {
return;
}
let Ok(meta) = MetaValue::from_slice(val) else {
return;
};
if self.meta_owner.get(key) != Some(&meta.key_id) {
self.meta_owner.insert(Box::from(key), meta.key_id);
}
if meta.size == 0 {
self.mark_death(meta.key_id, addr);
} else if let Some(&d) = self.death_addr.get(&meta.key_id) {
if addr >= d {
log::error!(
"key_id {:x} 的存活 meta 写入 {addr:#x} 出现在死亡登记 {d:#x} 之后:非尾部分配路径破坏墓碑必在尾部不变量,死条目回收前提失效",
meta.key_id
);
}
debug_assert!(
addr < d,
"key_id {:x} 墓碑必在尾部不变量破坏(详见 error 日志)",
meta.key_id
);
}
if store
.get_key_id_meta(meta.key_id)
.is_none_or(|(ver, _)| meta.version > ver)
{
store.update_key_id_meta(meta.key_id, meta.version, meta.size > 0);
}
}
pub(super) fn collect_dead<S: CompactStore>(&self, store: &S, actual_until: u64) {
for (&key_id, &da) in &self.death_addr {
if da <= actual_until
&& store
.get_key_id_meta(key_id)
.is_some_and(|(_, is_alive)| !is_alive)
{
store.remove_key_id_meta(key_id);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn meta_death_scope_mark_death() {
let mut scope = MetaDeathScope::new();
scope.mark_death(100, 1024);
scope.mark_death(100, 2048);
assert_eq!(scope.death_addr.get(&100), Some(&1024));
}
}