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" name = "lmdb-sys"
[dependencies.rust-bindgen] [dependencies.rust-bindgen]
git = "https://github.com/crabtw/rust-bindgen.git" path = "../../rust-bindgen"
[build-dependencies.pkg-config] [build-dependencies.pkg-config]
pkg-config = "*" pkg-config = "*"

@ -18,7 +18,7 @@ pub trait Cursor<'txn> {
} }
/// Cursor extension methods. /// 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 /// Retrieves a key/data pair from the cursor. Depending on the cursor op, the current key may
/// be returned. /// be returned.
@ -47,30 +47,47 @@ pub trait CursorExt<'txn> : Cursor<'txn> {
/// ///
/// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the duplicate data /// 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. /// items of each key will be returned before moving on to the next key.
fn iter(&mut self) -> Items<'txn> { fn iter(&mut self) -> Iter<'txn> {
Items::new(self, ffi::MDB_NEXT, ffi::MDB_NEXT) Iter::new(self.cursor(), ffi::MDB_NEXT, ffi::MDB_NEXT)
} }
/// Iterate over database items starting from the beginning of the database. /// Iterate over database items starting from the beginning of the database.
/// ///
/// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the duplicate data /// 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. /// 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(); 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. /// Iterate over database items starting from the given key.
/// ///
/// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the duplicate data /// 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. /// 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(); 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> { /// Iterate over duplicate database items. The iterator will begin with the item next after the
Items::new(self, ffi::MDB_NEXT_DUP, ffi::MDB_NEXT_DUP) /// 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, cursor: *mut ffi::MDB_cursor,
op: c_uint, op: c_uint,
next_op: c_uint, next_op: c_uint,
} }
impl <'txn> Items<'txn> { impl <'txn> Iter<'txn> {
/// Creates a new iterator backed by the given cursor. /// 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> { fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint, next_op: c_uint) -> Iter<'t> {
Items { cursor: cursor.cursor(), op: op, next_op: next_op } 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])> { fn next(&mut self) -> Option<(&'txn [u8], &'txn [u8])> {
let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; 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, 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. /// Creates a new iterator backed by the given cursor.
fn new<'t>(cursor: &mut Cursor<'t>) -> DupItems<'t> { fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint) -> IterDup<'t> {
DupItems { cursor: cursor.cursor() } 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 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 mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() };
let err_code = unsafe {
unsafe { ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op)
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 {
if err_code == ffi::MDB_SUCCESS { self.op = ffi::MDB_NEXT;
Some((val_to_slice(key), val_to_slice(data))) Some(Iter::new(self.cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
} else { } else {
// The documentation for mdb_cursor_get specifies that it may fail with MDB_NOTFOUND None
// 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
}
} }
} }
} }
@ -443,17 +459,20 @@ mod test {
let txn = env.begin_ro_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
let mut cursor = txn.open_ro_cursor(db).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(); cursor.get(Some(b"b"), None, MDB_SET).unwrap();
//assert_eq!(items.clone().into_iter().skip(4).collect::<Vec<(&[u8], &[u8])>>(), assert_eq!(items.clone().into_iter().skip(4).collect::<Vec<(&[u8], &[u8])>>(),
//cursor.iter().collect::<Vec<_>>()); 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])>>(), assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
//cursor.iter_from(b"b").collect::<Vec<_>>()); 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 libc::c_uint;
use std::c_str::ToCStr;
use std::ptr; use std::ptr;
use ffi; use ffi;
@ -8,7 +9,7 @@ use error::{LmdbResult, lmdb_result};
/// A handle to an individual database in an environment. /// A handle to an individual database in an environment.
/// ///
/// A database handle denotes the name and parameters of a 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 { pub struct Database {
dbi: ffi::MDB_dbi, dbi: ffi::MDB_dbi,
} }

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

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

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

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

Loading…
Cancel
Save