Blame tsearch/dwarf_tsearchhash.c

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