master xplshn/aruu / shared / libtcutil / table.c
  1#include "table.h"
  2
  3#include <assert.h>
  4#include <stdlib.h> /* malloc */
  5#include <string.h>
  6
  7/* fnv-1a */
  8static uint32_t
  9hash_string(const char *key, int length)
 10{
 11  const unsigned char *u    = (const unsigned char *)key;
 12  uint32_t             hash = 2166136261u;
 13  int                  i;
 14
 15  for (i = 0; i < length; ++i)
 16    hash = (hash ^ u[i]) * 16777619u;
 17  return hash;
 18}
 19
 20/* struct name: global interning table */
 21
 22static struct Table name_table;
 23
 24static const struct Name *
 25find_name_table(const char *chars, int bytes, uint32_t hash)
 26{
 27  const struct Table *table;
 28  uint32_t            index;
 29  struct TableEntry  *entry;
 30  const struct Name  *key;
 31
 32  table = &name_table;
 33  if (table->count == 0)
 34    return NULL;
 35
 36  for (index = hash % table->capacity;; index = (index + 1) % table->capacity) {
 37    entry = &table->entries[index];
 38    key   = entry->key;
 39    if (key == NULL) {
 40      if (entry->value == NULL)
 41        return NULL;
 42    } else if (key->bytes == bytes && key->hash == hash && memcmp(key->chars, chars, bytes) == 0) {
 43      return key;
 44    }
 45  }
 46}
 47
 48const struct Name *
 49alloc_name(const char *begin, const char *end, int make_copy)
 50{
 51  int                bytes;
 52  uint32_t           hash;
 53  const struct Name *name;
 54  char              *new_str;
 55  struct Name       *new_name;
 56
 57  bytes = end != NULL ? (int)(end - begin) : (int)strlen(begin);
 58  hash  = hash_string(begin, bytes);
 59  name  = find_name_table(begin, bytes, hash);
 60  if (name == NULL) {
 61    if (make_copy) {
 62      new_str = malloc(bytes);
 63      if (new_str == NULL)
 64        return NULL;
 65      memcpy(new_str, begin, bytes);
 66      begin = new_str;
 67    }
 68    new_name = malloc(sizeof(*new_name));
 69    if (new_name == NULL) {
 70      if (make_copy)
 71        free((void *)begin);
 72    } else {
 73      new_name->chars = begin;
 74      new_name->bytes = bytes;
 75      new_name->hash  = hash;
 76      table_put(&name_table, new_name, new_name);
 77      name = new_name;
 78    }
 79  }
 80  return name;
 81}
 82
 83const struct Name *
 84alloc_cname(const char *cstr)
 85{
 86  return alloc_name(cstr, NULL, 0);
 87}
 88
 89int
 90equal_name(const struct Name *name1, const struct Name *name2)
 91{
 92  /* all names are interned, compare by pointer */
 93  return name1 == name2;
 94}
 95
 96/* struct table: open-addressing hash table */
 97
 98static struct TableEntry *
 99find_entry(struct TableEntry *entries, int capacity, const struct Name *key)
100{
101  struct TableEntry *tombstone;
102  uint32_t           index;
103  struct TableEntry *entry;
104
105  tombstone = NULL;
106  for (index = key->hash % capacity;; index = (index + 1) % capacity) {
107    entry = &entries[index];
108    if (entry->key == NULL) {
109      if (entry->value == NULL)
110        return tombstone != NULL ? tombstone : entry;
111      /* tombstone */
112      if (tombstone == NULL)
113        tombstone = entry;
114    } else if (entry->key == key) {
115      return entry;
116    }
117  }
118}
119
120static void
121adjust_capacity(struct Table *table, int new_capacity)
122{
123  struct TableEntry *new_entries;
124  int                i;
125  struct TableEntry *old_entries;
126  int                old_capacity;
127  int                new_count;
128
129  new_entries = malloc(sizeof(struct TableEntry) * new_capacity);
130  if (new_entries == NULL)
131    return;
132  for (i = 0; i < new_capacity; ++i) {
133    new_entries[i].key   = NULL;
134    new_entries[i].value = NULL;
135  }
136
137  old_entries  = table->entries;
138  old_capacity = table->capacity;
139  new_count    = 0;
140  for (i = 0; i < old_capacity; ++i) {
141    struct TableEntry *entry;
142    struct TableEntry *dest;
143
144    entry = &old_entries[i];
145    if (entry->key == NULL)
146      continue;
147
148    dest        = find_entry(new_entries, new_capacity, entry->key);
149    dest->key   = entry->key;
150    dest->value = entry->value;
151    ++new_count;
152  }
153
154  free(old_entries);
155  table->entries  = new_entries;
156  table->capacity = new_capacity;
157  table->count = table->used = new_count;
158}
159
160struct Table *
161alloc_table(void)
162{
163  struct Table *table = malloc(sizeof(*table));
164  if (table != NULL)
165    table_init(table);
166  return table;
167}
168
169void
170table_init(struct Table *table)
171{
172  assert(table != NULL);
173  table->entries  = NULL;
174  table->count    = 0;
175  table->used     = 0;
176  table->capacity = 0;
177}
178
179void *
180table_get(struct Table *table, const struct Name *key)
181{
182  struct TableEntry *entry;
183
184  assert(table != NULL);
185  if (table->count == 0)
186    return NULL;
187
188  entry = find_entry(table->entries, table->capacity, key);
189  if (entry->key == NULL)
190    return NULL;
191
192  return entry->value;
193}
194
195int
196table_try_get(struct Table *table, const struct Name *key, void **output)
197{
198  struct TableEntry *entry;
199
200  assert(table != NULL);
201  if (table->count == 0)
202    return 0;
203
204  entry = find_entry(table->entries, table->capacity, key);
205  if (entry->key == NULL)
206    return 0;
207
208  if (output != NULL)
209    *output = entry->value;
210  return 1;
211}
212
213int
214table_put(struct Table *table, const struct Name *key, void *value)
215{
216  const int          min_capacity = 15;
217  int                capacity;
218  struct TableEntry *entry;
219  int                is_new_key;
220
221  assert(table != NULL);
222  if (table->used >= table->capacity / 2) {
223    capacity = table->capacity * 2 - 1; /* keep odd */
224    if (capacity < min_capacity)
225      capacity = min_capacity;
226    adjust_capacity(table, capacity);
227  }
228
229  entry      = find_entry(table->entries, table->capacity, key);
230  is_new_key = entry->key == NULL;
231  if (is_new_key) {
232    ++table->count;
233    if (entry->value == NULL)
234      ++table->used;
235  }
236
237  entry->key   = key;
238  entry->value = value;
239  return is_new_key;
240}
241
242int
243table_delete(struct Table *table, const struct Name *key)
244{
245  struct TableEntry *entry;
246
247  assert(table != NULL);
248  if (table->count == 0)
249    return 0;
250
251  entry = find_entry(table->entries, table->capacity, key);
252  if (entry->key == NULL)
253    return 0;
254
255  --table->count;
256  /* mark as tombstone */
257  entry->key   = NULL;
258  entry->value = entry;
259
260  return 1;
261}
262
263int
264table_iterate(const struct Table *table, int iterator, const struct Name **pkey, void **pvalue)
265{
266  int                      capacity;
267  const struct TableEntry *entry;
268  const struct Name       *key;
269
270  assert(table != NULL);
271  capacity = table->capacity;
272  for (; iterator < capacity; ++iterator) {
273    entry = &table->entries[iterator];
274    key   = entry->key;
275    if (key != NULL) {
276      if (pkey != NULL)
277        *pkey = key;
278      if (pvalue != NULL)
279        *pvalue = entry->value;
280      return iterator + 1;
281    }
282  }
283  return -1;
284}