#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
/* Perl portability code */
#ifndef cxinc
#define cxinc() Perl_cxinc(aTHX)
#endif
#ifdef SV_UNDEF_RETURNS_NULL
#define MySvPV(sv, len) SvPV_flags(sv, len, SV_GMAGIC|SV_UNDEF_RETURNS_NULL)
#else
#define MySvPV(sv, len) (SvOK(sv)?SvPV_flags(sv, len, SV_GMAGIC):((len=0), NULL))
#endif
#ifndef caller_cx
/* Copied from pp_ctl.c for pre 5.13.5 */
STATIC I32
S_dopoptosub_at(pTHX_ const PERL_CONTEXT *cxstk, I32 startingblock)
{
dVAR;
I32 i;
for (i = startingblock; i >= 0; i--) {
register const PERL_CONTEXT * const cx = &cxstk[i];
switch (CxTYPE(cx)) {
default:
continue;
case CXt_EVAL:
case CXt_SUB:
case CXt_FORMAT:
return i;
}
}
return i;
}
#define dopoptosub_at(c,s) S_dopoptosub_at(aTHX_ c,s)
STATIC const PERL_CONTEXT *
Perl_caller_cx(pTHX_ I32 count, const PERL_CONTEXT **dbcxp)
{
I32 cxix = dopoptosub_at(cxstack, cxstack_ix);
const PERL_CONTEXT *cx;
const PERL_CONTEXT *ccstack = cxstack;
const PERL_SI *top_si = PL_curstackinfo;
for (;;) {
while (cxix < 0 && top_si->si_type != PERLSI_MAIN) {
top_si = top_si->si_prev;
ccstack = top_si->si_cxstack;
cxix = dopoptosub_at(ccstack, top_si->si_cxix);
}
if (cxix < 0)
return NULL;
if (PL_DBsub && GvCV(PL_DBsub) && cxix >= 0 &&
ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub))
count++;
if (!count--)
break;
cxix = dopoptosub_at(ccstack, cxix - 1);
}
cx = &ccstack[cxix];
if (dbcxp) *dbcxp = cx;
if (CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT) {
const I32 dbcxix = dopoptosub_at(ccstack, cxix - 1);
if (PL_DBsub && GvCV(PL_DBsub) && dbcxix >= 0 && ccstack[dbcxix].blk_sub.cv == GvCV(PL_DBsub))
cx = &ccstack[dbcxix];
}
return cx;
}
#define caller_cx(count, dbcxp) Perl_caller_cx(aTHX_ count, dbcxp);
#endif
/*
* Can't use standard SvPVutf8 because the potential upgrade is in place
* and modifying a user scalar in any way is bad practice unless expected.
*/
STATIC char *
S_mySvPVutf8(pTHX_ SV *sv, STRLEN *const len) {
if(!SvOK(sv)) {
*len = 0;
return NULL;
}
SvGETMAGIC(sv);
if(!SvUTF8(sv)) {
sv = sv_mortalcopy(sv);
sv_utf8_upgrade_nomg(sv);
}
return SvPV_nomg(sv, *len);
}
#define MySvPVutf8(sv, len) S_mySvPVutf8(aTHX_ sv, &len)
#include <lmdb.h>
/* My own exportable constants */
#define LMDB_OFLAGN 2
#define LMDB_ZEROCOPY 0x0001
#define LMDB_UTF8 0x0002
#include "const-c.inc"
#define F_ISSET(w, f) (((w) & (f)) == (f))
#define TOHIWORD(F) ((F) << 16)
#define StoreUV(k, v) (void)hv_store(RETVAL, (k), sizeof(k) - 1, newSVuv(v), 0)
typedef IV MyInt;
#if defined PERL_VERSION_GT
#if PERL_VERSION_GT(5,36,'*')
/* lifted from Perl core and simplified [rt.cpan.org #148421] */
STATIC UV
my_do_vecget(pTHX_ SV *sv, STRLEN offset, int size)
{
STRLEN srclen;
const I32 svpv_flags = ((PL_op->op_flags & OPf_MOD || LVRET)
? SV_UNDEF_RETURNS_NULL : 0);
unsigned char *s = (unsigned char *)
SvPV_flags(sv, srclen, (svpv_flags|SV_GMAGIC));
UV retnum = 0;
if (!s) {
s = (unsigned char *)"";
}
/* aka. PERL_ARGS_ASSERT_DO_VECGET */
assert(sv);
/* sanity checks to make sure the premises for our simplifications still hold */
assert(LMDB_OFLAGN <= 8);
if (size != LMDB_OFLAGN)
Perl_croak(aTHX_ "This is a crippled version of vecget that supports size==%d (LMDB_OFLAGN)", LMDB_OFLAGN);
if (SvUTF8(sv)) {
if (Perl_sv_utf8_downgrade_flags(aTHX_ sv, TRUE, 0)) {
/* PVX may have changed */
s = (unsigned char *) SvPV_flags(sv, srclen, svpv_flags);
}
else {
Perl_croak(aTHX_ "Use of strings with code points over 0xFF"
" as arguments to vec is forbidden");
}
}
STRLEN bitoffs = ((offset % 8) * size) % 8;
STRLEN uoffset = offset / (8 / size);
if (uoffset >= srclen)
return 0;
retnum = (s[uoffset] >> bitoffs) & nBIT_MASK(size);
return retnum;
}
#endif
#else
#define my_do_vecget Perl_do_vecget
#endif
static void
populateStat(pTHX_ HV** hashptr, int res, MDB_stat *stat)
{
HV* RETVAL;
if(res)
croak("%s", mdb_strerror(res));
RETVAL = newHV();
StoreUV("psize", stat->ms_psize);
StoreUV("depth", stat->ms_depth);
StoreUV("branch_pages", stat->ms_branch_pages);
StoreUV("leaf_pages", stat->ms_leaf_pages);
StoreUV("overflow_pages", stat->ms_overflow_pages);
StoreUV("entries", stat->ms_entries);
*hashptr = RETVAL;
}
typedef MDB_env* LMDB__Env;
typedef MDB_txn* LMDB__Txn;
typedef MDB_txn* TxnOrNull;
typedef MDB_dbi LMDB;
typedef MDB_val DBD;
typedef MDB_val DBK;
typedef MDB_val DBKC;
typedef MDB_cursor* LMDB__Cursor;
typedef unsigned int flags_t;
#define MY_CXT_KEY "LMDB_File::_guts" XS_VERSION
typedef struct {
LMDB__Env envid;
AV *DCmps;
AV *Cmps;
SV *OFlags;
LMDB curdb;
unsigned int cflags;
SV *my_asv;
SV *my_bsv;
OP *lmdb_dcmp_cop;
} my_cxt_t;
START_MY_CXT
#define LMDB_OFLAGS TOHIWORD(my_do_vecget(aTHX_ MY_CXT.OFlags, dbi, LMDB_OFLAGN))
#define MY_CMP *av_fetch(MY_CXT.Cmps, MY_CXT.curdb, 1)
#define MY_DCMP *av_fetch(MY_CXT.DCmps, MY_CXT.curdb, 1)
#define CHECK_ALLCUR \
envid = mdb_txn_env(txn); \
if(envid != MY_CXT.envid) { \
SV* eidx = sv_2mortal(newSVuv(PTR2UV(MY_CXT.envid = envid))); \
HE* enve = hv_fetch_ent(get_hv("LMDB::Env::Envs", 0), eidx, 0, 0); \
AV* hh = (AV*)SvRV(HeVAL(enve)); \
MY_CXT.DCmps = (AV *)SvRV(*av_fetch(hh, 1, 0)); \
MY_CXT.Cmps = (AV *)SvRV(*av_fetch(hh, 2, 0)); \
MY_CXT.OFlags = *av_fetch(hh, 3, 0); \
MY_CXT.curdb = 0; /* Invalidate cached */ \
} \
if(MY_CXT.curdb != dbi) { \
MY_CXT.curdb = dbi; \
mdb_dbi_flags(txn, dbi, &MY_CXT.cflags); \
MY_CXT.cflags |= LMDB_OFLAGS; \
} \
my_cmpsv = MY_CMP; \
my_dcmpsv = MY_DCMP
#define ISDBKINT F_ISSET(MY_CXT.cflags, MDB_INTEGERKEY)
#define ISDBDINT F_ISSET(MY_CXT.cflags, MDB_DUPSORT|MDB_INTEGERDUP)
#define LwZEROCOPY F_ISSET(MY_CXT.cflags, TOHIWORD(LMDB_ZEROCOPY))
#define LwUTF8 F_ISSET(MY_CXT.cflags, TOHIWORD(LMDB_UTF8))
#define dCURSOR MDB_txn* txn; MDB_dbi dbi
#define PREC_FLGS(c) txn = mdb_cursor_txn(c); dbi = mdb_cursor_dbi(c); CHECK_ALLCUR
#define Sv2DBD(sv, data) \
if(ISDBDINT) { \
SvIV_please(sv); \
data.mv_data = &(((XPVIV*)SvANY(sv))->xiv_iv); \
data.mv_size = sizeof(MyInt); \
} \
else data.mv_data = LwUTF8 ? MySvPVutf8(sv, data.mv_size) \
: MySvPV(sv, data.mv_size)
/* ZeroCopy support
*
* The following code was originally copied from Leon Timmermans's File::Map module
*
* This software is copyright (c) 2008, 2009 by Leon Timmermans <leont@cpan.org>.
* This is free software; you can redistribute it and/or modify it under
* the same terms as perl itself.
*/
#define MMAP_MAGIC_NUMBER 0x4c4d
struct mmap_info {
void* real_address; /* Currently unused */
void* fake_address;
size_t real_length; /* Currently unused */
size_t fake_length;
int isutf8;
#ifdef USE_ITHREADS
perl_mutex count_mutex;
perl_mutex data_mutex;
PerlInterpreter* owner;
perl_cond cond;
int count;
#endif
};
static void
reset_var(pTHX_ SV* var, struct mmap_info* info) {
SvPVX(var) = info->fake_address;
SvLEN(var) = 0;
SvCUR(var) = info->fake_length;
SvPOK_only_UTF8(var);
#if DEBUG_AS_DUAL
SvUV_set(var, PTR2UV(info->fake_address));
SvIOK_on(var);
SvIsUV_on(var);
#endif
}
static void
mmap_fixup(pTHX_ SV* var, struct mmap_info* info, const char* string, STRLEN len) {
if (ckWARN(WARN_SUBSTR)) {
Perl_warn(aTHX_ "Writing directly to a memory mapped var is not recommended");
if (SvCUR(var) > info->fake_length)
Perl_warn(aTHX_ "Truncating new value to size of the memory map");
}
if (string && len)
Copy(string, info->fake_address, MIN(len, info->fake_length), char);
SV_CHECK_THINKFIRST_COW_DROP(var);
if (SvROK(var))
sv_unref_flags(var, SV_IMMEDIATE_UNREF);
if (SvPOK(var))
SvPV_free(var);
reset_var(aTHX_ var, info);
}
static int
mmap_write(pTHX_ SV* var, MAGIC* magic) {
struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
if (!SvOK(var))
mmap_fixup(aTHX_ var, info, NULL, 0);
else if (!SvPOK(var)) {
STRLEN len;
const char* string = info->isutf8 ? MySvPVutf8(var, len) : SvPV(var, len);
mmap_fixup(aTHX_ var, info, string, len);
}
else if (SvPVX(var) != info->fake_address)
mmap_fixup(aTHX_ var, info, SvPVX(var), SvCUR(var));
else
SvPOK_only_UTF8(var);
return 0;
}
static int
mmap_clear(pTHX_ SV* var, MAGIC* magic) {
Perl_die(aTHX_ "Can't clear a mapped variable");
return 0;
}
static int
mmap_free(pTHX_ SV* var, MAGIC* magic) {
struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
#ifdef USE_ITHREADS
MUTEX_LOCK(&info->count_mutex);
if (--info->count == 0) {
COND_DESTROY(&info->cond);
MUTEX_DESTROY(&info->data_mutex);
MUTEX_UNLOCK(&info->count_mutex);
MUTEX_DESTROY(&info->count_mutex);
PerlMemShared_free(info);
}
else {
MUTEX_UNLOCK(&info->count_mutex);
}
#else
PerlMemShared_free(info);
#endif
SvREADONLY_off(var);
SvPV_free(var);
SvPVX(var) = NULL;
SvCUR(var) = 0;
return 0;
}
#ifdef USE_ITHREADS
static int
mmap_dup(pTHX_ MAGIC* magic, CLONE_PARAMS* param)
{
struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
MUTEX_LOCK(&info->count_mutex);
assert(info->count);
++info->count;
MUTEX_UNLOCK(&info->count_mutex);
return 0;
}
#else
#define mmap_dup 0
#endif
#ifdef MGf_LOCAL
static int
mmap_local(pTHX_ SV* var, MAGIC* magic)
{
Perl_croak(aTHX_ "Can't localize file map");
}
#define mmap_local_tail , mmap_local
#else
#define mmap_local_tail
#endif
static MGVTBL
mmap_table = { 0, mmap_write, 0, mmap_clear, mmap_free, 0, mmap_dup mmap_local_tail };
static void
check_new_variable(pTHX_ SV* var)
{
if (SvTYPE(var) > SVt_PVMG && SvTYPE(var) != SVt_PVLV)
Perl_croak(aTHX_ "Trying to map into a nonscalar!\n");
#ifdef sv_unmagicext
sv_unmagicext(var, PERL_MAGIC_uvar, &mmap_table);
#else
sv_unmagic(var, PERL_MAGIC_uvar);
#endif
SV_CHECK_THINKFIRST_COW_DROP(var);
if (SvREADONLY(var))
Perl_croak(aTHX_ "%s", PL_no_modify);
if (SvROK(var))
sv_unref_flags(var, SV_IMMEDIATE_UNREF);
if (SvNIOK(var))
SvNIOK_off(var);
if (SvPOK(var))
SvPV_free(var);
SvUPGRADE(var, SVt_PVMG);
}
static struct mmap_info*
initialize_mmap_info(
pTHX_
void* address,
size_t len,
ptrdiff_t correction,
int isutf8
) {
struct mmap_info* info = PerlMemShared_malloc(sizeof *info);
info->real_address = address;
info->fake_address = (char*)address + correction;
info->real_length = len + correction;
info->fake_length = len;
#ifdef USE_ITHREADS
MUTEX_INIT(&info->count_mutex);
MUTEX_INIT(&info->data_mutex);
COND_INIT(&info->cond);
info->count = 1;
#endif
info->isutf8 = isutf8;
return info;
}
static void
add_magic(
pTHX_
SV* var,
struct mmap_info* info,
const MGVTBL* table,
int writable
) {
MAGIC* magic = sv_magicext(var, NULL, PERL_MAGIC_uvar, table, (const char*) info, 0);
magic->mg_private = MMAP_MAGIC_NUMBER;
#ifdef MGf_LOCAL
magic->mg_flags |= MGf_LOCAL;
#endif
#ifdef USE_ITHREADS
magic->mg_flags |= MGf_DUP;
#endif
if(info->isutf8)
SvUTF8_on(var);
else
SvUTF8_off(var);
SvTAINTED_on(var);
if (!writable)
SvREADONLY_on(var);
}
static void
sv_setstatic(pTHX_ pMY_CXT_ SV *const sv, MDB_val *data, bool is_res)
{
if(ISDBDINT && !is_res)
sv_setiv_mg(sv, *(MyInt *)data->mv_data);
else {
const PERL_CONTEXT *cx = caller_cx(0, NULL);
int utf8 = LwUTF8 && !(CopHINTS_get(cx ? cx->blk_oldcop : PL_curcop) & HINT_BYTES);
if(utf8 && !is_utf8_string(data->mv_data, data->mv_size)) {
if(ckWARN(WARN_UTF8))
Perl_warn(aTHX_ "Malformed UTF-8 in get");
utf8 = 0;
}
if(LwZEROCOPY || is_res) {
struct mmap_info* info;
unsigned int eflags;
int writable;
check_new_variable(aTHX_ sv);
info = initialize_mmap_info(aTHX_ data->mv_data, data->mv_size, 0, utf8);
mdb_env_get_flags(MY_CXT.envid, &eflags);
writable = is_res ||
(F_ISSET(eflags, MDB_WRITEMAP) && !F_ISSET(MY_CXT.cflags, MDB_RDONLY));
add_magic(aTHX_ sv, info, &mmap_table, writable);
reset_var(aTHX_ sv, info);
} else {
sv_setpvn_mg(sv, data->mv_data, data->mv_size);
if(utf8) SvUTF8_on(sv);
else SvUTF8_off(sv);
}
}
}
/* Callback Handling */
static int
LMDB_cmp(const MDB_val *a, const MDB_val *b) {
dTHX;
dMY_CXT;
dSP;
int ret;
ENTER; SAVETMPS;
PUSHMARK(SP);
sv_setpvn_mg(MY_CXT.my_asv, a->mv_data, a->mv_size);
sv_setpvn_mg(MY_CXT.my_bsv, b->mv_data, b->mv_size);
call_sv(SvRV(MY_CMP), G_SCALAR|G_NOARGS);
SPAGAIN;
ret = POPi;
PUTBACK;
FREETMPS; LEAVE;
return ret;
}
#define CvValid(rcv) (SvROK(rcv) && SvTYPE(SvRV(rcv)) == SVt_PVCV)
#define dMCOMMON \
dMY_CXT; \
int needsave = 0; \
SV *my_cmpsv; \
SV *my_dcmpsv; \
LMDB__Env envid
#define MY_PUSH_COMMON \
if(CvValid(my_cmpsv)) { \
mdb_set_compare(txn, dbi, LMDB_cmp); \
needsave++; \
} \
if(UNLIKELY(needsave)) { \
SAVESPTR(MY_CXT.my_asv); \
SAVESPTR(MY_CXT.my_bsv); \
}
#ifdef dMULTICALL
/* If this perl has MULTICALL support, use it for the DATA comparer */
#if PERL_VERSION < 13 || (PERL_VERSION == 13 && PERL_SUBVERSION < 9)
#define FIXREFCOUNT if(CvDEPTH(multicall_cv) > 1) \
SvREFCNT_inc_simple_void_NN(multicall_cv)
#else
#define FIXREFCOUNT
#endif
#if PERL_VERSION < 23 || (PERL_VERSION == 23 && PERL_SUBVERSION < 8)
#define MY_POP_MULTICALL \
if(multicall_cv) { \
FIXREFCOUNT; \
POP_MULTICALL; \
newsp = newsp; \
}
#define MYMCINIT multicall_cv = NULL
#else
#define MY_POP_MULTICALL if(multicall_cop) { POP_MULTICALL; }
#if PERL_VERSION == 23 && PERL_SUBVERSION == 8
#define MYMCINIT multicall_oldcatch = 0
#else
#define MYMCINIT
#endif
#endif
static int
LMDB_dcmp(const MDB_val *a, const MDB_val *b) {
dTHX;
dMY_CXT;
sv_setpvn_mg(MY_CXT.my_asv, a->mv_data, a->mv_size);
sv_setpvn_mg(MY_CXT.my_bsv, b->mv_data, b->mv_size);
PL_op = MY_CXT.lmdb_dcmp_cop;
CALLRUNOPS(aTHX);
return SvIV(*PL_stack_sp);
}
#define dMY_MULTICALL \
dMCOMMON; \
dMULTICALL; \
multicall_cop = NULL; \
I32 gimme = G_SCALAR
#define MY_PUSH_MULTICALL \
MYMCINIT; \
if(CvValid(my_dcmpsv)) { \
PUSH_MULTICALL((CV *)SvRV(my_dcmpsv)); \
MY_CXT.lmdb_dcmp_cop = multicall_cop; \
mdb_set_dupsort(txn, dbi, LMDB_dcmp); \
needsave++; \
} \
MY_PUSH_COMMON
#else /* NO MULTICALL support, use a slow path */
static int
LMDB_dcmp(const MDB_val *a, const MDB_val *b) {
dTHX;
dMY_CXT;
dSP;
int ret;
ENTER; SAVETMPS;
PUSHMARK(SP);
sv_setpvn_mg(MY_CXT.my_asv, a->mv_data, a->mv_size);
sv_setpvn_mg(MY_CXT.my_bsv, b->mv_data, b->mv_size);
call_sv(SvRV(MY_DCMP), G_SCALAR|G_NOARGS);
SPAGAIN;
ret = POPi;
PUTBACK;
FREETMPS; LEAVE;
return ret;
}
#define dMY_MULTICALL dMCOMMON
#define MY_PUSH_MULTICALL \
if(CvValid(my_dcmpsv)) { \
mdb_set_dupsort(txn, dbi, LMDB_dcmp); \
needsave++; \
} \
MY_PUSH_COMMON
#define MY_POP_MULTICALL
#endif /* dMULTICALL */
/* Error Handling */
#define DieOnErrSV GvSV(gv_fetchpv("LMDB_File::die_on_err", 0, SVt_IV))
#define DieOnErr SvTRUEx(DieOnErrSV)
#define LastErrSV GvSV(gv_fetchpv("LMDB_File::last_err", 0, SVt_IV))
#define ProcError(res) \
if(UNLIKELY(res)) { \
sv_setiv(LastErrSV, res); \
sv_setpv(ERRSV, mdb_strerror(res)); \
if(DieOnErr) croak(NULL); \
XSRETURN_IV(res); \
}
MODULE = LMDB_File PACKAGE = LMDB::Env PREFIX = mdb_env_
int
mdb_env_create(env)
LMDB::Env &env = NO_INIT
POSTCALL:
ProcError(RETVAL);
OUTPUT:
env
int
mdb_env_open(env, path, flags, mode)
LMDB::Env env
const char * path
flags_t flags
int mode
PREINIT:
dMY_CXT;
AV* av;
SV* eidx;
POSTCALL:
ProcError(RETVAL);
eidx = sv_2mortal(newSVuv(PTR2UV(MY_CXT.envid = env)));
av = newAV();
av_store(av, 0, newRV_noinc((SV *)newAV())); /* Txns */
av_store(av, 1, newRV_noinc((SV *)(MY_CXT.DCmps = newAV())));
av_store(av, 2, newRV_noinc((SV *)(MY_CXT.Cmps = newAV())));
av_store(av, 3, (MY_CXT.OFlags = newSVpv("",0))); /* FastMode */
hv_store_ent(get_hv("LMDB::Env::Envs", 0), eidx, newRV_noinc((SV *)av), 0);
int
mdb_env_copy(env, path, flags = 0)
LMDB::Env env
const char * path
unsigned flags
CODE:
#if MDB_VERSION_PATCH < 14
if(flags) croak("LMDB_File::copy: This version don't support flags");
RETVAL = mdb_env_copy(env, path);
#else
RETVAL = mdb_env_copy2(env, path, flags);
#endif
ProcError(RETVAL);
OUTPUT:
RETVAL
int
mdb_env_copyfd(env, fd, flags = 0)
LMDB::Env env
mdb_filehandle_t fd
unsigned flags
CODE:
#if MDB_VERSION_PATCH < 14
if(flags) croak("LMDB_File::copyfd: This version don't support flags");
RETVAL = mdb_env_copyfd(env, fd);
#else
RETVAL = mdb_env_copyfd2(env, fd, flags);
#endif
ProcError(RETVAL);
OUTPUT:
RETVAL
HV*
mdb_env_stat(env)
LMDB::Env env
PREINIT:
MDB_stat stat;
CODE:
populateStat(aTHX_ &RETVAL, mdb_env_stat(env, &stat), &stat);
OUTPUT:
RETVAL
HV*
mdb_env_info(env)
LMDB::Env env
PREINIT:
MDB_envinfo stat;
int res;
CODE:
res = mdb_env_info(env, &stat);
ProcError(res);
RETVAL = newHV();
StoreUV("mapaddr", (uintptr_t)stat.me_mapaddr);
StoreUV("mapsize", stat.me_mapsize);
StoreUV("last_pgno", stat.me_last_pgno);
StoreUV("last_txnid", stat.me_last_txnid);
StoreUV("maxreaders", stat.me_maxreaders);
StoreUV("numreaders", stat.me_numreaders);
OUTPUT:
RETVAL
int
mdb_env_sync(env, force=0)
LMDB::Env env
int force
void
mdb_env_close(env)
LMDB::Env env
PREINIT:
dMY_CXT;
SV *eidx;
POSTCALL:
eidx = sv_2mortal(newSVuv(PTR2UV(env)));
MY_CXT.envid = (LMDB__Env)hv_delete_ent(
get_hv("LMDB::Env::Envs", 0), eidx, G_DISCARD, 0
);
int
mdb_env_set_flags(env, flags, onoff)
LMDB::Env env
unsigned int flags
int onoff
#define CHANGEABLE (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC|MDB_NOMEMINIT)
#define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY| \
MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD)
int
mdb_env_get_flags(env, flags)
LMDB::Env env
unsigned int &flags = NO_INIT
POSTCALL:
flags &= (CHANGEABLE|CHANGELESS);
OUTPUT:
flags
int
mdb_env_get_path(env, path)
LMDB::Env env
const char * &path = NO_INIT
OUTPUT:
path
int
mdb_env_set_mapsize(env, size)
LMDB::Env env
size_t size
POSTCALL:
ProcError(RETVAL);
int
mdb_env_set_maxreaders(env, readers)
LMDB::Env env
unsigned int readers
POSTCALL:
ProcError(RETVAL);
int
mdb_env_get_maxreaders(env, readers)
LMDB::Env env
unsigned int &readers = NO_INIT
OUTPUT:
readers
POSTCALL:
ProcError(RETVAL);
int
mdb_env_set_maxdbs(env, dbs)
LMDB::Env env
int dbs
POSTCALL:
ProcError(RETVAL);
int
mdb_env_get_maxkeysize(env)
LMDB::Env env
UV
mdb_env_id(env)
LMDB::Env env
CODE:
RETVAL = PTR2UV(env);
OUTPUT:
RETVAL
void
_clone()
CODE:
MY_CXT_CLONE;
MY_CXT.envid = NULL;
MY_CXT.curdb = 0;
MY_CXT.my_asv = get_sv("::a", GV_ADDMULTI);
MY_CXT.my_bsv = get_sv("::b", GV_ADDMULTI);
BOOT:
MY_CXT_INIT;
MY_CXT.my_asv = get_sv("::a", GV_ADDMULTI);
MY_CXT.my_bsv = get_sv("::b", GV_ADDMULTI);
MODULE = LMDB_File PACKAGE = LMDB::Txn PREFIX = mdb_txn
int
mdb_txn_begin(env, parent, flags, txn)
LMDB::Env env
TxnOrNull parent
flags_t flags
LMDB::Txn &txn = NO_INIT
POSTCALL:
ProcError(RETVAL);
OUTPUT:
txn
UV
mdb_txn_env(txn)
LMDB::Txn txn
CODE:
RETVAL= PTR2UV(mdb_txn_env(txn));
OUTPUT:
RETVAL
int
mdb_txn_commit(txn)
LMDB::Txn txn
POSTCALL:
ProcError(RETVAL);
void
mdb_txn_abort(txn)
LMDB::Txn txn
void
mdb_txn_reset(txn)
LMDB::Txn txn
int
mdb_txn_renew(txn)
LMDB::Txn txn
POSTCALL:
ProcError(RETVAL);
UV
mdb_txn_id(txn)
LMDB::Txn txn
CODE:
RETVAL = PTR2UV(txn);
OUTPUT:
RETVAL
MODULE = LMDB_File PACKAGE = LMDB::Txn PREFIX = mdb_txn_
#if MDB_VERSION_FULL > MDB_VERINT(0,9,14)
size_t
mdb_txn_id(txn)
LMDB::Txn txn
#endif
MODULE = LMDB_File PACKAGE = LMDB::Txn PREFIX = mdb
int
mdb_dbi_open(txn, name, flags, dbi)
LMDB::Txn txn
const char * name = SvOK($arg) ? (const char *)SvPV_nolen($arg) : NULL;
flags_t flags
LMDB &dbi = NO_INIT
PREINIT:
dMY_CXT;
POSTCALL:
ProcError(RETVAL);
mdb_dbi_flags(txn, dbi, &MY_CXT.cflags);
MY_CXT.cflags |= LMDB_OFLAGS;
MY_CXT.curdb = dbi;
OUTPUT:
dbi
MODULE = LMDB_File PACKAGE = LMDB::Cursor PREFIX = mdb_cursor_
int
mdb_cursor_open(txn, dbi, cursor)
LMDB::Txn txn
LMDB dbi
LMDB::Cursor &cursor = NO_INIT
OUTPUT:
cursor
void
mdb_cursor_close(cursor)
LMDB::Cursor cursor
int
mdb_cursor_count(cursor, count)
LMDB::Cursor cursor
UV &count = NO_INIT
OUTPUT:
count
int
mdb_cursor_dbi(cursor)
LMDB::Cursor cursor
int
mdb_cursor_renew(txn, cursor)
LMDB::Txn txn
LMDB::Cursor cursor
UV
mdb_cursor_txn(cursor)
LMDB::Cursor cursor
CODE:
RETVAL = PTR2UV(mdb_cursor_txn(cursor));
OUTPUT:
RETVAL
MODULE = LMDB_File PACKAGE = LMDB::Cursor PREFIX = mdb_cursor
int
mdb_cursor_get(cursor, key, data, op = MDB_NEXT)
PREINIT:
dMY_MULTICALL;
dCURSOR;
INPUT:
LMDB::Cursor cursor +PREC_FLGS($var);
DBKC &key
DBD &data
MDB_cursor_op op
INIT:
MY_PUSH_MULTICALL;
POSTCALL:
MY_POP_MULTICALL;
ProcError(RETVAL);
OUTPUT:
key
data
int
mdb_cursor_put(cursor, key, data, flags = 0, ...)
PREINIT:
dMY_MULTICALL;
dCURSOR;
INPUT:
LMDB::Cursor cursor +PREC_FLGS($var);
DBKC &key
DBD &data = NO_INIT
flags_t flags
INIT:
if(flags & MDB_RESERVE) {
size_t res_size;
size_t max_size = F_ISSET(MY_CXT.cflags, MDB_DUPSORT)
? mdb_env_get_maxkeysize(envid)
: 0xffffffff;
if(items != 5)
croak("%s: MDB_RESERVE needs a length argument (1 .. %zu)",
"LMDB_File::_put", max_size);
res_size = SvUV(ST(5));
if(res_size == 0)
croak("%s: MDB_RESERVE length must be > 0",
"LMDB_File::_put");
if(ISDBDINT && res_size != sizeof(MyInt))
croak("%s: MDB_RESERVE with MDB_INTEGERDUP length should be %zu",
"LMDB_File::_put", sizeof(MyInt));
if(res_size > max_size)
croak("%s: MDB_RESERVE length should be <= %zu", "LMDB_File::_put", max_size);
data.mv_size = res_size;
data.mv_data = NULL;
} else {
/* Normal initialization */
Sv2DBD(ST(2), data);
}
MY_PUSH_MULTICALL;
POSTCALL:
MY_POP_MULTICALL;
if((flags & MDB_NOOVERWRITE) && RETVAL == MDB_KEYEXIST) {
sv_setstatic(aTHX_ aMY_CXT_ ST(2), &data, 0);
SvSETMAGIC(ST(2));
}
ProcError(RETVAL);
if(flags & MDB_RESERVE) {
sv_setstatic(aTHX_ aMY_CXT_ ST(2), &data, 1);
SvSETMAGIC(ST(2));
}
int
mdb_cursor_del(cursor, flags = 0)
PREINIT:
dMY_MULTICALL;
dCURSOR;
INPUT:
LMDB::Cursor cursor +PREC_FLGS($var);
flags_t flags
INIT:
MY_PUSH_MULTICALL;
POSTCALL:
MY_POP_MULTICALL;
ProcError(RETVAL);
MODULE = LMDB_File PACKAGE = LMDB_File PREFIX = mdb
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
INCLUDE: const-xs.inc
#ifdef __GNUC__
#pragma GCC diagnostic warning "-Wmaybe-uninitialized"
#endif
HV*
mdb_stat(txn, dbi)
LMDB::Txn txn
LMDB dbi
PREINIT:
MDB_stat stat;
CODE:
populateStat(aTHX_ &RETVAL, mdb_stat(txn, dbi, &stat), &stat);
OUTPUT:
RETVAL
int
mdb_dbi_flags(txn, dbi, flags)
LMDB::Txn txn
LMDB dbi
unsigned int &flags = NO_INIT
POSTCALL:
ProcError(RETVAL);
OUTPUT:
RETVAL
flags
void
mdb_dbi_close(env, dbi)
LMDB::Env env
LMDB dbi
int
mdb_drop(txn, dbi, del)
LMDB::Txn txn
LMDB dbi
int del
POSTCALL:
ProcError(RETVAL);
=pod
int
mdb_set_compare(txn, dbi, cmp)
LMDB::Txn txn
LMDB dbi
MDB_cmp_func * cmp
int
mdb_set_dupsort(txn, dbi, cmp)
LMDB::Txn txn
LMDB dbi
MDB_cmp_func * cmp
int
mdb_set_relfunc(txn, dbi, rel)
LMDB::Txn txn
LMDB dbi
MDB_rel_func * rel
int
mdb_set_relctx(txn, dbi, ctx)
LMDB::Txn txn
LMDB dbi
void * ctx
=cut
int
mdb_get(txn, dbi, key, data)
PREINIT:
dMY_MULTICALL;
INPUT:
LMDB::Txn txn +CHECK_ALLCUR;
LMDB dbi
DBK &key
DBD &data = NO_INIT
INIT:
MY_PUSH_MULTICALL;
POSTCALL:
MY_POP_MULTICALL;
ProcError(RETVAL);
OUTPUT:
data
int
mdb_put(txn, dbi, key, data, flags = 0, ...)
PREINIT:
dMY_MULTICALL;
INPUT:
LMDB::Txn txn +CHECK_ALLCUR;
LMDB dbi
DBK &key
DBD &data = NO_INIT
flags_t flags
INIT:
if(flags & MDB_RESERVE) {
size_t res_size;
size_t max_size = F_ISSET(MY_CXT.cflags, MDB_DUPSORT)
? mdb_env_get_maxkeysize(envid)
: 0xffffffff;
if(items != 6)
croak("%s: MDB_RESERVE needs a length argument (1 .. %zu)",
"LMDB_File::_put", max_size);
res_size = SvUV(ST(5));
if(res_size == 0)
croak("%s: MDB_RESERVE length must be > 0",
"LMDB_File::_put");
if(ISDBDINT && res_size != sizeof(MyInt))
croak("%s: MDB_RESERVE with MDB_INTEGERDUP length should be %zu",
"LMDB_File::_put", sizeof(MyInt));
if(res_size > max_size)
croak("%s: MDB_RESERVE length should be <= %zu",
"LMDB_File::_put", max_size);
data.mv_size = res_size;
data.mv_data = NULL;
} else {
/* Normal initialization */
Sv2DBD(ST(3), data);
}
MY_PUSH_MULTICALL;
POSTCALL:
MY_POP_MULTICALL;
if((flags & MDB_NOOVERWRITE) && RETVAL == MDB_KEYEXIST) {
sv_setstatic(aTHX_ aMY_CXT_ ST(3), &data, 0);
SvSETMAGIC(ST(3));
}
ProcError(RETVAL);
if(flags & MDB_RESERVE) {
sv_setstatic(aTHX_ aMY_CXT_ ST(3), &data, 1);
SvSETMAGIC(ST(3));
}
int
mdb_del(txn, dbi, key, data)
PREINIT:
dMY_MULTICALL;
INPUT:
LMDB::Txn txn +CHECK_ALLCUR;
LMDB dbi
DBK &key
DBD &data
INIT:
MY_PUSH_MULTICALL;
CODE:
RETVAL = mdb_del(txn, dbi, &key, (SvOK(ST(3)) ? &data : NULL));
MY_POP_MULTICALL;
ProcError(RETVAL);
OUTPUT:
RETVAL
int
mdb_cmp(txn, dbi, a, b)
PREINIT:
dMY_MULTICALL;
INPUT:
LMDB::Txn txn +CHECK_ALLCUR;
LMDB dbi
DBD &a
DBD &b
INIT:
MY_PUSH_MULTICALL;
POSTCALL:
MY_POP_MULTICALL;
int
mdb_dcmp(txn, dbi, a, b)
PREINIT:
dMY_MULTICALL;
INPUT:
LMDB::Txn txn +CHECK_ALLCUR;
LMDB dbi
DBD &a
DBD &b
INIT:
MY_PUSH_MULTICALL;
POSTCALL:
MY_POP_MULTICALL;
MODULE = LMDB_File PACKAGE = LMDB_File PREFIX = mdb_
=pod
int
mdb_reader_list(env, func, ctx)
LMDB::Env env
MDB_msg_func * func
void * ctx
=cut
void
_resetcurdbi()
CODE:
dMY_CXT;
MY_CXT.curdb = 0;
int
mdb_reader_check(env, dead)
LMDB::Env env
int &dead
OUTPUT:
dead
char *
mdb_strerror(err)
int err
char *
mdb_version(major, minor, patch)
int &major = NO_INIT
int &minor = NO_INIT
int &patch = NO_INIT
OUTPUT:
major
minor
patch