|
Packit |
cdaae3 |
/* Copyright (c) 2013-2014, David Anderson
|
|
Packit |
cdaae3 |
All rights reserved.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
Redistribution and use in source and binary forms, with
|
|
Packit |
cdaae3 |
or without modification, are permitted provided that the
|
|
Packit |
cdaae3 |
following conditions are met:
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
Redistributions of source code must retain the above
|
|
Packit |
cdaae3 |
copyright notice, this list of conditions and the following
|
|
Packit |
cdaae3 |
disclaimer.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
Redistributions in binary form must reproduce the above
|
|
Packit |
cdaae3 |
copyright notice, this list of conditions and the following
|
|
Packit |
cdaae3 |
disclaimer in the documentation and/or other materials
|
|
Packit |
cdaae3 |
provided with the distribution.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
Packit |
cdaae3 |
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
Packit |
cdaae3 |
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
Packit |
cdaae3 |
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
Packit |
cdaae3 |
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
Packit |
cdaae3 |
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
Packit |
cdaae3 |
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
Packit |
cdaae3 |
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
Packit |
cdaae3 |
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
Packit |
cdaae3 |
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
Packit |
cdaae3 |
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
Packit |
cdaae3 |
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
Packit |
cdaae3 |
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* The interfaces follow tsearch (See the Single
|
|
Packit |
cdaae3 |
Unix Specification) but the implementation is
|
|
Packit |
cdaae3 |
written without reference to the source of any
|
|
Packit |
cdaae3 |
version of tsearch or any hashing code.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
An additional interface is added (compared to
|
|
Packit |
cdaae3 |
a real tsearch) to let the caller identify a
|
|
Packit |
cdaae3 |
'hash' function with each hash table (called
|
|
Packit |
cdaae3 |
a tree below, but that is a misnomer).
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
So read 'tree' below as hash table.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
See http://www.prevanders.net/tsearch.html
|
|
Packit |
cdaae3 |
for information and an example of use.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
Based on Knuth, chapter 6.4
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
This uses a hash based on the key.
|
|
Packit |
cdaae3 |
Collision resolution is by chaining.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
twalk() and tdestroy() walk in a random order.
|
|
Packit |
cdaae3 |
The 'preorder' etc labels mean nothing in a hash, so everything
|
|
Packit |
cdaae3 |
is called a leaf.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
#include "config.h"
|
|
Packit |
cdaae3 |
#include "dwarf_incl.h"
|
|
Packit |
cdaae3 |
#include "stdlib.h" /* for free() etc */
|
|
Packit |
cdaae3 |
#include <stdio.h> /* for printf() */
|
|
Packit |
cdaae3 |
#include "dwarf_tsearch.h"
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* A table of primes used to size and resize the hash table.
|
|
Packit |
cdaae3 |
From public sources of prime numbers, arbitrarily chosen
|
|
Packit |
cdaae3 |
to approximately double in size at each step.
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
static unsigned long long primes[] =
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
#if 0 /* for testing only */
|
|
Packit |
cdaae3 |
5,11, 17,23, 31, 47, 53,
|
|
Packit |
cdaae3 |
#endif
|
|
Packit |
cdaae3 |
79,
|
|
Packit |
cdaae3 |
1009,
|
|
Packit |
cdaae3 |
5591,
|
|
Packit |
cdaae3 |
10007,
|
|
Packit |
cdaae3 |
21839,
|
|
Packit |
cdaae3 |
41413,
|
|
Packit |
cdaae3 |
99907,
|
|
Packit |
cdaae3 |
199967,
|
|
Packit |
cdaae3 |
400009,
|
|
Packit |
cdaae3 |
800029,
|
|
Packit |
cdaae3 |
1600141,
|
|
Packit |
cdaae3 |
3000089,
|
|
Packit |
cdaae3 |
6000121,
|
|
Packit |
cdaae3 |
12000257,
|
|
Packit |
cdaae3 |
24000143,
|
|
Packit |
cdaae3 |
48000203,
|
|
Packit |
cdaae3 |
100000127,
|
|
Packit |
cdaae3 |
200001611,
|
|
Packit |
cdaae3 |
400000669,
|
|
Packit |
cdaae3 |
800000573,
|
|
Packit |
cdaae3 |
0 /* Here we are giving up */
|
|
Packit |
cdaae3 |
};
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
static unsigned long allowed_fill_percent = 90;
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
struct hs_base {
|
|
Packit |
cdaae3 |
unsigned long tablesize_;
|
|
Packit |
cdaae3 |
unsigned long tablesize_entry_index_;
|
|
Packit |
cdaae3 |
unsigned long allowed_fill_;
|
|
Packit |
cdaae3 |
/* Record_count means number of active records,
|
|
Packit |
cdaae3 |
counting all records on chains.
|
|
Packit |
cdaae3 |
When the record_count is > 90% of a full
|
|
Packit |
cdaae3 |
tablesize_ we redo the table before adding
|
|
Packit |
cdaae3 |
a new entry. */
|
|
Packit |
cdaae3 |
unsigned long record_count_;
|
|
Packit |
cdaae3 |
/* hashtab_ is an array of hs_entry,
|
|
Packit |
cdaae3 |
indexes 0 through tablesize_ -1. */
|
|
Packit |
cdaae3 |
struct ts_entry * hashtab_;
|
|
Packit |
cdaae3 |
DW_TSHASHTYPE (*hashfunc_)(const void *key);
|
|
Packit |
cdaae3 |
};
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
struct ts_entry {
|
|
Packit |
cdaae3 |
const void * keyptr;
|
|
Packit |
cdaae3 |
/* So that a keyptr of 0 (if added) is
|
|
Packit |
cdaae3 |
not confused with an empty hash slot,
|
|
Packit |
cdaae3 |
we must mark used slots as used in the hash tab */
|
|
Packit |
cdaae3 |
unsigned char entryused;
|
|
Packit |
cdaae3 |
struct ts_entry *next;
|
|
Packit |
cdaae3 |
};
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
enum search_intent_t
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
want_insert,
|
|
Packit |
cdaae3 |
only_find,
|
|
Packit |
cdaae3 |
want_delete
|
|
Packit |
cdaae3 |
};
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
static struct ts_entry *
|
|
Packit |
cdaae3 |
tsearch_inner( const void *key, struct hs_base* head,
|
|
Packit |
cdaae3 |
int (*compar)(const void *, const void *),
|
|
Packit |
cdaae3 |
const enum search_intent_t intent, int*inserted,
|
|
Packit |
cdaae3 |
struct ts_entry **parent_ptr);
|
|
Packit |
cdaae3 |
static void
|
|
Packit |
cdaae3 |
dwarf_tdestroy_inner(struct hs_base*h,
|
|
Packit |
cdaae3 |
void (*free_node)(void *nodep),
|
|
Packit |
cdaae3 |
int depth);
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* A trivial integer-based percentage calculation.
|
|
Packit |
cdaae3 |
Percents >100 are reasonable for a hash-with-chains
|
|
Packit |
cdaae3 |
situation (even if they might not be the best choice
|
|
Packit |
cdaae3 |
for performance). */
|
|
Packit |
cdaae3 |
static unsigned long
|
|
Packit |
cdaae3 |
calculate_allowed_fill(unsigned long fill_percent, unsigned long ct)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
unsigned long v = 0;
|
|
Packit |
cdaae3 |
if(ct < 100000) {
|
|
Packit |
cdaae3 |
unsigned long v2 = (ct *fill_percent)/100;
|
|
Packit |
cdaae3 |
return v2;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
v = (ct /100)*fill_percent;
|
|
Packit |
cdaae3 |
return v;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* Initialize the hash and pass in the hash function.
|
|
Packit |
cdaae3 |
If the entry count needed is unknown, pass in 0 as a count estimate,
|
|
Packit |
cdaae3 |
but if the number of hash entries needed can be estimated,
|
|
Packit |
cdaae3 |
pass in the estimate (which need not be prime, we actually use
|
|
Packit |
cdaae3 |
the nearest higher prime from the above table).
|
|
Packit |
cdaae3 |
If the estimated count is
|
|
Packit |
cdaae3 |
Return the tree base, or return NULL if insufficient memory. */
|
|
Packit |
cdaae3 |
void *
|
|
Packit |
cdaae3 |
dwarf_initialize_search_hash( void **treeptr,
|
|
Packit |
cdaae3 |
DW_TSHASHTYPE(*hashfunc)(const void *key),
|
|
Packit |
cdaae3 |
unsigned long size_estimate)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
unsigned long prime_to_use =primes[0];
|
|
Packit |
cdaae3 |
unsigned entry_index = 0;
|
|
Packit |
cdaae3 |
unsigned k = 0;
|
|
Packit |
cdaae3 |
struct hs_base *base = 0;
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
base = *(struct hs_base **)treeptr;
|
|
Packit |
cdaae3 |
if(base) {
|
|
Packit |
cdaae3 |
/* initalized already. */
|
|
Packit |
cdaae3 |
return base ;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
base = calloc(sizeof(struct hs_base),1);
|
|
Packit |
cdaae3 |
if(!base) {
|
|
Packit |
cdaae3 |
/* Out of memory. */
|
|
Packit |
cdaae3 |
return NULL ;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
prime_to_use = primes[0];
|
|
Packit |
cdaae3 |
while(size_estimate && (size_estimate > prime_to_use)) {
|
|
Packit |
cdaae3 |
k = k +1;
|
|
Packit |
cdaae3 |
prime_to_use = primes[k];
|
|
Packit |
cdaae3 |
if(prime_to_use == 0) {
|
|
Packit |
cdaae3 |
/* Oops. Too large. */
|
|
Packit |
cdaae3 |
free(base);
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
entry_index = k;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
base->tablesize_ = prime_to_use;
|
|
Packit |
cdaae3 |
base->allowed_fill_ = calculate_allowed_fill(allowed_fill_percent,
|
|
Packit |
cdaae3 |
prime_to_use);
|
|
Packit |
cdaae3 |
if( base->allowed_fill_< (base->tablesize_/2)) {
|
|
Packit |
cdaae3 |
/* Oops. We are in trouble. Coding mistake here. */
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
base->record_count_ = 0;
|
|
Packit |
cdaae3 |
base->tablesize_entry_index_ = entry_index;
|
|
Packit |
cdaae3 |
/* hashtab_ is an array of hs_entry,
|
|
Packit |
cdaae3 |
indexes 0 through tablesize_ -1. */
|
|
Packit |
cdaae3 |
base->hashfunc_ = hashfunc;
|
|
Packit |
cdaae3 |
base->hashtab_ = calloc(sizeof(struct ts_entry),base->tablesize_);
|
|
Packit |
cdaae3 |
if(!base->hashtab_) {
|
|
Packit |
cdaae3 |
free(base);
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
*treeptr = base;
|
|
Packit |
cdaae3 |
return base;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
static void print_entry(struct ts_entry *t,const char *descr,
|
|
Packit |
cdaae3 |
char *(* keyprint)(const void *),
|
|
Packit |
cdaae3 |
unsigned long hashpos,
|
|
Packit |
cdaae3 |
unsigned long chainpos)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
char *v = 0;
|
|
Packit |
cdaae3 |
if(!t->entryused) {
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
v = keyprint(t->keyptr);
|
|
Packit |
cdaae3 |
printf(
|
|
Packit |
cdaae3 |
"[%4lu.%02lu] 0x%08lx <keyptr 0x%08lx> <key %s> %s\n",
|
|
Packit |
cdaae3 |
hashpos,chainpos,
|
|
Packit |
cdaae3 |
(unsigned long)t,
|
|
Packit |
cdaae3 |
(unsigned long)t->keyptr,
|
|
Packit |
cdaae3 |
v,
|
|
Packit |
cdaae3 |
descr);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* For debugging */
|
|
Packit |
cdaae3 |
static void
|
|
Packit |
cdaae3 |
dumptree_inner(const struct hs_base *h,
|
|
Packit |
cdaae3 |
char *(* keyprint)(const void *),
|
|
Packit |
cdaae3 |
const char *descr, int printdetails)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
unsigned long ix = 0;
|
|
Packit |
cdaae3 |
unsigned long tsize = h->tablesize_;
|
|
Packit |
cdaae3 |
struct ts_entry *p = &h->hashtab_[0];
|
|
Packit |
cdaae3 |
unsigned long hashused = 0;
|
|
Packit |
cdaae3 |
unsigned long maxchainlength = 0;
|
|
Packit |
cdaae3 |
unsigned long chainsgt1 = 0;
|
|
Packit |
cdaae3 |
printf("dumptree head ptr : 0x%08lx size %lu entries %lu allowed %lu %s\n",
|
|
Packit |
cdaae3 |
(unsigned long)h,
|
|
Packit |
cdaae3 |
(unsigned long)h->tablesize_,
|
|
Packit |
cdaae3 |
(unsigned long)h->record_count_,
|
|
Packit |
cdaae3 |
(unsigned long)h->allowed_fill_,
|
|
Packit |
cdaae3 |
descr);
|
|
Packit |
cdaae3 |
for( ; ix < tsize; ix++,p++) {
|
|
Packit |
cdaae3 |
unsigned long chainlength = 0;
|
|
Packit |
cdaae3 |
struct ts_entry*n = 0;
|
|
Packit |
cdaae3 |
int chainpos = 0;
|
|
Packit |
cdaae3 |
if(p->entryused) {
|
|
Packit |
cdaae3 |
++hashused;
|
|
Packit |
cdaae3 |
chainlength = 1;
|
|
Packit |
cdaae3 |
if(printdetails) {
|
|
Packit |
cdaae3 |
print_entry(p,"head",keyprint,ix,chainpos);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
chainpos++;
|
|
Packit |
cdaae3 |
for(n = p->next; n ; n = n->next) {
|
|
Packit |
cdaae3 |
chainlength++;
|
|
Packit |
cdaae3 |
if(printdetails) {
|
|
Packit |
cdaae3 |
print_entry(n,"chain",keyprint,ix,chainpos);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
if(chainlength > maxchainlength) {
|
|
Packit |
cdaae3 |
maxchainlength = chainlength;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
if (chainlength > 1) {
|
|
Packit |
cdaae3 |
chainsgt1++;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
printf("Hashtable: %lu of %lu hash entries used.\n",hashused,tsize);
|
|
Packit |
cdaae3 |
printf("Hashtable: %lu chains length longer than 1. \n",chainsgt1);
|
|
Packit |
cdaae3 |
printf("Hashtable: %lu is maximum chain length.\n",maxchainlength);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* Dumping the tree.
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
void
|
|
Packit |
cdaae3 |
dwarf_tdump(const void*headp_in,
|
|
Packit |
cdaae3 |
char *(* keyprint)(const void *),
|
|
Packit |
cdaae3 |
const char *msg)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
const struct hs_base *head = (const struct hs_base *)headp_in;
|
|
Packit |
cdaae3 |
if(!head) {
|
|
Packit |
cdaae3 |
printf("dumptree null tree ptr : %s\n",msg);
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
dumptree_inner(head,keyprint,msg,1);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
static struct ts_entry *
|
|
Packit |
cdaae3 |
allocate_ts_entry(const void *key)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct ts_entry *e = (struct ts_entry *)
|
|
Packit |
cdaae3 |
malloc(sizeof(struct ts_entry));
|
|
Packit |
cdaae3 |
if(!e) {
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
e->keyptr = key;
|
|
Packit |
cdaae3 |
e->entryused = 1;
|
|
Packit |
cdaae3 |
e->next = 0;
|
|
Packit |
cdaae3 |
return e;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
static void
|
|
Packit |
cdaae3 |
resize_table(struct hs_base *head,
|
|
Packit |
cdaae3 |
int (*compar)(const void *, const void *))
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct hs_base newhead;
|
|
Packit |
cdaae3 |
unsigned new_entry_index = 0;
|
|
Packit |
cdaae3 |
unsigned long prime_to_use = 0;
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* Copy the values we have. */
|
|
Packit |
cdaae3 |
newhead = *head;
|
|
Packit |
cdaae3 |
/* But drop the hashtab_ from new. calloc below. */
|
|
Packit |
cdaae3 |
newhead.hashtab_ = 0;
|
|
Packit |
cdaae3 |
newhead.record_count_ = 0;
|
|
Packit |
cdaae3 |
new_entry_index = head->tablesize_entry_index_ +1;
|
|
Packit |
cdaae3 |
prime_to_use = primes[new_entry_index];
|
|
Packit |
cdaae3 |
if(prime_to_use == 0) {
|
|
Packit |
cdaae3 |
/* Oops, too large. Leave table size as is, though
|
|
Packit |
cdaae3 |
it will get slow as it overfills. */
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
newhead.tablesize_ = prime_to_use;
|
|
Packit |
cdaae3 |
newhead.allowed_fill_ = calculate_allowed_fill(allowed_fill_percent,
|
|
Packit |
cdaae3 |
prime_to_use);
|
|
Packit |
cdaae3 |
if( newhead.allowed_fill_< (newhead.tablesize_/2)) {
|
|
Packit |
cdaae3 |
/* Oops. We are in trouble. */
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
newhead.tablesize_entry_index_ = new_entry_index;
|
|
Packit |
cdaae3 |
newhead.hashtab_ = calloc(sizeof(struct ts_entry),newhead.tablesize_);
|
|
Packit |
cdaae3 |
if(!newhead.hashtab_) {
|
|
Packit |
cdaae3 |
/* Oops, too large. Leave table size as is, though
|
|
Packit |
cdaae3 |
things will get slow as it overfills. */
|
|
Packit |
cdaae3 |
free(newhead.hashtab_);
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
/* Insert all the records from the old table into
|
|
Packit |
cdaae3 |
the new table. */
|
|
Packit |
cdaae3 |
int fillnewfail = 0;
|
|
Packit |
cdaae3 |
unsigned long ix = 0;
|
|
Packit |
cdaae3 |
unsigned long tsize = head->tablesize_;
|
|
Packit |
cdaae3 |
struct ts_entry *p = &head->hashtab_[0];
|
|
Packit |
cdaae3 |
for( ; ix < tsize; ix++,p++) {
|
|
Packit |
cdaae3 |
int inserted = 0;
|
|
Packit |
cdaae3 |
struct ts_entry*n = 0;
|
|
Packit |
cdaae3 |
if(fillnewfail) {
|
|
Packit |
cdaae3 |
break;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
if(p->keyptr) {
|
|
Packit |
cdaae3 |
tsearch_inner(p->keyptr,
|
|
Packit |
cdaae3 |
&newhead,compar,
|
|
Packit |
cdaae3 |
want_insert,
|
|
Packit |
cdaae3 |
&inserted,
|
|
Packit |
cdaae3 |
0);
|
|
Packit |
cdaae3 |
if(!inserted) {
|
|
Packit |
cdaae3 |
fillnewfail = 1;
|
|
Packit |
cdaae3 |
break;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
for(n = p->next; n ; n = n->next) {
|
|
Packit |
cdaae3 |
inserted = 0;
|
|
Packit |
cdaae3 |
tsearch_inner(n->keyptr,
|
|
Packit |
cdaae3 |
&newhead,compar,
|
|
Packit |
cdaae3 |
want_insert,
|
|
Packit |
cdaae3 |
&inserted,
|
|
Packit |
cdaae3 |
0);
|
|
Packit |
cdaae3 |
if(!inserted) {
|
|
Packit |
cdaae3 |
fillnewfail = 1;
|
|
Packit |
cdaae3 |
break;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
if(fillnewfail) {
|
|
Packit |
cdaae3 |
free(newhead.hashtab_);
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
/* Now get rid of the chain entries of the old table. */
|
|
Packit |
cdaae3 |
dwarf_tdestroy_inner(head,0,0);
|
|
Packit |
cdaae3 |
/* Now get rid of the old table itself. */
|
|
Packit |
cdaae3 |
free(head->hashtab_);
|
|
Packit |
cdaae3 |
head->hashtab_ = 0;
|
|
Packit |
cdaae3 |
*head = newhead;
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* Inner search of the hash and synonym chains.
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
static struct ts_entry *
|
|
Packit |
cdaae3 |
tsearch_inner( const void *key, struct hs_base* head,
|
|
Packit |
cdaae3 |
int (*compar)(const void *, const void *),
|
|
Packit |
cdaae3 |
const enum search_intent_t intent, int*inserted,
|
|
Packit |
cdaae3 |
/* owner_ptr used for delete. Only set
|
|
Packit |
cdaae3 |
if the to-be-deleted item is on a chain,
|
|
Packit |
cdaae3 |
not in the hashtab. Points to the item
|
|
Packit |
cdaae3 |
pointing to the to-be-deleted-item.*/
|
|
Packit |
cdaae3 |
struct ts_entry **owner_ptr)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct ts_entry *s =0;
|
|
Packit |
cdaae3 |
struct ts_entry *c =0;
|
|
Packit |
cdaae3 |
struct ts_entry *q =0;
|
|
Packit |
cdaae3 |
int kc = 0;
|
|
Packit |
cdaae3 |
DW_TSHASHTYPE keyhash = 0;
|
|
Packit |
cdaae3 |
DW_TSHASHTYPE hindx = 0;
|
|
Packit |
cdaae3 |
struct ts_entry *chain_parent = 0;
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
if(! head->hashfunc_) {
|
|
Packit |
cdaae3 |
/* Not fully initialized. */
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
keyhash = head->hashfunc_(key);
|
|
Packit |
cdaae3 |
if (intent == want_insert) {
|
|
Packit |
cdaae3 |
if( head->record_count_ > head->allowed_fill_) {
|
|
Packit |
cdaae3 |
resize_table(head,compar);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
hindx = keyhash%head->tablesize_;
|
|
Packit |
cdaae3 |
s = &head->hashtab_[hindx];
|
|
Packit |
cdaae3 |
if(!s->entryused) {
|
|
Packit |
cdaae3 |
/* Not found. */
|
|
Packit |
cdaae3 |
if(intent != want_insert) {
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
/* Insert in the base hash table in an
|
|
Packit |
cdaae3 |
empty slot. */
|
|
Packit |
cdaae3 |
*inserted = 1;
|
|
Packit |
cdaae3 |
head->record_count_++;
|
|
Packit |
cdaae3 |
s->keyptr = (const void *)key;
|
|
Packit |
cdaae3 |
s->entryused = 1;
|
|
Packit |
cdaae3 |
s->next = 0;
|
|
Packit |
cdaae3 |
return s;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
kc = compar(key,s->keyptr);
|
|
Packit |
cdaae3 |
if(kc == 0 ) {
|
|
Packit |
cdaae3 |
/* found! */
|
|
Packit |
cdaae3 |
if(want_delete) {
|
|
Packit |
cdaae3 |
*owner_ptr = 0;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
return (void *)&(s->keyptr);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
chain_parent = s;
|
|
Packit |
cdaae3 |
for(c = s->next; c; c = c->next) {
|
|
Packit |
cdaae3 |
kc = compar(key,c->keyptr);
|
|
Packit |
cdaae3 |
if(kc == 0 ) {
|
|
Packit |
cdaae3 |
/* found! */
|
|
Packit |
cdaae3 |
if(want_delete) {
|
|
Packit |
cdaae3 |
*owner_ptr = chain_parent;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
return (void *)&(c->keyptr);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
chain_parent = c;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
if(intent != want_insert) {
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
/* Insert following head record of the chain. */
|
|
Packit |
cdaae3 |
q = allocate_ts_entry(key);
|
|
Packit |
cdaae3 |
if (!q) {
|
|
Packit |
cdaae3 |
return q;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
q->next = s->next;
|
|
Packit |
cdaae3 |
s->next = q;
|
|
Packit |
cdaae3 |
head->record_count_++;
|
|
Packit |
cdaae3 |
*inserted = 1;
|
|
Packit |
cdaae3 |
return q;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
/* Search and, if missing, insert. */
|
|
Packit |
cdaae3 |
void *
|
|
Packit |
cdaae3 |
dwarf_tsearch(const void *key, void **headin,
|
|
Packit |
cdaae3 |
int (*compar)(const void *, const void *))
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct hs_base **rootp = (struct hs_base **)headin;
|
|
Packit |
cdaae3 |
struct hs_base *head = *rootp;
|
|
Packit |
cdaae3 |
struct ts_entry *r = 0;
|
|
Packit |
cdaae3 |
int inserted = 0;
|
|
Packit |
cdaae3 |
/* nullme won't be set. */
|
|
Packit |
cdaae3 |
struct ts_entry *nullme = 0;
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
if (!head) {
|
|
Packit |
cdaae3 |
/* something is wrong here, not initialized. */
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
r = tsearch_inner(key,head,compar,want_insert,&inserted,&nullme);
|
|
Packit |
cdaae3 |
if (!r) {
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
return (void *)&(r->keyptr);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* Search. */
|
|
Packit |
cdaae3 |
void *
|
|
Packit |
cdaae3 |
dwarf_tfind(const void *key, void *const *rootp,
|
|
Packit |
cdaae3 |
int (*compar)(const void *, const void *))
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
/* Nothing will change, but we discard const
|
|
Packit |
cdaae3 |
so we can use tsearch_inner(). */
|
|
Packit |
cdaae3 |
struct hs_base **proot = (struct hs_base **)rootp;
|
|
Packit |
cdaae3 |
struct hs_base *head = *proot;
|
|
Packit |
cdaae3 |
struct ts_entry *r = 0;
|
|
Packit |
cdaae3 |
/* inserted flag won't be set. */
|
|
Packit |
cdaae3 |
int inserted = 0;
|
|
Packit |
cdaae3 |
/* nullme won't be set. */
|
|
Packit |
cdaae3 |
struct ts_entry * nullme = 0;
|
|
Packit |
cdaae3 |
/* Get to actual tree. */
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
if (!head) {
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
r = tsearch_inner(key,head,compar,only_find,&inserted,&nullme);
|
|
Packit |
cdaae3 |
if(!r) {
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
return (void *)(&(r->keyptr));
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* Unlike the simple binary tree case,
|
|
Packit |
cdaae3 |
a fully-empty hash situation does not null the *rootp
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
void *
|
|
Packit |
cdaae3 |
dwarf_tdelete(const void *key, void **rootp,
|
|
Packit |
cdaae3 |
int (*compar)(const void *, const void *))
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct hs_base **proot = (struct hs_base **)rootp;
|
|
Packit |
cdaae3 |
struct hs_base *head = *proot;
|
|
Packit |
cdaae3 |
struct ts_entry *found = 0;
|
|
Packit |
cdaae3 |
/* inserted flag won't be set. */
|
|
Packit |
cdaae3 |
int inserted = 0;
|
|
Packit |
cdaae3 |
struct ts_entry * parentp = 0;
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
if (!head) {
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
found = tsearch_inner(key,head,compar,want_delete,&inserted,
|
|
Packit |
cdaae3 |
&parentp);
|
|
Packit |
cdaae3 |
if(found) {
|
|
Packit |
cdaae3 |
if(parentp) {
|
|
Packit |
cdaae3 |
/* Delete a chain entry. */
|
|
Packit |
cdaae3 |
head->record_count_--;
|
|
Packit |
cdaae3 |
parentp->next = found->next;
|
|
Packit |
cdaae3 |
/* We free our storage. It would be up
|
|
Packit |
cdaae3 |
to caller to do a tfind to find
|
|
Packit |
cdaae3 |
a record and delete content if necessary. */
|
|
Packit |
cdaae3 |
free(found);
|
|
Packit |
cdaae3 |
return (void *)&(parentp->keyptr);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
/* So found is the head of a chain. */
|
|
Packit |
cdaae3 |
if(found->next) {
|
|
Packit |
cdaae3 |
/* Delete a chain entry, pull up to hash tab, freeing
|
|
Packit |
cdaae3 |
up the chain entry. */
|
|
Packit |
cdaae3 |
struct ts_entry *pullup = found->next;
|
|
Packit |
cdaae3 |
*found = *pullup;
|
|
Packit |
cdaae3 |
free(pullup);
|
|
Packit |
cdaae3 |
head->record_count_--;
|
|
Packit |
cdaae3 |
return (void *)&(found->keyptr);
|
|
Packit |
cdaae3 |
} else {
|
|
Packit |
cdaae3 |
/* Delete a main hash table entry.
|
|
Packit |
cdaae3 |
Problem: what the heck to return as a keyptr pointer?
|
|
Packit |
cdaae3 |
Well, we return NULL. As in the standard
|
|
Packit |
cdaae3 |
tsearch, returning NULL does not mean
|
|
Packit |
cdaae3 |
failure! Here it just means 'empty chain somewhere'.
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
head->record_count_--;
|
|
Packit |
cdaae3 |
found->next = 0;
|
|
Packit |
cdaae3 |
found->keyptr = 0;
|
|
Packit |
cdaae3 |
found->entryused = 0;
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
return NULL;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
static void
|
|
Packit |
cdaae3 |
dwarf_twalk_inner(const struct hs_base *h,
|
|
Packit |
cdaae3 |
struct ts_entry *p,
|
|
Packit |
cdaae3 |
void (*action)(const void *nodep, const DW_VISIT which, const int depth),
|
|
Packit |
cdaae3 |
UNUSEDARG unsigned level)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
unsigned long ix = 0;
|
|
Packit |
cdaae3 |
unsigned long tsize = h->tablesize_;
|
|
Packit |
cdaae3 |
for( ; ix < tsize; ix++,p++) {
|
|
Packit |
cdaae3 |
struct ts_entry*n = 0;
|
|
Packit |
cdaae3 |
if(p->keyptr) {
|
|
Packit |
cdaae3 |
action((void *)(&(p->keyptr)),dwarf_leaf,level);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
for(n = p->next; n ; n = n->next) {
|
|
Packit |
cdaae3 |
action((void *)(&(n->keyptr)),dwarf_leaf,level);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
void
|
|
Packit |
cdaae3 |
dwarf_twalk(const void *rootp,
|
|
Packit |
cdaae3 |
void (*action)(const void *nodep, const DW_VISIT which, const int depth))
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
const struct hs_base *head = (const struct hs_base *)rootp;
|
|
Packit |
cdaae3 |
struct ts_entry *root = 0;
|
|
Packit |
cdaae3 |
if(!head) {
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
root = head->hashtab_;
|
|
Packit |
cdaae3 |
/* Get to actual tree. */
|
|
Packit |
cdaae3 |
dwarf_twalk_inner(head,root,action,0);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
static void
|
|
Packit |
cdaae3 |
dwarf_tdestroy_inner(struct hs_base*h,
|
|
Packit |
cdaae3 |
void (*free_node)(void *nodep),
|
|
Packit |
cdaae3 |
int depth)
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
unsigned long ix = 0;
|
|
Packit |
cdaae3 |
unsigned long tsize = h->tablesize_;
|
|
Packit |
cdaae3 |
struct ts_entry *p = &h->hashtab_[0];
|
|
Packit |
cdaae3 |
for( ; ix < tsize; ix++,p++) {
|
|
Packit |
cdaae3 |
struct ts_entry*n = 0;
|
|
Packit |
cdaae3 |
struct ts_entry*prev = 0;
|
|
Packit |
cdaae3 |
if(p->keyptr && p->entryused) {
|
|
Packit |
cdaae3 |
if(free_node) {
|
|
Packit |
cdaae3 |
free_node((void *)(p->keyptr));
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
--h->record_count_;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
/* Now walk and free up the chain entries. */
|
|
Packit |
cdaae3 |
for(n = p->next; n ; ) {
|
|
Packit |
cdaae3 |
if(free_node) {
|
|
Packit |
cdaae3 |
free_node((void *)(n->keyptr));
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
--h->record_count_;
|
|
Packit |
cdaae3 |
prev = n;
|
|
Packit |
cdaae3 |
n = n->next;
|
|
Packit |
cdaae3 |
free(prev);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
/* Walk the tree, freeing all space in the tree
|
|
Packit |
cdaae3 |
and calling the user's callback function on each node.
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
It is up to the caller to zero out anything pointing to
|
|
Packit |
cdaae3 |
head (ie, that has the value rootp holds) after this
|
|
Packit |
cdaae3 |
returns.
|
|
Packit |
cdaae3 |
*/
|
|
Packit |
cdaae3 |
void
|
|
Packit |
cdaae3 |
dwarf_tdestroy(void *rootp, void (*free_node)(void *nodep))
|
|
Packit |
cdaae3 |
{
|
|
Packit |
cdaae3 |
struct hs_base *head = (struct hs_base *)rootp;
|
|
Packit |
cdaae3 |
struct ts_entry *root = 0;
|
|
Packit |
cdaae3 |
if(!head) {
|
|
Packit |
cdaae3 |
return;
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
root = head->hashtab_;
|
|
Packit |
cdaae3 |
dwarf_tdestroy_inner(head,free_node,0);
|
|
Packit |
cdaae3 |
free(root);
|
|
Packit |
cdaae3 |
free(head);
|
|
Packit |
cdaae3 |
}
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
|
|
Packit |
cdaae3 |
|