diff --git a/.codecov.yml b/.codecov.yml index 750acffe..78ea96ba 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -5,9 +5,10 @@ ignore: - "**/integration/" - "**/examples/" - "**/benches/" + - "src/tests.rs" + - "src/error.rs" - "src/swmr/generic/traits/impls/" - "src/swmr/generic/traits/impls.rs" - - "src/tests.rs" - "src/swmr/generic/tests.rs" - "src/swmr/generic/tests/" - "src/swmr/wal/tests.rs" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 603a3fd2..8059f4ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -180,17 +180,22 @@ jobs: with: path: ~/.cargo key: ${{ runner.os }}-coverage-dotcargo - - name: Run test - run: cargo test --all-features - + - name: Run test (Unix) + run: RUSTFLAGS="--cfg all_tests" cargo test --all-features + if: matrix.os != 'windows-latest' + - name: Run test (Windows) + shell: pwsh + run: | + $env:RUSTFLAGS="--cfg all_tests" + cargo test --all-features + if: matrix.os == 'windows-latest' + sanitizer: name: sanitizer strategy: matrix: os: - ubuntu-latest - # - macos-latest - # - windows-latest runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 @@ -217,58 +222,44 @@ jobs: run: ci/sanitizer_generic.sh if: matrix.os != 'ubuntu-latest' - # miri-tb-unsync: - # name: miri-tb-unsync - # strategy: - # matrix: - # os: - # - ubuntu-latest - # # - macos-latest - # # - windows-latest - # runs-on: ${{ matrix.os }} - # steps: - # - uses: actions/checkout@v3 - # - name: Cache cargo build and registry - # uses: actions/cache@v3 - # with: - # path: | - # ~/.cargo/registry - # ~/.cargo/git - # target - # key: ${{ runner.os }}-miri-${{ hashFiles('**/Cargo.lock') }} - # restore-keys: | - # ${{ runner.os }}-miri- - # - name: Install cargo-hack - # run: cargo install cargo-hack - # - name: Miri (Linux) - # run: ci/miri_tb_unsync.sh - # if: matrix.os == 'ubuntu-latest' - miri-tb: - name: miri-tb-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.features }} + name: miri-tb-${{ matrix.target }}-${{ matrix.cfg }} strategy: matrix: os: - ubuntu-latest # - macos-latest - # - windows-latest target: - x86_64-unknown-linux-gnu - i686-unknown-linux-gnu - powerpc64-unknown-linux-gnu - features: - - test-unsync-insert - - test-unsync-iters - - test-unsync-get - - test-unsync-constructor - - test-swmr-insert - - test-swmr-iters - - test-swmr-get - - test-swmr-constructor - - test-swmr-generic-insert - - test-swmr-generic-iters - - test-swmr-generic-get - - test-swmr-generic-constructor + - x86_64-apple-darwin + - aarch64-apple-darwin + cfg: + - unsync_insert + - unsync_iters + - unsync_get + - unsync_constructor + - swmr_insert + - swmr_iters + - swmr_get + - swmr_constructor + - swmr_generic_insert + - swmr_generic_iters + - swmr_generic_get + - swmr_generic_constructor + # Exclude invalid combinations + exclude: + - os: ubuntu-latest + target: x86_64-apple-darwin + - os: ubuntu-latest + target: aarch64-apple-darwin + # - os: macos-latest + # target: x86_64-unknown-linux-gnu + # - os: macos-latest + # target: i686-unknown-linux-gnu + # - os: macos-latest + # target: powerpc64-unknown-linux-gnu runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 @@ -284,19 +275,48 @@ jobs: ${{ runner.os }}-miri- - name: Install cargo-hack run: cargo install cargo-hack - - name: Miri (Linux) + - name: Miri run: | - bash ci/miri_tb.sh ${{ matrix.target }} ${{ matrix.features }} - if: matrix.os == 'ubuntu-latest' + bash ci/miri_tb.sh ${{ matrix.target }} ${{ matrix.cfg }} # miri-sb: - # name: miri-sb + # name: miri-sb-${{ matrix.target }}-${{ matrix.cfg }} # strategy: # matrix: # os: # - ubuntu-latest # - macos-latest - # - windows-latest + # target: + # - x86_64-unknown-linux-gnu + # - i686-unknown-linux-gnu + # - powerpc64-unknown-linux-gnu + # - x86_64-apple-darwin + # - aarch64-apple-darwin + # cfg: + # - unsync_insert + # - unsync_iters + # - unsync_get + # - unsync_constructor + # - swmr_insert + # - swmr_iters + # - swmr_get + # - swmr_constructor + # - swmr_generic_insert + # - swmr_generic_iters + # - swmr_generic_get + # - swmr_generic_constructor + # # Exclude invalid combinations + # exclude: + # - os: ubuntu-latest + # target: x86_64-apple-darwin + # - os: ubuntu-latest + # target: aarch64-apple-darwin + # - os: macos-latest + # target: x86_64-unknown-linux-gnu + # - os: macos-latest + # target: i686-unknown-linux-gnu + # - os: macos-latest + # target: powerpc64-unknown-linux-gnu # runs-on: ${{ matrix.os }} # steps: # - uses: actions/checkout@v3 @@ -312,12 +332,9 @@ jobs: # ${{ runner.os }}-miri- # - name: Install cargo-hack # run: cargo install cargo-hack - # - name: Miri (Linux) - # run: ci/miri_sb.sh - # if: matrix.os == 'ubuntu-latest' # - name: Miri - # run: ci/miri_sb_generic.sh - # if: matrix.os != 'ubuntu-latest' + # run: | + # bash ci/miri_sb.sh ${{ matrix.target }} ${{ matrix.cfg }} # valgrind valgrind: @@ -388,6 +405,8 @@ jobs: key: ${{ runner.os }}-coverage-cargo-build-target - name: Run tarpaulin uses: actions-rs/cargo@v1 + env: + RUSTFLAGS: "--cfg all_tests" with: command: tarpaulin args: --all-features --run-types tests --run-types doctests --workspace --out xml diff --git a/Cargo.toml b/Cargo.toml index ab8a3ecc..3ecdcda3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,23 +23,6 @@ std = ["rarena-allocator/default", "crossbeam-skiplist/default", "bitflags/defau xxhash3 = ["dbutils/xxhash3", "std"] xxhash64 = ["dbutils/xxhash64", "std"] -## only for miri testing -test-unsync-insert = ["std"] -test-unsync-iters = ["std"] -test-unsync-get = ["std"] -test-unsync-constructor = ["std"] - -test-swmr-insert = ["std"] -test-swmr-iters = ["std"] -test-swmr-get = ["std"] -test-swmr-constructor = ["std"] - -test-swmr-generic-insert = ["std"] -test-swmr-generic-iters = ["std"] -test-swmr-generic-get = ["std"] -test-swmr-generic-constructor = ["std"] - - [dependencies] among = { version = "0.1", default-features = false, features = ["either"] } bitflags = { version = "1", default-features = false } @@ -76,3 +59,22 @@ rpath = false [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints.rust] +rust_2018_idioms = "warn" +single_use_lifetimes = "warn" +unexpected_cfgs = { level = "warn", check-cfg = [ + 'cfg(all_tests)', + 'cfg(test_unsync_constructor)', + 'cfg(test_unsync_insert)', + 'cfg(test_unsync_iters)', + 'cfg(test_unsync_get)', + 'cfg(test_swmr_constructor)', + 'cfg(test_swmr_insert)', + 'cfg(test_swmr_iters)', + 'cfg(test_swmr_get)', + 'cfg(test_swmr_generic_constructor)', + 'cfg(test_swmr_generic_insert)', + 'cfg(test_swmr_generic_iters)', + 'cfg(test_swmr_generic_get)', +] } diff --git a/ci/miri_sb.sh b/ci/miri_sb.sh index b8908131..baded7d0 100755 --- a/ci/miri_sb.sh +++ b/ci/miri_sb.sh @@ -1,13 +1,25 @@ #!/bin/bash set -e +# Check if TARGET and CONFIG_FLAGS are provided, otherwise panic +if [ -z "$1" ]; then + echo "Error: TARGET is not provided" + exit 1 +fi + +if [ -z "$2" ]; then + echo "Error: CONFIG_FLAGS are not provided" + exit 1 +fi + +TARGET=$1 +CONFIG_FLAGS=$2 + rustup toolchain install nightly --component miri rustup override set nightly cargo miri setup export MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-symbolic-alignment-check" +export RUSTFLAGS="--cfg test_$CONFIG_FLAGS" -cargo miri test --tests --target x86_64-unknown-linux-gnu --all-features -# cargo miri test --tests --target aarch64-unknown-linux-gnu #crossbeam_utils has problem on this platform -cargo miri test --tests --target i686-unknown-linux-gnu --all-features -cargo miri test --tests --target powerpc64-unknown-linux-gnu --all-features +cargo miri test --tests --target $TARGET --lib diff --git a/ci/miri_sb_generic.sh b/ci/miri_sb_generic.sh deleted file mode 100755 index 4b8f31ec..00000000 --- a/ci/miri_sb_generic.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e - -rustup toolchain install nightly --component miri -rustup override set nightly -cargo miri setup - -export MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-symbolic-alignment-check" - -cargo miri test --tests --all-features diff --git a/ci/miri_tb.sh b/ci/miri_tb.sh index 85cc5984..93fa21a0 100755 --- a/ci/miri_tb.sh +++ b/ci/miri_tb.sh @@ -1,19 +1,19 @@ #!/bin/bash set -e -# Check if TARGET and FEATURES are provided, otherwise panic +# Check if TARGET and CONFIG_FLAGS are provided, otherwise panic if [ -z "$1" ]; then echo "Error: TARGET is not provided" exit 1 fi if [ -z "$2" ]; then - echo "Error: FEATURES are not provided" + echo "Error: CONFIG_FLAGS are not provided" exit 1 fi TARGET=$1 -FEATURES=$2 +CONFIG_FLAGS=$2 rustup toolchain install nightly --component miri rustup override set nightly @@ -21,5 +21,7 @@ cargo miri setup export MIRIFLAGS="-Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-tree-borrows -Zmiri-ignore-leaks" -cargo miri test --tests --target $TARGET --features $FEATURES --lib +export RUSTFLAGS="--cfg test_$CONFIG_FLAGS" + +cargo miri test --tests --target $TARGET --lib diff --git a/ci/sanitizer.sh b/ci/sanitizer.sh index 2b196c4c..72cf1792 100755 --- a/ci/sanitizer.sh +++ b/ci/sanitizer.sh @@ -5,15 +5,15 @@ set -ex export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0" # Run address sanitizer -RUSTFLAGS="-Z sanitizer=address" \ +RUSTFLAGS="-Z sanitizer=address --cfg all_tests" \ cargo test -Z build-std --all --release --tests --target x86_64-unknown-linux-gnu --all-features --exclude benchmarks -- --test-threads=1 # Run memory sanitizer -RUSTFLAGS="-Z sanitizer=memory" \ +RUSTFLAGS="-Z sanitizer=memory --cfg all_tests" \ cargo test -Z build-std --all --release --tests --target x86_64-unknown-linux-gnu --all-features --exclude benchmarks -- --test-threads=1 # Run thread sanitizer cargo clean TSAN_OPTIONS="suppressions=$(pwd)/ci/tsan" \ -RUSTFLAGS="${RUSTFLAGS:-} -Z sanitizer=thread" \ +RUSTFLAGS="${RUSTFLAGS:-} -Z sanitizer=thread --cfg all_tests" \ cargo test -Z build-std --all --release --target x86_64-unknown-linux-gnu --all-features --tests --exclude benchmarks -- --test-threads=1 \ No newline at end of file diff --git a/ci/sanitizer_generic.sh b/ci/sanitizer_generic.sh deleted file mode 100755 index 728321ee..00000000 --- a/ci/sanitizer_generic.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -set -ex - -export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0" - -# Run address sanitizer -RUSTFLAGS="-Z sanitizer=address" \ -cargo test --tests --all-features - -# Run leak sanitizer -RUSTFLAGS="-Z sanitizer=leak" \ -cargo test --tests --all-features - diff --git a/examples/zero_cost.rs b/examples/zero_cost.rs index 7ffd06a1..e1dd6183 100644 --- a/examples/zero_cost.rs +++ b/examples/zero_cost.rs @@ -23,21 +23,21 @@ struct PersonRef<'a> { name: &'a str, } -impl<'a> PartialEq for PersonRef<'a> { +impl PartialEq for PersonRef<'_> { fn eq(&self, other: &Self) -> bool { self.id == other.id && self.name == other.name } } -impl<'a> Eq for PersonRef<'a> {} +impl Eq for PersonRef<'_> {} -impl<'a> PartialOrd for PersonRef<'a> { +impl PartialOrd for PersonRef<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl<'a> Ord for PersonRef<'a> { +impl Ord for PersonRef<'_> { fn cmp(&self, other: &Self) -> cmp::Ordering { self .id diff --git a/src/error.rs b/src/error.rs index be2bb365..1308a198 100644 --- a/src/error.rs +++ b/src/error.rs @@ -74,6 +74,17 @@ impl Error { } } + #[inline] + pub(crate) const fn from_insufficient_space(error: rarena_allocator::Error) -> Self { + match error { + rarena_allocator::Error::InsufficientSpace { + requested, + available, + } => Self::insufficient_space(requested, available), + _ => unreachable!(), + } + } + /// Create a new corrupted error. #[inline] pub(crate) fn corrupted() -> Error { diff --git a/src/lib.rs b/src/lib.rs index e04cbba2..896bbfd3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,24 @@ const MAGIC_TEXT_SIZE: usize = MAGIC_TEXT.len(); const MAGIC_VERSION_SIZE: usize = mem::size_of::(); const HEADER_SIZE: usize = MAGIC_TEXT_SIZE + MAGIC_VERSION_SIZE; -#[cfg(test)] +#[cfg(all( + test, + any( + all_tests, + test_unsync_constructor, + test_unsync_insert, + test_unsync_get, + test_unsync_iters, + test_swmr_constructor, + test_swmr_insert, + test_swmr_get, + test_swmr_iters, + test_swmr_generic_constructor, + test_swmr_generic_insert, + test_swmr_generic_get, + test_swmr_generic_iters, + ) +))] #[macro_use] mod tests; diff --git a/src/swmr.rs b/src/swmr.rs index 84a65d38..763bab86 100644 --- a/src/swmr.rs +++ b/src/swmr.rs @@ -1,4 +1,5 @@ -mod wal; +/// The ordered write-ahead log only supports bytes. +pub mod wal; pub use wal::OrderWal; /// The generic implementation of the ordered write-ahead log. diff --git a/src/swmr/generic.rs b/src/swmr/generic.rs index 4d97a137..f8c01985 100644 --- a/src/swmr/generic.rs +++ b/src/swmr/generic.rs @@ -9,8 +9,7 @@ use crossbeam_skiplist::SkipSet; pub use dbutils::equivalent::*; use dbutils::{Checksumer, Crc32}; use rarena_allocator::{ - either::Either, sync::Arena, Allocator, ArenaPosition, Error as ArenaError, Memory, MmapOptions, - OpenOptions, + either::Either, sync::Arena, Allocator, ArenaPosition, Memory, MmapOptions, OpenOptions, }; use crate::{ @@ -32,10 +31,20 @@ pub use reader::*; mod iter; pub use iter::*; -#[cfg(test)] +#[cfg(all( + test, + any( + all_tests, + test_swmr_generic_constructor, + test_swmr_generic_insert, + test_swmr_generic_get, + test_swmr_generic_iters, + ) +))] mod tests; #[doc(hidden)] +#[derive(Debug)] pub struct Pointer { /// The pointer to the start of the entry. ptr: *const u8, @@ -300,7 +309,7 @@ impl GenericOrderWalCore { } #[inline] - fn first(&self) -> Option> + fn first(&self) -> Option> where K: Type + Ord, for<'b> K::Ref<'b>: KeyRef<'b, K>, @@ -309,7 +318,7 @@ impl GenericOrderWalCore { } #[inline] - fn last(&self) -> Option> + fn last(&self) -> Option> where K: Type + Ord, for<'b> K::Ref<'b>: KeyRef<'b, K>, @@ -318,7 +327,7 @@ impl GenericOrderWalCore { } #[inline] - fn iter(&self) -> Iter + fn iter(&self) -> Iter<'_, K, V> where K: Type + Ord, for<'b> K::Ref<'b>: KeyRef<'b, K>, @@ -475,19 +484,19 @@ where V: Type, { #[inline] - fn contains_key<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> bool + fn contains_key<'a, Q>(&'a self, key: &'a Q) -> bool where Q: ?Sized + Ord + Comparable> + Comparable, { - self.map.contains::>(&Owned::new(key)) + self.map.contains::>(&Owned::new(key)) } #[inline] - fn contains_key_by_ref<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> bool + fn contains_key_by_ref<'a, Q>(&'a self, key: &'a Q) -> bool where Q: ?Sized + Ord + Comparable>, { - self.map.contains::>(&Ref::new(key)) + self.map.contains::>(&Ref::new(key)) } #[inline] @@ -498,26 +507,29 @@ where } #[inline] - fn get<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> Option> + fn get<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable> + Comparable, { self .map - .get::>(&Owned::new(key)) + .get::>(&Owned::new(key)) .map(EntryRef::new) } #[inline] - fn get_by_ref<'a, 'b: 'a, Q>(&'a self, key: &'b 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) + self + .map + .get::>(&Ref::new(key)) + .map(EntryRef::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())) @@ -574,19 +586,19 @@ 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() } /// Returns an iterator over the entries in the WAL. #[inline] - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter<'_, K, V> { self.core.iter() } @@ -810,15 +822,8 @@ impl GenericOrderWal { /// 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( - |e| match e { - ArenaError::InsufficientSpace { - requested, - available, - } => Error::insufficient_space(requested, available), - _ => unreachable!(), - }, - )?; + 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)) @@ -1058,7 +1063,7 @@ where { /// Returns `true` if the key exists in the WAL. #[inline] - pub fn contains_key<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> bool + pub fn contains_key<'a, Q>(&'a self, key: &'a Q) -> bool where Q: ?Sized + Ord + Comparable> + Comparable, { @@ -1076,7 +1081,7 @@ where /// Returns `true` if the key exists in the WAL. #[inline] - pub fn contains_key_by_ref<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> bool + pub fn contains_key_by_ref<'a, Q>(&'a self, key: &'a Q) -> bool where Q: ?Sized + Ord + Comparable>, { @@ -1085,7 +1090,7 @@ where /// Gets the value associated with the key. #[inline] - pub fn get<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> Option> + pub fn get<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable> + Comparable, { @@ -1094,7 +1099,7 @@ where /// Gets the value associated with the key. #[inline] - pub fn get_by_ref<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> Option> + pub fn get_by_ref<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable>, { @@ -1106,7 +1111,7 @@ where /// # 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) } } @@ -1465,10 +1470,6 @@ where key: Among, val: Among, ) -> Result<(), Among> { - if self.ro { - return Err(Among::Right(Error::read_only())); - } - let klen = key.encoded_len(); let vlen = val.encoded_len(); @@ -1479,17 +1480,7 @@ where let buf = self.core.arena.alloc_bytes(elen); match buf { - Err(e) => { - let e = match e { - ArenaError::InsufficientSpace { - requested, - available, - } => error::Error::insufficient_space(requested, available), - ArenaError::ReadOnly => error::Error::read_only(), - _ => unreachable!(), - }; - Err(Among::Right(e)) - } + 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. @@ -1548,6 +1539,7 @@ where vlen, self.opts.maximum_key_size(), self.opts.maximum_value_size(), + self.ro, ) } } diff --git a/src/swmr/generic/entry.rs b/src/swmr/generic/entry.rs index 101da391..c40d638e 100644 --- a/src/swmr/generic/entry.rs +++ b/src/swmr/generic/entry.rs @@ -22,7 +22,7 @@ where } } -impl<'a, K, V> Clone for EntryRef<'a, K, V> { +impl Clone for EntryRef<'_, K, V> { #[inline] fn clone(&self) -> Self { Self { diff --git a/src/swmr/generic/iter.rs b/src/swmr/generic/iter.rs index f0f80d7b..90938702 100644 --- a/src/swmr/generic/iter.rs +++ b/src/swmr/generic/iter.rs @@ -47,7 +47,7 @@ where } } -impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> +impl DoubleEndedIterator for Iter<'_, K, V> where K: Type + Ord, for<'b> K::Ref<'b>: KeyRef<'b, K>, diff --git a/src/swmr/generic/reader.rs b/src/swmr/generic/reader.rs index 7db0266a..36f4152a 100644 --- a/src/swmr/generic/reader.rs +++ b/src/swmr/generic/reader.rs @@ -54,19 +54,19 @@ 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.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() } /// Returns an iterator over the entries in the WAL. #[inline] - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter<'_, K, V> { self.0.iter() } @@ -105,7 +105,7 @@ where { /// Returns `true` if the key exists in the WAL. #[inline] - pub fn contains_key<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> bool + pub fn contains_key<'a, Q>(&'a self, key: &'a Q) -> bool where Q: ?Sized + Ord + Comparable> + Comparable, { @@ -114,7 +114,7 @@ where /// Returns `true` if the key exists in the WAL. #[inline] - pub fn contains_key_by_ref<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> bool + pub fn contains_key_by_ref<'a, Q>(&'a self, key: &'a Q) -> bool where Q: ?Sized + Ord + Comparable>, { @@ -132,7 +132,7 @@ where /// Gets the value associated with the key. #[inline] - pub fn get<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> Option> + pub fn get<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable> + Comparable, { @@ -141,7 +141,7 @@ where /// Gets the value associated with the key. #[inline] - pub fn get_by_ref<'a, 'b: 'a, Q>(&'a self, key: &'b Q) -> Option> + pub fn get_by_ref<'a, Q>(&'a self, key: &'a Q) -> Option> where Q: ?Sized + Ord + Comparable>, { @@ -153,7 +153,7 @@ where /// # 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 4836cb4d..82d640f0 100644 --- a/src/swmr/generic/tests.rs +++ b/src/swmr/generic/tests.rs @@ -8,15 +8,18 @@ use super::*; const MB: u32 = 1024 * 1024; -#[cfg(all(test, feature = "test-swmr-generic-constructor"))] +#[cfg(all(test, any(test_swmr_generic_constructor, all_tests)))] mod constructor; -#[cfg(all(test, feature = "test-swmr-generic-get"))] -mod get; -#[cfg(all(test, feature = "test-swmr-generic-insert"))] + +#[cfg(all(test, any(test_swmr_generic_insert, all_tests)))] mod insert; -#[cfg(all(test, feature = "test-swmr-generic-iters"))] + +#[cfg(all(test, any(test_swmr_generic_iters, all_tests)))] 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, @@ -51,7 +54,7 @@ struct PersonRef<'a> { name: &'a str, } -impl<'a> PersonRef<'a> { +impl PersonRef<'_> { fn encoded_len(&self) -> usize { encoded_u64_varint_len(self.id) + self.name.len() } @@ -69,21 +72,21 @@ impl<'a> PersonRef<'a> { } } -impl<'a> PartialEq for PersonRef<'a> { +impl PartialEq for PersonRef<'_> { fn eq(&self, other: &Self) -> bool { self.id == other.id && self.name == other.name } } -impl<'a> Eq for PersonRef<'a> {} +impl Eq for PersonRef<'_> {} -impl<'a> PartialOrd for PersonRef<'a> { +impl PartialOrd for PersonRef<'_> { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl<'a> Ord for PersonRef<'a> { +impl Ord for PersonRef<'_> { fn cmp(&self, other: &Self) -> cmp::Ordering { self .id diff --git a/src/swmr/generic/traits/impls/bytes.rs b/src/swmr/generic/traits/impls/bytes.rs index 34c752fa..779c3e2e 100644 --- a/src/swmr/generic/traits/impls/bytes.rs +++ b/src/swmr/generic/traits/impls/bytes.rs @@ -78,7 +78,7 @@ impl<'a> TypeRef<'a> for &'a [u8] { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SliceRef<'a>(&'a [u8]); -impl<'a> Borrow<[u8]> for SliceRef<'a> { +impl Borrow<[u8]> for SliceRef<'_> { fn borrow(&self) -> &[u8] { self.0 } diff --git a/src/swmr/wal.rs b/src/swmr/wal.rs index ded2db45..c6092321 100644 --- a/src/swmr/wal.rs +++ b/src/swmr/wal.rs @@ -9,7 +9,7 @@ use wal::{ }; use core::ptr::NonNull; -use rarena_allocator::{sync::Arena, Error as ArenaError}; +use rarena_allocator::sync::Arena; use std::sync::Arc; mod reader; @@ -18,9 +18,19 @@ pub use reader::*; mod iter; pub use iter::*; -#[cfg(test)] +#[cfg(all( + test, + any( + all_tests, + test_swmr_constructor, + test_swmr_insert, + test_swmr_get, + test_swmr_iters, + ) +))] mod tests; +#[doc(hidden)] pub struct OrderWalCore { arena: Arena, map: SkipSet>, @@ -31,7 +41,7 @@ pub struct OrderWalCore { impl OrderWalCore { #[inline] - fn iter(&self) -> Iter { + fn iter(&self) -> Iter<'_, C> { Iter::new(self.map.iter()) } } @@ -131,17 +141,7 @@ where let buf = self.core.arena.alloc_bytes(elen); match buf { - Err(e) => { - let e = match e { - ArenaError::InsufficientSpace { - requested, - available, - } => error::Error::insufficient_space(requested, available), - ArenaError::ReadOnly => error::Error::read_only(), - _ => unreachable!(), - }; - Err(Among::Right(e)) - } + 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. @@ -204,13 +204,18 @@ where } impl OrderWal { - // /// Returns the read-only view for the WAL. - // #[inline] - // pub fn reader(&self) -> OrderWalReader { - // OrderWalReader::new(self.core.clone()) - // } - /// Returns the path of the WAL if it is backed by a file. + /// + /// # Example + /// + /// ```rust + /// use orderwal::{swmr::OrderWal, Wal, Builder}; + /// + /// // A in-memory WAL + /// let wal = OrderWal::new(Builder::new().with_capacity(100)).unwrap(); + /// + /// assert!(wal.path_buf().is_none()); + /// ``` pub fn path_buf(&self) -> Option<&std::sync::Arc> { self.core.arena.path() } @@ -431,16 +436,13 @@ where C: Comparator + CheapClone, S: Checksumer, { - if self.read_only() { - return Err(Either::Right(Error::read_only())); - } - self .check( key.len(), vb.size() as usize, self.maximum_key_size(), self.maximum_value_size(), + self.read_only(), ) .map_err(Either::Right)?; diff --git a/src/swmr/wal/iter.rs b/src/swmr/wal/iter.rs index b69a583a..1b08166a 100644 --- a/src/swmr/wal/iter.rs +++ b/src/swmr/wal/iter.rs @@ -29,7 +29,7 @@ impl<'a, C: Comparator> Iterator for Iter<'a, C> { } } -impl<'a, C: Comparator> DoubleEndedIterator for Iter<'a, C> { +impl DoubleEndedIterator for Iter<'_, C> { #[inline] fn next_back(&mut self) -> Option { self @@ -60,7 +60,7 @@ impl<'a, C: Comparator> Iterator for Keys<'a, C> { } } -impl<'a, C: Comparator> DoubleEndedIterator for Keys<'a, C> { +impl DoubleEndedIterator for Keys<'_, C> { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back().map(|ptr| ptr.as_key_slice()) @@ -88,7 +88,7 @@ impl<'a, C: Comparator> Iterator for Values<'a, C> { } } -impl<'a, C: Comparator> DoubleEndedIterator for Values<'a, C> { +impl DoubleEndedIterator for Values<'_, C> { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back().map(|ptr| ptr.as_value_slice()) @@ -137,7 +137,7 @@ where } } -impl<'a, Q, R, C> DoubleEndedIterator for Range<'a, Q, R, C> +impl DoubleEndedIterator for Range<'_, Q, R, C> where C: Comparator, R: RangeBounds, @@ -192,7 +192,7 @@ where } } -impl<'a, Q, R, C> DoubleEndedIterator for RangeKeys<'a, Q, R, C> +impl DoubleEndedIterator for RangeKeys<'_, Q, R, C> where C: Comparator, R: RangeBounds, @@ -244,7 +244,7 @@ where } } -impl<'a, Q, R, C> DoubleEndedIterator for RangeValues<'a, Q, R, C> +impl DoubleEndedIterator for RangeValues<'_, Q, R, C> where C: Comparator, R: RangeBounds, diff --git a/src/swmr/wal/tests.rs b/src/swmr/wal/tests.rs index ac8c4f63..ed30fd4a 100644 --- a/src/swmr/wal/tests.rs +++ b/src/swmr/wal/tests.rs @@ -4,16 +4,16 @@ use crate::tests::*; use super::*; -#[cfg(all(test, feature = "test-swmr-constructor"))] +#[cfg(all(test, any(test_swmr_constructor, all_tests)))] mod constructor; -#[cfg(all(test, feature = "test-swmr-insert"))] +#[cfg(all(test, any(test_swmr_insert, all_tests)))] mod insert; -#[cfg(all(test, feature = "test-swmr-iters"))] +#[cfg(all(test, any(test_swmr_iters, all_tests)))] mod iter; -#[cfg(all(test, feature = "test-swmr-get"))] +#[cfg(all(test, any(test_swmr_get, all_tests)))] mod get; const MB: u32 = 1024 * 1024; diff --git a/src/tests.rs b/src/tests.rs index aecb18de..8aa7e91a 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -11,12 +11,12 @@ macro_rules! common_unittests { paste::paste! { #[test] fn test_insert_to_full_inmemory() { - insert_to_full(&mut OrderWal::new(Builder::new().with_capacity(MB)).unwrap()); + insert_to_full(&mut OrderWal::new(Builder::new().with_capacity(100)).unwrap()); } #[test] fn test_insert_to_full_map_anon() { - insert_to_full(&mut OrderWal::map_anon(Builder::new().with_capacity(MB)).unwrap()); + insert_to_full(&mut OrderWal::map_anon(Builder::new().with_capacity(100)).unwrap()); } #[test] @@ -28,7 +28,7 @@ macro_rules! common_unittests { dir.path().join(concat!("test_", stringify!($prefix), "_insert_to_full_map_file")), Builder::new(), OpenOptions::new() - .create_new(Some(MB)) + .create_new(Some(100)) .write(true) .read(true), ) diff --git a/src/unsync.rs b/src/unsync.rs index 0a346c12..aa372fc0 100644 --- a/src/unsync.rs +++ b/src/unsync.rs @@ -1,17 +1,17 @@ +use core::{cell::UnsafeCell, ops::RangeBounds, ptr::NonNull}; +use std::{collections::BTreeSet, rc::Rc}; + use super::*; use among::Among; use either::Either; use error::Error; +use rarena_allocator::unsync::Arena; use wal::{ sealed::{Constructor, Sealed}, ImmutableWal, }; -use core::{cell::UnsafeCell, ops::RangeBounds, ptr::NonNull}; -use rarena_allocator::{unsync::Arena, Error as ArenaError}; -use std::{collections::BTreeSet, rc::Rc}; - /// Iterators for the `OrderWal`. pub mod iter; use iter::*; @@ -19,7 +19,16 @@ use iter::*; mod c; use c::*; -#[cfg(test)] +#[cfg(all( + test, + any( + all_tests, + test_unsync_constructor, + test_unsync_insert, + test_unsync_get, + test_unsync_iters, + ) +))] mod tests; /// An ordered write-ahead log implementation for single thread environments. @@ -65,6 +74,17 @@ where impl OrderWal { /// Returns the path of the WAL if it is backed by a file. + /// + /// # Example + /// + /// ```rust + /// use orderwal::{unsync::OrderWal, Wal, Builder}; + /// + /// // A in-memory WAL + /// let wal = OrderWal::new(Builder::new().with_capacity(100)).unwrap(); + /// + /// assert!(wal.path_buf().is_none()); + /// ``` pub fn path_buf(&self) -> Option<&std::rc::Rc> { self.core().arena.path() } @@ -102,17 +122,7 @@ where let buf = core.arena.alloc_bytes(elen); match buf { - Err(e) => { - let e = match e { - ArenaError::InsufficientSpace { - requested, - available, - } => error::Error::insufficient_space(requested, available), - ArenaError::ReadOnly => error::Error::read_only(), - _ => unreachable!(), - }; - Err(Among::Right(e)) - } + 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. @@ -401,16 +411,13 @@ where C: Comparator + CheapClone, S: Checksumer, { - if self.read_only() { - return Err(Either::Right(Error::read_only())); - } - self .check( key.len(), vb.size() as usize, self.maximum_key_size(), self.maximum_value_size(), + self.read_only(), ) .map_err(Either::Right)?; diff --git a/src/unsync/iter.rs b/src/unsync/iter.rs index 777f9b03..d4eda63c 100644 --- a/src/unsync/iter.rs +++ b/src/unsync/iter.rs @@ -28,7 +28,7 @@ impl<'a, C> Iterator for Iter<'a, C> { } } -impl<'a, C> DoubleEndedIterator for Iter<'a, C> { +impl DoubleEndedIterator for Iter<'_, C> { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back().map(|ptr| { @@ -39,7 +39,7 @@ impl<'a, C> DoubleEndedIterator for Iter<'a, C> { } } -impl<'a, C> FusedIterator for Iter<'a, C> {} +impl FusedIterator for Iter<'_, C> {} /// Iterator over the keys in the WAL. pub struct Keys<'a, C> { @@ -62,14 +62,14 @@ impl<'a, C> Iterator for Keys<'a, C> { } } -impl<'a, C> DoubleEndedIterator for Keys<'a, C> { +impl DoubleEndedIterator for Keys<'_, C> { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back().map(|ptr| ptr.as_key_slice()) } } -impl<'a, C> FusedIterator for Keys<'a, C> {} +impl FusedIterator for Keys<'_, C> {} /// Iterator over the values in the WAL. pub struct Values<'a, C> { @@ -92,14 +92,14 @@ impl<'a, C> Iterator for Values<'a, C> { } } -impl<'a, C> DoubleEndedIterator for Values<'a, C> { +impl DoubleEndedIterator for Values<'_, C> { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back().map(|ptr| ptr.as_value_slice()) } } -impl<'a, C> FusedIterator for Values<'a, C> {} +impl FusedIterator for Values<'_, C> {} /// An iterator over a subset of the entries in the WAL. pub struct Range<'a, C> @@ -134,7 +134,7 @@ where } } -impl<'a, C> DoubleEndedIterator for Range<'a, C> +impl DoubleEndedIterator for Range<'_, C> where C: Comparator, { @@ -147,7 +147,7 @@ where } } -impl<'a, C> FusedIterator for Range<'a, C> where C: Comparator {} +impl FusedIterator for Range<'_, C> where C: Comparator {} /// An iterator over the keys in a subset of the entries in the WAL. pub struct RangeKeys<'a, C> @@ -179,7 +179,7 @@ where } } -impl<'a, C> DoubleEndedIterator for RangeKeys<'a, C> +impl DoubleEndedIterator for RangeKeys<'_, C> where C: Comparator, { @@ -189,7 +189,7 @@ where } } -impl<'a, C> FusedIterator for RangeKeys<'a, C> where C: Comparator {} +impl FusedIterator for RangeKeys<'_, C> where C: Comparator {} /// An iterator over the values in a subset of the entries in the WAL. pub struct RangeValues<'a, C> @@ -221,7 +221,7 @@ where } } -impl<'a, C> DoubleEndedIterator for RangeValues<'a, C> +impl DoubleEndedIterator for RangeValues<'_, C> where C: Comparator, { @@ -230,3 +230,5 @@ where self.iter.next_back().map(|ptr| ptr.as_value_slice()) } } + +impl FusedIterator for RangeValues<'_, C> where C: Comparator {} diff --git a/src/unsync/tests.rs b/src/unsync/tests.rs index 96d3abab..5cd48486 100644 --- a/src/unsync/tests.rs +++ b/src/unsync/tests.rs @@ -4,16 +4,16 @@ use crate::tests::*; use super::*; -#[cfg(all(test, feature = "test-unsync-constructor"))] +#[cfg(all(test, any(test_unsync_constructor, all_tests)))] mod constructor; -#[cfg(all(test, feature = "test-unsync-insert"))] +#[cfg(all(test, any(test_unsync_insert, all_tests)))] mod insert; -#[cfg(all(test, feature = "test-unsync-iters"))] +#[cfg(all(test, any(test_unsync_iters, all_tests)))] mod iter; -#[cfg(all(test, feature = "test-unsync-get"))] +#[cfg(all(test, any(test_unsync_get, all_tests)))] mod get; const MB: u32 = 1024 * 1024; diff --git a/src/utils.rs b/src/utils.rs index 7aab6027..10f6f572 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -50,7 +50,12 @@ pub(crate) const fn check( vlen: usize, max_key_size: u32, max_value_size: u32, + ro: bool, ) -> Result<(), error::Error> { + if ro { + return Err(error::Error::read_only()); + } + let max_ksize = min_u64(max_key_size as u64, u32::MAX as u64); let max_vsize = min_u64(max_value_size as u64, u32::MAX as u64); diff --git a/src/wal.rs b/src/wal.rs index b966529e..0e112163 100644 --- a/src/wal.rs +++ b/src/wal.rs @@ -1,7 +1,5 @@ use core::ops::RangeBounds; -use rarena_allocator::Error as ArenaError; - use super::*; mod builder; @@ -162,13 +160,7 @@ pub trait Wal: sealed::Sealed + ImmutableWal { let arena = ::new( arena_options(opts.reserved()).with_capacity(opts.capacity()), ) - .map_err(|e| match e { - ArenaError::InsufficientSpace { - requested, - available, - } => Error::insufficient_space(requested, available), - _ => unreachable!(), - })?; + .map_err(Error::from_insufficient_space)?; >::new_in(arena, opts, cmp, cks) .map(|core| Self::from_core(core, false)) } @@ -374,16 +366,13 @@ pub trait Wal: sealed::Sealed + ImmutableWal { C: Comparator + CheapClone, S: Checksumer, { - if self.read_only() { - return Err(Either::Right(Error::read_only())); - } - self .check( kb.size() as usize, value.len(), self.maximum_key_size(), self.maximum_value_size(), + self.read_only(), ) .map_err(Either::Right)?; @@ -411,16 +400,13 @@ pub trait Wal: sealed::Sealed + ImmutableWal { C: Comparator + CheapClone, S: Checksumer, { - if self.read_only() { - return Err(Either::Right(Error::read_only())); - } - self .check( key.len(), vb.size() as usize, self.maximum_key_size(), self.maximum_value_size(), + self.read_only(), ) .map_err(Either::Right)?; @@ -446,16 +432,13 @@ pub trait Wal: sealed::Sealed + ImmutableWal { C: Comparator + CheapClone, S: Checksumer, { - if self.read_only() { - return Err(Among::Right(Error::read_only())); - } - self .check( kb.size() as usize, vb.size() as usize, self.maximum_key_size(), self.maximum_value_size(), + self.read_only(), ) .map_err(Among::Right)?; @@ -468,15 +451,12 @@ pub trait Wal: sealed::Sealed + ImmutableWal { C: Comparator + CheapClone, S: Checksumer, { - if self.read_only() { - return Err(Error::read_only()); - } - self.check( key.len(), value.len(), self.maximum_key_size(), self.maximum_value_size(), + self.read_only(), )?; self diff --git a/src/wal/sealed.rs b/src/wal/sealed.rs index 7ab2c619..ac0a7e48 100644 --- a/src/wal/sealed.rs +++ b/src/wal/sealed.rs @@ -16,14 +16,16 @@ pub trait WalCore { } pub trait Sealed: Constructor { + #[inline] fn check( &self, klen: usize, vlen: usize, max_key_size: u32, max_value_size: u32, + ro: bool, ) -> Result<(), Error> { - crate::check(klen, vlen, max_key_size, max_value_size) + crate::check(klen, vlen, max_key_size, max_value_size, ro) } fn insert_with_in(