-
Notifications
You must be signed in to change notification settings - Fork 746
/
map.c
132 lines (119 loc) · 3.09 KB
/
map.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
// Copyright 2014 Rui Ueyama. Released under the MIT license.
// This is an implementation of hash table.
#include <stdlib.h>
#include <string.h>
#include "8cc.h"
#define INIT_SIZE 16
#define TOMBSTONE ((void *)-1)
static uint32_t hash(char *p) {
// FNV hash
uint32_t r = 2166136261;
for (; *p; p++) {
r ^= *p;
r *= 16777619;
}
return r;
}
static Map *do_make_map(Map *parent, int size) {
Map *r = malloc(sizeof(Map));
r->parent = parent;
r->key = calloc(size, sizeof(char *));
r->val = calloc(size, sizeof(void *));
r->size = size;
r->nelem = 0;
r->nused = 0;
return r;
}
static void maybe_rehash(Map *m) {
if (!m->key) {
m->key = calloc(INIT_SIZE, sizeof(char *));
m->val = calloc(INIT_SIZE, sizeof(void *));
m->size = INIT_SIZE;
return;
}
if (m->nused < m->size * 0.7)
return;
int newsize = (m->nelem < m->size * 0.35) ? m->size : m->size * 2;
char **k = calloc(newsize, sizeof(char *));
void **v = calloc(newsize, sizeof(void *));
int mask = newsize - 1;
for (int i = 0; i < m->size; i++) {
if (m->key[i] == NULL || m->key[i] == TOMBSTONE)
continue;
int j = hash(m->key[i]) & mask;
for (;; j = (j + 1) & mask) {
if (k[j] != NULL)
continue;
k[j] = m->key[i];
v[j] = m->val[i];
break;
}
}
m->key = k;
m->val = v;
m->size = newsize;
m->nused = m->nelem;
}
Map *make_map() {
return do_make_map(NULL, INIT_SIZE);
}
Map *make_map_parent(Map *parent) {
return do_make_map(parent, INIT_SIZE);
}
static void *map_get_nostack(Map *m, char *key) {
if (!m->key)
return NULL;
int mask = m->size - 1;
int i = hash(key) & mask;
for (; m->key[i] != NULL; i = (i + 1) & mask)
if (m->key[i] != TOMBSTONE && !strcmp(m->key[i], key))
return m->val[i];
return NULL;
}
void *map_get(Map *m, char *key) {
void *r = map_get_nostack(m, key);
if (r)
return r;
// Map is stackable. If no value is found,
// continue searching from the parent.
if (m->parent)
return map_get(m->parent, key);
return NULL;
}
void map_put(Map *m, char *key, void *val) {
maybe_rehash(m);
int mask = m->size - 1;
int i = hash(key) & mask;
for (;; i = (i + 1) & mask) {
char *k = m->key[i];
if (k == NULL || k == TOMBSTONE) {
m->key[i] = key;
m->val[i] = val;
m->nelem++;
if (k == NULL)
m->nused++;
return;
}
if (!strcmp(k, key)) {
m->val[i] = val;
return;
}
}
}
void map_remove(Map *m, char *key) {
if (!m->key)
return;
int mask = m->size - 1;
int i = hash(key) & mask;
for (; m->key[i] != NULL; i = (i + 1) & mask) {
if (m->key[i] == TOMBSTONE || strcmp(m->key[i], key))
continue;
m->key[i] = TOMBSTONE;
m->val[i] = NULL;
m->nelem--;
return;
}
}
size_t map_len(Map *m) {
return m->nelem;
}