#include "config.h"
#ifdef HAVE_UNUSED_ATTRIBUTE
#define UNUSEDARG __attribute__ ((unused))
#else
#define UNUSEDARG
#endif
#ifdef HAVE_STDLIB_H
#include "stdlib.h"
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#define Dwarf_Unsigned unsigned long long
#if defined(_WIN32) && defined(HAVE_NONSTANDARD_PRINTF_64_FORMAT)
#define DW_PR_DUx "I64x"
#define DW_PR_DUu "I64u"
#else
#define DW_PR_DUx "llx"
#define DW_PR_DUu "llu"
#endif
#include "dwarf_tsearch.h"
static unsigned long primes[] =
{
#if 0
5,11, 17,23, 31, 47, 53,
#endif
79,
1009,
5591,
10007,
21839,
41413,
99907,
199967,
400009,
800029,
1600141,
3000089,
6000121,
12000257,
24000143,
48000203,
100000127,
200001611,
400000669,
800000573,
0
};
static unsigned long allowed_fill_percent = 90;
struct hs_base {
unsigned long tablesize_;
unsigned long tablesize_entry_index_;
unsigned long allowed_fill_;
unsigned long record_count_;
struct ts_entry * hashtab_;
DW_TSHASHTYPE (*hashfunc_)(const void *key);
};
struct ts_entry {
const void * keyptr;
unsigned char entryused;
struct ts_entry *next;
};
enum search_intent_t
{
want_insert,
only_find,
want_delete
};
static struct ts_entry *
tsearch_inner( const void *key, struct hs_base* head,
int (*compar)(const void *, const void *),
const enum search_intent_t intent, int*inserted,
struct ts_entry **parent_ptr);
static void
dwarf_tdestroy_inner(struct hs_base*h,
void (*free_node)(void *nodep),
int depth);
static unsigned long
calculate_allowed_fill(unsigned long fill_percent, unsigned long ct)
{
unsigned long v = 0;
if(ct < 100000) {
unsigned long v2 = (ct *fill_percent)/100;
return v2;
}
v = (ct /100)*fill_percent;
return v;
}
void *
dwarf_initialize_search_hash( void **treeptr,
DW_TSHASHTYPE(*hashfunc)(const void *key),
unsigned long size_estimate)
{
unsigned long prime_to_use = primes[0];
unsigned entry_index = 0;
unsigned k = 0;
struct hs_base *base = 0;
base = *(struct hs_base **)treeptr;
if(base) {
return base ;
}
base = calloc(sizeof(struct hs_base),1);
if(!base) {
return NULL ;
}
prime_to_use = primes[0];
while(size_estimate && (size_estimate > prime_to_use)) {
k = k +1;
prime_to_use = primes[k];
if(prime_to_use == 0) {
free(base);
return NULL;
}
entry_index = k;
}
base->tablesize_ = prime_to_use;
base->allowed_fill_ = calculate_allowed_fill(allowed_fill_percent,
prime_to_use);
if( base->allowed_fill_< (base->tablesize_/2)) {
free(base);
return NULL;
}
base->record_count_ = 0;
base->tablesize_entry_index_ = entry_index;
base->hashfunc_ = hashfunc;
base->hashtab_ = calloc(sizeof(struct ts_entry),base->tablesize_);
if(!base->hashtab_) {
free(base);
return NULL;
}
*treeptr = base;
return base;
}
static void print_entry(struct ts_entry *t,const char *descr,
char *(* keyprint)(const void *),
unsigned long hashpos,
unsigned long chainpos)
{
char *v = 0;
if(!t->entryused) {
return;
}
v = keyprint(t->keyptr);
printf(
"[%4lu.%02lu] 0x%08" DW_PR_DUx
" <keyptr 0x%08" DW_PR_DUx
"> <key %s> %s\n",
hashpos,chainpos,
(Dwarf_Unsigned)(uintptr_t)t,
(Dwarf_Unsigned)(uintptr_t)t->keyptr,
v,
descr);
}
static void
dumptree_inner(const struct hs_base *h,
char *(* keyprint)(const void *),
const char *descr, int printdetails)
{
unsigned long ix = 0;
unsigned long tsize = h->tablesize_;
struct ts_entry *p = &h->hashtab_[0];
unsigned long hashused = 0;
unsigned long maxchainlength = 0;
unsigned long chainsgt1 = 0;
printf("dumptree head ptr : 0x%08" DW_PR_DUx
" size %" DW_PR_DUu
" entries %" DW_PR_DUu
" allowed %" DW_PR_DUu " %s\n",
(Dwarf_Unsigned)(uintptr_t)h,
(Dwarf_Unsigned)h->tablesize_,
(Dwarf_Unsigned)h->record_count_,
(Dwarf_Unsigned)h->allowed_fill_,
descr);
for( ; ix < tsize; ix++,p++) {
unsigned long chainlength = 0;
struct ts_entry*n = 0;
int chainpos = 0;
if(p->entryused) {
++hashused;
chainlength = 1;
if(printdetails) {
print_entry(p,"head",keyprint,ix,chainpos);
}
}
chainpos++;
for(n = p->next; n ; n = n->next) {
chainlength++;
if(printdetails) {
print_entry(n,"chain",keyprint,ix,chainpos);
}
}
if(chainlength > maxchainlength) {
maxchainlength = chainlength;
}
if (chainlength > 1) {
chainsgt1++;
}
}
printf("Hashtable: %lu of %lu hash entries used.\n",hashused,tsize);
printf("Hashtable: %lu chains length longer than 1. \n",chainsgt1);
printf("Hashtable: %lu is maximum chain length.\n",maxchainlength);
}
void
dwarf_tdump(const void*headp_in,
char *(* keyprint)(const void *),
const char *msg)
{
const struct hs_base *head = (const struct hs_base *)headp_in;
if(!head) {
printf("dumptree null tree ptr : %s\n",msg);
return;
}
dumptree_inner(head,keyprint,msg,1);
}
static struct ts_entry *
allocate_ts_entry(const void *key)
{
struct ts_entry *e = 0;
e = (struct ts_entry *) malloc(sizeof(struct ts_entry));
if(!e) {
return NULL;
}
e->keyptr = key;
e->entryused = 1;
e->next = 0;
return e;
}
static void
resize_table(struct hs_base *head,
int (*compar)(const void *, const void *))
{
struct hs_base newhead;
unsigned new_entry_index = 0;
unsigned long prime_to_use = 0;
newhead = *head;
newhead.hashtab_ = 0;
newhead.record_count_ = 0;
new_entry_index = head->tablesize_entry_index_ +1;
prime_to_use = primes[new_entry_index];
if(prime_to_use == 0) {
return;
}
newhead.tablesize_ = prime_to_use;
newhead.allowed_fill_ = calculate_allowed_fill(allowed_fill_percent,
prime_to_use);
if( newhead.allowed_fill_< (newhead.tablesize_/2)) {
return;
}
newhead.tablesize_entry_index_ = new_entry_index;
newhead.hashtab_ = calloc(sizeof(struct ts_entry),newhead.tablesize_);
if(!newhead.hashtab_) {
free(newhead.hashtab_);
return;
}
{
int fillnewfail = 0;
unsigned long ix = 0;
unsigned long tsize = head->tablesize_;
struct ts_entry *p = &head->hashtab_[0];
for( ; ix < tsize; ix++,p++) {
int inserted = 0;
struct ts_entry*n = 0;
if(fillnewfail) {
break;
}
if(p->keyptr) {
tsearch_inner(p->keyptr,
&newhead,compar,
want_insert,
&inserted,
0);
if(!inserted) {
fillnewfail = 1;
break;
}
}
for(n = p->next; n ; n = n->next) {
inserted = 0;
tsearch_inner(n->keyptr,
&newhead,compar,
want_insert,
&inserted,
0);
if(!inserted) {
fillnewfail = 1;
break;
}
}
}
if(fillnewfail) {
free(newhead.hashtab_);
return;
}
}
dwarf_tdestroy_inner(head,0,0);
free(head->hashtab_);
head->hashtab_ = 0;
*head = newhead;
return;
}
static struct ts_entry *
tsearch_inner( const void *key, struct hs_base* head,
int (*compar)(const void *, const void *),
const enum search_intent_t intent, int*inserted,
struct ts_entry **owner_ptr)
{
struct ts_entry *s =0;
struct ts_entry *c =0;
struct ts_entry *q =0;
int kc = 0;
DW_TSHASHTYPE keyhash = 0;
DW_TSHASHTYPE hindx = 0;
struct ts_entry *chain_parent = 0;
if(! head->hashfunc_) {
return NULL;
}
keyhash = head->hashfunc_(key);
if (intent == want_insert) {
if( head->record_count_ > head->allowed_fill_) {
resize_table(head,compar);
}
}
hindx = keyhash%head->tablesize_;
s = &head->hashtab_[hindx];
if(!s->entryused) {
if(intent != want_insert) {
return NULL;
}
*inserted = 1;
head->record_count_++;
s->keyptr = (const void *)key;
s->entryused = 1;
s->next = 0;
return s;
}
kc = compar(key,s->keyptr);
if(kc == 0 ) {
if(want_delete) {
*owner_ptr = 0;
}
return (void *)&(s->keyptr);
}
chain_parent = s;
for(c = s->next; c; c = c->next) {
kc = compar(key,c->keyptr);
if(kc == 0 ) {
if(want_delete) {
*owner_ptr = chain_parent;
}
return (void *)&(c->keyptr);
}
chain_parent = c;
}
if(intent != want_insert) {
return NULL;
}
q = allocate_ts_entry(key);
if (!q) {
return q;
}
q->next = s->next;
s->next = q;
head->record_count_++;
*inserted = 1;
return q;
}
void *
dwarf_tsearch(const void *key, void **headin,
int (*compar)(const void *, const void *))
{
struct hs_base **rootp = (struct hs_base **)headin;
struct hs_base *head = *rootp;
struct ts_entry *r = 0;
int inserted = 0;
struct ts_entry *nullme = 0;
if (!head) {
return NULL;
}
r = tsearch_inner(key,head,compar,want_insert,&inserted,&nullme);
if (!r) {
return NULL;
}
return (void *)&(r->keyptr);
}
void *
dwarf_tfind(const void *key, void *const *rootp,
int (*compar)(const void *, const void *))
{
struct hs_base **proot = (struct hs_base **)rootp;
struct hs_base *head = *proot;
struct ts_entry *r = 0;
int inserted = 0;
struct ts_entry * nullme = 0;
if (!head) {
return NULL;
}
r = tsearch_inner(key,head,compar,only_find,&inserted,&nullme);
if(!r) {
return NULL;
}
return (void *)(&(r->keyptr));
}
void *
dwarf_tdelete(const void *key, void **rootp,
int (*compar)(const void *, const void *))
{
struct hs_base **proot = (struct hs_base **)rootp;
struct hs_base *head = *proot;
struct ts_entry *found = 0;
int inserted = 0;
struct ts_entry * parentp = 0;
if (!head) {
return NULL;
}
found = tsearch_inner(key,head,compar,want_delete,&inserted,
&parentp);
if(found) {
if(parentp) {
head->record_count_--;
parentp->next = found->next;
free(found);
return (void *)&(parentp->keyptr);
}
if(found->next) {
struct ts_entry *pullup = found->next;
*found = *pullup;
free(pullup);
head->record_count_--;
return (void *)&(found->keyptr);
} else {
head->record_count_--;
found->next = 0;
found->keyptr = 0;
found->entryused = 0;
return NULL;
}
}
return NULL;
}
static void
dwarf_twalk_inner(const struct hs_base *h,
struct ts_entry *p,
void (*action)(const void *nodep, const DW_VISIT which,
UNUSEDARG const int depth),
UNUSEDARG unsigned level)
{
unsigned long ix = 0;
unsigned long tsize = h->tablesize_;
for( ; ix < tsize; ix++,p++) {
struct ts_entry*n = 0;
if(p->keyptr) {
action((void *)(&(p->keyptr)),dwarf_leaf,level);
}
for(n = p->next; n ; n = n->next) {
action((void *)(&(n->keyptr)),dwarf_leaf,level);
}
}
}
void
dwarf_twalk(const void *rootp,
void (*action)(const void *nodep, const DW_VISIT which,
UNUSEDARG const int depth))
{
const struct hs_base *head = (const struct hs_base *)rootp;
struct ts_entry *root = 0;
if(!head) {
return;
}
root = head->hashtab_;
dwarf_twalk_inner(head,root,action,0);
}
static void
dwarf_tdestroy_inner(struct hs_base*h,
void (*free_node)(void *nodep),
UNUSEDARG int depth)
{
unsigned long ix = 0;
unsigned long tsize = h->tablesize_;
struct ts_entry *p = &h->hashtab_[0];
for( ; ix < tsize; ix++,p++) {
struct ts_entry*n = 0;
struct ts_entry*prev = 0;
if(p->keyptr && p->entryused) {
if(free_node) {
free_node((void *)(p->keyptr));
}
--h->record_count_;
}
for(n = p->next; n ; ) {
if(free_node) {
free_node((void *)(n->keyptr));
}
--h->record_count_;
prev = n;
n = n->next;
free(prev);
}
}
}
void
dwarf_tdestroy(void *rootp, void (*free_node)(void *nodep))
{
struct hs_base *head = (struct hs_base *)rootp;
struct ts_entry *root = 0;
if(!head) {
return;
}
root = head->hashtab_;
dwarf_tdestroy_inner(head,free_node,0);
free(root);
free(head);
}