1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
//! Crate `heed` is a high-level wrapper of [LMDB], high-level doesn't mean heavy (think about Rust). //! //! It provides you a way to store types in LMDB without any limit and with a minimal overhead as possible, //! relying on the [zerocopy] library to avoid copying bytes when that's unnecessary and the [serde] library //! when this is unavoidable. //! //! The Lightning Memory-Mapped Database (LMDB) directly maps files parts into main memory, combined //! with the zerocopy library allows us to safely zero-copy parse and serialize Rust types into LMDB. //! //! [LMDB]: https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database //! [zerocopy]: https://docs.rs/zerocopy //! [serde]: https://docs.rs/serde //! //! # Examples //! //! Discern let you open a database, that will support some typed key/data //! and ensures, at compile time, that you'll write those types and not others. //! //! ``` //! use std::fs; //! use heed::{EnvOpenOptions, Database}; //! use heed::types::*; //! //! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! fs::create_dir_all("target/zerocopy.mdb")?; //! //! let env = EnvOpenOptions::new() //! .map_size(10 * 1024 * 1024 * 1024) // 10GB //! .max_dbs(3000) //! .open("target/zerocopy.mdb")?; //! //! // here we specify that the key is an i32 array and the data an str //! let db: Database<OwnedType<[i32; 2]>, Str> = env.create_database(Some("str"))?; //! //! let mut wtxn = env.write_txn()?; //! db.put(&mut wtxn, &[2, 3], "what's up?")?; //! //! let ret = db.get(&wtxn, &[2, 3])?; //! assert_eq!(ret, Some("what's up?")); //! //! wtxn.commit()?; //! //! // Be careful, you cannot open a database while in a transaction! //! // So don't forget to commit/abort it before. //! let db: Database<Str, ByteSlice> = env.create_database(Some("bytes"))?; //! //! let mut wtxn = env.write_txn()?; //! db.put(&mut wtxn, "hello", &[2, 3][..])?; //! //! let ret = db.get(&wtxn, "hello")?; //! assert_eq!(ret, Some(&[2, 3][..])); //! //! wtxn.commit()?; //! # Ok(()) } //! ``` mod cursor; mod db; mod env; mod lmdb_error; mod traits; mod txn; pub mod types; pub use byteorder; pub use zerocopy; use self::cursor::{RoCursor, RwCursor}; pub use self::db::{Database, PolyDatabase, RoIter, RoRange, RwIter, RwRange}; pub use self::env::{CompactionOption, Env, EnvOpenOptions}; pub use self::lmdb_error::Error as LmdbError; pub use self::traits::{BytesDecode, BytesEncode}; pub use self::txn::{RoTxn, RwTxn}; use std::{error, fmt, io, result}; use lmdb_sys as ffi; /// An error that encapsulates all possible errors in this crate. #[derive(Debug)] pub enum Error { Io(io::Error), Lmdb(LmdbError), Encoding, Decoding, InvalidDatabaseTyping, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::Io(error) => write!(f, "{}", error), Error::Lmdb(error) => write!(f, "{}", error), Error::Encoding => write!(f, "error while encoding"), Error::Decoding => write!(f, "error while decoding"), Error::InvalidDatabaseTyping => { write!(f, "database was previously opened with different types") } } } } impl error::Error for Error {} impl From<LmdbError> for Error { fn from(error: LmdbError) -> Error { match error { LmdbError::Other(e) => Error::Io(io::Error::from_raw_os_error(e)), _ => Error::Lmdb(error), } } } impl From<io::Error> for Error { fn from(error: io::Error) -> Error { Error::Io(error) } } pub type Result<T> = result::Result<T, Error>; unsafe fn into_val(value: &[u8]) -> ffi::MDB_val { ffi::MDB_val { mv_size: value.len(), mv_data: value.as_ptr() as *mut libc::c_void, } } unsafe fn from_val<'a>(value: ffi::MDB_val) -> &'a [u8] { std::slice::from_raw_parts(value.mv_data as *const u8, value.mv_size) }