batadase/
lmdb.rs

1use super::{Transaction, RwTxn};
2use std::convert::AsMut;
3use culpa::throws;
4pub use error::Error;
5pub use lmdb_sys as sys;
6
7pub mod error;
8
9#[enumflags2::bitflags]
10#[repr(u32)]
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum DbFlags {
13	ReverseKey = sys::MDB_REVERSEKEY, // keys compared in reverse order
14	IntegerKey = sys::MDB_INTEGERKEY, // keys are binary integers in native byte order (u32 [C unsigned int] or usize [C size_t]), all must be same size
15	Create = sys::MDB_CREATE,         // create db if it doesn't exist, only write tx
16
17	DupSort = sys::MDB_DUPSORT, // allow duplicate keys, stored in sorted order, limits the size of data to MDB_MAXKEYSIZE compile-time constant, default 511 bytes
18		DupFixed = sys::MDB_DUPFIXED,     // all values are same size
19		IntegerDup = sys::MDB_INTEGERDUP, // duplicate data items are binary integers
20		ReverseDup = sys::MDB_REVERSEDUP, // duplicate data items should be compared in reverse order
21}
22
23#[repr(i32)]
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum CursorOpFlags {
26	GetCurrent = sys::MDB_GET_CURRENT, // Return key/data at current cursor position
27
28	First = sys::MDB_FIRST, // Position at first key/data item
29	Last = sys::MDB_LAST,   // Position at last key/data item
30
31	Next = sys::MDB_NEXT, // Position at next data item
32	Prev = sys::MDB_PREV, // Position at previous data item
33
34	Set = sys::MDB_SET,            // Position at specified key
35	SetKey = sys::MDB_SET_KEY,     // Position at specified key, return key + data
36	SetRange = sys::MDB_SET_RANGE, // Position at first key greater than or equal to specified key.
37
38	// ONLY DbFlags::DupFixed
39		GetMultiple = sys::MDB_GET_MULTIPLE,   // Return key and up to a page of duplicate data items from current cursor position. Move cursor to prepare for CursorOpFlags::NextMultiple
40		NextMultiple = sys::MDB_NEXT_MULTIPLE, // Return key and up to a page of duplicate data items from next cursor position. Move cursor to prepare for CursorOpFlags::NextMultiple
41
42	// ONLY DbFlags::DupSort
43		FirstDup = sys::MDB_FIRST_DUP, // Position at first data item of current key
44		LastDup = sys::MDB_LAST_DUP,   // Position at last data item of current key
45
46		NextDup = sys::MDB_NEXT_DUP,     // Position at next data item of current key
47		NextNodup = sys::MDB_NEXT_NODUP, // Position at first data item of next key
48		PrevDup = sys::MDB_PREV_DUP,     // Position at previous data item of current key
49		PrevNodup = sys::MDB_PREV_NODUP, // Position at last data item of previous key
50
51		GetBoth = sys::MDB_GET_BOTH,            // Position at key/data pair
52		GetBothRange = sys::MDB_GET_BOTH_RANGE, // position at key, nearest data
53}
54
55#[repr(transparent)]
56struct Val<'a>(sys::MDB_val, std::marker::PhantomData<&'a ()>);
57
58impl<'a> Val<'a> {
59	fn from_buf(mut buf: impl AsMut<[u8]> + 'a) -> Self {
60		let buf = buf.as_mut();
61		Self(sys::MDB_val { mv_size: buf.len(), mv_data: buf.as_mut_ptr().cast() }, std::marker::PhantomData)
62	}
63
64	fn new_outparam<'tx: 'a>(_tx: &'tx impl Transaction) -> Self {
65		Self(sys::MDB_val { mv_size: 0, mv_data: std::ptr::null_mut() }, std::marker::PhantomData)
66	}
67
68	fn as_slice(&self) -> &'a [u8] {
69		unsafe { std::slice::from_raw_parts(self.mv_data.cast::<u8>(), self.mv_size) }
70	}
71}
72
73impl std::ops::Deref for Val<'_> {
74	type Target = sys::MDB_val;
75
76	fn deref(&self) -> &Self::Target { &self.0 }
77}
78
79impl std::ops::DerefMut for Val<'_> {
80	fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
81}
82
83pub(super) struct Cursor<'tx, TX: Transaction>(*mut sys::MDB_cursor, &'tx TX);
84unsafe impl<'tx, TX: Transaction> Send for Cursor<'tx, TX> {}
85unsafe impl<'tx, TX: Transaction> Sync for Cursor<'tx, TX> {}
86
87impl<'tx, TX: Transaction> Cursor<'tx, TX> {
88	#[throws]
89	pub(super) fn open(tx: &'tx TX, dbi: sys::MDB_dbi) -> Self {
90		let mut cursor = std::ptr::null_mut();
91		error::handle_cursor_open_code(unsafe { sys::mdb_cursor_open(tx.raw(), dbi, &mut cursor) })?;
92		Self(cursor, tx)
93	}
94
95	pub(super) fn get(&mut self, flags: CursorOpFlags) -> Option<(&'tx [u8], &'tx [u8])> {
96		let mut key = Val::new_outparam(self.1);
97		let mut value = Val::new_outparam(self.1);
98		if !error::handle_cursor_get_code(unsafe { sys::mdb_cursor_get(self.0, &mut *key, &mut *value, flags as _) }) { return None }
99		Some((
100			key.as_slice(),
101			value.as_slice(),
102		))
103	}
104
105	pub(super) fn get_with_u64_key(&mut self, flags: CursorOpFlags) -> Option<(u64, &'tx [u8])> {
106		let mut key = Val::new_outparam(self.1);
107		let mut value = Val::new_outparam(self.1);
108		if !error::handle_cursor_get_code(unsafe { sys::mdb_cursor_get(self.0, &mut *key, &mut *value, flags as _) }) { return None }
109		debug_assert!(key.mv_size == std::mem::size_of::<u64>());
110		Some((
111			u64::from_ne_bytes(unsafe { *key.mv_data.cast::<[u8; std::mem::size_of::<u64>()]>() }),
112			value.as_slice(),
113		))
114	}
115}
116
117impl<TX: Transaction> Drop for Cursor<'_, TX> {
118	fn drop(&mut self) {
119		unsafe { sys::mdb_cursor_close(self.0) };
120	}
121}
122
123#[throws]
124pub(super) fn put(tx: &RwTxn, dbi: sys::MDB_dbi, key: impl AsMut<[u8]>, val: impl AsMut<[u8]>) {
125	error::handle_put_code(unsafe { sys::mdb_put(tx.raw(), dbi, &mut *Val::from_buf(key), &mut *Val::from_buf(val), 0) })?;
126}
127
128#[throws]
129pub(super) fn del(tx: &RwTxn, dbi: sys::MDB_dbi, key: impl AsMut<[u8]>) -> bool {
130	error::handle_del_code(unsafe { sys::mdb_del(tx.raw(), dbi, &mut *Val::from_buf(key), std::ptr::null_mut()) })?
131}
132
133#[throws]
134pub(super) fn drop(tx: &RwTxn, dbi: sys::MDB_dbi) {
135	error::handle_drop_code(unsafe { sys::mdb_drop(tx.raw(), dbi, 0) })?;
136}
137
138#[throws]
139pub(super) fn get(tx: &impl Transaction, dbi: sys::MDB_dbi, key: impl AsMut<[u8]>) -> Option<&[u8]> {
140	let mut value = Val::new_outparam(tx);
141	if !error::handle_get_code(unsafe { sys::mdb_get(tx.raw(), dbi, &mut *Val::from_buf(key), &mut *value) })? { return None; }
142	Some(value.as_slice())
143}
144
145#[throws]
146pub(super) fn txn_begin(env: *mut sys::MDB_env, flags: u32) -> *mut sys::MDB_txn {
147	let mut tx: *mut sys::MDB_txn = std::ptr::null_mut();
148	error::handle_txn_begin_code(unsafe { sys::mdb_txn_begin(env, std::ptr::null_mut(), flags, &mut tx) })?;
149	tx
150}
151
152#[throws]
153pub(super) fn txn_commit(tx: *mut sys::MDB_txn) {
154	error::handle_txn_commit_code(unsafe { sys::mdb_txn_commit(tx) })?;
155}
156
157#[throws]
158pub(super) fn env_create() -> *mut sys::MDB_env {
159	let mut env: *mut sys::MDB_env = std::ptr::null_mut();
160	error::handle_env_create_code(unsafe { sys::mdb_env_create(&mut env) })?;
161	env
162}
163
164#[throws]
165pub(super) fn env_set_maxdbs(env: *mut sys::MDB_env, maxdbs: u32) {
166	error::handle_env_set_maxdbs_code(unsafe { sys::mdb_env_set_maxdbs(env, maxdbs) })?;
167}
168
169#[throws]
170pub(super) fn env_set_mapsize(env: *mut sys::MDB_env, mapsize: usize) {
171	error::handle_env_set_mapsize_code(unsafe { sys::mdb_env_set_mapsize(env, mapsize) })?;
172}
173
174#[throws]
175pub(super) fn env_set_maxreaders(env: *mut sys::MDB_env, maxreaders: u32) {
176	error::handle_env_set_maxreaders_code(unsafe { sys::mdb_env_set_maxreaders(env, maxreaders) })?;
177}
178
179#[allow(unused_variables)]
180#[throws]
181pub(super) fn env_open(env: *mut sys::MDB_env, path: &std::ffi::CStr, flags: u32, mode: u32) {
182	#[cfg(unix)] let mode = mode;
183	#[cfg(windows)] let mode = 0;
184	error::handle_env_open(unsafe { sys::mdb_env_open(env, path.as_ptr(), flags, mode) })?;
185}
186
187pub(super) fn dbi_open(tx: *mut sys::MDB_txn, name: &[u8], flags: enumflags2::BitFlags<DbFlags>) -> sys::MDB_dbi {
188	let mut dbi: sys::MDB_dbi = 0;
189	error::handle_dbi_open_code(unsafe { sys::mdb_dbi_open(tx, name.as_ptr().cast(), flags.bits(), &mut dbi) });
190	dbi
191}
192
193#[throws]
194pub(super) fn stat(txn: *mut sys::MDB_txn, dbi: sys::MDB_dbi) -> sys::MDB_stat {
195	let mut stat: sys::MDB_stat = unsafe { std::mem::zeroed() };
196	error::handle_stat_code(unsafe { sys::mdb_stat(txn, dbi, &mut stat) })?;
197	stat
198}
199
200pub trait MdbValExt {
201	#[expect(clippy::missing_safety_doc)]
202	unsafe fn as_slice(&self) -> &[u8];
203}
204
205impl MdbValExt for lmdb_sys::MDB_val {
206	unsafe fn as_slice(&self) -> &[u8] {
207		std::slice::from_raw_parts(self.mv_data.cast::<u8>(), self.mv_size)
208	}
209}