-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhashmap.c
155 lines (127 loc) · 3.7 KB
/
hashmap.c
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include "hashmap.h"
/**
* @brief Dan Bernsteins DJB2 Hash Function
* @detail http://www.cse.yorku.ca/~oz/hash.html
* String must be terminated with a null-byte.
*/
unsigned long hash(char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
return hash;
}
/**
* @brief Allocate new hash table.
* @detail Uses malloc. Free with hashmap_free.
*/
struct hashmap *hashmap_alloc(int capacity)
{
struct hashmap *new_table = malloc(sizeof(struct hashmap));
new_table->capacity = capacity;
new_table->size = 0;
new_table->values = malloc(capacity * sizeof(struct hashmap_entry));
for (int i = 0; i < new_table->capacity; i++) {
new_table->values[i].key = NULL;
}
return new_table;
}
void hashmap_free(struct hashmap **table, void (*value_free)(void *ptr))
{
if (!(*table)) {
return;
}
for (int i = 0; i < (*table)->capacity; i++) {
if ((*table)->values[i].key) {
free((*table)->values[i].key);
if (value_free) {
value_free((*table)->values[i].value);
}
}
}
free((*table)->values);
free(*table);
*table = NULL;
}
struct hashmap *clone_and_double(struct hashmap *table)
{
struct hashmap *new_table;
new_table = hashmap_alloc(2 * table->capacity);
struct hashmap_entry *entry;
for (int i = 0; i < table->capacity; i++) {
entry = table->values + i;
if (entry->key) {
hashmap_put(&new_table, entry->key, entry->value);
}
}
return new_table;
}
void *hashmap_put(struct hashmap **table, const char *key, void *value)
{
if (!key) {
return NULL;
}
if (*table == NULL) {
*table = hashmap_alloc(HASHMAP_INITIAL_CAPACITY);
} else if ((*table)->size > 0.7 * (*table)->capacity) {
struct hashmap *doubled_table = clone_and_double(*table);
hashmap_free(table, NULL);
*table = doubled_table;
}
unsigned long hashval = hash((char *)key);
unsigned long position;
unsigned char reassign = 0;
int i = 1;
do {
// Quadratic probing
position = hashval + ((int)(0.5 * i)) + ((int)(0.5 * i * i));
position %= (*table)->capacity;
i++;
} while ((*table)->values[position].key
&& !(reassign =
strcmp((*table)->values[position].key, key) == 0));
void *ret = value;
if (!reassign) {
(*table)->size++;
(*table)->values[position].key = malloc(strlen(key) + 1);
strcpy((*table)->values[position].key, key);
} else {
ret = (*table)->values[position].value;
}
(*table)->values[position].value = value;
return ret;
}
void *hashmap_get(struct hashmap *const *table, const char *key)
{
void *value = NULL;
if (table && *table) {
unsigned long hashval = hash((char *)key);
unsigned long position;
int i = 1;
do {
// Quadratic probing
position =
hashval + ((int)(0.5 * i)) + ((int)(0.5 * i * i));
position %= (*table)->capacity;
i++;
} while ((*table)->values[position].key &&
strcmp((*table)->values[position].key, key) != 0);
if ((*table)->values[position].key) {
value = (*table)->values[position].value;
}
}
return value;
}
void hashmap_update(struct hashmap **table_a, struct hashmap *table_b)
{
if (!table_b) {
return;
}
for (int i = 0; i < table_b->capacity; i++) {
if (table_b->values[i].key) {
hashmap_put(table_a, table_b->values[i].key, table_b->values[i].value);
}
}
}