add duplicate iterators

without.crypto
Dan Burkert 10 years ago
parent 8897ad4eab
commit a2abd18460
  1. 2
      lmdb-sys/Cargo.toml
  2. 103
      src/cursor.rs
  3. 3
      src/database.rs
  4. 6
      src/environment.rs
  5. 2
      src/error.rs
  6. 6
      src/flags.rs
  7. 2
      src/lib.rs
  8. 2
      src/transaction.rs

@ -13,7 +13,7 @@ build = "build.rs"
name = "lmdb-sys"
[dependencies.rust-bindgen]
git = "https://github.com/crabtw/rust-bindgen.git"
path = "../../rust-bindgen"
[build-dependencies.pkg-config]
pkg-config = "*"

@ -18,7 +18,7 @@ pub trait Cursor<'txn> {
}
/// Cursor extension methods.
pub trait CursorExt<'txn> : Cursor<'txn> {
pub trait CursorExt<'txn> : Cursor<'txn> + Sized {
/// Retrieves a key/data pair from the cursor. Depending on the cursor op, the current key may
/// be returned.
@ -47,30 +47,47 @@ pub trait CursorExt<'txn> : 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(&mut self) -> Items<'txn> {
Items::new(self, ffi::MDB_NEXT, ffi::MDB_NEXT)
fn iter(&mut self) -> Iter<'txn> {
Iter::new(self.cursor(), ffi::MDB_NEXT, ffi::MDB_NEXT)
}
/// Iterate over database items starting from the beginning of the database.
///
/// 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_start(&mut self) -> Items<'txn> {
fn iter_start(&mut self) -> Iter<'txn> {
self.get(None, None, ffi::MDB_FIRST).unwrap();
Items::new(self, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
}
/// Iterate over database items starting from the given key.
///
/// 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: &[u8]) -> Items<'txn> {
fn iter_from(&mut self, key: &[u8]) -> Iter<'txn> {
self.get(Some(key), None, ffi::MDB_SET_RANGE).unwrap();
Items::new(self, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
Iter::new(self.cursor(), ffi::MDB_GET_CURRENT, ffi::MDB_NEXT)
}
fn iter_dup(&mut self) -> Items<'txn> {
Items::new(self, ffi::MDB_NEXT_DUP, ffi::MDB_NEXT_DUP)
/// Iterate over duplicate database items. The iterator will begin with the item next after the
/// cursor, and continue until the end of the database. Each item will be returned as an
/// iterator of its duplicates.
fn iter_dup(&mut self) -> IterDup<'txn> {
IterDup::new(self.cursor(), ffi::MDB_NEXT)
}
/// Iterate over duplicate database items starting from the beginning of the database. Each item
/// will be returned as an iterator of its duplicates.
fn iter_dup_start(&mut self) -> IterDup<'txn> {
self.get(None, None, ffi::MDB_FIRST).unwrap();
IterDup::new(self.cursor(), ffi::MDB_GET_CURRENT)
}
/// 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: &[u8]) -> IterDup<'txn> {
self.get(Some(key), None, ffi::MDB_SET_RANGE).unwrap();
IterDup::new(self.cursor(), ffi::MDB_GET_CURRENT)
}
}
@ -199,21 +216,23 @@ unsafe fn val_to_slice<'a>(val: ffi::MDB_val) -> &'a [u8] {
})
}
pub struct Items<'txn> {
pub struct Iter<'txn> {
cursor: *mut ffi::MDB_cursor,
op: c_uint,
next_op: c_uint,
}
impl <'txn> Items<'txn> {
impl <'txn> Iter<'txn> {
/// Creates a new iterator backed by the given cursor.
fn new<'t>(cursor: &mut Cursor<'t>, op: c_uint, next_op: c_uint) -> Items<'t> {
Items { cursor: cursor.cursor(), op: op, next_op: next_op }
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 }
}
}
impl <'txn> Iterator<(&'txn [u8], &'txn [u8])> for Items<'txn> {
impl <'txn> Iterator for Iter<'txn> {
type Item = (&'txn [u8], &'txn [u8]);
fn next(&mut self) -> Option<(&'txn [u8], &'txn [u8])> {
let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
@ -237,38 +256,35 @@ impl <'txn> Iterator<(&'txn [u8], &'txn [u8])> for Items<'txn> {
}
}
pub struct Items<'txn> {
pub struct IterDup<'txn> {
cursor: *mut ffi::MDB_cursor,
op: c_uint,
}
impl <'txn> DupItems<'txn> {
impl <'txn> IterDup<'txn> {
/// Creates a new iterator backed by the given cursor.
fn new<'t>(cursor: &mut Cursor<'t>) -> DupItems<'t> {
DupItems { cursor: cursor.cursor() }
fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint) -> IterDup<'t> {
IterDup { cursor: cursor, op: op}
}
}
impl <'txn> Iterator<Items<'txn>> for DupItems<'txn> {
impl <'txn> Iterator for IterDup<'txn> {
type Item = Iter<'txn>;
fn next(&mut self) -> Option<Items<'txn>> {
fn next(&mut self) -> Option<Iter<'txn>> {
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() };
unsafe {
let err_code = ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op);
// Set the operation for the next get
self.op = self.next_op;
if err_code == ffi::MDB_SUCCESS {
Some((val_to_slice(key), val_to_slice(data)))
} else {
// The documentation for mdb_cursor_get specifies that it may fail with MDB_NOTFOUND
// and MDB_EINVAL (and we shouldn't be passing in invalid parameters).
// TODO: validate that these are the only failures possible.
debug_assert!(err_code == ffi::MDB_NOTFOUND,
"Unexpected LMDB error {}.", LmdbError::from_err_code(err_code));
None
}
let err_code = unsafe {
ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op)
};
if err_code == ffi::MDB_SUCCESS {
self.op = ffi::MDB_NEXT;
Some(Iter::new(self.cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
} else {
None
}
}
}
@ -443,17 +459,20 @@ mod test {
let txn = env.begin_ro_txn().unwrap();
let mut cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!(items, cursor.iter_dup().collect::<Vec<_>>());
assert_eq!(items, cursor.iter_dup().flat_map(|x| x).collect::<Vec<_>>());
//cursor.get(Some(b"b"), None, MDB_SET).unwrap();
//assert_eq!(items.clone().into_iter().skip(4).collect::<Vec<(&[u8], &[u8])>>(),
//cursor.iter().collect::<Vec<_>>());
cursor.get(Some(b"b"), None, MDB_SET).unwrap();
assert_eq!(items.clone().into_iter().skip(4).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup().flat_map(|x| x).collect::<Vec<_>>());
//assert_eq!(items, cursor.iter_start().collect::<Vec<(&[u8], &[u8])>>());
assert_eq!(items,
cursor.iter_dup_start().flat_map(|x| x).collect::<Vec<(&[u8], &[u8])>>());
//assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
//cursor.iter_from(b"b").collect::<Vec<_>>());
assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_from(b"b").flat_map(|x| x).collect::<Vec<_>>());
assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_from(b"ab").flat_map(|x| x).collect::<Vec<_>>());
}

@ -1,4 +1,5 @@
use libc::c_uint;
use std::c_str::ToCStr;
use std::ptr;
use ffi;
@ -8,7 +9,7 @@ use error::{LmdbResult, lmdb_result};
/// A handle to an individual database in an environment.
///
/// A database handle denotes the name and parameters of a database in an environment.
#[deriving(Show, Clone, Copy, Eq, PartialEq)]
#[derive(Show, Clone, Copy, Eq, PartialEq)]
pub struct Database {
dbi: ffi::MDB_dbi,
}

@ -1,4 +1,5 @@
use libc::{c_uint, size_t, mode_t};
use std::c_str::ToCStr;
use std::io::FilePermission;
use std::ptr;
use std::sync::Mutex;
@ -18,6 +19,9 @@ pub struct Environment {
dbi_open_mutex: Mutex<()>,
}
unsafe impl Send for Environment {}
unsafe impl Sync for Environment {}
impl Environment {
/// Creates a new builder for specifying options for opening an LMDB environment.
@ -145,7 +149,7 @@ impl Drop for Environment {
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Options for opening or creating an environment.
#[deriving(Show, PartialEq, Eq, Copy, Clone)]
#[derive(Show, PartialEq, Eq, Copy, Clone)]
pub struct EnvironmentBuilder {
flags: EnvironmentFlags,
max_readers: Option<c_uint>,

@ -4,7 +4,7 @@ use std::str;
use ffi;
#[deriving(Show, Eq, PartialEq, Copy, Clone)]
#[derive(Show, Eq, PartialEq, Copy, Clone)]
pub enum LmdbError {
/// key/data pair already exists.
KeyExist,

@ -4,7 +4,7 @@ use ffi::*;
bitflags! {
#[doc="Environment Options"]
#[deriving(Show)]
#[derive(Show)]
flags EnvironmentFlags: c_uint {
#[doc="Use a fixed address for the mmap region. This flag must be specified"]
@ -105,7 +105,7 @@ bitflags! {
bitflags! {
#[doc="Database Options"]
#[deriving(Show)]
#[derive(Show)]
flags DatabaseFlags: c_uint {
#[doc="Keys are strings to be compared in reverse order, from the end of the strings"]
@ -141,7 +141,7 @@ bitflags! {
bitflags! {
#[doc="Write Options"]
#[deriving(Show)]
#[derive(Show)]
flags WriteFlags: c_uint {
#[doc="Insert the new item only if the key does not already appear in the database."]

@ -4,7 +4,7 @@
//! Provides the minimal amount of abstraction necessary to interact with LMDB safely in Rust. In
//! general, the API is very similar to the LMDB [C-API](http://symas.com/mdb/doc/).
#![feature(phase, globs, macro_rules, unsafe_destructor)]
#![feature(phase, globs, macro_rules, unsafe_destructor, associated_types)]
#[phase(plugin, link)] extern crate log;
extern crate libc;

@ -24,7 +24,7 @@ pub trait Transaction<'env> {
}
/// Transaction extension methods.
pub trait TransactionExt<'env> : Transaction<'env> {
pub trait TransactionExt<'env> : Transaction<'env> + Sized {
/// Commits the transaction.
///

Loading…
Cancel
Save