Rustfmt pass (cargo +nightly fmt --all)

Signed-off-by: Victor Porof <victor.porof@gmail.com>
without.crypto
Victor Porof 5 years ago
parent ea60c303fe
commit 2acd21c529
  1. 24
      benches/cursor.rs
  2. 36
      benches/transaction.rs
  3. 6
      benches/utils.rs
  4. 330
      src/cursor.rs
  5. 21
      src/database.rs
  6. 136
      src/environment.rs
  7. 13
      src/error.rs
  8. 39
      src/lib.rs
  9. 232
      src/transaction.rs

@ -7,9 +7,17 @@ extern crate test;
mod utils; mod utils;
use ffi::*; use ffi::*;
use lmdb::{Cursor, Result, RoCursor, Transaction}; use lmdb::{
Cursor,
Result,
RoCursor,
Transaction,
};
use std::ptr; use std::ptr;
use test::{Bencher, black_box}; use test::{
black_box,
Bencher,
};
use utils::*; use utils::*;
/// Benchmark of iterator sequential read performance. /// Benchmark of iterator sequential read performance.
@ -85,8 +93,14 @@ fn bench_get_seq_raw(b: &mut Bencher) {
let _txn = env.begin_ro_txn().unwrap(); let _txn = env.begin_ro_txn().unwrap();
let txn = _txn.txn(); let txn = _txn.txn();
let mut key = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; let mut key = MDB_val {
let mut data = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; mv_size: 0,
mv_data: ptr::null_mut(),
};
let mut data = MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
let mut cursor: *mut MDB_cursor = ptr::null_mut(); let mut cursor: *mut MDB_cursor = ptr::null_mut();
b.iter(|| unsafe { b.iter(|| unsafe {
@ -97,7 +111,7 @@ fn bench_get_seq_raw(b: &mut Bencher) {
while mdb_cursor_get(cursor, &mut key, &mut data, MDB_NEXT) == 0 { while mdb_cursor_get(cursor, &mut key, &mut data, MDB_NEXT) == 0 {
i += key.mv_size + data.mv_size; i += key.mv_size + data.mv_size;
count += 1; count += 1;
}; }
black_box(i); black_box(i);
assert_eq!(count, n); assert_eq!(count, n);

@ -10,10 +10,19 @@ mod utils;
use ffi::*; use ffi::*;
use libc::size_t; use libc::size_t;
use lmdb::{Transaction, WriteFlags}; use lmdb::{
use rand::{Rng, XorShiftRng}; Transaction,
WriteFlags,
};
use rand::{
Rng,
XorShiftRng,
};
use std::ptr; use std::ptr;
use test::{Bencher, black_box}; use test::{
black_box,
Bencher,
};
use utils::*; use utils::*;
#[bench] #[bench]
@ -48,8 +57,14 @@ fn bench_get_rand_raw(b: &mut Bencher) {
let dbi = db.dbi(); let dbi = db.dbi();
let txn = _txn.txn(); let txn = _txn.txn();
let mut key_val: MDB_val = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; let mut key_val: MDB_val = MDB_val {
let mut data_val: MDB_val = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; mv_size: 0,
mv_data: ptr::null_mut(),
};
let mut data_val: MDB_val = MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
b.iter(|| unsafe { b.iter(|| unsafe {
let mut i: size_t = 0; let mut i: size_t = 0;
@ -95,8 +110,14 @@ fn bench_put_rand_raw(b: &mut Bencher) {
let dbi = db.dbi(); let dbi = db.dbi();
let env = _env.env(); let env = _env.env();
let mut key_val: MDB_val = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; let mut key_val: MDB_val = MDB_val {
let mut data_val: MDB_val = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; mv_size: 0,
mv_data: ptr::null_mut(),
};
let mut data_val: MDB_val = MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
b.iter(|| unsafe { b.iter(|| unsafe {
let mut txn: *mut MDB_txn = ptr::null_mut(); let mut txn: *mut MDB_txn = ptr::null_mut();
@ -104,7 +125,6 @@ fn bench_put_rand_raw(b: &mut Bencher) {
let mut i: ::libc::c_int = 0; let mut i: ::libc::c_int = 0;
for &(ref key, ref data) in items.iter() { for &(ref key, ref data) in items.iter() {
key_val.mv_size = key.len() as size_t; key_val.mv_size = key.len() as size_t;
key_val.mv_data = key.as_bytes().as_ptr() as *mut _; key_val.mv_data = key.as_bytes().as_ptr() as *mut _;
data_val.mv_size = data.len() as size_t; data_val.mv_size = data.len() as size_t;

@ -1,8 +1,12 @@
extern crate lmdb; extern crate lmdb;
extern crate tempdir; extern crate tempdir;
use lmdb::{Environment, Transaction, WriteFlags};
use self::tempdir::TempDir; use self::tempdir::TempDir;
use lmdb::{
Environment,
Transaction,
WriteFlags,
};
pub fn get_key(n: u32) -> String { pub fn get_key(n: u32) -> String {
format!("key{}", n) format!("key{}", n)

@ -1,17 +1,31 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::{fmt, mem, ptr, result, slice}; use std::{
fmt,
mem,
ptr,
result,
slice,
};
use libc::{EINVAL, c_void, size_t, c_uint}; use libc::{
c_uint,
c_void,
size_t,
EINVAL,
};
use database::Database; use database::Database;
use error::{Error, Result, lmdb_result}; use error::{
lmdb_result,
Error,
Result,
};
use ffi; use ffi;
use flags::WriteFlags; use flags::WriteFlags;
use transaction::Transaction; use transaction::Transaction;
/// An LMDB cursor. /// An LMDB cursor.
pub trait Cursor<'txn> { pub trait Cursor<'txn> {
/// Returns a raw pointer to the underlying LMDB cursor. /// Returns a raw pointer to the underlying LMDB cursor.
/// ///
/// The caller **must** ensure that the pointer is not used after the /// The caller **must** ensure that the pointer is not used after the
@ -26,7 +40,11 @@ pub trait Cursor<'txn> {
let mut data_val = slice_to_val(data); let mut data_val = slice_to_val(data);
let key_ptr = key_val.mv_data; let key_ptr = key_val.mv_data;
lmdb_result(ffi::mdb_cursor_get(self.cursor(), &mut key_val, &mut data_val, op))?; lmdb_result(ffi::mdb_cursor_get(self.cursor(), &mut key_val, &mut data_val, op))?;
let key_out = if key_ptr != key_val.mv_data { Some(val_to_slice(key_val)) } else { None }; let key_out = if key_ptr != key_val.mv_data {
Some(val_to_slice(key_val))
} else {
None
};
let data_out = val_to_slice(data_val); let data_out = val_to_slice(data_val);
Ok((key_out, data_out)) Ok((key_out, data_out))
} }
@ -57,7 +75,10 @@ pub trait Cursor<'txn> {
/// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the /// For databases with duplicate data items (`DatabaseFlags::DUP_SORT`), the
/// duplicate data items of each key will be returned before moving on to /// duplicate data items of each key will be returned before moving on to
/// the next key. /// the next key.
fn iter_from<K>(&mut self, key: K) -> Iter<'txn> where K: AsRef<[u8]> { fn iter_from<K>(&mut self, key: K) -> Iter<'txn>
where
K: AsRef<[u8]>,
{
match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) { match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
Ok(_) | Err(Error::NotFound) => (), Ok(_) | Err(Error::NotFound) => (),
Err(error) => return Iter::Err(error), Err(error) => return Iter::Err(error),
@ -80,7 +101,10 @@ pub trait Cursor<'txn> {
/// Iterate over duplicate items in the database starting from the given /// Iterate over duplicate items in the database starting from the given
/// key. Each item will be returned as an iterator of its duplicates. /// key. Each item will be returned as an iterator of its duplicates.
fn iter_dup_from<K>(&mut self, key: K) -> IterDup<'txn> where K: AsRef<[u8]> { fn iter_dup_from<K>(&mut self, key: K) -> IterDup<'txn>
where
K: AsRef<[u8]>,
{
match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) { match self.get(Some(key.as_ref()), None, ffi::MDB_SET_RANGE) {
Ok(_) | Err(Error::NotFound) => (), Ok(_) | Err(Error::NotFound) => (),
Err(error) => return IterDup::Err(error), Err(error) => return IterDup::Err(error),
@ -89,7 +113,10 @@ pub trait Cursor<'txn> {
} }
/// Iterate over the duplicates of the item in the database with the given key. /// Iterate over the duplicates of the item in the database with the given key.
fn iter_dup_of<K>(&mut self, key: K) -> Iter<'txn> where K: AsRef<[u8]> { fn iter_dup_of<K>(&mut self, key: K) -> Iter<'txn>
where
K: AsRef<[u8]>,
{
match self.get(Some(key.as_ref()), None, ffi::MDB_SET) { match self.get(Some(key.as_ref()), None, ffi::MDB_SET) {
Ok(_) | Err(Error::NotFound) => (), Ok(_) | Err(Error::NotFound) => (),
Err(error) => return Iter::Err(error), Err(error) => return Iter::Err(error),
@ -123,12 +150,16 @@ impl <'txn> Drop for RoCursor<'txn> {
} }
impl<'txn> RoCursor<'txn> { impl<'txn> RoCursor<'txn> {
/// Creates a new read-only cursor in the given database and transaction. /// Creates a new read-only cursor in the given database and transaction.
/// Prefer using `Transaction::open_cursor`. /// Prefer using `Transaction::open_cursor`.
pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RoCursor<'txn>> where T: Transaction { pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RoCursor<'txn>>
where
T: Transaction,
{
let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut();
unsafe { lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?; } unsafe {
lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?;
}
Ok(RoCursor { Ok(RoCursor {
cursor: cursor, cursor: cursor,
_marker: PhantomData, _marker: PhantomData,
@ -161,31 +192,40 @@ impl <'txn> Drop for RwCursor<'txn> {
} }
impl<'txn> RwCursor<'txn> { impl<'txn> RwCursor<'txn> {
/// Creates a new read-only cursor in the given database and transaction. /// Creates a new read-only cursor in the given database and transaction.
/// Prefer using `RwTransaction::open_rw_cursor`. /// Prefer using `RwTransaction::open_rw_cursor`.
pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RwCursor<'txn>> where T: Transaction { pub(crate) fn new<T>(txn: &'txn T, db: Database) -> Result<RwCursor<'txn>>
where
T: Transaction,
{
let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut();
unsafe { lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?; } unsafe {
Ok(RwCursor { cursor: cursor, _marker: PhantomData }) lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))?;
}
Ok(RwCursor {
cursor: cursor,
_marker: PhantomData,
})
} }
/// Puts a key/data pair into the database. The cursor will be positioned at /// Puts a key/data pair into the database. The cursor will be positioned at
/// the new data item, or on failure usually near it. /// the new data item, or on failure usually near it.
pub fn put<K, D>(&mut self, key: &K, data: &D, flags: WriteFlags) -> Result<()> pub fn put<K, D>(&mut self, key: &K, data: &D, flags: WriteFlags) -> Result<()>
where K: AsRef<[u8]>, D: AsRef<[u8]> { where
K: AsRef<[u8]>,
D: AsRef<[u8]>,
{
let key = key.as_ref(); let key = key.as_ref();
let data = data.as_ref(); let data = data.as_ref();
let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t, let mut key_val: ffi::MDB_val = ffi::MDB_val {
mv_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: data.len() as size_t, mv_data: key.as_ptr() as *mut c_void,
mv_data: data.as_ptr() as *mut c_void }; };
unsafe { let mut data_val: ffi::MDB_val = ffi::MDB_val {
lmdb_result(ffi::mdb_cursor_put(self.cursor(), mv_size: data.len() as size_t,
&mut key_val, mv_data: data.as_ptr() as *mut c_void,
&mut data_val, };
flags.bits())) unsafe { lmdb_result(ffi::mdb_cursor_put(self.cursor(), &mut key_val, &mut data_val, flags.bits())) }
}
} }
/// Deletes the current key/data pair. /// Deletes the current key/data pair.
@ -201,12 +241,14 @@ impl <'txn> RwCursor<'txn> {
unsafe fn slice_to_val(slice: Option<&[u8]>) -> ffi::MDB_val { unsafe fn slice_to_val(slice: Option<&[u8]>) -> ffi::MDB_val {
match slice { match slice {
Some(slice) => Some(slice) => ffi::MDB_val {
ffi::MDB_val { mv_size: slice.len() as size_t, mv_size: slice.len() as size_t,
mv_data: slice.as_ptr() as *mut c_void }, mv_data: slice.as_ptr() as *mut c_void,
None => },
ffi::MDB_val { mv_size: 0, None => ffi::MDB_val {
mv_data: ptr::null_mut() }, mv_size: 0,
mv_data: ptr::null_mut(),
},
} }
} }
@ -243,10 +285,14 @@ pub enum Iter<'txn> {
} }
impl<'txn> Iter<'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 ffi::MDB_cursor, op: c_uint, next_op: c_uint) -> Iter<'t> { fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint, next_op: c_uint) -> Iter<'t> {
Iter::Ok { cursor: cursor, op: op, next_op: next_op, _marker: PhantomData } Iter::Ok {
cursor: cursor,
op: op,
next_op: next_op,
_marker: PhantomData,
}
} }
} }
@ -257,14 +303,24 @@ impl <'txn> fmt::Debug for Iter<'txn> {
} }
impl<'txn> Iterator for Iter<'txn> { impl<'txn> Iterator for Iter<'txn> {
type Item = Result<(&'txn [u8], &'txn [u8])>; type Item = Result<(&'txn [u8], &'txn [u8])>;
fn next(&mut self) -> Option<Result<(&'txn [u8], &'txn [u8])>> { fn next(&mut self) -> Option<Result<(&'txn [u8], &'txn [u8])>> {
match self { match self {
&mut Iter::Ok { cursor, ref mut op, next_op, _marker } => { &mut Iter::Ok {
let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; cursor,
let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; 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); let op = mem::replace(op, next_op);
unsafe { unsafe {
match ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) { match ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) {
@ -310,10 +366,13 @@ pub enum IterDup<'txn> {
} }
impl<'txn> IterDup<'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 ffi::MDB_cursor, op: c_uint) -> IterDup<'t> { fn new<'t>(cursor: *mut ffi::MDB_cursor, op: c_uint) -> IterDup<'t> {
IterDup::Ok { cursor: cursor, op: op, _marker: PhantomData } IterDup::Ok {
cursor: cursor,
op: op,
_marker: PhantomData,
}
} }
} }
@ -324,18 +383,25 @@ impl <'txn> fmt::Debug for IterDup<'txn> {
} }
impl<'txn> Iterator for IterDup<'txn> { impl<'txn> Iterator for IterDup<'txn> {
type Item = Iter<'txn>; type Item = Iter<'txn>;
fn next(&mut self) -> Option<Iter<'txn>> { fn next(&mut self) -> Option<Iter<'txn>> {
match self { match self {
&mut IterDup::Ok { cursor, ref mut op, _marker } => { &mut IterDup::Ok {
let mut key = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; cursor,
let mut data = ffi::MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; ref mut op,
let op = mem::replace(op, ffi::MDB_NEXT_NODUP); _marker,
let err_code = unsafe { } => {
ffi::mdb_cursor_get(cursor, &mut key, &mut data, op) 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 { if err_code == ffi::MDB_SUCCESS {
Some(Iter::new(cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP)) Some(Iter::new(cursor, ffi::MDB_GET_CURRENT, ffi::MDB_NEXT_DUP))
@ -352,10 +418,10 @@ impl <'txn> Iterator for IterDup<'txn> {
mod test { mod test {
use tempdir::TempDir; use tempdir::TempDir;
use super::*;
use environment::*; use environment::*;
use ffi::*; use ffi::*;
use flags::*; use flags::*;
use super::*;
#[test] #[test]
fn test_get() { fn test_get() {
@ -369,22 +435,14 @@ mod test {
txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap(); txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap();
let cursor = txn.open_ro_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_FIRST).unwrap());
cursor.get(None, None, MDB_FIRST).unwrap()); assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_GET_CURRENT).unwrap());
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_NEXT).unwrap());
cursor.get(None, None, MDB_GET_CURRENT).unwrap()); assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_PREV).unwrap());
assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(None, None, MDB_LAST).unwrap());
cursor.get(None, None, MDB_NEXT).unwrap()); assert_eq!((None, &b"val2"[..]), cursor.get(Some(b"key2"), None, MDB_SET).unwrap());
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(Some(&b"key3"[..]), None, MDB_SET_KEY).unwrap());
cursor.get(None, None, MDB_PREV).unwrap()); assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(Some(&b"key2\0"[..]), None, MDB_SET_RANGE).unwrap());
assert_eq!((Some(&b"key3"[..]), &b"val3"[..]),
cursor.get(None, None, MDB_LAST).unwrap());
assert_eq!((None, &b"val2"[..]),
cursor.get(Some(b"key2"), None, MDB_SET).unwrap());
assert_eq!((Some(&b"key3"[..]), &b"val3"[..]),
cursor.get(Some(&b"key3"[..]), None, MDB_SET_KEY).unwrap());
assert_eq!((Some(&b"key3"[..]), &b"val3"[..]),
cursor.get(Some(&b"key2\0"[..]), None, MDB_SET_RANGE).unwrap());
} }
#[test] #[test]
@ -402,35 +460,24 @@ mod test {
txn.put(db, b"key2", b"val3", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val3", WriteFlags::empty()).unwrap();
let cursor = txn.open_ro_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_FIRST).unwrap());
cursor.get(None, None, MDB_FIRST).unwrap()); assert_eq!((None, &b"val1"[..]), cursor.get(None, None, MDB_FIRST_DUP).unwrap());
assert_eq!((None, &b"val1"[..]), assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_GET_CURRENT).unwrap());
cursor.get(None, None, MDB_FIRST_DUP).unwrap()); assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), cursor.get(None, None, MDB_NEXT_NODUP).unwrap());
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_NEXT_DUP).unwrap());
cursor.get(None, None, MDB_GET_CURRENT).unwrap()); assert_eq!((Some(&b"key2"[..]), &b"val3"[..]), cursor.get(None, None, MDB_NEXT_DUP).unwrap());
assert_eq!((Some(&b"key2"[..]), &b"val1"[..]),
cursor.get(None, None, MDB_NEXT_NODUP).unwrap());
assert_eq!((Some(&b"key2"[..]), &b"val2"[..]),
cursor.get(None, None, MDB_NEXT_DUP).unwrap());
assert_eq!((Some(&b"key2"[..]), &b"val3"[..]),
cursor.get(None, None, MDB_NEXT_DUP).unwrap());
assert!(cursor.get(None, None, MDB_NEXT_DUP).is_err()); assert!(cursor.get(None, None, MDB_NEXT_DUP).is_err());
assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_PREV_DUP).unwrap());
cursor.get(None, None, MDB_PREV_DUP).unwrap()); assert_eq!((None, &b"val3"[..]), cursor.get(None, None, MDB_LAST_DUP).unwrap());
assert_eq!((None, &b"val3"[..]), assert_eq!((Some(&b"key1"[..]), &b"val3"[..]), cursor.get(None, None, MDB_PREV_NODUP).unwrap());
cursor.get(None, None, MDB_LAST_DUP).unwrap()); assert_eq!((None, &b"val1"[..]), cursor.get(Some(&b"key1"[..]), None, MDB_SET).unwrap());
assert_eq!((Some(&b"key1"[..]), &b"val3"[..]), assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), cursor.get(Some(&b"key2"[..]), None, MDB_SET_KEY).unwrap());
cursor.get(None, None, MDB_PREV_NODUP).unwrap()); assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), cursor.get(Some(&b"key1\0"[..]), None, MDB_SET_RANGE).unwrap());
assert_eq!((None, &b"val1"[..]), assert_eq!((None, &b"val3"[..]), cursor.get(Some(&b"key1"[..]), Some(&b"val3"[..]), MDB_GET_BOTH).unwrap());
cursor.get(Some(&b"key1"[..]), None, MDB_SET).unwrap()); assert_eq!(
assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), (None, &b"val1"[..]),
cursor.get(Some(&b"key2"[..]), None, MDB_SET_KEY).unwrap()); cursor.get(Some(&b"key2"[..]), Some(&b"val"[..]), MDB_GET_BOTH_RANGE).unwrap()
assert_eq!((Some(&b"key2"[..]), &b"val1"[..]), );
cursor.get(Some(&b"key1\0"[..]), None, MDB_SET_RANGE).unwrap());
assert_eq!((None, &b"val3"[..]),
cursor.get(Some(&b"key1"[..]), Some(&b"val3"[..]), MDB_GET_BOTH).unwrap());
assert_eq!((None, &b"val1"[..]),
cursor.get(Some(&b"key2"[..]), Some(&b"val"[..]), MDB_GET_BOTH_RANGE).unwrap());
} }
#[test] #[test]
@ -448,10 +495,8 @@ mod test {
txn.put(db, b"key2", b"val6", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val6", WriteFlags::empty()).unwrap();
let cursor = txn.open_ro_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), assert_eq!((Some(&b"key1"[..]), &b"val1"[..]), cursor.get(None, None, MDB_FIRST).unwrap());
cursor.get(None, None, MDB_FIRST).unwrap()); assert_eq!((None, &b"val1val2val3"[..]), cursor.get(None, None, MDB_GET_MULTIPLE).unwrap());
assert_eq!((None, &b"val1val2val3"[..]),
cursor.get(None, None, MDB_GET_MULTIPLE).unwrap());
assert!(cursor.get(None, None, MDB_NEXT_MULTIPLE).is_err()); assert!(cursor.get(None, None, MDB_NEXT_MULTIPLE).is_err());
} }
@ -461,10 +506,8 @@ mod test {
let env = Environment::new().open(dir.path()).unwrap(); let env = Environment::new().open(dir.path()).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let items: Vec<(&[u8], &[u8])> = vec!((b"key1", b"val1"), let items: Vec<(&[u8], &[u8])> =
(b"key2", b"val2"), vec![(b"key1", b"val1"), (b"key2", b"val2"), (b"key3", b"val3"), (b"key5", b"val5")];
(b"key3", b"val3"),
(b"key5", b"val5"));
{ {
let mut txn = env.begin_rw_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
@ -487,19 +530,27 @@ mod test {
assert_eq!(items, retr.unwrap()); assert_eq!(items, retr.unwrap());
cursor.get(Some(b"key2"), None, MDB_SET).unwrap(); cursor.get(Some(b"key2"), None, MDB_SET).unwrap();
assert_eq!(items.clone().into_iter().skip(2).collect::<Vec<_>>(), assert_eq!(
cursor.iter().collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(2).collect::<Vec<_>>(),
cursor.iter().collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(items, cursor.iter_start().collect::<Result<Vec<_>>>().unwrap()); assert_eq!(items, cursor.iter_start().collect::<Result<Vec<_>>>().unwrap());
assert_eq!(items.clone().into_iter().skip(1).collect::<Vec<_>>(), assert_eq!(
cursor.iter_from(b"key2").collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(1).collect::<Vec<_>>(),
cursor.iter_from(b"key2").collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<_>>(), assert_eq!(
cursor.iter_from(b"key4").collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(3).collect::<Vec<_>>(),
cursor.iter_from(b"key4").collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(), assert_eq!(
cursor.iter_from(b"key6").collect::<Result<Vec<_>>>().unwrap()); vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_from(b"key6").collect::<Result<Vec<_>>>().unwrap()
);
} }
#[test] #[test]
@ -538,7 +589,8 @@ mod test {
let env = Environment::new().open(dir.path()).unwrap(); let env = Environment::new().open(dir.path()).unwrap();
let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap(); let db = env.create_db(None, DatabaseFlags::DUP_SORT).unwrap();
let items: Vec<(&[u8], &[u8])> = vec!((b"a", b"1"), let items: Vec<(&[u8], &[u8])> = vec![
(b"a", b"1"),
(b"a", b"2"), (b"a", b"2"),
(b"a", b"3"), (b"a", b"3"),
(b"b", b"1"), (b"b", b"1"),
@ -549,7 +601,8 @@ mod test {
(b"c", b"3"), (b"c", b"3"),
(b"e", b"1"), (b"e", b"1"),
(b"e", b"2"), (b"e", b"2"),
(b"e", b"3")); (b"e", b"3"),
];
{ {
let mut txn = env.begin_rw_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
@ -564,26 +617,37 @@ mod test {
assert_eq!(items, cursor.iter_dup().flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); assert_eq!(items, cursor.iter_dup().flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap());
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!(
cursor.iter_dup().flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(4).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup().flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()
assert_eq!(items, );
cursor.iter_dup_start().flat_map(|x| x).collect::<Result<Vec<(&[u8], &[u8])>>>().unwrap());
assert_eq!(items, cursor.iter_dup_start().flat_map(|x| x).collect::<Result<Vec<(&[u8], &[u8])>>>().unwrap());
assert_eq!(items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_from(b"b").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); 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_dup_from(b"b").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()
cursor.iter_dup_from(b"ab").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); );
assert_eq!(items.clone().into_iter().skip(9).collect::<Vec<(&[u8], &[u8])>>(), assert_eq!(
cursor.iter_dup_from(b"d").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()); items.clone().into_iter().skip(3).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_from(b"ab").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()
assert_eq!(vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(), );
cursor.iter_dup_from(b"f").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap());
assert_eq!(
assert_eq!(items.clone().into_iter().skip(3).take(3).collect::<Vec<(&[u8], &[u8])>>(), items.clone().into_iter().skip(9).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_of(b"b").collect::<Result<Vec<_>>>().unwrap()); cursor.iter_dup_from(b"d").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(
vec!().into_iter().collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_from(b"f").flat_map(|x| x).collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(
items.clone().into_iter().skip(3).take(3).collect::<Vec<(&[u8], &[u8])>>(),
cursor.iter_dup_of(b"b").collect::<Result<Vec<_>>>().unwrap()
);
assert_eq!(0, cursor.iter_dup_of(b"foo").count()); assert_eq!(0, cursor.iter_dup_of(b"foo").count());
} }
@ -601,11 +665,9 @@ mod test {
cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap(); cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap();
cursor.put(b"key3", b"val3", WriteFlags::empty()).unwrap(); cursor.put(b"key3", b"val3", WriteFlags::empty()).unwrap();
assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), assert_eq!((Some(&b"key3"[..]), &b"val3"[..]), cursor.get(None, None, MDB_GET_CURRENT).unwrap());
cursor.get(None, None, MDB_GET_CURRENT).unwrap());
cursor.del(WriteFlags::empty()).unwrap(); cursor.del(WriteFlags::empty()).unwrap();
assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), assert_eq!((Some(&b"key2"[..]), &b"val2"[..]), cursor.get(None, None, MDB_LAST).unwrap());
cursor.get(None, None, MDB_LAST).unwrap());
} }
} }

@ -4,7 +4,10 @@ use std::ptr;
use ffi; use ffi;
use error::{Result, lmdb_result}; use error::{
lmdb_result,
Result,
};
/// A handle to an individual database in an environment. /// A handle to an individual database in an environment.
/// ///
@ -15,20 +18,22 @@ pub struct Database {
} }
impl Database { impl Database {
/// Opens a new database handle in the given transaction. /// Opens a new database handle in the given transaction.
/// ///
/// Prefer using `Environment::open_db`, `Environment::create_db`, `TransactionExt::open_db`, /// Prefer using `Environment::open_db`, `Environment::create_db`, `TransactionExt::open_db`,
/// or `RwTransaction::create_db`. /// or `RwTransaction::create_db`.
pub(crate) unsafe fn new(txn: *mut ffi::MDB_txn, pub(crate) unsafe fn new(txn: *mut ffi::MDB_txn, name: Option<&str>, flags: c_uint) -> Result<Database> {
name: Option<&str>,
flags: c_uint)
-> Result<Database> {
let c_name = name.map(|n| CString::new(n).unwrap()); let c_name = name.map(|n| CString::new(n).unwrap());
let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() }; let name_ptr = if let Some(ref c_name) = c_name {
c_name.as_ptr()
} else {
ptr::null()
};
let mut dbi: ffi::MDB_dbi = 0; let mut dbi: ffi::MDB_dbi = 0;
lmdb_result(ffi::mdb_dbi_open(txn, name_ptr, flags, &mut dbi))?; lmdb_result(ffi::mdb_dbi_open(txn, name_ptr, flags, &mut dbi))?;
Ok(Database { dbi: dbi }) Ok(Database {
dbi: dbi,
})
} }
pub(crate) fn freelist_db() -> Database { pub(crate) fn freelist_db() -> Database {

@ -1,22 +1,44 @@
use libc::{c_uint, size_t}; use libc::{
use std::{fmt, ptr, result, mem}; c_uint,
size_t,
};
use std::ffi::CString; use std::ffi::CString;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
#[cfg(windows)] #[cfg(windows)]
use std::ffi::OsStr; use std::ffi::OsStr;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
use std::path::Path; use std::path::Path;
use std::sync::Mutex; use std::sync::Mutex;
use std::{
fmt,
mem,
ptr,
result,
};
use ffi; use ffi;
use byteorder::{ByteOrder, NativeEndian}; use byteorder::{
ByteOrder,
NativeEndian,
};
use cursor::Cursor; use cursor::Cursor;
use error::{Error, Result, lmdb_result};
use database::Database; use database::Database;
use transaction::{RoTransaction, RwTransaction, Transaction}; use error::{
use flags::{DatabaseFlags, EnvironmentFlags}; lmdb_result,
Error,
Result,
};
use flags::{
DatabaseFlags,
EnvironmentFlags,
};
use transaction::{
RoTransaction,
RwTransaction,
Transaction,
};
#[cfg(windows)] #[cfg(windows)]
/// Adding a 'missing' trait from windows OsStrExt /// Adding a 'missing' trait from windows OsStrExt
@ -39,14 +61,13 @@ pub struct 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.
pub fn new() -> EnvironmentBuilder { pub fn new() -> EnvironmentBuilder {
EnvironmentBuilder { EnvironmentBuilder {
flags: EnvironmentFlags::empty(), flags: EnvironmentFlags::empty(),
max_readers: None, max_readers: None,
max_dbs: None, max_dbs: None,
map_size: None map_size: None,
} }
} }
@ -95,10 +116,7 @@ impl Environment {
/// ///
/// This function will fail with `Error::BadRslot` if called by a thread with an open /// This function will fail with `Error::BadRslot` if called by a thread with an open
/// transaction. /// transaction.
pub fn create_db<'env>(&'env self, pub fn create_db<'env>(&'env self, name: Option<&str>, flags: DatabaseFlags) -> Result<Database> {
name: Option<&str>,
flags: DatabaseFlags)
-> Result<Database> {
let mutex = self.dbi_open_mutex.lock(); let mutex = self.dbi_open_mutex.lock();
let txn = self.begin_rw_txn()?; let txn = self.begin_rw_txn()?;
let db = unsafe { txn.create_db(name, flags)? }; let db = unsafe { txn.create_db(name, flags)? };
@ -137,7 +155,14 @@ impl Environment {
/// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`. /// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`.
pub fn sync(&self, force: bool) -> Result<()> { pub fn sync(&self, force: bool) -> Result<()> {
unsafe { unsafe {
lmdb_result(ffi::mdb_env_sync(self.env(), if force { 1 } else { 0 })) lmdb_result(ffi::mdb_env_sync(
self.env(),
if force {
1
} else {
0
},
))
} }
} }
@ -239,9 +264,7 @@ impl Environment {
/// with size 0 to update the environment. Otherwise, new transaction creation /// with size 0 to update the environment. Otherwise, new transaction creation
/// will fail with `Error::MapResized`. /// will fail with `Error::MapResized`.
pub fn set_map_size(&self, size: size_t) -> Result<()> { pub fn set_map_size(&self, size: size_t) -> Result<()> {
unsafe { unsafe { lmdb_result(ffi::mdb_env_set_mapsize(self.env(), size)) }
lmdb_result(ffi::mdb_env_set_mapsize(self.env(), size))
}
} }
} }
@ -253,9 +276,7 @@ pub struct Stat(ffi::MDB_stat);
impl Stat { impl Stat {
/// Create a new Stat with zero'd inner struct `ffi::MDB_stat`. /// Create a new Stat with zero'd inner struct `ffi::MDB_stat`.
pub(crate) fn new() -> Stat { pub(crate) fn new() -> Stat {
unsafe { unsafe { Stat(mem::zeroed()) }
Stat(mem::zeroed())
}
} }
/// Returns a mut pointer to `ffi::MDB_stat`. /// Returns a mut pointer to `ffi::MDB_stat`.
@ -368,7 +389,6 @@ pub struct EnvironmentBuilder {
} }
impl EnvironmentBuilder { impl EnvironmentBuilder {
/// Open an environment. /// Open an environment.
/// ///
/// On UNIX, the database files will be opened with 644 permissions. /// On UNIX, the database files will be opened with 644 permissions.
@ -390,26 +410,27 @@ impl EnvironmentBuilder {
unsafe { unsafe {
lmdb_try!(ffi::mdb_env_create(&mut env)); lmdb_try!(ffi::mdb_env_create(&mut env));
if let Some(max_readers) = self.max_readers { if let Some(max_readers) = self.max_readers {
lmdb_try_with_cleanup!(ffi::mdb_env_set_maxreaders(env, max_readers), lmdb_try_with_cleanup!(ffi::mdb_env_set_maxreaders(env, max_readers), ffi::mdb_env_close(env))
ffi::mdb_env_close(env))
} }
if let Some(max_dbs) = self.max_dbs { if let Some(max_dbs) = self.max_dbs {
lmdb_try_with_cleanup!(ffi::mdb_env_set_maxdbs(env, max_dbs), lmdb_try_with_cleanup!(ffi::mdb_env_set_maxdbs(env, max_dbs), ffi::mdb_env_close(env))
ffi::mdb_env_close(env))
} }
if let Some(map_size) = self.map_size { if let Some(map_size) = self.map_size {
lmdb_try_with_cleanup!(ffi::mdb_env_set_mapsize(env, map_size), lmdb_try_with_cleanup!(ffi::mdb_env_set_mapsize(env, map_size), ffi::mdb_env_close(env))
ffi::mdb_env_close(env))
} }
let path = match CString::new(path.as_os_str().as_bytes()) { let path = match CString::new(path.as_os_str().as_bytes()) {
Ok(path) => path, Ok(path) => path,
Err(..) => return Err(::Error::Invalid), Err(..) => return Err(::Error::Invalid),
}; };
lmdb_try_with_cleanup!(ffi::mdb_env_open(env, path.as_ptr(), self.flags.bits(), mode), lmdb_try_with_cleanup!(
ffi::mdb_env_close(env)); ffi::mdb_env_open(env, path.as_ptr(), self.flags.bits(), mode),
ffi::mdb_env_close(env)
);
} }
Ok(Environment { env: env, dbi_open_mutex: Mutex::new(()) }) Ok(Environment {
env: env,
dbi_open_mutex: Mutex::new(()),
})
} }
/// Sets the provided options in the environment. /// Sets the provided options in the environment.
@ -465,8 +486,11 @@ mod test {
extern crate byteorder; extern crate byteorder;
use self::byteorder::{
ByteOrder,
LittleEndian,
};
use tempdir::TempDir; use tempdir::TempDir;
use self::byteorder::{ByteOrder, LittleEndian};
use flags::*; use flags::*;
@ -477,34 +501,30 @@ mod test {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
// opening non-existent env with read-only should fail // opening non-existent env with read-only should fail
assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY) assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).is_err());
.open(dir.path())
.is_err());
// opening non-existent env should succeed // opening non-existent env should succeed
assert!(Environment::new().open(dir.path()).is_ok()); assert!(Environment::new().open(dir.path()).is_ok());
// opening env with read-only should succeed // opening env with read-only should succeed
assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY) assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).is_ok());
.open(dir.path())
.is_ok());
} }
#[test] #[test]
fn test_begin_txn() { fn test_begin_txn() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
{ // writable environment {
// writable environment
let env = Environment::new().open(dir.path()).unwrap(); let env = Environment::new().open(dir.path()).unwrap();
assert!(env.begin_rw_txn().is_ok()); assert!(env.begin_rw_txn().is_ok());
assert!(env.begin_ro_txn().is_ok()); assert!(env.begin_ro_txn().is_ok());
} }
{ // read-only environment {
let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY) // read-only environment
.open(dir.path()) let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).unwrap();
.unwrap();
assert!(env.begin_rw_txn().is_err()); assert!(env.begin_rw_txn().is_err());
assert!(env.begin_ro_txn().is_ok()); assert!(env.begin_ro_txn().is_ok());
@ -514,9 +534,7 @@ mod test {
#[test] #[test]
fn test_open_db() { fn test_open_db() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let env = Environment::new().set_max_dbs(1) let env = Environment::new().set_max_dbs(1).open(dir.path()).unwrap();
.open(dir.path())
.unwrap();
assert!(env.open_db(None).is_ok()); assert!(env.open_db(None).is_ok());
assert!(env.open_db(Some("testdb")).is_err()); assert!(env.open_db(Some("testdb")).is_err());
@ -525,9 +543,7 @@ mod test {
#[test] #[test]
fn test_create_db() { fn test_create_db() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let env = Environment::new().set_max_dbs(11) let env = Environment::new().set_max_dbs(11).open(dir.path()).unwrap();
.open(dir.path())
.unwrap();
assert!(env.open_db(Some("testdb")).is_err()); assert!(env.open_db(Some("testdb")).is_err());
assert!(env.create_db(Some("testdb"), DatabaseFlags::empty()).is_ok()); assert!(env.create_db(Some("testdb"), DatabaseFlags::empty()).is_ok());
assert!(env.open_db(Some("testdb")).is_ok()) assert!(env.open_db(Some("testdb")).is_ok())
@ -536,12 +552,12 @@ mod test {
#[test] #[test]
fn test_close_database() { fn test_close_database() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let mut env = Environment::new().set_max_dbs(10) let mut env = Environment::new().set_max_dbs(10).open(dir.path()).unwrap();
.open(dir.path())
.unwrap();
let db = env.create_db(Some("db"), DatabaseFlags::empty()).unwrap(); let db = env.create_db(Some("db"), DatabaseFlags::empty()).unwrap();
unsafe { env.close_db(db); } unsafe {
env.close_db(db);
}
assert!(env.open_db(Some("db")).is_ok()); assert!(env.open_db(Some("db")).is_ok());
} }
@ -551,10 +567,9 @@ mod test {
{ {
let env = Environment::new().open(dir.path()).unwrap(); let env = Environment::new().open(dir.path()).unwrap();
assert!(env.sync(true).is_ok()); assert!(env.sync(true).is_ok());
} { }
let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY) {
.open(dir.path()) let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).unwrap();
.unwrap();
assert!(env.sync(true).is_err()); assert!(env.sync(true).is_err());
} }
} }
@ -598,10 +613,7 @@ mod test {
fn test_info() { fn test_info() {
let map_size = 1024 * 1024; let map_size = 1024 * 1024;
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let env = Environment::new() let env = Environment::new().set_map_size(map_size).open(dir.path()).unwrap();
.set_map_size(map_size)
.open(dir.path())
.unwrap();
let info = env.info().unwrap(); let info = env.info().unwrap();
assert_eq!(info.map_size(), map_size); assert_eq!(info.map_size(), map_size);

@ -2,7 +2,11 @@ use libc::c_int;
use std::error::Error as StdError; use std::error::Error as StdError;
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::{fmt, result, str}; use std::{
fmt,
result,
str,
};
use ffi; use ffi;
@ -54,7 +58,6 @@ pub enum Error {
} }
impl Error { impl Error {
/// Converts a raw error code to an `Error`. /// Converts a raw error code to an `Error`.
pub fn from_err_code(err_code: c_int) -> Error { pub fn from_err_code(err_code: c_int) -> Error {
match err_code { match err_code {
@ -146,9 +149,7 @@ mod test {
#[test] #[test]
fn test_description() { fn test_description() {
assert_eq!("Permission denied", assert_eq!("Permission denied", Error::from_err_code(13).description());
Error::from_err_code(13).description()); assert_eq!("MDB_NOTFOUND: No matching key/data pair found", Error::NotFound.description());
assert_eq!("MDB_NOTFOUND: No matching key/data pair found",
Error::NotFound.description());
} }
} }

@ -4,19 +4,21 @@
#![deny(missing_docs)] #![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/lmdb-rkv/0.12.0")] #![doc(html_root_url = "https://docs.rs/lmdb-rkv/0.12.0")]
extern crate byteorder;
extern crate libc; extern crate libc;
extern crate lmdb_rkv_sys as ffi; extern crate lmdb_rkv_sys as ffi;
extern crate byteorder;
#[cfg(test)] extern crate tempdir; #[cfg(test)]
#[macro_use] extern crate bitflags; extern crate tempdir;
#[macro_use]
extern crate bitflags;
pub use cursor::{ pub use cursor::{
Cursor, Cursor,
RoCursor,
RwCursor,
Iter, Iter,
IterDup, IterDup,
RoCursor,
RwCursor,
}; };
pub use database::Database; pub use database::Database;
pub use environment::{ pub use environment::{
@ -25,7 +27,10 @@ pub use environment::{
Info, Info,
Stat, Stat,
}; };
pub use error::{Error, Result}; pub use error::{
Error,
Result,
};
pub use flags::*; pub use flags::*;
pub use transaction::{ pub use transaction::{
InactiveTransaction, InactiveTransaction,
@ -35,37 +40,40 @@ pub use transaction::{
}; };
macro_rules! lmdb_try { macro_rules! lmdb_try {
($expr:expr) => ({ ($expr:expr) => {{
match $expr { match $expr {
::ffi::MDB_SUCCESS => (), ::ffi::MDB_SUCCESS => (),
err_code => return Err(::Error::from_err_code(err_code)), err_code => return Err(::Error::from_err_code(err_code)),
} }
}) }};
} }
macro_rules! lmdb_try_with_cleanup { macro_rules! lmdb_try_with_cleanup {
($expr:expr, $cleanup:expr) => ({ ($expr:expr, $cleanup:expr) => {{
match $expr { match $expr {
::ffi::MDB_SUCCESS => (), ::ffi::MDB_SUCCESS => (),
err_code => { err_code => {
let _ = $cleanup; let _ = $cleanup;
return Err(::Error::from_err_code(err_code)) return Err(::Error::from_err_code(err_code));
}, },
} }
}) }};
} }
mod flags;
mod cursor; mod cursor;
mod database; mod database;
mod environment; mod environment;
mod error; mod error;
mod flags;
mod transaction; mod transaction;
#[cfg(test)] #[cfg(test)]
mod test_utils { mod test_utils {
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{
ByteOrder,
LittleEndian,
};
use tempdir::TempDir; use tempdir::TempDir;
use super::*; use super::*;
@ -91,10 +99,7 @@ mod test_utils {
let mut value = [0u8; 8]; let mut value = [0u8; 8];
LittleEndian::write_u64(&mut value, height); LittleEndian::write_u64(&mut value, height);
let mut tx = env.begin_rw_txn().expect("begin_rw_txn"); let mut tx = env.begin_rw_txn().expect("begin_rw_txn");
tx.put(index, tx.put(index, &HEIGHT_KEY, &value, WriteFlags::empty()).expect("tx.put");
&HEIGHT_KEY,
&value,
WriteFlags::empty()).expect("tx.put");
tx.commit().expect("tx.commit") tx.commit().expect("tx.commit")
} }
} }

@ -1,20 +1,43 @@
use libc::{c_uint, c_void, size_t}; use libc::{
use std::{fmt, mem, ptr, result, slice}; c_uint,
c_void,
size_t,
};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::{
fmt,
mem,
ptr,
result,
slice,
};
use ffi; use ffi;
use cursor::{RoCursor, RwCursor}; use cursor::{
use environment::{Environment, Stat}; RoCursor,
RwCursor,
};
use database::Database; use database::Database;
use error::{Error, Result, lmdb_result}; use environment::{
use flags::{DatabaseFlags, EnvironmentFlags, WriteFlags}; Environment,
Stat,
};
use error::{
lmdb_result,
Error,
Result,
};
use flags::{
DatabaseFlags,
EnvironmentFlags,
WriteFlags,
};
/// An LMDB transaction. /// An LMDB transaction.
/// ///
/// All database operations require a transaction. /// All database operations require a transaction.
pub trait Transaction: Sized { pub trait Transaction: Sized {
/// Returns a raw pointer to the underlying LMDB transaction. /// Returns a raw pointer to the underlying LMDB transaction.
/// ///
/// The caller **must** ensure that the pointer is not used after the /// The caller **must** ensure that the pointer is not used after the
@ -68,22 +91,22 @@ pub trait Transaction : Sized {
/// returned. Retrieval of other items requires the use of /// returned. Retrieval of other items requires the use of
/// `Transaction::cursor_get`. If the item is not in the database, then /// `Transaction::cursor_get`. If the item is not in the database, then
/// `Error::NotFound` will be returned. /// `Error::NotFound` will be returned.
fn get<'txn, K>(&'txn self, fn get<'txn, K>(&'txn self, database: Database, key: &K) -> Result<&'txn [u8]>
database: Database, where
key: &K) K: AsRef<[u8]>,
-> Result<&'txn [u8]> {
where K: AsRef<[u8]> {
let key = key.as_ref(); let key = key.as_ref();
let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t, let mut key_val: ffi::MDB_val = ffi::MDB_val {
mv_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: 0, mv_data: key.as_ptr() as *mut c_void,
mv_data: ptr::null_mut() }; };
let mut data_val: ffi::MDB_val = ffi::MDB_val {
mv_size: 0,
mv_data: ptr::null_mut(),
};
unsafe { unsafe {
match ffi::mdb_get(self.txn(), database.dbi(), &mut key_val, &mut data_val) { match ffi::mdb_get(self.txn(), database.dbi(), &mut key_val, &mut data_val) {
ffi::MDB_SUCCESS => { ffi::MDB_SUCCESS => Ok(slice::from_raw_parts(data_val.mv_data as *const u8, data_val.mv_size as usize)),
Ok(slice::from_raw_parts(data_val.mv_data as *const u8,
data_val.mv_size as usize))
},
err_code => Err(Error::from_err_code(err_code)), err_code => Err(Error::from_err_code(err_code)),
} }
} }
@ -132,14 +155,16 @@ impl <'env> Drop for RoTransaction<'env> {
} }
impl<'env> RoTransaction<'env> { impl<'env> RoTransaction<'env> {
/// Creates a new read-only transaction in the given environment. Prefer /// Creates a new read-only transaction in the given environment. Prefer
/// using `Environment::begin_ro_txn`. /// using `Environment::begin_ro_txn`.
pub(crate) fn new(env: &'env Environment) -> Result<RoTransaction<'env>> { pub(crate) fn new(env: &'env Environment) -> Result<RoTransaction<'env>> {
let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); let mut txn: *mut ffi::MDB_txn = ptr::null_mut();
unsafe { unsafe {
lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), ffi::MDB_RDONLY, &mut txn))?; lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), ffi::MDB_RDONLY, &mut txn))?;
Ok(RoTransaction { txn: txn, _marker: PhantomData }) Ok(RoTransaction {
txn: txn,
_marker: PhantomData,
})
} }
} }
@ -161,7 +186,10 @@ impl <'env> RoTransaction<'env> {
mem::forget(self); mem::forget(self);
ffi::mdb_txn_reset(txn) ffi::mdb_txn_reset(txn)
}; };
InactiveTransaction { txn: txn, _marker: PhantomData } InactiveTransaction {
txn: txn,
_marker: PhantomData,
}
} }
} }
@ -190,7 +218,6 @@ impl <'env> Drop for InactiveTransaction<'env> {
} }
impl<'env> InactiveTransaction<'env> { impl<'env> InactiveTransaction<'env> {
/// Renews the inactive transaction, returning an active read-only /// Renews the inactive transaction, returning an active read-only
/// transaction. /// transaction.
/// ///
@ -202,7 +229,10 @@ impl <'env> InactiveTransaction<'env> {
mem::forget(self); mem::forget(self);
lmdb_result(ffi::mdb_txn_renew(txn))? lmdb_result(ffi::mdb_txn_renew(txn))?
}; };
Ok(RoTransaction { txn: txn, _marker: PhantomData }) Ok(RoTransaction {
txn: txn,
_marker: PhantomData,
})
} }
} }
@ -225,17 +255,16 @@ impl <'env> Drop for RwTransaction<'env> {
} }
impl<'env> RwTransaction<'env> { impl<'env> RwTransaction<'env> {
/// Creates a new read-write transaction in the given environment. Prefer /// Creates a new read-write transaction in the given environment. Prefer
/// using `Environment::begin_ro_txn`. /// using `Environment::begin_ro_txn`.
pub(crate) fn new(env: &'env Environment) -> Result<RwTransaction<'env>> { pub(crate) fn new(env: &'env Environment) -> Result<RwTransaction<'env>> {
let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); let mut txn: *mut ffi::MDB_txn = ptr::null_mut();
unsafe { unsafe {
lmdb_result(ffi::mdb_txn_begin(env.env(), lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), EnvironmentFlags::empty().bits(), &mut txn))?;
ptr::null_mut(), Ok(RwTransaction {
EnvironmentFlags::empty().bits(), txn: txn,
&mut txn))?; _marker: PhantomData,
Ok(RwTransaction { txn: txn, _marker: PhantomData }) })
} }
} }
@ -271,51 +300,55 @@ impl <'env> RwTransaction<'env> {
/// behavior is to enter the new key/data pair, replacing any previously /// behavior is to enter the new key/data pair, replacing any previously
/// existing key if duplicates are disallowed, or adding a duplicate data /// existing key if duplicates are disallowed, or adding a duplicate data
/// item if duplicates are allowed (`DatabaseFlags::DUP_SORT`). /// item if duplicates are allowed (`DatabaseFlags::DUP_SORT`).
pub fn put<K, D>(&mut self, pub fn put<K, D>(&mut self, database: Database, key: &K, data: &D, flags: WriteFlags) -> Result<()>
database: Database, where
key: &K, K: AsRef<[u8]>,
data: &D, D: AsRef<[u8]>,
flags: WriteFlags) {
-> Result<()>
where K: AsRef<[u8]>, D: AsRef<[u8]> {
let key = key.as_ref(); let key = key.as_ref();
let data = data.as_ref(); let data = data.as_ref();
let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t, let mut key_val: ffi::MDB_val = ffi::MDB_val {
mv_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: data.len() as size_t, mv_data: key.as_ptr() as *mut c_void,
mv_data: data.as_ptr() as *mut c_void }; };
unsafe { let mut data_val: ffi::MDB_val = ffi::MDB_val {
lmdb_result(ffi::mdb_put(self.txn(), mv_size: data.len() as size_t,
database.dbi(), mv_data: data.as_ptr() as *mut c_void,
&mut key_val, };
&mut data_val, unsafe { lmdb_result(ffi::mdb_put(self.txn(), database.dbi(), &mut key_val, &mut data_val, flags.bits())) }
flags.bits()))
}
} }
/// Returns a buffer which can be used to write a value into the item at the /// Returns a buffer which can be used to write a value into the item at the
/// given key and with the given length. The buffer must be completely /// given key and with the given length. The buffer must be completely
/// filled by the caller. /// filled by the caller.
pub fn reserve<'txn, K>(&'txn mut self, pub fn reserve<'txn, K>(
&'txn mut self,
database: Database, database: Database,
key: &K, key: &K,
len: size_t, len: size_t,
flags: WriteFlags) flags: WriteFlags,
-> Result<&'txn mut [u8]> ) -> Result<&'txn mut [u8]>
where K: AsRef<[u8]> { where
K: AsRef<[u8]>,
{
let key = key.as_ref(); let key = key.as_ref();
let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t, let mut key_val: ffi::MDB_val = ffi::MDB_val {
mv_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let mut data_val: ffi::MDB_val = ffi::MDB_val { mv_size: len, mv_data: key.as_ptr() as *mut c_void,
mv_data: ptr::null_mut::<c_void>() }; };
let mut data_val: ffi::MDB_val = ffi::MDB_val {
mv_size: len,
mv_data: ptr::null_mut::<c_void>(),
};
unsafe { unsafe {
lmdb_result(ffi::mdb_put(self.txn(), lmdb_result(ffi::mdb_put(
self.txn(),
database.dbi(), database.dbi(),
&mut key_val, &mut key_val,
&mut data_val, &mut data_val,
flags.bits() | ffi::MDB_RESERVE))?; flags.bits() | ffi::MDB_RESERVE,
Ok(slice::from_raw_parts_mut(data_val.mv_data as *mut u8, ))?;
data_val.mv_size as usize)) Ok(slice::from_raw_parts_mut(data_val.mv_data as *mut u8, data_val.mv_size as usize))
} }
} }
@ -329,34 +362,24 @@ impl <'env> RwTransaction<'env> {
/// `Some` only the matching data item will be deleted. This function will /// `Some` only the matching data item will be deleted. This function will
/// return `Error::NotFound` if the specified key/data pair is not in the /// return `Error::NotFound` if the specified key/data pair is not in the
/// database. /// database.
pub fn del<K>(&mut self, pub fn del<K>(&mut self, database: Database, key: &K, data: Option<&[u8]>) -> Result<()>
database: Database, where
key: &K, K: AsRef<[u8]>,
data: Option<&[u8]>) {
-> Result<()>
where K: AsRef<[u8]> {
let key = key.as_ref(); let key = key.as_ref();
let mut key_val: ffi::MDB_val = ffi::MDB_val { mv_size: key.len() as size_t, let mut key_val: ffi::MDB_val = ffi::MDB_val {
mv_data: key.as_ptr() as *mut c_void }; mv_size: key.len() as size_t,
let data_val: Option<ffi::MDB_val> = mv_data: key.as_ptr() as *mut c_void,
data.map(|data| ffi::MDB_val { mv_size: data.len() as size_t, };
mv_data: data.as_ptr() as *mut c_void }); let data_val: Option<ffi::MDB_val> = data.map(|data| ffi::MDB_val {
mv_size: data.len() as size_t,
mv_data: data.as_ptr() as *mut c_void,
});
if let Some(mut d) = data_val { if let Some(mut d) = data_val {
unsafe { unsafe { lmdb_result(ffi::mdb_del(self.txn(), database.dbi(), &mut key_val, &mut d)) }
lmdb_result(ffi::mdb_del(self.txn(),
database.dbi(),
&mut key_val,
&mut d))
}
} else { } else {
unsafe { unsafe { lmdb_result(ffi::mdb_del(self.txn(), database.dbi(), &mut key_val, ptr::null_mut())) }
lmdb_result(ffi::mdb_del(self.txn(),
database.dbi(),
&mut key_val,
ptr::null_mut()))
}
} }
} }
@ -382,7 +405,10 @@ impl <'env> RwTransaction<'env> {
let env: *mut ffi::MDB_env = ffi::mdb_txn_env(self.txn()); let env: *mut ffi::MDB_env = ffi::mdb_txn_env(self.txn());
ffi::mdb_txn_begin(env, self.txn(), 0, &mut nested); ffi::mdb_txn_begin(env, self.txn(), 0, &mut nested);
} }
Ok(RwTransaction { txn: nested, _marker: PhantomData }) Ok(RwTransaction {
txn: nested,
_marker: PhantomData,
})
} }
} }
@ -396,16 +422,21 @@ impl <'env> Transaction for RwTransaction<'env> {
mod test { mod test {
use std::io::Write; use std::io::Write;
use std::sync::{Arc, Barrier}; use std::sync::{
use std::thread::{self, JoinHandle}; Arc,
Barrier,
};
use std::thread::{
self,
JoinHandle,
};
use tempdir::TempDir; use tempdir::TempDir;
use environment::*;
use error::*;
use flags::*;
use super::*; use super::*;
use cursor::Cursor; use cursor::Cursor;
use error::*;
use flags::*;
#[test] #[test]
fn test_put_get_del() { fn test_put_get_del() {
@ -453,7 +484,6 @@ mod test {
let iter = cur.iter_dup_of(b"key1"); let iter = cur.iter_dup_of(b"key1");
let vals = iter.map(|x| x.unwrap()).map(|(_, x)| x).collect::<Vec<_>>(); let vals = iter.map(|x| x.unwrap()).map(|(_, x)| x).collect::<Vec<_>>();
assert_eq!(vals, vec![b"val1", b"val2", b"val3"]); assert_eq!(vals, vec![b"val1", b"val2", b"val3"]);
} }
txn.commit().unwrap(); txn.commit().unwrap();
@ -475,7 +505,6 @@ mod test {
txn.commit().unwrap(); txn.commit().unwrap();
} }
#[test] #[test]
fn test_reserve() { fn test_reserve() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
@ -557,12 +586,10 @@ mod test {
assert_eq!(txn.get(db, b"key"), Err(Error::NotFound)); assert_eq!(txn.get(db, b"key"), Err(Error::NotFound));
} }
#[test] #[test]
fn test_drop_db() { fn test_drop_db() {
let dir = TempDir::new("test").unwrap(); let dir = TempDir::new("test").unwrap();
let env = Environment::new().set_max_dbs(2) let env = Environment::new().set_max_dbs(2).open(dir.path()).unwrap();
.open(dir.path()).unwrap();
let db = env.create_db(Some("test"), DatabaseFlags::empty()).unwrap(); let db = env.create_db(Some("test"), DatabaseFlags::empty()).unwrap();
{ {
@ -572,7 +599,9 @@ mod test {
} }
{ {
let mut txn = env.begin_rw_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
unsafe { txn.drop_db(db).unwrap(); } unsafe {
txn.drop_db(db).unwrap();
}
txn.commit().unwrap(); txn.commit().unwrap();
} }
@ -638,11 +667,7 @@ mod test {
threads.push(thread::spawn(move || { threads.push(thread::spawn(move || {
let db = writer_env.open_db(None).unwrap(); let db = writer_env.open_db(None).unwrap();
let mut txn = writer_env.begin_rw_txn().unwrap(); let mut txn = writer_env.begin_rw_txn().unwrap();
txn.put(db, txn.put(db, &format!("{}{}", key, i), &format!("{}{}", val, i), WriteFlags::empty()).unwrap();
&format!("{}{}", key, i),
&format!("{}{}", val, i),
WriteFlags::empty())
.unwrap();
txn.commit().is_ok() txn.commit().is_ok()
})); }));
} }
@ -652,8 +677,7 @@ mod test {
let txn = env.begin_ro_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
for i in 0..n { for i in 0..n {
assert_eq!(format!("{}{}", val, i).as_bytes(), assert_eq!(format!("{}{}", val, i).as_bytes(), txn.get(db, &format!("{}{}", key, i)).unwrap());
txn.get(db, &format!("{}{}", key, i)).unwrap());
} }
} }

Loading…
Cancel
Save