use crate::{
Stat,
error::{MdbxResult, mdbx_result},
flags::{DatabaseFlags, WriteFlags},
};
use std::{
ffi::{c_char, c_uint, c_void},
mem::size_of,
ptr, slice,
};
#[inline(always)]
pub(crate) unsafe fn get_raw(
txn: *mut ffi::MDBX_txn,
dbi: ffi::MDBX_dbi,
key: &[u8],
) -> MdbxResult<Option<ffi::MDBX_val>> {
let key_val = ffi::MDBX_val { iov_len: key.len(), iov_base: key.as_ptr() as *mut c_void };
let mut data_val = ffi::MDBX_val { iov_len: 0, iov_base: ptr::null_mut() };
match unsafe { ffi::mdbx_get(txn, dbi, &key_val, &mut data_val) } {
ffi::MDBX_SUCCESS => Ok(Some(data_val)),
ffi::MDBX_NOTFOUND => Ok(None),
err_code => Err(crate::MdbxError::from_err_code(err_code)),
}
}
#[inline(always)]
pub(crate) unsafe fn open_db_raw(
txn: *mut ffi::MDBX_txn,
name_ptr: *const c_char,
flags: DatabaseFlags,
) -> MdbxResult<(ffi::MDBX_dbi, DatabaseFlags)> {
let mut dbi: ffi::MDBX_dbi = 0;
let mut actual_flags: c_uint = 0;
let mut _status: c_uint = 0;
mdbx_result(unsafe { ffi::mdbx_dbi_open(txn, name_ptr, flags.bits(), &mut dbi) })?;
mdbx_result(unsafe { ffi::mdbx_dbi_flags_ex(txn, dbi, &mut actual_flags, &mut _status) })?;
#[cfg_attr(not(windows), allow(clippy::useless_conversion))]
let db_flags = DatabaseFlags::from_bits_truncate(actual_flags.try_into().unwrap());
Ok((dbi, db_flags))
}
#[inline(always)]
pub(crate) unsafe fn db_flags_raw(
txn: *mut ffi::MDBX_txn,
dbi: ffi::MDBX_dbi,
) -> MdbxResult<DatabaseFlags> {
let mut flags: c_uint = 0;
let mut _status: c_uint = 0;
mdbx_result(unsafe { ffi::mdbx_dbi_flags_ex(txn, dbi, &mut flags, &mut _status) })?;
#[cfg_attr(not(windows), allow(clippy::useless_conversion))]
Ok(DatabaseFlags::from_bits_truncate(flags.try_into().unwrap()))
}
#[inline(always)]
pub(crate) unsafe fn db_stat_raw(txn: *mut ffi::MDBX_txn, dbi: ffi::MDBX_dbi) -> MdbxResult<Stat> {
let mut stat = Stat::new();
mdbx_result(unsafe { ffi::mdbx_dbi_stat(txn, dbi, stat.mdb_stat(), size_of::<Stat>()) })?;
Ok(stat)
}
#[inline(always)]
pub(crate) unsafe fn commit_raw(
txn: *mut ffi::MDBX_txn,
latency: *mut ffi::MDBX_commit_latency,
) -> MdbxResult<bool> {
mdbx_result(unsafe { ffi::mdbx_txn_commit_ex(txn, latency) })
}
#[inline(always)]
pub(crate) unsafe fn put_raw(
txn: *mut ffi::MDBX_txn,
dbi: ffi::MDBX_dbi,
key: &[u8],
data: &[u8],
flags: WriteFlags,
) -> MdbxResult<()> {
let key_val = ffi::MDBX_val { iov_len: key.len(), iov_base: key.as_ptr() as *mut c_void };
let mut data_val =
ffi::MDBX_val { iov_len: data.len(), iov_base: data.as_ptr() as *mut c_void };
mdbx_result(unsafe { ffi::mdbx_put(txn, dbi, &key_val, &mut data_val, flags.bits()) })?;
Ok(())
}
#[inline(always)]
pub(crate) unsafe fn reserve_raw(
txn: *mut ffi::MDBX_txn,
dbi: ffi::MDBX_dbi,
key: &[u8],
len: usize,
flags: WriteFlags,
) -> MdbxResult<*mut u8> {
let key_val = ffi::MDBX_val { iov_len: key.len(), iov_base: key.as_ptr() as *mut c_void };
let mut data_val = ffi::MDBX_val { iov_len: len, iov_base: ptr::null_mut::<c_void>() };
mdbx_result(unsafe {
ffi::mdbx_put(txn, dbi, &key_val, &mut data_val, flags.bits() | ffi::MDBX_RESERVE)
})?;
Ok(data_val.iov_base as *mut u8)
}
#[inline(always)]
pub(crate) const unsafe fn slice_from_reserved(ptr: *mut u8, len: usize) -> &'static mut [u8] {
unsafe { slice::from_raw_parts_mut(ptr, len) }
}
#[inline(always)]
pub(crate) unsafe fn del_raw(
txn: *mut ffi::MDBX_txn,
dbi: ffi::MDBX_dbi,
key: &[u8],
data: Option<&[u8]>,
) -> MdbxResult<bool> {
let key_val = ffi::MDBX_val { iov_len: key.len(), iov_base: key.as_ptr() as *mut c_void };
let data_val: Option<ffi::MDBX_val> = data
.map(|data| ffi::MDBX_val { iov_len: data.len(), iov_base: data.as_ptr() as *mut c_void });
let ptr = data_val.as_ref().map_or(ptr::null(), |d| d as *const ffi::MDBX_val);
mdbx_result(unsafe { ffi::mdbx_del(txn, dbi, &key_val, ptr) }).map(|_| true).or_else(
|e| match e {
crate::MdbxError::NotFound => Ok(false),
other => Err(other),
},
)
}
#[inline(always)]
pub(crate) unsafe fn clear_db_raw(txn: *mut ffi::MDBX_txn, dbi: ffi::MDBX_dbi) -> MdbxResult<()> {
mdbx_result(unsafe { ffi::mdbx_drop(txn, dbi, false) })?;
Ok(())
}
#[inline(always)]
pub(crate) unsafe fn drop_db_raw(txn: *mut ffi::MDBX_txn, dbi: ffi::MDBX_dbi) -> MdbxResult<()> {
mdbx_result(unsafe { ffi::mdbx_drop(txn, dbi, true) })?;
Ok(())
}
#[inline(always)]
pub(crate) unsafe fn close_db_raw(env: *mut ffi::MDBX_env, dbi: ffi::MDBX_dbi) -> MdbxResult<()> {
mdbx_result(unsafe { ffi::mdbx_dbi_close(env, dbi) })?;
Ok(())
}
#[inline(always)]
pub(crate) unsafe fn is_dirty_raw(
txn: *const ffi::MDBX_txn,
ptr: *const c_void,
) -> MdbxResult<bool> {
mdbx_result(unsafe { ffi::mdbx_is_dirty(txn, ptr) })
}
#[cfg(debug_assertions)]
unsafe fn get_pagesize(txn: *mut ffi::MDBX_txn) -> usize {
let env_ptr = unsafe { ffi::mdbx_txn_env(txn) };
let mut stat: ffi::MDBX_stat = unsafe { std::mem::zeroed() };
unsafe { ffi::mdbx_env_stat_ex(env_ptr, ptr::null(), &mut stat, size_of::<ffi::MDBX_stat>()) };
stat.ms_psize as usize
}
#[cfg(debug_assertions)]
unsafe fn get_last_key(txn: *mut ffi::MDBX_txn, dbi: ffi::MDBX_dbi) -> Option<Vec<u8>> {
let mut cursor: *mut ffi::MDBX_cursor = ptr::null_mut();
if unsafe { ffi::mdbx_cursor_open(txn, dbi, &mut cursor) } != ffi::MDBX_SUCCESS {
return None;
}
let mut key_val = ffi::MDBX_val { iov_len: 0, iov_base: ptr::null_mut() };
let mut data_val = ffi::MDBX_val { iov_len: 0, iov_base: ptr::null_mut() };
let result =
if unsafe { ffi::mdbx_cursor_get(cursor, &mut key_val, &mut data_val, ffi::MDBX_LAST) }
== ffi::MDBX_SUCCESS
{
Some(unsafe {
slice::from_raw_parts(key_val.iov_base as *const u8, key_val.iov_len).to_vec()
})
} else {
None
};
unsafe { ffi::mdbx_cursor_close(cursor) };
result
}
#[cfg(debug_assertions)]
unsafe fn get_last_dup(txn: *mut ffi::MDBX_txn, dbi: ffi::MDBX_dbi, key: &[u8]) -> Option<Vec<u8>> {
let mut cursor: *mut ffi::MDBX_cursor = ptr::null_mut();
if unsafe { ffi::mdbx_cursor_open(txn, dbi, &mut cursor) } != ffi::MDBX_SUCCESS {
return None;
}
let mut key_val = ffi::MDBX_val { iov_len: key.len(), iov_base: key.as_ptr() as *mut c_void };
let mut data_val = ffi::MDBX_val { iov_len: 0, iov_base: ptr::null_mut() };
let result =
if unsafe { ffi::mdbx_cursor_get(cursor, &mut key_val, &mut data_val, ffi::MDBX_SET) }
== ffi::MDBX_SUCCESS
{
if unsafe {
ffi::mdbx_cursor_get(cursor, &mut key_val, &mut data_val, ffi::MDBX_LAST_DUP)
} == ffi::MDBX_SUCCESS
{
Some(unsafe {
slice::from_raw_parts(data_val.iov_base as *const u8, data_val.iov_len).to_vec()
})
} else {
None
}
} else {
None
};
unsafe { ffi::mdbx_cursor_close(cursor) };
result
}
#[cfg(debug_assertions)]
pub(crate) unsafe fn debug_assert_append(
txn: *mut ffi::MDBX_txn,
dbi: ffi::MDBX_dbi,
flags: DatabaseFlags,
key: &[u8],
data: &[u8],
) {
let pagesize = unsafe { get_pagesize(txn) };
let last_key = unsafe { get_last_key(txn, dbi) };
crate::tx::assertions::debug_assert_append(pagesize, flags, key, data, last_key.as_deref());
}
#[inline(always)]
pub(crate) unsafe fn cursor_dup_count(cursor: *mut ffi::MDBX_cursor) -> MdbxResult<usize> {
let mut count: usize = 0;
match unsafe { ffi::mdbx_cursor_count(cursor, &mut count) } {
ffi::MDBX_SUCCESS => Ok(count),
err_code => Err(crate::MdbxError::from_err_code(err_code)),
}
}
#[cfg(debug_assertions)]
pub(crate) unsafe fn debug_assert_append_dup(
txn: *mut ffi::MDBX_txn,
dbi: ffi::MDBX_dbi,
flags: DatabaseFlags,
key: &[u8],
data: &[u8],
) {
let pagesize = unsafe { get_pagesize(txn) };
let last_dup = unsafe { get_last_dup(txn, dbi, key) };
crate::tx::assertions::debug_assert_append_dup(pagesize, flags, key, data, last_dup.as_deref());
}