diff --git a/.gitignore b/.gitignore index f30fde5d9..14b395f06 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,8 @@ *.dylib docs/yap.info* .build + +.cproject +.project +.settings +autom4te.cache \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 367eac449..dc9ffa933 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,3 +43,6 @@ [submodule "packages/odbc"] path = packages/odbc url = git://yap.git.sourceforge.net/gitroot/yap/odbc +[submodule "packages/udi"] + path = packages/udi + url = https://github.com/davidvaz/yap-udi-indexers.git diff --git a/C/udi.c b/C/udi.c index 7091df6d4..48fcec985 100644 --- a/C/udi.c +++ b/C/udi.c @@ -1,169 +1,361 @@ - +#include +#include #include "Yap.h" +#include "YapInterface.h" #include "clause.h" -#include "udi.h" +#include "udi_private.h" +/* to keep an array with the registered udi indexers */ +UT_icd udicb_icd = {sizeof(UdiControlBlock), NULL, NULL, NULL}; +UT_array *indexing_structures; -#include "rtree_udi.h" - -/* we can have this stactic because it is written once */ -static struct udi_control_block RtreeCmd; - -/****** - All the info we need to enter user indexed code: - predicate - the user control block - functions used, in case we have different schema (maybe should part of previous) - right now, this is just a linked list.... -******/ -typedef struct udi_info -{ - PredEntry *p; - void *cb; - UdiControlBlock functions; - struct udi_info *next; -} *UdiInfo; - -/****** - we now have one extra user indexed predicate. We assume these - are few, so we can do with a linked list. -******/ -static int -add_udi_block(void *info, PredEntry *p, UdiControlBlock cmd) -{ - UdiInfo blk = (UdiInfo)Yap_AllocCodeSpace(sizeof(struct udi_info)); - if (!blk) - return FALSE; - blk->next = UdiControlBlocks; - UdiControlBlocks = blk; - blk->p = p; - blk->functions = cmd; - blk->cb = info; - return TRUE; +/* + * Register a new user indexer + */ +void +Yap_UdiRegister(UdiControlBlock cb){ + /*TODO: check structure integrity and duplicates */ + utarray_push_back(indexing_structures, &cb); } -/****** - new user indexed predicate; - the type right now is just rtrees, but in the future we'll have more. - the second argument is the term. -******/ -static Int +/* + * New user indexed predicate: + * the first argument is the term. + */ +static YAP_Int p_new_udi( USES_REGS1 ) { - Term spec = Deref(ARG2), udi_type = Deref(ARG1); - PredEntry *p; - UdiControlBlock cmd; - Atom udi_t; - void *info; + Term spec = Deref(ARG1); + + PredEntry *p; + UdiInfo blk; + int info; -/* fprintf(stderr,"new pred babe\n");*/ /* get the predicate from the spec, copied from cdmgr.c */ if (IsVarTerm(spec)) { - Yap_Error(INSTANTIATION_ERROR,spec,"new user index/1"); - return FALSE; + Yap_Error(INSTANTIATION_ERROR,spec,"new user index/1"); + return FALSE; } else if (!IsApplTerm(spec)) { - Yap_Error(TYPE_ERROR_COMPOUND,spec,"new user index/1"); - return FALSE; + Yap_Error(TYPE_ERROR_COMPOUND,spec,"new user index/1"); + return FALSE; } else { - Functor fun = FunctorOfTerm(spec); - Term tmod = CurrentModule; + Functor fun = FunctorOfTerm(spec); + Term tmod = CurrentModule; - while (fun == FunctorModule) { - tmod = ArgOfTerm(1,spec); - if (IsVarTerm(tmod) ) { - Yap_Error(INSTANTIATION_ERROR, spec, "new user index/1"); - return FALSE; - } - if (!IsAtomTerm(tmod) ) { - Yap_Error(TYPE_ERROR_ATOM, spec, "new user index/1"); - return FALSE; - } - spec = ArgOfTerm(2, spec); - fun = FunctorOfTerm(spec); - } - p = RepPredProp(PredPropByFunc(fun, tmod)); + while (fun == FunctorModule) { + tmod = ArgOfTerm(1,spec); + if (IsVarTerm(tmod) ) { + Yap_Error(INSTANTIATION_ERROR, spec, "new user index/1"); + return FALSE; + } + if (!IsAtomTerm(tmod) ) { + Yap_Error(TYPE_ERROR_ATOM, spec, "new user index/1"); + return FALSE; + } + spec = ArgOfTerm(2, spec); + fun = FunctorOfTerm(spec); + } + p = RepPredProp(PredPropByFunc(fun, tmod)); } if (!p) - return FALSE; + return FALSE; /* boring, boring, boring! */ - if ((p->PredFlags & (DynamicPredFlag|LogUpdatePredFlag|UserCPredFlag|CArgsPredFlag|NumberDBPredFlag|AtomDBPredFlag|TestPredFlag|AsmPredFlag|CPredFlag|BinaryPredFlag)) || - (p->ModuleOfPred == PROLOG_MODULE)) { - Yap_Error(PERMISSION_ERROR_MODIFY_STATIC_PROCEDURE, spec, "udi/2"); - return FALSE; + if ((p->PredFlags + & (DynamicPredFlag|LogUpdatePredFlag|UserCPredFlag|CArgsPredFlag|NumberDBPredFlag|AtomDBPredFlag|TestPredFlag|AsmPredFlag|CPredFlag|BinaryPredFlag)) + || (p->ModuleOfPred == PROLOG_MODULE)) { + Yap_Error(PERMISSION_ERROR_MODIFY_STATIC_PROCEDURE, spec, "udi/2"); + return FALSE; } if (p->PredFlags & (DynamicPredFlag|LogUpdatePredFlag|TabledPredFlag)) { - Yap_Error(PERMISSION_ERROR_ACCESS_PRIVATE_PROCEDURE, spec, "udi/2"); - return FALSE; - } - /* just make sure we're looking at the right user type! */ - if (IsVarTerm(udi_type)) { - Yap_Error(INSTANTIATION_ERROR,spec,"new user index/1"); - return FALSE; - } else if (!IsAtomTerm(udi_type)) { - Yap_Error(TYPE_ERROR_ATOM,spec,"new user index/1"); - return FALSE; - } - udi_t = AtomOfTerm(udi_type); - if (udi_t == AtomRTree) { - cmd = &RtreeCmd; - } else { - Yap_Error(TYPE_ERROR_ATOM,spec,"new user index/1"); - return FALSE; + Yap_Error(PERMISSION_ERROR_ACCESS_PRIVATE_PROCEDURE, spec, "udi/2"); + return FALSE; } + /* TODO: remove AtomRTree from atom list */ + /* this is the real work */ - info = cmd->init(spec, (void *)p, p->ArityOfPE); - if (!info) - return FALSE; - /* add to table */ - if (!add_udi_block(info, p, cmd)) { - Yap_Error(OUT_OF_HEAP_ERROR, spec, "new user index/1"); - return FALSE; + blk = (UdiInfo) Yap_AllocCodeSpace(sizeof(struct udi_info)); + memset((void *) blk,0, sizeof(struct udi_info)); + if (!blk) { + Yap_Error(OUT_OF_HEAP_ERROR, spec, "new user index/1"); + return FALSE; } + + /*Init UdiInfo */ + utarray_new(blk->args, &arg_icd); + utarray_new(blk->clauselist, &cl_icd); + blk->p = p; + + /*Now Init args list*/ + info = p_udi_args_init(spec, p->ArityOfPE, blk); + if (!info) + { + utarray_free(blk->args); + utarray_free(blk->clauselist); + Yap_FreeCodeSpace((char *) blk); + return FALSE; + } + + /*Push into the hash*/ + HASH_ADD_UdiInfo(UdiControlBlocks, p, blk); + p->PredFlags |= UDIPredFlag; + return TRUE; } -/* just pass info to user, called from cdmgr.c */ +/* + * Here we initialize the arguments indexing + */ +YAP_Int +p_udi_args_init(Term spec, int arity, UdiInfo blk) +{ + int i; + Term arg; + Atom idxtype; + UdiControlBlock *cb; + struct udi_p_args p_arg; + + for (i = 1; i <= arity; i++) { + arg = ArgOfTerm(i,spec); + if (IsAtomTerm(arg)) { + idxtype = AtomOfTerm(arg); + if (idxtype == AtomMinus) //skip this argument + continue; + p_arg.control = NULL; + cb = NULL; + while ((cb = (UdiControlBlock *) utarray_next(indexing_structures, cb))) { + if (idxtype == (*cb)->decl){ + p_arg.arg = i; + p_arg.control = *cb; + p_arg.idxstr = (*cb)->init(spec, i, arity); + utarray_push_back(blk->args, &p_arg); + } + } + if (p_arg.control == NULL){ /* not "-" and not found */ + fprintf(stderr, "Invalid Spec (%s)\n", AtomName(idxtype)); + return FALSE; + } + } + } + return TRUE; +} + +/* + * From now on this is called in several places of yap + * when the predicate has the UDIPredFlag + * and is what actually triggers the insert/search/abolish of indexing structures + */ + +/* + * Init Yap udi interface + */ +void +Yap_udi_init(void) +{ + UdiControlBlocks = NULL; + + /*init indexing structures array*/ + utarray_new(indexing_structures, &udicb_icd); + + Yap_InitCPred("$udi_init", 1, p_new_udi, 0); + /* TODO: decide if udi.yap should be loaded automaticaly in init.yap */ +} + +/* called from cdmgr.c + * + * for each assert of a udipredicate + * to pass info to user structure + */ int Yap_new_udi_clause(PredEntry *p, yamop *cl, Term t) { - struct udi_info *info = UdiControlBlocks; - while (info->p != p && info) - info = info->next; - if (!info) - return FALSE; - info->cb = info->functions->insert(t, info->cb, (void *)cl); - return TRUE; + int i; + UdiPArg parg; + UdiInfo info; + YAP_Int index; + + /* try to find our structure */ + HASH_FIND_UdiInfo(UdiControlBlocks,p,info); + if (!info) + return FALSE; + + /* insert into clauselist */ + utarray_push_back(info->clauselist, &cl); + + for (i = 0; i < utarray_len(info->args) ; i++) { + parg = (UdiPArg) utarray_eltptr(info->args,i); + index = (YAP_Int) utarray_len(info->clauselist); + parg->idxstr = parg->control->insert(parg->idxstr, t, + parg->arg, + (void *) index); + } + return TRUE; } -/* index, called from absmi.c */ +/* index, called from absmi.c + * + * Returns: + * NULL (yap fallback) No usable indexing available + * + * Yap_FAILCODE() (fail) No result found + * Yap_CauseListToClause(cl) 1 solution found + * Yap_ClauseListCode(cl) 2+ solutions found + */ yamop * Yap_udi_search(PredEntry *p) { - struct udi_info *info = UdiControlBlocks; - while (info->p != p && info) - info = info->next; - if (!info) - return NULL; - return info->functions->search(info->cb); + int r; + struct ClauseList clauselist; + UdiPArg parg; + UdiInfo info; + + /* find our structure*/ + HASH_FIND_UdiInfo(UdiControlBlocks,p,info); + if (!info || utarray_len(info->args) == 0) + return NULL; + + if (utarray_len(info->args) == 1){ //simple case no intersection needed + struct si_callback_h c; + + c.cl = Yap_ClauseListInit(&clauselist); + c.clauselist = info->clauselist; + c.pred = info->p; + if (!c.cl) + return NULL; + + parg = (UdiPArg) utarray_eltptr(info->args,0); + r = parg->control->search(parg->idxstr, parg->arg, si_callback, (void *) &c); + Yap_ClauseListClose(c.cl); + + if (r == -1) { + Yap_ClauseListDestroy(c.cl); + return NULL; + } + + if (Yap_ClauseListCount(c.cl) == 0) { + Yap_ClauseListDestroy(c.cl); + return Yap_FAILCODE(); + } + } else {//intersection needed using Judy1 +#ifdef USE_JUDY + /*TODO: do more tests to this algorithm*/ + int i; + Pvoid_t tmp = (Pvoid_t) NULL; + Pvoid_t result = (Pvoid_t) NULL; + Word_t count = 0L; + Word_t idx_r = 0L; + Word_t idx_tmp = 0L; + int rc = 0; + yamop **x; + + /* + * I will start with the simplest approach + * for each index create a set and intersect it with the + * next + * + * In the future it could pay to sort according to index type + * to improve intersection part + */ + for (i = 0; i < utarray_len(info->args) ; i++) { + parg = (UdiPArg) utarray_eltptr(info->args,i); + r = parg->control->search(parg->idxstr, parg->arg, j1_callback, &tmp); + if (r == -1) /*this arg does not prune search*/ + continue; + rc ++; + J1C(count, result, 0, -1); + if (r == 0) /* this arg gave 0 results -> FAIL*/ + { + if (count > 0) // clear previous result if they exists + J1FA(count, result); + return Yap_FAILCODE(); + } + + if (count == 0) // first result_set + { + result = tmp; + tmp = (Pvoid_t) NULL; + } + else /*intersection*/ + { + idx_tmp = 0L; + idx_r = 0L; + J1F(count, result, idx_r); //succeeds one time at least + assert(count > 0); + J1F(count, tmp, idx_tmp); //succeeds one time at least + assert(count > 0); + while (count) + { + while (idx_r < idx_tmp) + { + J1U(count, result, idx_r); //does not belong + J1N(count, result, idx_r); //next + if (! count) break; //end result set + } + if(idx_r == idx_tmp) + { + J1N(count, result, idx_r); //next + if (! count) break; //end result set + J1N(count, tmp, idx_tmp); //next tmp + //if (! count) break; //end tmp set will break while + } + else // (idx_r > idx_tmp) + { + idx_tmp = idx_r; // fast forward + J1F(count, tmp, idx_tmp); // first starting in idx_r + //if (! count) break; //end tmp set will break while + } + } + J1F(count, result, idx_r); // first starting in idx_r + //clear up the rest + while (idx_r > idx_tmp && count) //result has more setted values + { + J1U(count, result, idx_r); //does not belong + J1N(count, result, idx_r); //next + } + J1FA(count, tmp); //free tmp + } + } + if (rc == 0) /*no search performed*/ + return NULL; + + J1C(count, result, 0, -1); + if (count == 0) { /*result set empty -> FAIL */ + J1FA(count, result); + return Yap_FAILCODE(); + } + + /*convert Juddy1 to clauselist*/ + Yap_ClauseListInit(&clauselist); + idx_r = 0L; + J1F(count, result, idx_r); + while (count) + { + x = (yamop **) utarray_eltptr(info->clauselist, idx_r - 1); + Yap_ClauseListExtend( + &clauselist, + *x, + info->p); + J1N(count, result, idx_r); + } + J1FA(count,result); + fprintf(stderr,"J1 used space %d bytes for %d clausules\n", + count, Yap_ClauseListCount(&clauselist)); + Yap_ClauseListClose(&clauselist); +#else + fprintf(stderr,"Without libJudy only one argument indexed is allowed." + "Falling back to Yap Indexing\n"); + return NULL; //NO Judy Available +#endif + } + + if (Yap_ClauseListCount(&clauselist) == 1) + return Yap_ClauseListToClause(&clauselist); + return Yap_ClauseListCode(&clauselist); } /* index, called from absmi.c */ void Yap_udi_abolish(PredEntry *p) { - /* tell the predicate destroy */ + /* tell the predicate destroy */ } - -void -Yap_udi_init(void) -{ - UdiControlBlocks = NULL; - /* to be filled in by David */ - RtreeCmd.init = RtreeUdiInit; - RtreeCmd.insert = RtreeUdiInsert; - RtreeCmd.search = RtreeUdiSearch; - RtreeCmd.destroy = RtreeUdiDestroy; - Yap_InitCPred("$udi_init", 2, p_new_udi, 0); -} - diff --git a/H/clause.h b/H/clause.h index 6e2eacb32..afe3ab6fb 100644 --- a/H/clause.h +++ b/H/clause.h @@ -367,8 +367,9 @@ Term STD_PROTO(Yap_LUInstance,(LogUpdClause *, UInt)); /* udi.c */ void STD_PROTO(Yap_udi_init,(void)); -yamop *STD_PROTO(Yap_udi_search,(PredEntry *)); int STD_PROTO(Yap_new_udi_clause,(PredEntry *, yamop *, Term)); +yamop *STD_PROTO(Yap_udi_search,(PredEntry *)); +void STD_PROTO(Yap_udi_abolish,(PredEntry *p)); #ifdef DEBUG void STD_PROTO(Yap_bug_location,(yamop *)); diff --git a/H/udi_private.h b/H/udi_private.h new file mode 100644 index 000000000..df61dc182 --- /dev/null +++ b/H/udi_private.h @@ -0,0 +1,74 @@ +#include "config.h" +#include "udi.h" +#include "utarray.h" +#include "uthash.h" + +/* Argument Indexing */ +struct udi_p_args { + int arg; //indexed arg + void *idxstr; //user indexing structure + UdiControlBlock control; //user indexing structure functions +}; +typedef struct udi_p_args *UdiPArg; +UT_icd arg_icd = {sizeof(struct udi_p_args), NULL, NULL, NULL }; + +/* clauselist */ +UT_icd cl_icd = {sizeof(yamop *), NULL, NULL, NULL }; + +/* + * All the info we need to enter user indexed code + * stored in a uthash + */ +struct udi_info +{ + PredEntry *p; //predicate (need to identify asserts) + UT_array *clauselist; //clause list used on returns + UT_array *args; //indexed args + UT_hash_handle hh; //uthash handle +}; +typedef struct udi_info *UdiInfo; + +/* to ease code for a UdiInfo hash table*/ +#define HASH_FIND_UdiInfo(head,find,out) \ + HASH_FIND(hh,head,find,sizeof(PredEntry *),out) +#define HASH_ADD_UdiInfo(head,p,add) \ + HASH_ADD_KEYPTR(hh,head,p,sizeof(PredEntry *),add) + +/* used during init */ +static YAP_Int p_new_udi( USES_REGS1 ); +static YAP_Int p_udi_args_init(Term spec, int arity, UdiInfo blk); + +/* + * Indexing Search and intersection Helpers + */ + +/* single indexing helpers (no intersection needed just create clauselist) */ +#include "clause_list.h" +struct si_callback_h +{ + clause_list_t cl; + UT_array *clauselist; + void * pred; +}; +typedef struct si_callback_h * si_callback_h_t; + +static inline int si_callback(void *key, void *data, void *arg) +{ + si_callback_h_t c = (si_callback_h_t) arg; + yamop **cl = (yamop **) utarray_eltptr(c->clauselist, ((YAP_Int) data) - 1); + return Yap_ClauseListExtend(c->cl, *cl, c->pred); +} + +#ifdef USE_JUDY +#include +/* Judy1 integer sparse set intersection */ +static inline int j1_callback(void *key, void *data, void *arg) +{ + int r; + Pvoid_t *array = (Pvoid_t *) arg; + J1S(r, *array, (int) data); + if (r == JERR) + return FALSE; + return TRUE; +} +#endif diff --git a/H/utarray.h b/H/utarray.h new file mode 100644 index 000000000..0c1e59b5b --- /dev/null +++ b/H/utarray.h @@ -0,0 +1,233 @@ +/* +Copyright (c) 2008-2013, 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. +*/ + +/* a dynamic array implementation using macros + * see http://uthash.sourceforge.net/utarray + */ +#ifndef UTARRAY_H +#define UTARRAY_H + +#define UTARRAY_VERSION 1.9.7 + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((__unused__)) +#else +#define _UNUSED_ +#endif + +#include /* size_t */ +#include /* memset, etc */ +#include /* exit */ + +#define oom() exit(-1) + +typedef void (ctor_f)(void *dst, const void *src); +typedef void (dtor_f)(void *elt); +typedef void (init_f)(void *elt); +typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; +} UT_icd; + +typedef struct { + unsigned i,n;/* i: index of next available slot, n: num slots */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz*/ +} UT_array; + +#define utarray_init(a,_icd) do { \ + memset(a,0,sizeof(UT_array)); \ + (a)->icd=*_icd; \ +} while(0) + +#define utarray_done(a) do { \ + if ((a)->n) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + free((a)->d); \ + } \ + (a)->n=0; \ +} while(0) + +#define utarray_new(a,_icd) do { \ + a=(UT_array*)malloc(sizeof(UT_array)); \ + utarray_init(a,_icd); \ +} while(0) + +#define utarray_free(a) do { \ + utarray_done(a); \ + free(a); \ +} while(0) + +#define utarray_reserve(a,by) do { \ + if (((a)->i+by) > ((a)->n)) { \ + while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ + if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \ + } \ +} while(0) + +#define utarray_push_back(a,p) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ + else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ +} while(0) + +#define utarray_pop_back(a) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ + else { (a)->i--; } \ +} while(0) + +#define utarray_extend_back(a) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ + else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ + (a)->i++; \ +} while(0) + +#define utarray_len(a) ((a)->i) + +#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) +#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) ))) + +#define utarray_insert(a,p,j) do { \ + utarray_reserve(a,1); \ + if (j > (a)->i) break; \ + if ((j) < (a)->i) { \ + memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ + (a)->i++; \ +} while(0) + +#define utarray_inserta(a,w,j) do { \ + if (utarray_len(w) == 0) break; \ + if (j > (a)->i) break; \ + utarray_reserve(a,utarray_len(w)); \ + if ((j) < (a)->i) { \ + memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ + _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { \ + size_t _ut_i; \ + for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ + (a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \ + } \ + } else { \ + memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ + utarray_len(w)*((a)->icd.sz)); \ + } \ + (a)->i += utarray_len(w); \ +} while(0) + +#define utarray_resize(dst,num) do { \ + size_t _ut_i; \ + if (dst->i > (size_t)(num)) { \ + if ((dst)->icd.dtor) { \ + for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \ + (dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \ + } \ + } \ + } else if (dst->i < (size_t)(num)) { \ + utarray_reserve(dst,num-dst->i); \ + if ((dst)->icd.init) { \ + for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \ + (dst)->icd.init(utarray_eltptr(dst,_ut_i)); \ + } \ + } else { \ + memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \ + } \ + } \ + dst->i = num; \ +} while(0) + +#define utarray_concat(dst,src) do { \ + utarray_inserta((dst),(src),utarray_len(dst)); \ +} while(0) + +#define utarray_erase(a,pos,len) do { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < len; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \ + } \ + } \ + if ((a)->i > (pos+len)) { \ + memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \ + (((a)->i)-(pos+len))*((a)->icd.sz)); \ + } \ + (a)->i -= (len); \ +} while(0) + +#define utarray_renew(a,u) do { \ + if (a) utarray_clear(a); \ + else utarray_new((a),(u)); \ +} while(0) + +#define utarray_clear(a) do { \ + if ((a)->i > 0) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + (a)->i = 0; \ + } \ +} while(0) + +#define utarray_sort(a,cmp) do { \ + qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ +} while(0) + +#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) + +#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) +#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) +#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) +#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) +#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(a)->icd.sz) : -1) + +/* last we pre-define a few icd for common utarrays of ints and strings */ +static void utarray_str_cpy(void *dst, const void *src) { + char **_src = (char**)src, **_dst = (char**)dst; + *_dst = (*_src == NULL) ? NULL : strdup(*_src); +} +static void utarray_str_dtor(void *elt) { + char **eltc = (char**)elt; + if (*eltc) free(*eltc); +} +static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; +static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL}; +static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL}; + + +#endif /* UTARRAY_H */ diff --git a/H/uthash.h b/H/uthash.h new file mode 100644 index 000000000..fe2d51b6f --- /dev/null +++ b/H/uthash.h @@ -0,0 +1,917 @@ +/* +Copyright (c) 2003-2013, 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 /* memcmp,strlen */ +#include /* ptrdiff_t */ +#include /* 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 /* uint32_t */ +#endif + +#define UTHASH_VERSION 1.9.7 + +#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*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((ptrdiff_t)((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*)((ptrdiff_t)_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 ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6 */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) + +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)(key); \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) \ + hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + char *_hj_key=(char*)(key); \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = (unsigned)keylen; \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + char *_sfh_key=(char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6b; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35l; \ + _h ^= _h >> 16; \ +} while(0) + +#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353; \ + uint32_t _mur_c1 = 0xcc9e2d51; \ + uint32_t _mur_c2 = 0x1b873593; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = _mur_h1*5+0xe6546b64; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ + _mur_k1=0; \ + switch((keylen) & 3) { \ + case 3: _mur_k1 ^= _mur_tail[2] << 16; \ + case 2: _mur_k1 ^= _mur_tail[1] << 8; \ + case 1: _mur_k1 ^= _mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + _hs_tail->next = NULL; \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + 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; \ + } \ +} while(0) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/Makefile.in b/Makefile.in index 8e389dc96..bae5ddb3b 100755 --- a/Makefile.in +++ b/Makefile.in @@ -111,7 +111,6 @@ INTERFACE_HEADERS = \ $(srcdir)/include/clause_list.h \ $(srcdir)/include/dswiatoms.h \ $(srcdir)/include/udi.h \ - $(srcdir)/include/rtree_udi.h \ $(srcdir)/include/yap_structs.h \ $(srcdir)/include/YapInterface.h \ $(srcdir)/include/SWI-Prolog.h \ @@ -263,8 +262,6 @@ C_SOURCES= \ $(srcdir)/C/threads.c \ $(srcdir)/C/tracer.c $(srcdir)/C/unify.c $(srcdir)/C/userpreds.c \ $(srcdir)/C/udi.c \ - $(srcdir)/packages/udi/rtree.c \ - $(srcdir)/packages/udi/rtree_udi.c \ $(srcdir)/C/utilpreds.c $(srcdir)/C/write.c $(srcdir)/console/yap.c \ $(srcdir)/C/yap-args.c \ $(srcdir)/C/ypstdio.c \ @@ -371,7 +368,7 @@ ENGINE_OBJECTS = \ parser.o qlyr.o qlyw.o range.o \ save.o scanner.o sort.o stdpreds.o \ sysbits.o threads.o tracer.o \ - udi.o rtree.o rtree_udi.o\ + udi.o\ unify.o userpreds.o utilpreds.o \ yap-args.o write.o \ blobs.o swi.o ypstdio.o $(IOLIB_OBJECTS) @MPI_OBJS@ @@ -475,12 +472,6 @@ sysbits.o: $(srcdir)/C/sysbits.c config.h udi.o: $(srcdir)/C/udi.c config.h $(CC) -c $(C_INTERF_FLAGS) $(srcdir)/C/udi.c -o $@ -rtree.o: $(srcdir)/packages/udi/rtree.c config.h - $(CC) -c $(C_INTERF_FLAGS) $(srcdir)/packages/udi/rtree.c -o $@ - -rtree_udi.o: $(srcdir)/packages/udi/rtree_udi.c config.h - $(CC) -c $(C_INTERF_FLAGS) $(srcdir)/packages/udi/rtree_udi.c -o $@ - yap.o: $(srcdir)/console/yap.c config.h $(CC) -c $(CFLAGS) -I$(srcdir)/include $(srcdir)/console/yap.c -o $@ diff --git a/config.h.in b/config.h.in index 107ec7063..d270df6ad 100755 --- a/config.h.in +++ b/config.h.in @@ -24,6 +24,8 @@ /* Should we use gmp ? */ #undef HAVE_LIBGMP +#undef HAVE_LIBJUDY + /* What MPI libraries are there? */ #define HAVE_LIBMPI 0 #define HAVE_LIBMPICH @@ -67,6 +69,7 @@ #undef HAVE_GMP_H #undef HAVE_IEEEFP_H #undef HAVE_IO_H +#undef HAVE_JUDY_H #undef HAVE_LIMITS_H #undef HAVE_LOCALE_H #undef HAVE_MACH_O_DYLD_H @@ -326,6 +329,10 @@ #define USE_GMP 1 #endif +#if HAVE_JUDY_H && HAVE_LIBJUDY +#define USE_JUDY 1 +#endif + /* Should we use MPI ? */ #if defined(HAVE_MPI_H) && (defined(HAVE_LIBMPI) || defined(HAVE_LIBMPICH)) #define HAVE_MPI 1 diff --git a/configure b/configure index 506f3f791..a5a816eb5 100755 --- a/configure +++ b/configure @@ -849,6 +849,7 @@ enable_clpbn_bp with_gmp with_R with_python +with_judy with_minisat with_cudd enable_myddas @@ -1531,6 +1532,7 @@ Optional Packages: --with-gmp=DIR use GNU Multiple Precision in DIR --with-R=DIR interface to R language --with-python=DIR interface to R language + --with-judy=DIR UDI needs judy library --enable-minisat use minisat interface --with-cudd=DIR use CUDD package in DIR --with-java=JAVA_HOME use Java instalation in JAVA_HOME @@ -4717,6 +4719,21 @@ fi +# Check whether --with-judy was given. +if test "${with_judy+set}" = set; then : + withval=$with_judy; if test "$withval" = yes; then + yap_cv_judy=yes + elif test "$withval" = no; then + yap_cv_judy=no + else + yap_cv_judy=$withval + fi +else + yap_cv_judy=yes +fi + + + # Check whether --with-minisat was given. if test "${with_minisat+set}" = set; then : withval=$with_minisat; if test "$withval" = yes; then @@ -6910,6 +6927,56 @@ else ENABLE_PYTHON="@# " fi +if test "$yap_cv_judy" != "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Judy1Set in -lJudy" >&5 +$as_echo_n "checking for Judy1Set in -lJudy... " >&6; } +if ${ac_cv_lib_Judy_Judy1Set+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lJudy $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char Judy1Set (); +int +main () +{ +return Judy1Set (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_Judy_Judy1Set=yes +else + ac_cv_lib_Judy_Judy1Set=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Judy_Judy1Set" >&5 +$as_echo "$ac_cv_lib_Judy_Judy1Set" >&6; } +if test "x$ac_cv_lib_Judy_Judy1Set" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBJUDY 1 +_ACEOF + + LIBS="-lJudy $LIBS" + +else + as_fn_error $? "libJudy not found, UDI will only work with one Index at a time" "$LINENO" 5 +fi + +fi + if test "$yap_cv_myddas" != "no" then @@ -9179,6 +9246,20 @@ fi done +fi +if test "$yap_cv_judy" != "no"; then + for ac_header in Judy.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "Judy.h" "ac_cv_header_Judy_h" "$ac_includes_default" +if test "x$ac_cv_header_Judy_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_JUDY_H 1 +_ACEOF + +fi + +done + fi if test "$yap_cv_myddas" != "no" then diff --git a/configure.in b/configure.in index 4efa9ea45..d21d3ef9a 100755 --- a/configure.in +++ b/configure.in @@ -254,8 +254,18 @@ AC_ARG_WITH(python, yap_cv_python=$withval fi, [yap_cv_python=no]) + +AC_ARG_WITH(judy, + [ --with-judy[=DIR] UDI needs judy library], + if test "$withval" = yes; then + yap_cv_judy=yes + elif test "$withval" = no; then + yap_cv_judy=no + else + yap_cv_judy=$withval + fi, + [yap_cv_judy=yes]) -dnl best test we could do. AC_ARG_WITH(minisat, [ --enable-minisat use minisat interface], if test "$withval" = yes; then @@ -869,6 +879,10 @@ else ENABLE_PYTHON="@# " fi +if test "$yap_cv_judy" != "no"; then + AC_CHECK_LIB(Judy, Judy1Set,,[AC_MSG_ERROR([libJudy not found, UDI will only work with one Index at a time])]) +fi + dnl if test "$yap_cv_cudd" != "no" dnl then dnl AC_CHECK_LIB(cudd,Cudd_Init) @@ -1700,6 +1714,9 @@ if test "$yap_cv_gmp" != "no" then AC_CHECK_HEADERS(gmp.h) fi +if test "$yap_cv_judy" != "no"; then + AC_CHECK_HEADERS(Judy.h) +fi if test "$yap_cv_myddas" != "no" then AC_CHECK_HEADERS(mysql/mysql.h) diff --git a/include/rtree_udi.h b/include/rtree_udi.h deleted file mode 100644 index d701212f6..000000000 --- a/include/rtree_udi.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _RTREE_UDI_ -#define _RTREE_UDI_ - -#ifndef _RTREE_ -typedef void control_t; -#endif - -/*Prolog term from :- udi(a(-,+,+)). - User defined index announce -*/ -extern control_t *RtreeUdiInit (Term spec, - void *pred, - int arity); - -/*this is called in each asserted term that was declared to udi_init*/ -extern control_t *RtreeUdiInsert (Term term, /*asserted term*/ - control_t *control, - void *clausule); /*to store in tree and return - in search*/ - -extern void *RtreeUdiSearch (control_t *control); -extern int RtreeUdiDestroy(control_t *control); - -#endif /* _RTREE_UDI_ */ diff --git a/include/udi.h b/include/udi.h index d97a9cdc9..a7b428ec9 100644 --- a/include/udi.h +++ b/include/udi.h @@ -1,45 +1,85 @@ +/* + * This file is part of the YAP Prolog + * + * User Defined Indexing was developed by: + * David Vaz + * Vitor Santos Costa + * + * UDI Indexing Interface: + * + * Each new indexing mechanism should register it self by filling up a + * UdiControlBlock and calling Yap_UdiRegister(UdiControlBlock). + * + * UdiControlBlock has the main declaration that triggers the + * indexing structure as well as the pointers to the needed functions + * called at the appropriate times. + * + * For now each indexing structure only works with a single argument + * even when multiple arguments are indexed with the same struture. + * + * TODO: think of alternative ways of support both cases, e.g. a rtree + * does not benefit from multiple rtree indexing, but a hash table do + */ -/*chamada a cada index/2 - controi estrutura de control, para definir a indexação, contem a - rtree p.e. - retorna a estrutura de control -*/ -typedef void * -(* Yap_UdiInit)( - Term spec, /* mode spec */ - void *pred, /* pass predicate information */ - int arity); +/* This is called upon udi mode spec call, and the purpose is to allow + * the indexing struture to initialize itself. + * Should return the need opaque struture to be used in future calls + * + * arg is used to track the specific call, on multiple indexing with the + * same struture + */ +typedef void * (* Yap_UdiInit) + (YAP_Term spec, + int arg, /* argument regarding this call */ + int arity); -/*chamada a cada assert*/ -typedef void * -(* Yap_UdiInsert)(Term t, /* termo asserted */ - void *control, /* estrutura de control*/ - void *clausule); /* valor a guardar na arvore, para retornar na pesquisa */ +/* Upon each assert the struture insert method is called to perform + * its work + */ +typedef void * (* Yap_UdiInsert) + (void *control, /* indexing structure opaque handle */ + YAP_Term term, /* asserted argument */ + int arg, /* argument regarding this call */ + void *data); /* value to return on search */ -/* chamada cada vez que um predicado indexado aparece no código - Returns: - NULL quando não há indexação usavel no predicado (fallback to -yap indexing) - FALSE - TRY_RETRY_TRUST quando há resultados positivos -*/ -typedef void * -(* Yap_UdiSearch)(void * control); +/* Callback for each value found in a search + * if it returns FALSE the search should be immediately aborted + */ +typedef int (* Yap_UdiCallback) + (void *key, /* index key */ + void *data, /* data */ + void *arg); /* auxiliary data to callback */ -/* chamada cada vez que um predicado indexado aparece no código - Returns: - NULL quando não há indexação usavel no predicado (fallback to -yap indexing) - FALSE - TRY_RETRY_TRUST quando há resultados positivos -*/ -typedef int -(* Yap_UdiDestroy)(void * control); +/* Called upon search + * + * If there is any search to do with this structure should return >= 0 + * corresponding to the values found + * + * returns -1 if there is nothing to search with this indexing structure + * e.g. a Variable as argument + */ +typedef int (* Yap_UdiSearch) + (void * control, /* indexing structure opaque handle */ + int arg, /* argument regarding this call */ + Yap_UdiCallback f, /* callback on each found value */ + void *args); /* auxiliary data to callback */ +/* Called upon abolish of the term + * to allow for a clean destroy of the indexing structures + */ +typedef int (* Yap_UdiDestroy) + (void * control); + +/* + * Main structure used in UDI + */ typedef struct udi_control_block { - Yap_UdiInit init; - Yap_UdiInsert insert; - Yap_UdiSearch search; + YAP_Atom decl; //atom that triggers this indexing structure + Yap_UdiInit init; + Yap_UdiInsert insert; + Yap_UdiSearch search; Yap_UdiDestroy destroy; -} *UdiControlBlock; +} * UdiControlBlock; +/* Register a new indexing structure */ +void Yap_UdiRegister(UdiControlBlock); diff --git a/packages/udi b/packages/udi new file mode 160000 index 000000000..13ae724d3 --- /dev/null +++ b/packages/udi @@ -0,0 +1 @@ +Subproject commit 13ae724d30e4c9dd56ddde63cba4a34f1844c099 diff --git a/packages/udi/README b/packages/udi/README deleted file mode 100644 index f63b5294d..000000000 --- a/packages/udi/README +++ /dev/null @@ -1,7 +0,0 @@ -This directory contains support for user defined indexers, currently: - -- RTrees - -For Examples and Tests proceed as follows: - -git clone git://yap.dcc.fc.up.pt/udi-examples diff --git a/packages/udi/rtree.c b/packages/udi/rtree.c deleted file mode 100644 index 90bd5b219..000000000 --- a/packages/udi/rtree.c +++ /dev/null @@ -1,524 +0,0 @@ -#include -#include -#include -#include -#include - -#include "rtree.h" - -static node_t RTreeNewNode (void); -static void RTreeDestroyNode (node_t); -static void RTreeNodeInit (node_t); - -static int RTreeSearchNode (node_t, rect_t, SearchHitCallback, void *); -static int RTreeInsertNode (node_t, int, rect_t,void *,node_t *); - -static int RTreePickBranch (rect_t, node_t); -static int RTreeAddBranch(node_t, branch_t, node_t *); -static void RTreeSplitNode (node_t, branch_t, node_t *); - -static void RTreePickSeeds(partition_t *, node_t, node_t); -static void RTreeNodeAddBranch(rect_t *, node_t, branch_t); -static void RTreePickNext(partition_t *, node_t, node_t); - -static rect_t RTreeNodeCover(node_t); - -static double RectArea (rect_t); -static rect_t RectCombine (rect_t, rect_t); -static int RectOverlap (rect_t, rect_t); -static void RectPrint (rect_t); - -static partition_t PartitionNew (void); -static void PartitionPush (partition_t *, branch_t); -static branch_t PartitionPop (partition_t *); -static branch_t PartitionGet (partition_t *, int); - -rtree_t RTreeNew (void) -{ - rtree_t t; - t = RTreeNewNode(); - t->level = 0; /*leaf*/ - return t; -} - -void RTreeDestroy (rtree_t t) -{ - if (t) - RTreeDestroyNode (t); -} - -static node_t RTreeNewNode (void) -{ - node_t n; - - n = (node_t) malloc (sizeof(*n)); - assert(n); - RTreeNodeInit(n); - return n; -} - -static void RTreeDestroyNode (node_t node) -{ - int i; - - if (node->level == 0) /* leaf level*/ - { - for (i = 0; i < MAXCARD; i++) - if (node->branch[i].child) - ;/* allow user free data*/ - else - break; - } - else - { - for (i = 0; i < MAXCARD; i++) - if (node->branch[i].child) - RTreeDestroyNode (node->branch[i].child); - else - break; - } - free (node); -} - -static void RTreeNodeInit (node_t n) -{ - memset((void *) n,0, sizeof(*n)); - n->level = -1; -} - -int RTreeSearch (rtree_t t, rect_t s, SearchHitCallback f, void *arg) -{ - assert(t); - return RTreeSearchNode(t,s,f,arg); -} - -static int RTreeSearchNode (node_t n, rect_t s, SearchHitCallback f, void *arg) -{ - int i; - int c = 0; - - if (n->level > 0) - { - for (i = 0; i < MAXCARD; i++) - if (n->branch[i].child && - RectOverlap (s,n->branch[i].mbr)) - c += RTreeSearchNode ((node_t) n->branch[i].child, s, f, arg); - } - else - { - for (i = 0; i < MAXCARD; i++) - if (n->branch[i].child && - RectOverlap (s,n->branch[i].mbr)) - { - c ++; - if (f) - if ( !f(n->branch[i].mbr,n->branch[i].child,arg)) - return c; - } - } - return c; -} - -void RTreeInsert (rtree_t *t, rect_t r, void *data) -{ - node_t n2; - node_t new_root; - branch_t b; - assert(t && *t); - - if (RTreeInsertNode(*t, 0, r, data, &n2)) - /* deal with root split */ - { - new_root = RTreeNewNode(); - new_root->level = (*t)->level + 1; - b.mbr = RTreeNodeCover(*t); - b.child = (void *) *t; - RTreeAddBranch(new_root, b, NULL); - b.mbr = RTreeNodeCover(n2); - b.child = (void *) n2; - RTreeAddBranch(new_root, b, NULL); - *t = new_root; - } -} - -static int RTreeInsertNode (node_t n, int level, - rect_t r, void *data, - node_t *new_node) -{ - int i; - node_t n2; - branch_t b; - - assert(n && new_node); - assert(level >= 0 && level <= n->level); - - if (n->level > level) - { - i = RTreePickBranch(r,n); - if (!RTreeInsertNode((node_t) n->branch[i].child, level, - r, data,&n2)) /* not split */ - { - n->branch[i].mbr = RectCombine(r,n->branch[i].mbr); - return FALSE; - } - else /* node split */ - { - n->branch[i].mbr = RTreeNodeCover(n->branch[i].child); - b.child = n2; - b.mbr = RTreeNodeCover(n2); - return RTreeAddBranch(n, b, new_node); - } - } - else /*insert level*/ - { - b.mbr = r; - b.child = data; - return RTreeAddBranch(n, b, new_node); - } -} - -static int RTreeAddBranch(node_t n, branch_t b, node_t *new_node) -{ - int i; - - assert(n); - - if (n->count < MAXCARD) /*split not necessary*/ - { - for (i = 0; i < MAXCARD; i++) - if (n->branch[i].child == NULL) - { - n->branch[i] = b; - n->count ++; - break; - } - return FALSE; - } - else /*needs to split*/ - { - assert(new_node); - RTreeSplitNode (n, b, new_node); - return TRUE; - } -} - -static int RTreePickBranch (rect_t r, node_t n) -{ - int i; - double area; - double inc_area; - rect_t tmp; - int best_i; - double best_inc; - double best_i_area; - - best_i = 0; - best_inc = DBL_MAX; /* double Max value */ - best_i_area = DBL_MAX; - - for (i = 0; i < MAXCARD; i++) - if (n->branch[i].child) - { - area = RectArea (n->branch[i].mbr); - tmp = RectCombine (r, n->branch[i].mbr); - inc_area = RectArea (tmp) - area; - - if (inc_area < best_inc) - { - best_inc = inc_area; - best_i = i; - best_i_area = area; - } - else if (inc_area == best_inc && best_i_area > area) - { - best_inc = inc_area; - best_i = i; - best_i_area = area; - } - } - else - break; - return best_i; -} - -static void RTreeSplitNode (node_t n, branch_t b, node_t *new_node) -{ - partition_t p; - int level; - int i; - - assert(n); - assert(new_node); - - p = PartitionNew(); - - for (i = 0; i < MAXCARD; i ++) - PartitionPush(&p,n->branch[i]); - PartitionPush(&p,b); - - level = n->level; - RTreeNodeInit(n); - n->level = level; - *new_node = RTreeNewNode(); - (*new_node)->level = level; - - RTreePickSeeds(&p, n, *new_node); - - while (p.n) - if (n->count + p.n <= MINCARD) - /* first group (n) needs all entries */ - RTreeNodeAddBranch(&(p.cover[0]), n, PartitionPop(&p)); - else if ((*new_node)->count + p.n <= MINCARD) - /* second group (new_node) needs all entries */ - RTreeNodeAddBranch(&(p.cover[1]), *new_node, PartitionPop(&p)); - else - RTreePickNext(&p, n, *new_node); -} - -static void RTreePickNext(partition_t *p, node_t n1, node_t n2) -/* linear version */ -{ - branch_t b; - double area[2], inc_area[2]; - rect_t tmp; - - b = PartitionPop(p); - - area[0] = RectArea (p->cover[0]); - tmp = RectCombine (p->cover[0], b.mbr); - inc_area[0] = RectArea (tmp) - area[0]; - - area[1] = RectArea (p->cover[1]); - tmp = RectCombine (p->cover[1], b.mbr); - inc_area[1] = RectArea (tmp) - area[1]; - - if (inc_area[0] < inc_area[1] || - (inc_area[0] == inc_area[1] && area[0] < area[1])) - RTreeNodeAddBranch(&(p->cover[0]),n1,b); - else - RTreeNodeAddBranch(&(p->cover[1]),n2,b); -} - -static void RTreePickSeeds(partition_t *p, node_t n1, node_t n2) -/* puts in index 0 of each node the resulting entry, forming the two - groups - This is the linear version -*/ -{ - int dim,high, i; - int highestLow[NUMDIMS], lowestHigh[NUMDIMS]; - double width[NUMDIMS]; - int seed0, seed1; - double sep, best_sep; - - assert(p->n == MAXCARD + 1); - - for (dim = 0; dim < NUMDIMS; dim++) - { - high = dim + NUMDIMS; - highestLow[dim] = lowestHigh[dim] = 0; - for (i = 1; i < MAXCARD +1; i++) - { - if (p->buffer[i].mbr.coords[dim] > - p->buffer[highestLow[dim]].mbr.coords[dim]) - highestLow[dim] = i; - if (p->buffer[i].mbr.coords[high] < - p->buffer[lowestHigh[dim]].mbr.coords[high]) - lowestHigh[dim] = i; - } - width[dim] = p->cover_all.coords[high] - p->cover_all.coords[dim]; - assert(width[dim] >= 0); - } - - seed0 = lowestHigh[0]; - seed1 = highestLow[0]; - best_sep = 0; - for (dim = 0; dim < NUMDIMS; dim ++) - { - high = dim + NUMDIMS; - - sep = (p->buffer[highestLow[dim]].mbr.coords[dim] - - p->buffer[lowestHigh[dim]].mbr.coords[high]) / width[dim]; - if (sep > best_sep) - { - seed0 = lowestHigh[dim]; - seed1 = highestLow[dim]; - best_sep = sep; - } - } -/* assert (seed0 != seed1); */ - if (seed0 > seed1) - { - RTreeNodeAddBranch(&(p->cover[0]),n1,PartitionGet(p,seed0)); - RTreeNodeAddBranch(&(p->cover[1]),n2,PartitionGet(p,seed1)); - } - else if (seed0 < seed1) - { - RTreeNodeAddBranch(&(p->cover[0]),n1,PartitionGet(p,seed1)); - RTreeNodeAddBranch(&(p->cover[1]),n2,PartitionGet(p,seed0)); - } -} - -static void RTreeNodeAddBranch(rect_t *r, node_t n, branch_t b) -{ - int i; - - assert(n); - assert(n->count < MAXCARD); - - for (i = 0; i < MAXCARD; i++) - if (n->branch[i].child == NULL) - { - n->branch[i] = b; - n->count ++; - break; - } - *r = RectCombine(*r,b.mbr); -} - - -void RTreePrint(node_t t) -{ - int i; - - /* printf("rtree([_,_,_,_,_]).\n"); */ - printf("rtree(%p,%d,[",t,t->level); - for (i = 0; i < MAXCARD; i++) - { - if (t->branch[i].child != NULL) - { - printf("(%p,",t->branch[i].child); - RectPrint(t->branch[i].mbr); - printf(")"); - } - else - { - printf("nil"); - } - if (i < MAXCARD-1) - printf(","); - } - printf("]).\n"); - - if (t->level != 0) - for (i = 0; i < MAXCARD; i++) - if (t->branch[i].child != NULL) - RTreePrint((node_t) t->branch[i].child); - else - break; -} - -/* - * Partition related - */ - -static partition_t PartitionNew (void) -{ - partition_t p; - memset((void *) &p,0, sizeof(p)); - p.cover[0] = p.cover[1] = p.cover_all = RectInit(); - return p; -} - -static void PartitionPush (partition_t *p, branch_t b) -{ - assert(p->n < MAXCARD + 1); - p->buffer[p->n] = b; - p->n ++; - p->cover_all = RectCombine(p->cover_all,b.mbr); -} - -static branch_t PartitionPop (partition_t *p) -{ - assert(p->n > 0); - p->n --; - return p->buffer[p->n]; -} - -static branch_t PartitionGet (partition_t *p, int n) -{ - branch_t b; - assert (p->n > n); - b = p->buffer[n]; - p->buffer[n] = PartitionPop(p); - return b; -} - -/* - * Rect related - */ - -rect_t RectInit (void) -{ - rect_t r = {{DBL_MAX, DBL_MAX, DBL_MIN, DBL_MIN}}; - return (r); -} - -static double RectArea (rect_t r) -{ - int i; - double area; - - for (i = 0,area = 1; i < NUMDIMS; i++) - area *= r.coords[i+NUMDIMS] - r.coords[i]; - -/* area = (r.coords[1] - r.coords[0]) * */ -/* (r.coords[3] - r.coords[2]); */ - - return area; -} - -static rect_t RectCombine (rect_t r, rect_t s) -{ - int i; - rect_t new_rect; - - for (i = 0; i < NUMDIMS; i++) - { - new_rect.coords[i] = MIN(r.coords[i],s.coords[i]); - new_rect.coords[i+NUMDIMS] = MAX(r.coords[i+NUMDIMS],s.coords[i+NUMDIMS]); - } - - return new_rect; -} - -static int RectOverlap (rect_t r, rect_t s) -{ - int i; - - for (i = 0; i < NUMDIMS; i++) - if (r.coords[i] > s.coords[i + NUMDIMS] || - s.coords[i] > r.coords[i + NUMDIMS]) - return FALSE; - return TRUE; -} - -static rect_t RTreeNodeCover(node_t n) -{ - int i; - rect_t r = RectInit(); - - for (i = 0; i < MAXCARD; i++) - if (n->branch[i].child) - { - r = RectCombine (r, n->branch[i].mbr); - } - else - break; - - return r; -} - -static void RectPrint (rect_t r) -{ - int i; - - printf("["); - for (i = 0; i < 2*NUMDIMS; i++) - { - printf("%f",r.coords[i]); - if ( i < 2*NUMDIMS - 1) - printf(","); - } - printf("]"); -} diff --git a/packages/udi/rtree.h b/packages/udi/rtree.h deleted file mode 100644 index f05ae86e8..000000000 --- a/packages/udi/rtree.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef _RTREE_ -#define _RTREE_ - -#ifndef FALSE -#define FALSE 0 -#endif -#ifndef TRUE -#define TRUE !FALSE -#endif - -#define NUMDIMS 2 /* 2d */ - -struct Rect -{ - double coords[2*NUMDIMS]; /* x1min, y1min, ... , x1max, y1max, ...*/ -}; -typedef struct Rect rect_t; - -struct Branch -{ - rect_t mbr; - void * child; /*void * so user can store whatever he needs, in case - of non-leaf ndes it stores the child-pointer*/ -}; -typedef struct Branch branch_t; - -#define PGSIZE 196 -#define MAXCARD (int)((PGSIZE-(2*sizeof(int)))/ sizeof(struct Branch)) -#define MINCARD (MAXCARD / 2) - -struct Node -{ - int count; - int level; - branch_t branch[MAXCARD]; -}; -typedef struct Node * node_t; - -typedef node_t rtree_t; - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -/* CallBack to search function */ -typedef int (*SearchHitCallback)(rect_t r, void *data, void *arg); - -extern rtree_t RTreeNew (void); -extern void RTreeInsert (rtree_t *, rect_t, void *); -extern int RTreeSearch (rtree_t, rect_t, SearchHitCallback, void *); -extern void RTreeDestroy (rtree_t); -extern void RTreePrint(node_t); -extern rect_t RectInit (void); - -struct Partition -{ - branch_t buffer[MAXCARD+1]; - int n; - rect_t cover_all; - rect_t cover[2]; -}; -typedef struct Partition partition_t; - -#endif /* _RTREE_ */ diff --git a/packages/udi/rtree_udi.c b/packages/udi/rtree_udi.c deleted file mode 100644 index e9c092c98..000000000 --- a/packages/udi/rtree_udi.c +++ /dev/null @@ -1,179 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "Yap.h" - -#include "rtree.h" -#include "clause_list.h" -#include "rtree_udi_i.h" -#include "rtree_udi.h" - -static int YAP_IsNumberTermToFloat (Term term, YAP_Float *n) -{ - if (YAP_IsIntTerm (term) != FALSE) - { - if (n != NULL) - *n = (YAP_Float) YAP_IntOfTerm (term); - return (TRUE); - } - if (YAP_IsFloatTerm (term) != FALSE) - { - if (n != NULL) - *n = YAP_FloatOfTerm (term); - return (TRUE); - } - return (FALSE); -} - -static rect_t RectOfTerm (Term term) -{ - YAP_Term tmp; - rect_t rect; - int i; - - if (!YAP_IsPairTerm(term)) - return (RectInit()); - - for (i = 0; YAP_IsPairTerm(term) && i < 4; i++) - { - tmp = YAP_HeadOfTerm (term); - if (!YAP_IsNumberTermToFloat(tmp,&(rect.coords[i]))) - return (RectInit()); - term = YAP_TailOfTerm (term); - } - - return (rect); -} - -control_t *RtreeUdiInit (Term spec, - void * pred, - int arity){ - control_t *control; - YAP_Term arg; - int i, c; - /* YAP_Term mod; */ - - /* spec = Yap_StripModule(spec, &mod); */ - if (! YAP_IsApplTerm(spec)) - return (NULL); - - control = (control_t *) malloc (sizeof(*control)); - assert(control); - memset((void *) control,0, sizeof(*control)); - - c = 0; - for (i = 1; i <= arity; i ++) - { - arg = YAP_ArgOfTerm(i,spec); - if (YAP_IsAtomTerm(arg) - && strcmp("+",YAP_AtomName(YAP_AtomOfTerm(arg))) == 0) - { - - (*control)[c].pred = pred; - (*control)[c++].arg = i; - - } - } - -/* for (i = 0; i < NARGS; i++) - printf("%d,%p\t",(*control)[i].arg,(*control)[i].tree); - printf("\n"); */ - - return control; -} - -control_t *RtreeUdiInsert (Term term,control_t *control,void *clausule) -{ - int i; - rect_t r; - - assert(control); - - for (i = 0; i < NARGS && (*control)[i].arg != 0 ; i++) - { - r = RectOfTerm(YAP_ArgOfTerm((*control)[i].arg,term)); - if (!(*control)[i].tree) - (*control)[i].tree = RTreeNew(); - RTreeInsert(&(*control)[i].tree,r,clausule); - } - - /* printf("insert %p\n", clausule); */ - - return (control); -} - -static int callback(rect_t r, void *data, void *arg) -{ - callback_m_t x; - x = (callback_m_t) arg; - return Yap_ClauseListExtend(x->cl,data,x->pred); -} - -/*ARGS ARE AVAILABLE*/ -void *RtreeUdiSearch (control_t *control) -{ - rect_t r; - int i; - struct ClauseList clauselist; - struct CallbackM cm; - callback_m_t c; - YAP_Term Constraints; - - /*RTreePrint ((*control)[0].tree);*/ - - for (i = 0; i < NARGS && (*control)[i].arg != 0 ; i++) { - YAP_Term t = YAP_A((*control)[i].arg); - if (YAP_IsAttVar(t)) - { - fprintf(stderr,"i=%ld\n",i); - /*get the constraits rect*/ - Constraints = YAP_AttsOfVar(t); - /* Yap_DebugPlWrite(Constraints); */ - r = RectOfTerm(YAP_ArgOfTerm(2,Constraints)); - - c = &cm; - c->cl = Yap_ClauseListInit(&clauselist); - c->pred = (*control)[i].pred; - if (!c->cl) - return NULL; /*? or fail*/ - RTreeSearch((*control)[i].tree, r, callback, c); - Yap_ClauseListClose(c->cl); - - if (Yap_ClauseListCount(c->cl) == 0) - { - Yap_ClauseListDestroy(c->cl); - return Yap_FAILCODE(); - } - - if (Yap_ClauseListCount(c->cl) == 1) - { - return Yap_ClauseListToClause(c->cl); - } - - return Yap_ClauseListCode(c->cl); - } - } - return NULL; /*YAP FALLBACK*/ -} - -int RtreeUdiDestroy(control_t *control) -{ - int i; - - assert(control); - - for (i = 0; i < NARGS && (*control)[i].arg != 0; i++) - { - if ((*control)[i].tree) - RTreeDestroy((*control)[i].tree); - } - - free(control); - control = NULL; - - return TRUE; -} diff --git a/packages/udi/rtree_udi_i.h b/packages/udi/rtree_udi_i.h deleted file mode 100644 index d16aeb04e..000000000 --- a/packages/udi/rtree_udi_i.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _RTREE_UDI_I_ -#define _RTREE_UDI_I_ - -#define NARGS 5 -struct Control -{ - int arg; - void *pred; - rtree_t tree; -}; -typedef struct Control control_t[NARGS]; - -struct CallbackM -{ - clause_list_t cl; - void * pred; -}; -typedef struct CallbackM * callback_m_t; - -#endif /* _RTREE_UDI_I_ */ diff --git a/pl/udi.yap b/pl/udi.yap index a1e177174..945a77110 100644 --- a/pl/udi.yap +++ b/pl/udi.yap @@ -9,7 +9,7 @@ ************************************************************************** * * * File: udi.yap * -* Last rev: 8/2/88 * +* Last rev: 17/12/2012 * * mods: * * comments: support user defined indexing * * * @@ -22,5 +22,4 @@ ******************/ udi(Pred) :- - '$udi_init'(rtree, Pred). - + '$udi_init'(Pred).