#![deny(unsafe_op_in_unsafe_fn)]
#![warn(missing_docs)]
#![allow(unused_macro_rules)]
pub mod cow;
mod helpers;
pub mod lmdb;
pub mod storable;
mod syncutils;
#[cfg(test)]
mod tests;
pub use cow::{GenericCow, Owned};
use helpers::*;
pub use storable::{BorrowStorable, NoKey, NoValue, Storable, StorableConstBytesLen, StorableRef};
use syncutils::*;
use std::cell::UnsafeCell;
use std::collections::{HashMap, HashSet};
use std::ffi::{c_int, c_uint, CStr, CString};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::io;
use std::marker::PhantomData;
use std::mem::{forget, take, MaybeUninit};
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::ptr::{null, null_mut};
use std::slice;
use std::str;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering::Relaxed};
use std::sync::{Arc, Mutex, Weak};
#[allow(non_camel_case_types)]
type c_size_t = usize;
pub mod traits {
pub use super::Env as _;
pub use super::EnvRef as _;
pub use super::Txn as _;
}
unsafe fn check_err_code(code: c_int) -> io::Result<()> {
if code > 0 {
Err(io::Error::from_raw_os_error(code))
} else if code < 0 {
Err(io::Error::new(
io::ErrorKind::Other,
unsafe { CStr::from_ptr(lmdb::mdb_strerror(code)) }
.to_str()
.unwrap_or("unknown error (LMDB error string is not valid UTF-8)"),
))
} else {
Ok(())
}
}
type LmdbFlags = c_uint;
macro_rules! flag_get {
($self:ident, $flag:ident) => {
($self.lmdb_flags & (lmdb::$flag as LmdbFlags) != 0)
};
}
macro_rules! flag_set {
($self:ident, $flag:ident, $value:expr) => {
if $value {
$self.lmdb_flags |= lmdb::$flag as LmdbFlags;
} else {
$self.lmdb_flags &= !(lmdb::$flag as LmdbFlags);
}
};
}
static ENV_COUNTER: AtomicU64 = AtomicU64::new(0);
#[derive(Debug)]
struct EnvBackend {
inner: *mut lmdb::MDB_env,
id: u64,
max_keysize: usize,
nestable_txns: bool,
dbis: Mutex<HashMap<lmdb::MDB_dbi, Weak<DbBackend>>>,
}
unsafe impl Send for EnvBackend {}
unsafe impl Sync for EnvBackend {}
impl PartialEq for EnvBackend {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl Eq for EnvBackend {}
impl Hash for EnvBackend {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl Drop for EnvBackend {
fn drop(&mut self) {
for dbi in take(&mut *self.dbis.lock().unwrap()).into_keys() {
unsafe {
lmdb::mdb_dbi_close(self.inner, dbi);
}
}
unsafe { lmdb::mdb_env_close(self.inner) }
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct EnvRo {
backend: Arc<EnvBackend>,
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct EnvRw {
env_ro: EnvRo,
}
impl AsRef<EnvRo> for EnvRo {
fn as_ref(&self) -> &EnvRo {
&self
}
}
impl AsRef<EnvRo> for EnvRw {
fn as_ref(&self) -> &EnvRo {
&self.env_ro
}
}
pub trait EnvRef {
unsafe fn open_or_create_db<K, V, C>(
&mut self,
options: &DbSpec<K, V, C>,
) -> io::Result<Db<K, V, C>>
where
K: Storable + ?Sized,
V: Storable + ?Sized,
C: Constraint;
}
impl<T: Env> EnvRef for &T {
unsafe fn open_or_create_db<K, V, C>(
&mut self,
options: &DbSpec<K, V, C>,
) -> io::Result<Db<K, V, C>>
where
K: Storable + ?Sized,
V: Storable + ?Sized,
C: Constraint,
{
unsafe { self.open_db(options) }
}
}
impl EnvRef for &mut EnvRw {
unsafe fn open_or_create_db<K, V, C>(
&mut self,
options: &DbSpec<K, V, C>,
) -> io::Result<Db<K, V, C>>
where
K: Storable + ?Sized,
V: Storable + ?Sized,
C: Constraint,
{
unsafe { self.create_db(options) }
}
}
#[derive(Debug)]
struct TxnBackend<'a> {
env_ro: &'a EnvRo,
inner: *mut lmdb::MDB_txn,
cursors: WeakVec<CursorBackend>,
}
impl<'a> TxnBackend<'a> {
fn close_cursors(&mut self) {
for cursor in take(&mut self.cursors).iter() {
cursor.closed.store(true, Relaxed);
unsafe {
lmdb::mdb_cursor_close(cursor.inner);
}
}
}
fn dont_abort(mut self) {
self.close_cursors();
forget(self);
}
}
impl<'a> Drop for TxnBackend<'a> {
fn drop(&mut self) {
self.close_cursors();
unsafe {
lmdb::mdb_txn_abort(self.inner);
}
}
}
#[derive(Debug)]
pub struct TxnRo<'a> {
backend: UnsafeCell<TxnBackend<'a>>,
}
unsafe impl<'a> Send for TxnRo<'a> {}
#[derive(Debug)]
enum UsedDbs<'a> {
Toplevel(HashSet<ArcByAddr<DbBackend>>),
Nested(&'a mut HashSet<ArcByAddr<DbBackend>>),
}
impl<'a> Deref for UsedDbs<'a> {
type Target = HashSet<ArcByAddr<DbBackend>>;
fn deref(&self) -> &Self::Target {
match self {
UsedDbs::Toplevel(x) => x,
UsedDbs::Nested(x) => x,
}
}
}
impl<'a> DerefMut for UsedDbs<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
UsedDbs::Toplevel(x) => x,
UsedDbs::Nested(x) => x,
}
}
}
pub struct TxnRw<'a> {
backend: UnsafeCell<TxnBackend<'a>>,
used_dbs: UsedDbs<'a>,
commit_handlers: Vec<Box<dyn FnOnce(&mut Self) -> io::Result<()> + 'a>>,
}
impl<'a> fmt::Debug for TxnRw<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Point")
.field("backend", &self.backend)
.field("used_dbs", &self.used_dbs)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
struct DbBackend {
env_backend: Weak<EnvBackend>,
env_id: u64,
inner: lmdb::MDB_dbi,
}
impl DbBackend {
fn dont_close(mut self) {
self.env_backend = Default::default();
forget(self);
}
fn assert_env_backend(&self, env_backend: &EnvBackend) {
if self.env_id != env_backend.id {
panic!("database does not belong to environment");
}
}
}
impl Drop for DbBackend {
fn drop(&mut self) {
if let Some(env_backend) = Weak::upgrade(&self.env_backend) {
let mut dbis = env_backend.dbis.lock().unwrap();
if let Some(weak) = dbis.get(&self.inner) {
if weak.strong_count() == 0 {
dbis.remove(&self.inner);
unsafe {
lmdb::mdb_dbi_close(env_backend.inner, self.inner);
}
}
}
}
}
}
#[derive(Debug)]
pub struct Db<K: ?Sized, V: ?Sized, C> {
key: PhantomData<fn(&K) -> &K>,
value: PhantomData<fn(&V) -> &V>,
constraint: C,
backend: ArcByAddr<DbBackend>,
}
impl<K: ?Sized, V: ?Sized, C> Clone for Db<K, V, C>
where
C: Constraint,
{
fn clone(&self) -> Self {
Db {
key: PhantomData,
value: PhantomData,
constraint: self.constraint,
backend: self.backend.clone(),
}
}
}
impl<K: ?Sized, V: ?Sized, C> PartialEq for Db<K, V, C> {
fn eq(&self, other: &Self) -> bool {
self.backend == other.backend
}
}
impl<K: ?Sized, V: ?Sized, C> Eq for Db<K, V, C> {}
#[derive(Debug)]
struct CursorBackend {
inner: *mut lmdb::MDB_cursor,
closed: AtomicBool,
inner_txn: *mut lmdb::MDB_txn,
}
unsafe impl Send for CursorBackend {}
unsafe impl Sync for CursorBackend {}
impl CursorBackend {
fn assert_txn_backend(&self, txn_backend: &TxnBackend) {
if self.closed.load(Relaxed) || self.inner_txn != txn_backend.inner {
panic!("cursor does not belong to transaction");
}
}
}
impl Drop for CursorBackend {
fn drop(&mut self) {
if !*self.closed.get_mut() {
unsafe {
lmdb::mdb_cursor_close(self.inner);
}
}
}
}
#[derive(Debug)]
pub struct Cursor<K: ?Sized, V: ?Sized, C> {
backend: ArcByAddr<CursorBackend>,
db: Db<K, V, C>,
}
impl<K: ?Sized, V: ?Sized, C> Clone for Cursor<K, V, C>
where
C: Constraint,
{
fn clone(&self) -> Self {
Cursor {
db: self.db.clone(),
backend: self.backend.clone(),
}
}
}
impl<K: ?Sized, V: ?Sized, C> PartialEq for Cursor<K, V, C> {
fn eq(&self, other: &Self) -> bool {
self.backend == other.backend
}
}
impl<K: ?Sized, V: ?Sized, C> Eq for Cursor<K, V, C> {}
#[derive(Clone, Debug)]
pub struct EnvBuilder<P> {
path: P,
lmdb_flags: LmdbFlags,
max_size: usize,
max_readers: usize,
max_dbs: usize,
unix_file_mode: u32,
}
pub trait Constraint: Copy + 'static {
const DUPLICATE_KEYS: bool;
}
#[derive(Clone, Copy, Debug)]
pub struct KeysUnique;
impl Constraint for KeysUnique {
const DUPLICATE_KEYS: bool = false;
}
#[derive(Clone, Copy, Debug)]
pub struct KeysDuplicate;
impl Constraint for KeysDuplicate {
const DUPLICATE_KEYS: bool = true;
}
pub type DbSpec<K, V, C> = DbBuilder<K, V, C, Option<CString>>;
#[derive(Clone, Debug)]
pub struct DbBuilder<K: ?Sized, V: ?Sized, C, N> {
key: PhantomData<fn(&K) -> &K>,
value: PhantomData<fn(&V) -> &V>,
constraint: C,
lmdb_flags: LmdbFlags,
name: N,
}
pub mod env_builder_defaults {
pub const MAX_SIZE: usize = 1024 * 1024 * 1024;
pub const MAX_READERS: usize = 126;
pub const MAX_DBS: usize = 0;
pub const UNIX_FILE_MODE: u32 = 0o600;
}
impl EnvBuilder<()> {
pub const fn new() -> Self {
Self {
path: (),
lmdb_flags: lmdb::MDB_NORDAHEAD as LmdbFlags | lmdb::MDB_NOTLS as LmdbFlags,
max_size: env_builder_defaults::MAX_SIZE,
max_readers: env_builder_defaults::MAX_READERS,
max_dbs: env_builder_defaults::MAX_DBS,
unix_file_mode: env_builder_defaults::UNIX_FILE_MODE,
}
}
}
impl<P> EnvBuilder<P> {
pub fn dir<'a>(mut self, path: &'a (impl AsRef<Path> + ?Sized)) -> EnvBuilder<&'a Path> {
flag_set!(self, MDB_NOSUBDIR, false);
EnvBuilder {
path: path.as_ref(),
lmdb_flags: self.lmdb_flags,
max_size: self.max_size,
max_readers: self.max_readers,
max_dbs: self.max_dbs,
unix_file_mode: self.unix_file_mode,
}
}
pub fn file<'a>(mut self, path: &'a (impl AsRef<Path> + ?Sized)) -> EnvBuilder<&'a Path> {
flag_set!(self, MDB_NOSUBDIR, true);
EnvBuilder {
path: path.as_ref(),
lmdb_flags: self.lmdb_flags,
max_size: self.max_size,
max_readers: self.max_readers,
max_dbs: self.max_dbs,
unix_file_mode: self.unix_file_mode,
}
}
pub const fn get_writemap(&self) -> bool {
flag_get!(self, MDB_WRITEMAP)
}
pub const fn writemap(mut self, flag: bool) -> Self {
flag_set!(self, MDB_WRITEMAP, flag);
self
}
pub const fn get_nometasync(&self) -> bool {
flag_get!(self, MDB_NOMETASYNC)
}
pub const fn nometasync(mut self, flag: bool) -> Self {
flag_set!(self, MDB_NOMETASYNC, flag);
self
}
pub const fn get_nosync(&self) -> bool {
flag_get!(self, MDB_NOSYNC)
}
pub const fn nosync(mut self, flag: bool) -> Self {
flag_set!(self, MDB_NOSYNC, flag);
self
}
pub const fn get_mapasync(&self) -> bool {
flag_get!(self, MDB_MAPASYNC)
}
pub const fn mapasync(mut self, flag: bool) -> Self {
flag_set!(self, MDB_MAPASYNC, flag);
self
}
pub const fn get_nordahead(&self) -> bool {
flag_get!(self, MDB_NORDAHEAD)
}
pub const fn nordahead(mut self, flag: bool) -> Self {
flag_set!(self, MDB_NORDAHEAD, flag);
self
}
pub const fn get_max_size(&self) -> usize {
self.max_size
}
pub const fn max_size(mut self, max_size: usize) -> Self {
self.max_size = max_size;
self
}
pub const fn get_max_readers(&self) -> usize {
self.max_readers
}
pub const fn max_readers(mut self, max_readers: usize) -> Self {
self.max_readers = max_readers;
self
}
pub const fn get_max_dbs(&self) -> usize {
self.max_dbs
}
pub const fn max_dbs(mut self, max_dbs: usize) -> Self {
self.max_dbs = max_dbs;
self
}
pub const fn get_unix_file_mode(&self) -> u32 {
self.unix_file_mode
}
pub const fn unix_file_mode(mut self, mode: u32) -> Self {
self.unix_file_mode = mode;
self
}
pub const fn nestable_txns(&self) -> bool {
!self.get_writemap()
}
}
impl<'a> EnvBuilder<&'a Path> {
unsafe fn open_with_readonly_flag(&self, readonly: bool) -> io::Result<EnvRo> {
let path = path_to_cstring(self.path)?;
let mut inner = MaybeUninit::<*mut lmdb::MDB_env>::uninit();
unsafe {
check_err_code(lmdb::mdb_env_create(inner.as_mut_ptr()))?;
}
let inner = unsafe { inner.assume_init() };
let backend = EnvBackend {
inner: inner,
id: ENV_COUNTER.fetch_add(1, Relaxed),
max_keysize: unsafe { lmdb::mdb_env_get_maxkeysize(inner) }
.try_into()
.unwrap_or(usize::MAX),
nestable_txns: self.nestable_txns(),
dbis: Default::default(),
};
unsafe {
check_err_code(lmdb::mdb_env_set_mapsize(
backend.inner,
self.max_size.try_into().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "maximum size invalid")
})?,
))?;
}
unsafe {
check_err_code(lmdb::mdb_env_set_maxreaders(
backend.inner,
self.max_readers.try_into().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "maximum reader count invalid")
})?,
))?;
}
unsafe {
check_err_code(lmdb::mdb_env_set_maxdbs(
backend.inner,
self.max_dbs.try_into().map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"maximum number of named databases invalid",
)
})?,
))?;
}
unsafe {
check_err_code(lmdb::mdb_env_open(
backend.inner,
path.as_ptr(),
if readonly {
self.lmdb_flags | lmdb::MDB_RDONLY as LmdbFlags
} else {
self.lmdb_flags
},
self.unix_file_mode.try_into().map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"file mode bits integer invalid",
)
})?,
))?;
}
Ok(EnvRo {
backend: Arc::new(backend),
})
}
pub unsafe fn open_ro(&self) -> io::Result<EnvRo> {
Ok(unsafe { self.open_with_readonly_flag(true) }?)
}
pub unsafe fn open_rw(&self) -> io::Result<EnvRw> {
Ok(EnvRw {
env_ro: unsafe { self.open_with_readonly_flag(false) }?,
})
}
}
impl DbBuilder<[u8], [u8], KeysUnique, ()> {
pub const fn new() -> Self {
Self {
key: PhantomData,
value: PhantomData,
constraint: KeysUnique,
lmdb_flags: 0,
name: (),
}
}
}
impl<K: ?Sized, V: ?Sized, C> DbBuilder<K, V, C, ()>
where
C: Constraint,
{
pub const fn keys_unique(mut self) -> DbBuilder<K, V, KeysUnique, ()> {
flag_set!(self, MDB_DUPSORT, false);
flag_set!(self, MDB_REVERSEDUP, false);
DbBuilder {
constraint: KeysUnique,
key: PhantomData,
value: PhantomData,
lmdb_flags: self.lmdb_flags,
name: self.name,
}
}
pub const fn keys_duplicate(mut self) -> DbBuilder<K, V, KeysDuplicate, ()> {
flag_set!(self, MDB_DUPSORT, true);
DbBuilder {
constraint: KeysDuplicate,
key: PhantomData,
value: PhantomData,
lmdb_flags: self.lmdb_flags,
name: self.name,
}
}
pub const fn key_type<T>(mut self) -> DbBuilder<T, V, C, ()>
where
T: ?Sized + Storable,
{
flag_set!(self, MDB_INTEGERKEY, T::OPTIMIZE_INT);
DbBuilder {
key: PhantomData,
value: PhantomData,
constraint: self.constraint,
lmdb_flags: self.lmdb_flags,
name: self.name,
}
}
pub const fn value_type<T>(mut self) -> DbBuilder<K, T, C, ()>
where
T: ?Sized + Storable,
{
flag_set!(self, MDB_DUPFIXED, T::CONST_BYTES_LEN);
flag_set!(self, MDB_INTEGERDUP, T::OPTIMIZE_INT);
DbBuilder {
value: PhantomData,
key: PhantomData,
constraint: self.constraint,
lmdb_flags: self.lmdb_flags,
name: self.name,
}
}
}
impl<K: ?Sized, V: ?Sized, C, N> DbBuilder<K, V, C, N>
where
C: Constraint,
{
pub const fn get_reversekey(&self) -> bool {
flag_get!(self, MDB_REVERSEKEY)
}
pub const fn reversekey(mut self, flag: bool) -> Self {
flag_set!(self, MDB_REVERSEKEY, flag);
self
}
const fn cleansed_lmdb_flags(&self) -> LmdbFlags {
if flag_get!(self, MDB_DUPSORT) {
self.lmdb_flags
} else {
self.lmdb_flags
& !((lmdb::MDB_REVERSEDUP | lmdb::MDB_DUPFIXED | lmdb::MDB_INTEGERDUP) as LmdbFlags)
}
}
pub fn unnamed(self) -> DbSpec<K, V, C> {
DbBuilder {
value: PhantomData,
key: PhantomData,
constraint: self.constraint,
lmdb_flags: self.lmdb_flags,
name: None,
}
}
pub fn name<T: Into<Vec<u8>>>(self, name: T) -> DbSpec<K, V, C> {
let name = CString::new(name).expect("database name must not contain NULL byte");
DbBuilder {
value: PhantomData,
key: PhantomData,
constraint: self.constraint,
lmdb_flags: self.lmdb_flags,
name: Some(name),
}
}
pub fn strip_name<T: Into<Vec<u8>>>(self) -> DbBuilder<K, V, C, ()> {
DbBuilder {
value: PhantomData,
key: PhantomData,
constraint: self.constraint,
lmdb_flags: self.lmdb_flags,
name: (),
}
}
}
impl<K: ?Sized, V: ?Sized, C: Constraint, N> DbBuilder<K, V, C, N> {
pub fn has_duplicate_keys(&self) -> bool {
C::DUPLICATE_KEYS
}
}
impl<K: ?Sized, V: ?Sized, N> DbBuilder<K, V, KeysDuplicate, N> {
pub const fn get_reversedup(&self) -> bool {
flag_get!(self, MDB_REVERSEDUP)
}
pub const fn reversedup(mut self, flag: bool) -> Self {
flag_set!(self, MDB_REVERSEDUP, flag);
self
}
}
unsafe extern "C" fn compare_function<T>(a: *const lmdb::MDB_val, b: *const lmdb::MDB_val) -> c_int
where
T: ?Sized + Storable,
{
unsafe {
let a: &[u8] = slice::from_raw_parts((*a).mv_data as *const u8, (*a).mv_size);
let b: &[u8] = slice::from_raw_parts((*b).mv_data as *const u8, (*b).mv_size);
T::cmp_bytes_unchecked(a, b) as c_int
}
}
impl EnvRo {
fn size_valid_keysize(&self, size: usize) -> bool {
(1..=self.max_keysize()).contains(&size)
}
fn slice_valid_keysize(&self, key: &[u8]) -> bool {
self.size_valid_keysize(key.len())
}
fn assert_valid_keysize(&self, key: &[u8]) -> io::Result<()> {
if self.slice_valid_keysize(key) {
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid key size",
))
}
}
fn assert_valid_valuesize(&self, value: &[u8]) -> io::Result<()> {
if self.slice_valid_keysize(value) {
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid value size",
))
}
}
unsafe fn open_or_opt_create_db<'a, K, V, C>(
&self,
db_spec: &DbSpec<K, V, C>,
create: bool,
) -> io::Result<Db<K, V, C>>
where
K: ?Sized + Storable + 'a,
V: ?Sized + Storable + 'a,
C: Constraint,
{
let mut dbis = self.backend.dbis.lock().unwrap();
let mut txn_inner = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
unsafe {
check_err_code(lmdb::mdb_txn_begin(
self.backend.inner,
null_mut(),
if create {
0 as LmdbFlags
} else {
lmdb::MDB_RDONLY as LmdbFlags
},
txn_inner.as_mut_ptr(),
))?;
}
let txn_inner = unsafe { txn_inner.assume_init() };
let txn = TxnBackend {
env_ro: self,
inner: txn_inner,
cursors: Default::default(),
};
let mut db_inner = MaybeUninit::<lmdb::MDB_dbi>::uninit();
let mut flags = db_spec.cleansed_lmdb_flags();
if create {
flags |= lmdb::MDB_CREATE as LmdbFlags;
}
unsafe {
check_err_code(lmdb::mdb_dbi_open(
txn.inner,
db_spec.name.as_ref().map_or(null(), |x| x.as_ptr()),
flags,
db_inner.as_mut_ptr(),
))?;
}
let db_inner = unsafe { db_inner.assume_init() };
let backend = match dbis.get(&db_inner).and_then(Weak::upgrade) {
Some(arc) => {
txn.commit()?;
arc
}
None => {
unsafe {
if !K::TRIVIAL_CMP && !K::OPTIMIZE_INT {
check_err_code(lmdb::mdb_set_compare(
txn.inner,
db_inner,
Some(compare_function::<K>),
))?;
}
if !V::TRIVIAL_CMP && !V::OPTIMIZE_INT && C::DUPLICATE_KEYS {
check_err_code(lmdb::mdb_set_dupsort(
txn.inner,
db_inner,
Some(compare_function::<V>),
))?;
}
}
let env_backend = Arc::downgrade(&self.backend);
let env_id = self.backend.id;
txn.commit()?;
let db_backend = DbBackend {
env_backend,
env_id,
inner: db_inner,
};
let arc = Arc::new(db_backend);
dbis.insert(db_inner, Arc::downgrade(&arc));
arc
}
};
let db = Db {
key: PhantomData,
value: PhantomData,
constraint: db_spec.constraint,
backend: ArcByAddr::from(backend),
};
drop(dbis);
Ok(db)
}
}
pub trait Env: AsRef<EnvRo> {
fn as_env_ro(&self) -> &EnvRo {
self.as_ref()
}
fn clone_ro(&self) -> EnvRo {
self.as_env_ro().clone()
}
fn txn_ro(&self) -> io::Result<TxnRo<'_>>;
fn max_keysize(&self) -> usize;
fn valid_keysize<'kr, K, KRef>(&self, key: KRef) -> bool
where
K: ?Sized + Storable + 'kr,
KRef: StorableRef<'kr, K>;
unsafe fn open_db<K, V, C>(&self, options: &DbSpec<K, V, C>) -> io::Result<Db<K, V, C>>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint;
fn clear_stale_readers(&self) -> io::Result<usize>;
}
impl Env for EnvRo {
fn txn_ro(&self) -> io::Result<TxnRo<'_>> {
unsafe {
let mut inner_txn = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
check_err_code(lmdb::mdb_txn_begin(
self.backend.inner,
null_mut(),
lmdb::MDB_RDONLY as LmdbFlags,
inner_txn.as_mut_ptr(),
))?;
let inner_txn = inner_txn.assume_init();
Ok(TxnRo {
backend: UnsafeCell::new(TxnBackend {
env_ro: &self,
inner: inner_txn,
cursors: Default::default(),
}),
})
}
}
fn max_keysize(&self) -> usize {
self.backend.max_keysize
}
fn valid_keysize<'kr, K, KRef>(&self, key: KRef) -> bool
where
K: ?Sized + Storable + 'kr,
KRef: StorableRef<'kr, K>,
{
self.size_valid_keysize(key.ref_bytes_len())
}
unsafe fn open_db<K, V, C>(&self, options: &DbSpec<K, V, C>) -> io::Result<Db<K, V, C>>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
unsafe { self.open_or_opt_create_db(options, false) }
}
fn clear_stale_readers(&self) -> io::Result<usize> {
unsafe {
let mut dead = MaybeUninit::<c_int>::uninit();
check_err_code(lmdb::mdb_reader_check(
self.backend.inner,
dead.as_mut_ptr(),
))?;
let dead = dead.assume_init();
Ok(dead
.try_into()
.expect("reported number of stale reader transactions does not fit into usize"))
}
}
}
impl Env for EnvRw {
fn txn_ro(&self) -> io::Result<TxnRo<'_>> {
self.as_env_ro().txn_ro()
}
fn max_keysize(&self) -> usize {
self.as_env_ro().max_keysize()
}
fn valid_keysize<'kr, K, KRef>(&self, key: KRef) -> bool
where
K: ?Sized + Storable + 'kr,
KRef: StorableRef<'kr, K>,
{
self.as_env_ro().valid_keysize(key)
}
unsafe fn open_db<K, V, C>(&self, options: &DbSpec<K, V, C>) -> io::Result<Db<K, V, C>>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
unsafe { self.as_env_ro().open_db(options) }
}
fn clear_stale_readers(&self) -> io::Result<usize> {
self.as_env_ro().clear_stale_readers()
}
}
impl EnvRw {
pub fn nestable_txns(&self) -> bool {
self.env_ro.backend.nestable_txns
}
pub unsafe fn create_db<K, V, C>(
&mut self,
options: &DbSpec<K, V, C>,
) -> io::Result<Db<K, V, C>>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
unsafe { self.env_ro.open_or_opt_create_db(options, true) }
}
pub fn drop_db<K, V, C>(&mut self, db: Db<K, V, C>) -> io::Result<()>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
let db_backend = ArcByAddr::try_unwrap(db.backend).map_err(|_| {
io::Error::new(io::ErrorKind::Other, "database in use by current process")
})?;
db_backend.assert_env_backend(&self.env_ro.backend);
let dbis = self.env_ro.backend.dbis.lock().unwrap();
unsafe {
let mut txn_inner = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
check_err_code(lmdb::mdb_txn_begin(
self.env_ro.backend.inner,
null_mut(),
0 as LmdbFlags,
txn_inner.as_mut_ptr(),
))?;
let txn_inner = txn_inner.assume_init();
let txn = TxnBackend {
env_ro: &self.env_ro,
inner: txn_inner,
cursors: Default::default(),
};
let db_inner = db_backend.inner;
db_backend.dont_close();
check_err_code(lmdb::mdb_drop(txn.inner, db_inner, 1))?;
txn.commit()?;
}
drop(dbis);
Ok(())
}
pub fn txn_rw(&mut self) -> io::Result<TxnRw<'_>> {
let mut txn_inner = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
unsafe {
check_err_code(lmdb::mdb_txn_begin(
self.env_ro.backend.inner,
null_mut(),
0,
txn_inner.as_mut_ptr(),
))?;
}
let txn_inner = unsafe { txn_inner.assume_init() };
Ok(TxnRw {
backend: UnsafeCell::new(TxnBackend {
env_ro: &self.env_ro,
inner: txn_inner,
cursors: Default::default(),
}),
used_dbs: UsedDbs::Toplevel(Default::default()),
commit_handlers: Default::default(),
})
}
}
impl<'a> TxnBackend<'a> {
fn commit(self) -> io::Result<()> {
let inner = self.inner;
self.dont_abort();
unsafe {
check_err_code(lmdb::mdb_txn_commit(inner))?;
}
Ok(())
}
unsafe fn get_unsafe<'b, 'kr, K, V, C, KRef>(
&mut self,
db: &Db<K, V, C>,
key: KRef,
) -> io::Result<Option<V::AlignedRef<'b>>>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable,
C: Constraint,
KRef: StorableRef<'kr, K>,
{
db.backend.assert_env_backend(&self.env_ro.backend);
let key: K::BytesRef<'_> = key.ref_to_bytes();
let lmdb_key = lmdb::MDB_val {
mv_size: key.len(),
mv_data: key.as_ptr() as *mut _,
};
let mut lmdb_data = MaybeUninit::<lmdb::MDB_val>::uninit();
Ok(
match unsafe {
lmdb::mdb_get(
self.inner,
db.backend.inner,
&lmdb_key as *const _ as *mut lmdb::MDB_val,
lmdb_data.as_mut_ptr(),
)
} {
lmdb::MDB_NOTFOUND => None,
status => {
unsafe { check_err_code(status) }?;
let lmdb_data = unsafe { lmdb_data.assume_init() };
Some(unsafe {
V::from_bytes_unchecked(slice::from_raw_parts(
lmdb_data.mv_data as *const u8,
lmdb_data.mv_size,
))
})
}
},
)
}
fn new_cursor<K, V, C>(&mut self, db: &Db<K, V, C>) -> io::Result<Cursor<K, V, C>>
where
K: ?Sized,
V: ?Sized,
C: Constraint,
{
db.backend.assert_env_backend(&self.env_ro.backend);
let mut inner_cursor = MaybeUninit::<*mut lmdb::MDB_cursor>::uninit();
unsafe {
check_err_code(lmdb::mdb_cursor_open(
self.inner,
db.backend.inner,
inner_cursor.as_mut_ptr(),
))?;
}
let inner_cursor = unsafe { inner_cursor.assume_init() };
let db = db.clone();
let cursor_backend = Arc::new(CursorBackend {
inner: inner_cursor,
closed: Default::default(),
inner_txn: self.inner,
});
self.cursors.push(Arc::downgrade(&cursor_backend));
Ok(Cursor {
backend: ArcByAddr::from(cursor_backend),
db,
})
}
unsafe fn cursor_op_unsafe<'b, 'kr, 'vr, K, V, C, KRef, VRef>(
&mut self,
cursor: &Cursor<K, V, C>,
key: Option<KRef>,
value: Option<VRef>,
op: lmdb::MDB_cursor_op,
) -> io::Result<
Option<(
impl FnOnce() -> K::AlignedRef<'b>,
impl FnOnce() -> V::AlignedRef<'b>,
)>,
>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
C: Constraint,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>,
{
cursor.backend.assert_txn_backend(self);
let mut lmdb_key = match key {
Some(key) => {
let key: K::BytesRef<'_> = key.ref_to_bytes();
lmdb::MDB_val {
mv_size: key.len(),
mv_data: key.as_ptr() as *mut _,
}
}
None => lmdb::MDB_val {
mv_size: 0,
mv_data: null_mut(),
},
};
let mut lmdb_data = match value {
Some(value) => {
let value: V::BytesRef<'_> = value.ref_to_bytes();
lmdb::MDB_val {
mv_size: value.len(),
mv_data: value.as_ptr() as *mut _,
}
}
None => lmdb::MDB_val {
mv_size: 0,
mv_data: null_mut(),
},
};
Ok(
match unsafe {
lmdb::mdb_cursor_get(cursor.backend.inner, &mut lmdb_key, &mut lmdb_data, op)
} {
lmdb::MDB_NOTFOUND => None,
status => {
unsafe { check_err_code(status) }?;
Some(unsafe {
(
move || {
K::from_bytes_unchecked(slice::from_raw_parts(
lmdb_key.mv_data as *const u8,
lmdb_key.mv_size,
))
},
move || {
V::from_bytes_unchecked(slice::from_raw_parts(
lmdb_data.mv_data as *const u8,
lmdb_data.mv_size,
))
},
)
})
}
},
)
}
fn cursor_get_current_value_count<K, V>(
&mut self,
cursor: &Cursor<K, V, KeysDuplicate>,
) -> io::Result<usize>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
{
cursor.backend.assert_txn_backend(self);
let mut count = MaybeUninit::<usize>::uninit();
unsafe {
check_err_code(lmdb::mdb_cursor_count(
cursor.backend.inner,
count.as_mut_ptr(),
))?;
}
Ok(unsafe { count.assume_init() })
}
}
macro_rules! cursor_return_type {
(bool $(,)?) => (bool);
(key $(,)?) => (Option<K::AlignedRef<'_>>);
(value $(,)?) => (Option<V::AlignedRef<'_>>);
((key, value $(,)?)) => (Option<(K::AlignedRef<'_>, V::AlignedRef<'_>)>);
}
macro_rules! cursor_method_def {
($($meta:meta)*, $name:ident, (), $retval:tt $(,)?) => {
$(#[$meta])*
fn $name<K, V, C>(
&self,
cursor: &Cursor<K, V, C>,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint;
};
($($meta:meta)*, $name:ident, (key), $retval:tt $(,)?) => {
$(#[$meta])*
fn $name<'kr, K, V, C, KRef>(
&self,
cursor: &Cursor<K, V, C>,
key: KRef,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable,
C: Constraint,
KRef: StorableRef<'kr, K>;
};
($($meta:meta)*, $name:ident, (key, value), $retval:tt $(,)?) => {
$(#[$meta])*
fn $name<'kr, 'vr, K, V, C, KRef, VRef>(
&self,
cursor: &Cursor<K, V, C>,
key: KRef,
value: VRef,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
C: Constraint,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>;
};
}
macro_rules! cursor_method_defs {
{
$(
$(#[$meta:meta])*
fn $name:ident $args:tt -> $retval:tt;
)*
} => {
$(
cursor_method_def!($($meta)*, $name, $args, $retval);
)*
};
}
macro_rules! cursor_method_dupkey_def {
($($meta:meta)*, $name:ident, (), $retval:tt $(,)?) => {
$(#[$meta])*
fn $name<K, V>(
&self,
cursor: &Cursor<K, V, KeysDuplicate>,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable,
V: ?Sized + Storable;
};
($($meta:meta)*, $name:ident, (key), $retval:tt $(,)?) => {
$(#[$meta])*
fn $name<'kr, K, V, KRef>(
&self,
cursor: &Cursor<K, V, KeysDuplicate>,
key: KRef,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable,
KRef: StorableRef<'kr, K>;
};
($($meta:meta)*, $name:ident, (key, value), $retval:tt $(,)?) => {
$(#[$meta])*
fn $name<'kr, 'vr, K, V, KRef, VRef>(
&self,
cursor: &Cursor<K, V, KeysDuplicate>,
key: KRef,
value: VRef,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>;
};
}
macro_rules! cursor_method_dupkey_defs {
{
$(
$(#[$meta:meta])*
fn $name:ident $args:tt -> $retval:tt;
)*
} => {
$(
cursor_method_dupkey_def!($($meta)*, $name, $args, $retval);
)*
};
}
pub trait Txn {
fn get<'kr, K, V, C, KRef>(
&self,
db: &Db<K, V, C>,
key: KRef,
) -> io::Result<Option<V::AlignedRef<'_>>>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable,
C: Constraint,
KRef: StorableRef<'kr, K>;
fn get_owned<'a, 'kr, K, V, C, KRef>(
&'a self,
db: &Db<K, V, C>,
key: KRef,
) -> io::Result<Option<<V as ToOwned>::Owned>>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + ToOwned,
C: Constraint,
KRef: StorableRef<'kr, K>,
{
Ok(self.get(db, key)?.map(|x| x.into_owned()))
}
fn new_cursor<K, V, C>(&self, db: &Db<K, V, C>) -> io::Result<Cursor<K, V, C>>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint;
fn cursor_get_current_value_count<K, V>(
&self,
cursor: &Cursor<K, V, KeysDuplicate>,
) -> io::Result<usize>
where
K: ?Sized + Storable,
V: ?Sized + Storable;
cursor_method_defs! {
fn cursor_set_first() -> bool;
fn cursor_set_first_get_pair() -> (key, value);
fn cursor_set_last() -> bool;
fn cursor_set_last_get_pair() -> (key, value);
fn cursor_get_current_key() -> key;
fn cursor_get_current_value() -> value;
fn cursor_get_current_pair() -> (key, value);
fn cursor_set_key(key) -> bool;
fn cursor_set_key_get_value(key) -> value;
fn cursor_search_key(key) -> bool;
fn cursor_search_key_get_pair(key) -> (key, value);
fn cursor_set_next() -> bool;
fn cursor_set_next_get_pair() -> (key, value);
fn cursor_set_prev() -> bool;
fn cursor_set_prev_get_pair() -> (key, value);
fn cursor_set_next_key() -> bool;
fn cursor_set_next_key_get_pair() -> (key, value);
fn cursor_set_prev_key() -> bool;
fn cursor_set_prev_key_get_pair() -> (key, value);
}
cursor_method_dupkey_defs! {
fn cursor_set_first_value() -> bool;
fn cursor_set_first_value_get_value() -> value;
fn cursor_set_last_value() -> bool;
fn cursor_set_last_value_get_value() -> value;
fn cursor_set_pair(key, value) -> bool;
fn cursor_set_key_search_value(key, value) -> bool;
fn cursor_set_key_search_value_get_value(key, value) -> value;
fn cursor_set_next_value() -> bool;
fn cursor_set_next_value_get_value() -> value;
fn cursor_set_prev_value() -> bool;
fn cursor_set_prev_value_get_value() -> value;
}
}
macro_rules! cursor_return_value {
(bool, $x:expr $(,)?) => {
match $x {
Some(_) => true,
None => false,
}
};
(key, $x:expr $(,)?) => {
$x.map(|(k, _)| k())
};
(value, $x:expr $(,)?) => {
$x.map(|(_, v)| v())
};
((key, value), $x:expr $(,)?) => {
$x.map(|(k, v)| (k(), v()))
};
}
macro_rules! cursor_method_impl {
($name:ident, (), $retval:tt, $op:ident $(,)?) => {
fn $name<K, V, C>(
&self,
cursor: &Cursor<K, V, C>,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
Ok(cursor_return_value!($retval, unsafe {
let backend = &mut *self.backend.get();
backend.cursor_op_unsafe::<K, V, C, &K, &V>(cursor, None, None, lmdb::$op)?
}))
}
};
($name:ident, (key), $retval:tt, $op:ident) => {
fn $name<'kr, K, V, C, KRef>(
&self,
cursor: &Cursor<K, V, C>,
key: KRef,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable,
C: Constraint,
KRef: StorableRef<'kr, K>,
{
Ok(cursor_return_value!($retval, unsafe {
let backend = &mut *self.backend.get();
backend.cursor_op_unsafe::<K, V, C, KRef, &V>(cursor, Some(key), None, lmdb::$op)?
}))
}
};
($name:ident, (key, value), $retval:tt, $op:ident) => {
fn $name<'kr, 'vr, K, V, C, KRef, VRef>(
&self,
cursor: &Cursor<K, V, C>,
key: KRef,
value: VRef,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
C: Constraint,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>,
{
Ok(cursor_return_value!($retval, unsafe {
let backend = &mut *self.backend.get();
backend.cursor_op_unsafe::<K, V, C, KRef, VRef>(
cursor,
Some(key),
Some(value),
lmdb::$op,
)?
}))
}
};
}
macro_rules! cursor_method_dupkey_impl {
($name:ident, (), $retval:tt, $op:ident $(,)?) => {
fn $name<K, V>(
&self,
cursor: &Cursor<K, V, KeysDuplicate>,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
{
Ok(cursor_return_value!($retval, unsafe {
let backend = &mut *self.backend.get();
backend.cursor_op_unsafe::<K, V, KeysDuplicate, &K, &V>(
cursor,
None,
None,
lmdb::$op,
)?
}))
}
};
($name:ident, (key), $retval:tt, $op:ident) => {
fn $name<'kr, K, V, KRef>(
&self,
cursor: &Cursor<K, V, KeysDuplicate>,
key: KRef,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable,
KRef: StorableRef<'kr, K>,
{
Ok(cursor_return_value!($retval, unsafe {
let backend = &mut *self.backend.get();
backend.cursor_op_unsafe::<K, V, KeysDuplicate, KRef, &V>(
cursor,
Some(key),
None,
lmdb::$op,
)?
}))
}
};
($name:ident, (key, value), $retval:tt, $op:ident) => {
fn $name<'kr, 'vr, K, V, KRef, VRef>(
&self,
cursor: &Cursor<K, V, KeysDuplicate>,
key: KRef,
value: VRef,
) -> io::Result<cursor_return_type!($retval)>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>,
{
Ok(cursor_return_value!($retval, unsafe {
let backend = &mut *self.backend.get();
backend.cursor_op_unsafe::<K, V, KeysDuplicate, KRef, VRef>(
cursor,
Some(key),
Some(value),
lmdb::$op,
)?
}))
}
};
}
macro_rules! cursor_method_impls {
($macro:ident, $dupkey_macro:ident) => {
$macro!(cursor_set_first, (), bool, MDB_cursor_op_MDB_FIRST);
$macro!(
cursor_set_first_get_pair,
(),
(key, value),
MDB_cursor_op_MDB_FIRST
);
$macro!(cursor_set_last, (), bool, MDB_cursor_op_MDB_LAST);
$macro!(
cursor_set_last_get_pair,
(),
(key, value),
MDB_cursor_op_MDB_LAST
);
$macro!(
cursor_get_current_key,
(),
key,
MDB_cursor_op_MDB_GET_CURRENT
);
$macro!(
cursor_get_current_value,
(),
value,
MDB_cursor_op_MDB_GET_CURRENT
);
$macro!(
cursor_get_current_pair,
(),
(key, value),
MDB_cursor_op_MDB_GET_CURRENT
);
$macro!(cursor_set_key, (key), bool, MDB_cursor_op_MDB_SET);
$macro!(
cursor_set_key_get_value,
(key),
value,
MDB_cursor_op_MDB_SET
);
$macro!(cursor_search_key, (key), bool, MDB_cursor_op_MDB_SET_RANGE);
$macro!(
cursor_search_key_get_pair,
(key),
(key, value),
MDB_cursor_op_MDB_SET_RANGE
);
$macro!(cursor_set_next, (), bool, MDB_cursor_op_MDB_NEXT);
$macro!(
cursor_set_next_get_pair,
(),
(key, value),
MDB_cursor_op_MDB_NEXT
);
$macro!(cursor_set_prev, (), bool, MDB_cursor_op_MDB_PREV);
$macro!(
cursor_set_prev_get_pair,
(),
(key, value),
MDB_cursor_op_MDB_PREV
);
$macro!(cursor_set_next_key, (), bool, MDB_cursor_op_MDB_NEXT_NODUP);
$macro!(
cursor_set_next_key_get_pair,
(),
(key, value),
MDB_cursor_op_MDB_NEXT_NODUP
);
$macro!(cursor_set_prev_key, (), bool, MDB_cursor_op_MDB_PREV_NODUP);
$macro!(
cursor_set_prev_key_get_pair,
(),
(key, value),
MDB_cursor_op_MDB_PREV_NODUP
);
$dupkey_macro!(
cursor_set_first_value,
(),
bool,
MDB_cursor_op_MDB_FIRST_DUP
);
$dupkey_macro!(
cursor_set_first_value_get_value,
(),
value,
MDB_cursor_op_MDB_FIRST_DUP
);
$dupkey_macro!(cursor_set_last_value, (), bool, MDB_cursor_op_MDB_LAST_DUP);
$dupkey_macro!(
cursor_set_last_value_get_value,
(),
value,
MDB_cursor_op_MDB_LAST_DUP
);
$dupkey_macro!(
cursor_set_pair,
(key, value),
bool,
MDB_cursor_op_MDB_GET_BOTH
);
$dupkey_macro!(
cursor_set_key_search_value,
(key, value),
bool,
MDB_cursor_op_MDB_GET_BOTH_RANGE
);
$dupkey_macro!(
cursor_set_key_search_value_get_value,
(key, value),
value,
MDB_cursor_op_MDB_GET_BOTH_RANGE
);
$dupkey_macro!(cursor_set_next_value, (), bool, MDB_cursor_op_MDB_NEXT_DUP);
$dupkey_macro!(
cursor_set_next_value_get_value,
(),
value,
MDB_cursor_op_MDB_NEXT_DUP
);
$dupkey_macro!(cursor_set_prev_value, (), bool, MDB_cursor_op_MDB_PREV_DUP);
$dupkey_macro!(
cursor_set_prev_value_get_value,
(),
value,
MDB_cursor_op_MDB_PREV_DUP
);
};
}
impl<'a> Txn for TxnRo<'a> {
fn get<'kr, K, V, C, KRef>(
&self,
db: &Db<K, V, C>,
key: KRef,
) -> io::Result<Option<V::AlignedRef<'_>>>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable,
C: Constraint,
KRef: StorableRef<'kr, K>,
{
unsafe {
let backend = &mut *self.backend.get();
backend.get_unsafe::<K, V, C, KRef>(db, key)
}
}
fn new_cursor<K, V, C>(&self, db: &Db<K, V, C>) -> io::Result<Cursor<K, V, C>>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
let backend = unsafe { &mut *self.backend.get() };
backend.new_cursor(db)
}
fn cursor_get_current_value_count<K, V>(
&self,
cursor: &Cursor<K, V, KeysDuplicate>,
) -> io::Result<usize>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
{
let backend = unsafe { &mut *self.backend.get() };
backend.cursor_get_current_value_count(cursor)
}
cursor_method_impls!(cursor_method_impl, cursor_method_dupkey_impl);
}
impl<'a> Txn for TxnRw<'a> {
fn get<'kr, K, V, C, KRef>(
&self,
db: &Db<K, V, C>,
key: KRef,
) -> io::Result<Option<V::AlignedRef<'_>>>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable,
C: Constraint,
KRef: StorableRef<'kr, K>,
{
unsafe {
let backend = &mut *self.backend.get();
backend.get_unsafe::<K, V, C, KRef>(db, key)
}
}
fn new_cursor<K, V, C>(&self, db: &Db<K, V, C>) -> io::Result<Cursor<K, V, C>>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
let backend = unsafe { &mut *self.backend.get() };
backend.new_cursor(db)
}
fn cursor_get_current_value_count<K, V>(
&self,
cursor: &Cursor<K, V, KeysDuplicate>,
) -> io::Result<usize>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
{
let backend = unsafe { &mut *self.backend.get() };
backend.cursor_get_current_value_count(cursor)
}
cursor_method_impls!(cursor_method_impl, cursor_method_dupkey_impl);
}
impl<'a> TxnRw<'a> {
pub fn on_commit<F>(&mut self, commit_handler: F)
where
F: FnOnce(&mut Self) -> io::Result<()> + 'a,
{
self.commit_handlers.push(Box::new(commit_handler));
}
pub fn commit(mut self) -> io::Result<()> {
for handler in take(&mut self.commit_handlers) {
handler(&mut self)?;
}
self.backend.into_inner().commit()
}
pub fn abort(self) {}
pub fn nested(&mut self) -> io::Result<TxnRw<'_>> {
let backend = self.backend.get_mut();
assert!(
backend.env_ro.backend.nestable_txns,
"environment does not support nested transactions"
);
let mut txn_inner = MaybeUninit::<*mut lmdb::MDB_txn>::uninit();
unsafe {
check_err_code(lmdb::mdb_txn_begin(
backend.env_ro.backend.inner,
backend.inner,
0,
txn_inner.as_mut_ptr(),
))?;
}
let txn_inner = unsafe { txn_inner.assume_init() };
Ok(TxnRw {
backend: UnsafeCell::new(TxnBackend {
env_ro: &backend.env_ro,
inner: txn_inner,
cursors: Default::default(),
}),
used_dbs: UsedDbs::Nested(&mut self.used_dbs),
commit_handlers: Default::default(),
})
}
unsafe fn put_with_flags<'kr, 'vr, K, V, C, KRef, VRef>(
&mut self,
db: &Db<K, V, C>,
key: KRef,
value: VRef,
flags: LmdbFlags,
) -> io::Result<bool>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
C: Constraint,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>,
{
let backend = self.backend.get_mut();
db.backend.assert_env_backend(&backend.env_ro.backend);
let key: K::BytesRef<'_> = key.ref_to_bytes();
let value: V::BytesRef<'_> = value.ref_to_bytes();
backend.env_ro.assert_valid_keysize(&key)?;
if C::DUPLICATE_KEYS {
backend.env_ro.assert_valid_valuesize(&value)?;
}
if !self.used_dbs.contains(&db.backend) {
self.used_dbs.insert(db.backend.to_owned());
}
let lmdb_key = lmdb::MDB_val {
mv_size: key.len(),
mv_data: key.as_ptr() as *mut _,
};
let mut lmdb_data = lmdb::MDB_val {
mv_size: value.len(),
mv_data: value.as_ptr() as *mut _,
};
Ok(
match unsafe {
lmdb::mdb_put(
backend.inner,
db.backend.inner,
&lmdb_key as *const _ as *mut lmdb::MDB_val,
&mut lmdb_data,
flags,
)
} {
lmdb::MDB_KEYEXIST => false,
status => {
unsafe { check_err_code(status) }?;
true
}
},
)
}
pub fn put<'kr, 'vr, K, V, C, KRef, VRef>(
&mut self,
db: &Db<K, V, C>,
key: KRef,
value: VRef,
) -> io::Result<()>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
C: Constraint,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>,
{
unsafe {
self.put_with_flags(db, key, value, 0)?;
}
Ok(())
}
pub fn put_unless_key_exists<'kr, 'vr, K, V, C, KRef, VRef>(
&mut self,
db: &Db<K, V, C>,
key: KRef,
value: VRef,
) -> io::Result<bool>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
C: Constraint,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>,
{
unsafe { self.put_with_flags(db, key, value, lmdb::MDB_NOOVERWRITE) }
}
pub fn put_unless_pair_exists<'kr, 'vr, K, V, KRef, VRef>(
&mut self,
db: &Db<K, V, KeysDuplicate>,
key: KRef,
value: VRef,
) -> io::Result<bool>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>,
{
unsafe { self.put_with_flags(db, key, value, lmdb::MDB_NODUPDATA) }
}
pub fn delete_key<'kr, K, V, C, KRef>(
&mut self,
db: &Db<K, V, C>,
key: KRef,
) -> io::Result<bool>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable,
C: Constraint,
KRef: StorableRef<'kr, K>,
{
let backend = self.backend.get_mut();
db.backend.assert_env_backend(&backend.env_ro.backend);
let key: K::BytesRef<'_> = key.ref_to_bytes();
if !self.used_dbs.contains(&db.backend) {
self.used_dbs.insert(db.backend.to_owned());
}
let lmdb_key = lmdb::MDB_val {
mv_size: key.len(),
mv_data: key.as_ptr() as *mut _,
};
Ok(
match unsafe {
lmdb::mdb_del(
backend.inner,
db.backend.inner,
&lmdb_key as *const _ as *mut lmdb::MDB_val,
null_mut(),
)
} {
lmdb::MDB_NOTFOUND => false,
status => {
unsafe { check_err_code(status) }?;
true
}
},
)
}
pub fn delete_pair<'kr, 'vr, K, V, KRef, VRef>(
&mut self,
db: &Db<K, V, KeysDuplicate>,
key: KRef,
value: VRef,
) -> io::Result<bool>
where
K: ?Sized + Storable + 'kr,
V: ?Sized + Storable + 'vr,
KRef: StorableRef<'kr, K>,
VRef: StorableRef<'vr, V>,
{
let backend = self.backend.get_mut();
db.backend.assert_env_backend(&backend.env_ro.backend);
let key: K::BytesRef<'_> = key.ref_to_bytes();
let value: V::BytesRef<'_> = value.ref_to_bytes();
if !self.used_dbs.contains(&db.backend) {
self.used_dbs.insert(db.backend.to_owned());
}
let lmdb_key = lmdb::MDB_val {
mv_size: key.len(),
mv_data: key.as_ptr() as *mut _,
};
let lmdb_value = lmdb::MDB_val {
mv_size: value.len(),
mv_data: value.as_ptr() as *mut _,
};
Ok(
match unsafe {
lmdb::mdb_del(
backend.inner,
db.backend.inner,
&lmdb_key as *const _ as *mut lmdb::MDB_val,
&lmdb_value as *const _ as *mut lmdb::MDB_val,
)
} {
lmdb::MDB_NOTFOUND => false,
status => {
unsafe { check_err_code(status) }?;
true
}
},
)
}
pub fn delete_all<K, V, C>(&mut self, db: &Db<K, V, C>) -> io::Result<()>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
let backend = self.backend.get_mut();
db.backend.assert_env_backend(&backend.env_ro.backend);
unsafe {
check_err_code(lmdb::mdb_drop(backend.inner, db.backend.inner, 0))?;
}
Ok(())
}
pub fn cursor_delete_current<K, V, C>(&mut self, cursor: &Cursor<K, V, C>) -> io::Result<()>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
C: Constraint,
{
if !self.used_dbs.contains(&cursor.db.backend) {
self.used_dbs.insert(cursor.db.backend.to_owned());
}
let backend = self.backend.get_mut();
cursor.backend.assert_txn_backend(backend);
unsafe { check_err_code(lmdb::mdb_cursor_del(cursor.backend.inner, 0)) }
}
pub fn cursor_delete_current_key<K, V>(
&mut self,
cursor: &Cursor<K, V, KeysDuplicate>,
) -> io::Result<()>
where
K: ?Sized + Storable,
V: ?Sized + Storable,
{
if !self.used_dbs.contains(&cursor.db.backend) {
self.used_dbs.insert(cursor.db.backend.to_owned());
}
let backend = self.backend.get_mut();
cursor.backend.assert_txn_backend(backend);
unsafe {
check_err_code(lmdb::mdb_cursor_del(
cursor.backend.inner,
lmdb::MDB_NODUPDATA as LmdbFlags,
))
}
}
}
impl<K, V, C> Cursor<K, V, C> {
pub fn db(&self) -> &Db<K, V, C> {
&self.db
}
}
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct LmdbVersion {
pub string: &'static str,
pub major: i32,
pub minor: i32,
pub patch: i32,
}
pub fn lmdb_version() -> LmdbVersion {
let mut major: c_int = 0;
let mut minor: c_int = 0;
let mut patch: c_int = 0;
let string: &CStr = unsafe {
CStr::from_ptr(lmdb::mdb_version(
&mut major as *mut c_int,
&mut minor as *mut c_int,
&mut patch as *mut c_int,
))
};
LmdbVersion {
string: string.to_str().unwrap(),
major: major.try_into().unwrap(),
minor: minor.try_into().unwrap(),
patch: patch.try_into().unwrap(),
}
}