/* 
 * Copyright (C) 2005 iptelorg GmbH
 *
 * This file is part of ser, a free SIP server.
 *
 * ser is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * For a license to use the ser software under conditions
 * other than those described here, or to purchase support for this
 * software, please contact iptel.org by e-mail at the following addresses:
 *    info@iptel.org
 *
 * ser is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <cds/hash_table.h>
#include <cds/memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int ht_init(hash_table_t *ht, hash_func_t hash_func, key_cmp_func_t cmp_keys, int size)
{
	if (!ht) return -1;
	if ((!hash_func) || (!cmp_keys)) return -1;

	ht->cslots = (ht_cslot_t*)cds_malloc(size * sizeof(ht_cslot_t));
	if (!ht->cslots) return -1;
	memset(ht->cslots, 0, size * sizeof(ht_cslot_t));

	ht->size = size;
	ht->hash = hash_func;
	ht->cmp = cmp_keys;

	ht->find_cnt = 0;
	ht->cmp_cnt = 0;
	ht->nocmp_cnt = 0;
	ht->missed_cnt = 0;
	return 0;
}

void ht_destroy(hash_table_t *ht)
{
	ht_element_t *e, *n;
	int i;
	
	if (!ht) return;
	if (ht->cslots) {
		for (i = 0; i < ht->size; i++) {
			e = ht->cslots[i].first;
			while (e) {
				n = e->next;
				cds_free(e);
				e = n;
			}
		}
		cds_free(ht->cslots);
	}
	ht->cslots = NULL;
}

int ht_add(hash_table_t *ht, ht_key_t key, ht_data_t data)
{
	int h;
	ht_element_t *new_e;
	
	if (!ht) return -1;
	new_e = (ht_element_t*)cds_malloc(sizeof(ht_element_t));
	if (!new_e) return -1;
	new_e->next = NULL;
	new_e->key = key;
	new_e->data = data;
	
	h = ht->hash(key) % ht->size;
	if (h < 0) h = -h;
	
	if (!ht->cslots[h].last) {
		ht->cslots[h].first = new_e;
	}
	else {
		ht->cslots[h].last->next = new_e;
	}

	ht->cslots[h].cnt++;
	ht->cslots[h].last = new_e;
	return 0;
}

ht_data_t ht_find(hash_table_t *ht, ht_key_t key)
{
	int h;
	ht_element_t *e;

	if (!ht) return NULL;
	
	ht->find_cnt++;	//monitor
	
	h = ht->hash(key) % ht->size;
	if (h < 0) h = -h;
	e = ht->cslots[h].first;
	if (!e) ht->nocmp_cnt++;	//monitor
	while (e) {
		ht->cmp_cnt++;	//monitor
		if (ht->cmp(e->key, key) == 0) return e->data;
		e = e->next;
	}
	
	ht->missed_cnt++;	//monitor
	return NULL;
}

ht_data_t ht_remove(hash_table_t *ht, ht_key_t key)
{
	int h;
	ht_element_t *e,*p;
	ht_data_t data;
	
	if (!ht) return NULL;
	h = ht->hash(key) % ht->size;
	if (h < 0) h = -h;
	e = ht->cslots[h].first;
	p = NULL;
	while (e) {
		if (ht->cmp(e->key, key) == 0) {
			if (p) p->next = e->next;
			else ht->cslots[h].first = e->next;
			ht->cslots[h].cnt--;
			if (!e->next) ht->cslots[h].last = p;
			data = e->data;
			cds_free(e);
			return data;
		}
		p = e;
		e = e->next;
	}
	return NULL;
}

void ht_get_statistic(hash_table_t *ht, ht_statistic_t *s)
{
	if (!s) return;
	if (!ht) {
		s->find_cnt = 0;
		s->cmp_cnt = 0;
		s->nocmp_cnt = 0;
		s->missed_cnt = 0;
	}
	else {
		s->find_cnt = ht->find_cnt;
		s->cmp_cnt = ht->cmp_cnt;
		s->nocmp_cnt = ht->nocmp_cnt;
		s->missed_cnt = ht->missed_cnt;
	}
}

void ht_clear_statistic(hash_table_t *ht)
{
	if (!ht) return;
	
	ht->find_cnt = 0;
	ht->cmp_cnt = 0;
	ht->nocmp_cnt = 0;
	ht->missed_cnt = 0;
}

/* --------- hash table traversing functions -------- */

ht_element_t *get_first_ht_element(hash_table_t *ht, ht_traversal_info_t *info)
{
	int i;
	if (!info) return NULL;
	info->ht = ht;
	info->current = NULL;
	for (i = 0; i < ht->size; i++) {
		if (ht->cslots[i].first) {
			info->current = ht->cslots[i].first;
			break;
		}
	}
	info->slot_pos = i;
	return info->current;
}

ht_element_t *get_next_ht_element(ht_traversal_info_t *info)
{
	int i;
	if (!info) return NULL;

	if (info->current) info->current = info->current->next;
	
	if (info->current) return info->current;
	else {
		for (i = info->slot_pos + 1; i < info->ht->size; i++) {
			if (info->ht->cslots[i].first) {
				info->current = info->ht->cslots[i].first;
				break;
			}
		}
		info->slot_pos = i;
	}
	return info->current;
}

/* --------- HASH functions -------- */

unsigned int rshash(const char* str, unsigned int len)
{
	unsigned int b = 378551;
	unsigned int a = 63689;
	unsigned int hash = 0;
	unsigned int i = 0;

	for(i = 0; i < len; str++, i++) {
		hash = hash * a + (*str);
		a = a * b;
	}

	return (hash & 0x7FFFFFFF);
}