diff --git a/lmdb-sys/src/constants.rs b/lmdb-sys/src/constants.rs index 6fd7e7c..dfc5d69 100644 --- a/lmdb-sys/src/constants.rs +++ b/lmdb-sys/src/constants.rs @@ -1,208 +1,83 @@ use libc::{c_int, c_uint}; -bitflags! { - #[doc="Environment Options"] - #[deriving(Show)] - flags EnvironmentFlags: c_uint { - - #[doc="Use a fixed address for the mmap region. This flag must be specified"] - #[doc="when creating the environment, and is stored persistently in the environment."] - #[doc="If successful, the memory map will always reside at the same virtual address"] - #[doc="and pointers used to reference data items in the database will be constant"] - #[doc="across multiple invocations. This option may not always work, depending on"] - #[doc="how the operating system has allocated memory to shared libraries and other uses."] - #[doc="The feature is highly experimental."] - const MDB_FIXEDMAP = 0x01, - - #[doc="By default, LMDB creates its environment in a directory whose"] - #[doc="pathname is given in *path*, and creates its data and lock files"] - #[doc="under that directory. With this option, *path* is used as-is for"] - #[doc="the database main data file. The database lock file is the *path*"] - #[doc="with `-lock` appended."] - const MDB_NOSUBDIR = 0x4000, - - #[doc="Use a writeable memory map unless `MDB_RDONLY` is set. This is faster"] - #[doc="and uses fewer mallocs, but loses protection from application bugs"] - #[doc="like wild pointer writes and other bad updates into the database."] - #[doc="Incompatible with nested transactions."] - #[doc="Processes with and without `MDB_WRITEMAP` on the same environment do"] - #[doc="not cooperate well."] - const MDB_WRITEMAP = 0x80000, - - #[doc="Open the environment or transaction in read-only mode. No write operations"] - #[doc="will be allowed. When opening an environment, LMDB will still modify the lock"] - #[doc="file - except on read-only filesystems, where LMDB does not use locks."] - const MDB_RDONLY = 0x20000, - - #[doc="Flush system buffers to disk only once per transaction, omit the"] - #[doc="metadata flush. Defer that until the system flushes files to disk,"] - #[doc="or next non-`MDB_RDONLY` commit or #mdb_env_sync(). This optimization"] - #[doc="maintains database integrity, but a system crash may undo the last"] - #[doc="committed transaction. I.e. it preserves the ACI (atomicity,"] - #[doc="consistency, isolation) but not D (durability) database property."] - #[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."] - const MDB_NOMETASYNC = 0x40000, - - #[doc="Don't flush system buffers to disk when committing a transaction."] - #[doc="This optimization means a system crash can corrupt the database or"] - #[doc="lose the last transactions if buffers are not yet flushed to disk."] - #[doc="The risk is governed by how often the system flushes dirty buffers"] - #[doc="to disk and how often #mdb_env_sync() is called. However, if the"] - #[doc="filesystem preserves write order and the `MDB_WRITEMAP` flag is not"] - #[doc="used, transactions exhibit ACI (atomicity, consistency, isolation)"] - #[doc="properties and only lose D (durability). I.e. database integrity"] - #[doc="is maintained, but a system crash may undo the final transactions."] - #[doc="Note that (`MDB_NOSYNC | MDB_WRITEMAP`) leaves the system with no"] - #[doc="hint for when to write transactions to disk, unless #mdb_env_sync()"] - #[doc="is called. (`MDB_MAPASYNC | MDB_WRITEMAP`) may be preferable."] - #[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."] - const MDB_NOSYNC = 0x10000, - - #[doc="When using `MDB_WRITEMAP`, use asynchronous flushes to disk."] - #[doc="As with `MDB_NOSYNC`, a system crash can then corrupt the"] - #[doc="database or lose the last transactions. Calling #mdb_env_sync()"] - #[doc="ensures on-disk database integrity until next commit."] - #[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."] - const MDB_MAPASYNC = 0x100000, - - #[doc="Don't use Thread-Local Storage. Tie reader locktable slots to"] - #[doc="`MDB_txn` objects instead of to threads. I.e. #mdb_txn_reset() keeps"] - #[doc="the slot reseved for the #MDB_txn object. A thread may use parallel"] - #[doc="read-only transactions. A read-only transaction may span threads if"] - #[doc="the user synchronizes its use. Applications that multiplex many"] - #[doc="user threads over individual OS threads need this option. Such an"] - #[doc="application must also serialize the write transactions in an OS"] - #[doc="thread, since LMDB's write locking is unaware of the user threads."] - const MDB_NOTLS = 0x200000, - - #[doc="Don't do any locking. If concurrent access is anticipated, the"] - #[doc="caller must manage all concurrency itself. For proper operation"] - #[doc="the caller must enforce single-writer semantics, and must ensure"] - #[doc="that no readers are using old transactions while a writer is"] - #[doc="active. The simplest approach is to use an exclusive lock so that"] - #[doc="no readers may be active at all when a writer begins."] - const MDB_NOLOCK = 0x400000, - - #[doc="Turn off readahead. Most operating systems perform readahead on"] - #[doc="read requests by default. This option turns it off if the OS"] - #[doc="supports it. Turning it off may help random read performance"] - #[doc="when the DB is larger than RAM and system RAM is full."] - #[doc="The option is not implemented on Windows."] - const MDB_NORDAHEAD = 0x800000, - - #[doc="Don't initialize malloc'd memory before writing to unused spaces"] - #[doc="in the data file. By default, memory for pages written to the data"] - #[doc="file is obtained using malloc. While these pages may be reused in"] - #[doc="subsequent transactions, freshly malloc'd pages will be initialized"] - #[doc="to zeroes before use. This avoids persisting leftover data from other"] - #[doc="code (that used the heap and subsequently freed the memory) into the"] - #[doc="data file. Note that many other system libraries may allocate"] - #[doc="and free memory from the heap for arbitrary uses. E.g., stdio may"] - #[doc="use the heap for file I/O buffers. This initialization step has a"] - #[doc="modest performance cost so some applications may want to disable"] - #[doc="it using this flag. This option can be a problem for applications"] - #[doc="which handle sensitive data like passwords, and it makes memory"] - #[doc="checkers like Valgrind noisy. This flag is not needed with `MDB_WRITEMAP`,"] - #[doc="which writes directly to the mmap instead of using malloc for pages. The"] - #[doc="initialization is also skipped if `MDB_RESERVE` is used; the"] - #[doc="caller is expected to overwrite all of the memory that was"] - #[doc="reserved in that case."] - #[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."] - const MDB_NOMEMINIT = 0x1000000, - } -} - -bitflags! { - #[doc="Database Options"] - #[deriving(Show)] - flags DatabaseFlags: c_uint { - - #[doc="Keys are strings to be compared in reverse order, from the end"] - #[doc="of the strings to the beginning. By default, Keys are treated as strings and"] - #[doc="compared from beginning to end."] - const MDB_REVERSEKEY = 0x02, // 2 - - #[doc="Duplicate keys may be used in the database. (Or, from another perspective,"] - #[doc="keys may have multiple data items, stored in sorted order.) By default"] - #[doc="keys must be unique and may have only a single data item."] - const MDB_DUPSORT = 0x04, // 4 - - #[doc="Keys are binary integers in native byte order. Setting this option"] - #[doc="requires all keys to be the same size, typically sizeof(int)"] - #[doc="or sizeof(size_t)."] - const MDB_INTEGERKEY = 0x08, // 8 - - #[doc="This flag may only be used in combination with `MDB_DUPSORT`. This option"] - #[doc="tells the library that the data items for this database are all the same"] - #[doc="size, which allows further optimizations in storage and retrieval. When"] - #[doc="all data items are the same size, the `MDB_GET_MULTIPLE` and `MDB_NEXT_MULTIPLE`"] - #[doc="cursor operations may be used to retrieve multiple items at once."] - const MDB_DUPFIXED = 0x10, // 16 - - #[doc="This option specifies that duplicate data items are also integers, and"] - #[doc="should be sorted as such."] - const MDB_INTEGERDUP = 0x20, // 32 - - #[doc="This option specifies that duplicate data items should be compared as"] - #[doc="strings in reverse order."] - const MDB_REVERSEDUP = 0x40, // 64 - } -} - -/// Create the named database if it doesn't exist. This option is not -/// allowed in a read-only transaction or a read-only environment. +//////////////////////////////////////////////////////////////////////////////////////////////////// +//// Environment Flags +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// mmap at a fixed address (experimental) +pub const MDB_FIXEDMAP: c_uint = 0x01; +/// no environment directory +pub const MDB_NOSUBDIR: c_uint = 0x4000; +/// don't fsync after commit +pub const MDB_NOSYNC: c_uint = 0x10000; +/// read only +pub const MDB_RDONLY: c_uint = 0x20000; +/// don't fsync metapage after commit +pub const MDB_NOMETASYNC: c_uint = 0x40000; +/// use writable mmap +pub const MDB_WRITEMAP: c_uint = 0x80000; +/// use asynchronous msync when #MDB_WRITEMAP is used +pub const MDB_MAPASYNC: c_uint = 0x100000; +/// tie reader locktable slots to #MDB_txn objects instead of to threads +pub const MDB_NOTLS: c_uint = 0x200000; +/// don't do any locking, caller must manage their own locks +pub const MDB_NOLOCK: c_uint = 0x400000; +/// don't do readahead (no effect on Windows) +pub const MDB_NORDAHEAD: c_uint = 0x800000; +/// don't initialize malloc'd memory before writing to datafile +pub const MDB_NOMEMINIT: c_uint = 0x1000000; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//// Database Flags +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// use reverse string keys +pub const MDB_REVERSEKEY: c_uint = 0x02; +/// use sorted duplicates +pub const MDB_DUPSORT: c_uint = 0x04; +/// numeric keys in native byte order. The keys must all be of the same size. +pub const MDB_INTEGERKEY: c_uint = 0x08; +/// with `MDB_DUPSORT`, sorted dup items have fixed size. +pub const MDB_DUPFIXED: c_uint = 0x10; +/// with `MDB_DUPSORT`, dups are numeric in native byte order. +pub const MDB_INTEGERDUP: c_uint = 0x20; +/// with #MDB_DUPSORT, use reverse string dups. +pub const MDB_REVERSEDUP: c_uint = 0x40; +/// create DB if not already existing. pub const MDB_CREATE: c_uint = 0x40000; -bitflags! { - #[doc="Write Options"] - #[deriving(Show)] - flags WriteFlags: c_uint { - - #[doc="Enter the new key/data pair only if the key"] - #[doc="does not already appear in the database. The function will return"] - #[doc="`KeyExist` if the key already appears in the database, even if"] - #[doc="the database supports duplicates (`MDB_DUPSORT`). The `data`"] - #[doc="parameter will be set to point to the existing item."] - const MDB_NOOVERWRITE = 0x10, - - #[doc="Enter the new key/data pair only if it does not"] - #[doc="already appear in the database. This flag may only be specified"] - #[doc="if the database was opened with `MDB_DUPSORT`. The function will"] - #[doc="return `MDB_KEYEXIST` if the key/data pair already appears in the"] - #[doc="database."] - const MDB_NODUPDATA = 0x20, - - #[doc="For `Cursor::put`. Replace the item at the current cursor position."] - #[doc="The key parameter must match the current position. If using"] - #[doc="sorted duplicates (`MDB_DUPSORT`) the data item must still sort into the"] - #[doc="same position. This is intended to be used when the new data is the same"] - #[doc="size as the old. Otherwise it will simply perform a delete of the old"] - #[doc="record followed by an insert."] - const MDB_CURRENT = 0x40, - - #[doc="Append the given key/data pair to the end of the"] - #[doc="database. No key comparisons are performed. This option allows"] - #[doc="fast bulk loading when keys are already known to be in the"] - #[doc="correct order. Loading unsorted keys with this flag will cause"] - #[doc="data corruption."] - const MDB_APPEND = 0x20000, - - #[doc="Same as `MDB_APPEND`, but for sorted dup data."] - const MDB_APPENDDUP = 0x40000, - } -} - -/// Reserve space for data of the given size, but don't copy the given data. Instead, return a -/// pointer to the reserved space, which the caller can fill in later - before the next update -/// operation or the transaction ends. This saves an extra memcpy if the data is being generated -/// later. LMDB does nothing else with this memory, the caller is expected to modify all of the -/// space requested. +//////////////////////////////////////////////////////////////////////////////////////////////////// +//// Write Flags +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// For put: Don't write if the key already exists. +pub const MDB_NOOVERWRITE: c_uint = 0x10; +/// Only for `MDB_DUPSORT`. +/// +/// For put: don't write if the key and data pair already exist. +/// For `mdb_cursor_del`: remove all duplicate data items. +pub const MDB_NODUPDATA: c_uint = 0x20; +/// For `mdb_cursor_put`: overwrite the current key/data pair. +pub const MDB_CURRENT: c_uint = 0x40; +/// For put: Just reserve space for data, don't copy it. Return a pointer to the reserved space. pub const MDB_RESERVE: c_uint = 0x10000; +/// Data is being appended, don't split full pages. +pub const MDB_APPEND: c_uint = 0x20000; +/// Duplicate data is being appended, don't split full pages. +pub const MDB_APPENDDUP: c_uint = 0x40000; +/// Store multiple data items in one call. Only for #MDB_DUPFIXED. +pub const MDB_MULTIPLE: c_uint = 0x80000; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//// Copy Flags +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// Compacting copy: Omit free space from copy, and renumber all pages sequentially. +pub const MDB_CP_COMPACT: c_uint = 0x01; -/////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// //// Return Codes -/////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// /// Successful result. pub const MDB_SUCCESS: c_int = 0; diff --git a/src/cursor.rs b/src/cursor.rs index 682743f..6eeec94 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -2,10 +2,11 @@ use libc::{c_void, size_t, c_uint}; use std::{mem, ptr, raw}; use std::kinds::marker; +use ffi; + use database::Database; use error::{LmdbResult, lmdb_result, LmdbError}; -use ffi; -use ffi::{MDB_cursor, mdb_cursor_open, MDB_val, WriteFlags}; +use flags::WriteFlags; use transaction::Transaction; /// An LMDB cursor. @@ -13,9 +14,10 @@ pub trait Cursor<'txn> { /// Returns a raw pointer to the underlying LMDB cursor. /// /// The caller **must** ensure that the pointer is not used after the lifetime of the cursor. - fn cursor(&self) -> *mut MDB_cursor; + fn cursor(&self) -> *mut ffi::MDB_cursor; } +/// Cursor extension methods. pub trait CursorExt<'txn> : Cursor<'txn> { /// Retrieves a key/data pair from the cursor. Depending on the cursor op, the current key is @@ -49,14 +51,14 @@ impl<'txn, T> CursorExt<'txn> for T where T: Cursor<'txn> {} /// A read-only cursor for navigating items within a database. pub struct RoCursor<'txn> { - cursor: *mut MDB_cursor, + cursor: *mut ffi::MDB_cursor, _no_sync: marker::NoSync, _no_send: marker::NoSend, _contravariant: marker::ContravariantLifetime<'txn>, } impl <'txn> Cursor<'txn> for RoCursor<'txn> { - fn cursor(&self) -> *mut MDB_cursor { + fn cursor(&self) -> *mut ffi::MDB_cursor { self.cursor } } @@ -74,8 +76,8 @@ impl <'txn> RoCursor<'txn> { /// `Transaction::open_cursor()`. #[doc(hidden)] pub fn new(txn: &'txn Transaction, db: Database) -> LmdbResult> { - let mut cursor: *mut MDB_cursor = ptr::null_mut(); - unsafe { try!(lmdb_result(mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))); } + let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); + unsafe { try!(lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))); } Ok(RoCursor { cursor: cursor, _no_sync: marker::NoSync, @@ -87,14 +89,14 @@ impl <'txn> RoCursor<'txn> { /// A read-only cursor for navigating items within a database. pub struct RwCursor<'txn> { - cursor: *mut MDB_cursor, + cursor: *mut ffi::MDB_cursor, _no_sync: marker::NoSync, _no_send: marker::NoSend, _contravariant: marker::ContravariantLifetime<'txn>, } impl <'txn> Cursor<'txn> for RwCursor<'txn> { - fn cursor(&self) -> *mut MDB_cursor { + fn cursor(&self) -> *mut ffi::MDB_cursor { self.cursor } } @@ -112,8 +114,8 @@ impl <'txn> RwCursor<'txn> { /// `WriteTransaction::open_write_cursor()`. #[doc(hidden)] pub fn new(txn: &'txn Transaction, db: Database) -> LmdbResult> { - let mut cursor: *mut MDB_cursor = ptr::null_mut(); - unsafe { try!(lmdb_result(mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))); } + let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); + unsafe { try!(lmdb_result(ffi::mdb_cursor_open(txn.txn(), db.dbi(), &mut cursor))); } Ok(RwCursor { cursor: cursor, _no_sync: marker::NoSync, @@ -154,18 +156,18 @@ impl <'txn> RwCursor<'txn> { } } -unsafe fn slice_to_val(slice: Option<&[u8]>) -> MDB_val { +unsafe fn slice_to_val(slice: Option<&[u8]>) -> ffi::MDB_val { match slice { Some(slice) => - MDB_val { mv_size: slice.len() as size_t, - mv_data: slice.as_ptr() as *mut c_void }, + ffi::MDB_val { mv_size: slice.len() as size_t, + mv_data: slice.as_ptr() as *mut c_void }, None => - MDB_val { mv_size: 0, - mv_data: ptr::null_mut() }, + ffi::MDB_val { mv_size: 0, + mv_data: ptr::null_mut() }, } } -unsafe fn val_to_slice<'a>(val: MDB_val) -> &'a [u8] { +unsafe fn val_to_slice<'a>(val: ffi::MDB_val) -> &'a [u8] { mem::transmute(raw::Slice { data: val.mv_data as *const u8, len: val.mv_size as uint @@ -173,7 +175,7 @@ unsafe fn val_to_slice<'a>(val: MDB_val) -> &'a [u8] { } pub struct Items<'txn> { - cursor: *mut MDB_cursor, + cursor: *mut ffi::MDB_cursor, op: c_uint, next_op: c_uint, } @@ -189,8 +191,8 @@ impl <'txn> Items<'txn> { impl <'txn> Iterator<(&'txn [u8], &'txn [u8])> for Items<'txn> { fn next(&mut self) -> Option<(&'txn [u8], &'txn [u8])> { - let mut key = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; - let mut data = 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() }; unsafe { let err_code = ffi::mdb_cursor_get(self.cursor, &mut key, &mut data, self.op); @@ -216,11 +218,13 @@ mod test { use std::{io, ptr}; use test::{Bencher, black_box}; - use environment::*; use ffi::*; + + use environment::*; + use flags::*; use super::*; + use test_utils::*; use transaction::*; - use test_utils::setup_bench_db; #[test] fn test_iter() { diff --git a/src/database.rs b/src/database.rs index d201148..38722de 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,8 +1,10 @@ use std::kinds::marker; use std::ptr; -use error::{LmdbResult, lmdb_result}; use ffi::*; + +use error::{LmdbResult, lmdb_result}; +use flags::DatabaseFlags; use transaction::{RoTransaction, RwTransaction, Transaction}; /// A handle to an individual database in an environment. diff --git a/src/environment.rs b/src/environment.rs index 8037059..150af44 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -3,16 +3,18 @@ use std::io::FilePermission; use std::ptr; use std::sync::Mutex; +use ffi; + use error::{LmdbResult, lmdb_result}; use database::Database; -use ffi::*; use transaction::{RoTransaction, RwTransaction, Transaction, TransactionExt}; +use flags::{DatabaseFlags, EnvironmentFlags}; /// An LMDB environment. /// /// An environment supports multiple databases, all residing in the same shared-memory map. pub struct Environment { - env: *mut MDB_env, + env: *mut ffi::MDB_env, dbi_open_mutex: Mutex<()>, } @@ -32,7 +34,7 @@ impl Environment { /// /// The caller **must** ensure that the pointer is not dereferenced after the lifetime of the /// environment. - pub fn env(&self) -> *mut MDB_env { + pub fn env(&self) -> *mut ffi::MDB_env { self.env } @@ -81,7 +83,7 @@ impl Environment { let txn = try!(self.begin_read_txn()); let mut flags: c_uint = 0; unsafe { - try!(lmdb_result(mdb_dbi_flags(txn.txn(), db.dbi(), &mut flags))); + try!(lmdb_result(ffi::mdb_dbi_flags(txn.txn(), db.dbi(), &mut flags))); } Ok(DatabaseFlags::from_bits(flags).unwrap()) } @@ -104,7 +106,7 @@ impl Environment { /// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`. pub fn sync(&self, force: bool) -> LmdbResult<()> { unsafe { - lmdb_result(mdb_env_sync(self.env(), if force { 1 } else { 0 })) + lmdb_result(ffi::mdb_env_sync(self.env(), if force { 1 } else { 0 })) } } @@ -115,26 +117,26 @@ impl Environment { /// that value would be large. pub fn close_db(&mut self, name: Option<&str>) -> LmdbResult<()> { let db = try!(self.open_db(name)); - unsafe { mdb_dbi_close(self.env, db.dbi()) }; + unsafe { ffi::mdb_dbi_close(self.env, db.dbi()) }; Ok(()) } pub fn clear_db(&mut self, name: Option<&str>) -> LmdbResult<()> { let db = try!(self.open_db(name)); let txn = try!(self.begin_write_txn()); - unsafe { lmdb_result(mdb_drop(txn.txn(), db.dbi(), 0)) } + unsafe { lmdb_result(ffi::mdb_drop(txn.txn(), db.dbi(), 0)) } } pub fn drop_db(&mut self, name: Option<&str>) -> LmdbResult<()> { let db = try!(self.open_db(name)); let txn = try!(self.begin_write_txn()); - unsafe { lmdb_result(mdb_drop(txn.txn(), db.dbi(), 1)) } + unsafe { lmdb_result(ffi::mdb_drop(txn.txn(), db.dbi(), 1)) } } } impl Drop for Environment { fn drop(&mut self) { - unsafe { mdb_env_close(self.env) } + unsafe { ffi::mdb_env_close(self.env) } } } @@ -155,26 +157,26 @@ impl EnvironmentBuilder { /// Open an environment. pub fn open(&self, path: &Path, mode: FilePermission) -> LmdbResult { - let mut env: *mut MDB_env = ptr::null_mut(); + let mut env: *mut ffi::MDB_env = ptr::null_mut(); unsafe { - lmdb_try!(mdb_env_create(&mut env)); + lmdb_try!(ffi::mdb_env_create(&mut env)); if let Some(max_readers) = self.max_readers { - lmdb_try_with_cleanup!(mdb_env_set_maxreaders(env, max_readers), - mdb_env_close(env)) + lmdb_try_with_cleanup!(ffi::mdb_env_set_maxreaders(env, max_readers), + ffi::mdb_env_close(env)) } if let Some(max_dbs) = self.max_dbs { - lmdb_try_with_cleanup!(mdb_env_set_maxdbs(env, max_dbs), - mdb_env_close(env)) + lmdb_try_with_cleanup!(ffi::mdb_env_set_maxdbs(env, max_dbs), + ffi::mdb_env_close(env)) } if let Some(map_size) = self.map_size { - lmdb_try_with_cleanup!(mdb_env_set_mapsize(env, map_size), - mdb_env_close(env)) + lmdb_try_with_cleanup!(ffi::mdb_env_set_mapsize(env, map_size), + ffi::mdb_env_close(env)) } - lmdb_try_with_cleanup!(mdb_env_open(env, + lmdb_try_with_cleanup!(ffi::mdb_env_open(env, path.to_c_str().as_ptr(), self.flags.bits(), mode.bits() as mode_t), - mdb_env_close(env)); + ffi::mdb_env_close(env)); } Ok(Environment { env: env, dbi_open_mutex: Mutex::new(()) }) @@ -232,7 +234,7 @@ mod test { use std::io; - use ffi::*; + use flags::*; use super::*; #[test] diff --git a/src/flags.rs b/src/flags.rs new file mode 100644 index 0000000..2895ce6 --- /dev/null +++ b/src/flags.rs @@ -0,0 +1,183 @@ +use libc::c_uint; + +bitflags! { + #[doc="Environment Options"] + #[deriving(Show)] + flags EnvironmentFlags: c_uint { + + #[doc="Use a fixed address for the mmap region. This flag must be specified"] + #[doc="when creating the environment, and is stored persistently in the environment."] + #[doc="If successful, the memory map will always reside at the same virtual address"] + #[doc="and pointers used to reference data items in the database will be constant"] + #[doc="across multiple invocations. This option may not always work, depending on"] + #[doc="how the operating system has allocated memory to shared libraries and other uses."] + #[doc="The feature is highly experimental."] + const MDB_FIXEDMAP = 0x01, + + #[doc="By default, LMDB creates its environment in a directory whose"] + #[doc="pathname is given in `path`, and creates its data and lock files"] + #[doc="under that directory. With this option, `path` is used as-is for"] + #[doc="the database main data file. The database lock file is the `path`"] + #[doc="with `-lock` appended."] + const MDB_NOSUBDIR = 0x4000, + + #[doc="Use a writeable memory map unless `MDB_RDONLY` is set. This is faster and uses"] + #[doc="fewer mallocs, but loses protection from application bugs like wild pointer writes"] + #[doc="and other bad updates into the database. Incompatible with nested transactions."] + #[doc="Processes with and without `MDB_WRITEMAP` on the same environment do not cooperate"] + #[doc="well."] + const MDB_WRITEMAP = 0x80000, + + #[doc="Open the environment in read-only mode. No write operations will be allowed."] + #[doc="When opening an environment, LMDB will still modify the lock file - except on"] + #[doc="read-only filesystems, where LMDB does not use locks."] + const MDB_RDONLY = 0x20000, + + #[doc="Flush system buffers to disk only once per transaction, omit the"] + #[doc="metadata flush. Defer that until the system flushes files to disk,"] + #[doc="or next non-`MDB_RDONLY` commit or #mdb_env_sync(). This optimization"] + #[doc="maintains database integrity, but a system crash may undo the last"] + #[doc="committed transaction. I.e. it preserves the ACI (atomicity,"] + #[doc="consistency, isolation) but not D (durability) database property."] + #[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."] + const MDB_NOMETASYNC = 0x40000, + + #[doc="Don't flush system buffers to disk when committing a transaction."] + #[doc="This optimization means a system crash can corrupt the database or"] + #[doc="lose the last transactions if buffers are not yet flushed to disk."] + #[doc="The risk is governed by how often the system flushes dirty buffers"] + #[doc="to disk and how often `Environment::sync` is called. However, if the"] + #[doc="filesystem preserves write order and the `MDB_WRITEMAP` flag is not"] + #[doc="used, transactions exhibit ACI (atomicity, consistency, isolation)"] + #[doc="properties and only lose D (durability). I.e. database integrity"] + #[doc="is maintained, but a system crash may undo the final transactions."] + #[doc="Note that (`MDB_NOSYNC | MDB_WRITEMAP`) leaves the system with no"] + #[doc="hint for when to write transactions to disk, unless `Environment::sync`"] + #[doc="is called. (`MDB_MAPASYNC | MDB_WRITEMAP`) may be preferable."] + #[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."] + const MDB_NOSYNC = 0x10000, + + #[doc="When using `MDB_WRITEMAP`, use asynchronous flushes to disk."] + #[doc="As with `MDB_NOSYNC`, a system crash can then corrupt the"] + #[doc="database or lose the last transactions. Calling `Environment::sync`"] + #[doc="ensures on-disk database integrity until next commit."] + #[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."] + const MDB_MAPASYNC = 0x100000, + + #[doc="Don't use Thread-Local Storage. Tie reader locktable slots to"] + #[doc="transaction objects instead of to threads. I.e. `RoTransaction::reset` keeps"] + #[doc="the slot reseved for the transaction object. A thread may use parallel"] + #[doc="read-only transactions. A read-only transaction may span threads if"] + #[doc="the user synchronizes its use. Applications that multiplex many"] + #[doc="user threads over individual OS threads need this option. Such an"] + #[doc="application must also serialize the write transactions in an OS"] + #[doc="thread, since LMDB's write locking is unaware of the user threads."] + const MDB_NOTLS = 0x200000, + + #[doc="Don't do any locking. If concurrent access is anticipated, the"] + #[doc="caller must manage all concurrency themself. For proper operation"] + #[doc="the caller must enforce single-writer semantics, and must ensure"] + #[doc="that no readers are using old transactions while a writer is"] + #[doc="active. The simplest approach is to use an exclusive lock so that"] + #[doc="no readers may be active at all when a writer begins."] + const MDB_NOLOCK = 0x400000, + + #[doc="Turn off readahead. Most operating systems perform readahead on"] + #[doc="read requests by default. This option turns it off if the OS"] + #[doc="supports it. Turning it off may help random read performance"] + #[doc="when the DB is larger than RAM and system RAM is full."] + #[doc="The option is not implemented on Windows."] + const MDB_NORDAHEAD = 0x800000, + + #[doc="Don't initialize malloc'd memory before writing to unused spaces"] + #[doc="in the data file. By default, memory for pages written to the data"] + #[doc="file is obtained using malloc. While these pages may be reused in"] + #[doc="subsequent transactions, freshly malloc'd pages will be initialized"] + #[doc="to zeroes before use. This avoids persisting leftover data from other"] + #[doc="code (that used the heap and subsequently freed the memory) into the"] + #[doc="data file. Note that many other system libraries may allocate"] + #[doc="and free memory from the heap for arbitrary uses. E.g., stdio may"] + #[doc="use the heap for file I/O buffers. This initialization step has a"] + #[doc="modest performance cost so some applications may want to disable"] + #[doc="it using this flag. This option can be a problem for applications"] + #[doc="which handle sensitive data like passwords, and it makes memory"] + #[doc="checkers like Valgrind noisy. This flag is not needed with `MDB_WRITEMAP`,"] + #[doc="which writes directly to the mmap instead of using malloc for pages. The"] + #[doc="initialization is also skipped if `MDB_RESERVE` is used; the"] + #[doc="caller is expected to overwrite all of the memory that was"] + #[doc="reserved in that case."] + #[doc="\n\nThis flag may be changed at any time using `Environment::set_flags`."] + const MDB_NOMEMINIT = 0x1000000, + } +} + +bitflags! { + #[doc="Database Options"] + #[deriving(Show)] + flags DatabaseFlags: c_uint { + + #[doc="Keys are strings to be compared in reverse order, from the end"] + #[doc="of the strings to the beginning. By default, Keys are treated as strings and"] + #[doc="compared from beginning to end."] + const MDB_REVERSEKEY = 0x02, + + #[doc="Duplicate keys may be used in the database. (Or, from another perspective,"] + #[doc="keys may have multiple data items, stored in sorted order.) By default"] + #[doc="keys must be unique and may have only a single data item."] + const MDB_DUPSORT = 0x04, + + #[doc="Keys are binary integers in native byte order. Setting this option"] + #[doc="requires all keys to be the same size, typically sizeof(int)"] + #[doc="or sizeof(size_t)."] + const MDB_INTEGERKEY = 0x08, + + #[doc="This flag may only be used in combination with `MDB_DUPSORT`. This option"] + #[doc="tells the library that the data items for this database are all the same"] + #[doc="size, which allows further optimizations in storage and retrieval. When"] + #[doc="all data items are the same size, the `MDB_GET_MULTIPLE` and `MDB_NEXT_MULTIPLE`"] + #[doc="cursor operations may be used to retrieve multiple items at once."] + const MDB_DUPFIXED = 0x10, + + #[doc="This option specifies that duplicate data items are also integers, and"] + #[doc="should be sorted as such."] + const MDB_INTEGERDUP = 0x20, + + #[doc="This option specifies that duplicate data items should be compared as"] + #[doc="strings in reverse order."] + const MDB_REVERSEDUP = 0x40, + } +} + +bitflags! { + #[doc="Write Options"] + #[deriving(Show)] + flags WriteFlags: c_uint { + + #[doc="Insert the new item only if the key does not already appear in the database."] + #[doc="The function will return `LmdbError::KeyExist` if the key already appears in the"] + #[doc="database, even if the database supports duplicates (`MDB_DUPSORT`)."] + const MDB_NOOVERWRITE = 0x10, + + #[doc="Insert the new item only if it does not already appear in the database."] + #[doc="This flag may only be specified if the database was opened with `MDB_DUPSORT`."] + #[doc="The function will return `LmdbError::KeyExist` if the item already appears in the"] + #[doc="database."] + const MDB_NODUPDATA = 0x20, + + #[doc="For `Cursor::put`. Replace the item at the current cursor position."] + #[doc="The key parameter must match the current position. If using"] + #[doc="sorted duplicates (`MDB_DUPSORT`) the data item must still sort into the"] + #[doc="same position. This is intended to be used when the new data is the same"] + #[doc="size as the old. Otherwise it will simply perform a delete of the old"] + #[doc="record followed by an insert."] + const MDB_CURRENT = 0x40, + + #[doc="Append the given item to the end of the database. No key comparisons are performed."] + #[doc="This option allows fast bulk loading when keys are already known to be in the"] + #[doc="correct order. Loading unsorted keys with this flag will cause data corruption."] + const MDB_APPEND = 0x20000, + + #[doc="Same as `MDB_APPEND`, but for sorted dup data."] + const MDB_APPENDDUP = 0x40000, + } +} diff --git a/src/lib.rs b/src/lib.rs index e552262..fedfd70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ pub use cursor::{ pub use database::Database; pub use environment::{Environment, EnvironmentBuilder}; pub use error::{LmdbResult, LmdbError}; +pub use flags::*; pub use transaction::{ InactiveTransaction, RoTransaction, @@ -29,7 +30,6 @@ pub use transaction::{ Transaction, TransactionExt, }; -pub use ffi::{DatabaseFlags, EnvironmentFlags, WriteFlags}; macro_rules! lmdb_try { ($expr:expr) => ({ @@ -52,6 +52,7 @@ macro_rules! lmdb_try_with_cleanup { }) } +mod flags; mod cursor; mod database; mod environment; diff --git a/src/transaction.rs b/src/transaction.rs index f8ee9bc..8e5a36c 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -3,13 +3,13 @@ use std::{mem, ptr, raw}; use std::kinds::marker; use std::io::BufWriter; +use ffi; + use cursor::{RoCursor, RwCursor}; use environment::Environment; use database::Database; use error::{LmdbError, LmdbResult, lmdb_result}; -use ffi; -use ffi::MDB_txn; -use ffi::{DatabaseFlags, EnvironmentFlags, WriteFlags, MDB_RDONLY, MDB_RESERVE}; +use flags::{DatabaseFlags, EnvironmentFlags, WriteFlags}; /// An LMDB transaction. /// @@ -20,7 +20,7 @@ pub trait Transaction<'env> { /// /// The caller **must** ensure that the pointer is not used after the lifetime of the /// transaction. - fn txn(&self) -> *mut MDB_txn; + fn txn(&self) -> *mut ffi::MDB_txn; } /// Transaction extension methods. @@ -90,7 +90,7 @@ impl<'env, T> TransactionExt<'env> for T where T: Transaction<'env> {} /// An LMDB read-only transaction. pub struct RoTransaction<'env> { - txn: *mut MDB_txn, + txn: *mut ffi::MDB_txn, _no_sync: marker::NoSync, _no_send: marker::NoSend, _contravariant: marker::ContravariantLifetime<'env>, @@ -109,11 +109,11 @@ impl <'env> RoTransaction<'env> { /// `Environment::begin_ro_txn`. #[doc(hidden)] pub fn new(env: &'env Environment) -> LmdbResult> { - let mut txn: *mut MDB_txn = ptr::null_mut(); + let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); unsafe { try!(lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), - MDB_RDONLY.bits(), + ffi::MDB_RDONLY, &mut txn))); Ok(RoTransaction { txn: txn, @@ -141,13 +141,13 @@ impl <'env> RoTransaction<'env> { } impl <'env> Transaction<'env> for RoTransaction<'env> { - fn txn(&self) -> *mut MDB_txn { + fn txn(&self) -> *mut ffi::MDB_txn { self.txn } } pub struct InactiveTransaction<'env> { - txn: *mut MDB_txn, + txn: *mut ffi::MDB_txn, _no_sync: marker::NoSync, _no_send: marker::NoSend, _contravariant: marker::ContravariantLifetime<'env>, @@ -179,7 +179,7 @@ impl <'env> InactiveTransaction<'env> { /// An LMDB read-write transaction. pub struct RwTransaction<'env> { - txn: *mut MDB_txn, + txn: *mut ffi::MDB_txn, _no_sync: marker::NoSync, _no_send: marker::NoSend, _contravariant: marker::ContravariantLifetime<'env>, @@ -198,7 +198,7 @@ impl <'env> RwTransaction<'env> { /// `Environment::begin_ro_txn`. #[doc(hidden)] pub fn new(env: &'env Environment) -> LmdbResult> { - let mut txn: *mut MDB_txn = ptr::null_mut(); + let mut txn: *mut ffi::MDB_txn = ptr::null_mut(); unsafe { try!(lmdb_result(ffi::mdb_txn_begin(env.env(), ptr::null_mut(), @@ -259,7 +259,7 @@ impl <'env> RwTransaction<'env> { database.dbi(), &mut key_val, &mut data_val, - flags.bits() | MDB_RESERVE))); + flags.bits() | ffi::MDB_RESERVE))); let slice: &'txn mut [u8] = mem::transmute(raw::Slice { data: data_val.mv_data as *const u8, @@ -313,7 +313,7 @@ impl <'env> RwTransaction<'env> { } impl <'env> Transaction<'env> for RwTransaction<'env> { - fn txn(&self) -> *mut MDB_txn { + fn txn(&self) -> *mut ffi::MDB_txn { self.txn } } @@ -327,8 +327,10 @@ mod test { use std::sync::{Arc, Barrier, Future}; use test::{Bencher, black_box}; - use environment::*; use ffi::*; + + use environment::*; + use flags::*; use super::*; use test_utils::*;