use std::mem;
use std::ptr;
use libc::{self, c_void};
use ffi;
use supercow::{NonSyncSupercow, Supercow, Phantomcow};
use env::Environment;
use error::Result;
use mdb_vals::*;
use traits::*;
use dbi::Database;
use tx::{self, put, del, ConstAccessor, ConstTransaction, WriteAccessor};
use tx::assert_sensible_cursor;
#[derive(Debug)]
struct CursorHandle(*mut ffi::MDB_cursor);
impl Drop for CursorHandle {
fn drop(&mut self) {
unsafe {
ffi::mdb_cursor_close(self.0);
}
}
}
#[derive(Debug)]
pub struct Cursor<'txn,'db> {
cursor: CursorHandle,
txn: NonSyncSupercow<'txn, ConstTransaction<'txn>>,
_db: Phantomcow<'db, Database<'db>>,
}
pub unsafe fn create_cursor<'txn, 'db>(
raw: *mut ffi::MDB_cursor,
txn: NonSyncSupercow<'txn, ConstTransaction<'txn>>,
db: Phantomcow<'db, Database<'db>>)
-> Cursor<'txn, 'db>
{
Cursor {
cursor: CursorHandle(raw),
txn: txn,
_db: db,
}
}
pub fn txn_ref<'a,'txn: 'a,'db>(cursor: &'a Cursor<'txn,'db>)
-> &'a ConstTransaction<'txn> {
&*cursor.txn
}
#[derive(Debug)]
pub struct StaleCursor<'db> {
cursor: CursorHandle,
env: NonSyncSupercow<'db, Environment>,
_db: Phantomcow<'db, Database<'db>>,
}
pub fn to_stale<'a,'db>(cursor: Cursor<'a,'db>,
env: NonSyncSupercow<'db, Environment>)
-> StaleCursor<'db> {
StaleCursor {
cursor: cursor.cursor,
env: env,
_db: cursor._db,
}
}
pub fn env_ref<'a,'db>(cursor: &'a StaleCursor<'db>)
-> &'a Environment {
&*cursor.env
}
pub fn stale_cursor_ptr<'db>(cursor: &StaleCursor<'db>)
-> *mut ffi::MDB_cursor {
cursor.cursor.0
}
macro_rules! cursor_get_0_kv {
($(#[$doc:meta])* fn $method:ident, $op:path) => {
$(#[$doc])*
#[inline]
pub fn $method<'access, K : FromLmdbBytes + ?Sized,
V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor)
-> Result<(&'access K, &'access V)>
{
self.get_0_kv(access, $op)
}
}
}
macro_rules! cursor_get_0_v {
($(#[$doc:meta])* fn $method:ident, $op:path) => {
$(#[$doc])*
#[inline]
pub fn $method<'access, V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor)
-> Result<(&'access V)>
{
self.get_0_v(access, $op)
}
}
}
impl<'txn,'db> Cursor<'txn,'db> {
pub fn construct(
txn: NonSyncSupercow<'txn, ConstTransaction<'txn>>,
db: Supercow<'db, Database<'db>>)
-> Result<Self>
{
try!(tx::assert_same_env(&txn, &db));
let mut raw: *mut ffi::MDB_cursor = ptr::null_mut();
unsafe {
lmdb_call!(ffi::mdb_cursor_open(tx::txptr(&txn), db.as_raw(),
&mut raw));
}
Ok(unsafe { create_cursor(raw, txn,
Supercow::phantom(db)) })
}
pub fn from_stale(
stale: StaleCursor<'db>,
txn: NonSyncSupercow<'txn, ConstTransaction<'txn>>)
-> Result<Self>
{
try!(tx::assert_in_env(&txn, env_ref(&stale)));
unsafe {
lmdb_call!(ffi::mdb_cursor_renew(
tx::txptr(&txn), stale_cursor_ptr(&stale)));
}
Ok(Cursor {
cursor: stale.cursor,
txn: txn,
_db: stale._db,
})
}
#[inline]
fn get_0_kv<'access, K : FromLmdbBytes + ?Sized,
V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor,
op: ffi::MDB_cursor_op) -> Result<(&'access K, &'access V)>
{
try!(assert_sensible_cursor(access, self));
let mut out_key = EMPTY_VAL;
let mut out_val = EMPTY_VAL;
unsafe {
lmdb_call!(ffi::mdb_cursor_get(
self.cursor.0, &mut out_key, &mut out_val, op));
}
Ok((try!(from_val(access, &out_key)),
try!(from_val(access, &out_val))))
}
#[inline]
fn get_0_v<'access, V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor,
op: ffi::MDB_cursor_op) -> Result<&'access V>
{
try!(assert_sensible_cursor(access, self));
let mut null_key = EMPTY_VAL;
let mut out_val = EMPTY_VAL;
unsafe {
lmdb_call!(ffi::mdb_cursor_get(
self.cursor.0, &mut null_key, &mut out_val, op));
}
from_val(access, &out_val)
}
#[inline]
fn get_kv_0<K: AsLmdbBytes + ?Sized, V : AsLmdbBytes + ?Sized>
(&mut self, key: &K, val: &V, op: ffi::MDB_cursor_op) -> Result<()>
{
let mut mv_key = as_val(key);
let mut mv_val = as_val(val);
unsafe {
lmdb_call!(ffi::mdb_cursor_get(
self.cursor.0, &mut mv_key, &mut mv_val, op));
}
Ok(())
}
#[inline]
fn get_kv_v<'access, K : AsLmdbBytes + ?Sized,
V : AsLmdbBytes + FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor,
key: &K, val: &V, op: ffi::MDB_cursor_op) -> Result<&'access V>
{
try!(assert_sensible_cursor(access, self));
let mut mv_key = as_val(key);
let mut inout_val = as_val(val);
unsafe {
lmdb_call!(ffi::mdb_cursor_get(
self.cursor.0, &mut mv_key, &mut inout_val, op));
}
from_val(access, &inout_val)
}
#[inline]
fn get_k_v<'access, K : AsLmdbBytes + ?Sized,
V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor,
key: &K, op: ffi::MDB_cursor_op) -> Result<&'access V>
{
try!(assert_sensible_cursor(access, self));
let mut mv_key = as_val(key);
let mut out_val = EMPTY_VAL;
unsafe {
lmdb_call!(ffi::mdb_cursor_get(
self.cursor.0, &mut mv_key, &mut out_val, op));
}
from_val(access, &out_val)
}
#[inline]
fn get_k_kv<'access, K : AsLmdbBytes + FromLmdbBytes + ?Sized,
V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor,
key: &K, op: ffi::MDB_cursor_op) -> Result<(&'access K, &'access V)>
{
try!(assert_sensible_cursor(access, self));
let mut inout_key = as_val(key);
let mut out_val = EMPTY_VAL;
unsafe {
lmdb_call!(ffi::mdb_cursor_get(
self.cursor.0, &mut inout_key, &mut out_val, op));
}
Ok((try!(from_val(access, &inout_key)),
try!(from_val(access, &out_val))))
}
cursor_get_0_kv! {
fn first, ffi::MDB_cursor_op::MDB_FIRST
}
cursor_get_0_v! {
fn first_dup, ffi::MDB_cursor_op::MDB_FIRST_DUP
}
#[inline]
pub fn seek_kv<K : AsLmdbBytes + ?Sized, V : AsLmdbBytes + ?Sized>
(&mut self, key: &K, val: &V) -> Result<()>
{
self.get_kv_0(key, val, ffi::MDB_cursor_op::MDB_GET_BOTH)
}
#[inline]
pub fn seek_k_nearest_v<'access, K : AsLmdbBytes + ?Sized,
V : AsLmdbBytes + FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor,
key: &K, val: &V) -> Result<&'access V>
{
self.get_kv_v(access, key, val, ffi::MDB_cursor_op::MDB_GET_BOTH_RANGE)
}
cursor_get_0_kv! {
fn get_current, ffi::MDB_cursor_op::MDB_GET_CURRENT
}
#[inline]
pub fn get_multiple<'access, V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor)
-> Result<(&'access V)>
{
try!(assert_sensible_cursor(access, self));
let mut null_key = EMPTY_VAL;
let mut out_val = EMPTY_VAL;
unsafe {
lmdb_call!(ffi::mdb_cursor_get(
self.cursor.0, &mut null_key, &mut out_val,
ffi::MDB_cursor_op::MDB_GET_MULTIPLE));
}
if out_val.mv_data.is_null() {
unsafe {
lmdb_call!(ffi::mdb_cursor_get(
self.cursor.0, &mut null_key, &mut out_val,
ffi::MDB_cursor_op::MDB_GET_CURRENT));
}
}
from_val(access, &out_val)
}
cursor_get_0_v! {
fn next_multiple, ffi::MDB_cursor_op::MDB_NEXT_MULTIPLE
}
cursor_get_0_kv! {
fn last, ffi::MDB_cursor_op::MDB_LAST
}
cursor_get_0_v! {
fn last_dup, ffi::MDB_cursor_op::MDB_LAST_DUP
}
cursor_get_0_kv! {
fn next, ffi::MDB_cursor_op::MDB_NEXT
}
cursor_get_0_kv! {
fn next_dup, ffi::MDB_cursor_op::MDB_NEXT_DUP
}
cursor_get_0_kv! {
fn next_nodup, ffi::MDB_cursor_op::MDB_NEXT_NODUP
}
cursor_get_0_kv! {
fn prev, ffi::MDB_cursor_op::MDB_PREV
}
cursor_get_0_kv! {
fn prev_dup, ffi::MDB_cursor_op::MDB_PREV_DUP
}
cursor_get_0_kv! {
fn prev_nodup, ffi::MDB_cursor_op::MDB_PREV_NODUP
}
#[inline]
pub fn seek_k<'access, K : AsLmdbBytes + ?Sized,
V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor, key: &K)
-> Result<&'access V>
{
self.get_k_v(access, key, ffi::MDB_cursor_op::MDB_SET)
}
#[inline]
pub fn seek_k_both<'access, K : AsLmdbBytes + FromLmdbBytes + ?Sized,
V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor, key: &K)
-> Result<(&'access K, &'access V)>
{
self.get_k_kv(access, key, ffi::MDB_cursor_op::MDB_SET_KEY)
}
#[inline]
pub fn seek_range_k<'access, K : AsLmdbBytes + FromLmdbBytes + ?Sized,
V : FromLmdbBytes + ?Sized>
(&mut self, access: &'access ConstAccessor, key: &K)
-> Result<(&'access K, &'access V)>
{
self.get_k_kv(access, key, ffi::MDB_cursor_op::MDB_SET_RANGE)
}
#[inline]
pub fn put<K : AsLmdbBytes + ?Sized, V : AsLmdbBytes + ?Sized>
(&mut self, access: &mut WriteAccessor,
key: &K, val: &V, flags: put::Flags) -> Result<()>
{
try!(assert_sensible_cursor(&*access, self));
let mut mv_key = as_val(key);
let mut mv_val = as_val(val);
unsafe {
lmdb_call!(ffi::mdb_cursor_put(
self.cursor.0, &mut mv_key, &mut mv_val,
flags.bits()));
}
Ok(())
}
#[inline]
pub fn overwrite<K : AsLmdbBytes + ?Sized, V : AsLmdbBytes + ?Sized>
(&mut self, access: &mut WriteAccessor,
key: &K, val: &V, flags: put::Flags) -> Result<()>
{
try!(assert_sensible_cursor(&*access, self));
let mut mv_key = as_val(key);
let mut mv_val = as_val(val);
unsafe {
lmdb_call!(ffi::mdb_cursor_put(
self.cursor.0, &mut mv_key, &mut mv_val,
flags.bits() | ffi::MDB_CURRENT));
}
Ok(())
}
#[inline]
pub fn reserve<'access, K : AsLmdbBytes + ?Sized,
V : FromReservedLmdbBytes + Sized>
(&mut self, access: &'access mut WriteAccessor,
key: &K, flags: put::Flags) -> Result<&'access mut V>
{
unsafe {
self.reserve_unsized(access, key, mem::size_of::<V>(), flags)
}
}
#[inline]
pub fn reserve_array<'access, K : AsLmdbBytes + ?Sized,
V : LmdbRaw>
(&mut self, access: &'access mut WriteAccessor,
key: &K, count: usize, flags: put::Flags)
-> Result<&'access mut [V]>
{
unsafe {
self.reserve_unsized(
access, key, count * mem::size_of::<V>(), flags)
}
}
pub unsafe fn reserve_unsized<'access, K : AsLmdbBytes + ?Sized,
V : FromReservedLmdbBytes + ?Sized>
(&mut self, access: &'access mut WriteAccessor,
key: &K, size: usize, flags: put::Flags) -> Result<&'access mut V>
{
try!(assert_sensible_cursor(&*access, self));
let mut mv_key = as_val(key);
let mut out_val = EMPTY_VAL;
out_val.mv_size = size;
lmdb_call!(ffi::mdb_cursor_put(
self.cursor.0, &mut mv_key, &mut out_val,
flags.bits() | ffi::MDB_RESERVE));
Ok(from_reserved(access, &out_val))
}
#[inline]
pub fn overwrite_in_place<'access, K : AsLmdbBytes + ?Sized,
V : FromReservedLmdbBytes + Sized>
(&mut self, access: &'access mut WriteAccessor,
key: &K, flags: put::Flags) -> Result<&'access mut V>
{
unsafe {
self.overwrite_in_place_unsized(
access, key, mem::size_of::<V>(), flags)
}
}
#[inline]
pub fn overwrite_in_place_array<'access, K : AsLmdbBytes + ?Sized,
V : LmdbRaw>
(&mut self, access: &'access mut WriteAccessor,
key: &K, count: usize, flags: put::Flags)
-> Result<&'access mut [V]>
{
unsafe {
self.overwrite_in_place_unsized(
access, key, count * mem::size_of::<V>(), flags)
}
}
pub unsafe fn overwrite_in_place_unsized
<'access, K : AsLmdbBytes + ?Sized, V : FromReservedLmdbBytes + ?Sized>
(&mut self, access: &'access mut WriteAccessor,
key: &K, size: usize, flags: put::Flags) -> Result<&'access mut V>
{
try!(assert_sensible_cursor(&*access, self));
let mut mv_key = as_val(key);
let mut out_val = EMPTY_VAL;
out_val.mv_size = size;
lmdb_call!(ffi::mdb_cursor_put(
self.cursor.0, &mut mv_key, &mut out_val,
flags.bits() | ffi::MDB_RESERVE | ffi::MDB_CURRENT));
Ok(from_reserved(access, &out_val))
}
pub fn put_multiple<K : AsLmdbBytes + ?Sized, V : LmdbRaw>
(&mut self, access: &mut WriteAccessor,
key: &K, values: &[V], flags: put::Flags)
-> Result<usize>
{
try!(assert_sensible_cursor(&*access, self));
if values.is_empty() {
return Ok(0);
}
let mut mv_key = as_val(key);
let mut mv_vals = [ ffi::MDB_val {
mv_size: mem::size_of::<V>() as libc::size_t,
mv_data: values.as_lmdb_bytes().as_ptr() as *mut c_void,
}, ffi::MDB_val {
mv_size: values.len() as libc::size_t,
mv_data: ptr::null_mut(),
}];
unsafe {
lmdb_call!(ffi::mdb_cursor_put(
self.cursor.0, &mut mv_key, mv_vals.as_mut_ptr(),
flags.bits() | ffi::MDB_MULTIPLE));
}
Ok(mv_vals[1].mv_size as usize)
}
#[inline]
pub fn del(&mut self, access: &mut WriteAccessor,
flags: del::Flags) -> Result<()> {
try!(assert_sensible_cursor(&*access, self));
unsafe {
lmdb_call!(ffi::mdb_cursor_del(self.cursor.0, flags.bits()));
}
Ok(())
}
#[inline]
pub fn count(&mut self) -> Result<usize> {
let mut raw: libc::size_t = 0;
unsafe {
lmdb_call!(ffi::mdb_cursor_count(self.cursor.0, &mut raw));
}
Ok(raw as usize)
}
}
#[cfg(test)]
mod test {
use dbi::{db, Database, DatabaseOptions};
use error::LmdbResultExt;
use tx::{put, WriteTransaction};
use test_helpers::*;
use unaligned::Unaligned as U;
use unaligned::unaligned;
#[test]
fn get_multiple_with_one_item() {
let env = create_env();
let db = Database::open(
&env, None, &DatabaseOptions::new(
db::DUPSORT | db::INTEGERKEY | db::DUPFIXED | db::INTEGERDUP |
db::CREATE)).unwrap();
let txn = WriteTransaction::new(&env).unwrap();
{
let key: i32 = 42;
let val: i32 = 56;
let mut access = txn.access();
access.put(&db, &key, &val, put::Flags::empty()).unwrap();
let mut cursor = txn.cursor(&db).unwrap();
cursor.seek_k::<U<i32>, U<i32>>(&access, unaligned(&key)).unwrap();
let vals = cursor.get_multiple::<[U<i32>]>(&access).unwrap();
assert_eq!(1, vals.len());
assert_eq!(val, vals[0].get());
assert!(cursor.next_multiple::<[U<i32>]>(&access)
.to_opt().unwrap().is_none());
}
}
}