#include <linux/init.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include "hashtab.h"

int hashtab_init (struct hashtab * hashtab, unsigned long size)
{
	int ret = -ENOMEM;
	unsigned long table_size = size;
#define	BUCKET_RATIO	1
	unsigned long bucket_size = size * BUCKET_RATIO;

	memset(hashtab, 0, sizeof(*hashtab));

	if (!(hashtab->table = vmalloc(table_size * sizeof(unsigned long))))
		return ret;
	memset(hashtab->table, 0, table_size * sizeof(unsigned long));
	hashtab->table_size = table_size;

	if (!(hashtab->bucket =
			vmalloc(bucket_size * sizeof(struct bucket)))) {
		goto fail;
	}

	memset(hashtab->bucket, 0, bucket_size * sizeof(struct bucket));
	hashtab->bucket_size = bucket_size;
	hashtab->bucket_head = 1;	/* 0 was reserved by NULL bucket */

	return 0;
fail:
	vfree(hashtab->table);
	memset(hashtab, 0, sizeof(*hashtab));

	return ret;
}

void hashtab_destroy (struct hashtab * hashtab)
{
	if (hashtab->table)
		vfree(hashtab->table);
	if (hashtab->bucket) {
#ifdef DEBUG
		if ((hashtab_null_bucket(hashtab))->next != 0 ||
			(hashtab_null_bucket(hashtab)->value != 0)) {
			printk("kmemprof: NULL bucket was written\n");
		}
#endif
		vfree(hashtab->bucket);
	}
	memset(hashtab, 0, sizeof(*hashtab));
}

static unsigned long alloc_bucket(struct hashtab * hashtab)
{
	if (hashtab->bucket_size <= hashtab->bucket_head + 1)
		return 0;	/* no more buckets */

	return hashtab->bucket_head++;
}

extern unsigned long hashtab_get_value(struct hashtab * hashtab,
						struct bucket * bucket)
{
#ifdef DEBUG
	BUG_ON(bucket == hashtab_null_bucket(hashtab));
#endif
	return bucket->value;
}
extern void hashtab_set_value(struct hashtab * hashtab,
			struct bucket * bucket, unsigned long value)
{
#ifdef DEBUG
	BUG_ON(bucket == hashtab_null_bucket(hashtab));
#endif
	bucket->value = value;
}

/* if there are not any more buckets, returns NULL bucket */
struct bucket * hashtab_chain(struct hashtab * hashtab, unsigned long h)
{
	unsigned long * mark;
	struct bucket *bucket;

#ifdef DEBUG
	BUG_ON(h >= hashtab->table_size);
#endif
	mark = &hashtab->table[h];
	for (bucket = hashtab_entry(hashtab, h);
		bucket != hashtab_null_bucket(hashtab);
		bucket = hashtab_next(hashtab, bucket)) {
		mark = &bucket->next;
	}
	*mark = alloc_bucket(hashtab);

	return &hashtab->bucket[*mark];
}

struct bucket * hashtab_entry(struct hashtab * hashtab, unsigned long h)
{
	unsigned long index;

#ifdef DEBUG
	BUG_ON(h >= hashtab->table_size);
#endif
	index = hashtab->table[h];

	return &hashtab->bucket[index];

}

struct bucket * hashtab_null_bucket(struct hashtab * hashtab)
{
	return &hashtab->bucket[0];
}

struct bucket * hashtab_next(struct hashtab * hashtab, struct bucket * b)
{
	unsigned long next = b->next;

#ifdef DEBUG
	BUG_ON(next >= hashtab->bucket_size);
	BUG_ON(b == hashtab_null_bucket(hashtab));
#endif

	return &hashtab->bucket[next];
}

