From 915694da3b0b72b0c638ecaf764c70f8451a2b16 Mon Sep 17 00:00:00 2001 From: Al Liu Date: Thu, 19 Sep 2024 18:55:45 +0800 Subject: [PATCH] Batch insertion (#5) - Refactor constructors, use `Builder` and `GenericBuilder` to construct WALs - Support atomic batch insertion --- .codecov.yml | 3 +- Cargo.toml | 5 +- README.md | 12 +- examples/zero_cost.rs | 26 +- src/buffer.rs | 11 +- src/builder.rs | 592 ++++++++ src/entry.rs | 433 ++++++ src/error.rs | 63 +- src/lib.rs | 134 +- src/options.rs | 30 +- src/pointer.rs | 177 +++ src/swmr.rs | 4 +- src/swmr/generic.rs | 1255 ++++------------- src/swmr/generic/builder.rs | 584 ++++++++ src/swmr/generic/entry.rs | 59 - src/swmr/generic/iter.rs | 20 +- src/swmr/generic/reader.rs | 40 +- src/swmr/generic/tests.rs | 57 +- src/swmr/generic/tests/constructor.rs | 163 ++- src/swmr/generic/tests/get.rs | 261 ++-- src/swmr/generic/tests/insert.rs | 342 +++-- src/swmr/generic/tests/iters.rs | 87 +- src/swmr/wal.rs | 227 +-- src/swmr/wal/reader.rs | 35 +- src/swmr/wal/tests.rs | 4 - src/swmr/wal/tests/constructor.rs | 2 +- src/swmr/wal/tests/get.rs | 2 +- src/swmr/wal/tests/insert.rs | 4 +- src/swmr/wal/tests/iter.rs | 2 +- src/tests.rs | 576 +++++--- src/unsync.rs | 195 +-- src/unsync/c.rs | 18 +- src/unsync/tests.rs | 4 - src/unsync/tests/insert.rs | 2 + src/utils.rs | 34 +- src/wal.rs | 350 +++-- src/wal/batch.rs | 224 +++ src/wal/builder.rs | 328 ----- src/wal/sealed.rs | 530 ++++++- src/{swmr/generic/traits.rs => wal/type.rs} | 46 +- .../generic/traits => wal/type}/impls.rs | 58 +- .../traits => wal/type}/impls/bytes.rs | 22 +- .../generic/traits => wal/type}/impls/net.rs | 40 +- .../traits => wal/type}/impls/string.rs | 4 +- 44 files changed, 4450 insertions(+), 2615 deletions(-) create mode 100644 src/builder.rs create mode 100644 src/entry.rs create mode 100644 src/pointer.rs create mode 100644 src/swmr/generic/builder.rs delete mode 100644 src/swmr/generic/entry.rs create mode 100644 src/wal/batch.rs delete mode 100644 src/wal/builder.rs rename src/{swmr/generic/traits.rs => wal/type.rs} (60%) rename src/{swmr/generic/traits => wal/type}/impls.rs (75%) rename src/{swmr/generic/traits => wal/type}/impls/bytes.rs (86%) rename src/{swmr/generic/traits => wal/type}/impls/net.rs (81%) rename src/{swmr/generic/traits => wal/type}/impls/string.rs (97%) diff --git a/.codecov.yml b/.codecov.yml index 78ea96ba..074b20b6 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -7,12 +7,11 @@ ignore: - "**/benches/" - "src/tests.rs" - "src/error.rs" - - "src/swmr/generic/traits/impls/" - - "src/swmr/generic/traits/impls.rs" - "src/swmr/generic/tests.rs" - "src/swmr/generic/tests/" - "src/swmr/wal/tests.rs" - "src/swmr/wal/tests/" + - "src/wal/type/" - "src/unsync/tests.rs" - "src/unsync/tests/" diff --git a/Cargo.toml b/Cargo.toml index 3ecdcda3..7b9b83a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "orderwal" -version = "0.1.1" +version = "0.2.1" edition = "2021" repository = "https://github.com/al8n/orderwal" homepage = "https://github.com/al8n/orderwal" documentation = "https://docs.rs/orderwal" -description = "A generic-purpose, ordered, zero-copy, Write-Ahead Log implementation for Rust." +description = "A generic-purpose, atomic, ordered, zero-copy, Write-Ahead Log implementation for Rust." license = "MIT OR Apache-2.0" rust-version = "1.80" categories = ["filesystem", "database-implementations", "development-tools", "data-structures"] @@ -34,6 +34,7 @@ thiserror = "1" bytes = { version = "1", default-features = false, optional = true } smallvec = { version = "1", default-features = false, optional = true, features = ["const_generics"] } +smallvec-wrapper = { version = "0.1", optional = true, default-features = false, features = ["const_generics"] } smol_str = { version = "0.3", default-features = false, optional = true } faststr = { version = "0.2", default-features = false, optional = true } diff --git a/README.md b/README.md index c992b73a..bf71edf6 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@
-A generic-purpose, ordered, zero-copy, Write-Ahead Log implementation for Rust. +A generic-purpose, atomic, ordered, zero-copy, Write-Ahead Log implementation for Rust. [github][Github-url] LoC @@ -21,7 +21,7 @@ English | [简体中文][zh-cn-url] ## Introduction -`orderwal` is a generic-purpose, ordered, zero-copy, concurrent-safe, pre-allocate style (memory map) write-ahead-log for developing databases. +`orderwal` is generic-purpose, atomic, ordered, zero-copy, concurrent-safe, pre-allocate style (memory map) write-ahead-log for developing databases. `orderwal` also supports generic structured key and value types, which is not limited to just bytes like other implementations. @@ -29,9 +29,13 @@ English | [简体中文][zh-cn-url] ```toml [dependencies] -orderwal = "0.1" +orderwal = "0.2" ``` +## Example + +See [examples](./examples/) for more information. + ## Related projects - [`aol`](https://github.com/al8n/aol): Yet another generic purpose, append-only write-ahead log implementation based on `std::fs::File`. @@ -44,7 +48,7 @@ Apache License (Version 2.0). See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details. -Copyright (c) 2021 Al Liu. +Copyright (c) 2024 Al Liu. [Github-url]: https://github.com/al8n/orderwal/ [CI-url]: https://github.com/al8n/orderwal/actions/workflows/ci.yml diff --git a/examples/zero_cost.rs b/examples/zero_cost.rs index e1dd6183..45365782 100644 --- a/examples/zero_cost.rs +++ b/examples/zero_cost.rs @@ -1,6 +1,10 @@ use std::{cmp, sync::Arc, thread::spawn}; -use orderwal::{swmr::generic::*, utils::*, OpenOptions, Options}; +use orderwal::{ + swmr::generic::{Comparable, Equivalent, GenericBuilder, KeyRef, Type, TypeRef}, + utils::*, + OpenOptions, +}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] struct Person { @@ -112,7 +116,7 @@ impl Type for Person { } impl<'a> TypeRef<'a> for PersonRef<'a> { - fn from_slice(src: &'a [u8]) -> Self { + unsafe fn from_slice(src: &'a [u8]) -> Self { let (id_size, id) = decode_u64_varint(src).unwrap(); let name = std::str::from_utf8(&src[id_size..]).unwrap(); PersonRef { id, name } @@ -132,15 +136,15 @@ fn main() { .collect::>(); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(1024 * 1024)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut::( + &path, + OpenOptions::new() + .create_new(Some(1024 * 1024)) + .write(true) + .read(true), + ) + .unwrap() }; // Create 100 readers diff --git a/src/buffer.rs b/src/buffer.rs index 946a532d..2a3e2021 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -14,13 +14,22 @@ macro_rules! builder { impl [< $name Builder >] { #[doc = "Creates a new `" [<$name Builder>] "` with the given size and builder closure."] #[inline] - pub const fn new(size: $size, f: F) -> Self + pub const fn once(size: $size, f: F) -> Self where F: for<'a> FnOnce(&mut VacantBuffer<'a>) -> Result<(), E>, { Self { size, f } } + #[doc = "Creates a new `" [<$name Builder>] "` with the given size and builder closure."] + #[inline] + pub const fn new(size: $size, f: F) -> Self + where + F: for<'a> Fn(&mut VacantBuffer<'a>) -> Result<(), E>, + { + Self { size, f } + } + #[doc = "Returns the required" [< $name: snake>] "size."] #[inline] pub const fn size(&self) -> $size { diff --git a/src/builder.rs b/src/builder.rs new file mode 100644 index 00000000..29c6ad27 --- /dev/null +++ b/src/builder.rs @@ -0,0 +1,592 @@ +use checksum::BuildChecksumer; +use wal::{sealed::Constructor, Wal}; + +use super::*; + +/// A write-ahead log builder. +pub struct Builder { + pub(super) opts: Options, + pub(super) cmp: C, + pub(super) cks: S, +} + +impl Default for Builder { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Builder { + /// Returns a new write-ahead log builder with the given options. + #[inline] + pub fn new() -> Self { + Self { + opts: Options::default(), + cmp: Ascend, + cks: Crc32::default(), + } + } +} + +impl Builder { + /// Returns a new write-ahead log builder with the new comparator + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{Builder, Ascend}; + /// + /// let opts = Builder::new().with_comparator(Ascend); + /// ``` + #[inline] + pub fn with_comparator(self, cmp: NC) -> Builder { + Builder { + opts: self.opts, + cmp, + cks: self.cks, + } + } + + /// Returns a new write-ahead log builder with the new checksumer + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{Builder, Crc32}; + /// + /// let opts = Builder::new().with_checksumer(Crc32::new()); + /// ``` + #[inline] + pub fn with_checksumer(self, cks: NS) -> Builder { + Builder { + opts: self.opts, + cmp: self.cmp, + cks, + } + } + + /// Returns a new write-ahead log builder with the new options + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{Builder, Options}; + /// + /// let opts = Builder::new().with_options(Options::default()); + /// ``` + #[inline] + pub fn with_options(self, opts: Options) -> Self { + Self { + opts, + cmp: self.cmp, + cks: self.cks, + } + } + + /// Set the reserved bytes of the WAL. + /// + /// The `reserved` is used to configure the start position of the WAL. This is useful + /// when you want to add some bytes as your own WAL's header. + /// + /// The default reserved is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let opts = Builder::new().with_reserved(8); + /// ``` + #[inline] + pub const fn with_reserved(mut self, reserved: u32) -> Self { + self.opts = self.opts.with_reserved(reserved); + self + } + + /// Get the reserved of the WAL. + /// + /// The `reserved` is used to configure the start position of the WAL. This is useful + /// when you want to add some bytes as your own WAL's header. + /// + /// The default reserved is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let opts = Builder::new().with_reserved(8); + /// + /// assert_eq!(opts.reserved(), 8); + /// ``` + #[inline] + pub const fn reserved(&self) -> u32 { + self.opts.reserved() + } + + /// Returns the magic version. + /// + /// The default value is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_magic_version(1); + /// assert_eq!(options.magic_version(), 1); + /// ``` + #[inline] + pub const fn magic_version(&self) -> u16 { + self.opts.magic_version() + } + + /// Returns the capacity of the WAL. + /// + /// The default value is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_capacity(1000); + /// assert_eq!(options.capacity(), 1000); + /// ``` + #[inline] + pub const fn capacity(&self) -> u32 { + self.opts.capacity() + } + + /// Returns the maximum key length. + /// + /// The default value is `u16::MAX`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_maximum_key_size(1024); + /// assert_eq!(options.maximum_key_size(), 1024); + /// ``` + #[inline] + pub const fn maximum_key_size(&self) -> u32 { + self.opts.maximum_key_size() + } + + /// Returns the maximum value length. + /// + /// The default value is `u32::MAX`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_maximum_value_size(1024); + /// assert_eq!(options.maximum_value_size(), 1024); + /// ``` + #[inline] + pub const fn maximum_value_size(&self) -> u32 { + self.opts.maximum_value_size() + } + + /// Returns `true` if the WAL syncs on write. + /// + /// The default value is `true`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new(); + /// assert_eq!(options.sync_on_write(), true); + /// ``` + #[inline] + pub const fn sync_on_write(&self) -> bool { + self.opts.sync_on_write() + } + + /// Returns the bits of the page size. + /// + /// Configures the anonymous memory map to be allocated using huge pages. + /// + /// This option corresponds to the `MAP_HUGETLB` flag on Linux. It has no effect on Windows. + /// + /// The size of the requested page can be specified in page bits. + /// If not provided, the system default is requested. + /// The requested length should be a multiple of this, or the mapping will fail. + /// + /// This option has no effect on file-backed memory maps. + /// + /// The default value is `None`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_huge(64); + /// assert_eq!(options.huge(), Some(64)); + /// ``` + #[inline] + pub const fn huge(&self) -> Option { + self.opts.huge() + } + + /// Sets the capacity of the WAL. + /// + /// This configuration will be ignored when using file-backed memory maps. + /// + /// The default value is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_capacity(100); + /// assert_eq!(options.capacity(), 100); + /// ``` + #[inline] + pub const fn with_capacity(mut self, cap: u32) -> Self { + self.opts = self.opts.with_capacity(cap); + self + } + + /// Sets the maximum key length. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_maximum_key_size(1024); + /// assert_eq!(options.maximum_key_size(), 1024); + /// ``` + #[inline] + pub const fn with_maximum_key_size(mut self, size: u32) -> Self { + self.opts = self.opts.with_maximum_key_size(size); + self + } + + /// Sets the maximum value length. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_maximum_value_size(1024); + /// assert_eq!(options.maximum_value_size(), 1024); + /// ``` + #[inline] + pub const fn with_maximum_value_size(mut self, size: u32) -> Self { + self.opts = self.opts.with_maximum_value_size(size); + self + } + + /// Returns the bits of the page size. + /// + /// Configures the anonymous memory map to be allocated using huge pages. + /// + /// This option corresponds to the `MAP_HUGETLB` flag on Linux. It has no effect on Windows. + /// + /// The size of the requested page can be specified in page bits. + /// If not provided, the system default is requested. + /// The requested length should be a multiple of this, or the mapping will fail. + /// + /// This option has no effect on file-backed memory maps. + /// + /// The default value is `None`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_huge(64); + /// assert_eq!(options.huge(), Some(64)); + /// ``` + #[inline] + pub const fn with_huge(mut self, page_bits: u8) -> Self { + self.opts = self.opts.with_huge(page_bits); + self + } + + /// Sets the WAL to sync on write. + /// + /// The default value is `true`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_sync_on_write(false); + /// assert_eq!(options.sync_on_write(), false); + /// ``` + #[inline] + pub const fn with_sync_on_write(mut self, sync: bool) -> Self { + self.opts = self.opts.with_sync_on_write(sync); + self + } + + /// Sets the magic version. + /// + /// The default value is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::Builder; + /// + /// let options = Builder::new().with_magic_version(1); + /// assert_eq!(options.magic_version(), 1); + /// ``` + #[inline] + pub const fn with_magic_version(mut self, version: u16) -> Self { + self.opts = self.opts.with_magic_version(version); + self + } +} + +impl Builder { + /// Creates a new in-memory write-ahead log backed by an aligned vec. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::OrderWal, Builder}; + /// + /// let wal = Builder::new() + /// .with_capacity(1024) + /// .alloc::() + /// .unwrap(); + /// ``` + pub fn alloc(self) -> Result + where + W: Wal, + { + let Self { opts, cmp, cks } = self; + arena_options(opts.reserved()) + .with_capacity(opts.capacity()) + .alloc() + .map_err(Error::from_insufficient_space) + .and_then(|arena| >::new_in(arena, opts, cmp, cks).map(W::from_core)) + } + + /// Creates a new in-memory write-ahead log but backed by an anonymous mmap. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::OrderWal, Builder}; + /// + /// let wal = Builder::new() + /// .with_capacity(1024) + /// .map_anon::() + /// .unwrap(); + /// ``` + pub fn map_anon(self) -> Result + where + W: Wal, + { + let Self { opts, cmp, cks } = self; + let mmap_opts = MmapOptions::new().len(opts.capacity()); + arena_options(opts.reserved()) + .map_anon(mmap_opts) + .map_err(Into::into) + .and_then(|arena| >::new_in(arena, opts, cmp, cks).map(W::from_core)) + } + + /// Opens a write-ahead log backed by a file backed memory map in read-only mode. + /// + /// ## Safety + /// + /// All file-backed memory map constructors are marked `unsafe` because of the potential for + /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or + /// out of process. Applications must consider the risk and take appropriate precautions when + /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. + /// unlinked) files exist but are platform specific and limited. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::OrderWal, Builder}; + /// # use orderwal::OpenOptions; + /// + /// # let dir = tempfile::tempdir().unwrap(); + /// # let path = dir.path().join("map.wal"); + /// + /// # let wal = unsafe { + /// # Builder::new() + /// # .map_mut::(&path, OpenOptions::default().read(true).write(true).create(Some(1000))) + /// # .unwrap() + /// # }; + /// + /// let wal = unsafe { + /// Builder::new() + /// .map::(&path) + /// .unwrap() + /// }; + pub unsafe fn map(self, path: P) -> Result + where + C: Comparator + CheapClone + 'static, + S: BuildChecksumer, + P: AsRef, + W: Wal, + { + self + .map_with_path_builder::(|| Ok(path.as_ref().to_path_buf())) + .map_err(|e| e.unwrap_right()) + } + + /// Opens a write-ahead log backed by a file backed memory map in read-only mode. + /// + /// ## Safety + /// + /// All file-backed memory map constructors are marked `unsafe` because of the potential for + /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or + /// out of process. Applications must consider the risk and take appropriate precautions when + /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. + /// unlinked) files exist but are platform specific and limited. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::OrderWal, Builder}; + /// # use orderwal::OpenOptions; + /// + /// # let dir = tempfile::tempdir().unwrap(); + /// # let path = dir.path().join("map_with_path_builder.wal"); + /// + /// # let wal = unsafe { + /// # Builder::new() + /// # .map_mut::(&path, OpenOptions::default().read(true).write(true).create(Some(1000))) + /// # .unwrap() + /// # }; + /// + /// let wal = unsafe { + /// Builder::new() + /// .map_with_path_builder::(|| Ok(path)) + /// .unwrap() + /// }; + pub unsafe fn map_with_path_builder( + self, + path_builder: PB, + ) -> Result> + where + PB: FnOnce() -> Result, + C: Comparator + CheapClone + 'static, + S: BuildChecksumer, + W: Wal, + W::Pointer: Ord + 'static, + { + let open_options = OpenOptions::default().read(true); + + let Self { opts, cmp, cks } = self; + + arena_options(opts.reserved()) + .map_with_path_builder(path_builder, open_options, MmapOptions::new()) + .map_err(|e| e.map_right(Into::into)) + .and_then(|arena| { + >::replay(arena, Options::new(), true, cmp, cks) + .map(>::from_core) + .map_err(Either::Right) + }) + } + + /// Opens a write-ahead log backed by a file backed memory map. + /// + /// ## Safety + /// + /// All file-backed memory map constructors are marked `unsafe` because of the potential for + /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or + /// out of process. Applications must consider the risk and take appropriate precautions when + /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. + /// unlinked) files exist but are platform specific and limited. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::OrderWal, Builder, OpenOptions}; + /// + /// let dir = tempfile::tempdir().unwrap(); + /// let path = dir.path().join("map_mut_with_path_builder_example.wal"); + /// + /// let wal = unsafe { + /// Builder::new() + /// .map_mut::(&path, OpenOptions::default().read(true).write(true).create(Some(1000))) + /// .unwrap() + /// }; + /// ``` + pub unsafe fn map_mut(self, path: P, open_opts: OpenOptions) -> Result + where + C: Comparator + CheapClone + 'static, + S: BuildChecksumer, + P: AsRef, + W: Wal, + { + self + .map_mut_with_path_builder::(|| Ok(path.as_ref().to_path_buf()), open_opts) + .map_err(|e| e.unwrap_right()) + } + + /// Opens a write-ahead log backed by a file backed memory map. + /// + /// ## Safety + /// + /// All file-backed memory map constructors are marked `unsafe` because of the potential for + /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or + /// out of process. Applications must consider the risk and take appropriate precautions when + /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. + /// unlinked) files exist but are platform specific and limited. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::OrderWal, Builder, OpenOptions}; + /// + /// let dir = tempfile::tempdir().unwrap(); + /// + /// let wal = unsafe { + /// Builder::new() + /// .map_mut_with_path_builder::( + /// || Ok(dir.path().join("map_mut_with_path_builder_example.wal")), + /// OpenOptions::default().read(true).write(true).create(Some(1000)), + /// ) + /// .unwrap() + /// }; + /// ``` + pub unsafe fn map_mut_with_path_builder( + self, + path_builder: PB, + open_options: OpenOptions, + ) -> Result> + where + PB: FnOnce() -> Result, + C: Comparator + CheapClone + 'static, + S: BuildChecksumer, + W: Wal, + { + let path = path_builder().map_err(Either::Left)?; + let exist = path.exists(); + let Self { opts, cmp, cks } = self; + + arena_options(opts.reserved()) + .map_mut(path, open_options, MmapOptions::new()) + .map_err(Into::into) + .and_then(|arena| { + if !exist { + >::new_in(arena, opts, cmp, cks).map(W::from_core) + } else { + >::replay(arena, opts, false, cmp, cks).map(W::from_core) + } + }) + .map_err(Either::Right) + } +} diff --git a/src/entry.rs b/src/entry.rs new file mode 100644 index 00000000..cd39acaf --- /dev/null +++ b/src/entry.rs @@ -0,0 +1,433 @@ +use core::borrow::Borrow; + +use among::Among; +use crossbeam_skiplist::set::Entry as SetEntry; +use rarena_allocator::either::Either; + +use super::{ + pointer::{GenericPointer, Pointer}, + wal::r#type::{Type, TypeRef}, + KeyBuilder, ValueBuilder, +}; + +pub(crate) struct BatchEncodedEntryMeta { + /// The output of `merge_lengths(klen, vlen)` + pub(crate) kvlen: u64, + /// the length of `encoded_u64_varint(merge_lengths(klen, vlen))` + pub(crate) kvlen_size: usize, + pub(crate) klen: usize, + pub(crate) vlen: usize, +} + +impl BatchEncodedEntryMeta { + #[inline] + pub(crate) const fn new(klen: usize, vlen: usize, kvlen: u64, kvlen_size: usize) -> Self { + Self { + klen, + vlen, + kvlen, + kvlen_size, + } + } + + #[inline] + const fn zero() -> Self { + Self { + klen: 0, + vlen: 0, + kvlen: 0, + kvlen_size: 0, + } + } +} + +/// An entry which can be inserted into the [`Wal`](crate::wal::Wal). +pub struct Entry { + pub(crate) key: K, + pub(crate) value: V, + pub(crate) pointer: Option>, + pub(crate) meta: BatchEncodedEntryMeta, +} + +impl Entry +where + K: Borrow<[u8]>, + V: Borrow<[u8]>, +{ + /// Returns the length of the value. + #[inline] + pub fn key_len(&self) -> usize { + self.key.borrow().len() + } + + /// Returns the length of the value. + #[inline] + pub fn value_len(&self) -> usize { + self.value.borrow().len() + } +} + +impl Entry { + /// Creates a new entry. + #[inline] + pub const fn new(key: K, value: V) -> Self { + Self { + key, + value, + pointer: None, + meta: BatchEncodedEntryMeta::zero(), + } + } + + /// Returns the key. + #[inline] + pub const fn key(&self) -> &K { + &self.key + } + + /// Returns the value. + #[inline] + pub const fn value(&self) -> &V { + &self.value + } + + /// Consumes the entry and returns the key and value. + #[inline] + pub fn into_components(self) -> (K, V) { + (self.key, self.value) + } +} + +/// An entry builder which can build an [`Entry`] to be inserted into the [`Wal`](crate::wal::Wal). +pub struct EntryWithKeyBuilder { + pub(crate) kb: KeyBuilder, + pub(crate) value: V, + pub(crate) pointer: Option>, + pub(crate) meta: BatchEncodedEntryMeta, +} + +impl EntryWithKeyBuilder +where + V: Borrow<[u8]>, +{ + /// Returns the length of the value. + #[inline] + pub fn value_len(&self) -> usize { + self.value.borrow().len() + } +} + +impl EntryWithKeyBuilder { + /// Creates a new entry. + #[inline] + pub const fn new(kb: KeyBuilder, value: V) -> Self { + Self { + kb, + value, + pointer: None, + meta: BatchEncodedEntryMeta::zero(), + } + } + + /// Returns the key. + #[inline] + pub const fn key_builder(&self) -> &KeyBuilder { + &self.kb + } + + /// Returns the value. + #[inline] + pub const fn value(&self) -> &V { + &self.value + } + + /// Returns the length of the key. + #[inline] + pub const fn key_len(&self) -> usize { + self.kb.size() as usize + } + + /// Consumes the entry and returns the key and value. + #[inline] + pub fn into_components(self) -> (KeyBuilder, V) { + (self.kb, self.value) + } +} + +/// An entry builder which can build an [`Entry`] to be inserted into the [`Wal`](crate::wal::Wal). +pub struct EntryWithValueBuilder { + pub(crate) key: K, + pub(crate) vb: ValueBuilder, + pub(crate) pointer: Option>, + pub(crate) meta: BatchEncodedEntryMeta, +} + +impl EntryWithValueBuilder +where + K: Borrow<[u8]>, +{ + /// Returns the length of the key. + #[inline] + pub fn key_len(&self) -> usize { + self.key.borrow().len() + } +} + +impl EntryWithValueBuilder { + /// Creates a new entry. + #[inline] + pub const fn new(key: K, vb: ValueBuilder) -> Self { + Self { + key, + vb, + pointer: None, + meta: BatchEncodedEntryMeta::zero(), + } + } + + /// Returns the key. + #[inline] + pub const fn value_builder(&self) -> &ValueBuilder { + &self.vb + } + + /// Returns the value. + #[inline] + pub const fn key(&self) -> &K { + &self.key + } + + /// Returns the length of the value. + #[inline] + pub const fn value_len(&self) -> usize { + self.vb.size() as usize + } + + /// Consumes the entry and returns the key and value. + #[inline] + pub fn into_components(self) -> (K, ValueBuilder) { + (self.key, self.vb) + } +} + +/// An entry builder which can build an [`Entry`] to be inserted into the [`Wal`](crate::wal::Wal). +pub struct EntryWithBuilders { + pub(crate) kb: KeyBuilder, + pub(crate) vb: ValueBuilder, + pub(crate) pointer: Option>, + pub(crate) meta: BatchEncodedEntryMeta, +} + +impl EntryWithBuilders { + /// Creates a new entry. + #[inline] + pub const fn new(kb: KeyBuilder, vb: ValueBuilder) -> Self { + Self { + kb, + vb, + pointer: None, + meta: BatchEncodedEntryMeta::zero(), + } + } + + /// Returns the value builder. + #[inline] + pub const fn value_builder(&self) -> &ValueBuilder { + &self.vb + } + + /// Returns the key builder. + #[inline] + pub const fn key_builder(&self) -> &KeyBuilder { + &self.kb + } + + /// Returns the length of the key. + #[inline] + pub const fn key_len(&self) -> usize { + self.kb.size() as usize + } + + /// Returns the length of the value. + #[inline] + pub const fn value_len(&self) -> usize { + self.vb.size() as usize + } + + /// Consumes the entry and returns the key and value. + #[inline] + pub fn into_components(self) -> (KeyBuilder, ValueBuilder) { + (self.kb, self.vb) + } +} + +/// A wrapper around a generic type that can be used to construct a [`GenericEntry`]. +#[repr(transparent)] +pub struct Generic<'a, T> { + data: Among, +} + +impl Generic<'_, T> { + #[inline] + pub(crate) fn encoded_len(&self) -> usize { + match &self.data { + Among::Left(val) => val.encoded_len(), + Among::Middle(val) => val.encoded_len(), + Among::Right(val) => val.len(), + } + } + + #[inline] + pub(crate) fn encode(&self, buf: &mut [u8]) -> Result<(), T::Error> { + match &self.data { + Among::Left(val) => val.encode(buf), + Among::Middle(val) => val.encode(buf), + Among::Right(val) => { + buf.copy_from_slice(val); + Ok(()) + } + } + } +} + +impl<'a, T> Generic<'a, T> { + /// Returns the value contained in the generic. + #[inline] + pub const fn data(&self) -> Either<&T, &'a [u8]> { + match &self.data { + Among::Left(val) => Either::Left(val), + Among::Middle(val) => Either::Left(val), + Among::Right(val) => Either::Right(val), + } + } + + /// Creates a new generic from bytes for querying or inserting into the [`GenericOrderWal`](crate::swmr::GenericOrderWal). + /// + /// ## Safety + /// - the `slice` must the same as the one returned by [`T::encode`](Type::encode). + #[inline] + pub const unsafe fn from_slice(slice: &'a [u8]) -> Self { + Self { + data: Among::Right(slice), + } + } + + #[inline] + pub(crate) fn into_among(self) -> Among { + self.data + } +} + +impl<'a, T> From<&'a T> for Generic<'a, T> { + #[inline] + fn from(value: &'a T) -> Self { + Self { + data: Among::Middle(value), + } + } +} + +impl From for Generic<'_, T> { + #[inline] + fn from(value: T) -> Self { + Self { + data: Among::Left(value), + } + } +} + +/// An entry in the [`GenericOrderWal`](crate::swmr::GenericOrderWal). +pub struct GenericEntry<'a, K, V> { + pub(crate) key: Generic<'a, K>, + pub(crate) value: Generic<'a, V>, + pub(crate) pointer: Option>, + pub(crate) meta: BatchEncodedEntryMeta, +} + +impl<'a, K, V> GenericEntry<'a, K, V> { + /// Creates a new entry. + #[inline] + pub fn new(key: impl Into>, value: impl Into>) -> Self { + Self { + key: key.into(), + value: value.into(), + pointer: None, + meta: BatchEncodedEntryMeta::zero(), + } + } + + /// Returns the key. + #[inline] + pub const fn key(&self) -> Either<&K, &[u8]> { + self.key.data() + } + + /// Returns the value. + #[inline] + pub const fn value(&self) -> Either<&V, &[u8]> { + self.value.data() + } + + /// Consumes the entry and returns the key and value. + #[inline] + pub fn into_components(self) -> (Generic<'a, K>, Generic<'a, V>) { + (self.key, self.value) + } +} + +/// The reference to an entry in the [`GenericOrderWal`](crate::swmr::GenericOrderWal). +#[repr(transparent)] +pub struct GenericEntryRef<'a, K, V> { + ent: SetEntry<'a, GenericPointer>, +} + +impl<'a, K, V> core::fmt::Debug for GenericEntryRef<'a, K, V> +where + K: Type, + K::Ref<'a>: core::fmt::Debug, + V: Type, + V::Ref<'a>: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("EntryRef") + .field("key", &self.key()) + .field("value", &self.value()) + .finish() + } +} + +impl Clone for GenericEntryRef<'_, K, V> { + #[inline] + fn clone(&self) -> Self { + Self { + ent: self.ent.clone(), + } + } +} + +impl<'a, K, V> GenericEntryRef<'a, K, V> { + #[inline] + pub(super) fn new(ent: SetEntry<'a, GenericPointer>) -> Self { + Self { ent } + } +} + +impl<'a, K, V> GenericEntryRef<'a, K, V> +where + K: Type, + V: Type, +{ + /// Returns the key of the entry. + #[inline] + pub fn key(&self) -> K::Ref<'a> { + let p = self.ent.value(); + unsafe { TypeRef::from_slice(p.as_key_slice()) } + } + + /// Returns the value of the entry. + #[inline] + pub fn value(&self) -> V::Ref<'a> { + let p = self.ent.value(); + unsafe { TypeRef::from_slice(p.as_value_slice()) } + } +} diff --git a/src/error.rs b/src/error.rs index 1308a198..3169d91b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,19 @@ +/// The batch error type. +#[derive(Debug, thiserror::Error)] +pub enum BatchError { + /// Returned when the expected batch encoding size does not match the actual size. + #[error("the expected batch encoding size ({expected}) does not match the actual size {actual}")] + EncodedSizeMismatch { + /// The expected size. + expected: u32, + /// The actual size. + actual: u32, + }, + /// Larger encoding size than the expected batch encoding size. + #[error("larger encoding size than the expected batch encoding size {0}")] + LargerEncodedSize(u32), +} + /// The error type. #[derive(Debug, thiserror::Error)] pub enum Error { @@ -5,7 +21,7 @@ pub enum Error { #[error("insufficient space in the WAL (requested: {requested}, available: {available})")] InsufficientSpace { /// The requested size - requested: u32, + requested: u64, /// The remaining size available: u32, }, @@ -13,7 +29,7 @@ pub enum Error { #[error("the key size is {size} larger than the maximum key size {maximum_key_size}")] KeyTooLarge { /// The size of the key. - size: u32, + size: u64, /// The maximum key size. maximum_key_size: u32, }, @@ -21,7 +37,7 @@ pub enum Error { #[error("the value size is {size} larger than the maximum value size {maximum_value_size}")] ValueTooLarge { /// The size of the value. - size: u32, + size: u64, /// The maximum value size. maximum_value_size: u32, }, @@ -33,6 +49,9 @@ pub enum Error { /// The maximum entry size. maximum_entry_size: u64, }, + /// Returned when the expected batch encoding size does not match the actual size. + #[error(transparent)] + Batch(#[from] BatchError), /// I/O error. #[error("{0}")] IO(#[from] std::io::Error), @@ -43,7 +62,7 @@ pub enum Error { impl Error { /// Create a new `Error::InsufficientSpace` instance. - pub(crate) const fn insufficient_space(requested: u32, available: u32) -> Self { + pub(crate) const fn insufficient_space(requested: u64, available: u32) -> Self { Self::InsufficientSpace { requested, available, @@ -51,7 +70,7 @@ impl Error { } /// Create a new `Error::KeyTooLarge` instance. - pub(crate) const fn key_too_large(size: u32, maximum_key_size: u32) -> Self { + pub(crate) const fn key_too_large(size: u64, maximum_key_size: u32) -> Self { Self::KeyTooLarge { size, maximum_key_size, @@ -59,7 +78,7 @@ impl Error { } /// Create a new `Error::ValueTooLarge` instance. - pub(crate) const fn value_too_large(size: u32, maximum_value_size: u32) -> Self { + pub(crate) const fn value_too_large(size: u64, maximum_value_size: u32) -> Self { Self::ValueTooLarge { size, maximum_value_size, @@ -80,20 +99,46 @@ impl Error { rarena_allocator::Error::InsufficientSpace { requested, available, - } => Self::insufficient_space(requested, available), + } => Self::insufficient_space(requested as u64, available), _ => unreachable!(), } } /// Create a new corrupted error. #[inline] - pub(crate) fn corrupted() -> Error { + pub(crate) fn corrupted(e: E) -> Error + where + E: Into>, + { + #[derive(Debug)] + struct Corrupted(Box); + + impl std::fmt::Display for Corrupted { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "corrupted write-ahead log: {}", self.0) + } + } + + impl std::error::Error for Corrupted {} + Self::IO(std::io::Error::new( std::io::ErrorKind::InvalidData, - "corrupted write-ahead log", + Corrupted(e.into()), )) } + /// Create a new batch size mismatch error. + #[inline] + pub(crate) const fn batch_size_mismatch(expected: u32, actual: u32) -> Self { + Self::Batch(BatchError::EncodedSizeMismatch { expected, actual }) + } + + /// Create a new larger batch size error. + #[inline] + pub(crate) const fn larger_batch_size(size: u32) -> Self { + Self::Batch(BatchError::LargerEncodedSize(size)) + } + /// Create a read-only error. pub(crate) const fn read_only() -> Self { Self::ReadOnly diff --git a/src/lib.rs b/src/lib.rs index 882fc624..87b9e0bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ #![deny(missing_docs)] #![allow(clippy::type_complexity)] -use core::{borrow::Borrow, cmp, marker::PhantomData, mem, slice}; +use core::{borrow::Borrow, marker::PhantomData, mem}; pub use among; use among::Among; @@ -22,15 +22,18 @@ pub use rarena_allocator::OpenOptions; #[cfg(feature = "std")] extern crate std; -pub use dbutils::{Ascend, CheapClone, Checksumer, Comparator, Crc32, Descend}; +pub use dbutils::{ + checksum::{self, Crc32}, + Ascend, CheapClone, Comparator, Descend, +}; #[cfg(feature = "xxhash3")] #[cfg_attr(docsrs, doc(cfg(feature = "xxhash3")))] -pub use dbutils::XxHash3; +pub use dbutils::checksum::XxHash3; #[cfg(feature = "xxhash64")] #[cfg_attr(docsrs, doc(cfg(feature = "xxhash64")))] -pub use dbutils::XxHash64; +pub use dbutils::checksum::XxHash64; const STATUS_SIZE: usize = mem::size_of::(); const CHECKSUM_SIZE: usize = mem::size_of::(); @@ -67,12 +70,18 @@ pub mod error; mod buffer; pub use buffer::*; +mod builder; +pub use builder::Builder; + +mod entry; +pub use entry::*; + /// Utilities. pub mod utils; use utils::*; mod wal; -pub use wal::{Builder, Wal}; +pub use wal::{ImmutableWal, Wal}; mod options; pub use options::Options; @@ -83,121 +92,14 @@ pub mod swmr; /// An ordered write-ahead Log implementation. pub mod unsync; +mod pointer; + bitflags::bitflags! { /// The flags of the entry. struct Flags: u8 { /// First bit: 1 indicates committed, 0 indicates uncommitted const COMMITTED = 0b00000001; - } -} - -#[doc(hidden)] -pub struct Pointer { - /// The pointer to the start of the entry. - ptr: *const u8, - /// The length of the key. - key_len: usize, - /// The length of the value. - value_len: usize, - cmp: C, -} - -unsafe impl Send for Pointer {} -unsafe impl Sync for Pointer {} - -impl Pointer { - #[inline] - const fn new(key_len: usize, value_len: usize, ptr: *const u8, cmp: C) -> Self { - Self { - ptr, - key_len, - value_len, - cmp, - } - } - - #[inline] - const fn as_key_slice<'a>(&self) -> &'a [u8] { - if self.key_len == 0 { - return &[]; - } - - // SAFETY: `ptr` is a valid pointer to `len` bytes. - unsafe { slice::from_raw_parts(self.ptr, self.key_len) } - } - - #[inline] - const fn as_value_slice<'a, 'b: 'a>(&'a self) -> &'b [u8] { - if self.value_len == 0 { - return &[]; - } - - // SAFETY: `ptr` is a valid pointer to `len` bytes. - unsafe { slice::from_raw_parts(self.ptr.add(self.key_len), self.value_len) } - } -} - -impl PartialEq for Pointer { - fn eq(&self, other: &Self) -> bool { - self - .cmp - .compare(self.as_key_slice(), other.as_key_slice()) - .is_eq() - } -} - -impl Eq for Pointer {} - -impl PartialOrd for Pointer { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Pointer { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.cmp.compare(self.as_key_slice(), other.as_key_slice()) - } -} - -impl Borrow for Pointer -where - [u8]: Borrow, - Q: ?Sized + Ord, -{ - fn borrow(&self) -> &Q { - self.as_key_slice().borrow() - } -} - -/// Use to avoid the mutable borrow checker, for single writer multiple readers usecase. -struct UnsafeCellChecksumer(core::cell::UnsafeCell); - -impl UnsafeCellChecksumer { - #[inline] - const fn new(checksumer: S) -> Self { - Self(core::cell::UnsafeCell::new(checksumer)) - } -} - -impl UnsafeCellChecksumer -where - S: Checksumer, -{ - #[inline] - fn update(&self, buf: &[u8]) { - // SAFETY: the checksumer will not be invoked concurrently. - unsafe { (*self.0.get()).update(buf) } - } - - #[inline] - fn reset(&self) { - // SAFETY: the checksumer will not be invoked concurrently. - unsafe { (*self.0.get()).reset() } - } - - #[inline] - fn digest(&self) -> u64 { - unsafe { (*self.0.get()).digest() } + /// Second bit: 1 indicates batching, 0 indicates single entry + const BATCHING = 0b00000010; } } diff --git a/src/options.rs b/src/options.rs index cefac442..092460a8 100644 --- a/src/options.rs +++ b/src/options.rs @@ -21,7 +21,7 @@ impl Options { /// Create a new `Options` instance. /// /// - /// # Example + /// ## Example /// /// **Note:** If you are creating in-memory WAL, then you must specify the capacity. /// @@ -50,7 +50,7 @@ impl Options { /// /// The default reserved is `0`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -70,7 +70,7 @@ impl Options { /// /// The default reserved is `0`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -88,7 +88,7 @@ impl Options { /// /// The default value is `0`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -105,7 +105,7 @@ impl Options { /// /// The default value is `0`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -122,7 +122,7 @@ impl Options { /// /// The default value is `u16::MAX`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -139,7 +139,7 @@ impl Options { /// /// The default value is `u32::MAX`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -156,7 +156,7 @@ impl Options { /// /// The default value is `true`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -183,7 +183,7 @@ impl Options { /// /// The default value is `None`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -202,7 +202,7 @@ impl Options { /// /// The default value is `0`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -218,7 +218,7 @@ impl Options { /// Sets the maximum key length. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -234,7 +234,7 @@ impl Options { /// Sets the maximum value length. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -262,7 +262,7 @@ impl Options { /// /// The default value is `None`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -280,7 +280,7 @@ impl Options { /// /// The default value is `true`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; @@ -298,7 +298,7 @@ impl Options { /// /// The default value is `0`. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::Options; diff --git a/src/pointer.rs b/src/pointer.rs new file mode 100644 index 00000000..9963a18a --- /dev/null +++ b/src/pointer.rs @@ -0,0 +1,177 @@ +use core::{borrow::Borrow, cmp, marker::PhantomData, slice}; + +use dbutils::Comparator; + +use super::wal::r#type::{KeyRef, Type}; + +#[doc(hidden)] +pub struct Pointer { + /// The pointer to the start of the entry. + ptr: *const u8, + /// The length of the key. + key_len: usize, + /// The length of the value. + value_len: usize, + cmp: C, +} + +unsafe impl Send for Pointer {} +unsafe impl Sync for Pointer {} + +impl Pointer { + #[inline] + pub(crate) const fn new(key_len: usize, value_len: usize, ptr: *const u8, cmp: C) -> Self { + Self { + ptr, + key_len, + value_len, + cmp, + } + } + + #[inline] + pub const fn as_key_slice<'a>(&self) -> &'a [u8] { + if self.key_len == 0 { + return &[]; + } + + // SAFETY: `ptr` is a valid pointer to `len` bytes. + unsafe { slice::from_raw_parts(self.ptr, self.key_len) } + } + + #[inline] + pub const fn as_value_slice<'a, 'b: 'a>(&'a self) -> &'b [u8] { + if self.value_len == 0 { + return &[]; + } + + // SAFETY: `ptr` is a valid pointer to `len` bytes. + unsafe { slice::from_raw_parts(self.ptr.add(self.key_len), self.value_len) } + } +} + +impl PartialEq for Pointer { + fn eq(&self, other: &Self) -> bool { + self + .cmp + .compare(self.as_key_slice(), other.as_key_slice()) + .is_eq() + } +} + +impl Eq for Pointer {} + +impl PartialOrd for Pointer { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Pointer { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.cmp.compare(self.as_key_slice(), other.as_key_slice()) + } +} + +impl Borrow for Pointer +where + [u8]: Borrow, + Q: ?Sized + Ord, +{ + fn borrow(&self) -> &Q { + self.as_key_slice().borrow() + } +} + +impl super::wal::sealed::Pointer for Pointer { + type Comparator = C; + + #[inline] + fn new(klen: usize, vlen: usize, ptr: *const u8, cmp: C) -> Self { + Pointer::::new(klen, vlen, ptr, cmp) + } +} + +#[doc(hidden)] +#[derive(Debug)] +pub struct GenericPointer { + /// The pointer to the start of the entry. + ptr: *const u8, + /// The length of the key. + key_len: usize, + /// The length of the value. + value_len: usize, + _m: PhantomData<(fn() -> K, fn() -> V)>, +} + +impl crate::wal::sealed::Pointer for GenericPointer { + type Comparator = (); + + #[inline] + fn new(klen: usize, vlen: usize, ptr: *const u8, _cmp: Self::Comparator) -> Self { + Self::new(klen, vlen, ptr) + } +} + +impl PartialEq for GenericPointer { + fn eq(&self, other: &Self) -> bool { + self.as_key_slice() == other.as_key_slice() + } +} + +impl Eq for GenericPointer {} + +impl PartialOrd for GenericPointer +where + K: Type + Ord, + for<'a> K::Ref<'a>: KeyRef<'a, K>, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for GenericPointer +where + K: Type + Ord, + for<'a> K::Ref<'a>: KeyRef<'a, K>, +{ + fn cmp(&self, other: &Self) -> cmp::Ordering { + as KeyRef>::compare_binary(self.as_key_slice(), other.as_key_slice()) + } +} + +unsafe impl Send for GenericPointer {} +unsafe impl Sync for GenericPointer {} + +impl GenericPointer { + #[inline] + pub(crate) const fn new(key_len: usize, value_len: usize, ptr: *const u8) -> Self { + Self { + ptr, + key_len, + value_len, + _m: PhantomData, + } + } + + #[inline] + pub const fn as_key_slice<'a>(&self) -> &'a [u8] { + if self.key_len == 0 { + return &[]; + } + + // SAFETY: `ptr` is a valid pointer to `len` bytes. + unsafe { slice::from_raw_parts(self.ptr, self.key_len) } + } + + #[inline] + pub const fn as_value_slice<'a, 'b: 'a>(&'a self) -> &'b [u8] { + if self.value_len == 0 { + return &[]; + } + + // SAFETY: `ptr` is a valid pointer to `len` bytes. + unsafe { slice::from_raw_parts(self.ptr.add(self.key_len), self.value_len) } + } +} diff --git a/src/swmr.rs b/src/swmr.rs index 763bab86..c3758f85 100644 --- a/src/swmr.rs +++ b/src/swmr.rs @@ -1,7 +1,7 @@ /// The ordered write-ahead log only supports bytes. pub mod wal; -pub use wal::OrderWal; +pub use wal::{Builder, OrderWal}; /// The generic implementation of the ordered write-ahead log. pub mod generic; -pub use generic::GenericOrderWal; +pub use generic::{GenericBuilder, GenericOrderWal}; diff --git a/src/swmr/generic.rs b/src/swmr/generic.rs index f8c01985..dc0291bf 100644 --- a/src/swmr/generic.rs +++ b/src/swmr/generic.rs @@ -1,4 +1,10 @@ -use core::{cmp, marker::PhantomData, ops::Bound, slice}; +use core::{ + cmp, + marker::PhantomData, + ops::Bound, + slice, + sync::atomic::{AtomicPtr, Ordering}, +}; use std::{ path::{Path, PathBuf}, sync::Arc, @@ -6,24 +12,27 @@ use std::{ use among::Among; use crossbeam_skiplist::SkipSet; -pub use dbutils::equivalent::*; -use dbutils::{Checksumer, Crc32}; -use rarena_allocator::{ - either::Either, sync::Arena, Allocator, ArenaPosition, Memory, MmapOptions, OpenOptions, +use dbutils::{ + checksum::{BuildChecksumer, Checksumer, Crc32}, + leb128::encoded_u64_varint_len, }; +use rarena_allocator::{either::Either, sync::Arena, Allocator, Memory, MmapOptions, OpenOptions}; use crate::{ arena_options, check, entry_size, error::{self, Error}, - split_lengths, Flags, Options, UnsafeCellChecksumer, CHECKSUM_SIZE, HEADER_SIZE, MAGIC_TEXT, - STATUS_SIZE, + merge_lengths, + pointer::GenericPointer, + wal::sealed::Constructor, + BatchEncodedEntryMeta, Flags, Options, CHECKSUM_SIZE, HEADER_SIZE, STATUS_SIZE, }; -mod entry; -pub use entry::*; +pub use crate::{ + entry::{Generic, GenericEntry, GenericEntryRef}, + wal::{r#type::*, GenericBatch}, +}; -mod traits; -pub use traits::*; +pub use dbutils::equivalent::{Comparable, Equivalent}; mod reader; pub use reader::*; @@ -31,6 +40,9 @@ pub use reader::*; mod iter; pub use iter::*; +mod builder; +pub use builder::*; + #[cfg(all( test, any( @@ -43,81 +55,6 @@ pub use iter::*; ))] mod tests; -#[doc(hidden)] -#[derive(Debug)] -pub struct Pointer { - /// The pointer to the start of the entry. - ptr: *const u8, - /// The length of the key. - key_len: usize, - /// The length of the value. - value_len: usize, - _m: PhantomData<(K, V)>, -} - -impl PartialEq for Pointer { - fn eq(&self, other: &Self) -> bool { - self.as_key_slice() == other.as_key_slice() - } -} - -impl Eq for Pointer {} - -impl PartialOrd for Pointer -where - K: Type + Ord, - for<'a> K::Ref<'a>: KeyRef<'a, K>, -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Pointer -where - K: Type + Ord, - for<'a> K::Ref<'a>: KeyRef<'a, K>, -{ - fn cmp(&self, other: &Self) -> cmp::Ordering { - as KeyRef>::compare_binary(self.as_key_slice(), other.as_key_slice()) - } -} - -unsafe impl Send for Pointer {} -unsafe impl Sync for Pointer {} - -impl Pointer { - #[inline] - const fn new(key_len: usize, value_len: usize, ptr: *const u8) -> Self { - Self { - ptr, - key_len, - value_len, - _m: PhantomData, - } - } - - #[inline] - const fn as_key_slice<'a>(&self) -> &'a [u8] { - if self.key_len == 0 { - return &[]; - } - - // SAFETY: `ptr` is a valid pointer to `len` bytes. - unsafe { slice::from_raw_parts(self.ptr, self.key_len) } - } - - #[inline] - const fn as_value_slice<'a, 'b: 'a>(&'a self) -> &'b [u8] { - if self.value_len == 0 { - return &[]; - } - - // SAFETY: `ptr` is a valid pointer to `len` bytes. - unsafe { slice::from_raw_parts(self.ptr.add(self.key_len), self.value_len) } - } -} - struct PartialPointer { key_len: usize, ptr: *const u8, @@ -173,25 +110,27 @@ impl PartialPointer { } } -impl<'a, K, V> Equivalent> for PartialPointer +impl<'a, K, V> Equivalent> for PartialPointer where K: Type + Ord, K::Ref<'a>: KeyRef<'a, K>, { - fn equivalent(&self, key: &Pointer) -> bool { + fn equivalent(&self, key: &GenericPointer) -> bool { self.compare(key).is_eq() } } -impl<'a, K, V> Comparable> for PartialPointer +impl<'a, K, V> Comparable> for PartialPointer where K: Type + Ord, K::Ref<'a>: KeyRef<'a, K>, { - fn compare(&self, p: &Pointer) -> cmp::Ordering { - let kr: K::Ref<'_> = TypeRef::from_slice(p.as_key_slice()); - let or: K::Ref<'_> = TypeRef::from_slice(self.as_key_slice()); - KeyRef::compare(&kr, &or).reverse() + fn compare(&self, p: &GenericPointer) -> cmp::Ordering { + unsafe { + let kr: K::Ref<'_> = TypeRef::from_slice(p.as_key_slice()); + let or: K::Ref<'_> = TypeRef::from_slice(self.as_key_slice()); + KeyRef::compare(&kr, &or).reverse() + } } } @@ -211,25 +150,25 @@ impl<'a, K, Q: ?Sized> Ref<'a, K, Q> { } } -impl<'a, K, Q, V> Equivalent> for Ref<'a, K, Q> +impl<'a, K, Q, V> Equivalent> for Ref<'a, K, Q> where K: Type + Ord, K::Ref<'a>: KeyRef<'a, K>, Q: ?Sized + Ord + Comparable>, { - fn equivalent(&self, key: &Pointer) -> bool { + fn equivalent(&self, key: &GenericPointer) -> bool { self.compare(key).is_eq() } } -impl<'a, K, Q, V> Comparable> for Ref<'a, K, Q> +impl<'a, K, Q, V> Comparable> for Ref<'a, K, Q> where K: Type + Ord, K::Ref<'a>: KeyRef<'a, K>, Q: ?Sized + Ord + Comparable>, { - fn compare(&self, p: &Pointer) -> cmp::Ordering { - let kr = TypeRef::from_slice(p.as_key_slice()); + fn compare(&self, p: &GenericPointer) -> cmp::Ordering { + let kr = unsafe { TypeRef::from_slice(p.as_key_slice()) }; KeyRef::compare(&kr, self.key).reverse() } } @@ -250,36 +189,56 @@ impl<'a, K, Q: ?Sized> Owned<'a, K, Q> { } } -impl<'a, K, Q, V> Equivalent> for Owned<'a, K, Q> +impl<'a, K, Q, V> Equivalent> for Owned<'a, K, Q> where K: Type + Ord, K::Ref<'a>: KeyRef<'a, K>, Q: ?Sized + Ord + Comparable + Comparable>, { - fn equivalent(&self, key: &Pointer) -> bool { + fn equivalent(&self, key: &GenericPointer) -> bool { self.compare(key).is_eq() } } -impl<'a, K, Q, V> Comparable> for Owned<'a, K, Q> +impl<'a, K, Q, V> Comparable> for Owned<'a, K, Q> where K: Type + Ord, K::Ref<'a>: KeyRef<'a, K>, Q: ?Sized + Ord + Comparable + Comparable>, { - fn compare(&self, p: &Pointer) -> cmp::Ordering { - let kr = as TypeRef<'_>>::from_slice(p.as_key_slice()); + fn compare(&self, p: &GenericPointer) -> cmp::Ordering { + let kr = unsafe { as TypeRef<'_>>::from_slice(p.as_key_slice()) }; KeyRef::compare(&kr, self.key).reverse() } } -struct GenericOrderWalCore { +#[doc(hidden)] +pub struct GenericOrderWalCore { arena: Arena, - map: SkipSet>, - reserved: u32, + map: SkipSet>, + opts: Options, + cks: S, +} + +impl crate::wal::sealed::WalCore<(), S> for GenericOrderWalCore { + type Allocator = Arena; + + type Base = SkipSet>; + + type Pointer = GenericPointer; + + #[inline] + fn construct(arena: Self::Allocator, base: Self::Base, opts: Options, _cmp: (), cks: S) -> Self { + Self { + arena, + map: base, + opts, + cks, + } + } } -impl GenericOrderWalCore { +impl GenericOrderWalCore { #[inline] fn len(&self) -> usize { self.map.len() @@ -291,39 +250,21 @@ impl GenericOrderWalCore { } #[inline] - fn new(arena: Arena, magic_version: u16, flush: bool, reserved: u32) -> Result { - unsafe { - let slice = arena.reserved_slice_mut(); - slice[0..6].copy_from_slice(&MAGIC_TEXT); - slice[6..8].copy_from_slice(&magic_version.to_le_bytes()); - } - - if !flush { - return Ok(Self::construct(arena, SkipSet::new(), reserved)); - } - - arena - .flush_range(0, HEADER_SIZE) - .map(|_| Self::construct(arena, SkipSet::new(), reserved)) - .map_err(Into::into) - } - - #[inline] - fn first(&self) -> Option> + fn first(&self) -> Option> where K: Type + Ord, for<'b> K::Ref<'b>: KeyRef<'b, K>, { - self.map.front().map(EntryRef::new) + self.map.front().map(GenericEntryRef::new) } #[inline] - fn last(&self) -> Option> + fn last(&self) -> Option> where K: Type + Ord, for<'b> K::Ref<'b>: KeyRef<'b, K>, { - self.map.back().map(EntryRef::new) + self.map.back().map(GenericEntryRef::new) } #[inline] @@ -370,114 +311,28 @@ impl GenericOrderWalCore { .range((start_bound.map(Owned::new), end_bound.map(Owned::new))), ) } - - #[inline] - fn construct(arena: Arena, set: SkipSet>, reserved: u32) -> Self { - Self { - arena, - map: set, - reserved, - } - } } -impl GenericOrderWalCore -where - K: Type + Ord + 'static, - for<'a> ::Ref<'a>: KeyRef<'a, K>, - V: Type + 'static, -{ - fn replay( - arena: Arena, - opts: &Options, - ro: bool, - checksumer: &mut S, - ) -> Result { - let slice = arena.reserved_slice(); - let magic_text = &slice[0..6]; - let magic_version = u16::from_le_bytes(slice[6..8].try_into().unwrap()); - - if magic_text != MAGIC_TEXT { - return Err(Error::magic_text_mismatch()); - } - - if magic_version != opts.magic_version() { - return Err(Error::magic_version_mismatch()); - } - - let map = SkipSet::new(); - - let mut cursor = arena.data_offset(); - let allocated = arena.allocated(); - - loop { - unsafe { - // we reached the end of the arena, if we have any remaining, then if means two possibilities: - // 1. the remaining is a partial entry, but it does not be persisted to the disk, so following the write-ahead log principle, we should discard it. - // 2. our file may be corrupted, so we discard the remaining. - if cursor + STATUS_SIZE > allocated { - if !ro && cursor < allocated { - arena.rewind(ArenaPosition::Start(cursor as u32)); - arena.flush()?; - } +impl Constructor<(), S> for GenericOrderWal { + type Allocator = Arena; - break; - } - - let header = arena.get_u8(cursor).unwrap(); - let flag = Flags::from_bits_unchecked(header); - - let (kvsize, encoded_len) = arena.get_u64_varint(cursor + STATUS_SIZE).map_err(|_e| { - #[cfg(feature = "tracing")] - tracing::error!(err=%_e); - - Error::corrupted() - })?; - let (key_len, value_len) = split_lengths(encoded_len); - let key_len = key_len as usize; - let value_len = value_len as usize; - - // Same as above, if we reached the end of the arena, we should discard the remaining. - let cks_offset = STATUS_SIZE + kvsize + key_len + value_len; - if cks_offset + CHECKSUM_SIZE > allocated { - if !ro { - arena.rewind(ArenaPosition::Start(cursor as u32)); - arena.flush()?; - } + type Core = GenericOrderWalCore; - break; - } - - let cks = arena.get_u64_le(cursor + cks_offset).unwrap(); + type Pointer = GenericPointer; - if cks != checksumer.checksum(arena.get_bytes(cursor, cks_offset)) { - return Err(Error::corrupted()); - } - - // If the entry is not committed, we should not rewind - if !flag.contains(Flags::COMMITTED) { - if !ro { - arena.rewind(ArenaPosition::Start(cursor as u32)); - arena.flush()?; - } - - break; - } + fn allocator(&self) -> &Self::Allocator { + &self.core.arena + } - map.insert(Pointer::new( - key_len, - value_len, - arena.get_pointer(cursor + STATUS_SIZE + kvsize), - )); - cursor += cks_offset + CHECKSUM_SIZE; - } + fn from_core(core: Self::Core) -> Self { + Self { + core: Arc::new(core), + ro: false, } - - Ok(Self::construct(arena, map, opts.reserved())) } } -impl GenericOrderWalCore +impl GenericOrderWalCore where K: Type + Ord, for<'a> ::Ref<'a>: KeyRef<'a, K>, @@ -507,33 +362,33 @@ where } #[inline] - fn get<'a, Q>(&'a self, key: &'a Q) -> Option> + fn get<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable> + Comparable, { self .map .get::>(&Owned::new(key)) - .map(EntryRef::new) + .map(GenericEntryRef::new) } #[inline] - fn get_by_ref<'a, Q>(&'a self, key: &'a Q) -> Option> + fn get_by_ref<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable>, { self .map .get::>(&Ref::new(key)) - .map(EntryRef::new) + .map(GenericEntryRef::new) } #[inline] - unsafe fn get_by_bytes(&self, key: &[u8]) -> Option> { + unsafe fn get_by_bytes(&self, key: &[u8]) -> Option> { self .map .get(&PartialPointer::new(key.len(), key.as_ptr())) - .map(EntryRef::new) + .map(GenericEntryRef::new) } } @@ -543,42 +398,10 @@ where /// /// Users can create multiple readers from the WAL by [`GenericOrderWal::reader`], but only one writer is allowed. pub struct GenericOrderWal { - core: Arc>, - opts: Options, - cks: UnsafeCellChecksumer, + core: Arc>, ro: bool, } -impl GenericOrderWal { - /// Creates a new in-memory write-ahead log backed by an aligned vec with the given capacity and options. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::GenericOrderWal, Options}; - /// - /// let wal = GenericOrderWal::::new(Options::new().with_capacity(1024)).unwrap(); - /// ``` - #[inline] - pub fn new(opts: Options) -> Result { - Self::with_checksumer(opts, Crc32::default()) - } - - /// Creates a new in-memory write-ahead log backed by an anonymous memory map with the given options. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::GenericOrderWal, Options}; - /// - /// let wal = GenericOrderWal::::map_anon(Options::new().with_capacity(1024)).unwrap(); - /// ``` - #[inline] - pub fn map_anon(opts: Options) -> Result { - Self::map_anon_with_checksumer(opts, Crc32::default()) - } -} - impl GenericOrderWal where K: Type + Ord + 'static, @@ -586,13 +409,13 @@ where { /// Returns the first key-value pair in the map. The key in this pair is the minimum key in the wal. #[inline] - pub fn first(&self) -> Option> { + pub fn first(&self) -> Option> { self.core.first() } /// Returns the last key-value pair in the map. The key in this pair is the maximum key in the wal. #[inline] - pub fn last(&self) -> Option> { + pub fn last(&self) -> Option> { self.core.last() } @@ -629,140 +452,14 @@ where } } -impl GenericOrderWal +impl GenericOrderWal where - K: Type + Ord + 'static, - for<'a> ::Ref<'a>: KeyRef<'a, K>, - V: Type + 'static, + K: 'static, + V: 'static, { - /// Creates a new write-ahead log backed by a file backed memory map with the given options. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::{GenericOrderWal, generic::*}, OpenOptions, Options}; - /// - /// # let dir = tempfile::tempdir().unwrap(); - /// # let path = dir - /// # .path() - /// # .join("generic_wal_map_mut"); - /// - /// let mut wal = unsafe { - /// GenericOrderWal::::map_mut( - /// &path, - /// Options::new(), - /// OpenOptions::new() - /// .create_new(Some(1024)) - /// .write(true) - /// .read(true), - /// ) - /// .unwrap() - /// }; - /// ``` - #[inline] - pub unsafe fn map_mut>( - path: P, - opts: Options, - open_options: OpenOptions, - ) -> Result { - Self::map_mut_with_path_builder::<_, ()>(|| dummy_path_builder(path), opts, open_options) - .map_err(|e| e.unwrap_right()) - } - - /// Creates a new write-ahead log backed by a file backed memory map with the given options. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::{GenericOrderWal, generic::*}, OpenOptions, Options}; - /// - /// let dir = tempfile::tempdir().unwrap(); - /// - /// let mut wal = unsafe { - /// GenericOrderWal::::map_mut_with_path_builder::<_, ()>( - /// || { - /// Ok(dir.path().join("generic_wal_map_mut_with_path_builder")) - /// }, - /// Options::new(), - /// OpenOptions::new() - /// .create_new(Some(1024)) - /// .write(true) - /// .read(true), - /// ) - /// .unwrap() - /// }; - /// ``` - #[inline] - pub unsafe fn map_mut_with_path_builder( - pb: PB, - opts: Options, - open_options: OpenOptions, - ) -> Result> - where - PB: FnOnce() -> Result, - { - Self::map_mut_with_path_builder_and_checksumer(pb, opts, open_options, Crc32::default()) - } - - /// Open a write-ahead log backed by a file backed memory map in read only mode. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - #[inline] - pub unsafe fn map>( - path: P, - opts: Options, - ) -> Result, Error> { - Self::map_with_path_builder::<_, ()>(|| dummy_path_builder(path), opts) - .map_err(|e| e.unwrap_right()) - } - - /// Open a write-ahead log backed by a file backed memory map in read only mode. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - #[inline] - pub unsafe fn map_with_path_builder( - pb: PB, - opts: Options, - ) -> Result, Either> - where - PB: FnOnce() -> Result, - { - Self::map_with_path_builder_and_checksumer(pb, opts, Crc32::default()) - } -} - -impl GenericOrderWal { /// Returns a read-only WAL instance. #[inline] - pub fn reader(&self) -> GenericWalReader { + pub fn reader(&self) -> GenericWalReader { GenericWalReader::new(self.core.clone()) } @@ -774,12 +471,12 @@ impl GenericOrderWal { /// Returns the reserved space in the WAL. /// - /// # Safety + /// ## Safety /// - The writer must ensure that the returned slice is not modified. /// - This method is not thread-safe, so be careful when using it. #[inline] pub unsafe fn reserved_slice(&self) -> &[u8] { - if self.opts.reserved() == 0 { + if self.core.opts.reserved() == 0 { return &[]; } @@ -788,12 +485,12 @@ impl GenericOrderWal { /// Returns the mutable reference to the reserved slice. /// - /// # Safety + /// ## Safety /// - The caller must ensure that the there is no others accessing reserved slice for either read or write. /// - This method is not thread-safe, so be careful when using it. #[inline] pub unsafe fn reserved_slice_mut(&mut self) -> &mut [u8] { - if self.opts.reserved() == 0 { + if self.core.opts.reserved() == 0 { return &mut []; } @@ -811,248 +508,6 @@ impl GenericOrderWal { pub fn is_empty(&self) -> bool { self.core.is_empty() } - - /// Creates a new in-memory write-ahead log backed by an aligned vec with the given options and [`Checksumer`]. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::GenericOrderWal, Options, Crc32}; - /// - /// let wal = GenericOrderWal::::with_checksumer(Options::new().with_capacity(1024), Crc32::default()); - /// ``` - pub fn with_checksumer(opts: Options, cks: S) -> Result { - let arena = Arena::new(arena_options(opts.reserved()).with_capacity(opts.capacity())) - .map_err(Error::from_insufficient_space)?; - - GenericOrderWalCore::new(arena, opts.magic_version(), false, opts.reserved()) - .map(|core| Self::from_core(core, opts, cks, false)) - } - - /// Creates a new in-memory write-ahead log backed by an anonymous memory map with the given options and [`Checksumer`]. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::GenericOrderWal, Options, Crc32}; - /// - /// let wal = GenericOrderWal::::map_anon_with_checksumer(Options::new().with_capacity(1024), Crc32::default()).unwrap(); - /// ``` - pub fn map_anon_with_checksumer(opts: Options, cks: S) -> Result { - let arena = Arena::map_anon( - arena_options(opts.reserved()), - MmapOptions::new().len(opts.capacity()), - )?; - - GenericOrderWalCore::new(arena, opts.magic_version(), true, opts.reserved()) - .map(|core| Self::from_core(core, opts, cks, false)) - } - - #[inline] - fn from_core(core: GenericOrderWalCore, opts: Options, cks: S, ro: bool) -> Self { - Self { - core: Arc::new(core), - ro, - opts, - cks: UnsafeCellChecksumer::new(cks), - } - } -} - -impl GenericOrderWal -where - K: Type + Ord + 'static, - for<'a> ::Ref<'a>: KeyRef<'a, K>, - V: Type + 'static, - S: Checksumer, -{ - /// Returns a write-ahead log backed by a file backed memory map with the given options and [`Checksumer`]. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::{GenericOrderWal, generic::*}, Crc32, OpenOptions, Options}; - /// - /// # let dir = tempfile::tempdir().unwrap(); - /// # let path = dir - /// # .path() - /// # .join("generic_wal_map_mut_with_checksumer"); - /// - /// let mut wal = unsafe { - /// GenericOrderWal::::map_mut_with_checksumer( - /// &path, - /// Options::new(), - /// OpenOptions::new() - /// .create_new(Some(1024)) - /// .write(true) - /// .read(true), - /// Crc32::default(), - /// ) - /// .unwrap() - /// }; - /// ``` - #[inline] - pub unsafe fn map_mut_with_checksumer>( - path: P, - opts: Options, - open_options: OpenOptions, - cks: S, - ) -> Result { - Self::map_mut_with_path_builder_and_checksumer::<_, ()>( - || dummy_path_builder(path), - opts, - open_options, - cks, - ) - .map_err(|e| e.unwrap_right()) - } - - /// Returns a write-ahead log backed by a file backed memory map with the given options and [`Checksumer`]. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::{GenericOrderWal, generic::*}, Crc32, OpenOptions, Options}; - /// - /// let dir = tempfile::tempdir().unwrap(); - /// - /// let mut wal = unsafe { - /// GenericOrderWal::::map_mut_with_path_builder_and_checksumer::<_, ()>( - /// || { - /// Ok(dir.path().join("generic_wal_map_mut_with_path_builder_and_checksumer")) - /// }, - /// Options::new(), - /// OpenOptions::new() - /// .create_new(Some(1024)) - /// .write(true) - /// .read(true), - /// Crc32::default(), - /// ) - /// .unwrap() - /// }; - /// ``` - pub unsafe fn map_mut_with_path_builder_and_checksumer( - path_builder: PB, - opts: Options, - open_options: OpenOptions, - mut cks: S, - ) -> Result> - where - PB: FnOnce() -> Result, - { - let path = path_builder().map_err(Either::Left)?; - let exist = path.exists(); - let arena = Arena::map_mut_with_path_builder( - || Ok(path), - arena_options(opts.reserved()), - open_options, - MmapOptions::new(), - ) - .map_err(|e| e.map_right(Into::into))?; - - if !exist { - return GenericOrderWalCore::new(arena, opts.magic_version(), true, opts.reserved()) - .map(|core| Self::from_core(core, opts, cks, false)) - .map_err(Either::Right); - } - - GenericOrderWalCore::replay(arena, &opts, false, &mut cks) - .map(|core| Self::from_core(core, opts, cks, false)) - .map_err(Either::Right) - } - - /// Open a write-ahead log backed by a file backed memory map in read only mode with the given [`Checksumer`]. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::{GenericOrderWal, generic::*}, Crc32, OpenOptions, Options}; - /// - /// # let dir = tempfile::tempdir().unwrap(); - /// # let path = dir - /// # .path() - /// # .join("generic_wal_map_mut_with_checksumer"); - /// - /// # let mut wal = unsafe { - /// # GenericOrderWal::::map_mut_with_checksumer( - /// # &path, - /// # Options::new(), - /// # OpenOptions::new() - /// # .create_new(Some(1024)) - /// # .write(true) - /// # .read(true), - /// # Crc32::default(), - /// # ) - /// # .unwrap() - /// # }; - /// - /// let reader = unsafe { GenericOrderWal::::map_with_checksumer(&path, Options::new(), Crc32::default()).unwrap() }; - /// ``` - #[inline] - pub unsafe fn map_with_checksumer>( - path: P, - opts: Options, - cks: S, - ) -> Result, Error> { - Self::map_with_path_builder_and_checksumer::<_, ()>(|| dummy_path_builder(path), opts, cks) - .map_err(|e| e.unwrap_right()) - } - - /// Open a write-ahead log backed by a file backed memory map in read only mode with the given [`Checksumer`]. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - #[inline] - pub unsafe fn map_with_path_builder_and_checksumer( - path_builder: PB, - opts: Options, - mut cks: S, - ) -> Result, Either> - where - PB: FnOnce() -> Result, - { - let open_options = OpenOptions::default().read(true); - let arena = Arena::map_with_path_builder( - path_builder, - arena_options(opts.reserved()), - open_options, - MmapOptions::new(), - ) - .map_err(|e| e.map_right(Into::into))?; - - GenericOrderWalCore::replay(arena, &opts, true, &mut cks) - .map(|core| GenericWalReader::new(Arc::new(core))) - .map_err(Either::Right) - } } impl GenericOrderWal @@ -1072,7 +527,7 @@ where /// Returns `true` if the key exists in the WAL. /// - /// # Safety + /// ## Safety /// - The given `key` must be valid to construct to `K::Ref` without remaining. #[inline] pub unsafe fn contains_key_by_bytes(&self, key: &[u8]) -> bool { @@ -1090,7 +545,7 @@ where /// Gets the value associated with the key. #[inline] - pub fn get<'a, Q>(&'a self, key: &'a Q) -> Option> + pub fn get<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable> + Comparable, { @@ -1099,7 +554,7 @@ where /// Gets the value associated with the key. #[inline] - pub fn get_by_ref<'a, Q>(&'a self, key: &'a Q) -> Option> + pub fn get_by_ref<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable>, { @@ -1108,10 +563,10 @@ where /// Gets the value associated with the key. /// - /// # Safety + /// ## Safety /// - The given `key` must be valid to construct to `K::Ref` without remaining. #[inline] - pub unsafe fn get_by_bytes(&self, key: &[u8]) -> Option> { + pub unsafe fn get_by_bytes(&self, key: &[u8]) -> Option> { self.core.get_by_bytes(key) } } @@ -1121,150 +576,45 @@ where K: Type + Ord + for<'a> Comparable> + 'static, for<'a> K::Ref<'a>: KeyRef<'a, K>, V: Type + 'static, - S: Checksumer, + S: BuildChecksumer, { /// Gets or insert the key value pair. #[inline] - pub fn get_or_insert( - &mut self, - key: &K, - value: &V, - ) -> Either, Result<(), Among>> { - let ent = self - .core - .map - .get(&Owned::new(key)) - .map(|e| Either::Left(EntryRef::new(e))); - - match ent { - Some(e) => e, - None => { - let p = self.insert_in(Among::Middle(key), Among::Middle(value)); - Either::Right(p) - } - } - } - - /// Gets or insert the key value pair. - #[inline] - pub fn get_or_insert_with( - &mut self, - key: &K, - value: impl FnOnce() -> V, - ) -> Either, Result<(), Among>> { - let ent = self - .core - .map - .get(&Ref::new(key)) - .map(|e| Either::Left(EntryRef::new(e))); - - match ent { - Some(e) => e, - None => { - let p = self.insert_in(Among::Middle(key), Among::Left(value())); - Either::Right(p) - } - } - } - - /// Gets or insert the key value pair. - /// - /// # Safety - /// - The given `key` and `value` must be valid to construct to `K::Ref` and `V::Ref` without remaining. - #[inline] - pub unsafe fn get_by_bytes_or_insert_bytes( - &mut self, - key: &[u8], - value: &[u8], - ) -> Either, Result<(), Error>> { - let ent = self - .core - .map - .get(&PartialPointer::new(key.len(), key.as_ptr())) - .map(|e| Either::Left(EntryRef::new(e))); - - match ent { - Some(e) => e, - None => match self.insert_in(Among::Right(key), Among::Right(value)) { - Ok(_) => Either::Right(Ok(())), - Err(Among::Right(e)) => Either::Right(Err(e)), - _ => unreachable!(), - }, - } - } - - /// Gets or insert the key value pair. - /// - /// # Safety - /// - The given `value` must be valid to construct to `V::Ref` without remaining. - #[inline] - pub unsafe fn get_or_insert_bytes( - &mut self, - key: &K, - value: &[u8], - ) -> Either, Result<(), Either>> { - let ent = self - .core - .map - .get(&Owned::new(key)) - .map(|e| Either::Left(EntryRef::new(e))); - - match ent { - Some(e) => e, - None => match self.insert_in(Among::Middle(key), Among::Right(value)) { - Ok(_) => Either::Right(Ok(())), - Err(e) => Either::Right(Err(e.into_left_right())), - }, - } - } - - /// Gets or insert the key value pair. - /// - /// # Safety - /// - The given `key` must be valid to construct to `K::Ref` without remaining. - #[inline] - pub unsafe fn get_by_bytes_or_insert( + pub fn get_or_insert<'a>( &mut self, - key: &[u8], - value: &V, - ) -> Either, Result<(), Either>> { - let ent = self - .core - .map - .get(&PartialPointer::new(key.len(), key.as_ptr())) - .map(|e| Either::Left(EntryRef::new(e))); - - match ent { + key: impl Into>, + value: impl Into>, + ) -> Either, Result<(), Among>> { + let key: Generic<'a, K> = key.into(); + let map = &self.core.map; + let ent = match key.data() { + Either::Left(k) => map.get(&Owned::new(k)), + Either::Right(key) => map.get(&PartialPointer::new(key.len(), key.as_ptr())), + }; + + match ent.map(|e| Either::Left(GenericEntryRef::new(e))) { Some(e) => e, - None => match self.insert_in(Among::Right(key), Among::Middle(value)) { - Ok(_) => Either::Right(Ok(())), - Err(e) => Either::Right(Err(e.into_middle_right())), - }, + None => Either::Right(self.insert_in(key.into_among(), value.into().into_among())), } } - /// Gets or insert the key value pair. - /// - /// # Safety - /// - The given `key` must be valid to construct to `K::Ref` without remaining. + /// Gets or insert the key with a value builder. #[inline] - pub unsafe fn get_by_bytes_or_insert_with( + pub fn get_or_insert_with<'a>( &mut self, - key: &[u8], - value: impl FnOnce() -> V, - ) -> Either, Result<(), Either>> { - let ent = self - .core - .map - .get(&PartialPointer::new(key.len(), key.as_ptr())) - .map(|e| Either::Left(EntryRef::new(e))); - - match ent { + key: impl Into>, + value: impl FnOnce() -> Generic<'a, V>, + ) -> Either, Result<(), Among>> { + let key: Generic<'a, K> = key.into(); + let map = &self.core.map; + let ent = match key.data() { + Either::Left(k) => map.get(&Owned::new(k)), + Either::Right(key) => map.get(&PartialPointer::new(key.len(), key.as_ptr())), + }; + + match ent.map(|e| Either::Left(GenericEntryRef::new(e))) { Some(e) => e, - None => match self.insert_in(Among::Right(key), Among::Left(value())) { - Ok(_) => Either::Right(Ok(())), - Err(e) => Either::Right(Err(e.into_middle_right())), - }, + None => Either::Right(self.insert_in(key.into_among(), value().into_among())), } } } @@ -1274,195 +624,186 @@ where K: Type + Ord + 'static, for<'a> K::Ref<'a>: KeyRef<'a, K>, V: Type + 'static, - S: Checksumer, + S: BuildChecksumer, { /// Inserts a key-value pair into the write-ahead log. - #[inline] - pub fn insert(&mut self, key: &K, val: &V) -> Result<(), Among> { - self.insert_in(Among::Middle(key), Among::Middle(val)) - } - - /// Inserts a key in structured format and value in bytes format into the write-ahead log directly. /// - /// # Safety - /// - The given `value` must be valid to construct to `V::Ref` without remaining. + /// ## Example /// - /// # Example + /// Here are three examples of how flexible the `insert` method is: /// - /// See [`insert_bytes`](GenericOrderWal::insert_bytes) for more details. - #[inline] - pub unsafe fn insert_key_with_value_bytes( - &mut self, - key: &K, - value: &[u8], - ) -> Result<(), Either> { - self - .insert_in(Among::Middle(key), Among::Right(value)) - .map_err(Among::into_left_right) - } - - /// Inserts a key in bytes format and value in structured format into the write-ahead log directly. + /// The `Person` struct implementation can be found [here](https://github.com/al8n/orderwal/blob/main/src/swmr/generic/tests.rs#L24). /// - /// # Safety - /// - The given `key` and `value` must be valid to construct to `K::Ref` and `V::Ref` without remaining. + /// 1. **Inserting an owned key-value pair with structured key and value** /// - /// # Example + /// ```rust,ignore + /// use orderwal::swmr::{*, generic::*}; /// - /// ```rust - /// use orderwal::{swmr::{*, generic::*}, utils::*, Options}; - /// use std::cmp; + /// let person = Person { + /// id: 1, + /// name: "Alice".to_string(), + /// }; /// - /// #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] - /// struct Person { - /// id: u64, - /// name: String, - /// } + /// let mut wal = GenericBuilder::new().with_capacity(1024).alloc::().unwrap(); + /// let value = "Hello, Alice!".to_string(); + /// wal.insert(&person, value); + /// ``` /// - /// #[derive(Debug)] - /// struct PersonRef<'a> { - /// id: u64, - /// name: &'a str, - /// } + /// 2. **Inserting a key-value pair, key is a reference, value is owned** + /// + /// ```rust,ignore + /// use orderwal::swmr::{*, generic::*}; /// - /// impl<'a> PartialEq for PersonRef<'a> { - /// fn eq(&self, other: &Self) -> bool { - /// self.id == other.id && self.name == other.name - /// } - /// } + /// let mut wal = GenericBuilder::new().with_capacity(1024).alloc::().unwrap(); /// - /// impl<'a> Eq for PersonRef<'a> {} + /// let person = Person { + /// id: 1, + /// name: "Alice".to_string(), + /// }; /// - /// impl<'a> PartialOrd for PersonRef<'a> { - /// fn partial_cmp(&self, other: &Self) -> Option { - /// Some(self.cmp(other)) - /// } - /// } + /// wal.insert(&person, "value".to_string()); + /// ``` /// - /// impl<'a> Ord for PersonRef<'a> { - /// fn cmp(&self, other: &Self) -> cmp::Ordering { - /// self - /// .id - /// .cmp(&other.id) - /// .then_with(|| self.name.cmp(other.name)) - /// } - /// } + /// 3. **Inserting a key-value pair, both of them are in encoded format** /// - /// impl Equivalent for PersonRef<'_> { - /// fn equivalent(&self, key: &Person) -> bool { - /// self.id == key.id && self.name == key.name - /// } - /// } + /// ```rust,ignore + /// use orderwal::swmr::{*, generic::*}; + /// + /// let mut wal = GenericBuilder::new().with_capacity(1024).alloc::().unwrap(); /// - /// impl Comparable for PersonRef<'_> { - /// fn compare(&self, key: &Person) -> std::cmp::Ordering { - /// self.id.cmp(&key.id).then_with(|| self.name.cmp(&key.name)) - /// } - /// } + /// let person = Person { + /// id: 1, + /// name: "Alice".to_string(), + /// }.encode_into_vec(); /// - /// impl Equivalent> for Person { - /// fn equivalent(&self, key: &PersonRef<'_>) -> bool { - /// self.id == key.id && self.name == key.name - /// } - /// } /// - /// impl Comparable> for Person { - /// fn compare(&self, key: &PersonRef<'_>) -> std::cmp::Ordering { - /// self - /// .id - /// .cmp(&key.id) - /// .then_with(|| self.name.as_str().cmp(key.name)) - /// } - /// } - /// - /// impl<'a> KeyRef<'a, Person> for PersonRef<'a> { - /// fn compare(&self, a: &Q) -> cmp::Ordering - /// where - /// Q: ?Sized + Ord + Comparable, - /// { - /// Comparable::compare(a, self).reverse() - /// } - /// - /// fn compare_binary(this: &[u8], other: &[u8]) -> cmp::Ordering { - /// let (this_id_size, this_id) = decode_u64_varint(this).unwrap(); - /// let (other_id_size, other_id) = decode_u64_varint(other).unwrap(); - /// - /// PersonRef { - /// id: this_id, - /// name: std::str::from_utf8(&this[this_id_size..]).unwrap(), + /// unsafe { + /// let key = Generic::from_slice(person.as_ref()); + /// let value = Generic::from_slice("Hello, Alice!".as_bytes()); + /// wal.insert(key, value).unwrap(); /// } - /// .cmp(&PersonRef { - /// id: other_id, - /// name: std::str::from_utf8(&other[other_id_size..]).unwrap(), - /// }) - /// } - /// } - /// - /// impl Type for Person { - /// type Ref<'a> = PersonRef<'a>; - /// type Error = EncodeVarintError; - /// - /// fn encoded_len(&self) -> usize { - /// encoded_u64_varint_len(self.id) + self.name.len() - /// } - /// - /// fn encode(&self, buf: &mut [u8]) -> Result<(), Self::Error> { - /// let id_size = encode_u64_varint(self.id, buf)?; - /// buf[id_size..].copy_from_slice(self.name.as_bytes()); - /// Ok(()) - /// } - /// } - /// - /// impl<'a> TypeRef<'a> for PersonRef<'a> { - /// fn from_slice(src: &'a [u8]) -> Self { - /// let (id_size, id) = decode_u64_varint(src).unwrap(); - /// let name = std::str::from_utf8(&src[id_size..]).unwrap(); - /// PersonRef { id, name } - /// } - /// } - /// - /// let mut wal = GenericOrderWal::::new(Options::new().with_capacity(1024)).unwrap(); - /// - /// # let key = Person { - /// # id: 1, - /// # name: "Alice".to_string(), - /// # }; - /// - /// - /// # let mut person = vec![0; key.encoded_len()]; - /// # key.encode(&mut person).unwrap(); - /// - /// // Assume `person` comes from somewhere else, e.g. from the network. - /// unsafe { - /// wal.insert_bytes(person.as_ref(), b"Hello, Alice!").unwrap(); - /// } - /// ``` + /// ``` #[inline] - pub unsafe fn insert_bytes(&mut self, key: &[u8], value: &[u8]) -> Result<(), Error> { - self - .insert_in(Among::Right(key), Among::Right(value)) - .map_err(|e| match e { - Among::Right(e) => e, - _ => unreachable!(), - }) + pub fn insert<'a>( + &mut self, + key: impl Into>, + val: impl Into>, + ) -> Result<(), Among> { + self.insert_in(key.into().into_among(), val.into().into_among()) } - /// Inserts a key in bytes format and value in structured format into the write-ahead log directly. - /// - /// # Safety - /// - The given `key` must be valid to construct to `K::Ref` without remaining. - /// - /// # Example - /// - /// See [`insert_bytes`](GenericOrderWal::insert_bytes) for more details. - #[inline] - pub unsafe fn insert_key_bytes_with_value( - &mut self, - key: &[u8], - value: &V, - ) -> Result<(), Either> { - self - .insert_in(Among::Right(key), Among::Middle(value)) - .map_err(Among::into_middle_right) + /// Inserts a batch of entries into the write-ahead log. + pub fn insert_batch<'a, 'b: 'a, B: GenericBatch<'b, Key = K, Value = V>>( + &'a mut self, + batch: &'b mut B, + ) -> Result<(), Among> { + // TODO: is there another way to avoid the borrow checker? + let batch_ptr = AtomicPtr::new(batch); + + let batch = batch_ptr.load(Ordering::Acquire); + let (num_entries, batch_encoded_size) = unsafe { + (*batch) + .iter_mut() + .try_fold((0u32, 0u64), |(num_entries, size), ent| { + let klen = ent.key.encoded_len(); + let vlen = ent.value.encoded_len(); + crate::utils::check_batch_entry( + klen, + vlen, + self.core.opts.maximum_key_size(), + self.core.opts.maximum_value_size(), + )?; + let merged_len = merge_lengths(klen as u32, vlen as u32); + let merged_len_size = encoded_u64_varint_len(merged_len); + + let ent_size = klen as u64 + vlen as u64 + merged_len_size as u64; + ent.meta = BatchEncodedEntryMeta::new(klen, vlen, merged_len, merged_len_size); + Ok((num_entries + 1, size + ent_size)) + }) + .map_err(Among::Right)? + }; + + // safe to cast batch_encoded_size to u32 here, we already checked it's less than capacity (less than u32::MAX). + let batch_meta = merge_lengths(num_entries, batch_encoded_size as u32); + let batch_meta_size = encoded_u64_varint_len(batch_meta); + let allocator = self.allocator(); + let remaining = allocator.remaining() as u64; + let total_size = + STATUS_SIZE as u64 + batch_meta_size as u64 + batch_encoded_size + CHECKSUM_SIZE as u64; + if total_size > remaining { + return Err(Among::Right(Error::insufficient_space( + total_size, + remaining as u32, + ))); + } + + let mut buf = allocator + .alloc_bytes(total_size as u32) + .map_err(|e| Among::Right(Error::from_insufficient_space(e)))?; + + unsafe { + let committed_flag = Flags::BATCHING | Flags::COMMITTED; + let mut cks = self.core.cks.build_checksumer(); + let flag = Flags::BATCHING; + buf.put_u8_unchecked(flag.bits); + buf.put_u64_varint_unchecked(batch_meta); + let mut cursor = STATUS_SIZE + batch_meta_size; + + { + let batch = batch_ptr.load(Ordering::Acquire); + for ent in (*batch).iter_mut() { + let remaining = buf.remaining(); + if remaining < ent.meta.kvlen_size + ent.meta.klen + ent.meta.vlen { + return Err(Among::Right(Error::larger_batch_size(total_size as u32))); + } + + let ent_len_size = buf.put_u64_varint_unchecked(ent.meta.kvlen); + let ko = cursor as usize + ent_len_size; + buf.set_len(ko + ent.meta.klen + ent.meta.vlen); + let ptr = buf.as_mut_ptr().add(ko); + + let key_buf = slice::from_raw_parts_mut(ptr, ent.meta.klen); + ent.key.encode(key_buf).map_err(Among::Left)?; + let value_buf = slice::from_raw_parts_mut(ptr.add(ent.meta.klen), ent.meta.vlen); + ent.value.encode(value_buf).map_err(Among::Middle)?; + + cursor += ent_len_size + ent.meta.klen + ent.meta.vlen; + ent.pointer = Some(GenericPointer::new(ent.meta.klen, ent.meta.vlen, ptr)); + } + } + + if (cursor + CHECKSUM_SIZE) as u64 != total_size { + return Err(Among::Right(Error::batch_size_mismatch( + total_size as u32 - CHECKSUM_SIZE as u32, + cursor as u32, + ))); + } + + cks.update(&[committed_flag.bits]); + cks.update(&buf[1..]); + let checksum = cks.digest(); + buf.put_u64_le_unchecked(checksum); + + // commit the entry + buf[0] = committed_flag.bits; + let buf_cap = buf.capacity(); + + if self.core.opts.sync_on_write() && allocator.is_ondisk() { + allocator + .flush_range(buf.offset(), buf_cap) + .map_err(|e| Among::Right(e.into()))?; + } + buf.detach(); + + { + let batch = batch_ptr.load(Ordering::Acquire); + (*batch).iter_mut().for_each(|ent| { + self.core.map.insert(ent.pointer.take().unwrap()); + }); + } + + Ok(()) + } } fn insert_in( @@ -1486,8 +827,8 @@ where // We allocate the buffer with the exact size, so it's safe to write to the buffer. let flag = Flags::COMMITTED.bits(); - self.cks.reset(); - self.cks.update(&[flag]); + let mut cks = self.core.cks.build_checksumer(); + cks.update(&[flag]); buf.put_u8_unchecked(Flags::empty().bits()); let written = buf.put_u64_varint_unchecked(kvlen); @@ -1507,15 +848,15 @@ where val.encode(value_buf).map_err(Among::Middle)?; let cks = { - self.cks.update(&buf[1..]); - self.cks.digest() + cks.update(&buf[1..]); + cks.digest() }; buf.put_u64_le_unchecked(cks); // commit the entry buf[0] |= Flags::COMMITTED.bits(); - if self.opts.sync_on_write() && self.core.arena.is_ondisk() { + if self.core.opts.sync_on_write() && self.core.arena.is_ondisk() { self .core .arena @@ -1524,7 +865,7 @@ where } buf.detach(); - let p = Pointer::new(klen, vlen, buf.as_ptr().add(ko)); + let p = GenericPointer::new(klen, vlen, buf.as_ptr().add(ko)); self.core.map.insert(p); Ok(()) } @@ -1537,8 +878,8 @@ where check( klen, vlen, - self.opts.maximum_key_size(), - self.opts.maximum_value_size(), + self.core.opts.maximum_key_size(), + self.core.opts.maximum_value_size(), self.ro, ) } diff --git a/src/swmr/generic/builder.rs b/src/swmr/generic/builder.rs new file mode 100644 index 00000000..a3a47eba --- /dev/null +++ b/src/swmr/generic/builder.rs @@ -0,0 +1,584 @@ +use std::path::{Path, PathBuf}; + +use super::*; + +/// A write-ahead log builder. +pub struct GenericBuilder { + pub(super) opts: Options, + pub(super) cks: S, +} + +impl Default for GenericBuilder { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl GenericBuilder { + /// Returns a new write-ahead log builder with the given options. + #[inline] + pub fn new() -> Self { + Self { + opts: Options::default(), + cks: Crc32::default(), + } + } +} + +impl GenericBuilder { + /// Returns a new write-ahead log builder with the new checksumer + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::GenericBuilder, Crc32}; + /// + /// let opts = GenericBuilder::new().with_checksumer(Crc32::new()); + /// ``` + #[inline] + pub fn with_checksumer(self, cks: NS) -> GenericBuilder { + GenericBuilder { + opts: self.opts, + cks, + } + } + + /// Returns a new write-ahead log builder with the new options + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::GenericBuilder, Options}; + /// + /// let opts = GenericBuilder::new().with_options(Options::default()); + /// ``` + #[inline] + pub fn with_options(self, opts: Options) -> Self { + Self { + opts, + cks: self.cks, + } + } + + /// Set the reserved bytes of the WAL. + /// + /// The `reserved` is used to configure the start position of the WAL. This is useful + /// when you want to add some bytes as your own WAL's header. + /// + /// The default reserved is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let opts = GenericBuilder::new().with_reserved(8); + /// ``` + #[inline] + pub const fn with_reserved(mut self, reserved: u32) -> Self { + self.opts = self.opts.with_reserved(reserved); + self + } + + /// Get the reserved of the WAL. + /// + /// The `reserved` is used to configure the start position of the WAL. This is useful + /// when you want to add some bytes as your own WAL's header. + /// + /// The default reserved is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let opts = GenericBuilder::new().with_reserved(8); + /// + /// assert_eq!(opts.reserved(), 8); + /// ``` + #[inline] + pub const fn reserved(&self) -> u32 { + self.opts.reserved() + } + + /// Returns the magic version. + /// + /// The default value is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_magic_version(1); + /// assert_eq!(options.magic_version(), 1); + /// ``` + #[inline] + pub const fn magic_version(&self) -> u16 { + self.opts.magic_version() + } + + /// Returns the capacity of the WAL. + /// + /// The default value is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_capacity(1000); + /// assert_eq!(options.capacity(), 1000); + /// ``` + #[inline] + pub const fn capacity(&self) -> u32 { + self.opts.capacity() + } + + /// Returns the maximum key length. + /// + /// The default value is `u16::MAX`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_maximum_key_size(1024); + /// assert_eq!(options.maximum_key_size(), 1024); + /// ``` + #[inline] + pub const fn maximum_key_size(&self) -> u32 { + self.opts.maximum_key_size() + } + + /// Returns the maximum value length. + /// + /// The default value is `u32::MAX`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_maximum_value_size(1024); + /// assert_eq!(options.maximum_value_size(), 1024); + /// ``` + #[inline] + pub const fn maximum_value_size(&self) -> u32 { + self.opts.maximum_value_size() + } + + /// Returns `true` if the WAL syncs on write. + /// + /// The default value is `true`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new(); + /// assert_eq!(options.sync_on_write(), true); + /// ``` + #[inline] + pub const fn sync_on_write(&self) -> bool { + self.opts.sync_on_write() + } + + /// Returns the bits of the page size. + /// + /// Configures the anonymous memory map to be allocated using huge pages. + /// + /// This option corresponds to the `MAP_HUGETLB` flag on Linux. It has no effect on Windows. + /// + /// The size of the requested page can be specified in page bits. + /// If not provided, the system default is requested. + /// The requested length should be a multiple of this, or the mapping will fail. + /// + /// This option has no effect on file-backed memory maps. + /// + /// The default value is `None`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_huge(64); + /// assert_eq!(options.huge(), Some(64)); + /// ``` + #[inline] + pub const fn huge(&self) -> Option { + self.opts.huge() + } + + /// Sets the capacity of the WAL. + /// + /// This configuration will be ignored when using file-backed memory maps. + /// + /// The default value is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_capacity(100); + /// assert_eq!(options.capacity(), 100); + /// ``` + #[inline] + pub const fn with_capacity(mut self, cap: u32) -> Self { + self.opts = self.opts.with_capacity(cap); + self + } + + /// Sets the maximum key length. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_maximum_key_size(1024); + /// assert_eq!(options.maximum_key_size(), 1024); + /// ``` + #[inline] + pub const fn with_maximum_key_size(mut self, size: u32) -> Self { + self.opts = self.opts.with_maximum_key_size(size); + self + } + + /// Sets the maximum value length. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_maximum_value_size(1024); + /// assert_eq!(options.maximum_value_size(), 1024); + /// ``` + #[inline] + pub const fn with_maximum_value_size(mut self, size: u32) -> Self { + self.opts = self.opts.with_maximum_value_size(size); + self + } + + /// Returns the bits of the page size. + /// + /// Configures the anonymous memory map to be allocated using huge pages. + /// + /// This option corresponds to the `MAP_HUGETLB` flag on Linux. It has no effect on Windows. + /// + /// The size of the requested page can be specified in page bits. + /// If not provided, the system default is requested. + /// The requested length should be a multiple of this, or the mapping will fail. + /// + /// This option has no effect on file-backed memory maps. + /// + /// The default value is `None`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_huge(64); + /// assert_eq!(options.huge(), Some(64)); + /// ``` + #[inline] + pub const fn with_huge(mut self, page_bits: u8) -> Self { + self.opts = self.opts.with_huge(page_bits); + self + } + + /// Sets the WAL to sync on write. + /// + /// The default value is `true`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_sync_on_write(false); + /// assert_eq!(options.sync_on_write(), false); + /// ``` + #[inline] + pub const fn with_sync_on_write(mut self, sync: bool) -> Self { + self.opts = self.opts.with_sync_on_write(sync); + self + } + + /// Sets the magic version. + /// + /// The default value is `0`. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::GenericBuilder; + /// + /// let options = GenericBuilder::new().with_magic_version(1); + /// assert_eq!(options.magic_version(), 1); + /// ``` + #[inline] + pub const fn with_magic_version(mut self, version: u16) -> Self { + self.opts = self.opts.with_magic_version(version); + self + } +} + +impl GenericBuilder { + /// Creates a new in-memory write-ahead log backed by an aligned vec with the given capacity and options. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::{GenericOrderWal, GenericBuilder}; + /// + /// let wal = GenericBuilder::new().with_capacity(1024).alloc::().unwrap(); + /// ``` + #[inline] + pub fn alloc(self) -> Result, Error> { + let Self { opts, cks } = self; + + arena_options(opts.reserved()) + .with_capacity(opts.capacity()) + .alloc() + .map_err(Error::from_insufficient_space) + .and_then(|arena| { + GenericOrderWal::new_in(arena, opts, (), cks).map(GenericOrderWal::from_core) + }) + } + + /// Creates a new in-memory write-ahead log backed by an anonymous memory map with the given options. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::swmr::{GenericOrderWal, GenericBuilder}; + /// + /// let wal = GenericBuilder::new().with_capacity(1024).map_anon::().unwrap(); + /// ``` + #[inline] + pub fn map_anon(self) -> Result, Error> { + let Self { opts, cks } = self; + + arena_options(opts.reserved()) + .map_anon(MmapOptions::new().len(opts.capacity())) + .map_err(Into::into) + .and_then(|arena| { + GenericOrderWal::new_in(arena, opts, (), cks).map(GenericOrderWal::from_core) + }) + } + + /// Open a write-ahead log backed by a file backed memory map in read only mode. + /// + /// ## Safety + /// + /// All file-backed memory map constructors are marked `unsafe` because of the potential for + /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or + /// out of process. Applications must consider the risk and take appropriate precautions when + /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. + /// unlinked) files exist but are platform specific and limited. + /// + /// ## Example + /// + /// ```rust + /// + /// use orderwal::{swmr::{GenericOrderWal, GenericBuilder, generic::*}, OpenOptions}; + /// # let dir = tempfile::tempdir().unwrap(); + /// # let path = dir + /// # .path() + /// # .join("generic_wal_map_mut_with_checksumer"); + /// # + /// # let mut wal = unsafe { + /// # GenericBuilder::new() + /// # .map_mut::( + /// # &path, + /// # OpenOptions::new() + /// # .create_new(Some(1024)) + /// # .write(true) + /// # .read(true), + /// # ) + /// # .unwrap() + /// # }; + /// + /// let reader = unsafe { GenericBuilder::new().map::(path).unwrap() }; + /// ``` + #[inline] + pub unsafe fn map>(self, path: P) -> Result, Error> + where + K: Type + Ord + 'static, + for<'a> K::Ref<'a>: KeyRef<'a, K>, + V: 'static, + { + self + .map_with_path_builder::(|| dummy_path_builder(path)) + .map_err(|e| e.unwrap_right()) + } + + /// Open a write-ahead log backed by a file backed memory map in read only mode with the given [`Checksumer`]. + /// + /// ## Safety + /// + /// All file-backed memory map constructors are marked `unsafe` because of the potential for + /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or + /// out of process. Applications must consider the risk and take appropriate precautions when + /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. + /// unlinked) files exist but are platform specific and limited. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::{GenericOrderWal, GenericBuilder, generic::*}, OpenOptions}; + /// # let dir = tempfile::tempdir().unwrap(); + /// # let path = dir + /// # .path() + /// # .join("generic_wal_map_mut_with_checksumer"); + /// # + /// # let mut wal = unsafe { + /// # GenericBuilder::new() + /// # .map_mut::( + /// # &path, + /// # OpenOptions::new() + /// # .create_new(Some(1024)) + /// # .write(true) + /// # .read(true), + /// # ) + /// # .unwrap() + /// # }; + /// + /// let reader = unsafe { GenericBuilder::new().map_with_path_builder::(|| Ok(path)).unwrap() }; + /// ``` + #[inline] + pub unsafe fn map_with_path_builder( + self, + path_builder: PB, + ) -> Result, Either> + where + K: Type + Ord + 'static, + for<'a> K::Ref<'a>: KeyRef<'a, K>, + V: 'static, + PB: FnOnce() -> Result, + { + let Self { cks, opts } = self; + let open_options = OpenOptions::default().read(true); + + arena_options(opts.reserved()) + .map_with_path_builder(path_builder, open_options, MmapOptions::new()) + .map_err(|e| e.map_right(Into::into)) + .and_then(|arena| { + GenericOrderWal::replay(arena, opts, true, (), cks) + .map(|core| GenericWalReader::new(Arc::new(core))) + .map_err(Either::Right) + }) + } + + /// Creates a new write-ahead log backed by a file backed memory map with the given options. + /// + /// ## Safety + /// + /// All file-backed memory map constructors are marked `unsafe` because of the potential for + /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or + /// out of process. Applications must consider the risk and take appropriate precautions when + /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. + /// unlinked) files exist but are platform specific and limited. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::{GenericOrderWal, GenericBuilder, generic::*}, OpenOptions}; + /// + /// # let dir = tempfile::tempdir().unwrap(); + /// # let path = dir + /// # .path() + /// # .join("generic_wal_map_mut"); + /// + /// let mut wal = unsafe { + /// GenericBuilder::new().map_mut::( + /// &path, + /// OpenOptions::new() + /// .create_new(Some(1024)) + /// .write(true) + /// .read(true), + /// ) + /// .unwrap() + /// }; + /// ``` + #[inline] + pub unsafe fn map_mut>( + self, + path: P, + open_options: OpenOptions, + ) -> Result, Error> + where + K: Type + Ord + 'static, + for<'a> K::Ref<'a>: KeyRef<'a, K>, + V: 'static, + { + self + .map_mut_with_path_builder::(|| dummy_path_builder(path), open_options) + .map_err(|e| e.unwrap_right()) + } + + /// Returns a write-ahead log backed by a file backed memory map with the given options and [`Checksumer`]. + /// + /// ## Safety + /// + /// All file-backed memory map constructors are marked `unsafe` because of the potential for + /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or + /// out of process. Applications must consider the risk and take appropriate precautions when + /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. + /// unlinked) files exist but are platform specific and limited. + /// + /// ## Example + /// + /// ```rust + /// use orderwal::{swmr::{GenericOrderWal, GenericBuilder, generic::*}, OpenOptions}; + /// + /// let dir = tempfile::tempdir().unwrap(); + /// + /// let mut wal = unsafe { + /// GenericBuilder::new().map_mut_with_path_builder::( + /// || { + /// Ok(dir.path().join("generic_wal_map_mut_with_path_builder_and_checksumer")) + /// }, + /// OpenOptions::new() + /// .create_new(Some(1024)) + /// .write(true) + /// .read(true), + /// ) + /// .unwrap() + /// }; + /// ``` + pub unsafe fn map_mut_with_path_builder( + self, + path_builder: PB, + open_options: OpenOptions, + ) -> Result, Either> + where + K: Type + Ord + 'static, + for<'a> K::Ref<'a>: KeyRef<'a, K>, + V: 'static, + PB: FnOnce() -> Result, + { + let Self { opts, cks } = self; + let path = path_builder().map_err(Either::Left)?; + let exist = path.exists(); + let arena = arena_options(opts.reserved()) + .map_mut_with_path_builder(|| Ok(path), open_options, MmapOptions::new()) + .map_err(|e| e.map_right(Into::into))?; + + if !exist { + GenericOrderWal::new_in(arena, opts, (), cks) + } else { + GenericOrderWal::replay(arena, opts, false, (), cks) + } + .map(GenericOrderWal::from_core) + .map_err(Either::Right) + } +} diff --git a/src/swmr/generic/entry.rs b/src/swmr/generic/entry.rs deleted file mode 100644 index c40d638e..00000000 --- a/src/swmr/generic/entry.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crossbeam_skiplist::set::Entry; - -use super::{Pointer, Type, TypeRef}; - -/// The reference to an entry in the [`GenericOrderWal`](super::GenericOrderWal). -pub struct EntryRef<'a, K, V> { - ent: Entry<'a, Pointer>, -} - -impl<'a, K, V> core::fmt::Debug for EntryRef<'a, K, V> -where - K: Type, - K::Ref<'a>: core::fmt::Debug, - V: Type, - V::Ref<'a>: core::fmt::Debug, -{ - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("EntryRef") - .field("key", &self.key()) - .field("value", &self.value()) - .finish() - } -} - -impl Clone for EntryRef<'_, K, V> { - #[inline] - fn clone(&self) -> Self { - Self { - ent: self.ent.clone(), - } - } -} - -impl<'a, K, V> EntryRef<'a, K, V> { - #[inline] - pub(super) fn new(ent: Entry<'a, Pointer>) -> Self { - Self { ent } - } -} - -impl<'a, K, V> EntryRef<'a, K, V> -where - K: Type, - V: Type, -{ - /// Returns the key of the entry. - #[inline] - pub fn key(&self) -> K::Ref<'a> { - let p = self.ent.value(); - TypeRef::from_slice(p.as_key_slice()) - } - - /// Returns the value of the entry. - #[inline] - pub fn value(&self) -> V::Ref<'a> { - let p = self.ent.value(); - TypeRef::from_slice(p.as_value_slice()) - } -} diff --git a/src/swmr/generic/iter.rs b/src/swmr/generic/iter.rs index 90938702..477ba6b2 100644 --- a/src/swmr/generic/iter.rs +++ b/src/swmr/generic/iter.rs @@ -2,7 +2,7 @@ use core::ops::Bound; use crossbeam_skiplist::Comparable; -use super::{EntryRef, KeyRef, Owned, Pointer, Ref, Type}; +use super::{GenericEntryRef, GenericPointer as Pointer, KeyRef, Owned, Ref, Type}; type SetRefRange<'a, Q, K, V> = crossbeam_skiplist::set::Range< 'a, @@ -34,11 +34,11 @@ where K: Type + Ord, for<'b> K::Ref<'b>: KeyRef<'b, K>, { - type Item = EntryRef<'a, K, V>; + type Item = GenericEntryRef<'a, K, V>; #[inline] fn next(&mut self) -> Option { - self.iter.next().map(|ptr| EntryRef::new(ptr)) + self.iter.next().map(|ptr| GenericEntryRef::new(ptr)) } #[inline] @@ -54,7 +54,7 @@ where { #[inline] fn next_back(&mut self) -> Option { - self.iter.next_back().map(|ptr| EntryRef::new(ptr)) + self.iter.next_back().map(|ptr| GenericEntryRef::new(ptr)) } } @@ -86,11 +86,11 @@ where for<'b> K::Ref<'b>: KeyRef<'b, K>, Q: Ord + ?Sized + Comparable>, { - type Item = EntryRef<'a, K, V>; + type Item = GenericEntryRef<'a, K, V>; #[inline] fn next(&mut self) -> Option { - self.iter.next().map(|ptr| EntryRef::new(ptr)) + self.iter.next().map(|ptr| GenericEntryRef::new(ptr)) } } @@ -102,7 +102,7 @@ where { #[inline] fn next_back(&mut self) -> Option { - self.iter.next_back().map(|ptr| EntryRef::new(ptr)) + self.iter.next_back().map(|ptr| GenericEntryRef::new(ptr)) } } @@ -134,11 +134,11 @@ where for<'b> K::Ref<'b>: KeyRef<'b, K>, Q: Ord + ?Sized + Comparable> + Comparable, { - type Item = EntryRef<'a, K, V>; + type Item = GenericEntryRef<'a, K, V>; #[inline] fn next(&mut self) -> Option { - self.iter.next().map(|ptr| EntryRef::new(ptr)) + self.iter.next().map(|ptr| GenericEntryRef::new(ptr)) } } @@ -150,6 +150,6 @@ where { #[inline] fn next_back(&mut self) -> Option { - self.iter.next_back().map(|ptr| EntryRef::new(ptr)) + self.iter.next_back().map(|ptr| GenericEntryRef::new(ptr)) } } diff --git a/src/swmr/generic/reader.rs b/src/swmr/generic/reader.rs index 36f4152a..8e385bb7 100644 --- a/src/swmr/generic/reader.rs +++ b/src/swmr/generic/reader.rs @@ -1,16 +1,24 @@ -use super::*; +use core::ops::Bound; +use std::sync::Arc; + +use dbutils::equivalent::Comparable; +use rarena_allocator::Allocator; + +use super::{ + GenericEntryRef, GenericOrderWalCore, Iter, KeyRef, Range, RefRange, Type, HEADER_SIZE, +}; /// A read-only view of a generic single-writer, multi-reader WAL. -pub struct GenericWalReader(Arc>); +pub struct GenericWalReader(Arc>); -impl Clone for GenericWalReader { +impl Clone for GenericWalReader { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl GenericWalReader { - pub(super) fn new(wal: Arc>) -> Self { +impl GenericWalReader { + pub(super) fn new(wal: Arc>) -> Self { Self(wal) } @@ -22,12 +30,12 @@ impl GenericWalReader { /// Returns the reserved space in the WAL. /// - /// # Safety + /// ## Safety /// - The writer must ensure that the returned slice is not modified. /// - This method is not thread-safe, so be careful when using it. #[inline] pub unsafe fn reserved_slice(&self) -> &[u8] { - if self.0.reserved == 0 { + if self.0.opts.reserved() == 0 { return &[]; } @@ -47,20 +55,20 @@ impl GenericWalReader { } } -impl GenericWalReader +impl GenericWalReader where K: Type + Ord, for<'a> K::Ref<'a>: KeyRef<'a, K>, { /// Returns the first key-value pair in the map. The key in this pair is the minimum key in the wal. #[inline] - pub fn first(&self) -> Option> { + pub fn first(&self) -> Option> { self.0.first() } /// Returns the last key-value pair in the map. The key in this pair is the maximum key in the wal. #[inline] - pub fn last(&self) -> Option> { + pub fn last(&self) -> Option> { self.0.last() } @@ -97,7 +105,7 @@ where } } -impl GenericWalReader +impl GenericWalReader where K: Type + Ord, for<'a> K::Ref<'a>: KeyRef<'a, K>, @@ -123,7 +131,7 @@ where /// Returns `true` if the key exists in the WAL. /// - /// # Safety + /// ## Safety /// - The given `key` must be valid to construct to `K::Ref` without remaining. #[inline] pub unsafe fn contains_key_by_bytes(&self, key: &[u8]) -> bool { @@ -132,7 +140,7 @@ where /// Gets the value associated with the key. #[inline] - pub fn get<'a, Q>(&'a self, key: &'a Q) -> Option> + pub fn get<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable> + Comparable, { @@ -141,7 +149,7 @@ where /// Gets the value associated with the key. #[inline] - pub fn get_by_ref<'a, Q>(&'a self, key: &'a Q) -> Option> + pub fn get_by_ref<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable>, { @@ -150,10 +158,10 @@ where /// Gets the value associated with the key. /// - /// # Safety + /// ## Safety /// - The given `key` must be valid to construct to `K::Ref` without remaining. #[inline] - pub unsafe fn get_by_bytes(&self, key: &[u8]) -> Option> { + pub unsafe fn get_by_bytes(&self, key: &[u8]) -> Option> { self.0.get_by_bytes(key) } } diff --git a/src/swmr/generic/tests.rs b/src/swmr/generic/tests.rs index 82d640f0..ddadbd6d 100644 --- a/src/swmr/generic/tests.rs +++ b/src/swmr/generic/tests.rs @@ -1,6 +1,5 @@ use std::{collections::BTreeMap, thread::spawn}; -use arbitrary::Arbitrary; use dbutils::leb128::{decode_u64_varint, encode_u64_varint, encoded_u64_varint_len}; use tempfile::tempdir; @@ -20,27 +19,36 @@ mod iters; #[cfg(all(test, any(test_swmr_generic_get, all_tests)))] mod get; -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Arbitrary)] -struct Person { - id: u64, - name: String, +#[doc(hidden)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Person { + #[doc(hidden)] + pub id: u64, + #[doc(hidden)] + pub name: String, } impl Person { - fn random() -> Self { + #[doc(hidden)] + #[cfg(test)] + pub fn random() -> Self { Self { id: rand::random(), name: names::Generator::default().next().unwrap(), } } - fn as_ref(&self) -> PersonRef<'_> { + #[doc(hidden)] + pub fn as_ref(&self) -> PersonRef<'_> { PersonRef { id: self.id, name: &self.name, } } + #[doc(hidden)] + #[cfg(test)] + #[allow(dead_code)] fn to_vec(&self) -> Vec { let mut buf = vec![0; self.encoded_len()]; self.encode(&mut buf).unwrap(); @@ -48,30 +56,13 @@ impl Person { } } +#[doc(hidden)] #[derive(Debug)] -struct PersonRef<'a> { +pub struct PersonRef<'a> { id: u64, name: &'a str, } -impl PersonRef<'_> { - fn encoded_len(&self) -> usize { - encoded_u64_varint_len(self.id) + self.name.len() - } - - fn encode(&self, buf: &mut [u8]) -> Result<(), dbutils::leb128::EncodeVarintError> { - let id_size = encode_u64_varint(self.id, buf)?; - buf[id_size..].copy_from_slice(self.name.as_bytes()); - Ok(()) - } - - fn to_vec(&self) -> Vec { - let mut buf = vec![0; self.encoded_len()]; - self.encode(&mut buf).unwrap(); - buf - } -} - impl PartialEq for PersonRef<'_> { fn eq(&self, other: &Self) -> bool { self.id == other.id && self.name == other.name @@ -133,7 +124,6 @@ impl<'a> KeyRef<'a, Person> for PersonRef<'a> { fn compare_binary(this: &[u8], other: &[u8]) -> cmp::Ordering { let (this_id_size, this_id) = decode_u64_varint(this).unwrap(); let (other_id_size, other_id) = decode_u64_varint(other).unwrap(); - PersonRef { id: this_id, name: std::str::from_utf8(&this[this_id_size..]).unwrap(), @@ -161,9 +151,20 @@ impl Type for Person { } impl<'a> TypeRef<'a> for PersonRef<'a> { - fn from_slice(src: &'a [u8]) -> Self { + unsafe fn from_slice(src: &'a [u8]) -> Self { let (id_size, id) = decode_u64_varint(src).unwrap(); let name = std::str::from_utf8(&src[id_size..]).unwrap(); PersonRef { id, name } } } + +impl PersonRef<'_> { + #[cfg(test)] + #[allow(dead_code)] + fn encode_into_vec(&self) -> Result, dbutils::leb128::EncodeVarintError> { + let mut buf = vec![0; encoded_u64_varint_len(self.id) + self.name.len()]; + let id_size = encode_u64_varint(self.id, &mut buf)?; + buf[id_size..].copy_from_slice(self.name.as_bytes()); + Ok(buf) + } +} diff --git a/src/swmr/generic/tests/constructor.rs b/src/swmr/generic/tests/constructor.rs index 6ddd1b1c..14633a20 100644 --- a/src/swmr/generic/tests/constructor.rs +++ b/src/swmr/generic/tests/constructor.rs @@ -1,6 +1,7 @@ use super::*; #[test] +#[allow(clippy::needless_borrows_for_generic_args)] fn owned_comparable() { let p1 = Person { id: 3127022870678870148, @@ -11,11 +12,11 @@ fn owned_comparable() { name: "damaged-friend".into(), }; - let p1bytes = p1.to_vec(); - let p2bytes = p2.to_vec(); + let p1bytes = p1.encode_into_vec().unwrap(); + let p2bytes = p2.encode_into_vec().unwrap(); - let ptr1 = Pointer::::new(p1bytes.len(), 0, p1bytes.as_ptr()); - let ptr2 = Pointer::::new(p2bytes.len(), 0, p2bytes.as_ptr()); + let ptr1 = GenericPointer::::new(p1bytes.len(), 0, p1bytes.as_ptr()); + let ptr2 = GenericPointer::::new(p2bytes.len(), 0, p2bytes.as_ptr()); let map = SkipSet::new(); map.insert(ptr1); @@ -27,7 +28,10 @@ fn owned_comparable() { assert!(map.contains(&Owned::new(&p2))); assert!(map.get(&Owned::new(&p2)).is_some()); - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); wal.insert(&p1, &"My name is Alice!".to_string()).unwrap(); wal.insert(&p2, &"My name is Bob!".to_string()).unwrap(); @@ -49,11 +53,11 @@ fn ref_comparable() { name: "damaged-friend", }; - let p1bytes = p1.to_vec(); - let p2bytes = p2.to_vec(); + let p1bytes = p1.encode_into_vec().unwrap(); + let p2bytes = p2.encode_into_vec().unwrap(); - let ptr1 = Pointer::::new(p1bytes.len(), 0, p1bytes.as_ptr()); - let ptr2 = Pointer::::new(p2bytes.len(), 0, p2bytes.as_ptr()); + let ptr1 = GenericPointer::::new(p1bytes.len(), 0, p1bytes.as_ptr()); + let ptr2 = GenericPointer::::new(p2bytes.len(), 0, p2bytes.as_ptr()); let map = SkipSet::new(); map.insert(ptr1); @@ -65,14 +69,23 @@ fn ref_comparable() { assert!(map.contains(&Owned::new(&p2))); assert!(map.get(&Owned::new(&p2)).is_some()); - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); unsafe { wal - .insert_key_bytes_with_value(&p1bytes, &"My name is Alice!".to_string()) + .insert( + Generic::from_slice(p1bytes.as_ref()), + &"My name is Alice!".to_string(), + ) .unwrap(); wal - .insert_key_bytes_with_value(&p2bytes, &"My name is Bob!".to_string()) + .insert( + Generic::from_slice(p2bytes.as_ref()), + &"My name is Bob!".to_string(), + ) .unwrap(); } @@ -98,8 +111,12 @@ fn ref_comparable() { } #[test] +#[allow(clippy::needless_borrows_for_generic_args)] fn construct_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); let person = Person { id: 1, @@ -119,10 +136,12 @@ fn construct_inmemory() { } #[test] +#[allow(clippy::needless_borrows_for_generic_args)] fn construct_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); - + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); let person = Person { id: 1, name: "Alice".to_string(), @@ -135,20 +154,21 @@ fn construct_map_anon() { #[test] #[cfg_attr(miri, ignore)] +#[allow(clippy::needless_borrows_for_generic_args)] fn construct_map_file() { let dir = tempdir().unwrap(); let path = dir.path().join("generic_wal_construct_map_file"); unsafe { - let mut wal = GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap(); + let mut wal = GenericBuilder::new() + .map_mut::( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap(); let person = Person { id: 1, name: "Alice".to_string(), @@ -168,22 +188,28 @@ fn construct_map_file() { }; unsafe { - let wal = GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new().create(Some(MB)).write(true).read(true), - ) - .unwrap(); + let wal = GenericBuilder::new() + .map_mut::( + &path, + OpenOptions::new().create(Some(MB)).write(true).read(true), + ) + .unwrap(); assert_eq!(wal.get(&pr).unwrap().value(), "My name is Alice!"); } - let wal = unsafe { GenericOrderWal::::map(&path, Options::new()).unwrap() }; + let wal = unsafe { + GenericBuilder::new() + .map::(&path) + .unwrap() + }; assert_eq!(wal.get(&pr).unwrap().value(), "My name is Alice!"); } #[test] fn construct_with_small_capacity_inmemory() { - let wal = GenericOrderWal::::new(Options::new().with_capacity(1)); + let wal = GenericBuilder::new() + .with_capacity(1) + .alloc::(); assert!(wal.is_err()); match wal { @@ -194,7 +220,9 @@ fn construct_with_small_capacity_inmemory() { #[test] fn construct_with_small_capacity_map_anon() { - let wal = GenericOrderWal::::map_anon(Options::new().with_capacity(1)); + let wal = GenericBuilder::new() + .with_capacity(1) + .map_anon::(); assert!(wal.is_err()); match wal { @@ -211,9 +239,8 @@ fn construct_with_small_capacity_map_file() { .join("generic_wal_construct_with_small_capacity_map_file"); let wal = unsafe { - GenericOrderWal::::map_mut( + GenericBuilder::new().map_mut::( &path, - Options::new(), OpenOptions::new() .create_new(Some(1)) .write(true) @@ -240,14 +267,19 @@ fn zero_reserved(wal: &mut GenericOrderWal) { #[test] fn zero_reserved_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); zero_reserved(&mut wal); } #[test] fn zero_reserved_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); zero_reserved(&mut wal); } @@ -258,15 +290,15 @@ fn zero_reserved_map_file() { let path = dir.path().join("generic_wal_zero_reserved_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut::( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; zero_reserved(&mut wal); @@ -286,17 +318,21 @@ fn reserved(wal: &mut GenericOrderWal) { #[test] fn reserved_inmemory() { - let mut wal = - GenericOrderWal::::new(Options::new().with_capacity(MB).with_reserved(4)) - .unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .with_reserved(4) + .alloc() + .unwrap(); reserved(&mut wal); } #[test] fn reserved_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB).with_reserved(4)) - .unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .with_reserved(4) + .map_anon() + .unwrap(); reserved(&mut wal); } @@ -307,15 +343,16 @@ fn reserved_map_file() { let path = dir.path().join("generic_wal_reserved_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new().with_reserved(4), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .with_reserved(4) + .map_mut::( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; reserved(&mut wal); diff --git a/src/swmr/generic/tests/get.rs b/src/swmr/generic/tests/get.rs index c6df9581..d454d7d1 100644 --- a/src/swmr/generic/tests/get.rs +++ b/src/swmr/generic/tests/get.rs @@ -25,14 +25,19 @@ fn first(wal: &mut GenericOrderWal) { #[test] fn first_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); first(&mut wal); } #[test] fn first_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); first(&mut wal); } @@ -43,15 +48,15 @@ fn first_map_file() { let path = dir.path().join("generic_wal_first_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; first(&mut wal); @@ -81,14 +86,19 @@ fn last(wal: &mut GenericOrderWal) { #[test] fn last_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); last(&mut wal); } #[test] fn last_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); last(&mut wal); } @@ -99,20 +109,21 @@ fn last_map_file() { let path = dir.path().join("generic_wal_last_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; last(&mut wal); } +#[allow(clippy::needless_borrows_for_generic_args)] fn get_or_insert(wal: &mut GenericOrderWal) { let people = (0..100) .map(|_| { @@ -128,6 +139,7 @@ fn get_or_insert(wal: &mut GenericOrderWal) { for (p, pv) in &people { assert!(wal.contains_key(p)); assert!(wal.contains_key_by_ref(&p.as_ref())); + assert_eq!( wal .get_or_insert(p, &format!("Hello! {}!", p.name)) @@ -145,14 +157,19 @@ fn get_or_insert(wal: &mut GenericOrderWal) { #[test] fn get_or_insert_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); get_or_insert(&mut wal); } #[test] fn get_or_insert_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); get_or_insert(&mut wal); } @@ -163,15 +180,15 @@ fn get_or_insert_map_file() { let path = dir.path().join("generic_wal_get_or_insert_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; get_or_insert(&mut wal); @@ -183,7 +200,7 @@ fn get_or_insert_with(wal: &mut GenericOrderWal) { let p = Person::random(); let v = format!("My name is {}", p.name); wal - .get_or_insert_with(&p, || v.clone()) + .get_or_insert_with(&p, || v.clone().into()) .unwrap_right() .unwrap(); (p, v) @@ -197,7 +214,7 @@ fn get_or_insert_with(wal: &mut GenericOrderWal) { assert!(wal.contains_key_by_ref(&p.as_ref())); assert_eq!( wal - .get_or_insert_with(p, || format!("Hello! {}!", p.name)) + .get_or_insert_with(p, || format!("Hello! {}!", p.name).into()) .unwrap_left() .value(), pv @@ -212,14 +229,19 @@ fn get_or_insert_with(wal: &mut GenericOrderWal) { #[test] fn get_or_insert_with_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); get_or_insert_with(&mut wal); } #[test] fn get_or_insert_with_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); get_or_insert_with(&mut wal); } @@ -230,20 +252,21 @@ fn get_or_insert_with_map_file() { let path = dir.path().join("generic_wal_get_or_insert_with_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; get_or_insert_with(&mut wal); } +#[allow(clippy::needless_borrows_for_generic_args)] fn get_or_insert_key_with_value_bytes(wal: &mut GenericOrderWal) { let people = (0..100) .map(|_| { @@ -252,7 +275,7 @@ fn get_or_insert_key_with_value_bytes(wal: &mut GenericOrderWal) let v = format!("My name is {}", p.name); unsafe { wal - .get_by_bytes_or_insert(pvec.as_ref(), &v) + .get_or_insert(Generic::from_slice(pvec.as_ref()), &v) .unwrap_right() .unwrap(); } @@ -265,6 +288,7 @@ fn get_or_insert_key_with_value_bytes(wal: &mut GenericOrderWal) for (p, pv) in &people { assert!(wal.contains_key(p)); assert!(wal.contains_key_by_ref(&p.as_ref())); + assert_eq!( wal .get_or_insert(p, &format!("Hello! {}!", p.name)) @@ -282,14 +306,19 @@ fn get_or_insert_key_with_value_bytes(wal: &mut GenericOrderWal) #[test] fn get_or_insert_key_with_value_bytes_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); get_or_insert_key_with_value_bytes(&mut wal); } #[test] fn get_or_insert_key_with_value_bytes_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); get_or_insert_key_with_value_bytes(&mut wal); } @@ -302,15 +331,15 @@ fn get_or_insert_key_with_value_bytes_map_file() { .join("generic_wal_get_or_insert_key_with_value_bytes_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; get_or_insert_key_with_value_bytes(&mut wal); @@ -323,7 +352,7 @@ fn get_or_insert_value_bytes(wal: &mut GenericOrderWal) { let v = format!("My name is {}", p.name); unsafe { wal - .get_or_insert_bytes(&p, v.as_bytes()) + .get_or_insert(&p, Generic::from_slice(v.as_bytes())) .unwrap_right() .unwrap(); } @@ -339,7 +368,7 @@ fn get_or_insert_value_bytes(wal: &mut GenericOrderWal) { unsafe { assert_eq!( wal - .get_or_insert_bytes(p, pv.as_bytes()) + .get_or_insert(p, Generic::from_slice(pv.as_bytes())) .unwrap_left() .value(), pv @@ -355,14 +384,19 @@ fn get_or_insert_value_bytes(wal: &mut GenericOrderWal) { #[test] fn get_or_insert_value_bytes_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); get_or_insert_value_bytes(&mut wal); } #[test] fn get_or_insert_value_bytes_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); get_or_insert_value_bytes(&mut wal); } @@ -375,15 +409,15 @@ fn get_or_insert_value_bytes_map_file() { .join("generic_wal_get_or_insert_value_bytes_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; get_or_insert_value_bytes(&mut wal); @@ -397,7 +431,7 @@ fn get_by_bytes_or_insert_with(wal: &mut GenericOrderWal) { let v = format!("My name is {}", p.name); unsafe { wal - .get_by_bytes_or_insert_with(pvec.as_ref(), || v.clone()) + .get_or_insert_with(Generic::from_slice(pvec.as_ref()), || v.clone().into()) .unwrap_right() .unwrap(); } @@ -413,7 +447,8 @@ fn get_by_bytes_or_insert_with(wal: &mut GenericOrderWal) { unsafe { assert_eq!( wal - .get_by_bytes_or_insert_with(pvec, || format!("Hello! {}!", p.name)) + .get_or_insert_with(Generic::from_slice(pvec), || format!("Hello! {}!", p.name) + .into()) .unwrap_left() .value(), pv @@ -429,14 +464,19 @@ fn get_by_bytes_or_insert_with(wal: &mut GenericOrderWal) { #[test] fn get_by_bytes_or_insert_with_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); get_by_bytes_or_insert_with(&mut wal); } #[test] fn get_by_bytes_or_insert_with_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); get_by_bytes_or_insert_with(&mut wal); } @@ -449,15 +489,15 @@ fn get_by_bytes_or_insert_with_map_file() { .join("generic_wal_get_by_bytes_or_insert_with_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; get_by_bytes_or_insert_with(&mut wal); @@ -471,7 +511,10 @@ fn get_by_bytes_or_insert_bytes(wal: &mut GenericOrderWal) { let v = format!("My name is {}", p.name); unsafe { wal - .get_by_bytes_or_insert_bytes(pvec.as_ref(), v.as_bytes()) + .get_or_insert( + Generic::from_slice(pvec.as_ref()), + Generic::from_slice(v.as_bytes()), + ) .unwrap_right() .unwrap(); } @@ -487,7 +530,10 @@ fn get_by_bytes_or_insert_bytes(wal: &mut GenericOrderWal) { unsafe { assert_eq!( wal - .get_by_bytes_or_insert_bytes(pvec, pv.as_bytes()) + .get_or_insert( + Generic::from_slice(pvec), + Generic::from_slice(pv.as_bytes()) + ) .unwrap_left() .value(), pv @@ -503,14 +549,19 @@ fn get_by_bytes_or_insert_bytes(wal: &mut GenericOrderWal) { #[test] fn get_by_bytes_or_insert_bytes_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); get_by_bytes_or_insert_bytes(&mut wal); } #[test] fn get_by_bytes_or_insert_bytes_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); get_by_bytes_or_insert_bytes(&mut wal); } @@ -523,15 +574,15 @@ fn get_by_bytes_or_insert_bytes_map_file() { .join("generic_wal_get_by_bytes_or_insert_bytes_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; get_by_bytes_or_insert_bytes(&mut wal); diff --git a/src/swmr/generic/tests/insert.rs b/src/swmr/generic/tests/insert.rs index 62713d32..95d84b40 100644 --- a/src/swmr/generic/tests/insert.rs +++ b/src/swmr/generic/tests/insert.rs @@ -4,6 +4,7 @@ fn insert_to_full(wal: &mut GenericOrderWal) { let mut full = false; for _ in 0u32.. { let p = Person::random(); + #[allow(clippy::needless_borrows_for_generic_args)] match wal.insert(&p, &format!("My name is {}", p.name)) { Ok(_) => {} Err(e) => match e { @@ -20,14 +21,13 @@ fn insert_to_full(wal: &mut GenericOrderWal) { #[test] fn insert_to_full_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(100)).unwrap(); + let mut wal = GenericBuilder::new().with_capacity(100).alloc().unwrap(); insert_to_full(&mut wal); } #[test] fn insert_to_full_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(100)).unwrap(); + let mut wal = GenericBuilder::new().with_capacity(100).map_anon().unwrap(); insert_to_full(&mut wal); } @@ -38,15 +38,15 @@ fn insert_to_full_map_file() { let path = dir.path().join("generic_wal_insert_to_full_map_file"); unsafe { - let mut wal = GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(100)) - .write(true) - .read(true), - ) - .unwrap(); + let mut wal = GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(100)) + .write(true) + .read(true), + ) + .unwrap(); insert_to_full(&mut wal); } } @@ -55,6 +55,7 @@ fn insert(wal: &mut GenericOrderWal) -> Vec { let people = (0..100) .map(|_| { let p = Person::random(); + #[allow(clippy::needless_borrows_for_generic_args)] wal.insert(&p, &format!("My name is {}", p.name)).unwrap(); p }) @@ -76,14 +77,19 @@ fn insert(wal: &mut GenericOrderWal) -> Vec { #[test] fn insert_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); insert(&mut wal); } #[test] fn insert_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); insert(&mut wal); } @@ -94,19 +100,23 @@ fn insert_map_file() { let path = dir.path().join("generic_wal_insert_map_file"); let people = unsafe { - let mut wal = GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap(); + let mut wal = GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap(); insert(&mut wal) }; - let wal = unsafe { GenericOrderWal::::map(&path, Options::new()).unwrap() }; + let wal = unsafe { + GenericBuilder::new() + .map::(&path) + .unwrap() + }; for p in people { assert!(wal.contains_key(&p)); @@ -127,7 +137,10 @@ fn insert_key_bytes_with_value( let pbytes = p.to_vec(); unsafe { wal - .insert_key_bytes_with_value(&pbytes, &format!("My name is {}", p.name)) + .insert( + Generic::from_slice(&pbytes), + &format!("My name is {}", p.name), + ) .unwrap(); } (pbytes, p) @@ -157,14 +170,19 @@ fn insert_key_bytes_with_value( #[test] fn insert_key_bytes_with_value_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); insert_key_bytes_with_value(&mut wal); } #[test] fn insert_key_bytes_with_value_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); insert_key_bytes_with_value(&mut wal); } @@ -177,15 +195,15 @@ fn insert_key_bytes_with_value_map_file() { .join("generic_wal_insert_key_bytes_with_value_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; let people = insert_key_bytes_with_value(&mut wal); @@ -206,7 +224,11 @@ fn insert_key_bytes_with_value_map_file() { ); } - let wal = unsafe { GenericOrderWal::::map(&path, Options::new()).unwrap() }; + let wal = unsafe { + GenericBuilder::new() + .map::(&path) + .unwrap() + }; for (pbytes, p) in people { assert!(wal.contains_key(&p)); @@ -230,7 +252,10 @@ fn insert_key_with_value_bytes(wal: &mut GenericOrderWal) -> Vec let p = Person::random(); unsafe { wal - .insert_key_with_value_bytes(&p, format!("My name is {}", p.name).as_bytes()) + .insert( + &p, + Generic::from_slice(format!("My name is {}", p.name).as_bytes()), + ) .unwrap(); } p @@ -253,14 +278,19 @@ fn insert_key_with_value_bytes(wal: &mut GenericOrderWal) -> Vec #[test] fn insert_key_with_value_bytes_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); insert_key_with_value_bytes(&mut wal); } #[test] fn insert_key_with_value_bytes_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); insert_key_with_value_bytes(&mut wal); } @@ -273,15 +303,15 @@ fn insert_key_with_value_bytes_map_file() { .join("generic_wal_insert_key_with_value_bytes_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; let people = insert_key_with_value_bytes(&mut wal); @@ -304,7 +334,10 @@ fn insert_bytes(wal: &mut GenericOrderWal) -> Vec { let pbytes = p.to_vec(); unsafe { wal - .insert_bytes(&pbytes, format!("My name is {}", p.name).as_bytes()) + .insert( + Generic::from_slice(&pbytes), + Generic::from_slice(format!("My name is {}", p.name).as_bytes()), + ) .unwrap(); } p @@ -329,14 +362,19 @@ fn insert_bytes(wal: &mut GenericOrderWal) -> Vec { #[test] fn insert_bytes_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); insert_bytes(&mut wal); } #[test] fn insert_bytes_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); insert_bytes(&mut wal); } @@ -347,15 +385,15 @@ fn insert_bytes_map_file() { let path = dir.path().join("generic_wal_insert_bytes_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; let people = insert_bytes(&mut wal); @@ -389,6 +427,7 @@ fn concurrent_basic(mut w: GenericOrderWal) { spawn(move || { for i in 0..100u32 { + #[allow(clippy::needless_borrows_for_generic_args)] w.insert(&i, &i.to_le_bytes()).unwrap(); } }); @@ -400,16 +439,13 @@ fn concurrent_basic(mut w: GenericOrderWal) { #[test] fn concurrent_basic_inmemory() { - let wal = GenericOrderWal::::new(Options::new().with_capacity(MB).with_reserved(4)) - .unwrap(); + let wal = GenericBuilder::new().with_capacity(MB).alloc().unwrap(); concurrent_basic(wal); } #[test] fn concurrent_basic_map_anon() { - let wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB).with_reserved(4)) - .unwrap(); + let wal = GenericBuilder::new().with_capacity(MB).map_anon().unwrap(); concurrent_basic(wal); } @@ -420,21 +456,20 @@ fn concurrent_basic_map_file() { let path = dir.path().join("generic_wal_concurrent_basic_map_file"); let wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new().with_reserved(4), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; concurrent_basic(wal); - let wal = - unsafe { GenericOrderWal::::map(path, Options::new().with_reserved(4)).unwrap() }; + let wal = unsafe { GenericBuilder::new().map::(path).unwrap() }; for i in 0..100u32 { assert!(wal.contains_key(&i)); @@ -453,7 +488,7 @@ fn concurrent_one_key(mut w: GenericOrderWal) { }) }); - w.insert(&1, &1u32.to_le_bytes()).unwrap(); + w.insert(1, 1u32.to_le_bytes()).unwrap(); for handle in handles { handle.join().unwrap(); @@ -462,16 +497,13 @@ fn concurrent_one_key(mut w: GenericOrderWal) { #[test] fn concurrent_one_key_inmemory() { - let wal = GenericOrderWal::::new(Options::new().with_capacity(MB).with_reserved(4)) - .unwrap(); + let wal = GenericBuilder::new().with_capacity(MB).alloc().unwrap(); concurrent_one_key(wal); } #[test] fn concurrent_one_key_map_anon() { - let wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB).with_reserved(4)) - .unwrap(); + let wal = GenericBuilder::new().with_capacity(MB).map_anon().unwrap(); concurrent_one_key(wal); } @@ -482,21 +514,135 @@ fn concurrent_one_key_map_file() { let path = dir.path().join("generic_wal_concurrent_basic_map_file"); let wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new().with_reserved(4), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; concurrent_one_key(wal); - let wal = - unsafe { GenericOrderWal::::map(path, Options::new().with_reserved(4)).unwrap() }; + let wal = unsafe { GenericBuilder::new().map::(path).unwrap() }; assert!(wal.contains_key(&1)); } + +fn insert_batch( + wal: &mut GenericOrderWal, +) -> (Person, Vec<(Person, String)>, Person) { + const N: u32 = 5; + + let mut batch = vec![]; + let output = (0..N) + .map(|i| { + ( + { + let mut p = Person::random(); + p.id = i as u64; + p + }, + format!("My id is {i}"), + ) + .clone() + }) + .collect::>(); + + for (person, val) in output.iter() { + if person.id % 3 == 0 { + batch.push(GenericEntry::new(person.clone(), val.clone())); + } else if person.id % 3 == 1 { + batch.push(GenericEntry::new(person, val)); + } else { + unsafe { + batch.push(GenericEntry::new( + person, + Generic::from_slice(val.as_bytes()), + )); + } + } + } + + let rp1 = Person::random(); + wal.insert(&rp1, "rp1".to_string()).unwrap(); + wal.insert_batch(&mut batch).unwrap(); + let rp2 = Person::random(); + wal.insert(&rp2, "rp2".to_string()).unwrap(); + + for (p, val) in output.iter() { + assert_eq!(wal.get(p).unwrap().value(), val); + } + + assert_eq!(wal.get(&rp1).unwrap().value(), "rp1"); + assert_eq!(wal.get(&rp2).unwrap().value(), "rp2"); + + let wal = wal.reader(); + for (p, val) in output.iter() { + assert_eq!(wal.get(p).unwrap().value(), val); + } + + assert_eq!(wal.get(&rp1).unwrap().value(), "rp1"); + assert_eq!(wal.get(&rp2).unwrap().value(), "rp2"); + + (rp1, output, rp2) +} + +#[test] +fn test_insert_batch_inmemory() { + insert_batch( + &mut GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(), + ); +} + +#[test] +fn test_insert_batch_map_anon() { + insert_batch( + &mut GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(), + ); +} + +#[test] +#[cfg_attr(miri, ignore)] +fn test_insert_batch_map_file() { + let dir = ::tempfile::tempdir().unwrap(); + let path = dir.path().join(concat!( + "test_", + stringify!($prefix), + "_insert_batch_map_file" + )); + let mut map = unsafe { + GenericBuilder::new() + .map_mut::( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() + }; + + let (rp1, data, rp2) = insert_batch(&mut map); + + let map = unsafe { + GenericBuilder::new() + .map::(&path) + .unwrap() + }; + + for (p, val) in data { + assert_eq!(map.get(&p).unwrap().value(), val); + } + assert_eq!(map.get(&rp1).unwrap().value(), "rp1"); + assert_eq!(map.get(&rp2).unwrap().value(), "rp2"); +} diff --git a/src/swmr/generic/tests/iters.rs b/src/swmr/generic/tests/iters.rs index 33702369..611e2a7b 100644 --- a/src/swmr/generic/tests/iters.rs +++ b/src/swmr/generic/tests/iters.rs @@ -31,14 +31,19 @@ fn iter(wal: &mut GenericOrderWal) -> Vec<(Person, String)> { #[test] fn iter_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); iter(&mut wal); } #[test] fn iter_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); iter(&mut wal); } @@ -49,15 +54,15 @@ fn iter_map_file() { let path = dir.path().join("generic_wal_iter_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut::( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; let people = iter(&mut wal); @@ -113,14 +118,19 @@ fn range(wal: &mut GenericOrderWal) { #[test] fn range_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); range(&mut wal); } #[test] fn range_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); range(&mut wal); } @@ -131,15 +141,15 @@ fn range_map_file() { let path = dir.path().join("generic_wal_range_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut::( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; range(&mut wal); @@ -190,14 +200,19 @@ fn range_ref(wal: &mut GenericOrderWal) { #[test] fn range_ref_inmemory() { - let mut wal = GenericOrderWal::::new(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .alloc::() + .unwrap(); range(&mut wal); } #[test] fn range_ref_map_anon() { - let mut wal = - GenericOrderWal::::map_anon(Options::new().with_capacity(MB)).unwrap(); + let mut wal = GenericBuilder::new() + .with_capacity(MB) + .map_anon::() + .unwrap(); range_ref(&mut wal); } @@ -208,15 +223,15 @@ fn range_ref_map_file() { let path = dir.path().join("generic_wal_range_map_file"); let mut wal = unsafe { - GenericOrderWal::::map_mut( - &path, - Options::new(), - OpenOptions::new() - .create_new(Some(MB)) - .write(true) - .read(true), - ) - .unwrap() + GenericBuilder::new() + .map_mut( + &path, + OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() }; range_ref(&mut wal); diff --git a/src/swmr/wal.rs b/src/swmr/wal.rs index c6092321..3be71f8a 100644 --- a/src/swmr/wal.rs +++ b/src/swmr/wal.rs @@ -1,14 +1,25 @@ -use super::super::*; - -use among::Among; -use either::Either; -use error::Error; -use wal::{ - sealed::{Base, Constructor, Sealed, WalCore}, - ImmutableWal, +use crossbeam_skiplist::SkipSet; + +use crate::{ + error::Error, + pointer::Pointer, + wal::sealed::{Constructor, Sealed, WalCore}, + Options, +}; +use dbutils::{ + checksum::{BuildChecksumer, Crc32}, + Ascend, +}; +use rarena_allocator::{either::Either, Allocator}; + +pub use crate::{ + builder::Builder, + wal::{Batch, BatchWithBuilders, BatchWithKeyBuilder, BatchWithValueBuilder, ImmutableWal, Wal}, + Comparator, KeyBuilder, VacantBuffer, ValueBuilder, }; +pub use dbutils::CheapClone; -use core::ptr::NonNull; +use core::{borrow::Borrow, marker::PhantomData}; use rarena_allocator::sync::Arena; use std::sync::Arc; @@ -36,7 +47,7 @@ pub struct OrderWalCore { map: SkipSet>, opts: Options, cmp: C, - cks: UnsafeCellChecksumer, + cks: S, } impl OrderWalCore { @@ -46,33 +57,22 @@ impl OrderWalCore { } } -impl Base for SkipSet> { - fn insert(&mut self, ele: Pointer) - where - C: Comparator, - { - SkipSet::insert(self, ele); - } -} - -impl WalCore for OrderWalCore { +impl WalCore for OrderWalCore +where + C: Comparator + CheapClone + Send + 'static, +{ type Allocator = Arena; type Base = SkipSet>; + type Pointer = Pointer; #[inline] - fn construct( - arena: Arena, - set: SkipSet>, - opts: Options, - cmp: C, - checksumer: S, - ) -> Self { + fn construct(arena: Arena, set: SkipSet>, opts: Options, cmp: C, cks: S) -> Self { Self { arena, map: set, cmp, opts, - cks: UnsafeCellChecksumer::new(checksumer), + cks, } } } @@ -99,22 +99,26 @@ impl WalCore for OrderWalCore { // ``` pub struct OrderWal { core: Arc>, - ro: bool, _s: PhantomData, } impl Constructor for OrderWal where - C: Send + 'static, + C: Comparator + CheapClone + Send + 'static, { type Allocator = Arena; type Core = OrderWalCore; + type Pointer = Pointer; + + #[inline] + fn allocator(&self) -> &Self::Allocator { + &self.core.arena + } #[inline] - fn from_core(core: Self::Core, ro: bool) -> Self { + fn from_core(core: Self::Core) -> Self { Self { core: Arc::new(core), - ro, _s: PhantomData, } } @@ -122,83 +126,33 @@ where impl Sealed for OrderWal where - C: Send + 'static, + C: Comparator + CheapClone + Send + 'static, { - fn insert_with_in( - &mut self, - kb: KeyBuilder) -> Result<(), KE>>, - vb: ValueBuilder) -> Result<(), VE>>, - ) -> Result<(), Among> + fn hasher(&self) -> &S { + &self.core.cks + } + + fn options(&self) -> &Options { + &self.core.opts + } + + fn comparator(&self) -> &C { + &self.core.cmp + } + + fn insert_pointer(&self, ptr: Pointer) where - C: Comparator + CheapClone, - S: Checksumer, + C: Comparator, + { + self.core.map.insert(ptr); + } + + fn insert_pointers(&self, ptrs: impl Iterator>) + where + C: Comparator, { - let (klen, kf) = kb.into_components(); - let (vlen, vf) = vb.into_components(); - let (len_size, kvlen, elen) = entry_size(klen, vlen); - let klen = klen as usize; - let vlen = vlen as usize; - let buf = self.core.arena.alloc_bytes(elen); - - match buf { - Err(e) => Err(Among::Right(Error::from_insufficient_space(e))), - Ok(mut buf) => { - unsafe { - // We allocate the buffer with the exact size, so it's safe to write to the buffer. - let flag = Flags::COMMITTED.bits(); - - self.core.cks.reset(); - self.core.cks.update(&[flag]); - - buf.put_u8_unchecked(Flags::empty().bits()); - let written = buf.put_u64_varint_unchecked(kvlen); - debug_assert_eq!( - written, len_size, - "the precalculated size should be equal to the written size" - ); - - let ko = STATUS_SIZE + written; - buf.set_len(ko + klen + vlen); - - kf(&mut VacantBuffer::new( - klen, - NonNull::new_unchecked(buf.as_mut_ptr().add(ko)), - )) - .map_err(Among::Left)?; - - let vo = ko + klen; - vf(&mut VacantBuffer::new( - vlen, - NonNull::new_unchecked(buf.as_mut_ptr().add(vo)), - )) - .map_err(Among::Middle)?; - - let cks = { - self.core.cks.update(&buf[1..]); - self.core.cks.digest() - }; - buf.put_u64_le_unchecked(cks); - - // commit the entry - buf[0] |= Flags::COMMITTED.bits(); - - if self.core.opts.sync_on_write() && self.core.arena.is_ondisk() { - self - .core - .arena - .flush_range(buf.offset(), elen as usize) - .map_err(|e| Among::Right(e.into()))?; - } - buf.detach(); - self.core.map.insert(Pointer::new( - klen, - vlen, - buf.as_ptr().add(ko), - self.core.cmp.cheap_clone(), - )); - Ok(()) - } - } + for ptr in ptrs { + self.core.map.insert(ptr); } } } @@ -206,13 +160,13 @@ where impl OrderWal { /// Returns the path of the WAL if it is backed by a file. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::{swmr::OrderWal, Wal, Builder}; /// /// // A in-memory WAL - /// let wal = OrderWal::new(Builder::new().with_capacity(100)).unwrap(); + /// let wal = Builder::new().with_capacity(100).alloc::().unwrap(); /// /// assert!(wal.path_buf().is_none()); /// ``` @@ -223,7 +177,7 @@ impl OrderWal { impl ImmutableWal for OrderWal where - C: Send + 'static, + C: Comparator + CheapClone + Send + 'static, { type Iter<'a> = Iter<'a, C> where Self: 'a, C: Comparator; type Range<'a, Q, R> = Range<'a, Q, R, C> @@ -253,15 +207,6 @@ where Self: 'a, C: Comparator; - #[inline] - unsafe fn reserved_slice(&self) -> &[u8] { - if self.core.opts.reserved() == 0 { - return &[]; - } - - &self.core.arena.reserved_slice()[HEADER_SIZE..] - } - #[inline] fn path(&self) -> Option<&std::path::Path> { self.core.arena.path().map(|p| p.as_ref().as_path()) @@ -282,6 +227,16 @@ where self.core.opts.maximum_value_size() } + #[inline] + fn remaining(&self) -> u32 { + self.core.arena.remaining() as u32 + } + + #[inline] + fn options(&self) -> &Options { + &self.core.opts + } + #[inline] fn contains_key(&self, key: &Q) -> bool where @@ -386,47 +341,15 @@ where impl Wal for OrderWal where - C: Send + 'static, + C: Comparator + CheapClone + Send + 'static, { type Reader = OrderWalReader; - #[inline] - fn read_only(&self) -> bool { - self.ro - } - #[inline] fn reader(&self) -> Self::Reader { OrderWalReader::new(self.core.clone()) } - #[inline] - fn flush(&self) -> Result<(), Error> { - if self.ro { - return Err(error::Error::read_only()); - } - - self.core.arena.flush().map_err(Into::into) - } - - #[inline] - fn flush_async(&self) -> Result<(), Error> { - if self.ro { - return Err(error::Error::read_only()); - } - - self.core.arena.flush_async().map_err(Into::into) - } - - #[inline] - unsafe fn reserved_slice_mut(&mut self) -> &mut [u8] { - if self.core.opts.reserved() == 0 { - return &mut []; - } - - &mut self.core.arena.reserved_slice_mut()[HEADER_SIZE..] - } - fn get_or_insert_with_value_builder( &mut self, key: &[u8], @@ -434,7 +357,7 @@ where ) -> Result, Either> where C: Comparator + CheapClone, - S: Checksumer, + S: BuildChecksumer, { self .check( diff --git a/src/swmr/wal/reader.rs b/src/swmr/wal/reader.rs index 6164cbc7..62bbfa8c 100644 --- a/src/swmr/wal/reader.rs +++ b/src/swmr/wal/reader.rs @@ -9,27 +9,36 @@ impl OrderWalReader { pub(super) fn new(wal: Arc>) -> Self { Self(OrderWal { core: wal.clone(), - ro: true, _s: PhantomData, }) } } -impl Constructor for OrderWalReader { +impl Constructor for OrderWalReader +where + C: Comparator + CheapClone + Send + 'static, +{ type Allocator = Arena; - type Core = OrderWalCore; + type Pointer = Pointer; - fn from_core(core: Self::Core, _ro: bool) -> Self { + #[inline] + fn allocator(&self) -> &Self::Allocator { + self.0.allocator() + } + + fn from_core(core: Self::Core) -> Self { Self(OrderWal { core: Arc::new(core), - ro: true, _s: PhantomData, }) } } -impl ImmutableWal for OrderWalReader { +impl ImmutableWal for OrderWalReader +where + C: Comparator + CheapClone + Send + 'static, +{ type Iter<'a> = Iter<'a, C> where Self: 'a, C: Comparator; type Range<'a, Q, R> = Range<'a, Q, R, C> where @@ -58,11 +67,6 @@ impl ImmutableWal for OrderWalReader { Self: 'a, C: Comparator; - #[inline] - unsafe fn reserved_slice(&self) -> &[u8] { - self.0.reserved_slice() - } - #[inline] fn path(&self) -> Option<&std::path::Path> { self.0.path() @@ -74,13 +78,8 @@ impl ImmutableWal for OrderWalReader { } #[inline] - fn maximum_key_size(&self) -> u32 { - self.0.maximum_key_size() - } - - #[inline] - fn maximum_value_size(&self) -> u32 { - self.0.maximum_value_size() + fn options(&self) -> &Options { + ImmutableWal::options(&self.0) } #[inline] diff --git a/src/swmr/wal/tests.rs b/src/swmr/wal/tests.rs index ed30fd4a..80a65a8b 100644 --- a/src/swmr/wal/tests.rs +++ b/src/swmr/wal/tests.rs @@ -1,7 +1,3 @@ -use tempfile::tempdir; - -use crate::tests::*; - use super::*; #[cfg(all(test, any(test_swmr_constructor, all_tests)))] diff --git a/src/swmr/wal/tests/constructor.rs b/src/swmr/wal/tests/constructor.rs index 03e864a3..c80352ea 100644 --- a/src/swmr/wal/tests/constructor.rs +++ b/src/swmr/wal/tests/constructor.rs @@ -1,3 +1,3 @@ use super::*; -common_unittests!(unsync::constructor::OrderWal); +common_unittests!(swmr::constructor::OrderWal); diff --git a/src/swmr/wal/tests/get.rs b/src/swmr/wal/tests/get.rs index c9eefcf8..0165171d 100644 --- a/src/swmr/wal/tests/get.rs +++ b/src/swmr/wal/tests/get.rs @@ -1,3 +1,3 @@ use super::*; -common_unittests!(unsync::get::OrderWal); +common_unittests!(swmr::get::OrderWal); diff --git a/src/swmr/wal/tests/insert.rs b/src/swmr/wal/tests/insert.rs index 6aacd454..7d457550 100644 --- a/src/swmr/wal/tests/insert.rs +++ b/src/swmr/wal/tests/insert.rs @@ -1,3 +1,5 @@ use super::*; -common_unittests!(unsync::insert::OrderWal); +common_unittests!(swmr::insert::OrderWal); + +common_unittests!(swmr::insert_batch::OrderWal); diff --git a/src/swmr/wal/tests/iter.rs b/src/swmr/wal/tests/iter.rs index f20d78e8..e42d2ed6 100644 --- a/src/swmr/wal/tests/iter.rs +++ b/src/swmr/wal/tests/iter.rs @@ -1,3 +1,3 @@ use super::*; -common_unittests!(unsync::iters::OrderWal); +common_unittests!(swmr::iters::OrderWal); diff --git a/src/tests.rs b/src/tests.rs index 8aa7e91a..2201c916 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,33 +1,31 @@ use core::ops::Bound; use super::*; -use tempfile::tempdir; -use wal::ImmutableWal; +use wal::{ImmutableWal, Wal}; const MB: usize = 1024 * 1024; macro_rules! common_unittests { - ($prefix:ident::insert::$wal:ident) => { + ($prefix:ident::insert::$wal:ty) => { paste::paste! { #[test] fn test_insert_to_full_inmemory() { - insert_to_full(&mut OrderWal::new(Builder::new().with_capacity(100)).unwrap()); + $crate::tests::insert_to_full(&mut $crate::Builder::new().with_capacity(100).alloc::<$wal>().unwrap()); } #[test] fn test_insert_to_full_map_anon() { - insert_to_full(&mut OrderWal::map_anon(Builder::new().with_capacity(100)).unwrap()); + $crate::tests::insert_to_full(&mut $crate::Builder::new().with_capacity(100).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_insert_to_full_map_file() { - let dir = tempdir().unwrap(); - insert_to_full( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::insert_to_full( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_insert_to_full_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(100)) .write(true) .read(true), @@ -38,23 +36,22 @@ macro_rules! common_unittests { #[test] fn test_insert_inmemory() { - insert(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::insert(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_insert_map_anon() { - insert(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::insert(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_insert_map_file() { - let dir = tempdir().unwrap(); - insert( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::insert( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_insert_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -65,23 +62,22 @@ macro_rules! common_unittests { #[test] fn test_insert_with_key_builder_inmemory() { - insert_with_key_builder(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::insert_with_key_builder(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_insert_with_key_builder_map_anon() { - insert_with_key_builder(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::insert_with_key_builder(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_insert_with_key_builder_map_file() { - let dir = tempdir().unwrap(); - insert_with_key_builder( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::insert_with_key_builder( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_insert_with_key_builder_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -92,23 +88,22 @@ macro_rules! common_unittests { #[test] fn test_insert_with_value_builder_inmemory() { - insert_with_value_builder(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::insert_with_value_builder(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_insert_with_value_builder_map_anon() { - insert_with_value_builder(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::insert_with_value_builder(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_insert_with_value_builder_map_file() { - let dir = tempdir().unwrap(); - insert_with_value_builder( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::insert_with_value_builder( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_insert_with_value_builder_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -119,23 +114,22 @@ macro_rules! common_unittests { #[test] fn test_insert_with_builders_inmemory() { - insert_with_builders(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::insert_with_builders(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_insert_with_builders_map_anon() { - insert_with_builders(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::insert_with_builders(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_insert_with_builders_map_file() { - let dir = tempdir().unwrap(); - insert_with_builders( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::insert_with_builders( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_insert_with_builders_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -145,27 +139,187 @@ macro_rules! common_unittests { } } }; + ($prefix:ident::insert_batch::$wal:ident) => { + paste::paste! { + #[test] + fn test_insert_batch_inmemory() { + $crate::tests::insert_batch(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); + } + + #[test] + fn test_insert_batch_map_anon() { + $crate::tests::insert_batch(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn test_insert_batch_map_file() { + let dir = ::tempfile::tempdir().unwrap(); + let path = dir.path().join(concat!( + "test_", + stringify!($prefix), + "_insert_batch_map_file" + )); + let mut map = unsafe { + $crate::Builder::new().map_mut::<$wal, _>( + &path, + $crate::OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() + }; + + $crate::tests::insert_batch(&mut map); + + let map = unsafe { $crate::Builder::new().map::<$wal, _>(&path).unwrap() }; + + for i in 0..100u32 { + assert_eq!(map.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } + + assert_eq!(map.get(&1000u32.to_be_bytes()).unwrap(), 1000u32.to_be_bytes()); + } + + #[test] + fn test_insert_batch_with_key_builder_inmemory() { + $crate::tests::insert_batch_with_key_builder(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); + } + + #[test] + fn test_insert_batch_with_key_builder_map_anon() { + $crate::tests::insert_batch_with_key_builder(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn test_insert_batch_with_key_builder_map_file() { + let dir = ::tempfile::tempdir().unwrap(); + let path = dir.path().join(concat!( + "test_", + stringify!($prefix), + "_insert_batch_with_key_builder_map_file" + )); + let mut map = unsafe { + $crate::Builder::new().map_mut::<$wal, _>( + &path, + $crate::OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() + }; + + $crate::tests::insert_batch_with_key_builder(&mut map); + + let map = unsafe { $crate::Builder::new().map::<$wal, _>(&path).unwrap() }; + + for i in 0..100u32 { + assert_eq!(map.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } + } + + #[test] + fn test_insert_batch_with_value_builder_inmemory() { + $crate::tests::insert_batch_with_value_builder(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); + } + + #[test] + fn test_insert_batch_with_value_builder_map_anon() { + $crate::tests::insert_batch_with_value_builder(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn test_insert_batch_with_value_builder_map_file() { + let dir = ::tempfile::tempdir().unwrap(); + let path = dir.path().join(concat!( + "test_", + stringify!($prefix), + "_insert_batch_with_value_builder_map_file" + )); + let mut map = unsafe { + $crate::Builder::new().map_mut::<$wal, _>( + &path, + $crate::OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() + }; + + $crate::tests::insert_batch_with_value_builder(&mut map); + + let map = unsafe { $crate::Builder::new().map::<$wal, _>(&path).unwrap() }; + + for i in 0..100u32 { + assert_eq!(map.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } + } + + #[test] + fn test_insert_batch_with_builders_inmemory() { + $crate::tests::insert_batch_with_builders(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); + } + + #[test] + fn test_insert_batch_with_builders_map_anon() { + $crate::tests::insert_batch_with_builders(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn test_insert_batch_with_builders_map_file() { + let dir = ::tempfile::tempdir().unwrap(); + let path = dir.path().join(concat!( + "test_", + stringify!($prefix), + "_insert_batch_with_builders_map_file" + )); + let mut map = unsafe { + $crate::Builder::new().map_mut::<$wal, _>( + &path, + $crate::OpenOptions::new() + .create_new(Some(MB)) + .write(true) + .read(true), + ) + .unwrap() + }; + + $crate::tests::insert_batch_with_builders(&mut map); + + let map = unsafe { $crate::Builder::new().map::<$wal, _>(&path).unwrap() }; + + for i in 0..100u32 { + assert_eq!(map.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } + } + } + }; ($prefix:ident::iters::$wal:ident) => { paste::paste! { #[test] fn test_iter_inmemory() { - iter(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::iter(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_iter_map_anon() { - iter(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::iter(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_iter_map_file() { - let dir = tempdir().unwrap(); - iter( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::iter( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_iter_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -176,23 +330,22 @@ macro_rules! common_unittests { #[test] fn test_range() { - range(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::range(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_range_map_anon() { - range(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::range(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_range_map_file() { - let dir = tempdir().unwrap(); - range( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::range( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_range_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -203,23 +356,22 @@ macro_rules! common_unittests { #[test] fn test_keys() { - keys(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::keys(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_keys_map_anon() { - keys(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::keys(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_keys_map_file() { - let dir = tempdir().unwrap(); - keys( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::keys( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_keys_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -230,23 +382,22 @@ macro_rules! common_unittests { #[test] fn test_values() { - values(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::values(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_values_map_anon() { - values(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::values(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_values_map_file() { - let dir = tempdir().unwrap(); - values( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::values( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_values_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -257,23 +408,22 @@ macro_rules! common_unittests { #[test] fn test_range_keys() { - range_keys(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::range_keys(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_range_keys_map_anon() { - range_keys(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::range_keys(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_range_keys_map_file() { - let dir = tempdir().unwrap(); - range_keys( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::range_keys( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_range_keys_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -284,23 +434,22 @@ macro_rules! common_unittests { #[test] fn test_range_values() { - range_values(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::range_values(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_range_values_map_anon() { - range_values(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::range_values(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_range_values_map_file() { - let dir = tempdir().unwrap(); - range_values( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::range_values( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test", stringify!($prefix), "_range_values_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -314,23 +463,22 @@ macro_rules! common_unittests { paste::paste! { #[test] fn test_first_inmemory() { - first(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::first(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_first_map_anon() { - first(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::first(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_first_map_file() { - let dir = tempdir().unwrap(); - first( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::first( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_first_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -341,23 +489,22 @@ macro_rules! common_unittests { #[test] fn test_last_inmemory() { - last(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::last(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_last_map_anon() { - last(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::last(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_last_map_file() { - let dir = tempdir().unwrap(); - last( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::last( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_last_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -368,30 +515,29 @@ macro_rules! common_unittests { #[test] fn test_get_or_insert_inmemory() { - get_or_insert(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::get_or_insert(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_get_or_insert_map_anon() { - get_or_insert(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::get_or_insert(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_get_or_insert_map_file() { - let dir = tempdir().unwrap(); + let dir = ::tempfile::tempdir().unwrap(); - let mut wal = unsafe { OrderWal::map_mut( + let mut wal = unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_get_or_insert_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), ) .unwrap() }; - get_or_insert( + $crate::tests::get_or_insert( &mut wal, ); @@ -400,29 +546,28 @@ macro_rules! common_unittests { #[test] fn test_get_or_insert_with_value_builder_inmemory() { - get_or_insert_with_value_builder(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::get_or_insert_with_value_builder(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_get_or_insert_with_value_builder_map_anon() { - get_or_insert_with_value_builder(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::get_or_insert_with_value_builder(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_get_or_insert_with_value_builder_map_file() { - let dir = tempdir().unwrap(); + let dir = ::tempfile::tempdir().unwrap(); let path = dir.path().join(concat!("test_", stringify!($prefix), "_get_or_insert_with_value_builder_map_file")); - let mut wal = unsafe { OrderWal::map_mut( + let mut wal = unsafe { $crate::Builder::new().map_mut::<$wal, _>( &path, - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), ) .unwrap() }; - get_or_insert_with_value_builder( + $crate::tests::get_or_insert_with_value_builder( &mut wal, ); @@ -436,55 +581,54 @@ macro_rules! common_unittests { paste::paste! { #[test] fn test_construct_inmemory() { - construct_inmemory::>(); + $crate::tests::construct_inmemory::>(); } #[test] fn test_construct_map_anon() { - construct_map_anon::>(); + $crate::tests::construct_map_anon::>(); } #[test] #[cfg_attr(miri, ignore)] fn test_construct_map_file() { - construct_map_file::>(stringify!($prefix)); + $crate::tests::construct_map_file::>(stringify!($prefix)); } #[test] fn test_construct_with_small_capacity_inmemory() { - construct_with_small_capacity_inmemory::>(); + $crate::tests::construct_with_small_capacity_inmemory::>(); } #[test] fn test_construct_with_small_capacity_map_anon() { - construct_with_small_capacity_map_anon::>(); + $crate::tests::construct_with_small_capacity_map_anon::>(); } #[test] #[cfg_attr(miri, ignore)] fn test_construct_with_small_capacity_map_file() { - construct_with_small_capacity_map_file::>(stringify!($prefix)); + $crate::tests::construct_with_small_capacity_map_file::>(stringify!($prefix)); } #[test] fn test_zero_reserved_inmemory() { - zero_reserved(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::zero_reserved(&mut $crate::Builder::new().with_capacity(MB).alloc::<$wal>().unwrap()); } #[test] fn test_zero_reserved_map_anon() { - zero_reserved(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + $crate::tests::zero_reserved(&mut $crate::Builder::new().with_capacity(MB).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_zero_reserved_map_file() { - let dir = tempdir().unwrap(); - zero_reserved( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::zero_reserved( + &mut unsafe { $crate::Builder::new().map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_zero_reserved_map_file")), - Builder::new(), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -495,23 +639,22 @@ macro_rules! common_unittests { #[test] fn test_reserved_inmemory() { - reserved(&mut OrderWal::new(Builder::new().with_capacity(MB).with_reserved(4)).unwrap()); + $crate::tests::reserved(&mut $crate::Builder::new().with_capacity(MB).with_reserved(4).alloc::<$wal>().unwrap()); } #[test] fn test_reserved_map_anon() { - reserved(&mut OrderWal::map_anon(Builder::new().with_capacity(MB).with_reserved(4)).unwrap()); + $crate::tests::reserved(&mut $crate::Builder::new().with_capacity(MB).with_reserved(4).map_anon::<$wal>().unwrap()); } #[test] #[cfg_attr(miri, ignore)] fn test_reserved_map_file() { - let dir = tempdir().unwrap(); - reserved( - &mut unsafe { OrderWal::map_mut( + let dir = ::tempfile::tempdir().unwrap(); + $crate::tests::reserved( + &mut unsafe { Builder::new().with_reserved(4).map_mut::<$wal, _>( dir.path().join(concat!("test_", stringify!($prefix), "_reserved_map_file")), - Builder::new().with_reserved(4), - OpenOptions::new() + $crate::OpenOptions::new() .create_new(Some(MB)) .write(true) .read(true), @@ -524,32 +667,38 @@ macro_rules! common_unittests { } pub(crate) fn construct_inmemory>() { - let mut wal = W::new(Builder::new().with_capacity(MB as u32)).unwrap(); + let mut wal = Builder::new() + .with_capacity(MB as u32) + .alloc::() + .unwrap(); let wal = &mut wal; assert!(wal.is_empty()); wal.insert(b"key1", b"value1").unwrap(); } pub(crate) fn construct_map_anon>() { - let mut wal = W::map_anon(Builder::new().with_capacity(MB as u32)).unwrap(); + let mut wal = Builder::new() + .with_capacity(MB as u32) + .map_anon::() + .unwrap(); let wal = &mut wal; wal.insert(b"key1", b"value1").unwrap(); } pub(crate) fn construct_map_file>(prefix: &str) { - let dir = tempdir().unwrap(); + let dir = ::tempfile::tempdir().unwrap(); let path = dir.path().join(format!("{prefix}_construct_map_file")); unsafe { - let mut wal = W::map_mut( - &path, - Builder::new(), - OpenOptions::new() - .create_new(Some(MB as u32)) - .write(true) - .read(true), - ) - .unwrap(); + let mut wal = Builder::new() + .map_mut::( + &path, + OpenOptions::new() + .create_new(Some(MB as u32)) + .write(true) + .read(true), + ) + .unwrap(); let wal = &mut wal; wal.insert(b"key1", b"value1").unwrap(); @@ -557,21 +706,21 @@ pub(crate) fn construct_map_file>(prefix: &str) { } unsafe { - let wal = W::map_mut( - &path, - Builder::new(), - OpenOptions::new() - .create(Some(MB as u32)) - .write(true) - .read(true), - ) - .unwrap(); + let wal = Builder::new() + .map_mut::( + &path, + OpenOptions::new() + .create(Some(MB as u32)) + .write(true) + .read(true), + ) + .unwrap(); assert_eq!(wal.get(b"key1").unwrap(), b"value1"); assert!(!wal.read_only()); } - let wal = unsafe { W::map(&path, Builder::new()).unwrap() }; + let wal = unsafe { Builder::new().map::(&path).unwrap() }; assert_eq!(wal.get(b"key1").unwrap(), b"value1"); assert_eq!(wal.path().unwrap(), path); assert_eq!(wal.maximum_key_size(), Options::new().maximum_key_size()); @@ -582,7 +731,7 @@ pub(crate) fn construct_map_file>(prefix: &str) { } pub(crate) fn construct_with_small_capacity_inmemory>() { - let wal = W::new(Builder::new().with_capacity(1)); + let wal = Builder::new().with_capacity(1).alloc::(); assert!(wal.is_err()); match wal { @@ -592,7 +741,7 @@ pub(crate) fn construct_with_small_capacity_inmemory>() { } pub(crate) fn construct_with_small_capacity_map_anon>() { - let wal = W::map_anon(Builder::new().with_capacity(1)); + let wal = Builder::new().with_capacity(1).map_anon::(); assert!(wal.is_err()); match wal { @@ -602,15 +751,14 @@ pub(crate) fn construct_with_small_capacity_map_anon>() { } pub(crate) fn construct_with_small_capacity_map_file>(prefix: &str) { - let dir = tempdir().unwrap(); + let dir = ::tempfile::tempdir().unwrap(); let path = dir .path() .join(format!("{prefix}_construct_with_small_capacity_map_file")); let wal = unsafe { - W::map_mut( + Builder::new().map_mut::( &path, - Builder::new(), OpenOptions::new() .create_new(Some(1)) .write(true) @@ -669,7 +817,7 @@ pub(crate) fn insert_with_key_builder>(wal: &mut W) { for i in 0..100u32 { wal .insert_with_key_builder::<()>( - KeyBuilder::<_>::new(4, |buf| { + KeyBuilder::<_>::once(4, |buf| { let _ = buf.put_u32_be(i); Ok(()) }), @@ -693,7 +841,7 @@ pub(crate) fn insert_with_value_builder>(wal: &mut W) { wal .insert_with_value_builder::<()>( &i.to_be_bytes(), - ValueBuilder::<_>::new(4, |buf| { + ValueBuilder::<_>::once(4, |buf| { let _ = buf.put_u32_be(i); Ok(()) }), @@ -715,11 +863,11 @@ pub(crate) fn insert_with_builders>(wal: &mut W) { for i in 0..100u32 { wal .insert_with_builders::<(), ()>( - KeyBuilder::<_>::new(4, |buf| { + KeyBuilder::<_>::once(4, |buf| { let _ = buf.put_u32_be(i); Ok(()) }), - ValueBuilder::<_>::new(4, |buf| { + ValueBuilder::<_>::once(4, |buf| { let _ = buf.put_u32_be(i); Ok(()) }), @@ -1023,7 +1171,7 @@ pub(crate) fn get_or_insert_with_value_builder>(wal: &mut wal .get_or_insert_with_value_builder::<()>( &i.to_be_bytes(), - ValueBuilder::<_>::new(4, |buf| { + ValueBuilder::<_>::once(4, |buf| { let _ = buf.put_u32_be(i); Ok(()) }), @@ -1035,7 +1183,7 @@ pub(crate) fn get_or_insert_with_value_builder>(wal: &mut wal .get_or_insert_with_value_builder::<()>( &i.to_be_bytes(), - ValueBuilder::<_>::new(4, |buf| { + ValueBuilder::<_>::once(4, |buf| { let _ = buf.put_u32_be(i * 2); Ok(()) }), @@ -1053,6 +1201,112 @@ pub(crate) fn get_or_insert_with_value_builder>(wal: &mut } } +pub(crate) fn insert_batch>(wal: &mut W) { + const N: u32 = 100; + + let mut batch = vec![]; + + for i in 0..N { + batch.push(Entry::new(i.to_be_bytes(), i.to_be_bytes())); + } + + wal.insert_batch(&mut batch).unwrap(); + + wal + .insert(&1000u32.to_be_bytes(), &1000u32.to_be_bytes()) + .unwrap(); + + for i in 0..N { + assert_eq!(wal.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } + + assert_eq!( + wal.get(&1000u32.to_be_bytes()).unwrap(), + 1000u32.to_be_bytes() + ); + + let wal = wal.reader(); + for i in 0..N { + assert_eq!(wal.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } + + assert_eq!( + wal.get(&1000u32.to_be_bytes()).unwrap(), + 1000u32.to_be_bytes() + ); +} + +pub(crate) fn insert_batch_with_key_builder>(wal: &mut W) { + const N: u32 = 100; + + let mut batch = vec![]; + + for i in 0..N { + batch.push(EntryWithKeyBuilder::new( + KeyBuilder::new(4, move |buf| buf.put_u32_be(i)), + i.to_be_bytes(), + )); + } + + wal.insert_batch_with_key_builder(&mut batch).unwrap(); + + for i in 0..N { + assert_eq!(wal.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } + + let wal = wal.reader(); + for i in 0..N { + assert_eq!(wal.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } +} + +pub(crate) fn insert_batch_with_value_builder>(wal: &mut W) { + const N: u32 = 100; + + let mut batch = vec![]; + for i in 0..N { + batch.push(EntryWithValueBuilder::new( + i.to_be_bytes(), + ValueBuilder::new(4, move |buf| buf.put_u32_be(i)), + )); + } + + wal.insert_batch_with_value_builder(&mut batch).unwrap(); + + for i in 0..N { + assert_eq!(wal.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } + + let wal = wal.reader(); + for i in 0..N { + assert_eq!(wal.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } +} + +pub(crate) fn insert_batch_with_builders>(wal: &mut W) { + const N: u32 = 100; + + let mut batch = vec![]; + + for i in 0..N { + batch.push(EntryWithBuilders::new( + KeyBuilder::new(4, move |buf| buf.put_u32_be(i)), + ValueBuilder::new(4, move |buf| buf.put_u32_be(i)), + )); + } + + wal.insert_batch_with_builders(&mut batch).unwrap(); + + for i in 0..N { + assert_eq!(wal.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } + + let wal = wal.reader(); + for i in 0..N { + assert_eq!(wal.get(&i.to_be_bytes()).unwrap(), i.to_be_bytes()); + } +} + pub(crate) fn zero_reserved>(wal: &mut W) { unsafe { assert_eq!(wal.reserved_slice(), &[]); diff --git a/src/unsync.rs b/src/unsync.rs index aa372fc0..615789d7 100644 --- a/src/unsync.rs +++ b/src/unsync.rs @@ -1,15 +1,19 @@ -use core::{cell::UnsafeCell, ops::RangeBounds, ptr::NonNull}; +use core::{cell::UnsafeCell, ops::RangeBounds}; use std::{collections::BTreeSet, rc::Rc}; use super::*; -use among::Among; +use checksum::BuildChecksumer; use either::Either; use error::Error; +use pointer::Pointer; use rarena_allocator::unsync::Arena; -use wal::{ - sealed::{Constructor, Sealed}, - ImmutableWal, +use wal::sealed::{Constructor, Sealed}; + +pub use super::{ + builder::Builder, + wal::{Batch, BatchWithBuilders, BatchWithKeyBuilder, BatchWithValueBuilder, ImmutableWal, Wal}, + Comparator, KeyBuilder, VacantBuffer, ValueBuilder, }; /// Iterators for the `OrderWal`. @@ -51,22 +55,26 @@ mod tests; // ``` pub struct OrderWal { core: Rc>>, - ro: bool, _s: PhantomData, } impl Constructor for OrderWal where - C: 'static, + C: Comparator + 'static, { type Allocator = Arena; type Core = OrderWalCore; + type Pointer = Pointer; + + #[inline] + fn allocator(&self) -> &Self::Allocator { + &self.core().arena + } #[inline] - fn from_core(core: Self::Core, ro: bool) -> Self { + fn from_core(core: Self::Core) -> Self { Self { core: Rc::new(UnsafeCell::new(core)), - ro, _s: PhantomData, } } @@ -75,13 +83,13 @@ where impl OrderWal { /// Returns the path of the WAL if it is backed by a file. /// - /// # Example + /// ## Example /// /// ```rust /// use orderwal::{unsync::OrderWal, Wal, Builder}; /// /// // A in-memory WAL - /// let wal = OrderWal::new(Builder::new().with_capacity(100)).unwrap(); + /// let wal = Builder::new().with_capacity(100).alloc::().unwrap(); /// /// assert!(wal.path_buf().is_none()); /// ``` @@ -93,99 +101,51 @@ impl OrderWal { fn core(&self) -> &OrderWalCore { unsafe { &*self.core.get() } } - - #[inline] - fn core_mut(&mut self) -> &mut OrderWalCore { - unsafe { &mut *self.core.get() } - } } impl Sealed for OrderWal where - C: 'static, + C: Comparator + 'static, { - fn insert_with_in( - &mut self, - kb: KeyBuilder) -> Result<(), KE>>, - vb: ValueBuilder) -> Result<(), VE>>, - ) -> Result<(), Among> + #[inline] + fn hasher(&self) -> &S { + &self.core().cks + } + + #[inline] + fn options(&self) -> &Options { + &self.core().opts + } + + #[inline] + fn comparator(&self) -> &C { + &self.core().cmp + } + + #[inline] + fn insert_pointer(&self, ptr: Pointer) where - C: Comparator + CheapClone, - S: Checksumer, + C: Comparator, { - let (klen, kf) = kb.into_components(); - let (vlen, vf) = vb.into_components(); - let (len_size, kvlen, elen) = entry_size(klen, vlen); - let klen = klen as usize; - let vlen = vlen as usize; - let core = self.core_mut(); - let buf = core.arena.alloc_bytes(elen); - - match buf { - Err(e) => Err(Among::Right(Error::from_insufficient_space(e))), - Ok(mut buf) => { - unsafe { - // We allocate the buffer with the exact size, so it's safe to write to the buffer. - let flag = Flags::COMMITTED.bits(); - - core.cks.reset(); - core.cks.update(&[flag]); - - buf.put_u8_unchecked(Flags::empty().bits()); - let written = buf.put_u64_varint_unchecked(kvlen); - debug_assert_eq!( - written, len_size, - "the precalculated size should be equal to the written size" - ); - - let ko = STATUS_SIZE + written; - buf.set_len(ko + klen + vlen); - - kf(&mut VacantBuffer::new( - klen, - NonNull::new_unchecked(buf.as_mut_ptr().add(ko)), - )) - .map_err(Among::Left)?; - - let vo = ko + klen; - vf(&mut VacantBuffer::new( - vlen, - NonNull::new_unchecked(buf.as_mut_ptr().add(vo)), - )) - .map_err(Among::Middle)?; - - let cks = { - core.cks.update(&buf[1..]); - core.cks.digest() - }; - buf.put_u64_le_unchecked(cks); - - // commit the entry - buf[0] |= Flags::COMMITTED.bits(); - - if core.opts.sync_on_write() && core.arena.is_ondisk() { - core - .arena - .flush_range(buf.offset(), elen as usize) - .map_err(|e| Among::Right(e.into()))?; - } - buf.detach(); - core.map.insert(Pointer::new( - klen, - vlen, - buf.as_ptr().add(ko), - core.cmp.cheap_clone(), - )); - Ok(()) - } - } + unsafe { + (*self.core.get()).map.insert(ptr); + } + } + + #[inline] + fn insert_pointers(&self, ptrs: impl Iterator>) + where + C: Comparator, + { + unsafe { + (*self.core.get()).map.extend(ptrs); } } } impl ImmutableWal for OrderWal where - C: 'static, + C: Comparator + 'static, { type Iter<'a> = Iter<'a, C> where Self: 'a, C: Comparator; type Range<'a, Q, R> = Range<'a, C> @@ -216,13 +176,9 @@ where Self: 'a, C: Comparator; - unsafe fn reserved_slice(&self) -> &[u8] { - let core = self.core(); - if core.opts.reserved() == 0 { - return &[]; - } - - &core.arena.reserved_slice()[HEADER_SIZE..] + #[inline] + fn options(&self) -> &Options { + &self.core().opts } #[inline] @@ -252,6 +208,11 @@ where self.core().opts.maximum_value_size() } + #[inline] + fn remaining(&self) -> u32 { + self.core().arena.remaining() as u32 + } + #[inline] fn contains_key(&self, key: &Q) -> bool where @@ -356,52 +317,18 @@ where impl Wal for OrderWal where - C: 'static, + C: Comparator + 'static, { type Reader = Self; - #[inline] - fn read_only(&self) -> bool { - self.ro - } - #[inline] fn reader(&self) -> Self::Reader { Self { core: self.core.clone(), - ro: true, _s: PhantomData, } } - #[inline] - unsafe fn reserved_slice_mut(&mut self) -> &mut [u8] { - let core = self.core_mut(); - if core.opts.reserved() == 0 { - return &mut []; - } - - &mut core.arena.reserved_slice_mut()[HEADER_SIZE..] - } - - #[inline] - fn flush(&self) -> Result<(), Error> { - if self.ro { - return Err(error::Error::read_only()); - } - - self.core().arena.flush().map_err(Into::into) - } - - #[inline] - fn flush_async(&self) -> Result<(), Error> { - if self.ro { - return Err(error::Error::read_only()); - } - - self.core().arena.flush_async().map_err(Into::into) - } - fn get_or_insert_with_value_builder( &mut self, key: &[u8], @@ -409,7 +336,7 @@ where ) -> Result, Either> where C: Comparator + CheapClone, - S: Checksumer, + S: BuildChecksumer, { self .check( diff --git a/src/unsync/c.rs b/src/unsync/c.rs index 46037bb9..04175ba1 100644 --- a/src/unsync/c.rs +++ b/src/unsync/c.rs @@ -10,18 +10,24 @@ pub struct OrderWalCore { pub(super) cks: S, } -impl Base for BTreeSet> { - fn insert(&mut self, ele: Pointer) - where - C: Comparator, - { +impl Base for BTreeSet> +where + C: Comparator, +{ + type Pointer = Pointer; + + fn insert(&mut self, ele: Self::Pointer) { BTreeSet::insert(self, ele); } } -impl WalCore for OrderWalCore { +impl WalCore for OrderWalCore +where + C: Comparator, +{ type Allocator = Arena; type Base = BTreeSet>; + type Pointer = Pointer; #[inline] fn construct( diff --git a/src/unsync/tests.rs b/src/unsync/tests.rs index 5cd48486..df919bca 100644 --- a/src/unsync/tests.rs +++ b/src/unsync/tests.rs @@ -1,7 +1,3 @@ -use tempfile::tempdir; - -use crate::tests::*; - use super::*; #[cfg(all(test, any(test_unsync_constructor, all_tests)))] diff --git a/src/unsync/tests/insert.rs b/src/unsync/tests/insert.rs index 6aacd454..99bb41af 100644 --- a/src/unsync/tests/insert.rs +++ b/src/unsync/tests/insert.rs @@ -1,3 +1,5 @@ use super::*; common_unittests!(unsync::insert::OrderWal); + +common_unittests!(unsync::insert_batch::OrderWal); diff --git a/src/utils.rs b/src/utils.rs index 10f6f572..b08c761e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,11 +2,19 @@ pub use dbutils::leb128::*; use super::*; +/// Merge two `u32` into a `u64`. +/// +/// - high 32 bits: `a` +/// - low 32 bits: `b` #[inline] -pub(crate) const fn merge_lengths(klen: u32, vlen: u32) -> u64 { - (klen as u64) << 32 | vlen as u64 +pub(crate) const fn merge_lengths(a: u32, b: u32) -> u64 { + (a as u64) << 32 | b as u64 } +/// Split a `u64` into two `u32`. +/// +/// - high 32 bits: the first `u32` +/// - low 32 bits: the second `u32` #[inline] pub(crate) const fn split_lengths(len: u64) -> (u32, u32) { ((len >> 32) as u32, len as u32) @@ -60,11 +68,11 @@ pub(crate) const fn check( let max_vsize = min_u64(max_value_size as u64, u32::MAX as u64); if max_ksize < klen as u64 { - return Err(error::Error::key_too_large(klen as u32, max_key_size)); + return Err(error::Error::key_too_large(klen as u64, max_key_size)); } if max_vsize < vlen as u64 { - return Err(error::Error::value_too_large(vlen as u32, max_value_size)); + return Err(error::Error::value_too_large(vlen as u64, max_value_size)); } let (_, _, elen) = entry_size(klen as u32, vlen as u32); @@ -78,3 +86,21 @@ pub(crate) const fn check( Ok(()) } + +#[inline] +pub(crate) fn check_batch_entry( + klen: usize, + vlen: usize, + max_key_size: u32, + max_value_size: u32, +) -> Result<(), Error> { + if klen > max_key_size as usize { + return Err(Error::key_too_large(klen as u64, max_key_size)); + } + + if vlen > max_value_size as usize { + return Err(Error::value_too_large(vlen as u64, max_value_size)); + } + + Ok(()) +} diff --git a/src/wal.rs b/src/wal.rs index 0e112163..d49f7748 100644 --- a/src/wal.rs +++ b/src/wal.rs @@ -1,12 +1,15 @@ +use checksum::BuildChecksumer; use core::ops::RangeBounds; use super::*; -mod builder; -pub use builder::*; - pub(crate) mod sealed; +pub(crate) mod r#type; + +mod batch; +pub use batch::*; +/// An abstract layer for the immutable write-ahead log. pub trait ImmutableWal: sealed::Constructor { /// The iterator type. type Iter<'a>: Iterator + DoubleEndedIterator @@ -55,10 +58,22 @@ pub trait ImmutableWal: sealed::Constructor { /// Returns the reserved space in the WAL. /// - /// # Safety + /// ## Safety /// - The writer must ensure that the returned slice is not modified. /// - This method is not thread-safe, so be careful when using it. - unsafe fn reserved_slice(&self) -> &[u8]; + unsafe fn reserved_slice<'a>(&'a self) -> &'a [u8] + where + Self::Allocator: 'a, + { + let reserved = self.options().reserved(); + if reserved == 0 { + return &[]; + } + + let allocator = self.allocator(); + let reserved_slice = allocator.reserved_slice(); + &reserved_slice[HEADER_SIZE..] + } /// Returns the path of the WAL if it is backed by a file. fn path(&self) -> Option<&std::path::Path>; @@ -67,15 +82,37 @@ pub trait ImmutableWal: sealed::Constructor { fn len(&self) -> usize; /// Returns `true` if the WAL is empty. + #[inline] fn is_empty(&self) -> bool { self.len() == 0 } /// Returns the maximum key size allowed in the WAL. - fn maximum_key_size(&self) -> u32; + #[inline] + fn maximum_key_size(&self) -> u32 { + self.options().maximum_key_size() + } /// Returns the maximum value size allowed in the WAL. - fn maximum_value_size(&self) -> u32; + #[inline] + fn maximum_value_size(&self) -> u32 { + self.options().maximum_value_size() + } + + /// Returns the remaining capacity of the WAL. + #[inline] + fn remaining(&self) -> u32 { + self.allocator().remaining() as u32 + } + + /// Returns the capacity of the WAL. + #[inline] + fn capacity(&self) -> u32 { + self.options().capacity() + } + + /// Returns the options used to create this WAL instance. + fn options(&self) -> &Options; /// Returns `true` if the WAL contains the specified key. fn contains_key(&self, key: &Q) -> bool @@ -142,186 +179,53 @@ pub trait ImmutableWal: sealed::Constructor { } /// An abstract layer for the write-ahead log. -pub trait Wal: sealed::Sealed + ImmutableWal { +pub trait Wal: + sealed::Sealed> + ImmutableWal +{ /// The read only reader type for this wal. - type Reader: ImmutableWal; - - /// Creates a new in-memory write-ahead log backed by an aligned vec. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::OrderWal, Builder, Options, Wal}; - /// - /// let wal = OrderWal::new(Builder::new().with_capacity(1024)).unwrap(); - /// ``` - fn new(b: Builder) -> Result { - let Builder { opts, cmp, cks } = b; - let arena = ::new( - arena_options(opts.reserved()).with_capacity(opts.capacity()), - ) - .map_err(Error::from_insufficient_space)?; - >::new_in(arena, opts, cmp, cks) - .map(|core| Self::from_core(core, false)) - } - - /// Creates a new in-memory write-ahead log but backed by an anonymous mmap. - /// - /// # Example - /// - /// ```rust - /// use orderwal::{swmr::OrderWal, Builder, Wal}; - /// - /// let wal = OrderWal::map_anon(Builder::new().with_capacity(1024)).unwrap(); - /// ``` - fn map_anon(b: Builder) -> Result { - let Builder { opts, cmp, cks } = b; - let mmap_opts = MmapOptions::new().len(opts.capacity()); - ::map_anon(arena_options(opts.reserved()), mmap_opts) - .map_err(Into::into) - .and_then(|arena| { - >::new_in(arena, opts, cmp, cks) - .map(|core| Self::from_core(core, false)) - }) - } + type Reader: ImmutableWal; - /// Opens a write-ahead log backed by a file backed memory map in read-only mode. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - unsafe fn map

(path: P, b: Builder) -> Result - where - C: Comparator + CheapClone, - S: Checksumer, - P: AsRef, - { - >::map_with_path_builder::<_, ()>(|| Ok(path.as_ref().to_path_buf()), b) - .map_err(|e| e.unwrap_right()) - } - - /// Opens a write-ahead log backed by a file backed memory map in read-only mode. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - unsafe fn map_with_path_builder( - path_builder: PB, - b: Builder, - ) -> Result> - where - PB: FnOnce() -> Result, - C: Comparator + CheapClone, - S: Checksumer, - { - let open_options = OpenOptions::default().read(true); - - let Builder { opts, cmp, cks } = b; - - <>::Allocator as Allocator>::map_with_path_builder( - path_builder, - arena_options(opts.reserved()), - open_options, - MmapOptions::new(), - ) - .map_err(|e| e.map_right(Into::into)) - .and_then(|arena| { - >::replay(arena, Options::new(), true, cmp, cks) - .map(|core| >::from_core(core, true)) - .map_err(Either::Right) - }) - } - - /// Opens a write-ahead log backed by a file backed memory map. - /// - /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - unsafe fn map_mut

(path: P, b: Builder, open_opts: OpenOptions) -> Result - where - C: Comparator + CheapClone, - S: Checksumer, - P: AsRef, - { - >::map_mut_with_path_builder::<_, ()>( - || Ok(path.as_ref().to_path_buf()), - b, - open_opts, - ) - .map_err(|e| e.unwrap_right()) + /// Returns `true` if this WAL instance is read-only. + fn read_only(&self) -> bool { + self.allocator().read_only() } - /// Opens a write-ahead log backed by a file backed memory map. + /// Returns the mutable reference to the reserved slice. /// /// ## Safety - /// - /// All file-backed memory map constructors are marked `unsafe` because of the potential for - /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or - /// out of process. Applications must consider the risk and take appropriate precautions when - /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. - /// unlinked) files exist but are platform specific and limited. - unsafe fn map_mut_with_path_builder( - path_builder: PB, - b: Builder, - open_options: OpenOptions, - ) -> Result> + /// - The caller must ensure that the there is no others accessing reserved slice for either read or write. + /// - This method is not thread-safe, so be careful when using it. + unsafe fn reserved_slice_mut<'a>(&'a mut self) -> &'a mut [u8] where - PB: FnOnce() -> Result, - C: Comparator + CheapClone, - S: Checksumer, + Self::Allocator: 'a, { - let path = path_builder().map_err(Either::Left)?; - - let exist = path.exists(); - - let Builder { opts, cmp, cks } = b; - - ::map_mut( - path, - arena_options(opts.reserved()), - open_options, - MmapOptions::new(), - ) - .map_err(Into::into) - .and_then(|arena| { - if !exist { - >::new_in(arena, opts, cmp, cks) - .map(|core| Self::from_core(core, false)) - } else { - >::replay(arena, opts, false, cmp, cks) - .map(|core| Self::from_core(core, false)) - } - }) - .map_err(Either::Right) + let reserved = sealed::Sealed::options(self).reserved(); + if reserved == 0 { + return &mut []; + } + + let allocator = self.allocator(); + let reserved_slice = allocator.reserved_slice_mut(); + &mut reserved_slice[HEADER_SIZE..] } - /// Returns `true` if this WAL instance is read-only. - fn read_only(&self) -> bool; - - /// Returns the mutable reference to the reserved slice. - /// - /// # Safety - /// - The caller must ensure that the there is no others accessing reserved slice for either read or write. - /// - This method is not thread-safe, so be careful when using it. - unsafe fn reserved_slice_mut(&mut self) -> &mut [u8]; - /// Flushes the to disk. - fn flush(&self) -> Result<(), Error>; + fn flush(&self) -> Result<(), Error> { + if !self.read_only() { + self.allocator().flush().map_err(Into::into) + } else { + Err(Error::read_only()) + } + } /// Flushes the to disk. - fn flush_async(&self) -> Result<(), Error>; + fn flush_async(&self) -> Result<(), Error> { + if !self.read_only() { + self.allocator().flush_async().map_err(Into::into) + } else { + Err(Error::read_only()) + } + } /// Returns the read-only view for the WAL. fn reader(&self) -> Self::Reader; @@ -330,12 +234,12 @@ pub trait Wal: sealed::Sealed + ImmutableWal { fn get_or_insert(&mut self, key: &[u8], value: &[u8]) -> Result, Error> where C: Comparator + CheapClone, - S: Checksumer, + S: BuildChecksumer, { self .get_or_insert_with_value_builder::<()>( key, - ValueBuilder::new(value.len() as u32, |buf| { + ValueBuilder::once(value.len() as u32, |buf| { buf.put_slice(value).unwrap(); Ok(()) }), @@ -351,7 +255,7 @@ pub trait Wal: sealed::Sealed + ImmutableWal { ) -> Result, Either> where C: Comparator + CheapClone, - S: Checksumer; + S: BuildChecksumer; /// Inserts a key-value pair into the WAL. This method /// allows the caller to build the key in place. @@ -364,7 +268,7 @@ pub trait Wal: sealed::Sealed + ImmutableWal { ) -> Result<(), Either> where C: Comparator + CheapClone, - S: Checksumer, + S: BuildChecksumer, { self .check( @@ -379,11 +283,12 @@ pub trait Wal: sealed::Sealed + ImmutableWal { self .insert_with_in::( kb, - ValueBuilder::new(value.len() as u32, |buf| { + ValueBuilder::once(value.len() as u32, |buf| { buf.put_slice(value).unwrap(); Ok(()) }), ) + .map(|ptr| self.insert_pointer(ptr)) .map_err(Among::into_left_right) } @@ -398,7 +303,7 @@ pub trait Wal: sealed::Sealed + ImmutableWal { ) -> Result<(), Either> where C: Comparator + CheapClone, - S: Checksumer, + S: BuildChecksumer, { self .check( @@ -412,12 +317,13 @@ pub trait Wal: sealed::Sealed + ImmutableWal { self .insert_with_in::<(), E>( - KeyBuilder::new(key.len() as u32, |buf| { + KeyBuilder::once(key.len() as u32, |buf| { buf.put_slice(key).unwrap(); Ok(()) }), vb, ) + .map(|ptr| self.insert_pointer(ptr)) .map_err(Among::into_middle_right) } @@ -430,7 +336,7 @@ pub trait Wal: sealed::Sealed + ImmutableWal { ) -> Result<(), Among> where C: Comparator + CheapClone, - S: Checksumer, + S: BuildChecksumer, { self .check( @@ -442,14 +348,85 @@ pub trait Wal: sealed::Sealed + ImmutableWal { ) .map_err(Among::Right)?; - self.insert_with_in(kb, vb) + self + .insert_with_in(kb, vb) + .map(|ptr| self.insert_pointer(ptr)) + } + + /// Inserts a batch of key-value pairs into the WAL. + fn insert_batch_with_key_builder>( + &mut self, + batch: &mut B, + ) -> Result<(), Either> + where + C: Comparator + CheapClone, + S: BuildChecksumer, + { + if self.read_only() { + return Err(Either::Right(Error::read_only())); + } + + self + .insert_batch_with_key_builder_in(batch) + .map(|_| self.insert_pointers(batch.iter_mut().map(|ent| ent.pointer.take().unwrap()))) + } + + /// Inserts a batch of key-value pairs into the WAL. + fn insert_batch_with_value_builder>( + &mut self, + batch: &mut B, + ) -> Result<(), Either> + where + C: Comparator + CheapClone, + S: BuildChecksumer, + { + if self.read_only() { + return Err(Either::Right(Error::read_only())); + } + + self + .insert_batch_with_value_builder_in(batch) + .map(|_| self.insert_pointers(batch.iter_mut().map(|ent| ent.pointer.take().unwrap()))) + } + + /// Inserts a batch of key-value pairs into the WAL. + fn insert_batch_with_builders>( + &mut self, + batch: &mut B, + ) -> Result<(), Among> + where + C: Comparator + CheapClone, + S: BuildChecksumer, + { + if self.read_only() { + return Err(Among::Right(Error::read_only())); + } + + self + .insert_batch_with_builders_in(batch) + .map(|_| self.insert_pointers(batch.iter_mut().map(|ent| ent.pointer.take().unwrap()))) + } + + /// Inserts a batch of key-value pairs into the WAL. + fn insert_batch>(&mut self, batch: &mut B) -> Result<(), Error> + where + C: Comparator + CheapClone, + S: BuildChecksumer, + { + if self.read_only() { + return Err(Error::read_only()); + } + + self + .insert_batch_in(batch) + .map(|_| self.insert_pointers(batch.iter_mut().map(|ent| ent.pointer.take().unwrap()))) } /// Inserts a key-value pair into the WAL. fn insert(&mut self, key: &[u8], value: &[u8]) -> Result<(), Error> where C: Comparator + CheapClone, - S: Checksumer, + S: BuildChecksumer, { self.check( key.len(), @@ -461,15 +438,16 @@ pub trait Wal: sealed::Sealed + ImmutableWal { self .insert_with_in::<(), ()>( - KeyBuilder::new(key.len() as u32, |buf| { + KeyBuilder::once(key.len() as u32, |buf: &mut VacantBuffer<'_>| { buf.put_slice(key).unwrap(); Ok(()) }), - ValueBuilder::new(value.len() as u32, |buf| { + ValueBuilder::once(value.len() as u32, |buf: &mut VacantBuffer<'_>| { buf.put_slice(value).unwrap(); Ok(()) }), ) + .map(|ptr| self.insert_pointer(ptr)) .map_err(Among::unwrap_right) } } diff --git a/src/wal/batch.rs b/src/wal/batch.rs new file mode 100644 index 00000000..39967af8 --- /dev/null +++ b/src/wal/batch.rs @@ -0,0 +1,224 @@ +use core::borrow::Borrow; + +use dbutils::{buffer::VacantBuffer, Comparator}; + +use super::entry::{ + Entry, EntryWithBuilders, EntryWithKeyBuilder, EntryWithValueBuilder, GenericEntry, +}; + +/// A batch of keys and values that can be inserted into the [`Wal`](super::Wal). +pub trait Batch { + /// The key type. + type Key: Borrow<[u8]>; + + /// The value type. + type Value: Borrow<[u8]>; + + /// The [`Comparator`] type. + type Comparator: Comparator; + + /// The iterator type. + type IterMut<'a>: Iterator> + where + Self: 'a; + + /// Returns an iterator over the keys and values. + fn iter_mut(&mut self) -> Self::IterMut<'_>; +} + +impl Batch for T +where + K: Borrow<[u8]>, + V: Borrow<[u8]>, + C: Comparator, + for<'a> &'a mut T: IntoIterator>, +{ + type Key = K; + type Value = V; + type Comparator = C; + + type IterMut<'a> = <&'a mut T as IntoIterator>::IntoIter where Self: 'a; + + fn iter_mut(&mut self) -> Self::IterMut<'_> { + IntoIterator::into_iter(self) + } +} + +/// A batch of keys and values that can be inserted into the [`Wal`](super::Wal). +/// Comparing to [`Batch`], this trait is used to build +/// the key in place. +pub trait BatchWithKeyBuilder { + /// The key builder type. + type KeyBuilder: Fn(&mut VacantBuffer<'_>) -> Result<(), Self::Error>; + + /// The error for the key builder. + type Error; + + /// The value type. + type Value: Borrow<[u8]>; + + /// The [`Comparator`] type. + type Comparator: Comparator; + + /// The iterator type. + type IterMut<'a>: Iterator< + Item = &'a mut EntryWithKeyBuilder, + > + where + Self: 'a; + + /// Returns an iterator over the keys and values. + fn iter_mut(&mut self) -> Self::IterMut<'_>; +} + +impl BatchWithKeyBuilder for T +where + KB: Fn(&mut VacantBuffer<'_>) -> Result<(), E>, + V: Borrow<[u8]>, + C: Comparator, + for<'a> &'a mut T: IntoIterator>, +{ + type KeyBuilder = KB; + type Error = E; + type Value = V; + type Comparator = C; + + type IterMut<'a> = <&'a mut T as IntoIterator>::IntoIter where Self: 'a; + + fn iter_mut(&mut self) -> Self::IterMut<'_> { + IntoIterator::into_iter(self) + } +} + +/// A batch of keys and values that can be inserted into the [`Wal`](super::Wal). +/// Comparing to [`Batch`], this trait is used to build +/// the value in place. +pub trait BatchWithValueBuilder { + /// The value builder type. + type ValueBuilder: Fn(&mut VacantBuffer<'_>) -> Result<(), Self::Error>; + + /// The error for the value builder. + type Error; + + /// The key type. + type Key: Borrow<[u8]>; + + /// The [`Comparator`] type. + type Comparator: Comparator; + + /// The iterator type. + type IterMut<'a>: Iterator< + Item = &'a mut EntryWithValueBuilder, + > + where + Self: 'a; + + /// Returns an iterator over the keys and values. + fn iter_mut(&mut self) -> Self::IterMut<'_>; +} + +impl BatchWithValueBuilder for T +where + VB: Fn(&mut VacantBuffer<'_>) -> Result<(), E>, + K: Borrow<[u8]>, + C: Comparator, + for<'a> &'a mut T: IntoIterator>, +{ + type Key = K; + type Error = E; + type ValueBuilder = VB; + type Comparator = C; + + type IterMut<'a> = <&'a mut T as IntoIterator>::IntoIter where Self: 'a; + + fn iter_mut(&mut self) -> Self::IterMut<'_> { + IntoIterator::into_iter(self) + } +} + +/// A batch of keys and values that can be inserted into the [`Wal`](super::Wal). +/// Comparing to [`Batch`], this trait is used to build +/// the key and value in place. +pub trait BatchWithBuilders { + /// The value builder type. + type ValueBuilder: Fn(&mut VacantBuffer<'_>) -> Result<(), Self::ValueError>; + + /// The error for the value builder. + type ValueError; + + /// The value builder type. + type KeyBuilder: Fn(&mut VacantBuffer<'_>) -> Result<(), Self::KeyError>; + + /// The error for the value builder. + type KeyError; + + /// The [`Comparator`] type. + type Comparator: Comparator; + + /// The iterator type. + type IterMut<'a>: Iterator< + Item = &'a mut EntryWithBuilders, + > + where + Self: 'a; + + /// Returns an iterator over the keys and values. + fn iter_mut(&mut self) -> Self::IterMut<'_>; +} + +impl BatchWithBuilders for T +where + VB: Fn(&mut VacantBuffer<'_>) -> Result<(), VE>, + KB: Fn(&mut VacantBuffer<'_>) -> Result<(), KE>, + C: Comparator, + for<'a> &'a mut T: IntoIterator>, +{ + type KeyBuilder = KB; + type KeyError = KE; + type ValueBuilder = VB; + type ValueError = VE; + type Comparator = C; + + type IterMut<'a> = <&'a mut T as IntoIterator>::IntoIter where Self: 'a; + + fn iter_mut(&mut self) -> Self::IterMut<'_> { + IntoIterator::into_iter(self) + } +} + +/// The container for entries in the [`GenericBatch`]. +pub trait GenericBatch<'e> { + /// The key type. + type Key: 'e; + + /// The value type. + type Value: 'e; + + /// The mutable iterator type. + type IterMut<'a>: Iterator> + where + Self: 'e, + 'e: 'a; + + /// Returns an mutable iterator over the keys and values. + fn iter_mut(&'e mut self) -> Self::IterMut<'e>; +} + +impl<'e, K, V, T> GenericBatch<'e> for T +where + K: 'e, + V: 'e, + for<'a> &'a mut T: IntoIterator>, +{ + type Key = K; + type Value = V; + + type IterMut<'a> = <&'a mut T as IntoIterator>::IntoIter + where + Self: 'e, + 'e: 'a; + + fn iter_mut(&'e mut self) -> Self::IterMut<'e> { + IntoIterator::into_iter(self) + } +} diff --git a/src/wal/builder.rs b/src/wal/builder.rs deleted file mode 100644 index a77b1ab7..00000000 --- a/src/wal/builder.rs +++ /dev/null @@ -1,328 +0,0 @@ -use super::*; - -/// A write-ahead log builder. -pub struct Builder { - pub(super) opts: Options, - pub(super) cmp: C, - pub(super) cks: S, -} - -impl Default for Builder { - #[inline] - fn default() -> Self { - Self::new() - } -} - -impl Builder { - /// Returns a new write-ahead log builder with the given options. - #[inline] - pub fn new() -> Self { - Self { - opts: Options::default(), - cmp: Ascend, - cks: Crc32::default(), - } - } -} - -impl Builder { - /// Returns a new write-ahead log builder with the new comparator - #[inline] - pub fn with_comparator(self, cmp: NC) -> Builder { - Builder { - opts: self.opts, - cmp, - cks: self.cks, - } - } - - /// Returns a new write-ahead log builder with the new checksumer - #[inline] - pub fn with_checksumer(self, cks: NS) -> Builder { - Builder { - opts: self.opts, - cmp: self.cmp, - cks, - } - } - - /// Returns a new write-ahead log builder with the new options - #[inline] - pub fn with_options(self, opts: Options) -> Self { - Builder { - opts, - cmp: self.cmp, - cks: self.cks, - } - } - - /// Set the reserved bytes of the WAL. - /// - /// The `reserved` is used to configure the start position of the WAL. This is useful - /// when you want to add some bytes as your own WAL's header. - /// - /// The default reserved is `0`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let opts = Builder::new().with_reserved(8); - /// ``` - #[inline] - pub const fn with_reserved(mut self, reserved: u32) -> Self { - self.opts = self.opts.with_reserved(reserved); - self - } - - /// Get the reserved of the WAL. - /// - /// The `reserved` is used to configure the start position of the WAL. This is useful - /// when you want to add some bytes as your own WAL's header. - /// - /// The default reserved is `0`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let opts = Builder::new().with_reserved(8); - /// - /// assert_eq!(opts.reserved(), 8); - /// ``` - #[inline] - pub const fn reserved(&self) -> u32 { - self.opts.reserved() - } - - /// Returns the magic version. - /// - /// The default value is `0`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_magic_version(1); - /// assert_eq!(options.magic_version(), 1); - /// ``` - #[inline] - pub const fn magic_version(&self) -> u16 { - self.opts.magic_version() - } - - /// Returns the capacity of the WAL. - /// - /// The default value is `0`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_capacity(1000); - /// assert_eq!(options.capacity(), 1000); - /// ``` - #[inline] - pub const fn capacity(&self) -> u32 { - self.opts.capacity() - } - - /// Returns the maximum key length. - /// - /// The default value is `u16::MAX`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_maximum_key_size(1024); - /// assert_eq!(options.maximum_key_size(), 1024); - /// ``` - #[inline] - pub const fn maximum_key_size(&self) -> u32 { - self.opts.maximum_key_size() - } - - /// Returns the maximum value length. - /// - /// The default value is `u32::MAX`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_maximum_value_size(1024); - /// assert_eq!(options.maximum_value_size(), 1024); - /// ``` - #[inline] - pub const fn maximum_value_size(&self) -> u32 { - self.opts.maximum_value_size() - } - - /// Returns `true` if the WAL syncs on write. - /// - /// The default value is `true`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new(); - /// assert_eq!(options.sync_on_write(), true); - /// ``` - #[inline] - pub const fn sync_on_write(&self) -> bool { - self.opts.sync_on_write() - } - - /// Returns the bits of the page size. - /// - /// Configures the anonymous memory map to be allocated using huge pages. - /// - /// This option corresponds to the `MAP_HUGETLB` flag on Linux. It has no effect on Windows. - /// - /// The size of the requested page can be specified in page bits. - /// If not provided, the system default is requested. - /// The requested length should be a multiple of this, or the mapping will fail. - /// - /// This option has no effect on file-backed memory maps. - /// - /// The default value is `None`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_huge(64); - /// assert_eq!(options.huge(), Some(64)); - /// ``` - #[inline] - pub const fn huge(&self) -> Option { - self.opts.huge() - } - - /// Sets the capacity of the WAL. - /// - /// This configuration will be ignored when using file-backed memory maps. - /// - /// The default value is `0`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_capacity(100); - /// assert_eq!(options.capacity(), 100); - /// ``` - #[inline] - pub const fn with_capacity(mut self, cap: u32) -> Self { - self.opts = self.opts.with_capacity(cap); - self - } - - /// Sets the maximum key length. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_maximum_key_size(1024); - /// assert_eq!(options.maximum_key_size(), 1024); - /// ``` - #[inline] - pub const fn with_maximum_key_size(mut self, size: u32) -> Self { - self.opts = self.opts.with_maximum_key_size(size); - self - } - - /// Sets the maximum value length. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_maximum_value_size(1024); - /// assert_eq!(options.maximum_value_size(), 1024); - /// ``` - #[inline] - pub const fn with_maximum_value_size(mut self, size: u32) -> Self { - self.opts = self.opts.with_maximum_value_size(size); - self - } - - /// Returns the bits of the page size. - /// - /// Configures the anonymous memory map to be allocated using huge pages. - /// - /// This option corresponds to the `MAP_HUGETLB` flag on Linux. It has no effect on Windows. - /// - /// The size of the requested page can be specified in page bits. - /// If not provided, the system default is requested. - /// The requested length should be a multiple of this, or the mapping will fail. - /// - /// This option has no effect on file-backed memory maps. - /// - /// The default value is `None`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_huge(64); - /// assert_eq!(options.huge(), Some(64)); - /// ``` - #[inline] - pub const fn with_huge(mut self, page_bits: u8) -> Self { - self.opts = self.opts.with_huge(page_bits); - self - } - - /// Sets the WAL to sync on write. - /// - /// The default value is `true`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_sync_on_write(false); - /// assert_eq!(options.sync_on_write(), false); - /// ``` - #[inline] - pub const fn with_sync_on_write(mut self, sync: bool) -> Self { - self.opts = self.opts.with_sync_on_write(sync); - self - } - - /// Sets the magic version. - /// - /// The default value is `0`. - /// - /// # Example - /// - /// ```rust - /// use orderwal::Builder; - /// - /// let options = Builder::new().with_magic_version(1); - /// assert_eq!(options.magic_version(), 1); - /// ``` - #[inline] - pub const fn with_magic_version(mut self, version: u16) -> Self { - self.opts = self.opts.with_magic_version(version); - self - } -} diff --git a/src/wal/sealed.rs b/src/wal/sealed.rs index ac0a7e48..1aaa8dbb 100644 --- a/src/wal/sealed.rs +++ b/src/wal/sealed.rs @@ -1,20 +1,89 @@ -use rarena_allocator::ArenaPosition; +use core::ptr::NonNull; + +use checksum::{BuildChecksumer, Checksumer}; +use rarena_allocator::{ArenaPosition, BytesRefMut}; use super::*; -pub trait Base: Default { - fn insert(&mut self, ele: Pointer) +pub trait Pointer { + type Comparator; + + fn new(klen: usize, vlen: usize, ptr: *const u8, cmp: Self::Comparator) -> Self; +} + +pub trait Base: Default { + type Pointer: Pointer; + + fn insert(&mut self, ele: Self::Pointer) where - C: Comparator; + Self::Pointer: Ord + 'static; +} + +impl

Base for SkipSet

+where + P: Pointer + Send, +{ + type Pointer = P; + + fn insert(&mut self, ele: Self::Pointer) + where + P: Ord + 'static, + { + SkipSet::insert(self, ele); + } } pub trait WalCore { type Allocator: Allocator; - type Base: Base; + type Base: Base; + type Pointer: Pointer; fn construct(arena: Self::Allocator, base: Self::Base, opts: Options, cmp: C, cks: S) -> Self; } +macro_rules! preprocess_batch { + ($this:ident($batch:ident)) => {{ + $batch + .iter_mut() + .try_fold((0u32, 0u64), |(num_entries, size), ent| { + let klen = ent.key_len(); + let vlen = ent.value_len(); + $this.check_batch_entry(klen, vlen).map(|_| { + let merged_len = merge_lengths(klen as u32, vlen as u32); + let merged_len_size = encoded_u64_varint_len(merged_len); + let ent_size = klen as u64 + vlen as u64 + merged_len_size as u64; + ent.meta = BatchEncodedEntryMeta::new(klen, vlen, merged_len, merged_len_size); + (num_entries + 1, size + ent_size) + }) + }) + .and_then(|(num_entries, batch_encoded_size)| { + // safe to cast batch_encoded_size to u32 here, we already checked it's less than capacity (less than u32::MAX). + let batch_meta = merge_lengths(num_entries, batch_encoded_size as u32); + let batch_meta_size = encoded_u64_varint_len(batch_meta); + let allocator = $this.allocator(); + let remaining = allocator.remaining() as u64; + let total_size = + STATUS_SIZE as u64 + batch_meta_size as u64 + batch_encoded_size + CHECKSUM_SIZE as u64; + if total_size > remaining { + return Err(Error::insufficient_space(total_size, remaining as u32)); + } + + let mut buf = allocator + .alloc_bytes(total_size as u32) + .map_err(Error::from_insufficient_space)?; + + let flag = Flags::BATCHING; + + unsafe { + buf.put_u8_unchecked(flag.bits); + buf.put_u64_varint_unchecked(batch_meta); + } + + Ok((1 + batch_meta_size, allocator, buf)) + }) + }}; +} + pub trait Sealed: Constructor { #[inline] fn check( @@ -28,19 +97,319 @@ pub trait Sealed: Constructor { crate::check(klen, vlen, max_key_size, max_value_size, ro) } + #[inline] + fn check_batch_entry(&self, klen: usize, vlen: usize) -> Result<(), Error> { + let opts = self.options(); + let max_key_size = opts.maximum_key_size(); + let max_value_size = opts.maximum_value_size(); + + crate::utils::check_batch_entry(klen, vlen, max_key_size, max_value_size) + } + + fn hasher(&self) -> &S; + + fn options(&self) -> &Options; + + fn comparator(&self) -> &C; + + fn insert_pointer(&self, ptr: Self::Pointer) + where + C: Comparator; + + fn insert_pointers(&self, ptrs: impl Iterator) + where + C: Comparator; + + fn insert_batch_with_key_builder_in>( + &mut self, + batch: &mut B, + ) -> Result<(), Either> + where + C: Comparator + CheapClone, + S: BuildChecksumer, + { + let (mut cursor, allocator, mut buf) = preprocess_batch!(self(batch)).map_err(Either::Right)?; + + unsafe { + let cmp = self.comparator(); + + for ent in batch.iter_mut() { + let klen = ent.key_len(); + let vlen = ent.value_len(); + let merged_kv_len = ent.meta.kvlen; + let merged_kv_len_size = ent.meta.kvlen_size; + let remaining = buf.remaining(); + if remaining < merged_kv_len_size + klen + vlen { + return Err(Either::Right(Error::larger_batch_size( + buf.capacity() as u32 + ))); + } + + let ent_len_size = buf.put_u64_varint_unchecked(merged_kv_len); + let ptr = buf.as_mut_ptr().add(cursor + ent_len_size); + buf.set_len(cursor + ent_len_size + klen); + let f = ent.key_builder().builder(); + f(&mut VacantBuffer::new(klen, NonNull::new_unchecked(ptr))).map_err(Either::Left)?; + + cursor += ent_len_size + klen; + cursor += vlen; + buf.put_slice_unchecked(ent.value().borrow()); + ent.pointer = Some(Pointer::new(klen, vlen, ptr, cmp.cheap_clone())); + } + + self + .insert_batch_helper(allocator, buf, cursor) + .map_err(Either::Right) + } + } + + fn insert_batch_with_value_builder_in>( + &mut self, + batch: &mut B, + ) -> Result<(), Either> + where + C: Comparator + CheapClone, + S: BuildChecksumer, + { + let (mut cursor, allocator, mut buf) = preprocess_batch!(self(batch)).map_err(Either::Right)?; + + unsafe { + let cmp = self.comparator(); + + for ent in batch.iter_mut() { + let klen = ent.key_len(); + let vlen = ent.value_len(); + let merged_kv_len = ent.meta.kvlen; + let merged_kv_len_size = ent.meta.kvlen_size; + let remaining = buf.remaining(); + if remaining < merged_kv_len_size + klen + vlen { + return Err(Either::Right(Error::larger_batch_size( + buf.capacity() as u32 + ))); + } + + let ent_len_size = buf.put_u64_varint_unchecked(merged_kv_len); + let ptr = buf.as_mut_ptr().add(cursor + ent_len_size); + cursor += klen + ent_len_size; + buf.put_slice_unchecked(ent.key().borrow()); + buf.set_len(cursor + vlen); + let f = ent.vb.builder(); + let mut vacant_buffer = VacantBuffer::new(klen, NonNull::new_unchecked(ptr.add(klen))); + f(&mut vacant_buffer).map_err(Either::Left)?; + + cursor += vlen; + ent.pointer = Some(Pointer::new(klen, vlen, ptr, cmp.cheap_clone())); + } + + self + .insert_batch_helper(allocator, buf, cursor) + .map_err(Either::Right) + } + } + + fn insert_batch_with_builders_in>( + &mut self, + batch: &mut B, + ) -> Result<(), Among> + where + C: Comparator + CheapClone, + S: BuildChecksumer, + { + let (mut cursor, allocator, mut buf) = preprocess_batch!(self(batch)).map_err(Among::Right)?; + + unsafe { + let cmp = self.comparator(); + + for ent in batch.iter_mut() { + let klen = ent.key_len(); + let vlen = ent.value_len(); + let merged_kv_len = ent.meta.kvlen; + let merged_kv_len_size = ent.meta.kvlen_size; + + let remaining = buf.remaining(); + if remaining < merged_kv_len_size + klen + vlen { + return Err(Among::Right( + Error::larger_batch_size(buf.capacity() as u32), + )); + } + + let ent_len_size = buf.put_u64_varint_unchecked(merged_kv_len); + let ptr = buf.as_mut_ptr().add(cursor + ent_len_size); + buf.set_len(cursor + ent_len_size + klen); + let f = ent.key_builder().builder(); + f(&mut VacantBuffer::new(klen, NonNull::new_unchecked(ptr))).map_err(Among::Left)?; + cursor += ent_len_size + klen; + buf.set_len(cursor + vlen); + let f = ent.value_builder().builder(); + f(&mut VacantBuffer::new( + klen, + NonNull::new_unchecked(ptr.add(klen)), + )) + .map_err(Among::Middle)?; + cursor += vlen; + ent.pointer = Some(Pointer::new(klen, vlen, ptr, cmp.cheap_clone())); + } + + self + .insert_batch_helper(allocator, buf, cursor) + .map_err(Among::Right) + } + } + + fn insert_batch_in>(&mut self, batch: &mut B) -> Result<(), Error> + where + C: Comparator + CheapClone, + S: BuildChecksumer, + { + let (mut cursor, allocator, mut buf) = preprocess_batch!(self(batch))?; + + unsafe { + let cmp = self.comparator(); + + for ent in batch.iter_mut() { + let klen = ent.key_len(); + let vlen = ent.value_len(); + let merged_kv_len = ent.meta.kvlen; + let merged_kv_len_size = ent.meta.kvlen_size; + + let remaining = buf.remaining(); + if remaining < merged_kv_len_size + klen + vlen { + return Err(Error::larger_batch_size(buf.capacity() as u32)); + } + + let ent_len_size = buf.put_u64_varint_unchecked(merged_kv_len); + let ptr = buf.as_mut_ptr().add(cursor + ent_len_size); + cursor += ent_len_size + klen; + buf.put_slice_unchecked(ent.key().borrow()); + cursor += vlen; + buf.put_slice_unchecked(ent.value().borrow()); + ent.pointer = Some(Pointer::new(klen, vlen, ptr, cmp.cheap_clone())); + } + + self.insert_batch_helper(allocator, buf, cursor) + } + } + fn insert_with_in( &mut self, kb: KeyBuilder) -> Result<(), KE>>, vb: ValueBuilder) -> Result<(), VE>>, - ) -> Result<(), Among> + ) -> Result> where C: Comparator + CheapClone, - S: Checksumer; + S: BuildChecksumer, + { + let (klen, kf) = kb.into_components(); + let (vlen, vf) = vb.into_components(); + let (len_size, kvlen, elen) = entry_size(klen, vlen); + let klen = klen as usize; + let vlen = vlen as usize; + let allocator = self.allocator(); + let is_ondisk = allocator.is_ondisk(); + let buf = allocator.alloc_bytes(elen); + let mut cks = self.hasher().build_checksumer(); + + match buf { + Err(e) => Err(Among::Right(Error::from_insufficient_space(e))), + Ok(mut buf) => { + unsafe { + // We allocate the buffer with the exact size, so it's safe to write to the buffer. + let flag = Flags::COMMITTED.bits(); + + cks.update(&[flag]); + + buf.put_u8_unchecked(Flags::empty().bits()); + let written = buf.put_u64_varint_unchecked(kvlen); + debug_assert_eq!( + written, len_size, + "the precalculated size should be equal to the written size" + ); + + let ko = STATUS_SIZE + written; + buf.set_len(ko + klen + vlen); + + kf(&mut VacantBuffer::new( + klen, + NonNull::new_unchecked(buf.as_mut_ptr().add(ko)), + )) + .map_err(Among::Left)?; + + let vo = ko + klen; + vf(&mut VacantBuffer::new( + vlen, + NonNull::new_unchecked(buf.as_mut_ptr().add(vo)), + )) + .map_err(Among::Middle)?; + + let cks = { + cks.update(&buf[1..]); + cks.digest() + }; + buf.put_u64_le_unchecked(cks); + + // commit the entry + buf[0] |= Flags::COMMITTED.bits(); + + if self.options().sync_on_write() && is_ondisk { + allocator + .flush_range(buf.offset(), elen as usize) + .map_err(|e| Among::Right(e.into()))?; + } + buf.detach(); + let cmp = self.comparator().cheap_clone(); + let ptr = buf.as_ptr().add(ko); + Ok(Pointer::new(klen, vlen, ptr, cmp)) + } + } + } + } } +trait SealedExt: Sealed { + unsafe fn insert_batch_helper( + &self, + allocator: &Self::Allocator, + mut buf: BytesRefMut<'_, Self::Allocator>, + cursor: usize, + ) -> Result<(), Error> + where + S: BuildChecksumer, + { + let total_size = buf.capacity(); + if cursor + CHECKSUM_SIZE != total_size { + return Err(Error::batch_size_mismatch( + total_size as u32 - CHECKSUM_SIZE as u32, + cursor as u32, + )); + } + + let mut cks = self.hasher().build_checksumer(); + let committed_flag = Flags::BATCHING | Flags::COMMITTED; + cks.update(&[committed_flag.bits]); + cks.update(&buf[1..]); + let checksum = cks.digest(); + buf.put_u64_le_unchecked(checksum); + + // commit the entry + buf[0] = committed_flag.bits; + let buf_cap = buf.capacity(); + + if self.options().sync_on_write() && allocator.is_ondisk() { + allocator.flush_range(buf.offset(), buf_cap)?; + } + buf.detach(); + Ok(()) + } +} + +impl SealedExt for T where T: Sealed {} + pub trait Constructor: Sized { type Allocator: Allocator; - type Core: WalCore; + type Core: WalCore; + type Pointer: Pointer; + + fn allocator(&self) -> &Self::Allocator; fn new_in(arena: Self::Allocator, opts: Options, cmp: C, cks: S) -> Result { unsafe { @@ -63,8 +432,9 @@ pub trait Constructor: Sized { checksumer: S, ) -> Result where - C: Comparator + CheapClone, - S: Checksumer, + C: CheapClone, + S: BuildChecksumer, + Self::Pointer: Ord + 'static, { let slice = arena.reserved_slice(); let magic_text = &slice[0..6]; @@ -99,50 +469,120 @@ pub trait Constructor: Sized { let header = arena.get_u8(cursor).unwrap(); let flag = Flags::from_bits_unchecked(header); - let (kvsize, encoded_len) = arena.get_u64_varint(cursor + STATUS_SIZE).map_err(|_e| { - #[cfg(feature = "tracing")] - tracing::error!(err=%_e); + if !flag.contains(Flags::BATCHING) { + let (readed, encoded_len) = arena.get_u64_varint(cursor + STATUS_SIZE).map_err(|e| { + #[cfg(feature = "tracing")] + tracing::error!(err=%e); - Error::corrupted() - })?; + Error::corrupted(e) + })?; + let (key_len, value_len) = split_lengths(encoded_len); + let key_len = key_len as usize; + let value_len = value_len as usize; + // Same as above, if we reached the end of the arena, we should discard the remaining. + let cks_offset = STATUS_SIZE + readed + key_len + value_len; + if cks_offset + CHECKSUM_SIZE > allocated { + // If the entry is committed, then it means our file is truncated, so we should report corrupted. + if flag.contains(Flags::COMMITTED) { + return Err(Error::corrupted("file is truncated")); + } - let (key_len, value_len) = split_lengths(encoded_len); - let key_len = key_len as usize; - let value_len = value_len as usize; - // Same as above, if we reached the end of the arena, we should discard the remaining. - let cks_offset = STATUS_SIZE + kvsize + key_len + value_len; - if cks_offset + CHECKSUM_SIZE > allocated { - if !ro { - arena.rewind(ArenaPosition::Start(cursor as u32)); - arena.flush()?; + if !ro { + arena.rewind(ArenaPosition::Start(cursor as u32)); + arena.flush()?; + } + + break; } - break; - } + let cks = arena.get_u64_le(cursor + cks_offset).unwrap(); - let cks = arena.get_u64_le(cursor + cks_offset).unwrap(); + if cks != checksumer.checksum_one(arena.get_bytes(cursor, cks_offset)) { + return Err(Error::corrupted("checksum mismatch")); + } - if cks != checksumer.checksum(arena.get_bytes(cursor, cks_offset)) { - return Err(Error::corrupted()); - } + // If the entry is not committed, we should not rewind + if !flag.contains(Flags::COMMITTED) { + if !ro { + arena.rewind(ArenaPosition::Start(cursor as u32)); + arena.flush()?; + } - // If the entry is not committed, we should not rewind - if !flag.contains(Flags::COMMITTED) { - if !ro { - arena.rewind(ArenaPosition::Start(cursor as u32)); - arena.flush()?; + break; } - break; - } + set.insert(Pointer::new( + key_len, + value_len, + arena.get_pointer(cursor + STATUS_SIZE + readed), + cmp.cheap_clone(), + )); + cursor += cks_offset + CHECKSUM_SIZE; + } else { + let (readed, encoded_len) = arena.get_u64_varint(cursor + STATUS_SIZE).map_err(|e| { + #[cfg(feature = "tracing")] + tracing::error!(err=%e); + + Error::corrupted(e) + })?; + + let (num_entries, encoded_data_len) = split_lengths(encoded_len); + + // Same as above, if we reached the end of the arena, we should discard the remaining. + let cks_offset = STATUS_SIZE + readed + encoded_data_len as usize; + if cks_offset + CHECKSUM_SIZE > allocated { + // If the entry is committed, then it means our file is truncated, so we should report corrupted. + if flag.contains(Flags::COMMITTED) { + return Err(Error::corrupted("file is truncated")); + } + + if !ro { + arena.rewind(ArenaPosition::Start(cursor as u32)); + arena.flush()?; + } + + break; + } - set.insert(Pointer::new( - key_len, - value_len, - arena.get_pointer(cursor + STATUS_SIZE + kvsize), - cmp.cheap_clone(), - )); - cursor += cks_offset + CHECKSUM_SIZE; + let cks = arena.get_u64_le(cursor + cks_offset).unwrap(); + let mut batch_data_buf = arena.get_bytes(cursor, cks_offset); + if cks != checksumer.checksum_one(batch_data_buf) { + return Err(Error::corrupted("checksum mismatch")); + } + + let mut sub_cursor = 0; + batch_data_buf = &batch_data_buf[1 + readed..]; + for _ in 0..num_entries { + let (kvlen, ent_len) = decode_u64_varint(batch_data_buf).map_err(|e| { + #[cfg(feature = "tracing")] + tracing::error!(err=%e); + + Error::corrupted(e) + })?; + + let (klen, vlen) = split_lengths(ent_len); + let klen = klen as usize; + let vlen = vlen as usize; + + let ptr = Pointer::new( + klen, + vlen, + arena.get_pointer(cursor + STATUS_SIZE + readed + sub_cursor + kvlen), + cmp.cheap_clone(), + ); + set.insert(ptr); + let ent_len = kvlen + klen + vlen; + sub_cursor += kvlen + klen + vlen; + batch_data_buf = &batch_data_buf[ent_len..]; + } + + debug_assert_eq!( + sub_cursor, encoded_data_len as usize, + "expected encoded batch data size is not equal to the actual size" + ); + + cursor += cks_offset + CHECKSUM_SIZE; + } } } @@ -151,5 +591,5 @@ pub trait Constructor: Sized { )) } - fn from_core(core: Self::Core, ro: bool) -> Self; + fn from_core(core: Self::Core) -> Self; } diff --git a/src/swmr/generic/traits.rs b/src/wal/type.rs similarity index 60% rename from src/swmr/generic/traits.rs rename to src/wal/type.rs index 2b34ffd4..bcbb1cdf 100644 --- a/src/swmr/generic/traits.rs +++ b/src/wal/type.rs @@ -1,16 +1,16 @@ use core::cmp; use among::Among; -use crossbeam_skiplist::Comparable; +use dbutils::equivalent::Comparable; mod impls; pub use impls::*; -/// The type trait for limiting the types that can be used as keys and values in the [`GenericOrderWal`]. +/// The type trait for limiting the types that can be used as keys and values in the [`GenericOrderWal`](crate::swmr::GenericOrderWal). /// -/// This trait and its implementors can only be used with the [`GenericOrderWal`] type, otherwise +/// This trait and its implementors can only be used with the [`GenericOrderWal`](crate::swmr::GenericOrderWal) type, otherwise /// the correctness of the implementations is not guaranteed. -pub trait Type { +pub trait Type: core::fmt::Debug { /// The reference type for the type. type Ref<'a>: TypeRef<'a>; @@ -20,11 +20,34 @@ pub trait Type { /// Returns the length of the encoded type size. fn encoded_len(&self) -> usize; - /// Encodes the type into a binary slice, you can assume that the buf length is equal to the value returned by [`encoded_len`](Type::encoded_len). + /// Encodes the type into a bytes slice, you can assume that the buf length is equal to the value returned by [`encoded_len`](Type::encoded_len). fn encode(&self, buf: &mut [u8]) -> Result<(), Self::Error>; + + /// Encodes the type into a [`Vec`]. + #[inline] + fn encode_into_vec(&self) -> Result, Self::Error> { + let mut buf = vec![0; self.encoded_len()]; + self.encode(&mut buf)?; + Ok(buf) + } +} + +impl Type for &T { + type Ref<'a> = T::Ref<'a>; + type Error = T::Error; + + #[inline] + fn encoded_len(&self) -> usize { + T::encoded_len(*self) + } + + #[inline] + fn encode(&self, buf: &mut [u8]) -> Result<(), Self::Error> { + T::encode(*self, buf) + } } -pub(super) trait InsertAmongExt { +pub(crate) trait InsertAmongExt { fn encoded_len(&self) -> usize; fn encode(&self, buf: &mut [u8]) -> Result<(), T::Error>; } @@ -53,13 +76,16 @@ impl InsertAmongExt for Among { } /// The reference type trait for the [`Type`] trait. -pub trait TypeRef<'a> { - /// Creates a reference type from a binary slice, when using it with [`GenericOrderWal`], +pub trait TypeRef<'a>: core::fmt::Debug { + /// Creates a reference type from a binary slice, when using it with [`GenericOrderWal`](crate::swmr::GenericOrderWal), /// you can assume that the slice is the same as the one returned by [`encode`](Type::encode). - fn from_slice(src: &'a [u8]) -> Self; + /// + /// ## Safety + /// - the `src` must the same as the one returned by [`encode`](Type::encode). + unsafe fn from_slice(src: &'a [u8]) -> Self; } -/// The key reference trait for comparing `K` in the [`GenericOrderWal`]. +/// The key reference trait for comparing `K` in the [`GenericOrderWal`](crate::swmr::GenericOrderWal). pub trait KeyRef<'a, K>: Ord + Comparable { /// Compares with a type `Q` which can be borrowed from [`T::Ref`](Type::Ref). fn compare(&self, a: &Q) -> cmp::Ordering diff --git a/src/swmr/generic/traits/impls.rs b/src/wal/type/impls.rs similarity index 75% rename from src/swmr/generic/traits/impls.rs rename to src/wal/type/impls.rs index fe5f0013..37813140 100644 --- a/src/swmr/generic/traits/impls.rs +++ b/src/wal/type/impls.rs @@ -21,7 +21,7 @@ impl Type for () { } impl TypeRef<'_> for () { - fn from_slice(_buf: &[u8]) -> Self {} + unsafe fn from_slice(_buf: &[u8]) -> Self {} } impl Type for [u8; N] { @@ -41,7 +41,7 @@ impl Type for [u8; N] { impl TypeRef<'_> for [u8; N] { #[inline] - fn from_slice(src: &'_ [u8]) -> Self { + unsafe fn from_slice(src: &'_ [u8]) -> Self { let mut this = [0; N]; this.copy_from_slice(src); this @@ -70,7 +70,7 @@ macro_rules! impl_numbers { impl TypeRef<'_> for $ty { #[inline] - fn from_slice(buf: &[u8]) -> Self { + unsafe fn from_slice(buf: &[u8]) -> Self { const SIZE: usize = core::mem::size_of::<$ty>(); $ty::from_le_bytes(buf[..SIZE].try_into().unwrap()) @@ -121,7 +121,7 @@ impl Type for f32 { impl TypeRef<'_> for f32 { #[inline] - fn from_slice(buf: &[u8]) -> Self { + unsafe fn from_slice(buf: &[u8]) -> Self { const SIZE: usize = core::mem::size_of::(); f32::from_le_bytes(buf[..SIZE].try_into().unwrap()) @@ -148,9 +148,57 @@ impl Type for f64 { impl TypeRef<'_> for f64 { #[inline] - fn from_slice(buf: &[u8]) -> Self { + unsafe fn from_slice(buf: &[u8]) -> Self { const SIZE: usize = core::mem::size_of::(); f64::from_le_bytes(buf[..SIZE].try_into().unwrap()) } } + +impl Type for bool { + type Ref<'a> = Self; + + type Error = (); + + #[inline] + fn encoded_len(&self) -> usize { + 1 + } + + #[inline] + fn encode(&self, buf: &mut [u8]) -> Result<(), Self::Error> { + buf[0] = *self as u8; + Ok(()) + } +} + +impl TypeRef<'_> for bool { + #[inline] + unsafe fn from_slice(buf: &[u8]) -> Self { + buf[0] != 0 + } +} + +impl Type for char { + type Ref<'a> = Self; + + type Error = (); + + #[inline] + fn encoded_len(&self) -> usize { + self.len_utf8() + } + + #[inline] + fn encode(&self, buf: &mut [u8]) -> Result<(), Self::Error> { + self.encode_utf8(buf); + Ok(()) + } +} + +impl TypeRef<'_> for char { + #[inline] + unsafe fn from_slice(buf: &[u8]) -> Self { + core::str::from_utf8_unchecked(buf).chars().next().unwrap() + } +} diff --git a/src/swmr/generic/traits/impls/bytes.rs b/src/wal/type/impls/bytes.rs similarity index 86% rename from src/swmr/generic/traits/impls/bytes.rs rename to src/wal/type/impls/bytes.rs index 779c3e2e..4461ed08 100644 --- a/src/swmr/generic/traits/impls/bytes.rs +++ b/src/wal/type/impls/bytes.rs @@ -69,7 +69,7 @@ macro_rules! impls { } impl<'a> TypeRef<'a> for &'a [u8] { - fn from_slice(src: &'a [u8]) -> Self { + unsafe fn from_slice(src: &'a [u8]) -> Self { src } } @@ -97,7 +97,7 @@ impl<'a> From> for &'a [u8] { } impl<'a> TypeRef<'a> for SliceRef<'a> { - fn from_slice(src: &'a [u8]) -> Self { + unsafe fn from_slice(src: &'a [u8]) -> Self { Self(src) } } @@ -171,6 +171,24 @@ impls! { Arc<[u8]>, #[cfg(feature = "bytes")] ::bytes::Bytes, + #[cfg(feature = "smallvec-wrapper")] + ::smallvec_wrapper::OneOrMore, + #[cfg(feature = "smallvec-wrapper")] + ::smallvec_wrapper::TinyVec, + #[cfg(feature = "smallvec-wrapper")] + ::smallvec_wrapper::TriVec, + #[cfg(feature = "smallvec-wrapper")] + ::smallvec_wrapper::SmallVec, + #[cfg(feature = "smallvec-wrapper")] + ::smallvec_wrapper::MediumVec, + #[cfg(feature = "smallvec-wrapper")] + ::smallvec_wrapper::LargeVec, + #[cfg(feature = "smallvec-wrapper")] + ::smallvec_wrapper::XLargeVec, + #[cfg(feature = "smallvec-wrapper")] + ::smallvec_wrapper::XXLargeVec, + #[cfg(feature = "smallvec-wrapper")] + ::smallvec_wrapper::XXXLargeVec, } #[cfg(feature = "smallvec")] diff --git a/src/swmr/generic/traits/impls/net.rs b/src/wal/type/impls/net.rs similarity index 81% rename from src/swmr/generic/traits/impls/net.rs rename to src/wal/type/impls/net.rs index 30359db0..95c07286 100644 --- a/src/swmr/generic/traits/impls/net.rs +++ b/src/wal/type/impls/net.rs @@ -22,7 +22,7 @@ impl Type for Ipv4Addr { impl TypeRef<'_> for Ipv4Addr { #[inline] - fn from_slice(buf: &[u8]) -> Self { + unsafe fn from_slice(buf: &[u8]) -> Self { let octets = <[u8; 4]>::from_slice(&buf[..4]); Ipv4Addr::from(octets) } @@ -37,9 +37,11 @@ impl KeyRef<'_, Ipv4Addr> for Ipv4Addr { } fn compare_binary(a: &[u8], b: &[u8]) -> cmp::Ordering { - let a = ::from_slice(a); - let b = ::from_slice(b); - a.cmp(&b) + unsafe { + let a = ::from_slice(a); + let b = ::from_slice(b); + a.cmp(&b) + } } } @@ -62,7 +64,7 @@ impl Type for Ipv6Addr { impl TypeRef<'_> for Ipv6Addr { #[inline] - fn from_slice(buf: &[u8]) -> Self { + unsafe fn from_slice(buf: &[u8]) -> Self { let octets = <[u8; 16]>::from_slice(&buf[..16]); Ipv6Addr::from(octets) } @@ -77,9 +79,11 @@ impl KeyRef<'_, Ipv6Addr> for Ipv6Addr { } fn compare_binary(a: &[u8], b: &[u8]) -> cmp::Ordering { - let a = ::from_slice(a); - let b = ::from_slice(b); - a.cmp(&b) + unsafe { + let a = ::from_slice(a); + let b = ::from_slice(b); + a.cmp(&b) + } } } @@ -103,7 +107,7 @@ impl Type for SocketAddrV4 { impl TypeRef<'_> for SocketAddrV4 { #[inline] - fn from_slice(buf: &[u8]) -> Self { + unsafe fn from_slice(buf: &[u8]) -> Self { let octets = <[u8; 4]>::from_slice(&buf[..4]); let port = u16::from_le_bytes(buf[4..6].try_into().unwrap()); SocketAddrV4::new(Ipv4Addr::from(octets), port) @@ -119,9 +123,11 @@ impl KeyRef<'_, SocketAddrV4> for SocketAddrV4 { } fn compare_binary(a: &[u8], b: &[u8]) -> cmp::Ordering { - let a = ::from_slice(a); - let b = ::from_slice(b); - a.cmp(&b) + unsafe { + let a = ::from_slice(a); + let b = ::from_slice(b); + a.cmp(&b) + } } } @@ -145,7 +151,7 @@ impl Type for SocketAddrV6 { impl TypeRef<'_> for SocketAddrV6 { #[inline] - fn from_slice(buf: &[u8]) -> Self { + unsafe fn from_slice(buf: &[u8]) -> Self { let octets = <[u8; 16]>::from_slice(&buf[..16]); let port = u16::from_le_bytes(buf[16..18].try_into().unwrap()); SocketAddrV6::new(Ipv6Addr::from(octets), port, 0, 0) @@ -161,8 +167,10 @@ impl KeyRef<'_, SocketAddrV6> for SocketAddrV6 { } fn compare_binary(a: &[u8], b: &[u8]) -> cmp::Ordering { - let a = ::from_slice(a); - let b = ::from_slice(b); - a.cmp(&b) + unsafe { + let a = ::from_slice(a); + let b = ::from_slice(b); + a.cmp(&b) + } } } diff --git a/src/swmr/generic/traits/impls/string.rs b/src/wal/type/impls/string.rs similarity index 97% rename from src/swmr/generic/traits/impls/string.rs rename to src/wal/type/impls/string.rs index 1d43ea80..7ab8c4ce 100644 --- a/src/swmr/generic/traits/impls/string.rs +++ b/src/wal/type/impls/string.rs @@ -70,7 +70,7 @@ macro_rules! impls { } impl<'a> TypeRef<'a> for &'a str { - fn from_slice(src: &'a [u8]) -> Self { + unsafe fn from_slice(src: &'a [u8]) -> Self { core::str::from_utf8(src).unwrap() } } @@ -92,7 +92,7 @@ impl<'a> From> for &'a str { } impl<'a> TypeRef<'a> for Str<'a> { - fn from_slice(src: &'a [u8]) -> Self { + unsafe fn from_slice(src: &'a [u8]) -> Self { Self(core::str::from_utf8(src).unwrap()) } }