diff --git a/lmdb-sys/Cargo.toml b/lmdb-sys/Cargo.toml index 57c1a8c..6f12c97 100644 --- a/lmdb-sys/Cargo.toml +++ b/lmdb-sys/Cargo.toml @@ -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 = "*" diff --git a/src/cursor.rs b/src/cursor.rs index 1a0c455..9946d90 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -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> for DupItems<'txn> { +impl <'txn> Iterator for IterDup<'txn> { + + type Item = Iter<'txn>; - fn next(&mut self) -> Option> { + 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() }; - - 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::>()); + assert_eq!(items, cursor.iter_dup().flat_map(|x| x).collect::>()); - //cursor.get(Some(b"b"), None, MDB_SET).unwrap(); - //assert_eq!(items.clone().into_iter().skip(4).collect::>(), - //cursor.iter().collect::>()); + cursor.get(Some(b"b"), None, MDB_SET).unwrap(); + assert_eq!(items.clone().into_iter().skip(4).collect::>(), + cursor.iter_dup().flat_map(|x| x).collect::>()); - //assert_eq!(items, cursor.iter_start().collect::>()); + assert_eq!(items, + cursor.iter_dup_start().flat_map(|x| x).collect::>()); - //assert_eq!(items.clone().into_iter().skip(3).collect::>(), - //cursor.iter_from(b"b").collect::>()); + assert_eq!(items.clone().into_iter().skip(3).collect::>(), + cursor.iter_dup_from(b"b").flat_map(|x| x).collect::>()); + assert_eq!(items.clone().into_iter().skip(3).collect::>(), + cursor.iter_dup_from(b"ab").flat_map(|x| x).collect::>()); } diff --git a/src/database.rs b/src/database.rs index 73e9a33..373bcc1 100644 --- a/src/database.rs +++ b/src/database.rs @@ -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, } diff --git a/src/environment.rs b/src/environment.rs index 88cfd60..3859cae 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -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, diff --git a/src/error.rs b/src/error.rs index 918d43e..38042c4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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, diff --git a/src/flags.rs b/src/flags.rs index 8016aa5..04fa8dc 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -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."] diff --git a/src/lib.rs b/src/lib.rs index b659851..f659c72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/transaction.rs b/src/transaction.rs index 64f97d6..c5fac55 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -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. ///