From 485fab7b195910c35f69e3830b4cda1f33fadaf2 Mon Sep 17 00:00:00 2001 From: Al Liu Date: Tue, 18 Jun 2024 23:12:00 +0800 Subject: [PATCH] use `u5` and `u27` in public API --- Cargo.toml | 3 ++- src/lib.rs | 5 +++-- src/map.rs | 35 +++++++++++++---------------- src/map/api.rs | 40 ++++++++++++++++----------------- src/map/tests.rs | 56 ++++++++++++++++++++++++++++++++-------------- src/options.rs | 58 +++++++++++++++++++++++++++--------------------- src/types.rs | 26 +++++++++++++++++----- 7 files changed, 134 insertions(+), 89 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3e179469..fa55e65d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "skl" -version = "0.11.1" +version = "0.11.4" edition = "2021" rust-version = "1.56.0" repository = "https://github.com/al8n/skl" @@ -46,6 +46,7 @@ getrandom = { version = "0.2", features = ["js"] } either = { version = "1", default-features = false } rand = { version = "0.8", default-features = false, features = ["getrandom"] } rarena-allocator = { version = "0.1", default-features = false } +ux2 = { version = "0.8", default-features = false, features = ["32"] } tracing = { version = "0.1", optional = true } diff --git a/src/lib.rs b/src/lib.rs index bfd82d8d..d2cf5293 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,9 +30,10 @@ pub use options::{MmapOptions, OpenOptions}; mod types; pub use types::*; -pub use rarena_allocator::{Arena, Error as ArenaError}; - +pub use either; pub use map::{AllVersionsIter, SkipMap}; +pub use rarena_allocator::{Arena, Error as ArenaError}; +pub use ux2::{u27, u5}; const MAX_HEIGHT: usize = 32; diff --git a/src/map.rs b/src/map.rs index 5eb9e3d3..adc9e6b3 100644 --- a/src/map.rs +++ b/src/map.rs @@ -287,7 +287,7 @@ impl NodePtr { /// /// - The caller must ensure that the node is allocated by the arena. /// - The caller must ensure that the offset is less than the capacity of the arena and larger than 0. - unsafe fn cas_next_offset_weak( + unsafe fn cas_next_offset( &self, arena: &Arena, idx: usize, @@ -296,11 +296,10 @@ impl NodePtr { success: Ordering, failure: Ordering, ) -> Result { - #[cfg(not(feature = "unaligned"))] self .tower(arena, idx) .next_offset - .compare_exchange_weak(current, new, success, failure) + .compare_exchange(current, new, success, failure) } } @@ -610,7 +609,7 @@ impl Drop for SkipMap { impl SkipMap { fn new_in(arena: Arena, cmp: C, opts: Options) -> Result { - let data_offset = Self::check_capacity(&arena, opts.max_height())?; + let data_offset = Self::check_capacity(&arena, opts.max_height().into())?; if arena.read_only() { let (meta, head, tail) = Self::get_pointers(&arena); @@ -640,14 +639,15 @@ impl SkipMap { } }; - let head = Self::allocate_full_node(&arena, opts.max_height())?; - let tail = Self::allocate_full_node(&arena, opts.max_height())?; + let max_height: u8 = opts.max_height().into(); + let head = Self::allocate_full_node(&arena, max_height)?; + let tail = Self::allocate_full_node(&arena, max_height)?; // Safety: // We will always allocate enough space for the head node and the tail node. unsafe { // Link all head/tail levels together. - for i in 0..(opts.max_height() as usize) { + for i in 0..(max_height as usize) { let head_link = head.tower(&arena, i); let tail_link = tail.tower(&arena, i); head_link.next_offset.store(tail.offset, Ordering::Relaxed); @@ -1085,17 +1085,14 @@ impl SkipMap { } #[inline] - const fn check_node_size( - &self, - height: u32, - key_size: u32, - mut value_size: u32, - ) -> Result<(), Error> { - if height < 1 || height > self.opts.max_height() as u32 { + fn check_node_size(&self, height: u32, key_size: u32, mut value_size: u32) -> Result<(), Error> { + let max_height: u32 = self.opts.max_height().into(); + if height < 1 || height > max_height { panic!("height cannot be less than one or greater than the max height"); } - if key_size > self.opts.max_key_size() { + let max_key_size: u32 = self.opts.max_key_size().into(); + if key_size > max_key_size { return Err(Error::KeyTooLarge(key_size as u64)); } @@ -1156,7 +1153,7 @@ impl SkipMap { value_size: u32, f: impl FnOnce(&mut VacantBuffer<'a>) -> Result<(), E>, ) -> Result<(NodePtr, u32, Deallocator), Either> { - let height = super::random_height(self.opts.max_height()); + let height = super::random_height(self.opts.max_height().into()); let (nd, deallocator) = match key { Key::Occupied(key) => self.allocate_entry_node( height, @@ -1700,12 +1697,12 @@ impl SkipMap { fn fetch_vacant_key<'a, 'b: 'a, E>( &'a self, - key_size: u16, + key_size: u32, key: impl FnOnce(&mut VacantBuffer<'a>) -> Result<(), E>, ) -> Result, Either> { let (key_offset, key_size) = self .arena - .alloc_bytes(key_size as u32) + .alloc_bytes(key_size) .map(|mut b| { b.detach(); (b.offset(), b.capacity()) @@ -1866,7 +1863,7 @@ impl SkipMap { } } - match prev.cas_next_offset_weak( + match prev.cas_next_offset( &self.arena, i, next.offset, diff --git a/src/map/api.rs b/src/map/api.rs index cf61cb49..43a18551 100644 --- a/src/map/api.rs +++ b/src/map/api.rs @@ -1,8 +1,9 @@ use rarena_allocator::ArenaOptions; +use ux2::u27; use super::*; -impl SkipMap { +impl SkipMap { /// Create a new skipmap with default options. /// /// **Note:** The capacity stands for how many memory allocated, @@ -389,14 +390,15 @@ impl SkipMap { self.meta = meta; - let head = Self::allocate_full_node(&self.arena, self.opts.max_height())?; - let tail = Self::allocate_full_node(&self.arena, self.opts.max_height())?; + let max_height: u8 = self.opts.max_height().into(); + let head = Self::allocate_full_node(&self.arena, max_height)?; + let tail = Self::allocate_full_node(&self.arena, max_height)?; // Safety: // We will always allocate enough space for the head node and the tail node. unsafe { // Link all head/tail levels together. - for i in 0..(self.opts.max_height() as usize) { + for i in 0..(max_height as usize) { let head_link = head.tower(&self.arena, i); let tail_link = tail.tower(&self.arena, i); head_link.next_offset.store(tail.offset, Ordering::Relaxed); @@ -701,7 +703,7 @@ impl SkipMap { /// # Example /// /// ```rust - /// use skl::SkipMap; + /// use skl::{SkipMap, u27}; /// /// struct Person { /// id: u32, @@ -724,7 +726,7 @@ impl SkipMap { /// /// let l = SkipMap::new().unwrap(); /// - /// l.insert_with::(1, 5, |key| { + /// l.insert_with::(1, u27::new(5), |key| { /// key.write(b"alice").unwrap(); /// Ok(()) /// }, encoded_size as u32, |mut val| { @@ -737,12 +739,12 @@ impl SkipMap { pub fn insert_with<'a, E>( &'a self, trailer: T, - key_size: u16, + key_size: u27, key: impl FnOnce(&mut VacantBuffer<'a>) -> Result<(), E>, val_size: u32, val: impl FnOnce(&mut VacantBuffer<'a>) -> Result<(), E> + Copy, ) -> Result>, Either> { - let vk = self.fetch_vacant_key(key_size, key)?; + let vk = self.fetch_vacant_key(u32::from(key_size), key)?; self .update( @@ -779,7 +781,7 @@ impl SkipMap { /// # Example /// /// ```rust - /// use skl::SkipMap; + /// use skl::{SkipMap, u27}; /// /// struct Person { /// id: u32, @@ -802,7 +804,7 @@ impl SkipMap { /// /// let l = SkipMap::new().unwrap(); /// - /// l.get_or_insert_with::(1, 5, |key| { + /// l.get_or_insert_with::(1, u27::new(5), |key| { /// key.write(b"alice").unwrap(); /// Ok(()) /// }, encoded_size as u32, |mut val| { @@ -815,12 +817,12 @@ impl SkipMap { pub fn get_or_insert_with<'a, E>( &'a self, trailer: T, - key_size: u16, + key_size: u27, key: impl FnOnce(&mut VacantBuffer<'a>) -> Result<(), E>, val_size: u32, val: impl FnOnce(&mut VacantBuffer<'a>) -> Result<(), E> + Copy, ) -> Result>, Either> { - let vk = self.fetch_vacant_key(key_size, key)?; + let vk = self.fetch_vacant_key(u32::from(key_size), key)?; self .update( @@ -944,7 +946,7 @@ impl SkipMap { /// # Example /// /// ```rust - /// use skl::SkipMap; + /// use skl::{SkipMap, u27}; /// /// struct Person { /// id: u32, @@ -967,7 +969,7 @@ impl SkipMap { /// /// let l = SkipMap::new().unwrap(); /// - /// l.get_or_remove_with::(1, 5, |key| { + /// l.get_or_remove_with::(1, u27::new(5), |key| { /// key.write(b"alice").unwrap(); /// Ok(()) /// }) @@ -976,15 +978,15 @@ impl SkipMap { pub fn get_or_remove_with<'a, 'b: 'a, E>( &'a self, trailer: T, - key_size: u16, + key_size: u27, key: impl FnOnce(&mut VacantBuffer<'a>) -> Result<(), E>, ) -> Result>, Either> { - let vk = self.fetch_vacant_key(key_size, key)?; - + let vk = self.fetch_vacant_key(u32::from(key_size), key)?; + let key = Key::RemoveVacant(vk); self .update( trailer, - Key::RemoveVacant(vk), + key, 0, noop::, Ordering::Relaxed, @@ -995,8 +997,6 @@ impl SkipMap { .map(|res| match res { Either::Left(old) => match old { Some(old) => { - self.arena.increase_discarded(key_size as u32); - if old.is_removed() { None } else { diff --git a/src/map/tests.rs b/src/map/tests.rs index cd4848e9..1d317fc0 100644 --- a/src/map/tests.rs +++ b/src/map/tests.rs @@ -5,6 +5,7 @@ use std::format; use std::sync::Arc; +use rarena_allocator::Freelist; #[cfg(feature = "std")] use wg::WaitGroup; @@ -180,12 +181,30 @@ fn full_in(l: impl FnOnce(usize) -> SkipMap) { #[test] fn test_full() { - run(|| full_in(|n| SkipMap::with_options(Options::new().with_capacity(n as u32)).unwrap())) + run(|| { + full_in(|n| { + SkipMap::with_options( + Options::new() + .with_capacity(n as u32) + .with_freelist(Freelist::None), + ) + .unwrap() + }) + }) } #[test] fn test_full_unify() { - run(|| full_in(|n| SkipMap::with_options(UNIFY_TEST_OPTIONS.with_capacity(n as u32)).unwrap())) + run(|| { + full_in(|n| { + SkipMap::with_options( + UNIFY_TEST_OPTIONS + .with_capacity(n as u32) + .with_freelist(Freelist::None), + ) + .unwrap() + }) + }) } #[test] @@ -202,7 +221,13 @@ fn test_full_map_mut() { .read(true) .write(true); let map_options = MmapOptions::default(); - SkipMap::map_mut(p, open_options, map_options).unwrap() + SkipMap::map_mut_with_options( + p, + Options::new().with_freelist(Freelist::None), + open_options, + map_options, + ) + .unwrap() }); }) } @@ -213,7 +238,8 @@ fn test_full_map_anon() { run(|| { full_in(|n| { let map_options = MmapOptions::default().len(n as u32); - SkipMap::map_anon(map_options).unwrap() + SkipMap::map_anon_with_options(Options::new().with_freelist(Freelist::None), map_options) + .unwrap() }); }) } @@ -224,7 +250,8 @@ fn test_full_map_anon_unify() { run(|| { full_in(|n| { let map_options = MmapOptions::default().len(n as u32); - SkipMap::map_anon_with_options(TEST_OPTIONS, map_options).unwrap() + SkipMap::map_anon_with_options(Options::new().with_freelist(Freelist::None), map_options) + .unwrap() }); }) } @@ -2265,8 +2292,7 @@ fn test_reopen_mmap() { let open_options = OpenOptions::default() .create(Some(ARENA_SIZE as u32)) .read(true) - .write(true) - .lock_exclusive(true); + .write(true); let map_options = MmapOptions::default(); let l = SkipMap::map_mut(&p, open_options, map_options).unwrap(); for i in 0..1000 { @@ -2275,12 +2301,9 @@ fn test_reopen_mmap() { l.flush().unwrap(); } - let open_options = OpenOptions::default() - .read(true) - .lock_shared(true) - .shrink_on_drop(true); + let open_options = OpenOptions::default().read(true).shrink_on_drop(true); let map_options = MmapOptions::default(); - let l = SkipMap::map(&p, open_options, map_options, 0).unwrap(); + let l = SkipMap::::map(&p, open_options, map_options, 0).unwrap(); assert_eq!(1000, l.len()); for i in 0..1000 { let k = key(i); @@ -2305,8 +2328,7 @@ fn test_reopen_mmap2() { let open_options = OpenOptions::default() .create(Some(ARENA_SIZE as u32)) .read(true) - .write(true) - .lock_shared(true); + .write(true); let map_options = MmapOptions::default(); let l = SkipMap::map_mut_with_comparator(&p, open_options, map_options, Ascend).unwrap(); let mut data = (0..1000).collect::>(); @@ -2440,7 +2462,7 @@ fn get_or_insert_with(l: SkipMap) { l.get_or_insert_with::<()>( 1, - 5, + u27::new(5), |key| { key.write(b"alice").unwrap(); Ok(()) @@ -2695,7 +2717,7 @@ fn insert_with(l: SkipMap) { l.insert_with::<()>( 1, - 5, + u27::new(5), |key| { key.write(b"alice").unwrap(); Ok(()) @@ -2730,7 +2752,7 @@ fn insert_with(l: SkipMap) { let old = l .insert_with::<()>( 1, - 5, + u27::new(5), |key| { key.write(b"alice").unwrap(); Ok(()) diff --git a/src/options.rs b/src/options.rs index bb1b2fee..9dffb838 100644 --- a/src/options.rs +++ b/src/options.rs @@ -4,14 +4,14 @@ pub use rarena_allocator::{MmapOptions, OpenOptions}; pub use rarena_allocator::Freelist; -const U27_MAX: u32 = (1 << 27) - 1; +use ux2::{u27, u5}; /// Options for `SkipMap`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Options { max_value_size: u32, - max_key_size: u32, - max_height: u8, + max_key_size: u27, + max_height: u5, magic_version: u16, capacity: u32, unify: bool, @@ -31,8 +31,8 @@ impl Options { pub const fn new() -> Self { Self { max_value_size: u32::MAX, - max_key_size: U27_MAX, - max_height: 20, + max_key_size: u27::MAX, + max_height: u5::new(20), capacity: 1024, unify: false, magic_version: 0, @@ -127,13 +127,13 @@ impl Options { /// # Example /// /// ``` - /// use skl::Options; + /// use skl::{Options, u27}; /// - /// let options = Options::new().with_max_key_size(1024); + /// let options = Options::new().with_max_key_size(u27::new(1024)); /// ``` #[inline] - pub const fn with_max_key_size(mut self, size: u32) -> Self { - self.max_key_size = if size > U27_MAX { U27_MAX } else { size }; + pub const fn with_max_key_size(mut self, size: u27) -> Self { + self.max_key_size = size; self } @@ -144,19 +144,13 @@ impl Options { /// # Example /// /// ``` - /// use skl::Options; + /// use skl::{Options, u5}; /// - /// let options = Options::new().with_max_height(20); + /// let options = Options::new().with_max_height(u5::new(20)); /// ``` #[inline] - pub const fn with_max_height(mut self, height: u8) -> Self { - self.max_height = if height == 0 { - 1 - } else if height > 31 { - 31 - } else { - height - }; + pub const fn with_max_height(mut self, height: u5) -> Self { + self.max_height = height; self } @@ -194,26 +188,40 @@ impl Options { } /// Returns the maximum size of the key. + /// + /// The maximum size of the key is `u27::MAX`. + /// + /// Default is `u27::MAX`. + /// + /// # Example + /// + /// ``` + /// use skl::{Options, u27}; + /// + /// let options = Options::new().with_max_key_size(u27::new(1024)); + /// + /// assert_eq!(options.max_key_size(), u27::new(1024)); + /// ``` #[inline] - pub const fn max_key_size(&self) -> u32 { + pub const fn max_key_size(&self) -> u27 { self.max_key_size } /// Returns the maximum height. /// - /// Default is `20`. The maximum height is `31`. The minimum height is `1`. + /// Default is `20`. The maximum height is `u5::MAX`. The minimum height is `1`. /// /// # Example /// /// ``` - /// use skl::Options; + /// use skl::{Options, u5}; /// - /// let options = Options::new().with_max_height(20); + /// let options = Options::new().with_max_height(u5::new(5)); /// - /// assert_eq!(options.max_height(), 20); + /// assert_eq!(options.max_height(), u5::new(5)); /// ``` #[inline] - pub const fn max_height(&self) -> u8 { + pub const fn max_height(&self) -> u5 { self.max_height } diff --git a/src/types.rs b/src/types.rs index d507ace0..b6485379 100644 --- a/src/types.rs +++ b/src/types.rs @@ -29,7 +29,13 @@ pub struct VacantBuffer<'a> { } impl<'a> VacantBuffer<'a> { - /// Write bytes to the occupied value. + /// Fill the remaining space with the given byte. + pub fn fill(&mut self, byte: u8) { + self.len = self.cap; + self.value[self.len..].fill(byte); + } + + /// Write bytes to the vacant value. pub fn write(&mut self, bytes: &[u8]) -> Result<(), TooLarge> { let len = bytes.len(); let remaining = self.cap - self.len; @@ -45,25 +51,35 @@ impl<'a> VacantBuffer<'a> { Ok(()) } - /// Returns the capacity of the occupied value. + /// Write bytes to the vacant value without bounds checking. + /// + /// # Panics + /// - If a slice is larger than the remaining space. + pub fn write_unchecked(&mut self, bytes: &[u8]) { + let len = bytes.len(); + self.value[self.len..self.len + len].copy_from_slice(bytes); + self.len += len; + } + + /// Returns the capacity of the vacant value. #[inline] pub const fn capacity(&self) -> usize { self.cap } - /// Returns the length of the occupied value. + /// Returns the length of the vacant value. #[inline] pub const fn len(&self) -> usize { self.len } - /// Returns `true` if the occupied value is empty. + /// Returns `true` if the vacant value is empty. #[inline] pub const fn is_empty(&self) -> bool { self.len == 0 } - /// Returns the remaining space of the occupied value. + /// Returns the remaining space of the vacant value. #[inline] pub const fn remaining(&self) -> usize { self.cap - self.len