summaryrefslogtreecommitdiffstats
path: root/lib/hashtable.c
blob: 17f1e74e292d026900f4e0dce3dd1be02ff51ccb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "hashtable.h"
#include "string.h"
#include "mem/memory.h"

static size_t
s_hash_function_mul(const void *data, size_t len)
{
    size_t hash = 1;
    const char *cdata = (const char*)data;
    for(size_t i = 0; i < len; i++) {
        hash *= ((size_t)cdata[i]) + 1;
    }
    return hash;
}

static size_t
s_hash_function_mul_s(const void *data, size_t _)
{
    return s_hash_function_mul(data, strlen(data));
}

void
_hashtable_new(struct HashTable *table, size_t obj_size, size_t key_size)
{
    *table = (struct HashTable){
        .buckets = NULL,
        .bucket_count = 0,
        .bucket_capacity = 2,
        .obj_size = obj_size,
        .key_size = key_size,
        .hash_function = s_hash_function_mul
    };
    table->buckets = mem_alloc(sizeof(struct SLinkedList) * 2);
}

void
_hashtable_news(struct HashTable *table, size_t obj_size)
{
    *table = (struct HashTable){
        .buckets = NULL,
        .bucket_count = 0,
        .bucket_capacity = 2,
        .obj_size = obj_size,
        .key_size = -1,
        .hash_function = s_hash_function_mul_s
    };
    table->buckets = mem_alloc(sizeof(struct SLinkedList) * 2);
}

void
hashtable_insert(struct HashTable *table, const void *key, void *data)
{
    size_t hash = table->hash_function(key, table->key_size);
    if(table->bucket_capacity == table->bucket_count) {
        struct SLinkedList *old_buckets = table->buckets;
        size_t old_buckets_count = table->bucket_count;

        table->bucket_capacity *= 2;
        table->bucket_count = 0;
        table->buckets = mem_alloc(sizeof(struct SLinkedList) * table->bucket_capacity);
        for(size_t i = 0; i < old_buckets_count; i++) {
            for(struct SLLNode *node = old_buckets[i].head; node != NULL; node = node->next) {
                struct HashTableValue *value = (struct HashTableValue*)(&node->data);
                hashtable_insert(table, value->key, value->value);
                mem_free(node);
            }
        }
    }

    size_t index = hash % table->bucket_capacity;
    struct SLinkedList *bucket = &table->buckets[index];
    struct SLLNode *node = mem_alloc(sizeof(struct SLLNode) + sizeof(struct HashTableValue) + table->obj_size);
    struct HashTableValue *value = (struct HashTableValue*)(node->data);
    
    value->key = key;
    memcpy(value->value, data, table->obj_size);

    table->bucket_count++;
    if(bucket->count == 0) {
        sll_new(bucket, table->obj_size);
        sll_push(bucket, node);
        return;
    }
    for(struct SLLNode *onode = bucket->head; onode != NULL; onode = onode->next)
    {
        struct HashTableValue *ovalue = (struct HashTableValue*)onode->data;
        size_t keylen = table->key_size > 0 ? table->key_size : strlen((const char*)key) + 1;
        if(memcmp(ovalue->key, value->key, keylen) == 0) {
            memcpy(ovalue->value, value->value, table->obj_size);
            return;
        }
    }
    sll_push(bucket, node);
}

void*
_hashtable_get(struct HashTable *table, const void *key)
{
    size_t hash = table->hash_function(key, table->key_size);
    size_t index = hash % table->bucket_capacity;
    struct SLinkedList *bucket = &table->buckets[index];
    for(struct SLLNode *node = bucket->head; node != NULL; node = node->next)
    {
        struct HashTableValue *value = (struct HashTableValue*)node->data;
        size_t keylen = table->key_size > 0 ? table->key_size : strlen((const char*)value->key);
        if(memcmp(key, value->key, keylen) == 0) return value->value;
    }
    return NULL;
}