initial release
This commit is contained in:
parent
952eff3c9a
commit
6498e5a4e3
18 changed files with 10251 additions and 3 deletions
8
3rdLibs
Normal file
8
3rdLibs
Normal file
|
@ -0,0 +1,8 @@
|
|||
o MDB: http://highlandsun.com/hyc/mdb/
|
||||
source : from OpenLDAP git repository
|
||||
commit : 7333b6bdc90c49c332d4728c62c7904e64c95077
|
||||
license: OpenLDAP Public License
|
||||
|
||||
o UTHash: http://uthash.sourceforge.net/
|
||||
version: 1.9.6
|
||||
license: BSD revised
|
3
ChangeLog
Normal file
3
ChangeLog
Normal file
|
@ -0,0 +1,3 @@
|
|||
o Sep 29th, 2012
|
||||
initial release
|
||||
|
49
LICENSE
Normal file
49
LICENSE
Normal file
|
@ -0,0 +1,49 @@
|
|||
-------------------------------------------------------------------------
|
||||
The OpenLDAP Public License
|
||||
Version 2.8, 17 August 2003
|
||||
|
||||
Redistribution and use of this software and associated documentation
|
||||
("Software"), with or without modification, are permitted provided
|
||||
that the following conditions are met:
|
||||
|
||||
1. Redistributions in source form must retain copyright statements
|
||||
and notices,
|
||||
|
||||
2. Redistributions in binary form must reproduce applicable copyright
|
||||
statements and notices, this list of conditions, and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution, and
|
||||
|
||||
3. Redistributions must contain a verbatim copy of this document.
|
||||
|
||||
The OpenLDAP Foundation may revise this license from time to time.
|
||||
Each revision is distinguished by a version number. You may use
|
||||
this Software under terms of this license revision or under the
|
||||
terms of any subsequent revision of the license.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
|
||||
CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
|
||||
OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The names of the authors and copyright holders must not be used in
|
||||
advertising or otherwise to promote the sale, use or other dealing
|
||||
in this Software without specific, written prior permission. Title
|
||||
to copyright in this Software shall at all times remain with copyright
|
||||
holders.
|
||||
|
||||
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
||||
|
||||
Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
|
||||
California, USA. All Rights Reserved. Permission to copy and
|
||||
distribute verbatim copies of this document is granted.
|
||||
-------------------------------------------------------------------------
|
24
Makefile
Normal file
24
Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
# See LICENSE for licensing information.
|
||||
|
||||
MODULE = emdb
|
||||
|
||||
DIALYZER = dialyzer
|
||||
REBAR = rebar
|
||||
|
||||
.PHONY: build clean
|
||||
|
||||
all: ebin priv build
|
||||
|
||||
ebin:
|
||||
@mkdir -p $@
|
||||
|
||||
priv:
|
||||
@mkdir -p $@
|
||||
|
||||
build:
|
||||
@$(REBAR) compile
|
||||
|
||||
clean:
|
||||
@$(REBAR) clean
|
||||
@rm -f *~ */*~ erl_crash.dump
|
||||
@rm -rf ebin priv
|
97
README.md
97
README.md
|
@ -1,4 +1,95 @@
|
|||
emdb
|
||||
====
|
||||
EMDB
====
EMDB is a NIF library for the [Memory-Mapped Database](http://highlandsun.com/hyc/mdb/) database, aka. MDB.
The main purpose of this package is to provide a **very fast** Riak [backend](http://wiki.basho.com/Storage-Backends.html).
|
||||
|
||||
But this module could also be used as a general key-value store to replace:
|
||||
|
||||
* [DETS](http://www.erlang.org/doc/man/dets.html)
|
||||
* TokyoCabinet: [TCERL](http://code.google.com/p/tcerl/)
|
||||
* [QDBM](http://fallabs.com/qdbm/)
|
||||
* [Bitcask](https://github.com/basho/bitcask)
|
||||
* [eLevelDB](https://github.com/basho/eleveldb)
|
||||
* [BerkleyDB](http://www.oracle.com/technetwork/products/berkeleydb/overview/index.html)
|
||||
* ...
Requirements
------------
|
||||
* Erlang R14B04+
* GCC 4.2+ or MS VisualStudio 2010+
Build
-----
$ make
API
---
|
||||
The following functions were implemented:
|
||||
|
||||
* `open/1`: same as emdb:open(DBName, []).
|
||||
* `open/2`: creates a new MDB database. This call also re-open an already existing one. The second argument lets you tune the database. See [mdb.h](http://gitorious.org/mdb/mdb/blobs/master/libraries/libmdb/mdb.h)
|
||||
* `close/2`: closes the database
|
||||
* `put/2`: inserts Key with value Val into the database. Assumes that the key is not present, 'key_exit' is returned otherwise.
|
||||
* `get/1`: retrieves the value stored with Key in the database.
|
||||
* `del/1`: Removes the key-value with key Key from database.
|
||||
* `update/2`: inserts Key with value Val into the database if the key is not present, otherwise updates Key to value Val.
|
||||
* `drop/1`: deletes all key-value pairs in the database.
|
||||
|
||||
|
||||
Usage
-----
$ make
|
||||
|
||||
$ ./start.sh
|
||||
|
||||
%% create a new database
1> {ok, Handle} = emdb:open("/tmp/emdb1").
|
||||
|
||||
%% insert the key <<"a">> with value <<"1">>
2> ok = Handle:put(<<"a">>, <<"1">>).
|
||||
|
||||
%% try to re-insert the same key <<"a">>
3> key_exist = Handle:put(<<"a">>, <<"2">>).
|
||||
|
||||
%% add a new key-value pair
4> ok = Handle:put(<<"b">>, <<"2">>).
|
||||
|
||||
%% search a non-existing key <<"c">>
5> none = Handle:get(<<"c">>).
|
||||
|
||||
%% retrieve the value for key <<"b">>
6> {ok, <<"2">>} = Handle:get(<<"b">>).
|
||||
|
||||
%% retrieve the value for key <<"a">>
7> {ok, <<"1">>} = Handle:get(<<"a">>).
|
||||
|
||||
%% delete key <<"b">>
8> ok = Handle:del(<<"b">>).
|
||||
|
||||
%% search a non-existing key <<"b">>
|
||||
9> none = Handle:get(<<"b">>).
|
||||
|
||||
%% delete a non-existing key <<"z">>
10> none = Handle:del(<<"z">>).
|
||||
11> {ok, <<"1">>} = Handle:get(<<"a">>).
|
||||
%% update the value for key <<"a">>
|
||||
12> ok = Handle:update(<<"a">>, <<"7">>).
|
||||
|
||||
%% check the new value for key <<"a">>
|
||||
13> {ok, <<"7">>} = Handle:get(<<"a">>).
|
||||
|
||||
%% delete all key-value pairs in the database
14> ok = Handle:drop().
|
||||
|
||||
%% try to retrieve key <<"a">> value
15> none = Handle:get(<<"a">>).
|
||||
|
||||
%% close the database
16> ok = Handle:close().
|
||||
|
||||
17> q().
|
||||
|
||||
|
||||
Performance
-----------
For maximum speed, this library use only binaries for both keys and values.
|
||||
See the impressive [microbench](http://highlandsun.com/hyc/mdb/microbench/) against:
|
||||
|
||||
* Google's LevelDB
|
||||
* SQLite
|
||||
* Kyoto TreeDB
|
||||
* BerkeleyDB
|
||||
|
||||
MDB performs better on 64-bit arch.
|
||||
|
||||
|
||||
Supported OSes
--------------
|
||||
|
||||
Should work on 32/64-bit architectures:
|
||||
|
||||
* Linux
|
||||
* OSX
|
||||
* FreeBSD
|
||||
* Windows
|
||||
|
||||
TODO
----
|
||||
|
||||
* Unit tests
* PropEr testing
|
||||
* Bulk "writing"
|
||||
|
||||
Volunteers are always welcome!
Status
|
||||
------
|
||||
#### Work in progress. Don't use it in production!
|
||||
LICENSE
-------
|
||||
EMDB is Copyright (C) 2012 by Aleph Archives, and released under the [OpenLDAP](http://www.OpenLDAP.org/license.html) License.
|
||||
|
||||
EMDB is a NIF library for the Memory-Mapped Database database
|
469
c_src/emdb_drv.c
Normal file
469
c_src/emdb_drv.c
Normal file
|
@ -0,0 +1,469 @@
|
|||
/* -------------------------------------------------------------------------
|
||||
* This file is part of EMDB - Erlang MDB API
|
||||
*
|
||||
* Copyright (c) 2012 by Aleph Archives. All rights reserved.
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* C headers
|
||||
*/
|
||||
|
||||
#include <sys/param.h> /* for MAXPATHLEN constant */
|
||||
#include <erl_nif.h> /* for Erlang NIF interface */
|
||||
#include "uthash.h" /* for uthash */
|
||||
#include "mdb.h" /* for MDB interface */
|
||||
|
||||
|
||||
|
||||
#define FREE(p) (NULL == (p) ? 0 : (free(p), p = NULL))
|
||||
|
||||
#define FAIL_FAST(Error, Goto) \
|
||||
do{ \
|
||||
err = Error; \
|
||||
goto Goto; \
|
||||
}while(0)
|
||||
|
||||
|
||||
struct emdb_map_t {
|
||||
MDB_env * env;
|
||||
MDB_dbi dbi;
|
||||
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
|
||||
static ERL_NIF_TERM atom_ok;
|
||||
static ERL_NIF_TERM atom_none;
|
||||
|
||||
|
||||
static struct emdb_map_t * emdb_map = NULL;
|
||||
|
||||
|
||||
/* emdb ret */
|
||||
#define EMDB_RET_KEY_EXIST "key_exist"
|
||||
|
||||
/* emdb errors */
|
||||
#define EMDB_MALLOC_ERR "error_malloc"
|
||||
#define EMDB_CREATE_ERR "error_create"
|
||||
#define EMDB_MAPSIZE_ERR "error_mapsize"
|
||||
#define EMDB_OPEN_ERR "error_open"
|
||||
#define EMDB_TXN_BEGIN_ERR "error_txn_begin"
|
||||
#define EMDB_TXN_COMMIT_ERR "error_txn_commit"
|
||||
#define EMDB_OPEN_DBI_ERR "error_open_dbi"
|
||||
#define EMDB_INVALID_HANDLE_ERR "error_invalid_handle"
|
||||
#define EMDB_PUT_ERR "error_put"
|
||||
#define EMDB_UPDATE_ERR "error_update"
|
||||
#define EMDB_KEY_NOT_FOUND "error_key_not_found"
|
||||
#define EMDB_DROP_ERR "error_drop"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Error handling callbacks
|
||||
*/
|
||||
|
||||
static void emdb_free (struct emdb_map_t * emdb_obj)
|
||||
{
|
||||
FREE(emdb_obj);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Driver callbacks
|
||||
*/
|
||||
|
||||
static ERL_NIF_TERM emdb_open_nif (ErlNifEnv * env,
|
||||
int argc, const ERL_NIF_TERM argv[])
|
||||
{
|
||||
char dirname [MAXPATHLEN];
|
||||
struct emdb_map_t * node;
|
||||
MDB_txn * txn;
|
||||
char * err;
|
||||
|
||||
if (enif_get_string(env, argv[0], dirname, MAXPATHLEN, ERL_NIF_LATIN1) <= 0)
|
||||
return enif_make_badarg(env);
|
||||
|
||||
if(! (node = calloc(1, sizeof(struct emdb_map_t))))
|
||||
FAIL_FAST(EMDB_MALLOC_ERR, err3);
|
||||
|
||||
if (mdb_env_create(& (node -> env)))
|
||||
FAIL_FAST(EMDB_CREATE_ERR, err2);
|
||||
|
||||
if (mdb_env_set_mapsize(node -> env, 10485760))
|
||||
FAIL_FAST(EMDB_MAPSIZE_ERR, err2);
|
||||
|
||||
if (mdb_env_open(node -> env, dirname, MDB_FIXEDMAP, 0664))
|
||||
FAIL_FAST(EMDB_OPEN_ERR, err2);
|
||||
|
||||
if (mdb_txn_begin(node -> env, NULL, 0, & txn))
|
||||
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err2);
|
||||
|
||||
if (mdb_open(txn, NULL, 0, & (node -> dbi)))
|
||||
FAIL_FAST(EMDB_OPEN_DBI_ERR, err1);
|
||||
|
||||
if (mdb_txn_commit(txn))
|
||||
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err1);
|
||||
|
||||
HASH_ADD_PTR(emdb_map, env, node);
|
||||
|
||||
return enif_make_tuple(env, 2,
|
||||
atom_ok,
|
||||
enif_make_ulong(env, (unsigned long) node -> env));
|
||||
|
||||
err1:
|
||||
mdb_txn_abort(txn);
|
||||
err2:
|
||||
mdb_env_close(node -> env);
|
||||
err3:
|
||||
emdb_free(node);
|
||||
|
||||
return enif_make_atom(env, err);
|
||||
}
|
||||
|
||||
static ERL_NIF_TERM emdb_close_nif (ErlNifEnv * env,
|
||||
int argc, const ERL_NIF_TERM argv[])
|
||||
{
|
||||
MDB_env * handle;
|
||||
struct emdb_map_t * node;
|
||||
unsigned long addr;
|
||||
|
||||
if (! enif_get_ulong(env, argv[0], & addr))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
handle = (MDB_env *) addr;
|
||||
|
||||
HASH_FIND_PTR(emdb_map, & handle, node);
|
||||
if (NULL == node)
|
||||
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
|
||||
|
||||
HASH_DEL(emdb_map, node);
|
||||
|
||||
mdb_env_close(node -> env);
|
||||
emdb_free(node);
|
||||
|
||||
return atom_ok;
|
||||
}
|
||||
|
||||
|
||||
static ERL_NIF_TERM emdb_put_nif (ErlNifEnv * env,
|
||||
int argc, const ERL_NIF_TERM argv[])
|
||||
{
|
||||
ErlNifBinary key;
|
||||
ErlNifBinary val;
|
||||
|
||||
MDB_val mkey;
|
||||
MDB_val mdata;
|
||||
|
||||
MDB_env * handle;
|
||||
MDB_txn * txn;
|
||||
|
||||
struct emdb_map_t * node;
|
||||
unsigned long addr;
|
||||
char * err;
|
||||
int ret;
|
||||
|
||||
if (! enif_get_ulong(env, argv[0], & addr))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
handle = (MDB_env *) addr;
|
||||
|
||||
HASH_FIND_PTR(emdb_map, & handle, node);
|
||||
if (NULL == node)
|
||||
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
|
||||
|
||||
if (! enif_inspect_iolist_as_binary(env, argv[1], &key))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
if (! enif_inspect_iolist_as_binary(env, argv[2], &val))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
if (mdb_txn_begin(node -> env, NULL, 0, & txn))
|
||||
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err2);
|
||||
|
||||
mkey.mv_size = key.size;
|
||||
mkey.mv_data = key.data;
|
||||
mdata.mv_size = val.size;
|
||||
mdata.mv_data = val.data;
|
||||
|
||||
ret = mdb_put(txn, node -> dbi, & mkey, & mdata, MDB_NOOVERWRITE);
|
||||
if (MDB_KEYEXIST == ret)
|
||||
FAIL_FAST(EMDB_RET_KEY_EXIST, err1);
|
||||
if (ret)
|
||||
FAIL_FAST(EMDB_PUT_ERR, err1);
|
||||
|
||||
if (mdb_txn_commit(txn))
|
||||
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err1);
|
||||
|
||||
return atom_ok;
|
||||
|
||||
err1:
|
||||
mdb_txn_abort(txn);
|
||||
err2:
|
||||
return enif_make_atom(env, err);
|
||||
}
|
||||
|
||||
|
||||
static ERL_NIF_TERM emdb_get_nif (ErlNifEnv * env,
|
||||
int argc, const ERL_NIF_TERM argv[])
|
||||
{
|
||||
ErlNifBinary key;
|
||||
ErlNifBinary val;
|
||||
|
||||
MDB_val mkey;
|
||||
MDB_val mdata;
|
||||
|
||||
MDB_env * handle;
|
||||
MDB_txn * txn;
|
||||
|
||||
struct emdb_map_t * node;
|
||||
char * err;
|
||||
unsigned long addr;
|
||||
|
||||
if (! enif_get_ulong(env, argv[0], & addr))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
handle = (MDB_env *) addr;
|
||||
|
||||
HASH_FIND_PTR(emdb_map, & handle, node);
|
||||
if (NULL == node)
|
||||
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
|
||||
|
||||
if (! enif_inspect_iolist_as_binary(env, argv[1], &key))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
mkey.mv_size = key.size;
|
||||
mkey.mv_data = key.data;
|
||||
|
||||
if (mdb_txn_begin(node -> env, NULL, 0, & txn))
|
||||
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err1);
|
||||
|
||||
if(mdb_get(txn, node -> dbi, & mkey, & mdata))
|
||||
{
|
||||
mdb_txn_abort(txn);
|
||||
return atom_none;
|
||||
}
|
||||
|
||||
if (! enif_alloc_binary(mdata.mv_size, & val))
|
||||
FAIL_FAST(EMDB_MALLOC_ERR, err2);
|
||||
|
||||
memcpy(val.data, mdata.mv_data, mdata.mv_size);
|
||||
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
return enif_make_tuple(env, 2,
|
||||
atom_ok,
|
||||
enif_make_binary(env, & val));
|
||||
|
||||
err2:
|
||||
mdb_txn_abort(txn);
|
||||
err1:
|
||||
return enif_make_atom(env, err);
|
||||
}
|
||||
|
||||
|
||||
static ERL_NIF_TERM emdb_del_nif (ErlNifEnv * env,
|
||||
int argc, const ERL_NIF_TERM argv[])
|
||||
{
|
||||
ErlNifBinary key;
|
||||
|
||||
MDB_val mkey;
|
||||
|
||||
MDB_env * handle;
|
||||
MDB_txn * txn;
|
||||
|
||||
struct emdb_map_t * node;
|
||||
char * err;
|
||||
unsigned long addr;
|
||||
int ret;
|
||||
|
||||
if (! enif_get_ulong(env, argv[0], & addr))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
handle = (MDB_env *) addr;
|
||||
|
||||
HASH_FIND_PTR(emdb_map, & handle, node);
|
||||
if (NULL == node)
|
||||
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
|
||||
|
||||
if (! enif_inspect_iolist_as_binary(env, argv[1], &key))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
mkey.mv_size = key.size;
|
||||
mkey.mv_data = key.data;
|
||||
|
||||
if (mdb_txn_begin(node -> env, NULL, 0, & txn))
|
||||
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err);
|
||||
|
||||
ret = mdb_del(txn, node -> dbi, & mkey, NULL);
|
||||
|
||||
if (mdb_txn_commit(txn))
|
||||
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err);
|
||||
|
||||
if(ret)
|
||||
return atom_none;
|
||||
|
||||
return atom_ok;
|
||||
|
||||
err:
|
||||
return enif_make_atom(env, err);
|
||||
}
|
||||
|
||||
|
||||
static ERL_NIF_TERM emdb_update_nif (ErlNifEnv * env,
|
||||
int argc, const ERL_NIF_TERM argv[])
|
||||
{
|
||||
ErlNifBinary key;
|
||||
ErlNifBinary val;
|
||||
|
||||
MDB_val mkey;
|
||||
MDB_val mdata;
|
||||
|
||||
MDB_env * handle;
|
||||
MDB_txn * txn;
|
||||
|
||||
struct emdb_map_t * node;
|
||||
unsigned long addr;
|
||||
char * err;
|
||||
|
||||
if (! enif_get_ulong(env, argv[0], & addr))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
handle = (MDB_env *) addr;
|
||||
|
||||
HASH_FIND_PTR(emdb_map, & handle, node);
|
||||
if (NULL == node)
|
||||
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
|
||||
|
||||
if (! enif_inspect_iolist_as_binary(env, argv[1], &key))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
if (! enif_inspect_iolist_as_binary(env, argv[2], &val))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
if (mdb_txn_begin(node -> env, NULL, 0, & txn))
|
||||
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err2);
|
||||
|
||||
mkey.mv_size = key.size;
|
||||
mkey.mv_data = key.data;
|
||||
mdata.mv_size = val.size;
|
||||
mdata.mv_data = val.data;
|
||||
|
||||
if (mdb_put(txn, node -> dbi, & mkey, & mdata, 0))
|
||||
FAIL_FAST(EMDB_UPDATE_ERR, err1);
|
||||
|
||||
if (mdb_txn_commit(txn))
|
||||
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err1);
|
||||
|
||||
return atom_ok;
|
||||
|
||||
err1:
|
||||
mdb_txn_abort(txn);
|
||||
err2:
|
||||
return enif_make_atom(env, err);
|
||||
}
|
||||
|
||||
|
||||
static ERL_NIF_TERM emdb_drop_nif (ErlNifEnv * env,
|
||||
int argc, const ERL_NIF_TERM argv[])
|
||||
{
|
||||
MDB_env * handle;
|
||||
MDB_txn * txn;
|
||||
struct emdb_map_t * node;
|
||||
unsigned long addr;
|
||||
char * err;
|
||||
int ret;
|
||||
|
||||
if (! enif_get_ulong(env, argv[0], & addr))
|
||||
return enif_make_badarg(env);
|
||||
|
||||
handle = (MDB_env *) addr;
|
||||
|
||||
HASH_FIND_PTR(emdb_map, & handle, node);
|
||||
if (NULL == node)
|
||||
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
|
||||
|
||||
if (mdb_txn_begin(node -> env, NULL, 0, & txn))
|
||||
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err2);
|
||||
|
||||
ret = mdb_drop(txn, node -> dbi, 0);
|
||||
if (ret)
|
||||
FAIL_FAST(EMDB_DROP_ERR, err1);
|
||||
|
||||
if (mdb_txn_commit(txn))
|
||||
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err1);
|
||||
|
||||
return atom_ok;
|
||||
|
||||
err1:
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
err2:
|
||||
return enif_make_atom(env, err);
|
||||
}
|
||||
|
||||
|
||||
static int emdb_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info)
|
||||
{
|
||||
atom_ok = enif_make_atom(env, "ok");
|
||||
atom_none = enif_make_atom(env, "none");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int emdb_reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static int emdb_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM load_info)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static void emdb_unload(ErlNifEnv* env, void* priv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ErlNifFunc nif_funcs [] = {
|
||||
{"open", 1, emdb_open_nif},
|
||||
{"close", 1, emdb_close_nif},
|
||||
{"put", 3, emdb_put_nif},
|
||||
{"get", 2, emdb_get_nif},
|
||||
{"del", 2, emdb_del_nif},
|
||||
{"update", 3, emdb_update_nif},
|
||||
{"drop", 1, emdb_drop_nif}
|
||||
};
|
||||
|
||||
/* driver entry point */
|
||||
ERL_NIF_INIT(emdb_drv,
|
||||
nif_funcs,
|
||||
& emdb_load,
|
||||
& emdb_reload,
|
||||
& emdb_upgrade,
|
||||
& emdb_unload)
|
6763
c_src/mdb.c
Normal file
6763
c_src/mdb.c
Normal file
File diff suppressed because it is too large
Load diff
1065
c_src/mdb.h
Normal file
1065
c_src/mdb.h
Normal file
File diff suppressed because it is too large
Load diff
327
c_src/midl.c
Normal file
327
c_src/midl.c
Normal file
|
@ -0,0 +1,327 @@
|
|||
/** @file midl.c
|
||||
* @brief ldap bdb back-end ID List functions */
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2000-2012 The OpenLDAP Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include "midl.h"
|
||||
|
||||
/** @defgroup internal MDB Internals
|
||||
* @{
|
||||
*/
|
||||
/** @defgroup idls ID List Management
|
||||
* @{
|
||||
*/
|
||||
#define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) )
|
||||
|
||||
#if 0 /* superseded by append/sort */
|
||||
static unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id )
|
||||
{
|
||||
/*
|
||||
* binary search of id in ids
|
||||
* if found, returns position of id
|
||||
* if not found, returns first position greater than id
|
||||
*/
|
||||
unsigned base = 0;
|
||||
unsigned cursor = 1;
|
||||
int val = 0;
|
||||
unsigned n = ids[0];
|
||||
|
||||
while( 0 < n ) {
|
||||
unsigned pivot = n >> 1;
|
||||
cursor = base + pivot + 1;
|
||||
val = CMP( ids[cursor], id );
|
||||
|
||||
if( val < 0 ) {
|
||||
n = pivot;
|
||||
|
||||
} else if ( val > 0 ) {
|
||||
base = cursor;
|
||||
n -= pivot + 1;
|
||||
|
||||
} else {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if( val > 0 ) {
|
||||
++cursor;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
int mdb_midl_insert( MDB_IDL ids, MDB_ID id )
|
||||
{
|
||||
unsigned x, i;
|
||||
|
||||
if (MDB_IDL_IS_RANGE( ids )) {
|
||||
/* if already in range, treat as a dup */
|
||||
if (id >= MDB_IDL_RANGE_FIRST(ids) && id <= MDB_IDL_RANGE_LAST(ids))
|
||||
return -1;
|
||||
if (id < MDB_IDL_RANGE_FIRST(ids))
|
||||
ids[1] = id;
|
||||
else if (id > MDB_IDL_RANGE_LAST(ids))
|
||||
ids[2] = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
x = mdb_midl_search( ids, id );
|
||||
assert( x > 0 );
|
||||
|
||||
if( x < 1 ) {
|
||||
/* internal error */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( x <= ids[0] && ids[x] == id ) {
|
||||
/* duplicate */
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ++ids[0] >= MDB_IDL_DB_MAX ) {
|
||||
if( id < ids[1] ) {
|
||||
ids[1] = id;
|
||||
ids[2] = ids[ids[0]-1];
|
||||
} else if ( ids[ids[0]-1] < id ) {
|
||||
ids[2] = id;
|
||||
} else {
|
||||
ids[2] = ids[ids[0]-1];
|
||||
}
|
||||
ids[0] = MDB_NOID;
|
||||
|
||||
} else {
|
||||
/* insert id */
|
||||
for (i=ids[0]; i>x; i--)
|
||||
ids[i] = ids[i-1];
|
||||
ids[x] = id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
MDB_IDL mdb_midl_alloc()
|
||||
{
|
||||
MDB_IDL ids = malloc((MDB_IDL_UM_MAX+1) * sizeof(MDB_ID));
|
||||
*ids++ = MDB_IDL_UM_MAX;
|
||||
return ids;
|
||||
}
|
||||
|
||||
void mdb_midl_free(MDB_IDL ids)
|
||||
{
|
||||
free(ids-1);
|
||||
}
|
||||
|
||||
int mdb_midl_shrink( MDB_IDL *idp )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
if (ids[-1] > MDB_IDL_UM_MAX) {
|
||||
ids = realloc(ids, (MDB_IDL_UM_MAX+1) * sizeof(MDB_ID));
|
||||
*ids++ = MDB_IDL_UM_MAX;
|
||||
*idp = ids;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append( MDB_IDL *idp, MDB_ID id )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
/* Too big? */
|
||||
if (ids[0] >= ids[-1]) {
|
||||
MDB_IDL idn = ids-1;
|
||||
/* grow it */
|
||||
idn = realloc(idn, (*idn + MDB_IDL_UM_MAX + 1) * sizeof(MDB_ID));
|
||||
if (!idn)
|
||||
return -1;
|
||||
*idn++ += MDB_IDL_UM_MAX;
|
||||
ids = idn;
|
||||
*idp = ids;
|
||||
}
|
||||
ids[0]++;
|
||||
ids[ids[0]] = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
/* Too big? */
|
||||
if (ids[0] + app[0] >= ids[-1]) {
|
||||
MDB_IDL idn = ids-1;
|
||||
/* grow it */
|
||||
idn = realloc(idn, (*idn + app[-1]) * sizeof(MDB_ID));
|
||||
if (!idn)
|
||||
return -1;
|
||||
*idn++ += app[-1];
|
||||
ids = idn;
|
||||
*idp = ids;
|
||||
}
|
||||
memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID));
|
||||
ids[0] += app[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Quicksort + Insertion sort for small arrays */
|
||||
|
||||
#define SMALL 8
|
||||
#define SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; }
|
||||
|
||||
void
|
||||
mdb_midl_sort( MDB_IDL ids )
|
||||
{
|
||||
/* Max possible depth of int-indexed tree * 2 items/level */
|
||||
int istack[sizeof(int)*CHAR_BIT * 2];
|
||||
int i,j,k,l,ir,jstack;
|
||||
MDB_ID a, itmp;
|
||||
|
||||
ir = ids[0];
|
||||
l = 1;
|
||||
jstack = 0;
|
||||
for(;;) {
|
||||
if (ir - l < SMALL) { /* Insertion sort */
|
||||
for (j=l+1;j<=ir;j++) {
|
||||
a = ids[j];
|
||||
for (i=j-1;i>=1;i--) {
|
||||
if (ids[i] >= a) break;
|
||||
ids[i+1] = ids[i];
|
||||
}
|
||||
ids[i+1] = a;
|
||||
}
|
||||
if (jstack == 0) break;
|
||||
ir = istack[jstack--];
|
||||
l = istack[jstack--];
|
||||
} else {
|
||||
k = (l + ir) >> 1; /* Choose median of left, center, right */
|
||||
SWAP(ids[k], ids[l+1]);
|
||||
if (ids[l] < ids[ir]) {
|
||||
SWAP(ids[l], ids[ir]);
|
||||
}
|
||||
if (ids[l+1] < ids[ir]) {
|
||||
SWAP(ids[l+1], ids[ir]);
|
||||
}
|
||||
if (ids[l] < ids[l+1]) {
|
||||
SWAP(ids[l], ids[l+1]);
|
||||
}
|
||||
i = l+1;
|
||||
j = ir;
|
||||
a = ids[l+1];
|
||||
for(;;) {
|
||||
do i++; while(ids[i] > a);
|
||||
do j--; while(ids[j] < a);
|
||||
if (j < i) break;
|
||||
SWAP(ids[i],ids[j]);
|
||||
}
|
||||
ids[l+1] = ids[j];
|
||||
ids[j] = a;
|
||||
jstack += 2;
|
||||
if (ir-i+1 >= j-1) {
|
||||
istack[jstack] = ir;
|
||||
istack[jstack-1] = i;
|
||||
ir = j-1;
|
||||
} else {
|
||||
istack[jstack] = j-1;
|
||||
istack[jstack-1] = l;
|
||||
l = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id )
|
||||
{
|
||||
/*
|
||||
* binary search of id in ids
|
||||
* if found, returns position of id
|
||||
* if not found, returns first position greater than id
|
||||
*/
|
||||
unsigned base = 0;
|
||||
unsigned cursor = 1;
|
||||
int val = 0;
|
||||
unsigned n = ids[0].mid;
|
||||
|
||||
while( 0 < n ) {
|
||||
unsigned pivot = n >> 1;
|
||||
cursor = base + pivot + 1;
|
||||
val = CMP( id, ids[cursor].mid );
|
||||
|
||||
if( val < 0 ) {
|
||||
n = pivot;
|
||||
|
||||
} else if ( val > 0 ) {
|
||||
base = cursor;
|
||||
n -= pivot + 1;
|
||||
|
||||
} else {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if( val > 0 ) {
|
||||
++cursor;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id )
|
||||
{
|
||||
unsigned x, i;
|
||||
|
||||
x = mdb_mid2l_search( ids, id->mid );
|
||||
assert( x > 0 );
|
||||
|
||||
if( x < 1 ) {
|
||||
/* internal error */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( x <= ids[0].mid && ids[x].mid == id->mid ) {
|
||||
/* duplicate */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ids[0].mid >= MDB_IDL_UM_MAX ) {
|
||||
/* too big */
|
||||
return -2;
|
||||
|
||||
} else {
|
||||
/* insert id */
|
||||
ids[0].mid++;
|
||||
for (i=ids[0].mid; i>x; i--)
|
||||
ids[i] = ids[i-1];
|
||||
ids[x] = *id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id )
|
||||
{
|
||||
/* Too big? */
|
||||
if (ids[0].mid >= MDB_IDL_UM_MAX) {
|
||||
return -2;
|
||||
}
|
||||
ids[0].mid++;
|
||||
ids[ids[0].mid] = *id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
195
c_src/midl.h
Normal file
195
c_src/midl.h
Normal file
|
@ -0,0 +1,195 @@
|
|||
/** @file midl.h
|
||||
* @brief mdb ID List header file.
|
||||
*
|
||||
* This file was originally part of back-bdb but has been
|
||||
* modified for use in libmdb. Most of the macros defined
|
||||
* in this file are unused, just left over from the original.
|
||||
*
|
||||
* This file is only used internally in libmdb and its definitions
|
||||
* are not exposed publicly.
|
||||
*/
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2000-2012 The OpenLDAP Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#ifndef _MDB_MIDL_H_
|
||||
#define _MDB_MIDL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup internal MDB Internals
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup idls ID List Management
|
||||
* @{
|
||||
*/
|
||||
/** A generic ID number. These were entryIDs in back-bdb.
|
||||
* Preferably it should have the same size as a pointer.
|
||||
*/
|
||||
typedef size_t MDB_ID;
|
||||
|
||||
/** An IDL is an ID List, a sorted array of IDs. The first
|
||||
* element of the array is a counter for how many actual
|
||||
* IDs are in the list. In the original back-bdb code, IDLs are
|
||||
* sorted in ascending order. For libmdb IDLs are sorted in
|
||||
* descending order.
|
||||
*/
|
||||
typedef MDB_ID *MDB_IDL;
|
||||
|
||||
#define MDB_NOID (~(MDB_ID)0)
|
||||
|
||||
/* IDL sizes - likely should be even bigger
|
||||
* limiting factors: sizeof(ID), thread stack size
|
||||
*/
|
||||
#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
|
||||
#define MDB_IDL_DB_SIZE (1<<MDB_IDL_LOGN)
|
||||
#define MDB_IDL_UM_SIZE (1<<(MDB_IDL_LOGN+1))
|
||||
#define MDB_IDL_UM_SIZEOF (MDB_IDL_UM_SIZE * sizeof(MDB_ID))
|
||||
|
||||
#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE-1)
|
||||
|
||||
#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE-1)
|
||||
|
||||
#define MDB_IDL_IS_RANGE(ids) ((ids)[0] == MDB_NOID)
|
||||
#define MDB_IDL_RANGE_SIZE (3)
|
||||
#define MDB_IDL_RANGE_SIZEOF (MDB_IDL_RANGE_SIZE * sizeof(MDB_ID))
|
||||
#define MDB_IDL_SIZEOF(ids) ((MDB_IDL_IS_RANGE(ids) \
|
||||
? MDB_IDL_RANGE_SIZE : ((ids)[0]+1)) * sizeof(MDB_ID))
|
||||
|
||||
#define MDB_IDL_RANGE_FIRST(ids) ((ids)[1])
|
||||
#define MDB_IDL_RANGE_LAST(ids) ((ids)[2])
|
||||
|
||||
#define MDB_IDL_RANGE( ids, f, l ) \
|
||||
do { \
|
||||
(ids)[0] = MDB_NOID; \
|
||||
(ids)[1] = (f); \
|
||||
(ids)[2] = (l); \
|
||||
} while(0)
|
||||
|
||||
#define MDB_IDL_ZERO(ids) \
|
||||
do { \
|
||||
(ids)[0] = 0; \
|
||||
(ids)[1] = 0; \
|
||||
(ids)[2] = 0; \
|
||||
} while(0)
|
||||
|
||||
#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
|
||||
#define MDB_IDL_IS_ALL( range, ids ) ( (ids)[0] == MDB_NOID \
|
||||
&& (ids)[1] <= (range)[1] && (range)[2] <= (ids)[2] )
|
||||
|
||||
#define MDB_IDL_CPY( dst, src ) (memcpy( dst, src, MDB_IDL_SIZEOF( src ) ))
|
||||
|
||||
#define MDB_IDL_ID( bdb, ids, id ) MDB_IDL_RANGE( ids, id, ((bdb)->bi_lastid) )
|
||||
#define MDB_IDL_ALL( bdb, ids ) MDB_IDL_RANGE( ids, 1, ((bdb)->bi_lastid) )
|
||||
|
||||
#define MDB_IDL_FIRST( ids ) ( (ids)[1] )
|
||||
#define MDB_IDL_LAST( ids ) ( MDB_IDL_IS_RANGE(ids) \
|
||||
? (ids)[2] : (ids)[(ids)[0]] )
|
||||
|
||||
#define MDB_IDL_N( ids ) ( MDB_IDL_IS_RANGE(ids) \
|
||||
? ((ids)[2]-(ids)[1])+1 : (ids)[0] )
|
||||
|
||||
#if 0 /* superseded by append/sort */
|
||||
/** Insert an ID into an IDL.
|
||||
* @param[in,out] ids The IDL to insert into.
|
||||
* @param[in] id The ID to insert.
|
||||
* @return 0 on success, -1 if the ID was already present in the IDL.
|
||||
*/
|
||||
int mdb_midl_insert( MDB_IDL ids, MDB_ID id );
|
||||
#endif
|
||||
|
||||
/** Allocate an IDL.
|
||||
* Allocates memory for an IDL of a default size.
|
||||
* @return IDL on success, NULL on failure.
|
||||
*/
|
||||
MDB_IDL mdb_midl_alloc();
|
||||
|
||||
/** Free an IDL.
|
||||
* @param[in] ids The IDL to free.
|
||||
*/
|
||||
void mdb_midl_free(MDB_IDL ids);
|
||||
|
||||
/** Shrink an IDL.
|
||||
* Return the IDL to the default size if it has grown larger.
|
||||
* @param[in,out] idp Address of the IDL to shrink.
|
||||
* @return 0 on no change, non-zero if shrunk.
|
||||
*/
|
||||
int mdb_midl_shrink(MDB_IDL *idp);
|
||||
|
||||
/** Append an ID onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] id The ID to append.
|
||||
* @return 0 on success, -1 if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append( MDB_IDL *idp, MDB_ID id );
|
||||
|
||||
/** Append an IDL onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] app The IDL to append.
|
||||
* @return 0 on success, -1 if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app );
|
||||
|
||||
/** Sort an IDL.
|
||||
* @param[in,out] ids The IDL to sort.
|
||||
*/
|
||||
void mdb_midl_sort( MDB_IDL ids );
|
||||
|
||||
/** An ID2 is an ID/pointer pair.
|
||||
*/
|
||||
typedef struct MDB_ID2 {
|
||||
MDB_ID mid; /**< The ID */
|
||||
void *mptr; /**< The pointer */
|
||||
} MDB_ID2;
|
||||
|
||||
/** An ID2L is an ID2 List, a sorted array of ID2s.
|
||||
* The first element's \b mid member is a count of how many actual
|
||||
* elements are in the array. The \b mptr member of the first element is unused.
|
||||
* The array is sorted in ascending order by \b mid.
|
||||
*/
|
||||
typedef MDB_ID2 *MDB_ID2L;
|
||||
|
||||
/** Search for an ID in an ID2L.
|
||||
* @param[in] ids The ID2L to search.
|
||||
* @param[in] id The ID to search for.
|
||||
* @return The index of the first ID2 whose \b mid member is greater than or equal to \b id.
|
||||
*/
|
||||
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id );
|
||||
|
||||
|
||||
/** Insert an ID2 into a ID2L.
|
||||
* @param[in,out] ids The ID2L to insert into.
|
||||
* @param[in] id The ID2 to insert.
|
||||
* @return 0 on success, -1 if the ID was already present in the ID2L.
|
||||
*/
|
||||
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id );
|
||||
|
||||
/** Append an ID2 into a ID2L.
|
||||
* @param[in,out] ids The ID2L to append into.
|
||||
* @param[in] id The ID2 to append.
|
||||
* @return 0 on success, -2 if the ID2L is too big.
|
||||
*/
|
||||
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id );
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _MDB_MIDL_H_ */
|
915
c_src/uthash.h
Normal file
915
c_src/uthash.h
Normal file
|
@ -0,0 +1,915 @@
|
|||
/*
|
||||
Copyright (c) 2003-2012, Troy D. Hanson http://uthash.sourceforge.net
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef UTHASH_H
|
||||
#define UTHASH_H
|
||||
|
||||
#include <string.h> /* memcmp,strlen */
|
||||
#include <stddef.h> /* ptrdiff_t */
|
||||
#include <stdlib.h> /* exit() */
|
||||
|
||||
/* These macros use decltype or the earlier __typeof GNU extension.
|
||||
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
|
||||
when compiling c++ source) this code uses whatever method is needed
|
||||
or, for VS2008 where neither is available, uses casting workarounds. */
|
||||
#ifdef _MSC_VER /* MS compiler */
|
||||
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
|
||||
#define DECLTYPE(x) (decltype(x))
|
||||
#else /* VS2008 or older (or VS2010 in C mode) */
|
||||
#define NO_DECLTYPE
|
||||
#define DECLTYPE(x)
|
||||
#endif
|
||||
#else /* GNU, Sun and other compilers */
|
||||
#define DECLTYPE(x) (__typeof(x))
|
||||
#endif
|
||||
|
||||
#ifdef NO_DECLTYPE
|
||||
#define DECLTYPE_ASSIGN(dst,src) \
|
||||
do { \
|
||||
char **_da_dst = (char**)(&(dst)); \
|
||||
*_da_dst = (char*)(src); \
|
||||
} while(0)
|
||||
#else
|
||||
#define DECLTYPE_ASSIGN(dst,src) \
|
||||
do { \
|
||||
(dst) = DECLTYPE(dst)(src); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
/* a number of the hash function use uint32_t which isn't defined on win32 */
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
#else
|
||||
#include <inttypes.h> /* uint32_t */
|
||||
#endif
|
||||
|
||||
#define UTHASH_VERSION 1.9.6
|
||||
|
||||
#ifndef uthash_fatal
|
||||
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
|
||||
#endif
|
||||
#ifndef uthash_malloc
|
||||
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
|
||||
#endif
|
||||
#ifndef uthash_free
|
||||
#define uthash_free(ptr,sz) free(ptr) /* free fcn */
|
||||
#endif
|
||||
|
||||
#ifndef uthash_noexpand_fyi
|
||||
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
|
||||
#endif
|
||||
#ifndef uthash_expand_fyi
|
||||
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
|
||||
#endif
|
||||
|
||||
/* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
|
||||
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
|
||||
|
||||
/* calculate the element whose hash handle address is hhe */
|
||||
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
|
||||
|
||||
#define HASH_FIND(hh,head,keyptr,keylen,out) \
|
||||
do { \
|
||||
unsigned _hf_bkt,_hf_hashv; \
|
||||
out=NULL; \
|
||||
if (head) { \
|
||||
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
|
||||
if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
|
||||
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
|
||||
keyptr,keylen,out); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef HASH_BLOOM
|
||||
#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
|
||||
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
|
||||
#define HASH_BLOOM_MAKE(tbl) \
|
||||
do { \
|
||||
(tbl)->bloom_nbits = HASH_BLOOM; \
|
||||
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
|
||||
if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
|
||||
memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
|
||||
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
|
||||
} while (0)
|
||||
|
||||
#define HASH_BLOOM_FREE(tbl) \
|
||||
do { \
|
||||
uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
|
||||
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
|
||||
|
||||
#define HASH_BLOOM_ADD(tbl,hashv) \
|
||||
HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
|
||||
|
||||
#define HASH_BLOOM_TEST(tbl,hashv) \
|
||||
HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
|
||||
|
||||
#else
|
||||
#define HASH_BLOOM_MAKE(tbl)
|
||||
#define HASH_BLOOM_FREE(tbl)
|
||||
#define HASH_BLOOM_ADD(tbl,hashv)
|
||||
#define HASH_BLOOM_TEST(tbl,hashv) (1)
|
||||
#endif
|
||||
|
||||
#define HASH_MAKE_TABLE(hh,head) \
|
||||
do { \
|
||||
(head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
|
||||
sizeof(UT_hash_table)); \
|
||||
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
|
||||
(head)->hh.tbl->tail = &((head)->hh); \
|
||||
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
|
||||
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
|
||||
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
|
||||
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl->buckets, 0, \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
HASH_BLOOM_MAKE((head)->hh.tbl); \
|
||||
(head)->hh.tbl->signature = HASH_SIGNATURE; \
|
||||
} while(0)
|
||||
|
||||
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
|
||||
HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
|
||||
|
||||
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
|
||||
do { \
|
||||
unsigned _ha_bkt; \
|
||||
(add)->hh.next = NULL; \
|
||||
(add)->hh.key = (char*)keyptr; \
|
||||
(add)->hh.keylen = (unsigned)keylen_in; \
|
||||
if (!(head)) { \
|
||||
head = (add); \
|
||||
(head)->hh.prev = NULL; \
|
||||
HASH_MAKE_TABLE(hh,head); \
|
||||
} else { \
|
||||
(head)->hh.tbl->tail->next = (add); \
|
||||
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
|
||||
(head)->hh.tbl->tail = &((add)->hh); \
|
||||
} \
|
||||
(head)->hh.tbl->num_items++; \
|
||||
(add)->hh.tbl = (head)->hh.tbl; \
|
||||
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
|
||||
(add)->hh.hashv, _ha_bkt); \
|
||||
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
|
||||
HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
|
||||
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
|
||||
do { \
|
||||
bkt = ((hashv) & ((num_bkts) - 1)); \
|
||||
} while(0)
|
||||
|
||||
/* delete "delptr" from the hash table.
|
||||
* "the usual" patch-up process for the app-order doubly-linked-list.
|
||||
* The use of _hd_hh_del below deserves special explanation.
|
||||
* These used to be expressed using (delptr) but that led to a bug
|
||||
* if someone used the same symbol for the head and deletee, like
|
||||
* HASH_DELETE(hh,users,users);
|
||||
* We want that to work, but by changing the head (users) below
|
||||
* we were forfeiting our ability to further refer to the deletee (users)
|
||||
* in the patch-up process. Solution: use scratch space to
|
||||
* copy the deletee pointer, then the latter references are via that
|
||||
* scratch pointer rather than through the repointed (users) symbol.
|
||||
*/
|
||||
#define HASH_DELETE(hh,head,delptr) \
|
||||
do { \
|
||||
unsigned _hd_bkt; \
|
||||
struct UT_hash_handle *_hd_hh_del; \
|
||||
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
|
||||
uthash_free((head)->hh.tbl->buckets, \
|
||||
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
|
||||
HASH_BLOOM_FREE((head)->hh.tbl); \
|
||||
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
|
||||
head = NULL; \
|
||||
} else { \
|
||||
_hd_hh_del = &((delptr)->hh); \
|
||||
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
|
||||
(head)->hh.tbl->tail = \
|
||||
(UT_hash_handle*)((char*)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho); \
|
||||
} \
|
||||
if ((delptr)->hh.prev) { \
|
||||
((UT_hash_handle*)((char*)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \
|
||||
} else { \
|
||||
DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
|
||||
} \
|
||||
if (_hd_hh_del->next) { \
|
||||
((UT_hash_handle*)((char*)_hd_hh_del->next + \
|
||||
(head)->hh.tbl->hho))->prev = \
|
||||
_hd_hh_del->prev; \
|
||||
} \
|
||||
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
|
||||
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
|
||||
(head)->hh.tbl->num_items--; \
|
||||
} \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
|
||||
#define HASH_FIND_STR(head,findstr,out) \
|
||||
HASH_FIND(hh,head,findstr,strlen(findstr),out)
|
||||
#define HASH_ADD_STR(head,strfield,add) \
|
||||
HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
|
||||
#define HASH_FIND_INT(head,findint,out) \
|
||||
HASH_FIND(hh,head,findint,sizeof(int),out)
|
||||
#define HASH_ADD_INT(head,intfield,add) \
|
||||
HASH_ADD(hh,head,intfield,sizeof(int),add)
|
||||
#define HASH_FIND_PTR(head,findptr,out) \
|
||||
HASH_FIND(hh,head,findptr,sizeof(void *),out)
|
||||
#define HASH_ADD_PTR(head,ptrfield,add) \
|
||||
HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
|
||||
#define HASH_DEL(head,delptr) \
|
||||
HASH_DELETE(hh,head,delptr)
|
||||
|
||||
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
|
||||
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
|
||||
*/
|
||||
#ifdef HASH_DEBUG
|
||||
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
|
||||
#define HASH_FSCK(hh,head) \
|
||||
do { \
|
||||
unsigned _bkt_i; \
|
||||
unsigned _count, _bkt_count; \
|
||||
char *_prev; \
|
||||
struct UT_hash_handle *_thh; \
|
||||
if (head) { \
|
||||
_count = 0; \
|
||||
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
|
||||
_bkt_count = 0; \
|
||||
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
|
||||
_prev = NULL; \
|
||||
while (_thh) { \
|
||||
if (_prev != (char*)(_thh->hh_prev)) { \
|
||||
HASH_OOPS("invalid hh_prev %p, actual %p\n", \
|
||||
_thh->hh_prev, _prev ); \
|
||||
} \
|
||||
_bkt_count++; \
|
||||
_prev = (char*)(_thh); \
|
||||
_thh = _thh->hh_next; \
|
||||
} \
|
||||
_count += _bkt_count; \
|
||||
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
|
||||
HASH_OOPS("invalid bucket count %d, actual %d\n", \
|
||||
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
|
||||
} \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid hh item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||
} \
|
||||
/* traverse hh in app order; check next/prev integrity, count */ \
|
||||
_count = 0; \
|
||||
_prev = NULL; \
|
||||
_thh = &(head)->hh; \
|
||||
while (_thh) { \
|
||||
_count++; \
|
||||
if (_prev !=(char*)(_thh->prev)) { \
|
||||
HASH_OOPS("invalid prev %p, actual %p\n", \
|
||||
_thh->prev, _prev ); \
|
||||
} \
|
||||
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
|
||||
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
|
||||
(head)->hh.tbl->hho) : NULL ); \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid app item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||