initial release

This commit is contained in:
younes 2012-09-30 19:42:44 +02:00
parent 952eff3c9a
commit 6498e5a4e3
18 changed files with 10251 additions and 3 deletions

8
3rdLibs Normal file
View 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
View file

@ -0,0 +1,3 @@
o Sep 29th, 2012
initial release

49
LICENSE Normal file
View 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
View 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

View file

@ -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
View 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

File diff suppressed because it is too large Load diff

1065
c_src/mdb.h Normal file

File diff suppressed because it is too large Load diff

327
c_src/midl.c Normal file
View 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
View 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
View 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 ); \