diff --git a/heed/examples/cursor-append.rs b/heed/examples/cursor-append.rs index 5c080c15..6f487c41 100644 --- a/heed/examples/cursor-append.rs +++ b/heed/examples/cursor-append.rs @@ -30,9 +30,9 @@ fn main() -> Result<(), Box> { // We try to append ordered entries in the second database. let mut iter = second.iter_mut(&mut wtxn)?; - iter.append("aaaa", "lol")?; - iter.append("abcd", "lol")?; - iter.append("bcde", "lol")?; + unsafe { iter.append("aaaa", "lol")? }; + unsafe { iter.append("abcd", "lol")? }; + unsafe { iter.append("bcde", "lol")? }; drop(iter); diff --git a/heed/src/cursor.rs b/heed/src/cursor.rs index feee096d..ccd9a5b3 100644 --- a/heed/src/cursor.rs +++ b/heed/src/cursor.rs @@ -1,9 +1,9 @@ use std::ops::{Deref, DerefMut}; use std::{marker, mem, ptr}; -use crate::*; use crate::mdb::error::mdb_result; use crate::mdb::ffi; +use crate::*; pub struct RoCursor<'txn> { cursor: *mut ffi::MDB_cursor, @@ -193,9 +193,22 @@ impl<'txn> RwCursor<'txn> { }) } - pub fn del_current(&mut self) -> Result { + /// Delete the entry the cursor is currently pointing to. + /// + /// Returns `true` if the entry was successfully deleted. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database + /// while modifying it. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn del_current(&mut self) -> Result { // Delete the current entry - let result = unsafe { mdb_result(ffi::mdb_cursor_del(self.cursor.cursor, 0)) }; + let result = mdb_result(ffi::mdb_cursor_del(self.cursor.cursor, 0)); match result { Ok(()) => Ok(true), @@ -204,19 +217,40 @@ impl<'txn> RwCursor<'txn> { } } - pub fn put_current(&mut self, key: &[u8], data: &[u8]) -> Result { - let mut key_val = unsafe { crate::into_val(&key) }; - let mut data_val = unsafe { crate::into_val(&data) }; + /// Write a new value to the current entry. + /// + /// The given key **must** be equal to the one this cursor is pointing otherwise the database + /// can be put into an inconsistent state. + /// + /// Returns `true` if the entry was successfully written. + /// + /// > This is intended to be used when the new data is the same size as the old. + /// > Otherwise it will simply perform a delete of the old record followed by an insert. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn put_current(&mut self, key: &[u8], data: &[u8]) -> Result { + let mut key_val = crate::into_val(&key); + let mut data_val = crate::into_val(&data); // Modify the pointed data - let result = unsafe { - mdb_result(ffi::mdb_cursor_put( - self.cursor.cursor, - &mut key_val, - &mut data_val, - ffi::MDB_CURRENT, - )) - }; + let result = mdb_result(ffi::mdb_cursor_put( + self.cursor.cursor, + &mut key_val, + &mut data_val, + ffi::MDB_CURRENT, + )); match result { Ok(()) => Ok(true), @@ -225,19 +259,35 @@ impl<'txn> RwCursor<'txn> { } } - pub fn append(&mut self, key: &[u8], data: &[u8]) -> Result<()> { - let mut key_val = unsafe { crate::into_val(&key) }; - let mut data_val = unsafe { crate::into_val(&data) }; + /// Append the given key/value pair to the end of the database. + /// + /// If a key is inserted that is less than any previous key a `KeyExist` error + /// is returned and the key is not inserted into the database. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn append(&mut self, key: &[u8], data: &[u8]) -> Result<()> { + let mut key_val = crate::into_val(&key); + let mut data_val = crate::into_val(&data); // Modify the pointed data - let result = unsafe { - mdb_result(ffi::mdb_cursor_put( - self.cursor.cursor, - &mut key_val, - &mut data_val, - ffi::MDB_APPEND, - )) - }; + let result = mdb_result(ffi::mdb_cursor_put( + self.cursor.cursor, + &mut key_val, + &mut data_val, + ffi::MDB_APPEND, + )); result.map_err(Into::into) } diff --git a/heed/src/db/polymorph.rs b/heed/src/db/polymorph.rs index 5f715e5b..de91dc00 100644 --- a/heed/src/db/polymorph.rs +++ b/heed/src/db/polymorph.rs @@ -2,10 +2,10 @@ use std::borrow::Cow; use std::ops::{Bound, RangeBounds}; use std::{mem, ptr}; -use crate::*; use crate::mdb::error::mdb_result; use crate::mdb::ffi; use crate::types::DecodeIgnore; +use crate::*; /// A polymorphic database that accepts types on call methods and not at creation. /// @@ -615,7 +615,10 @@ impl PolyDatabase { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn first<'txn, T, KC, DC>(&self, txn: &'txn RoTxn) -> Result> + pub fn first<'txn, T, KC, DC>( + &self, + txn: &'txn RoTxn, + ) -> Result> where KC: BytesDecode<'txn>, DC: BytesDecode<'txn>, @@ -668,7 +671,10 @@ impl PolyDatabase { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn last<'txn, T, KC, DC>(&self, txn: &'txn RoTxn) -> Result> + pub fn last<'txn, T, KC, DC>( + &self, + txn: &'txn RoTxn, + ) -> Result> where KC: BytesDecode<'txn>, DC: BytesDecode<'txn>, @@ -860,12 +866,12 @@ impl PolyDatabase { /// /// let mut iter = db.iter_mut::<_, OwnedType, Str>(&mut wtxn)?; /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(13), "i-am-thirteen"))); - /// let ret = iter.del_current()?; + /// let ret = unsafe { iter.del_current()? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(27), "i-am-twenty-seven"))); /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(42), "i-am-forty-two"))); - /// let ret = iter.put_current(&BEI32::new(42), "i-am-the-new-forty-two")?; + /// let ret = unsafe { iter.put_current(&BEI32::new(42), "i-am-the-new-forty-two")? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, None); @@ -881,7 +887,10 @@ impl PolyDatabase { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn iter_mut<'txn, T, KC, DC>(&self, txn: &'txn mut RwTxn) -> Result> { + pub fn iter_mut<'txn, T, KC, DC>( + &self, + txn: &'txn mut RwTxn, + ) -> Result> { assert_eq!(self.env_ident, txn.txn.env.env_mut_ptr() as usize); RwCursor::new(txn, self.dbi).map(|cursor| RwIter::new(cursor)) @@ -923,7 +932,10 @@ impl PolyDatabase { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn rev_iter<'txn, T, KC, DC>(&self, txn: &'txn RoTxn) -> Result> { + pub fn rev_iter<'txn, T, KC, DC>( + &self, + txn: &'txn RoTxn, + ) -> Result> { assert_eq!(self.env_ident, txn.env.env_mut_ptr() as usize); RoCursor::new(txn, self.dbi).map(|cursor| RoRevIter::new(cursor)) @@ -958,12 +970,12 @@ impl PolyDatabase { /// /// let mut iter = db.rev_iter_mut::<_, OwnedType, Str>(&mut wtxn)?; /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(42), "i-am-forty-two"))); - /// let ret = iter.del_current()?; + /// let ret = unsafe { iter.del_current()? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(27), "i-am-twenty-seven"))); /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(13), "i-am-thirteen"))); - /// let ret = iter.put_current(&BEI32::new(13), "i-am-the-new-thirteen")?; + /// let ret = unsafe { iter.put_current(&BEI32::new(13), "i-am-the-new-thirteen")? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, None); @@ -979,7 +991,10 @@ impl PolyDatabase { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn rev_iter_mut<'txn, T, KC, DC>(&self, txn: &'txn mut RwTxn) -> Result> { + pub fn rev_iter_mut<'txn, T, KC, DC>( + &self, + txn: &'txn mut RwTxn, + ) -> Result> { assert_eq!(self.env_ident, txn.env.env_mut_ptr() as usize); RwCursor::new(txn, self.dbi).map(|cursor| RwRevIter::new(cursor)) @@ -1095,10 +1110,10 @@ impl PolyDatabase { /// let range = BEI32::new(27)..=BEI32::new(42); /// let mut range = db.range_mut::<_, OwnedType, Str, _>(&mut wtxn, &range)?; /// assert_eq!(range.next().transpose()?, Some((BEI32::new(27), "i-am-twenty-seven"))); - /// let ret = range.del_current()?; + /// let ret = unsafe { range.del_current()? }; /// assert!(ret); /// assert_eq!(range.next().transpose()?, Some((BEI32::new(42), "i-am-forty-two"))); - /// let ret = range.put_current(&BEI32::new(42), "i-am-the-new-forty-two")?; + /// let ret = unsafe { range.put_current(&BEI32::new(42), "i-am-the-new-forty-two")? }; /// assert!(ret); /// /// assert_eq!(range.next().transpose()?, None); @@ -1264,10 +1279,10 @@ impl PolyDatabase { /// let range = BEI32::new(27)..=BEI32::new(42); /// let mut range = db.rev_range_mut::<_, OwnedType, Str, _>(&mut wtxn, &range)?; /// assert_eq!(range.next().transpose()?, Some((BEI32::new(42), "i-am-forty-two"))); - /// let ret = range.del_current()?; + /// let ret = unsafe { range.del_current()? }; /// assert!(ret); /// assert_eq!(range.next().transpose()?, Some((BEI32::new(27), "i-am-twenty-seven"))); - /// let ret = range.put_current(&BEI32::new(27), "i-am-the-new-twenty-seven")?; + /// let ret = unsafe { range.put_current(&BEI32::new(27), "i-am-the-new-twenty-seven")? }; /// assert!(ret); /// /// assert_eq!(range.next().transpose()?, None); @@ -1411,12 +1426,12 @@ impl PolyDatabase { /// /// let mut iter = db.prefix_iter_mut::<_, Str, OwnedType>(&mut wtxn, "i-am-twenty")?; /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-eight", BEI32::new(28)))); - /// let ret = iter.del_current()?; + /// let ret = unsafe { iter.del_current()? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-nine", BEI32::new(29)))); /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-seven", BEI32::new(27)))); - /// let ret = iter.put_current("i-am-twenty-seven", &BEI32::new(27000))?; + /// let ret = unsafe { iter.put_current("i-am-twenty-seven", &BEI32::new(27000))? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, None); @@ -1536,12 +1551,12 @@ impl PolyDatabase { /// /// let mut iter = db.rev_prefix_iter_mut::<_, Str, OwnedType>(&mut wtxn, "i-am-twenty")?; /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-seven", BEI32::new(27)))); - /// let ret = iter.del_current()?; + /// let ret = unsafe { iter.del_current()? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-nine", BEI32::new(29)))); /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-eight", BEI32::new(28)))); - /// let ret = iter.put_current("i-am-twenty-eight", &BEI32::new(28000))?; + /// let ret = unsafe { iter.put_current("i-am-twenty-eight", &BEI32::new(28000))? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, None); @@ -1809,7 +1824,6 @@ impl PolyDatabase { /// let ret = db.delete_range::<_, OwnedType, _>(&mut wtxn, &range)?; /// assert_eq!(ret, 2); /// - /// /// let mut iter = db.iter::<_, OwnedType, Str>(&wtxn)?; /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(13), "i-am-thirteen"))); /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(521), "i-am-five-hundred-and-twenty-one"))); @@ -1819,7 +1833,11 @@ impl PolyDatabase { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn delete_range<'a, 'txn, T, KC, R>(&self, txn: &'txn mut RwTxn, range: &'a R) -> Result + pub fn delete_range<'a, 'txn, T, KC, R>( + &self, + txn: &'txn mut RwTxn, + range: &'a R, + ) -> Result where KC: BytesEncode<'a> + BytesDecode<'txn>, R: RangeBounds, @@ -1829,8 +1847,11 @@ impl PolyDatabase { let mut count = 0; let mut iter = self.range_mut::(txn, range)?; - while let Some(_) = iter.next() { - iter.del_current()?; + while iter.next().is_some() { + // safety: We do not keep any reference from the database while using `del_current`. + // The user can't keep any reference inside of the database as we ask for a + // mutable reference to the `txn`. + unsafe { iter.del_current()? }; count += 1; } diff --git a/heed/src/db/uniform.rs b/heed/src/db/uniform.rs index 1a1d2bb1..e68cf85f 100644 --- a/heed/src/db/uniform.rs +++ b/heed/src/db/uniform.rs @@ -1,8 +1,8 @@ use std::marker; use std::ops::RangeBounds; -use crate::*; use crate::mdb::ffi; +use crate::*; /// A typed database that accepts only the types it was created with. /// @@ -183,7 +183,11 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn get<'a, 'txn, T>(&self, txn: &'txn RoTxn, key: &'a KC::EItem) -> Result> + pub fn get<'a, 'txn, T>( + &self, + txn: &'txn RoTxn, + key: &'a KC::EItem, + ) -> Result> where KC: BytesEncode<'a>, DC: BytesDecode<'txn>, @@ -408,7 +412,8 @@ impl Database { KC: BytesEncode<'a> + BytesDecode<'txn>, DC: BytesDecode<'txn>, { - self.dyndb.get_greater_than_or_equal_to::(txn, key) + self.dyndb + .get_greater_than_or_equal_to::(txn, key) } /// Retrieves the first key/value pair of this database. @@ -649,12 +654,12 @@ impl Database { /// /// let mut iter = db.iter_mut(&mut wtxn)?; /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(13), "i-am-thirteen"))); - /// let ret = iter.del_current()?; + /// let ret = unsafe { iter.del_current()? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(27), "i-am-twenty-seven"))); /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(42), "i-am-forty-two"))); - /// let ret = iter.put_current(&BEI32::new(42), "i-am-the-new-forty-two")?; + /// let ret = unsafe { iter.put_current(&BEI32::new(42), "i-am-the-new-forty-two")? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, None); @@ -743,12 +748,12 @@ impl Database { /// /// let mut iter = db.rev_iter_mut(&mut wtxn)?; /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(42), "i-am-forty-two"))); - /// let ret = iter.del_current()?; + /// let ret = unsafe { iter.del_current()? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(27), "i-am-twenty-seven"))); /// assert_eq!(iter.next().transpose()?, Some((BEI32::new(13), "i-am-thirteen"))); - /// let ret = iter.put_current(&BEI32::new(13), "i-am-the-new-thirteen")?; + /// let ret = unsafe { iter.put_current(&BEI32::new(13), "i-am-the-new-thirteen")? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, None); @@ -764,7 +769,10 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn rev_iter_mut<'txn, T>(&self, txn: &'txn mut RwTxn) -> Result> { + pub fn rev_iter_mut<'txn, T>( + &self, + txn: &'txn mut RwTxn, + ) -> Result> { self.dyndb.rev_iter_mut::(txn) } @@ -852,10 +860,10 @@ impl Database { /// let range = BEI32::new(27)..=BEI32::new(42); /// let mut range = db.range_mut(&mut wtxn, &range)?; /// assert_eq!(range.next().transpose()?, Some((BEI32::new(27), "i-am-twenty-seven"))); - /// let ret = range.del_current()?; + /// let ret = unsafe { range.del_current()? }; /// assert!(ret); /// assert_eq!(range.next().transpose()?, Some((BEI32::new(42), "i-am-forty-two"))); - /// let ret = range.put_current(&BEI32::new(42), "i-am-the-new-forty-two")?; + /// let ret = unsafe { range.put_current(&BEI32::new(42), "i-am-the-new-forty-two")? }; /// assert!(ret); /// /// assert_eq!(range.next().transpose()?, None); @@ -969,10 +977,10 @@ impl Database { /// let range = BEI32::new(27)..=BEI32::new(42); /// let mut range = db.rev_range_mut(&mut wtxn, &range)?; /// assert_eq!(range.next().transpose()?, Some((BEI32::new(42), "i-am-forty-two"))); - /// let ret = range.del_current()?; + /// let ret = unsafe { range.del_current()? }; /// assert!(ret); /// assert_eq!(range.next().transpose()?, Some((BEI32::new(27), "i-am-twenty-seven"))); - /// let ret = range.put_current(&BEI32::new(27), "i-am-the-new-twenty-seven")?; + /// let ret = unsafe { range.put_current(&BEI32::new(27), "i-am-the-new-twenty-seven")? }; /// assert!(ret); /// /// assert_eq!(range.next().transpose()?, None); @@ -1086,12 +1094,12 @@ impl Database { /// /// let mut iter = db.prefix_iter_mut(&mut wtxn, "i-am-twenty")?; /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-eight", BEI32::new(28)))); - /// let ret = iter.del_current()?; + /// let ret = unsafe { iter.del_current()? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-nine", BEI32::new(29)))); /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-seven", BEI32::new(27)))); - /// let ret = iter.put_current("i-am-twenty-seven", &BEI32::new(27000))?; + /// let ret = unsafe { iter.put_current("i-am-twenty-seven", &BEI32::new(27000))? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, None); @@ -1203,12 +1211,12 @@ impl Database { /// /// let mut iter = db.rev_prefix_iter_mut(&mut wtxn, "i-am-twenty")?; /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-seven", BEI32::new(27)))); - /// let ret = iter.del_current()?; + /// let ret = unsafe { iter.del_current()? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-nine", BEI32::new(29)))); /// assert_eq!(iter.next().transpose()?, Some(("i-am-twenty-eight", BEI32::new(28)))); - /// let ret = iter.put_current("i-am-twenty-eight", &BEI32::new(28000))?; + /// let ret = unsafe { iter.put_current("i-am-twenty-eight", &BEI32::new(28000))? }; /// assert!(ret); /// /// assert_eq!(iter.next().transpose()?, None); @@ -1268,7 +1276,12 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn put<'a, T>(&self, txn: &mut RwTxn, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> + pub fn put<'a, T>( + &self, + txn: &mut RwTxn, + key: &'a KC::EItem, + data: &'a DC::EItem, + ) -> Result<()> where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -1312,7 +1325,12 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn append<'a, T>(&self, txn: &mut RwTxn, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> + pub fn append<'a, T>( + &self, + txn: &mut RwTxn, + key: &'a KC::EItem, + data: &'a DC::EItem, + ) -> Result<()> where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -1416,7 +1434,11 @@ impl Database { /// wtxn.commit()?; /// # Ok(()) } /// ``` - pub fn delete_range<'a, 'txn, T, R>(&self, txn: &'txn mut RwTxn, range: &'a R) -> Result + pub fn delete_range<'a, 'txn, T, R>( + &self, + txn: &'txn mut RwTxn, + range: &'a R, + ) -> Result where KC: BytesEncode<'a> + BytesDecode<'txn>, R: RangeBounds, diff --git a/heed/src/iter/iter.rs b/heed/src/iter/iter.rs index a11732bb..f9c2a459 100644 --- a/heed/src/iter/iter.rs +++ b/heed/src/iter/iter.rs @@ -10,7 +10,11 @@ pub struct RoIter<'txn, KC, DC> { impl<'txn, KC, DC> RoIter<'txn, KC, DC> { pub(crate) fn new(cursor: RoCursor<'txn>) -> RoIter<'txn, KC, DC> { - RoIter { cursor, move_on_first: true, _phantom: marker::PhantomData } + RoIter { + cursor, + move_on_first: true, + _phantom: marker::PhantomData, + } } /// Change the codec types of this iterator, specifying the codecs. @@ -70,7 +74,7 @@ where match (self.cursor.current(), self.cursor.move_on_last()) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -95,24 +99,58 @@ pub struct RwIter<'txn, KC, DC> { impl<'txn, KC, DC> RwIter<'txn, KC, DC> { pub(crate) fn new(cursor: RwCursor<'txn>) -> RwIter<'txn, KC, DC> { - RwIter { cursor, move_on_first: true, _phantom: marker::PhantomData } + RwIter { + cursor, + move_on_first: true, + _phantom: marker::PhantomData, + } } /// Delete the entry the cursor is currently pointing to. /// /// Returns `true` if the entry was successfully deleted. - pub fn del_current(&mut self) -> Result { + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database + /// while modifying it. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn del_current(&mut self) -> Result { self.cursor.del_current() } /// Write a new value to the current entry. - /// The given key must be equal to the one this cursor is pointing at. + /// + /// The given key **must** be equal to the one this cursor is pointing otherwise the database + /// can be put into an inconsistent state. /// /// Returns `true` if the entry was successfully written. /// - /// This is intended to be used when the new data is the same size as the old. - /// Otherwise it will simply perform a delete of the old record followed by an insert. - pub fn put_current<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result + /// > This is intended to be used when the new data is the same size as the old. + /// > Otherwise it will simply perform a delete of the old record followed by an insert. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn put_current<'a>( + &mut self, + key: &'a KC::EItem, + data: &'a DC::EItem, + ) -> Result where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -126,7 +164,21 @@ impl<'txn, KC, DC> RwIter<'txn, KC, DC> { /// /// If a key is inserted that is less than any previous key a `KeyExist` error /// is returned and the key is not inserted into the database. - pub fn append<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn append<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -193,7 +245,7 @@ where match (self.cursor.current(), self.cursor.move_on_last()) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -218,7 +270,11 @@ pub struct RoRevIter<'txn, KC, DC> { impl<'txn, KC, DC> RoRevIter<'txn, KC, DC> { pub(crate) fn new(cursor: RoCursor<'txn>) -> RoRevIter<'txn, KC, DC> { - RoRevIter { cursor, move_on_last: true, _phantom: marker::PhantomData } + RoRevIter { + cursor, + move_on_last: true, + _phantom: marker::PhantomData, + } } /// Change the codec types of this iterator, specifying the codecs. @@ -278,7 +334,7 @@ where match (self.cursor.current(), self.cursor.move_on_first()) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -303,24 +359,58 @@ pub struct RwRevIter<'txn, KC, DC> { impl<'txn, KC, DC> RwRevIter<'txn, KC, DC> { pub(crate) fn new(cursor: RwCursor<'txn>) -> RwRevIter<'txn, KC, DC> { - RwRevIter { cursor, move_on_last: true, _phantom: marker::PhantomData } + RwRevIter { + cursor, + move_on_last: true, + _phantom: marker::PhantomData, + } } /// Delete the entry the cursor is currently pointing to. /// /// Returns `true` if the entry was successfully deleted. - pub fn del_current(&mut self) -> Result { + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database + /// while modifying it. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn del_current(&mut self) -> Result { self.cursor.del_current() } /// Write a new value to the current entry. - /// The given key must be equal to the one this cursor is pointing at. + /// + /// The given key **must** be equal to the one this cursor is pointing otherwise the database + /// can be put into an inconsistent state. /// /// Returns `true` if the entry was successfully written. /// - /// This is intended to be used when the new data is the same size as the old. - /// Otherwise it will simply perform a delete of the old record followed by an insert. - pub fn put_current<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result + /// > This is intended to be used when the new data is the same size as the old. + /// > Otherwise it will simply perform a delete of the old record followed by an insert. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn put_current<'a>( + &mut self, + key: &'a KC::EItem, + data: &'a DC::EItem, + ) -> Result where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -334,7 +424,7 @@ impl<'txn, KC, DC> RwRevIter<'txn, KC, DC> { /// /// If a key is inserted that is less than any previous key a `KeyExist` error /// is returned and the key is not inserted into the database. - pub fn append<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> + pub unsafe fn append<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -401,7 +491,7 @@ where match (self.cursor.current(), self.cursor.move_on_first()) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } diff --git a/heed/src/iter/prefix.rs b/heed/src/iter/prefix.rs index 25eaa4fa..56f236ac 100644 --- a/heed/src/iter/prefix.rs +++ b/heed/src/iter/prefix.rs @@ -1,14 +1,13 @@ use std::borrow::Cow; use std::marker; -use crate::*; use super::{advance_key, retreat_key}; +use crate::*; fn move_on_prefix_end<'txn>( cursor: &mut RoCursor<'txn>, prefix: &mut Vec, -) -> Result> -{ +) -> Result> { advance_key(prefix); let result = cursor .move_on_key_greater_than_or_equal_to(prefix) @@ -26,7 +25,12 @@ pub struct RoPrefix<'txn, KC, DC> { impl<'txn, KC, DC> RoPrefix<'txn, KC, DC> { pub(crate) fn new(cursor: RoCursor<'txn>, prefix: Vec) -> RoPrefix<'txn, KC, DC> { - RoPrefix { cursor, prefix, move_on_first: true, _phantom: marker::PhantomData } + RoPrefix { + cursor, + prefix, + move_on_first: true, + _phantom: marker::PhantomData, + } } /// Change the codec types of this iterator, specifying the codecs. @@ -65,7 +69,8 @@ where fn next(&mut self) -> Option { let result = if self.move_on_first { self.move_on_first = false; - self.cursor.move_on_key_greater_than_or_equal_to(&self.prefix) + self.cursor + .move_on_key_greater_than_or_equal_to(&self.prefix) } else { self.cursor.move_on_next() }; @@ -80,7 +85,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -90,10 +95,13 @@ where let result = if self.move_on_first { move_on_prefix_end(&mut self.cursor, &mut self.prefix) } else { - match (self.cursor.current(), move_on_prefix_end(&mut self.cursor, &mut self.prefix)) { + match ( + self.cursor.current(), + move_on_prefix_end(&mut self.cursor, &mut self.prefix), + ) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -109,7 +117,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -125,14 +133,59 @@ pub struct RwPrefix<'txn, KC, DC> { impl<'txn, KC, DC> RwPrefix<'txn, KC, DC> { pub(crate) fn new(cursor: RwCursor<'txn>, prefix: Vec) -> RwPrefix<'txn, KC, DC> { - RwPrefix { cursor, prefix, move_on_first: true, _phantom: marker::PhantomData } + RwPrefix { + cursor, + prefix, + move_on_first: true, + _phantom: marker::PhantomData, + } } - pub fn del_current(&mut self) -> Result { + /// Delete the entry the cursor is currently pointing to. + /// + /// Returns `true` if the entry was successfully deleted. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database + /// while modifying it. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn del_current(&mut self) -> Result { self.cursor.del_current() } - pub fn put_current<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result + /// Write a new value to the current entry. + /// + /// The given key **must** be equal to the one this cursor is pointing otherwise the database + /// can be put into an inconsistent state. + /// + /// Returns `true` if the entry was successfully written. + /// + /// > This is intended to be used when the new data is the same size as the old. + /// > Otherwise it will simply perform a delete of the old record followed by an insert. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn put_current<'a>( + &mut self, + key: &'a KC::EItem, + data: &'a DC::EItem, + ) -> Result where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -142,6 +195,34 @@ impl<'txn, KC, DC> RwPrefix<'txn, KC, DC> { self.cursor.put_current(&key_bytes, &data_bytes) } + /// Append the given key/value pair to the end of the database. + /// + /// If a key is inserted that is less than any previous key a `KeyExist` error + /// is returned and the key is not inserted into the database. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn append<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> + where + KC: BytesEncode<'a>, + DC: BytesEncode<'a>, + { + let key_bytes: Cow<[u8]> = KC::bytes_encode(&key).ok_or(Error::Encoding)?; + let data_bytes: Cow<[u8]> = DC::bytes_encode(&data).ok_or(Error::Encoding)?; + self.cursor.append(&key_bytes, &data_bytes) + } + /// Change the codec types of this iterator, specifying the codecs. pub fn remap_types(self) -> RwPrefix<'txn, KC2, DC2> { RwPrefix { @@ -178,7 +259,8 @@ where fn next(&mut self) -> Option { let result = if self.move_on_first { self.move_on_first = false; - self.cursor.move_on_key_greater_than_or_equal_to(&self.prefix) + self.cursor + .move_on_key_greater_than_or_equal_to(&self.prefix) } else { self.cursor.move_on_next() }; @@ -193,7 +275,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -203,10 +285,13 @@ where let result = if self.move_on_first { move_on_prefix_end(&mut self.cursor, &mut self.prefix) } else { - match (self.cursor.current(), move_on_prefix_end(&mut self.cursor, &mut self.prefix)) { + match ( + self.cursor.current(), + move_on_prefix_end(&mut self.cursor, &mut self.prefix), + ) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -222,7 +307,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -238,7 +323,12 @@ pub struct RoRevPrefix<'txn, KC, DC> { impl<'txn, KC, DC> RoRevPrefix<'txn, KC, DC> { pub(crate) fn new(cursor: RoCursor<'txn>, prefix: Vec) -> RoRevPrefix<'txn, KC, DC> { - RoRevPrefix { cursor, prefix, move_on_last: true, _phantom: marker::PhantomData } + RoRevPrefix { + cursor, + prefix, + move_on_last: true, + _phantom: marker::PhantomData, + } } /// Change the codec types of this iterator, specifying the codecs. @@ -292,7 +382,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -300,14 +390,17 @@ where fn last(mut self) -> Option { let result = if self.move_on_last { - self.cursor.move_on_key_greater_than_or_equal_to(&self.prefix) + self.cursor + .move_on_key_greater_than_or_equal_to(&self.prefix) } else { let current = self.cursor.current(); - let start = self.cursor.move_on_key_greater_than_or_equal_to(&self.prefix); + let start = self + .cursor + .move_on_key_greater_than_or_equal_to(&self.prefix); match (current, start) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -323,7 +416,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -339,14 +432,59 @@ pub struct RwRevPrefix<'txn, KC, DC> { impl<'txn, KC, DC> RwRevPrefix<'txn, KC, DC> { pub(crate) fn new(cursor: RwCursor<'txn>, prefix: Vec) -> RwRevPrefix<'txn, KC, DC> { - RwRevPrefix { cursor, prefix, move_on_last: true, _phantom: marker::PhantomData } + RwRevPrefix { + cursor, + prefix, + move_on_last: true, + _phantom: marker::PhantomData, + } } - pub fn del_current(&mut self) -> Result { + /// Delete the entry the cursor is currently pointing to. + /// + /// Returns `true` if the entry was successfully deleted. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database + /// while modifying it. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn del_current(&mut self) -> Result { self.cursor.del_current() } - pub fn put_current<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result + /// Write a new value to the current entry. + /// + /// The given key **must** be equal to the one this cursor is pointing otherwise the database + /// can be put into an inconsistent state. + /// + /// Returns `true` if the entry was successfully written. + /// + /// > This is intended to be used when the new data is the same size as the old. + /// > Otherwise it will simply perform a delete of the old record followed by an insert. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn put_current<'a>( + &mut self, + key: &'a KC::EItem, + data: &'a DC::EItem, + ) -> Result where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -356,6 +494,34 @@ impl<'txn, KC, DC> RwRevPrefix<'txn, KC, DC> { self.cursor.put_current(&key_bytes, &data_bytes) } + /// Append the given key/value pair to the end of the database. + /// + /// If a key is inserted that is less than any previous key a `KeyExist` error + /// is returned and the key is not inserted into the database. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn append<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> + where + KC: BytesEncode<'a>, + DC: BytesEncode<'a>, + { + let key_bytes: Cow<[u8]> = KC::bytes_encode(&key).ok_or(Error::Encoding)?; + let data_bytes: Cow<[u8]> = DC::bytes_encode(&data).ok_or(Error::Encoding)?; + self.cursor.append(&key_bytes, &data_bytes) + } + /// Change the codec types of this iterator, specifying the codecs. pub fn remap_types(self) -> RwRevPrefix<'txn, KC2, DC2> { RwRevPrefix { @@ -407,7 +573,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -415,14 +581,17 @@ where fn last(mut self) -> Option { let result = if self.move_on_last { - self.cursor.move_on_key_greater_than_or_equal_to(&self.prefix) + self.cursor + .move_on_key_greater_than_or_equal_to(&self.prefix) } else { let current = self.cursor.current(); - let start = self.cursor.move_on_key_greater_than_or_equal_to(&self.prefix); + let start = self + .cursor + .move_on_key_greater_than_or_equal_to(&self.prefix); match (current, start) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -438,7 +607,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } diff --git a/heed/src/iter/range.rs b/heed/src/iter/range.rs index 3fd88c2a..4e979de2 100644 --- a/heed/src/iter/range.rs +++ b/heed/src/iter/range.rs @@ -2,27 +2,22 @@ use std::borrow::Cow; use std::marker; use std::ops::Bound; -use crate::*; use super::{advance_key, retreat_key}; +use crate::*; fn move_on_range_end<'txn>( cursor: &mut RoCursor<'txn>, end_bound: &Bound>, -) -> Result> -{ +) -> Result> { match end_bound { - Bound::Included(end) => { - match cursor.move_on_key_greater_than_or_equal_to(end) { - Ok(Some((key, data))) if key == &end[..] => Ok(Some((key, data))), - Ok(_) => cursor.move_on_prev(), - Err(e) => Err(e), - } - }, - Bound::Excluded(end) => { - cursor - .move_on_key_greater_than_or_equal_to(end) - .and_then(|_| cursor.move_on_prev()) + Bound::Included(end) => match cursor.move_on_key_greater_than_or_equal_to(end) { + Ok(Some((key, data))) if key == &end[..] => Ok(Some((key, data))), + Ok(_) => cursor.move_on_prev(), + Err(e) => Err(e), }, + Bound::Excluded(end) => cursor + .move_on_key_greater_than_or_equal_to(end) + .and_then(|_| cursor.move_on_prev()), Bound::Unbounded => cursor.move_on_last(), } } @@ -30,18 +25,15 @@ fn move_on_range_end<'txn>( fn move_on_range_start<'txn>( cursor: &mut RoCursor<'txn>, start_bound: &mut Bound>, -) -> Result> -{ +) -> Result> { match start_bound { - Bound::Included(start) => { - cursor.move_on_key_greater_than_or_equal_to(start) - }, + Bound::Included(start) => cursor.move_on_key_greater_than_or_equal_to(start), Bound::Excluded(start) => { advance_key(start); let result = cursor.move_on_key_greater_than_or_equal_to(start); retreat_key(start); result - }, + } Bound::Unbounded => cursor.move_on_first(), } } @@ -59,8 +51,7 @@ impl<'txn, KC, DC> RoRange<'txn, KC, DC> { cursor: RoCursor<'txn>, start_bound: Bound>, end_bound: Bound>, - ) -> RoRange<'txn, KC, DC> - { + ) -> RoRange<'txn, KC, DC> { RoRange { cursor, move_on_start: true, @@ -138,10 +129,13 @@ where let result = if self.move_on_start { move_on_range_end(&mut self.cursor, &self.end_bound) } else { - match (self.cursor.current(), move_on_range_end(&mut self.cursor, &self.end_bound)) { + match ( + self.cursor.current(), + move_on_range_end(&mut self.cursor, &self.end_bound), + ) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -163,7 +157,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -183,8 +177,7 @@ impl<'txn, KC, DC> RwRange<'txn, KC, DC> { cursor: RwCursor<'txn>, start_bound: Bound>, end_bound: Bound>, - ) -> RwRange<'txn, KC, DC> - { + ) -> RwRange<'txn, KC, DC> { RwRange { cursor, move_on_start: true, @@ -194,11 +187,51 @@ impl<'txn, KC, DC> RwRange<'txn, KC, DC> { } } - pub fn del_current(&mut self) -> Result { + /// Delete the entry the cursor is currently pointing to. + /// + /// Returns `true` if the entry was successfully deleted. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database + /// while modifying it. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn del_current(&mut self) -> Result { self.cursor.del_current() } - pub fn put_current<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result + /// Write a new value to the current entry. + /// + /// The given key **must** be equal to the one this cursor is pointing otherwise the database + /// can be put into an inconsistent state. + /// + /// Returns `true` if the entry was successfully written. + /// + /// > This is intended to be used when the new data is the same size as the old. + /// > Otherwise it will simply perform a delete of the old record followed by an insert. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn put_current<'a>( + &mut self, + key: &'a KC::EItem, + data: &'a DC::EItem, + ) -> Result where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -208,6 +241,34 @@ impl<'txn, KC, DC> RwRange<'txn, KC, DC> { self.cursor.put_current(&key_bytes, &data_bytes) } + /// Append the given key/value pair to the end of the database. + /// + /// If a key is inserted that is less than any previous key a `KeyExist` error + /// is returned and the key is not inserted into the database. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn append<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> + where + KC: BytesEncode<'a>, + DC: BytesEncode<'a>, + { + let key_bytes: Cow<[u8]> = KC::bytes_encode(&key).ok_or(Error::Encoding)?; + let data_bytes: Cow<[u8]> = DC::bytes_encode(&data).ok_or(Error::Encoding)?; + self.cursor.append(&key_bytes, &data_bytes) + } + /// Change the codec types of this iterator, specifying the codecs. pub fn remap_types(self) -> RwRange<'txn, KC2, DC2> { RwRange { @@ -276,10 +337,13 @@ where let result = if self.move_on_start { move_on_range_end(&mut self.cursor, &self.end_bound) } else { - match (self.cursor.current(), move_on_range_end(&mut self.cursor, &self.end_bound)) { + match ( + self.cursor.current(), + move_on_range_end(&mut self.cursor, &self.end_bound), + ) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -301,7 +365,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -321,8 +385,7 @@ impl<'txn, KC, DC> RoRevRange<'txn, KC, DC> { cursor: RoCursor<'txn>, start_bound: Bound>, end_bound: Bound>, - ) -> RoRevRange<'txn, KC, DC> - { + ) -> RoRevRange<'txn, KC, DC> { RoRevRange { cursor, move_on_end: true, @@ -405,7 +468,7 @@ where match (current, start) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -427,7 +490,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -447,8 +510,7 @@ impl<'txn, KC, DC> RwRevRange<'txn, KC, DC> { cursor: RwCursor<'txn>, start_bound: Bound>, end_bound: Bound>, - ) -> RwRevRange<'txn, KC, DC> - { + ) -> RwRevRange<'txn, KC, DC> { RwRevRange { cursor, move_on_end: true, @@ -458,11 +520,51 @@ impl<'txn, KC, DC> RwRevRange<'txn, KC, DC> { } } - pub fn del_current(&mut self) -> Result { + /// Delete the entry the cursor is currently pointing to. + /// + /// Returns `true` if the entry was successfully deleted. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database + /// while modifying it. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn del_current(&mut self) -> Result { self.cursor.del_current() } - pub fn put_current<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result + /// Write a new value to the current entry. + /// + /// The given key **must** be equal to the one this cursor is pointing otherwise the database + /// can be put into an inconsistent state. + /// + /// Returns `true` if the entry was successfully written. + /// + /// > This is intended to be used when the new data is the same size as the old. + /// > Otherwise it will simply perform a delete of the old record followed by an insert. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn put_current<'a>( + &mut self, + key: &'a KC::EItem, + data: &'a DC::EItem, + ) -> Result where KC: BytesEncode<'a>, DC: BytesEncode<'a>, @@ -472,6 +574,34 @@ impl<'txn, KC, DC> RwRevRange<'txn, KC, DC> { self.cursor.put_current(&key_bytes, &data_bytes) } + /// Append the given key/value pair to the end of the database. + /// + /// If a key is inserted that is less than any previous key a `KeyExist` error + /// is returned and the key is not inserted into the database. + /// + /// # Safety + /// + /// It is _[undefined behavior]_ to keep a reference of a value from this database while + /// modifying it, so you can't use the key/value that comes from the cursor to feed + /// this function. + /// + /// In other words: Tranform the key and value that you borrow from this database into an owned + /// version of them i.e. `&str` into `String`. + /// + /// > [Values returned from the database are valid only until a subsequent update operation, + /// or the end of the transaction.](http://www.lmdb.tech/doc/group__mdb.html#structMDB__val). + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub unsafe fn append<'a>(&mut self, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<()> + where + KC: BytesEncode<'a>, + DC: BytesEncode<'a>, + { + let key_bytes: Cow<[u8]> = KC::bytes_encode(&key).ok_or(Error::Encoding)?; + let data_bytes: Cow<[u8]> = DC::bytes_encode(&data).ok_or(Error::Encoding)?; + self.cursor.append(&key_bytes, &data_bytes) + } + /// Change the codec types of this iterator, specifying the codecs. pub fn remap_types(self) -> RwRevRange<'txn, KC2, DC2> { RwRevRange { @@ -545,7 +675,7 @@ where match (current, start) { (Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => { Ok(Some((key, data))) - }, + } (Ok(_), Ok(_)) => Ok(None), (Err(e), _) | (_, Err(e)) => Err(e), } @@ -567,7 +697,7 @@ where } else { None } - }, + } Ok(None) => None, Err(e) => Some(Err(e)), }