Skip to content

Commit 2bc86bf

Browse files
committed
Safer API for RwIter
1 parent bd96d8f commit 2bc86bf

File tree

3 files changed

+188
-9
lines changed

3 files changed

+188
-9
lines changed

heed/src/databases/database.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,16 +1069,16 @@ impl<KC, DC, C, CDUP> Database<KC, DC, C, CDUP> {
10691069
/// db.put(&mut wtxn, &13, "i-am-thirteen")?;
10701070
///
10711071
/// let mut iter = db.iter_mut(&mut wtxn)?;
1072-
/// assert_eq!(iter.next().transpose()?, Some((13, "i-am-thirteen")));
1072+
/// assert_eq!(Iterator::next(&mut iter).transpose()?, Some((13, "i-am-thirteen")));
10731073
/// let ret = unsafe { iter.del_current()? };
10741074
/// assert!(ret);
10751075
///
1076-
/// assert_eq!(iter.next().transpose()?, Some((27, "i-am-twenty-seven")));
1077-
/// assert_eq!(iter.next().transpose()?, Some((42, "i-am-forty-two")));
1076+
/// assert_eq!(Iterator::next(&mut iter).transpose()?, Some((27, "i-am-twenty-seven")));
1077+
/// assert_eq!(Iterator::next(&mut iter).transpose()?, Some((42, "i-am-forty-two")));
10781078
/// let ret = unsafe { iter.put_current(&42, "i-am-the-new-forty-two")? };
10791079
/// assert!(ret);
10801080
///
1081-
/// assert_eq!(iter.next().transpose()?, None);
1081+
/// assert_eq!(Iterator::next(&mut iter).transpose()?, None);
10821082
///
10831083
/// drop(iter);
10841084
///

heed/src/databases/encrypted_database.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -914,16 +914,16 @@ impl<KC, DC, C, CDUP> EncryptedDatabase<KC, DC, C, CDUP> {
914914
/// db.put(&mut wtxn, &13, "i-am-thirteen")?;
915915
///
916916
/// let mut iter = db.iter_mut(&mut wtxn)?;
917-
/// assert_eq!(iter.next().transpose()?, Some((13, "i-am-thirteen")));
917+
/// assert_eq!(Iterator::next(&mut iter).transpose()?, Some((13, "i-am-thirteen")));
918918
/// let ret = unsafe { iter.del_current()? };
919919
/// assert!(ret);
920920
///
921-
/// assert_eq!(iter.next().transpose()?, Some((27, "i-am-twenty-seven")));
922-
/// assert_eq!(iter.next().transpose()?, Some((42, "i-am-forty-two")));
921+
/// assert_eq!(Iterator::next(&mut iter).transpose()?, Some((27, "i-am-twenty-seven")));
922+
/// assert_eq!(Iterator::next(&mut iter).transpose()?, Some((42, "i-am-forty-two")));
923923
/// let ret = unsafe { iter.put_current(&42, "i-am-the-new-forty-two")? };
924924
/// assert!(ret);
925925
///
926-
/// assert_eq!(iter.next().transpose()?, None);
926+
/// assert_eq!(Iterator::next(&mut iter).transpose()?, None);
927927
///
928928
/// drop(iter);
929929
///

heed/src/iterator/iter.rs

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::borrow::Cow;
22
use std::marker;
33

4-
use types::LazyDecode;
4+
use types::{Lazy, LazyDecode};
55

66
use crate::iteration_method::{IterationMethod, MoveBetweenKeys, MoveThroughDuplicateValues};
77
use crate::*;
@@ -437,6 +437,185 @@ impl<KC, DC, IM> fmt::Debug for RwIter<'_, KC, DC, IM> {
437437
}
438438
}
439439

440+
pub struct RwIterItem<'a, 'txn, KC, DC, IM = MoveThroughDuplicateValues> {
441+
iter: &'a mut RwIter<'txn, KC, DC, IM>,
442+
key: &'txn [u8],
443+
data: &'txn [u8],
444+
}
445+
446+
impl<'a, 'txn, KC, DC, IM> RwIterItem<'a, 'txn, KC, DC, IM> {
447+
/// Get the key at the current cursor, which is valid for at least as long
448+
/// as the `RwIterItem` exists.
449+
pub fn key<'b>(&'b self) -> Lazy<'b, KC>
450+
where
451+
KC: 'static,
452+
{
453+
LazyDecode::<KC>::bytes_decode(self.key).unwrap()
454+
}
455+
456+
/// Get the data at the current cursor, which is valid for at least as long
457+
/// as the `RwIterItem` exists.
458+
pub fn data<'b>(&'b self) -> Lazy<'b, DC>
459+
where
460+
DC: 'static,
461+
{
462+
LazyDecode::<DC>::bytes_decode(self.data).unwrap()
463+
}
464+
465+
/// Keep the entry the cursor is currently pointing to.
466+
/// Returns the key and data at the current cursor, which are valid until
467+
/// the end of the transaction.
468+
pub fn keep(self) -> (Lazy<'txn, KC>, Lazy<'txn, DC>)
469+
where
470+
KC: 'static,
471+
DC: 'static,
472+
{
473+
let key = LazyDecode::<KC>::bytes_decode(self.key).unwrap();
474+
let data = LazyDecode::<DC>::bytes_decode(self.data).unwrap();
475+
(key, data)
476+
}
477+
478+
/// Delete the entry the cursor is currently pointing to.
479+
///
480+
/// Returns `true`` if the entry was successfully deleted.
481+
pub fn delete(self) -> Result<bool> {
482+
// SAFETY: It is not possible to keep reference to the current value
483+
// after deleting, because this method takes `self` by value, and
484+
// the current key / value are only available for the lifetime of
485+
// `self`.
486+
unsafe { self.iter.del_current() }
487+
}
488+
489+
/// Write a new value to the current entry. The entry is written with the specified flags.
490+
///
491+
/// > This is intended to be used when the new data is the same size as the old.
492+
/// > Otherwise it will simply perform a delete of the old record followed by an insert.
493+
pub fn put_reserved_with_flags<F>(
494+
mut self,
495+
flags: PutFlags,
496+
data_size: usize,
497+
write_func: F,
498+
) -> Result<Self>
499+
where
500+
F: FnOnce(&mut ReservedSpace<'_>) -> io::Result<()>,
501+
{
502+
let key_owned: Vec<u8> = self.key.to_owned();
503+
// SAFETY: The key is owned, and `write_func` cannot use a reference
504+
// from this entry while modifying it, because this method takes `self`
505+
// by value, and the current key / value are only available for the
506+
// lifetime of `self`.
507+
if unsafe {
508+
self.iter.cursor.put_current_reserved_with_flags(
509+
flags,
510+
key_owned.as_slice(),
511+
data_size,
512+
write_func,
513+
)
514+
}? {
515+
let (key, data) = self.iter.cursor.current()?.ok_or(Error::Mdb(MdbError::NotFound))?;
516+
self.key = key;
517+
self.data = data;
518+
Ok(self)
519+
} else {
520+
Err(Error::Mdb(MdbError::NotFound))
521+
}
522+
}
523+
524+
/// Insert a key-value pair in this database.
525+
/// The entry is written with the specified flags and data codec.
526+
pub fn put_with_options<'b, NDC>(
527+
mut self,
528+
flags: PutFlags,
529+
data: &'b NDC::EItem,
530+
) -> Result<Self>
531+
where
532+
NDC: BytesEncode<'b>,
533+
{
534+
let key_owned: Vec<u8> = self.key.to_owned();
535+
let data_bytes: Cow<[u8]> = NDC::bytes_encode(data).map_err(Error::Encoding)?;
536+
// SAFETY: The key is owned, and `data` cannot use a reference
537+
// from this entry while modifying it, because this method takes `self`
538+
// by value, and the current key / value are only available for the
539+
// lifetime of `self`.
540+
let () = unsafe {
541+
self.iter.cursor.put_current_with_flags(flags, key_owned.as_slice(), &data_bytes)
542+
}?;
543+
let (key, data) = self.iter.cursor.current()?.ok_or(Error::Mdb(MdbError::NotFound))?;
544+
self.key = key;
545+
self.data = data;
546+
Ok(self)
547+
}
548+
549+
/// Write a new value to the current entry.
550+
///
551+
/// > This is intended to be used when the new data is the same size as the old.
552+
/// > Otherwise it will simply perform a delete of the old record followed by an insert.
553+
pub fn put<'b>(mut self, data: &'b DC::EItem) -> Result<Self>
554+
where
555+
DC: BytesEncode<'b>,
556+
{
557+
let key_owned: Vec<u8> = self.key.to_owned();
558+
let data_bytes: Cow<[u8]> = DC::bytes_encode(data).map_err(Error::Encoding)?;
559+
// SAFETY: The key is owned, and `data` cannot use a reference
560+
// from this entry while modifying it, because this method takes `self`
561+
// by value, and the current key / value are only available for the
562+
// lifetime of `self`.
563+
if unsafe { self.iter.cursor.put_current(key_owned.as_slice(), &data_bytes) }? {
564+
let (key, data) = self.iter.cursor.current()?.ok_or(Error::Mdb(MdbError::NotFound))?;
565+
self.key = key;
566+
self.data = data;
567+
Ok(self)
568+
} else {
569+
Err(Error::Mdb(MdbError::NotFound))
570+
}
571+
}
572+
}
573+
574+
impl<'txn, KC, DC, IM> RwIter<'txn, KC, DC, IM> {
575+
/// Advance the iterator to the next item.
576+
pub fn next<'a>(&'a mut self) -> Option<Result<RwIterItem<'a, 'txn, KC, DC, IM>>>
577+
where
578+
IM: IterationMethod,
579+
{
580+
let result = if self.move_on_first {
581+
self.move_on_first = false;
582+
self.cursor.move_on_first(IM::MOVE_OPERATION)
583+
} else {
584+
self.cursor.move_on_next(IM::MOVE_OPERATION)
585+
};
586+
587+
match result {
588+
Ok(Some((key, data))) => Some(Ok(RwIterItem { iter: self, key, data })),
589+
Ok(None) => None,
590+
Err(e) => Some(Err(e)),
591+
}
592+
}
593+
594+
/// Advance the iterator to the last item.
595+
pub fn last<'a>(&'a mut self) -> Option<Result<RwIterItem<'a, 'txn, KC, DC, IM>>>
596+
where
597+
IM: IterationMethod,
598+
{
599+
let result = if self.move_on_first {
600+
self.cursor.move_on_last(IM::MOVE_OPERATION)
601+
} else {
602+
match (self.cursor.current(), self.cursor.move_on_last(IM::MOVE_OPERATION)) {
603+
(Ok(Some((ckey, _))), Ok(Some((key, data)))) if ckey != key => {
604+
Ok(Some((key, data)))
605+
}
606+
(Ok(_), Ok(_)) => Ok(None),
607+
(Err(e), _) | (_, Err(e)) => Err(e),
608+
}
609+
};
610+
611+
match result {
612+
Ok(Some((key, data))) => Some(Ok(RwIterItem { iter: self, key, data })),
613+
Ok(None) => None,
614+
Err(e) => Some(Err(e)),
615+
}
616+
}
617+
}
618+
440619
/// A reverse read-only iterator structure.
441620
pub struct RoRevIter<'txn, KC, DC, IM = MoveThroughDuplicateValues> {
442621
cursor: RoCursor<'txn>,

0 commit comments

Comments
 (0)