crossbeam_skiplist_mvcc/
lib.rs

1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![cfg_attr(docsrs, allow(unused_attributes))]
5#![deny(missing_docs)]
6
7macro_rules! common {
8  ($mod_name: literal) => {
9    /// Inserts a `key`-`value` pair into the map and returns the new entry.
10    ///
11    /// If there is an existing entry with this key, it will be removed before inserting the new
12    /// one.
13    ///
14    /// This function returns an [`Ok(Entry)`](Entry) which
15    /// can be used to access the inserted key's associated value.
16    ///
17    /// This function returns an [`Err(Error)`](Error) if the given version has already been discarded by [`compact`](SkipMap::compact).
18    ///
19    /// See also [`insert_unchecked`](SkipMap::insert_unchecked).
20    ///
21    /// ## Example
22    ///
23    /// ```rust
24    #[doc = concat!("use crossbeam_skiplist_mvcc::", $mod_name, "::SkipMap;")]
25    ///
26    /// let map = SkipMap::new();
27    /// map.insert(1, "key", "value").unwrap();
28    ///
29    /// assert_eq!(*map.get(1, "key").unwrap().value(), "value");
30    /// ```
31    pub fn insert(
32      &self,
33      version: u64,
34      key: K,
35      value: V,
36    ) -> Result<Entry<'_, K, V, Active, C>, Error> {
37      self
38        .check_discard(version)
39        .map(|_| self.insert_in(version, key, value))
40    }
41
42    /// Inserts a `key`-`value` pair into the map and returns the new entry.
43    ///
44    /// If there is an existing entry with this key, it will be removed before inserting the new
45    /// one.
46    ///
47    /// This function returns an [`Entry`] which
48    /// can be used to access the inserted key's associated value.
49    ///
50    /// See also [`insert`](SkipMap::insert).
51    ///
52    /// ## Panics
53    /// - If the given `version` has already been discarded by [`compact`](SkipMap::compact).
54    ///
55    /// ## Example
56    ///
57    /// ```rust
58    #[doc = concat!("use crossbeam_skiplist_mvcc::", $mod_name, "::SkipMap;")]
59    ///
60    /// let map = SkipMap::new();
61    /// map.insert_unchecked(1, "key", "value");
62    ///
63    /// assert_eq!(*map.get(1, "key").unwrap().value(), "value");
64    /// ```
65    pub fn insert_unchecked(&self, version: u64, key: K, value: V) -> Entry<'_, K, V, Active, C> {
66      self
67        .check_discard(version)
68        .expect("version has already been discarded");
69      self.insert_in(version, key, value)
70    }
71
72    /// Inserts a `key`-`value` pair into the skip list and returns the new entry.
73    ///
74    /// If there is an existing entry with this key and compare(entry.value) returns true,
75    /// it will be removed before inserting the new one.
76    /// The closure will not be called if the key is not present.
77    ///
78    /// This function returns an [`Ok(Entry)`](`Entry`) which
79    /// can be used to access the inserted key's associated value.
80    ///
81    /// This function returns an [`Err(Error)`](`Error`) if the given version has already been discarded by [`compact`](SkipMap::compact).
82    ///
83    /// See also [`compare_insert_unchecked`](SkipMap::compare_insert_unchecked).
84    ///
85    /// ## Example
86    ///
87    /// ```rust
88    #[doc = concat!("use crossbeam_skiplist_mvcc::", $mod_name, "::SkipMap;")]
89    ///
90    /// let map = SkipMap::new();
91    /// map.insert(1, "key", 1);
92    /// map.compare_insert(1, "key", 0, |x| x.unwrap() < &0).unwrap();
93    /// assert_eq!(*map.get(1, "key").unwrap().value(), 1);
94    /// map.compare_insert(1, "key", 2, |x| x.unwrap() < &2).unwrap();
95    /// assert_eq!(*map.get(1, "key").unwrap().value(), 2);
96    /// map.compare_insert(1, "absent_key", 0, |_| false).unwrap();
97    /// assert_eq!(*map.get(1, "absent_key").unwrap().value(), 0);
98    /// ```
99    pub fn compare_insert<F>(
100      &self,
101      version: u64,
102      key: K,
103      value: V,
104      compare_fn: F,
105    ) -> Result<Entry<'_, K, V, Active, C>, Error>
106    where
107      F: Fn(Option<&V>) -> bool,
108    {
109      self
110        .check_discard(version)
111        .map(|_| self.compare_insert_in(version, key, value, compare_fn))
112    }
113
114    /// Inserts a `key`-`value` pair into the skip list and returns the new entry.
115    ///
116    /// If there is an existing entry with this key and compare(entry.value) returns true,
117    /// it will be removed before inserting the new one.
118    /// The closure will not be called if the key is not present.
119    ///
120    /// This function returns an [`Entry`] which
121    /// can be used to access the inserted key's associated value.
122    ///
123    /// ## Example
124    ///
125    /// ```rust
126    #[doc = concat!("use crossbeam_skiplist_mvcc::", $mod_name, "::SkipMap;")]
127    ///
128    /// let map = SkipMap::new();
129    /// map.insert(1, "key", 1);
130    /// map.compare_insert_unchecked(1, "key", 0, |x| x.unwrap() < &0);
131    /// assert_eq!(*map.get(1, "key").unwrap().value(), 1);
132    /// map.compare_insert_unchecked(1, "key", 2, |x| x.unwrap() < &2);
133    /// assert_eq!(*map.get(1, "key").unwrap().value(), 2);
134    /// map.compare_insert_unchecked(1, "absent_key", 0, |_| false);
135    /// assert_eq!(*map.get(1, "absent_key").unwrap().value(), 0);
136    /// ```
137    pub fn compare_insert_unchecked<F>(
138      &self,
139      version: u64,
140      key: K,
141      value: V,
142      compare_fn: F,
143    ) -> Entry<'_, K, V, Active, C>
144    where
145      F: Fn(Option<&V>) -> bool,
146    {
147      self
148        .check_discard(version)
149        .expect("version has already been discarded");
150      self.compare_insert_in(version, key, value, compare_fn)
151    }
152
153    /// Insert a tombstone entry for the specified `key` from the map and returns it.
154    ///
155    /// Note that this will not actually remove the entry from the map,
156    /// but only insert a new entry and mark it as removed.
157    /// To actually remove entries with old versions, use [`compact`](SkipMap::compact).
158    ///
159    /// This function returns an [`Entry`] which
160    /// can be used to access the removed key's associated value.
161    ///
162    /// This function returns an [`Err(Error)`](`Error`) if the given version has already been discarded by [`compact`](SkipMap::compact).
163    ///
164    /// See also [`remove_unchecked`](SkipMap::remove_unchecked).
165    ///
166    /// ## Example
167    ///
168    /// ```rust
169    #[doc = concat!("use crossbeam_skiplist_mvcc::", $mod_name, "::SkipMap;")]
170    ///
171    /// let map: SkipMap<&str, &str> = SkipMap::new();
172    /// assert!(map.remove(1, "invalid key").unwrap().is_none());
173    ///
174    /// map.insert(0, "key", "value");
175    /// assert_eq!(*map.remove(1, "key").unwrap().unwrap().value(), "value");
176    /// ```
177    pub fn remove(
178      &self,
179      version: u64,
180      key: K,
181    ) -> Result<Option<Entry<'_, K, V, Active, C>>, Error> {
182      self
183        .check_discard(version)
184        .map(|_| self.remove_in(version, key))
185    }
186
187    /// Insert a tombstone entry for the specified `key` from the map and returns it.
188    ///
189    /// Note that this will not actually remove the entry from the map,
190    /// but only insert a new entry and mark it as removed.
191    /// To actually remove entries with old versions, use [`compact`](SkipMap::compact).
192    ///
193    /// This function returns an [`Entry`] which
194    /// can be used to access the removed key's associated value.
195    ///
196    /// ## Panics
197    /// - If the given `version` has already been discarded by [`compact`](SkipMap::compact).
198    ///
199    /// ## Example
200    ///
201    /// ```rust
202    #[doc = concat!("use crossbeam_skiplist_mvcc::", $mod_name, "::SkipMap;")]
203    ///
204    /// let map: SkipMap<&str, &str> = SkipMap::new();
205    /// assert!(map.remove_unchecked(1, "invalid key").is_none());
206    ///
207    /// map.insert(0, "key", "value");
208    /// assert_eq!(*map.remove_unchecked(1, "key").unwrap().value(), "value");
209    /// ```
210    pub fn remove_unchecked(&self, version: u64, key: K) -> Option<Entry<'_, K, V, Active, C>> {
211      self
212        .check_discard(version)
213        .expect("version has already been discarded");
214      self.remove_in(version, key)
215    }
216
217    #[inline]
218    fn check_discard(&self, version: u64) -> Result<(), Error> {
219      let last = self.last_discard_version.load(Ordering::Acquire);
220      if last != 0 && last >= version {
221        return Err(Error::AlreadyDiscarded(version));
222      }
223      Ok(())
224    }
225  };
226}
227
228/// A multiple version ordered map based on a lock-free skip list. See [`SkipMap`](crate::nested::SkipMap).
229pub mod nested;
230
231/// A multiple version ordered map based on a lock-free skip list. See [`SkipMap`](crate::flatten::SkipMap).
232pub mod flatten;
233
234/// Error types for this crate.
235pub mod error;
236
237pub use crossbeam_skiplist::{equivalentor, Ascend, Descend};
238
239pub use dbutils::state;
240
241/// Transfer the value from `V` to `Self::To`.
242pub trait Transfer<'a, V: 'a>: sealed::Sealed<'a, V> {}
243
244impl<'a, V: 'a, T> Transfer<'a, V> for T where T: sealed::Sealed<'a, V> {}
245
246mod sealed {
247  pub struct TombstoneValidator;
248
249  impl<V> snapshotor::Validator<Option<V>> for TombstoneValidator {
250    #[inline]
251    fn validate(&self, value: &Option<V>) -> bool {
252      value.is_some()
253    }
254  }
255
256  pub trait Sealed<'a, V: 'a>: dbutils::state::State {
257    type To: 'a;
258
259    fn output(data: Option<&'a V>) -> Self::To;
260  }
261
262  impl<'a, V: 'a> Sealed<'a, V> for dbutils::state::Active {
263    type To = &'a V;
264
265    #[inline]
266    fn output(data: Option<&'a V>) -> Self::To {
267      data.expect("entry in Active state must have a value")
268    }
269  }
270
271  impl<'a, V: 'a> Sealed<'a, V> for dbutils::state::MaybeTombstone {
272    type To = Option<&'a V>;
273
274    #[inline]
275    fn output(data: Option<&'a V>) -> Self::To {
276      data
277    }
278  }
279}