/*
 * $Header: /san01/cvs/ashpool/csrc/continuity/mecha/Attic/hsh.c,v 1.24 2004/05/27 14:31:42 aleigh Exp $ 
 */

/*
 * Copyright (c) 1994, 2004 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Use, disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

/*
 * TA/hashlist general purpose hashing algorithm system. Alex Leigh,
 * 1994,1997,1999, 2000 $Id: hsh.c,v 1.24 2004/05/27 14:31:42 aleigh Exp $
 */

/*
 * hsh provides a general purpose, efficient data storage and retrieval
 * system. The algorithm can store an arbitrary list of sequential
 * name/keyvalue pairs, each list being keyed by a character string.
 * 
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>

#include "mecha.h"

/* double-check to make sure hashing is working properly */
#undef VALIDATING_HASH

#define DEFAULT_HSH_SIZE 16

/*
 * the maximum number of elements in the hashtable that are allowed for the
 * hashtable to use a linear search to find.
 */
#define LINEAR_SEARCH_MAX_COUNT  0

unsigned int hshGrand8[256] =
    { 16838, 5758, 10113, 17515, 31051, 5627, 23010,
    7419, 16212, 4086, 2749, 12767, 9084, 12060,
    32225, 17543, 25089, 21183, 25137, 25566, 26966, 4978,
    20495, 10311, 11367, 30054, 17031, 13145, 19882, 25736,
    30524, 28505, 28394, 22102, 24851, 19067, 12754, 11653,
    6561, 27096, 13628, 15188, 32085, 4143, 6967, 31406,
    24165, 13403, 25562, 24834, 31353, 920, 10444, 24803,
    7962, 19318, 1422, 31327, 10457, 1945, 14479, 29983,
    18751, 3894, 18670, 8259, 16248, 7757, 15629, 13306,
    28606, 13990, 11738, 12516, 1414, 5262, 17116, 22825,
    3181, 13134, 25343, 8022, 11233, 7536, 9760, 9979,
    29071, 1201, 21336, 13061, 22160, 24005, 30729, 7644,
    27475, 31693, 25514, 14139, 22088, 26521, 5202, 9171, 4434,
    28317, 24582, 6815, 4586, 9653, 26306, 7174, 18451, 23448,
    6473, 32434, 8193, 14110, 24748, 28210, 29320, 32049,
    12956, 14162, 4166, 14997, 7793, 32310, 21391, 19799,
    7926, 14905, 25885, 2582, 15610, 5000, 8052, 30965, 20120,
    32380, 15639, 26204, 24385, 12475, 15725, 17265, 3214,
    19471, 11376, 4697, 25543, 23297, 14619, 23087, 3123,
    31549, 18065, 24256, 18973, 20901, 25613, 6157, 9899,
    9267, 22413, 9598, 18526, 13711, 10046, 14566, 18536,
    15988, 19878, 13626, 4273, 8387, 1171, 32017, 3752, 12388,
    21191, 11483, 18122, 11744, 18528, 15585, 5363, 20159,
    5641, 18176, 9575, 28578, 27363, 27685, 29344, 19489,
    17713, 5511, 21461, 22626, 8645, 3496, 26703, 6270, 13870,
    11529, 27499, 4500, 8607, 5808, 15725, 12457, 16542, 16474,
    11531, 17222, 3952, 17024, 19894, 24015, 18247, 11276,
    26278, 19365, 8746, 21976, 18092, 25851, 29088, 29163,
    2231, 26233, 29732, 21106, 5411, 9874, 5448, 9344, 27589,
    17574, 1191, 6789, 695, 11735, 20364, 17040, 17892, 5035,
    26979, 1092, 850, 12390, 20195, 668, 20531, 29989, 12281, 23902
};

int hshGprimes[] = { 11, 19, 37, 73, 109, 163, 251, 367, 557,
    823, 1237, 1861, 2777, 4177, 6247, 9371, 14057, 21089,
    31627, 47431, 71143, 106721, 160073, 240101, 360163,
    540217, 810343, 1215497, 1823231, 2734867, 4102283,
    6153409, 9230113, 13845163, -1
};

struct hshSvoid_node {
    char *name;
    unsigned short int hashcode;

    size_t name_len;
    void *data;
    struct hshSvoid_node *next;
};
typedef struct hshSvoid_node hshTvoid_node;

struct hshSvoid_list {
    struct hshSvoid_node **nodes;
    void (*destructor) (void *);
    void (*name_destructor) (void *);
    int (*name_comparator) (const void *s1, const void *s2, size_t len);
    unsigned int serial;

    unsigned int size;
    unsigned int count;
    float load_factor;
    unsigned int collisions;
    unsigned int threshold;
};

struct hshSiterator {
    unsigned int serial;
    unsigned int count;
    struct hshSvoid_list *list;
    hshTvoid_node *node;
};

static int hshFvoid_next_size(int size);
static void hshFvoid_resize(hshTvoid_list * list);
static void hshFvoid_adjust_threshold(hshTvoid_list * list);
static hshTvoid_node *_hshFvoid_raw_find(hshTvoid_list * list,
                                         const char *name,
                                         size_t name_len);
static void _hshFvoid_node_free(hshTvoid_list * list, hshTvoid_node * node,
                                int free_value);
static void *_hshFvoid_raw_remove(hshTvoid_list * list, const char *name,
                                  size_t name_len, int free_value);

void hshFvalue_destructor_set(hshTvoid_list *list, void (*destructor)(void *)) {
    assert(list!=NULL);

    list->destructor=destructor;
}

/*
 * aleigh- calculate the hash value for a given string. This hash value is
 * based on the random set. Assuming the set is sufficiently random, the data
 * should distribute in a random way throughout the hash-space.
 */
unsigned short int hshFraw_hash(const char *str, size_t len,
                                unsigned int buckets)
{
    return (hshFraw_hashcode(str, len) % buckets);
}

unsigned short int hshFraw_hashcode(const char *str, size_t len)
{
    unsigned short int h;
    unsigned char h1, h2;
    size_t pos = 0;

    assert(str != NULL);

    h1 = *str;
    h2 = *(str + 1);
    str++;
    pos++;

    if (pos != len)
        while (1) {
            h1 = hshGrand8[h1 ^ *str];
            h2 = hshGrand8[h2 ^ *str];
            str++;
            pos++;
            if (pos == len)
                break;
        }

    h = ((unsigned short int) h1 << 8) | (unsigned short int) h2;
    return (h);
}

unsigned short int hshFhash(const char *str)
{
    assert(str != NULL);
    return hshFraw_hash(str, strlen(str), DEFAULT_HSH_SIZE);
}

/**********************************************************************/

/*
 * VOID hash list implementation This is the currently recommended
 * implementation, because it uses less space in memory and is slightly
 * faster.
 */

hshTvoid_list *hshFvoid_init(void (*destructor) (void *))
{
    return hshFvoid_raw_init(destructor, free, memcmp, DEFAULT_HSH_SIZE);
}

hshTvoid_list *hshFvoid_int_init(void (*destructor) (void *))
{
    return hshFvoid_raw_init(destructor, free, memcmp, DEFAULT_HSH_SIZE);
}

hshTvoid_list *hshFvoid_raw_init(void (*destructor) (void *),
                                 void (*name_destructor) (void *),
                                 int (name_comparator) (const void *s1,
                                                        const void *s2,
                                                        size_t len),
                                 unsigned int buckets)
{
    hshTvoid_list *list;

    list = (hshTvoid_list *) malloc(sizeof(hshTvoid_list));
    memFclear(list, sizeof(hshTvoid_list));
    list->destructor = destructor;
    list->name_destructor = name_destructor;
    list->name_comparator = name_comparator;
    list->load_factor = 0.75;
    list->count = 0;

    list->nodes =
        (hshTvoid_node **) malloc(sizeof(hshTvoid_node) * buckets);
    memFclear(list->nodes, sizeof(hshTvoid_node) * buckets);
    list->size = buckets;

    hshFvoid_adjust_threshold(list);

    return list;
}

static int hshFvoid_next_size(int size)
{
    int i;

    size *= 2;

    for (i = 0; hshGprimes[i] != -1; i++) {
        if (size <= hshGprimes[i]) {
            return (hshGprimes[i]);
        }
    }

    /*
     * if we don't have a prime number big enough, just double the size and
     * go with that -- we should fix this.
     */
    return (size);
}

static void hshFvoid_resize(hshTvoid_list * list)
{
    int new_size = hshFvoid_next_size(list->size);
    int collisions = 0;
    size_t i;
    struct hshSvoid_node **nodes;

    nodes = (hshTvoid_node **) calloc(new_size, sizeof(hshTvoid_node));

    for (i = 0; i < list->size; i++) {
        hshTvoid_node *node = list->nodes[i];

        while (node != NULL) {
            hshTvoid_node *next_node = node->next;
            int hash = node->hashcode % new_size;

            node->next = NULL;

            if (nodes[hash] == NULL) {
                nodes[hash] = node;
            } else {
                node->next = nodes[hash];
                nodes[hash] = node;

                collisions++;
            }

            node = next_node;
        }
    }

    free(list->nodes);

    list->nodes = nodes;
    list->size = new_size;
    list->collisions = collisions;
    list->serial++;

    hshFvoid_adjust_threshold(list);
}

static void hshFvoid_adjust_threshold(hshTvoid_list * list)
{
    list->threshold = (int) (((float) list->size) * list->load_factor);

    if (list->threshold >= list->size) {
        list->threshold = list->size - 1;
    }
}

int hshFvoid_size(hshTvoid_list * list)
{
    return (list->count);
}

/* Deprecated */
void hshFvoid_int_replace(hshTvoid_list *list, const int key, void *data) {
    hshFvoid_int_update(list,key,data);
}

void hshFvoid_int_update(hshTvoid_list * list, const int key, void *data)
{
    char *p = malloc(sizeof(int));

    memcpy(p, &key, sizeof(int));
    hshFvoid_raw_add(list, p, sizeof(int), data, 1);
}

int hshFvoid_int_add(hshTvoid_list * list, const int key, void *data)
{
    char *p = malloc(sizeof(int));

    memcpy(p, &key, sizeof(int));
    return (hshFvoid_raw_add(list, p, sizeof(int), data, 0));
}

/* Deprecated */
void hshFvoid_replace(hshTvoid_list * list, const char *name, void *data) {
    hshFvoid_update(list,name,data);
}

void hshFvoid_update(hshTvoid_list * list, const char *name, void *data)
{
    char *p = strdup(name);

    hshFvoid_raw_add(list, p, strlen(name), data, 1);
}

int hshFvoid_add(hshTvoid_list * list, const char *name, void *data)
{
    char *p = strdup(name);

    return (hshFvoid_raw_add(list, p, strlen(name), data, 0));
}

/**
 * Returns 1 if the value was added, otherwise, 0.
 */
int hshFvoid_raw_add(hshTvoid_list * list, char *name, size_t name_len, void *data, int replace)
{
    unsigned short int hash, hashcode;
    hshTvoid_node *node;

    if (list->count >= list->threshold) {
        hshFvoid_resize(list);
    }
    hashcode = hshFraw_hashcode(name, name_len);

    hash = hashcode % list->size;

    if (list->nodes[hash] != NULL) {
        hshTvoid_node *curNode, *prevNode;

        for (curNode = prevNode = list->nodes[hash]; curNode != NULL;
             prevNode = curNode, curNode = curNode->next) {
            if (curNode->name_len == name_len &&
                list->name_comparator(curNode->name, name,
                                      name_len) == 0) {
                /*
                 * if we aren't looking to replace the node, if the node
                 * already exists, we just return that we didn't add anything
                 */
                if (replace == 0) {
                    return (0);
                } else {
                    /*
                     * if we're supposed to replace the node, we have to
                     * delete the node if it exists
                     */
                    if (list->nodes[hash] == curNode) {
                        list->nodes[hash] = curNode->next;
                    } else {
                        prevNode->next = curNode->next;
                    }

                    _hshFvoid_node_free(list, curNode, 1);
                }

                break;
            }
        }
    }
    node = (hshTvoid_node *) malloc(sizeof(hshTvoid_node));
    node->hashcode = hashcode;
    node->data = data;
    node->name = name;
    node->name_len = name_len;
    node->next = NULL;

    if (list->nodes[hash] == NULL) {
        list->nodes[hash] = node;
    } else {
        node->next = list->nodes[hash];
        list->nodes[hash] = node;
        list->collisions++;
    }

    list->serial++;
    list->count++;



    return (1);
}

static hshTvoid_node *_hshFvoid_raw_find_hash(hshTvoid_list * list,
                                              const char *name,
                                              size_t name_len)
{
    unsigned short int hash;
    hshTvoid_node *node;

    assert(name != NULL);

    hash = hshFraw_hash(name, name_len, list->size);

    if (list->nodes[hash] == NULL)
        return NULL;

    for (node = list->nodes[hash]; node != NULL; node = node->next) {
        if (node->name_len == name_len) {
            if (list->name_comparator(node->name, name, name_len) == 0)
                return node;
        }
    }

    return NULL;
}


static hshTvoid_node *_hshFvoid_raw_find_linear(hshTvoid_list * list,
                                                const char *name,
                                                size_t name_len)
{
    size_t i;
    hshTvoid_node *node;

    assert(name != NULL);

    for (i = 0; i < list->size; i++) {
        for (node = list->nodes[i]; node != NULL; node = node->next) {
            if (node->name_len == name_len) {
                if (list->name_comparator(node->name, name, name_len) == 0)
                    return node;
            }
        }
    }

    return NULL;
}

static hshTvoid_node *_hshFvoid_raw_find(hshTvoid_list * list,
                                         const char *name, size_t name_len)
{
    hshTvoid_node *node;

    assert(name != NULL);

    if (list->count == 0) {
        return (NULL);
    }
#ifdef VALIDATING_HASH
    {
        hshTvoid_node *linear_node, *hash_node;

        linear_node = _hshFvoid_raw_find_linear(list, name, name_len);
        hash_node = _hshFvoid_raw_find_hash(list, name, name_len);


        assert(linear_node == hash_node);

        node = hash_node;
    }
#else
    if (list->count <= LINEAR_SEARCH_MAX_COUNT) {
        node = _hshFvoid_raw_find_linear(list, name, name_len);
    } else {
        node = _hshFvoid_raw_find_hash(list, name, name_len);
    }
#endif

    return (node);
}

void *hshFvoid_find(hshTvoid_list * list, const char *name)
{
    return hshFvoid_raw_find(list, name, strlen(name));
}

void *hshFvoid_int_find(hshTvoid_list * list, int key)
{
    return hshFvoid_raw_find(list, (char *) &key, sizeof(int));
}

void *hshFvoid_raw_find(hshTvoid_list * list, const char *name,
                        size_t name_len)
{
    hshTvoid_node *node;

    assert(list != NULL);
    assert(name != NULL);

    node = _hshFvoid_raw_find(list, name, name_len);
    if (node == NULL)
        return NULL;
    return node->data;
}

int hshFvoid_contains(hshTvoid_list * list, const char *key)
{
    hshTvoid_node *node = _hshFvoid_raw_find(list, key, strlen(key));

    if (node != NULL) {
        return (1);
    } else {
        return (0);
    }
}

int hshFvoid_int_contains(hshTvoid_list * list, int key)
{
    hshTvoid_node *node =
        _hshFvoid_raw_find(list, (char *) &key, sizeof(int));

    if (node != NULL) {
        return (1);
    } else {
        return (0);
    }
}

static void _hshFvoid_node_free(hshTvoid_list * list, hshTvoid_node * node,
                                int free_data)
{
    assert(node->name != NULL);
    assert(node->data != NULL);

    if (free_data && list->destructor != NULL && node->data != NULL) {
        list->destructor(node->data);
    }
    if (list->name_destructor != NULL && node->name != NULL) {
        list->name_destructor(node->name);
    }
    free(node);
}

void hshFvoid_del(hshTvoid_list * list, const char *name)
{
    hshFvoid_raw_del(list, name, strlen(name));
}

void hshFvoid_int_del(hshTvoid_list * list, int key)
{
    hshFvoid_raw_del(list, (char *) &key, sizeof(int));
}

void hshFvoid_raw_del(hshTvoid_list * list, const char *name,
                      size_t name_len)
{
    _hshFvoid_raw_remove(list, name, name_len, 1);
}

void *hshFvoid_remove(hshTvoid_list * list, const char *name)
{
    return (hshFvoid_raw_remove(list, name, strlen(name)));
}

void *hshFvoid_int_remove(hshTvoid_list * list, int key)
{
    return (hshFvoid_raw_remove(list, (char *) &key, sizeof(int)));
}

void *hshFvoid_raw_remove(hshTvoid_list * list, const char *name,
                          size_t name_len)
{
    return (_hshFvoid_raw_remove(list, name, name_len, 0));
}

static void *_hshFvoid_raw_remove(hshTvoid_list * list, const char *name,
                                  size_t name_len, int free_value)
{
    hshTvoid_node *p, *last;
    unsigned short int hash;
    void *data = NULL;

    assert(list != NULL);
    assert(name != NULL);

    hash = hshFraw_hash(name, name_len, list->size);

    list->serial++;

    if (list->nodes[hash] == NULL)
        return (NULL);

    for (p = last = list->nodes[hash]; p != NULL; last = p, p = p->next) {
        if (p->name_len == name_len &&
            list->name_comparator(p->name, name, name_len) == 0) {
            if (list->nodes[hash] == p) {
                /* First entry */
                list->nodes[hash] = p->next;
                break;
            } else {
                last->next = p->next;
                break;
            }
        }
    }

    if (p != NULL) {
        if (free_value) {
            data = NULL;
        } else {
            data = p->data;
        }

        _hshFvoid_node_free(list, p, free_value);

        list->count--;
    }
    return (data);
}

void hshFvoid_free(hshTvoid_list *list) {
    hshFvoid_destroy(list);
}

/* DEPRECATED */
void hshFvoid_destroy(hshTvoid_list * list)
{
    size_t i = 0;

    for (i = 0; i < list->size; i++) {
        hshTvoid_node *node = list->nodes[i];

        while (node != NULL) {
            hshTvoid_node *next = node->next;

            _hshFvoid_node_free(list, node, 1);

            node = next;
        }
    }

    free(list->nodes);

    free(list);
}


hshTiterator *hshFiterator_init(hshTvoid_list * list)
{
    hshTiterator *it;

    assert(list != NULL);

    it = (hshTiterator *) malloc(sizeof(hshTiterator));

    it->list = list;
    it->count = 0;
    it->serial = list->serial;
    it->node = NULL;

    return it;

}

int hshFiterator_next(hshTiterator * iterator)
{
    assert(iterator != NULL);

    if (iterator->serial != iterator->list->serial)
        return -2;

    if (iterator->node != NULL) {
        if (iterator->node->next != NULL) {
            iterator->node = iterator->node->next;
            return 1;
        } else {
            iterator->count++;
            iterator->node = NULL;
        }
    }
    for (; iterator->count < iterator->list->size; iterator->count++) {
        iterator->node = iterator->list->nodes[iterator->count];
        if (iterator->node == NULL)
            continue;
        return 1;
    }

    return 0;
}

void *hshFiterator_current_key(hshTiterator * iterator)
{
    assert(iterator != NULL);
    assert(iterator->node != NULL);

    return iterator->node->name;
}

void *hshFiterator_current_value(hshTiterator * iterator)
{
    assert(iterator != NULL);
    assert(iterator->node->data != NULL);

    return iterator->node->data;
}


void hshFiterator_free(hshTiterator * iterator)
{
    assert(iterator != NULL);

    free(iterator);
}

void hshFiterator_reset(hshTiterator * iterator)
{
    iterator->count = 0;
    iterator->serial = iterator->list->serial;
    iterator->node = NULL;
}

dynTstring *hshFdiag_output(hshTvoid_list * list)
{
    dynTstring *string = dynFinit();

    dynFappend_print(string, "hash diag %p:\n", list);

    dynFappend_print(string, "  serial: %d size: %d count: %d\n",
                     list->serial, list->size, list->count);

    dynFappend_print(string,
                     "  load_factor: %.2f collisions: %d threshold: %d\n",
                     list->load_factor, list->collisions, list->threshold);

    dynFsappend(string, "   elems:\n");

    {
        unsigned int i;

        for (i = 0; i < list->size; i++) {
            hshTvoid_node *node;

            for (node = list->nodes[i]; node != NULL; node = node->next) {
                dynFappend_print(string, "    %d] %s: hashcode: %d\n",
                                 i, node->name, node->hashcode);
            }
        }
    }

    return (string);
}

/*
 * 32 bit magic FNV-1a prime
 */
#define FNV_32_PRIME ((uint32_t)0x01000193)
#define FNV1_32_INIT ((uint32_t)0x811c9dc5)
#define FNV1_32A_INIT FNV1_32_INIT

uint32_t hshFraw_hash32(const void *buf, size_t len, uint32_t buckets)
{
    return hshFraw_hashcode32(buf, len) % buckets;
}

/*
 * fnv_32a_buf - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a buffer
 * 
 * input: buf	- start of buffer to hash len	- length of buffer in octets
 * hval	- previous hash value or 0 if first call
 * 
 * returns: 32 bit hash as a static hash type
 * 
 * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the
 * hval arg on the first call to either fnv_32a_buf() or fnv_32a_str().
 */
uint32_t hshFraw_hashcode32(const void *buf, size_t len)
{
    uint32_t hval = FNV1_32A_INIT;
    unsigned char *bp = (unsigned char *) buf;  /* start of buffer */
    unsigned char *be = bp + len;       /* beyond end of buffer */

    /*
     * FNV-1a hash each octet in the buffer
     */
    while (bp < be) {

        /* xor the bottom with the current octet */
        hval ^= (uint32_t) * bp++;

        /* multiply by the 32 bit FNV magic prime mod 2^32 */
#if defined(NO_FNV_GCC_OPTIMIZATION)
        hval *= FNV_32_PRIME;
#else
        hval +=
            (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) +
            (hval << 24);
#endif
    }

    /* return our new hash value */
    return hval;
}


/*
 * fnv_32a_str - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a string
 * 
 * input: str	- string to hash hval	- previous hash value or 0 if first
 * call
 * 
 * returns: 32 bit hash as a static hash type
 * 
 * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the
 * hval arg on the first call to either fnv_32a_buf() or fnv_32a_str().
 */
uint32_t hshFhash32(const char *str)
{
    uint32_t hval = FNV1_32A_INIT;
    unsigned char *s = (unsigned char *) str;   /* unsigned string */

    /*
     * FNV-1a hash each octet in the buffer
     */
    while (*s) {

        /* xor the bottom with the current octet */
        hval ^= (uint32_t) * s++;

        /* multiply by the 32 bit FNV magic prime mod 2^32 */
#if defined(NO_FNV_GCC_OPTIMIZATION)
        hval *= FNV_32_PRIME;
#else
        hval +=
            (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) +
            (hval << 24);
#endif
    }

    /* return our new hash value */
    return hval;
}
