use std::hash::Hash;
use std::marker::PhantomData;
use crate::Key;
use crate::compaction::CompactionIndex;
use crate::config::Config;
use crate::const_map::{ConstMap, ConstMapShard};
use crate::durability::{Bitcask, Durability, Fixed};
use crate::error::{DbError, DbResult};
use crate::fixed::config::FixedConfig;
use crate::hook::{NoHook, TypedWriteHook, ZeroHookAdapter};
use crate::zero_tree::{from_value_bytes, to_bytes};
pub struct ZeroMap<
K: Key + Send + Sync + Hash + Eq,
const V: usize,
T: Copy = [u8; V],
H: TypedWriteHook<K, T> = NoHook,
D: Durability = Bitcask,
> {
inner: ConstMap<K, V, ZeroHookAdapter<K, T, H>, D>,
_marker: PhantomData<T>,
}
impl<K: Key + Send + Sync + Hash + Eq, const V: usize, T: Copy> ZeroMap<K, V, T, NoHook, Bitcask> {
pub fn open(path: impl AsRef<std::path::Path>, config: Config) -> DbResult<Self> {
const { assert!(size_of::<T>() == V) }
let adapter = ZeroHookAdapter {
inner: NoHook,
_marker: PhantomData,
};
Ok(Self {
inner: ConstMap::open_hooked(path, config, adapter)?,
_marker: PhantomData,
})
}
}
impl<K: Key + Send + Sync + Hash + Eq, const V: usize, T: Copy, H: TypedWriteHook<K, T>>
ZeroMap<K, V, T, H, Bitcask>
{
pub fn open_hooked(
path: impl AsRef<std::path::Path>,
config: Config,
hook: H,
) -> DbResult<Self> {
const { assert!(size_of::<T>() == V) }
let adapter = ZeroHookAdapter {
inner: hook,
_marker: PhantomData,
};
Ok(Self {
inner: ConstMap::open_hooked(path, config, adapter)?,
_marker: PhantomData,
})
}
pub fn close(self) -> DbResult<()> {
self.inner.close()
}
pub fn flush_buffers(&self) -> DbResult<()> {
self.inner.flush_buffers()
}
pub fn config(&self) -> &Config {
self.inner.config()
}
pub fn compact(&self) -> DbResult<usize> {
self.inner.compact()
}
pub fn sync_hints(&self) -> DbResult<()> {
self.inner.sync_hints()
}
pub fn as_inner(&self) -> &ConstMap<K, V, ZeroHookAdapter<K, T, H>, Bitcask> {
&self.inner
}
}
impl<K: Key + Send + Sync + Hash + Eq, const V: usize, T: Copy> ZeroMap<K, V, T, NoHook, Fixed> {
pub fn open(path: impl AsRef<std::path::Path>, config: FixedConfig) -> DbResult<Self> {
const { assert!(size_of::<T>() == V) }
let adapter = ZeroHookAdapter {
inner: NoHook,
_marker: PhantomData,
};
Ok(Self {
inner: ConstMap::open_with_hook(path, config, adapter)?,
_marker: PhantomData,
})
}
}
impl<K: Key + Send + Sync + Hash + Eq, const V: usize, T: Copy, H: TypedWriteHook<K, T>>
ZeroMap<K, V, T, H, Fixed>
{
pub fn open_with_hook(
path: impl AsRef<std::path::Path>,
config: FixedConfig,
hook: H,
) -> DbResult<Self> {
const { assert!(size_of::<T>() == V) }
let adapter = ZeroHookAdapter {
inner: hook,
_marker: PhantomData,
};
Ok(Self {
inner: ConstMap::open_with_hook(path, config, adapter)?,
_marker: PhantomData,
})
}
pub fn close(self) -> DbResult<()> {
self.inner.close()
}
}
impl<
K: Key + Send + Sync + Hash + Eq,
const V: usize,
T: Copy,
H: TypedWriteHook<K, T>,
D: Durability,
> ZeroMap<K, V, T, H, D>
{
pub fn get(&self, key: &K) -> Option<T> {
let bytes = self.inner.get(key)?;
Some(from_value_bytes::<V, T>(&bytes))
}
pub fn get_or_err(&self, key: &K) -> DbResult<T> {
self.get(key).ok_or(DbError::KeyNotFound)
}
pub fn contains(&self, key: &K) -> bool {
self.inner.contains(key)
}
pub fn put(&self, key: &K, value: &T) -> DbResult<Option<T>> {
let bytes = to_bytes::<V, T>(value);
self.inner
.put(key, &bytes)
.map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
}
pub fn insert(&self, key: &K, value: &T) -> DbResult<()> {
let bytes = to_bytes::<V, T>(value);
self.inner.insert(key, &bytes)
}
pub fn delete(&self, key: &K) -> DbResult<Option<T>> {
self.inner
.delete(key)
.map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
}
pub fn cas(&self, key: &K, expected: &T, new_value: &T) -> DbResult<()> {
let exp_bytes = to_bytes::<V, T>(expected);
let new_bytes = to_bytes::<V, T>(new_value);
self.inner.cas(key, &exp_bytes, &new_bytes)
}
pub fn update(&self, key: &K, f: impl FnOnce(&T) -> T) -> DbResult<Option<T>> {
self.inner
.update(key, |bytes| {
let val = from_value_bytes::<V, T>(bytes);
let new_val = f(&val);
to_bytes::<V, T>(&new_val)
})
.map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
}
pub fn fetch_update(&self, key: &K, f: impl FnOnce(&T) -> T) -> DbResult<Option<T>> {
self.inner
.fetch_update(key, |bytes| {
let val = from_value_bytes::<V, T>(bytes);
let new_val = f(&val);
to_bytes::<V, T>(&new_val)
})
.map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
}
pub fn atomic<R>(
&self,
shard_key: &K,
f: impl FnOnce(&mut ZeroMapShard<'_, K, V, T, D>) -> DbResult<R>,
) -> DbResult<R> {
self.inner.atomic(shard_key, |const_shard| {
let shard = unsafe {
&mut *(const_shard as *mut ConstMapShard<'_, K, V, ZeroHookAdapter<K, T, H>, D>
as *mut ZeroMapShard<'_, K, V, T, D>)
};
f(shard)
})
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn shard_for(&self, key: &K) -> usize {
self.inner.shard_for(key)
}
pub fn flush(&self) -> DbResult<()> {
self.inner.flush()
}
pub fn migrate(&self, f: impl Fn(&K, &T) -> crate::MigrateAction<T>) -> DbResult<usize> {
self.inner.migrate(|key, bytes| {
let val: T = from_value_bytes(bytes);
match f(key, &val) {
crate::MigrateAction::Keep => crate::MigrateAction::Keep,
crate::MigrateAction::Update(new) => {
crate::MigrateAction::Update(to_bytes::<V, T>(&new))
}
crate::MigrateAction::Delete => crate::MigrateAction::Delete,
}
})
}
pub(crate) fn replay_init(&self) {
self.inner.replay_init();
}
}
impl<
K: Key + Send + Sync + Hash + Eq,
const V: usize,
T: Copy + Send + Sync,
H: TypedWriteHook<K, T>,
> CompactionIndex<K> for ZeroMap<K, V, T, H, Bitcask>
{
fn update_if_match(
&self,
key: &K,
old_loc: crate::disk_loc::DiskLoc,
new_loc: crate::disk_loc::DiskLoc,
) -> bool {
self.inner.update_if_match(key, old_loc, new_loc)
}
fn contains_key(&self, key: &K) -> bool {
self.contains(key)
}
}
#[repr(transparent)]
pub struct ZeroMapShard<
'a,
K: Key + Send + Sync + Hash + Eq,
const V: usize,
T: Copy = [u8; V],
D: Durability = Bitcask,
> {
inner: ConstMapShard<'a, K, V, NoHook, D>,
_marker: PhantomData<T>,
}
impl<K: Key + Send + Sync + Hash + Eq, const V: usize, T: Copy, D: Durability>
ZeroMapShard<'_, K, V, T, D>
{
pub fn put(&mut self, key: &K, value: &T) -> DbResult<Option<T>> {
let bytes = to_bytes::<V, T>(value);
self.inner
.put(key, &bytes)
.map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
}
pub fn insert(&mut self, key: &K, value: &T) -> DbResult<()> {
let bytes = to_bytes::<V, T>(value);
self.inner.insert(key, &bytes)
}
pub fn delete(&mut self, key: &K) -> DbResult<Option<T>> {
self.inner
.delete(key)
.map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
}
pub fn get(&self, key: &K) -> Option<T> {
let bytes = self.inner.get(key)?;
Some(from_value_bytes::<V, T>(&bytes))
}
pub fn get_or_err(&self, key: &K) -> DbResult<T> {
self.get(key).ok_or(DbError::KeyNotFound)
}
pub fn contains(&self, key: &K) -> bool {
self.inner.contains(key)
}
}
#[cfg(feature = "armour")]
impl<T, const V: usize, H> crate::armour::collection::Collection
for ZeroMap<T::SelfId, V, T, H, Bitcask>
where
T: crate::CollectionMeta + Copy + Send + Sync,
H: crate::hook::TypedWriteHook<T::SelfId, T>,
T::SelfId: crate::Key + Send + Sync + std::hash::Hash + Eq,
{
fn name(&self) -> &str {
T::NAME
}
fn len(&self) -> usize {
self.len()
}
fn compact(&self) -> crate::DbResult<usize> {
self.compact()
}
}
#[cfg(feature = "armour")]
impl<T, const V: usize, H> crate::armour::collection::Collection
for ZeroMap<T::SelfId, V, T, H, Fixed>
where
T: crate::CollectionMeta + Copy + Send + Sync,
H: crate::hook::TypedWriteHook<T::SelfId, T>,
T::SelfId: crate::Key + Send + Sync + std::hash::Hash + Eq,
{
fn name(&self) -> &str {
T::NAME
}
fn len(&self) -> usize {
self.len()
}
fn compact(&self) -> crate::DbResult<usize> {
Ok(0)
}
}