1use std::marker::PhantomData;
2use std::ops::Bound;
3
4use crate::Key;
5use crate::compaction::CompactionIndex;
6use crate::config::Config;
7use crate::const_tree::{ConstIter, ConstShard, ConstTree};
8use crate::disk_loc::DiskLoc;
9use crate::durability::{Bitcask, Durability, Fixed};
10use crate::error::{DbError, DbResult};
11use crate::fixed::config::FixedConfig;
12use crate::hook::{NoHook, TypedWriteHook, ZeroHookAdapter};
13use crate::key::Location;
14
15pub struct ZeroTree<
71 K: Key,
72 const V: usize,
73 T: Copy = [u8; V],
74 H: TypedWriteHook<K, T> = NoHook,
75 D: Durability = Bitcask,
76> {
77 inner: ConstTree<K, V, ZeroHookAdapter<K, T, H>, D>,
78 _marker: PhantomData<T>,
79}
80
81impl<K: Key, const V: usize, T: Copy> ZeroTree<K, V, T, NoHook, Bitcask> {
86 pub fn open(path: impl AsRef<std::path::Path>, config: Config) -> DbResult<Self> {
89 const { assert!(size_of::<T>() == V) }
90 let adapter = ZeroHookAdapter {
91 inner: NoHook,
92 _marker: PhantomData,
93 };
94 Ok(Self {
95 inner: ConstTree::open_hooked(path, config, adapter)?,
96 _marker: PhantomData,
97 })
98 }
99}
100
101impl<K: Key, const V: usize, T: Copy, H: TypedWriteHook<K, T>> ZeroTree<K, V, T, H, Bitcask> {
102 pub fn open_hooked(
104 path: impl AsRef<std::path::Path>,
105 config: Config,
106 hook: H,
107 ) -> DbResult<Self> {
108 const { assert!(size_of::<T>() == V) }
109 let adapter = ZeroHookAdapter {
110 inner: hook,
111 _marker: PhantomData,
112 };
113 Ok(Self {
114 inner: ConstTree::open_hooked(path, config, adapter)?,
115 _marker: PhantomData,
116 })
117 }
118
119 pub fn close(self) -> DbResult<()> {
121 self.inner.close()
122 }
123
124 pub fn flush_buffers(&self) -> DbResult<()> {
126 self.inner.flush_buffers()
127 }
128
129 pub fn config(&self) -> &Config {
131 self.inner.config()
132 }
133
134 pub fn compact(&self) -> DbResult<usize> {
136 self.inner.compact()
137 }
138
139 pub fn sync_hints(&self) -> DbResult<()> {
141 self.inner.sync_hints()
142 }
143
144 pub fn migrate(&self, f: impl Fn(&K, &T) -> crate::MigrateAction<T>) -> DbResult<usize> {
154 self.inner.migrate(|key, bytes| {
155 let val: T = from_value_bytes(bytes);
156 match f(key, &val) {
157 crate::MigrateAction::Keep => crate::MigrateAction::Keep,
158 crate::MigrateAction::Update(new) => {
159 crate::MigrateAction::Update(to_bytes::<V, T>(&new))
160 }
161 crate::MigrateAction::Delete => crate::MigrateAction::Delete,
162 }
163 })
164 }
165
166 pub(crate) fn replay_init(&self) {
168 self.inner.replay_init();
169 }
170
171 pub fn as_inner(&self) -> &ConstTree<K, V, ZeroHookAdapter<K, T, H>, Bitcask> {
173 &self.inner
174 }
175}
176
177impl<K: Key, const V: usize, T: Copy> ZeroTree<K, V, T, NoHook, Fixed> {
182 pub fn open(path: impl AsRef<std::path::Path>, config: FixedConfig) -> DbResult<Self> {
185 const { assert!(size_of::<T>() == V) }
186 let adapter = ZeroHookAdapter {
187 inner: NoHook,
188 _marker: PhantomData,
189 };
190 Ok(Self {
191 inner: ConstTree::open_with_hook(path, config, adapter)?,
192 _marker: PhantomData,
193 })
194 }
195}
196
197impl<K: Key, const V: usize, T: Copy, H: TypedWriteHook<K, T>> ZeroTree<K, V, T, H, Fixed> {
198 pub fn open_with_hook(
200 path: impl AsRef<std::path::Path>,
201 config: FixedConfig,
202 hook: H,
203 ) -> DbResult<Self> {
204 const { assert!(size_of::<T>() == V) }
205 let adapter = ZeroHookAdapter {
206 inner: hook,
207 _marker: PhantomData,
208 };
209 Ok(Self {
210 inner: ConstTree::open_with_hook(path, config, adapter)?,
211 _marker: PhantomData,
212 })
213 }
214
215 pub fn close(self) -> DbResult<()> {
217 self.inner.close()
218 }
219}
220
221impl<K: Key, const V: usize, T: Copy, H: TypedWriteHook<K, T>, D: Durability>
226 ZeroTree<K, V, T, H, D>
227{
228 pub fn get(&self, key: &K) -> Option<T> {
232 let bytes = self.inner.get(key)?;
233 Some(from_value_bytes::<V, T>(&bytes))
234 }
235
236 pub fn get_or_err(&self, key: &K) -> DbResult<T> {
238 self.get(key).ok_or(DbError::KeyNotFound)
239 }
240
241 pub fn contains(&self, key: &K) -> bool {
243 self.inner.contains(key)
244 }
245
246 pub fn first(&self) -> Option<(K, T)> {
250 self.inner
251 .first()
252 .map(|(k, v)| (k, from_value_bytes::<V, T>(&v)))
253 }
254
255 pub fn last(&self) -> Option<(K, T)> {
258 self.inner
259 .last()
260 .map(|(k, v)| (k, from_value_bytes::<V, T>(&v)))
261 }
262
263 pub fn put(&self, key: &K, value: &T) -> DbResult<Option<T>> {
267 let bytes = to_bytes::<V, T>(value);
268 self.inner
269 .put(key, &bytes)
270 .map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
271 }
272
273 pub fn insert(&self, key: &K, value: &T) -> DbResult<()> {
276 let bytes = to_bytes::<V, T>(value);
277 self.inner.insert(key, &bytes)
278 }
279
280 pub fn delete(&self, key: &K) -> DbResult<Option<T>> {
282 self.inner
283 .delete(key)
284 .map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
285 }
286
287 pub fn cas(&self, key: &K, expected: &T, new_value: &T) -> DbResult<()> {
291 let exp_bytes = to_bytes::<V, T>(expected);
292 let new_bytes = to_bytes::<V, T>(new_value);
293 self.inner.cas(key, &exp_bytes, &new_bytes)
294 }
295
296 pub fn update(&self, key: &K, f: impl FnOnce(&T) -> T) -> DbResult<Option<T>> {
300 self.inner
301 .update(key, |bytes| {
302 let val = from_value_bytes::<V, T>(bytes);
303 let new_val = f(&val);
304 to_bytes::<V, T>(&new_val)
305 })
306 .map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
307 }
308
309 pub fn fetch_update(&self, key: &K, f: impl FnOnce(&T) -> T) -> DbResult<Option<T>> {
311 self.inner
312 .fetch_update(key, |bytes| {
313 let val = from_value_bytes::<V, T>(bytes);
314 let new_val = f(&val);
315 to_bytes::<V, T>(&new_val)
316 })
317 .map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
318 }
319
320 pub fn atomic<R>(
326 &self,
327 shard_key: &K,
328 f: impl FnOnce(&mut ZeroShard<'_, K, V, T, D>) -> DbResult<R>,
329 ) -> DbResult<R> {
330 self.inner.atomic(shard_key, |const_shard| {
331 let shard = unsafe {
334 &mut *(const_shard as *mut ConstShard<'_, K, V, ZeroHookAdapter<K, T, H>, D>
335 as *mut ZeroShard<'_, K, V, T, D>)
336 };
337 f(shard)
338 })
339 }
340
341 pub fn prefix_iter(&self, prefix: &[u8]) -> ZeroIter<'_, K, V, T, D::Loc> {
348 ZeroIter {
349 inner: self.inner.prefix_iter(prefix),
350 _marker: PhantomData,
351 }
352 }
353
354 pub fn iter(&self) -> ZeroIter<'_, K, V, T, D::Loc> {
359 ZeroIter {
360 inner: self.inner.iter(),
361 _marker: PhantomData,
362 }
363 }
364
365 pub fn range(&self, start: &K, end: &K) -> ZeroIter<'_, K, V, T, D::Loc> {
370 self.range_bounds(Bound::Included(start), Bound::Excluded(end))
371 }
372
373 pub fn range_bounds(&self, start: Bound<&K>, end: Bound<&K>) -> ZeroIter<'_, K, V, T, D::Loc> {
381 ZeroIter {
382 inner: self.inner.range_bounds(start, end),
383 _marker: PhantomData,
384 }
385 }
386
387 pub fn len(&self) -> usize {
390 self.inner.len()
391 }
392
393 pub fn is_empty(&self) -> bool {
394 self.inner.is_empty()
395 }
396
397 pub fn shard_for(&self, key: &K) -> usize {
398 self.inner.shard_for(key)
399 }
400
401 pub fn flush(&self) -> DbResult<()> {
403 self.inner.flush()
404 }
405}
406
407impl<K: Key, const V: usize, T: Copy + Send + Sync, H: TypedWriteHook<K, T>> CompactionIndex<K>
408 for ZeroTree<K, V, T, H, Bitcask>
409{
410 fn update_if_match(
411 &self,
412 key: &K,
413 old_loc: crate::disk_loc::DiskLoc,
414 new_loc: crate::disk_loc::DiskLoc,
415 ) -> bool {
416 self.inner.update_if_match(key, old_loc, new_loc)
417 }
418
419 fn contains_key(&self, key: &K) -> bool {
420 self.contains(key)
421 }
422}
423
424pub struct ZeroIter<'a, K: Key, const V: usize, T = [u8; V], L: Location = DiskLoc> {
431 inner: ConstIter<'a, K, V, L>,
432 _marker: PhantomData<T>,
433}
434
435impl<'a, K: Key, const V: usize, T: Copy, L: Location> Iterator for ZeroIter<'a, K, V, T, L> {
436 type Item = (K, T);
437
438 fn next(&mut self) -> Option<Self::Item> {
439 self.inner
440 .next()
441 .map(|(k, v)| (k, from_value_bytes::<V, T>(&v)))
442 }
443}
444
445impl<'a, K: Key, const V: usize, T: Copy, L: Location> DoubleEndedIterator
446 for ZeroIter<'a, K, V, T, L>
447{
448 fn next_back(&mut self) -> Option<Self::Item> {
449 self.inner
450 .next_back()
451 .map(|(k, v)| (k, from_value_bytes::<V, T>(&v)))
452 }
453}
454
455#[repr(transparent)]
462pub struct ZeroShard<'a, K: Key, const V: usize, T: Copy = [u8; V], D: Durability = Bitcask> {
463 inner: ConstShard<'a, K, V, NoHook, D>,
467 _marker: PhantomData<T>,
468}
469
470impl<K: Key, const V: usize, T: Copy, D: Durability> ZeroShard<'_, K, V, T, D> {
471 pub fn put(&mut self, key: &K, value: &T) -> DbResult<Option<T>> {
472 let bytes = to_bytes::<V, T>(value);
473 self.inner
474 .put(key, &bytes)
475 .map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
476 }
477
478 pub fn insert(&mut self, key: &K, value: &T) -> DbResult<()> {
479 let bytes = to_bytes::<V, T>(value);
480 self.inner.insert(key, &bytes)
481 }
482
483 pub fn delete(&mut self, key: &K) -> DbResult<Option<T>> {
484 self.inner
485 .delete(key)
486 .map(|opt| opt.map(|b| from_value_bytes::<V, T>(&b)))
487 }
488
489 pub fn get(&self, key: &K) -> Option<T> {
490 let bytes = self.inner.get(key)?;
491 Some(from_value_bytes::<V, T>(&bytes))
492 }
493
494 pub fn get_or_err(&self, key: &K) -> DbResult<T> {
495 self.get(key).ok_or(DbError::KeyNotFound)
496 }
497
498 pub fn contains(&self, key: &K) -> bool {
499 self.inner.contains(key)
500 }
501}
502
503#[inline(always)]
508pub(crate) fn to_bytes<const V: usize, T: Copy>(value: &T) -> [u8; V] {
509 debug_assert_eq!(size_of::<T>(), V);
510 unsafe { std::ptr::read(std::ptr::from_ref(value).cast()) }
511}
512
513#[inline(always)]
514pub(crate) fn from_value_bytes<const V: usize, T: Copy>(bytes: &[u8; V]) -> T {
515 debug_assert_eq!(V, size_of::<T>());
516 unsafe { std::ptr::read(bytes.as_ptr().cast()) }
517}
518
519#[cfg(feature = "armour")]
520impl<T, const V: usize, H> crate::armour::collection::Collection
521 for ZeroTree<T::SelfId, V, T, H, Bitcask>
522where
523 T: crate::CollectionMeta + Copy + Send + Sync,
524 H: crate::hook::TypedWriteHook<T::SelfId, T>,
525 T::SelfId: crate::Key + Ord,
526{
527 fn name(&self) -> &str {
528 T::NAME
529 }
530 fn len(&self) -> usize {
531 self.len()
532 }
533 fn compact(&self) -> crate::DbResult<usize> {
534 self.compact()
535 }
536}