use std::char;
use std::cmp::Ord;
use std::ffi::CStr;
use std::mem;
use std::num::Wrapping;
use std::rc::Rc;
use std::slice;
use std::str;
use std::sync::Arc;
use supercow::Supercow;
use crate::Ignore;
use crate::cursor::{Cursor, StaleCursor};
use crate::dbi::Database;
use crate::tx::{ConstTransaction, ReadTransaction, WriteTransaction};
pub use crate::error::{self, LmdbResultExt};
pub trait TxExt {
#[allow(missing_docs)]
type Const;
fn to_const(self) -> Self::Const;
}
impl<'a> TxExt for Rc<ReadTransaction<'a>> {
type Const = Rc<ConstTransaction<'a>>;
fn to_const(self) -> Self::Const {
unsafe { mem::transmute(self) }
}
}
impl<'a> TxExt for Rc<WriteTransaction<'a>> {
type Const = Rc<ConstTransaction<'a>>;
fn to_const(self) -> Self::Const {
unsafe { mem::transmute(self) }
}
}
impl<'a> TxExt for Arc<ReadTransaction<'a>> {
type Const = Arc<ConstTransaction<'a>>;
fn to_const(self) -> Self::Const {
unsafe { mem::transmute(self) }
}
}
impl<'a> TxExt for Arc<WriteTransaction<'a>> {
type Const = Arc<ConstTransaction<'a>>;
fn to_const(self) -> Self::Const {
unsafe { mem::transmute(self) }
}
}
pub trait CreateCursor<'txn> {
fn cursor<'db, DB>(&self, db: DB) -> error::Result<Cursor<'txn, 'db>>
where
DB: Into<Supercow<'db, Database<'db>>>;
}
impl<'txn, 'env: 'txn> CreateCursor<'txn> for &'txn ConstTransaction<'env> {
#[inline]
fn cursor<'db, DB>(&self, db: DB) -> error::Result<Cursor<'txn, 'db>>
where
DB: Into<Supercow<'db, Database<'db>>>,
{
ConstTransaction::cursor(self, db)
}
}
impl<'txn, 'env: 'txn> CreateCursor<'txn> for &'txn ReadTransaction<'env> {
#[inline]
fn cursor<'db, DB>(&self, db: DB) -> error::Result<Cursor<'txn, 'db>>
where
DB: Into<Supercow<'db, Database<'db>>>,
{
ConstTransaction::cursor(self, db)
}
}
impl<'txn, 'env: 'txn> CreateCursor<'txn> for &'txn WriteTransaction<'env> {
#[inline]
fn cursor<'db, DB>(&self, db: DB) -> error::Result<Cursor<'txn, 'db>>
where
DB: Into<Supercow<'db, Database<'db>>>,
{
ConstTransaction::cursor(self, db)
}
}
impl<'txn> CreateCursor<'txn> for Rc<ReadTransaction<'static>> {
#[inline]
fn cursor<'db, DB>(&self, db: DB) -> error::Result<Cursor<'txn, 'db>>
where
DB: Into<Supercow<'db, Database<'db>>>,
{
Cursor::construct(Supercow::shared(self.clone().to_const()), db.into())
}
}
impl<'txn> CreateCursor<'txn> for Arc<ReadTransaction<'static>> {
#[inline]
fn cursor<'db, DB>(&self, db: DB) -> error::Result<Cursor<'txn, 'db>>
where
DB: Into<Supercow<'db, Database<'db>>>,
{
Cursor::construct(Supercow::shared(self.clone().to_const()), db.into())
}
}
impl<'txn> CreateCursor<'txn> for Rc<WriteTransaction<'static>> {
#[inline]
fn cursor<'db, DB>(&self, db: DB) -> error::Result<Cursor<'txn, 'db>>
where
DB: Into<Supercow<'db, Database<'db>>>,
{
Cursor::construct(Supercow::shared(self.clone().to_const()), db.into())
}
}
impl<'txn> CreateCursor<'txn> for Arc<WriteTransaction<'static>> {
#[inline]
fn cursor<'db, DB>(&self, db: DB) -> error::Result<Cursor<'txn, 'db>>
where
DB: Into<Supercow<'db, Database<'db>>>,
{
Cursor::construct(Supercow::shared(self.clone().to_const()), db.into())
}
}
pub trait AssocCursor<'txn> {
fn assoc_cursor<'db>(&self, cursor: StaleCursor<'db>) -> error::Result<Cursor<'txn, 'db>>;
}
impl<'txn, 'env: 'txn> AssocCursor<'txn> for &'txn ReadTransaction<'env> {
fn assoc_cursor<'db>(&self, cursor: StaleCursor<'db>) -> error::Result<Cursor<'txn, 'db>> {
ReadTransaction::assoc_cursor(self, cursor)
}
}
impl<'txn> AssocCursor<'txn> for Rc<ReadTransaction<'static>> {
fn assoc_cursor<'db>(&self, cursor: StaleCursor<'db>) -> error::Result<Cursor<'txn, 'db>> {
Cursor::from_stale(cursor, Supercow::shared(self.clone().to_const()))
}
}
impl<'txn> AssocCursor<'txn> for Arc<ReadTransaction<'static>> {
fn assoc_cursor<'db>(&self, cursor: StaleCursor<'db>) -> error::Result<Cursor<'txn, 'db>> {
Cursor::from_stale(cursor, Supercow::shared(self.clone().to_const()))
}
}
pub trait AsLmdbBytes {
fn as_lmdb_bytes(&self) -> &[u8];
}
pub trait FromLmdbBytes {
fn from_lmdb_bytes(val: &[u8]) -> Result<&Self, String>;
}
pub trait FromReservedLmdbBytes {
unsafe fn from_reserved_lmdb_bytes(val: &mut [u8]) -> &mut Self;
}
pub unsafe trait LmdbRaw: Copy + Sized {
#[must_use]
fn reported_type() -> String {
"?".to_owned()
}
}
#[allow(clippy::missing_safety_doc)]
pub unsafe trait LmdbRawIfUnaligned: Copy + Sized {
#[must_use]
fn reported_type() -> String {
"?".to_owned()
}
}
unsafe impl<T: LmdbRaw> LmdbRawIfUnaligned for T {
fn reported_type() -> String {
<T as LmdbRaw>::reported_type()
}
}
pub unsafe trait LmdbOrdKey: FromLmdbBytes + Ord {
#[must_use]
fn ordered_by_bytes() -> bool {
false
}
#[must_use]
fn ordered_as_integer() -> bool {
false
}
}
pub unsafe trait LmdbOrdKeyIfUnaligned: LmdbRawIfUnaligned + Ord {
#[must_use]
fn ordered_by_bytes() -> bool {
false
}
#[must_use]
fn ordered_as_integer() -> bool {
false
}
}
unsafe impl<T: LmdbRaw + LmdbOrdKey> LmdbOrdKeyIfUnaligned for T {
fn ordered_by_bytes() -> bool {
<T as LmdbOrdKey>::ordered_by_bytes()
}
fn ordered_as_integer() -> bool {
<T as LmdbOrdKey>::ordered_as_integer()
}
}
macro_rules! raw {
($typ:ident) => {
unsafe impl LmdbRawIfUnaligned for $typ {
fn reported_type() -> String {
stringify!($typ).to_owned()
}
}
impl AsLmdbBytes for $typ {
fn as_lmdb_bytes(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
std::ptr::from_ref::<$typ>(self).cast::<u8>(),
mem::size_of::<$typ>(),
)
}
}
}
impl AsLmdbBytes for [$typ] {
fn as_lmdb_bytes(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self.as_ptr().cast::<u8>(),
self.len() * mem::size_of::<$typ>(),
)
}
}
}
};
($typ:ident, Ord) => {
raw!($typ);
unsafe impl LmdbOrdKeyIfUnaligned for $typ {}
};
($typ:ident, Int) => {
raw!($typ);
unsafe impl LmdbOrdKeyIfUnaligned for $typ {
fn ordered_as_integer() -> bool {
true
}
}
};
}
unsafe impl LmdbRaw for u8 {
fn reported_type() -> String {
"u8".to_owned()
}
}
unsafe impl LmdbOrdKey for u8 {
fn ordered_by_bytes() -> bool {
true
}
}
unsafe impl LmdbRaw for i8 {
fn reported_type() -> String {
"i8".to_owned()
}
}
unsafe impl LmdbOrdKey for i8 {}
raw!(u16, Ord);
raw!(i16, Ord);
raw!(u32, Int);
raw!(i32, Ord);
raw!(u64, Ord);
raw!(i64, Ord);
raw!(f32);
raw!(f64);
macro_rules! raw_array {
($n:expr) => {
unsafe impl<V: LmdbRaw> LmdbRaw for [V; $n] {
fn reported_type() -> String {
format!("[{};{}]", V::reported_type(), $n)
}
}
unsafe impl<V: LmdbOrdKey + LmdbRaw> LmdbOrdKey for [V; $n] {
fn ordered_by_bytes() -> bool {
V::ordered_by_bytes()
}
}
};
}
raw_array!(0);
raw_array!(1);
raw_array!(2);
raw_array!(3);
raw_array!(4);
raw_array!(5);
raw_array!(6);
raw_array!(7);
raw_array!(8);
raw_array!(9);
raw_array!(10);
raw_array!(11);
raw_array!(12);
raw_array!(13);
raw_array!(14);
raw_array!(15);
raw_array!(16);
raw_array!(17);
raw_array!(18);
raw_array!(19);
raw_array!(20);
raw_array!(21);
raw_array!(22);
raw_array!(23);
raw_array!(24);
raw_array!(25);
raw_array!(26);
raw_array!(27);
raw_array!(28);
raw_array!(29);
raw_array!(30);
raw_array!(31);
raw_array!(32);
raw_array!(33);
unsafe impl<V: LmdbRawIfUnaligned> LmdbRawIfUnaligned for Wrapping<V> {
fn reported_type() -> String {
format!("Wrapping<{}>", V::reported_type())
}
}
unsafe impl<V: LmdbOrdKeyIfUnaligned> LmdbOrdKeyIfUnaligned for Wrapping<V> {
fn ordered_by_bytes() -> bool {
V::ordered_by_bytes()
}
}
unsafe impl LmdbRaw for () {}
impl<V: LmdbRaw> AsLmdbBytes for V {
fn as_lmdb_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(std::ptr::from_ref::<V>(self).cast::<u8>(), size_of::<V>()) }
}
}
impl<V: LmdbRaw> FromLmdbBytes for V {
fn from_lmdb_bytes(bytes: &[u8]) -> Result<&Self, String> {
let size = size_of::<V>();
let align = align_of::<V>();
if bytes.len() != size {
return Err(format!(
"Type {} is size {}, but byte array has size {}",
V::reported_type(),
size,
bytes.len()
));
}
let misalign = (bytes.as_ptr() as usize) % align;
if 0 != misalign {
return Err(format!(
"Type {} requires alignment {}, but byte array \
at {:08x} is misaligned by {} bytes \
(see https://docs.rs/saferlmdb/{}/\
saferlmdb/traits/trait.LmdbRaw.html#alignment)",
V::reported_type(),
align,
bytes.as_ptr() as usize,
misalign,
env!("CARGO_PKG_VERSION")
));
}
Ok(unsafe { &*bytes.as_ptr().cast::<V>() })
}
}
impl<V: LmdbRaw> FromReservedLmdbBytes for V {
unsafe fn from_reserved_lmdb_bytes(bytes: &mut [u8]) -> &mut Self {
unsafe { &mut *(bytes.as_ptr() as *mut V) }
}
}
impl<V: LmdbRaw> AsLmdbBytes for [V] {
fn as_lmdb_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr().cast::<u8>(), size_of_val(self)) }
}
}
impl<V: LmdbRaw> FromLmdbBytes for [V] {
fn from_lmdb_bytes(bytes: &[u8]) -> Result<&Self, String> {
let size = size_of::<V>();
let align = align_of::<V>();
let size_mod = bytes.len() % size;
if 0 != size_mod {
return Err(format!(
"Type [{}] must have a size which is a multiple \
of {}, but byte array has size {} ({} trailing bytes)",
V::reported_type(),
size,
bytes.len(),
size_mod
));
}
let misalign = (bytes.as_ptr() as usize) % align;
if 0 != misalign {
return Err(format!(
"Type [{}] requires alignment {}, but byte array \
at {:08x} is misaligned by {} bytes \
(see https://docs.rs/saferlmdb/{}/\
saferlmdb/traits/trait.LmdbRaw.html#alignment)",
V::reported_type(),
align,
bytes.as_ptr() as usize,
misalign,
env!("CARGO_PKG_VERSION")
));
}
unsafe {
Ok(slice::from_raw_parts(
bytes.as_ptr().cast::<V>(),
bytes.len() / size,
))
}
}
}
impl<V: LmdbRaw> FromReservedLmdbBytes for [V] {
unsafe fn from_reserved_lmdb_bytes(bytes: &mut [u8]) -> &mut Self {
unsafe { slice::from_raw_parts_mut(bytes.as_ptr() as *mut V, bytes.len() / size_of::<V>()) }
}
}
unsafe impl<V: LmdbOrdKey + LmdbRaw> LmdbOrdKey for [V] {
fn ordered_by_bytes() -> bool {
V::ordered_by_bytes()
}
}
impl AsLmdbBytes for CStr {
fn as_lmdb_bytes(&self) -> &[u8] {
self.to_bytes_with_nul()
}
}
impl FromLmdbBytes for CStr {
fn from_lmdb_bytes(bytes: &[u8]) -> Result<&Self, String> {
CStr::from_bytes_with_nul(bytes).map_err(|_| "NUL byte in CString value".to_owned())
}
}
unsafe impl LmdbOrdKey for CStr {
fn ordered_by_bytes() -> bool {
true
}
}
impl AsLmdbBytes for str {
fn as_lmdb_bytes(&self) -> &[u8] {
self.as_bytes()
}
}
impl FromLmdbBytes for str {
fn from_lmdb_bytes(bytes: &[u8]) -> Result<&str, String> {
str::from_utf8(bytes).map_err(|_| "String is not valid UTF-8".to_owned())
}
}
unsafe impl LmdbOrdKey for str {
fn ordered_by_bytes() -> bool {
true
}
}
impl<V: LmdbRaw> AsLmdbBytes for Vec<V> {
fn as_lmdb_bytes(&self) -> &[u8] {
self[..].as_lmdb_bytes()
}
}
static IGNORE: Ignore = Ignore;
impl FromLmdbBytes for Ignore {
fn from_lmdb_bytes(_: &[u8]) -> Result<&Ignore, String> {
Ok(&IGNORE)
}
}
impl AsLmdbBytes for char {
fn as_lmdb_bytes(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
std::ptr::from_ref::<char>(self).cast::<u8>(),
size_of::<char>(),
)
}
}
}
impl AsLmdbBytes for [char] {
fn as_lmdb_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr().cast::<u8>(), size_of_val(self)) }
}
}