use crate::Key;
pub trait WriteHook<K: Key>: Send + Sync {
const NEEDS_OLD_VALUE: bool = true;
const NEEDS_INIT: bool = false;
const NEEDS_WRITE: bool = true;
fn on_write(&self, key: &K, old: Option<&[u8]>, new: Option<&[u8]>);
fn on_init(&self, _key: &K, _value: &[u8]) {}
}
pub struct NoHook;
impl<K: Key> WriteHook<K> for NoHook {
const NEEDS_OLD_VALUE: bool = false;
const NEEDS_INIT: bool = false;
const NEEDS_WRITE: bool = false;
#[inline(always)]
fn on_write(&self, _key: &K, _old: Option<&[u8]>, _new: Option<&[u8]>) {}
#[inline(always)]
fn on_init(&self, _key: &K, _value: &[u8]) {}
}
#[cfg(feature = "typed-tree")]
pub trait TypedWriteHook<K: Key, T>: Send + Sync {
const NEEDS_OLD_VALUE: bool = true;
const NEEDS_INIT: bool = false;
const NEEDS_WRITE: bool = true;
fn on_write(&self, key: &K, old: Option<&T>, new: Option<&T>);
fn on_init(&self, _key: &K, _value: &T) {}
}
#[cfg(feature = "typed-tree")]
impl<K: Key, T> TypedWriteHook<K, T> for NoHook {
const NEEDS_OLD_VALUE: bool = false;
const NEEDS_INIT: bool = false;
const NEEDS_WRITE: bool = false;
#[inline(always)]
fn on_write(&self, _key: &K, _old: Option<&T>, _new: Option<&T>) {}
#[inline(always)]
fn on_init(&self, _key: &K, _value: &T) {}
}
#[cfg(feature = "typed-tree")]
pub struct ZeroHookAdapter<K, T, H> {
pub(crate) inner: H,
pub(crate) _marker: std::marker::PhantomData<fn() -> (K, T)>,
}
#[cfg(feature = "typed-tree")]
unsafe impl<K, T, H: Send> Send for ZeroHookAdapter<K, T, H> {}
#[cfg(feature = "typed-tree")]
unsafe impl<K, T, H: Sync> Sync for ZeroHookAdapter<K, T, H> {}
#[cfg(feature = "typed-tree")]
impl<K: Key, T: Copy + zerocopy::FromBytes, H: TypedWriteHook<K, T>> WriteHook<K>
for ZeroHookAdapter<K, T, H>
{
const NEEDS_OLD_VALUE: bool = H::NEEDS_OLD_VALUE;
const NEEDS_INIT: bool = H::NEEDS_INIT;
const NEEDS_WRITE: bool = H::NEEDS_WRITE;
#[inline]
fn on_write(&self, key: &K, old: Option<&[u8]>, new: Option<&[u8]>) {
if !H::NEEDS_WRITE {
return;
}
let read = |b: &[u8]| -> T {
debug_assert_eq!(b.len(), size_of::<T>());
zerocopy::FromBytes::read_from_bytes(b).expect("slice len == size_of::<T>()")
};
let old_val = old.map(read);
let new_val = new.map(read);
self.inner.on_write(key, old_val.as_ref(), new_val.as_ref());
}
#[inline]
fn on_init(&self, key: &K, value: &[u8]) {
debug_assert_eq!(value.len(), size_of::<T>());
let val: T =
zerocopy::FromBytes::read_from_bytes(value).expect("slice len == size_of::<T>()");
self.inner.on_init(key, &val);
}
}
#[cfg(all(feature = "typed-tree", feature = "var-collections"))]
pub struct VarTypedHookAdapter<K, T, C, H> {
pub(crate) inner: H,
pub(crate) codec: C,
pub(crate) _marker: std::marker::PhantomData<fn() -> (K, T)>,
}
#[cfg(all(feature = "typed-tree", feature = "var-collections"))]
unsafe impl<K, T, C: Send, H: Send> Send for VarTypedHookAdapter<K, T, C, H> {}
#[cfg(all(feature = "typed-tree", feature = "var-collections"))]
unsafe impl<K, T, C: Sync, H: Sync> Sync for VarTypedHookAdapter<K, T, C, H> {}
#[cfg(all(feature = "typed-tree", feature = "var-collections"))]
impl<K, T, C, H> WriteHook<K> for VarTypedHookAdapter<K, T, C, H>
where
K: Key,
T: Send + Sync,
C: crate::codec::Codec<T>,
H: TypedWriteHook<K, T>,
{
const NEEDS_OLD_VALUE: bool = H::NEEDS_OLD_VALUE;
const NEEDS_INIT: bool = H::NEEDS_INIT;
const NEEDS_WRITE: bool = H::NEEDS_WRITE;
#[inline]
fn on_write(&self, key: &K, old: Option<&[u8]>, new: Option<&[u8]>) {
if !H::NEEDS_WRITE {
return;
}
let old_t = old.and_then(|b| {
self.codec
.decode_from(b)
.map_err(|_| {
tracing::warn!(
side = "old",
value_len = b.len(),
"var_typed hook adapter: on_write decode failed"
);
})
.ok()
});
let new_t = new.and_then(|b| {
self.codec
.decode_from(b)
.map_err(|_| {
tracing::warn!(
side = "new",
value_len = b.len(),
"var_typed hook adapter: on_write decode failed"
);
})
.ok()
});
self.inner.on_write(key, old_t.as_ref(), new_t.as_ref());
}
#[inline]
fn on_init(&self, key: &K, value: &[u8]) {
match self.codec.decode_from(value) {
Ok(val) => self.inner.on_init(key, &val),
Err(_) => {
tracing::warn!(
key_len = std::mem::size_of::<K>(),
value_len = value.len(),
"var_typed hook adapter: on_init decode failed, entry skipped"
);
}
}
}
}