diff --git a/src/cursor.rs b/src/cursor.rs index 8838100..0cdf541 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -1,5 +1,5 @@ use std::marker::PhantomData; -use std::{fmt, iter, mem, ptr, result, slice}; +use std::{fmt, mem, ptr, result, slice}; use libc::{EINVAL, c_void, size_t, c_uint}; @@ -9,13 +9,6 @@ use ffi; use flags::WriteFlags; use transaction::Transaction; -// A type alias to a boxed trait object representing an Iterator over -// key/pair results. The Iter struct implements this trait, and we return -// the trait object instead of the Iter itself from Cursor.iter*() methods -// to delay returning an error until the consumer actually iterates -// or collects the Iter. -type BoxedIter<'txn> = Box>>; - /// An LMDB cursor. pub trait Cursor<'txn> { @@ -64,12 +57,12 @@ pub trait Cursor<'txn> { /// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the /// duplicate data items of each key will be returned before moving on to /// the next key. - fn iter_from(&mut self, key: K) -> BoxedIter where K: AsRef<[u8]> { + fn iter_from(&mut self, key: K) -> Iter<'txn> where K: AsRef<[u8]> { match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) { Ok(_) | Err(Error::NotFound) => (), - Err(error) => return Box::new(iter::once(Err(error))), + Err(error) => return Iter::Err(error), }; - Box::new(Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)) + Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT) } /// Iterate over duplicate database items. The iterator will begin with the @@ -87,21 +80,21 @@ pub trait Cursor<'txn> { /// Iterate over duplicate items in the database starting from the given /// key. Each item will be returned as an iterator of its duplicates. - fn iter_dup_from(&mut self, key: &K) -> Box> where K: AsRef<[u8]> { + fn iter_dup_from(&mut self, key: &K) -> IterDup<'txn> where K: AsRef<[u8]> { match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) { Ok(_) | Err(Error::NotFound) => (), - Err(error) => return Box::new(iter::once::(Box::new(iter::once(Err(error))))), + Err(error) => return IterDup::Err(error), }; - Box::new(IterDup::new(self.cursor(), ffi::MDB_GET_CURRENT)) + IterDup::new(self.cursor(), ffi::MDB_GET_CURRENT) } /// Iterate over the duplicates of the item in the database with the given key. - fn iter_dup_of(&mut self, key: &K) -> BoxedIter where K: AsRef<[u8]> { + fn iter_dup_of(&mut self, key: &K) -> Iter<'txn> where K: AsRef<[u8]> { match self.get(Some(key.as_ref()), None, ffi::MDB_SET) { Ok(_) | Err(Error::NotFound) => (), - Err(error) => return Box::new(iter::once(Err(error))), + Err(error) => return Iter::Err(error), }; - Box::new(Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP)) + Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP) } } @@ -221,19 +214,39 @@ unsafe fn val_to_slice<'a>(val: ffi::MDB_val) -> &'a [u8] { slice::from_raw_parts(val.mv_data as *const u8, val.mv_size as usize) } -/// An iterator over the values in an LMDB database. -pub struct Iter<'txn> { - cursor: *mut ffi::MDB_cursor, - op: c_uint, - next_op: c_uint, - _marker: PhantomData, +/// An iterator over the key/value pairs in an LMDB database. +pub enum Iter<'txn> { + /// An iterator that returns an error on every call to Iter.next(). + /// Cursor.iter*() creates an Iter of this type when LMDB returns an error + /// on retrieval of a cursor. Using this variant instead of returning + /// an error makes Cursor.iter()* methods infallible, so consumers only + /// need to check the result of Iter.next(). + Err(Error), + + /// An iterator that returns an Item on calls to Iter.next(). + /// The Item is a Result<(&'txn [u8], &'txn [u8])>, so this variant + /// might still return an error, if retrieval of the key/value pair + /// fails for some reason. + Ok { + /// The LMDB cursor with which to iterate. + cursor: *mut ffi::MDB_cursor, + + /// The first operation to perform when the consumer calls Iter.next(). + op: c_uint, + + /// The next and subsequent operations to perform. + next_op: c_uint, + + /// A marker to ensure the iterator doesn't outlive the transaction. + _marker: PhantomData, + }, } impl <'txn> Iter<'txn> { /// Creates a new iterator backed by the given cursor. fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint, next_op: c_uint) -> Iter<'t> { - Iter { cursor: cursor, op: op, next_op: next_op, _marker: PhantomData } + Iter::Ok { cursor: cursor, op: op, next_op: next_op, _marker: PhantomData } } } @@ -248,17 +261,22 @@ impl <'txn> Iterator for Iter<'txn> { type Item = Result<(&'txn [u8], &'txn [u8])>; fn next(&mut self) -> Option> { - let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; - let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; - let op = mem::replace(&mut self.op, self.next_op); - unsafe { - match ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, op) { - ffi::MDB_SUCCESS => Some(Ok((val_to_slice(key), val_to_slice(data)))), - // EINVAL can occur when the cursor was previously seeked to a non-existent value, - // e.g. iter_from with a key greater than all values in the database. - ffi::MDB_NOTFOUND | EINVAL => None, - error => Some(Err(Error::from_err_code(error))), - } + match self { + Iter::Ok { cursor, ref mut op, next_op, _marker } => { + let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; + let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; + let op = mem::replace(op, *next_op); + unsafe { + match ffi::mdb_cursor_get(*cursor, &mut key, &mut data, op) { + ffi::MDB_SUCCESS => Some(Ok((val_to_slice(key), val_to_slice(data)))), + // EINVAL can occur when the cursor was previously seeked to a non-existent value, + // e.g. iter_from with a key greater than all values in the database. + ffi::MDB_NOTFOUND | EINVAL => None, + error => Some(Err(Error::from_err_code(error))), + } + } + }, + Iter::Err(err) => Some(Err(*err)), } } } @@ -267,17 +285,35 @@ impl <'txn> Iterator for Iter<'txn> { /// /// The yielded items of the iterator are themselves iterators over the duplicate values for a /// specific key. -pub struct IterDup<'txn> { - cursor: *mut ffi::MDB_cursor, - op: c_uint, - _marker: PhantomData, +pub enum IterDup<'txn> { + /// An iterator that returns an error on every call to Iter.next(). + /// Cursor.iter*() creates an Iter of this type when LMDB returns an error + /// on retrieval of a cursor. Using this variant instead of returning + /// an error makes Cursor.iter()* methods infallible, so consumers only + /// need to check the result of Iter.next(). + Err(Error), + + /// An iterator that returns an Item on calls to Iter.next(). + /// The Item is a Result<(&'txn [u8], &'txn [u8])>, so this variant + /// might still return an error, if retrieval of the key/value pair + /// fails for some reason. + Ok { + /// The LMDB cursor with which to iterate. + cursor: *mut ffi::MDB_cursor, + + /// The first operation to perform when the consumer calls Iter.next(). + op: c_uint, + + /// A marker to ensure the iterator doesn't outlive the transaction. + _marker: PhantomData, + }, } impl <'txn> IterDup<'txn> { /// Creates a new iterator backed by the given cursor. fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint) -> IterDup<'t> { - IterDup { cursor: cursor, op: op, _marker: PhantomData } + IterDup::Ok { cursor: cursor, op: op, _marker: PhantomData } } } @@ -289,20 +325,25 @@ impl <'txn> fmt::Debug for IterDup<'txn> { impl <'txn> Iterator for IterDup<'txn> { - type Item = BoxedIter<'txn>; - - fn next(&mut self) -> Option> { - let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; - let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; - let op = mem::replace(&mut self.op, ffi::MDB_NEXT_NODUP); - let err_code = unsafe { - ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, op) - }; - - if err_code == ffi::MDB_SUCCESS { - Some(Box::new(Iter::new(self.cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))) - } else { - None + type Item = Iter<'txn>; + + fn next(&mut self) -> Option> { + match self { + IterDup::Ok { cursor, ref mut op, _marker } => { + let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; + let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; + let op = mem::replace(op, ffi::MDB_NEXT_NODUP); + let err_code = unsafe { + ffi::mdb_cursor_get(*cursor, &mut key, &mut data, op) + }; + + if err_code == ffi::MDB_SUCCESS { + Some(Iter::new(*cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP)) + } else { + None + } + }, + IterDup::Err(err) => Some(Iter::Err(*err)), } } }