/*
 * FILE:    asarray.c
 *
 * AUTHORS: Orion Hodson
 *
 * Copyright (c) 1999-2000 University College London
 * All rights reserved.
 */
 
#ifndef HIDE_SOURCE_STRINGS
static const char cvsid[] = 
	"$Id: asarray.c,v 1.1 2000/02/11 18:34:15 ucacoxh Exp $";
#endif /* HIDE_SOURCE_STRINGS */

#include "config_unix.h"
#include "config_win32.h"

#include "debug.h"
#include "memory.h"
#include "util.h"

#include "asarray.h"

typedef struct s_hash_tuple {
        uint32_t hash;
        char *key;
        char *value;
        struct s_hash_tuple *next;
} hash_tuple;

#define ASARRAY_SIZE 11

struct _asarray {
        hash_tuple *table[ASARRAY_SIZE];
        int32_t     nitems[ASARRAY_SIZE];
};

static uint32_t 
asarray_hash(const char *key)
{
        uint32_t hash = 0;

        while(*key != '\0') {
                hash = hash * 31;
                hash += ((uint32_t)*key) + 1;
                key++;
        }

        return hash;
}

int32_t
asarray_add(asarray *pa, const char *key, const char *value)
{
        hash_tuple *t;
        int row;

        t = (hash_tuple*)xmalloc(sizeof(hash_tuple));
        if (t) {
                /* transfer values */
                t->hash  = asarray_hash(key);
                t->key   = xstrdup(key);
                t->value = xstrdup(value);
                /* Add to table */
                row            = t->hash % ASARRAY_SIZE;
                t->next        = pa->table[row];
                pa->table[row] = t;
                pa->nitems[row]++;
                return TRUE;
        }
        return FALSE;
}

void
asarray_remove(asarray *pa, const char *key)
{
        hash_tuple **t, *e;
        uint32_t hash;
        int row;

        hash = asarray_hash(key);
        row  = hash % ASARRAY_SIZE;
        t    = &pa->table[row];
        while((*t) != NULL) {
                if ((hash == (*t)->hash) && 
                    (strcmp(key, (*t)->key) == 0)) {
                        e = *t;
                        *t = e->next;
                        xfree(e->key);
                        xfree(e->value);
                        xfree(e);
                        pa->nitems[row]--;
                        assert(pa->nitems[row] >= 0);
                        break;
                } else {
                        t = &(*t)->next;
                }
        }
}

const char* 
asarray_get_key_no(asarray *pa, int32_t index)
{
        int32_t row = 0;

        index += 1;
        while (row < ASARRAY_SIZE && index > pa->nitems[row]) {
                index -= pa->nitems[row];
                row++;
        }

        if (row < ASARRAY_SIZE) {
                hash_tuple *t;
                t = pa->table[row];
                while(--index > 0) {
                        assert(t->next != NULL);
                        t = t->next;
                }
                return t->key;
        }
        return NULL;
}

/* asarray_lookup points value at actual value        */
/* and return TRUE if key found.                      */
int32_t
asarray_lookup(asarray *pa, const char *key, char **value)
{
        hash_tuple *t;
        int          row;
        uint32_t     hash;

        hash = asarray_hash(key);
        row  = hash % ASARRAY_SIZE;

        t = pa->table[row];
        while(t != NULL) {
                if (t->hash == hash && strcmp(key, t->key) == 0) {
                        *value = t->value;
                        return TRUE;
                }
                t = t->next;
        }
        *value = NULL;
        return FALSE;
}

int32_t
asarray_create(asarray **ppa)
{
        asarray *pa;
        pa = (asarray*)xmalloc(sizeof(asarray));
        if (pa != NULL) {
                memset(pa, 0, sizeof(asarray));
                *ppa = pa;
                return TRUE;
        }
        return FALSE;
}

void
asarray_destroy(asarray **ppa)
{
        asarray    *pa;
        const char *key;

        pa = *ppa;
        assert(pa != NULL);

        while ((key = asarray_get_key_no(pa, 0)) != NULL) {
                asarray_remove(pa, key);
        }

        xfree(pa);
        *ppa = NULL;
        xmemchk();
}
