csnip  0.1
lphash_table.h
Go to the documentation of this file.
1 #ifndef CSNIP_LPHASH_TABLE
2 #define CSNIP_LPHASH_TABLE
3 
13 #include <assert.h>
14 #include <stdint.h>
15 #include <stddef.h>
16 
17 #include <csnip/mem.h>
18 #include <csnip/preproc.h>
19 #include <csnip/lphash.h>
20 
32 #define CSNIP_LPHASH_TABLE_DEF_TYPE(struct_tbltype, \
33  entrytype) \
34  struct struct_tbltype { \
35  size_t cap; /* Capacity */ \
36  size_t size; /* Number of used entries */ \
37  entrytype* entry; /* The table entries */ \
38  unsigned char* occ; /* Occupancy indicators */ \
39  };
40 
48 #define CSNIP_LPHASH_TABLE_DECL_FUNCS(scope, \
49  prefix, \
50  keytype, \
51  entrytype, \
52  tbltype) \
53  /* Creation & Deletion */ \
54  scope tbltype* prefix##make(int* err); \
55  scope void prefix##free(tbltype* tbl); \
56  \
57  /* Element manipulation */ \
58  scope int prefix##insert( \
59  tbltype* tbl, \
60  int* err, \
61  entrytype E); \
62  scope int prefix##insert_or_assign( \
63  tbltype* tbl, \
64  int* err, \
65  entrytype E, \
66  entrytype* ret_old); \
67  scope entrytype* prefix##find_or_insert( \
68  tbltype* tbl, \
69  int* err, \
70  entrytype E); \
71  scope _Bool prefix##remove( \
72  tbltype* tbl, \
73  int* err, \
74  keytype key); \
75  scope entrytype* prefix##find( \
76  const tbltype* tbl, \
77  keytype key); \
78  \
79  /* Size and capacity */ \
80  scope size_t prefix##size(const tbltype* tbl); \
81  scope size_t prefix##capacity(const tbltype* tbl); \
82  \
83  /* Slot functions */ \
84  scope size_t prefix##findslot( \
85  const tbltype* tbl, \
86  keytype key); \
87  scope _Bool prefix##isslotoccupied( \
88  const tbltype* tbl, \
89  size_t i); \
90  scope entrytype* prefix##getslotentryaddress( \
91  const tbltype* tbl, \
92  size_t i); \
93  scope size_t prefix##getslotfromentryaddress( \
94  const tbltype* tbl, \
95  entrytype const * entry); \
96  scope size_t prefix##removeatslot( \
97  tbltype* tbl, \
98  int* err, \
99  size_t i); \
100  scope size_t prefix##firstoccupiedslot( \
101  const tbltype* tbl); \
102  scope size_t prefix##nextoccupiedslot( \
103  const tbltype* tbl, \
104  size_t i);
105 
202 #define CSNIP_LPHASH_TABLE_DEF_FUNCS(scope, \
203  prefix, \
204  keytype, \
205  entrytype, \
206  tbltype, \
207  k1, k2, /* key dummy vars */ \
208  e, /* entry dummy var */ \
209  hash, /* evaluate to hash(k1) */ \
210  is_match, /* Check whether k1 and k2 match */ \
211  get_key) /* evaluate to the key of e */ \
212  \
213  /* Declare functions in case they weren't yet. */ \
214  CSNIP_LPHASH_TABLE_DECL_FUNCS(scope, prefix, keytype, entrytype, \
215  tbltype) \
216  \
217  /* Private methods */ \
218  static size_t prefix##_internal_findloc( \
219  const tbltype* T, \
220  keytype key, \
221  int* state_) \
222  { \
223  size_t ret_; \
224  entrytype e; \
225  keytype k2; \
226  csnip_lphash_Find(T->cap, keytype, k1, u, \
227  hash, \
228  !T->occ[u], \
229  (e = T->entry[u], k2 = (get_key), (is_match)), \
230  (e = T->entry[u], (get_key)), \
231  key, \
232  ret_, \
233  *state_); \
234  return ret_; \
235  } \
236  \
237  static void prefix##_internal_deleteloc(tbltype* T, \
238  size_t loc) \
239  { \
240  entrytype e; \
241  csnip_lphash_Delete(T->cap, keytype, k1, u, v, \
242  hash, \
243  !T->occ[u], \
244  (e = T->entry[u], (get_key)), \
245  (T->entry[v] = T->entry[u], T->occ[v] = T->occ[u]), \
246  T->occ[u] = 0,\
247  loc); \
248  } \
249  \
250  static _Bool prefix##_internal_grow(tbltype* T, \
251  int* err, \
252  size_t min_size) \
253  { \
254  if (min_size * 3 <= T->cap * 2) { \
255  /* No need to grow */ \
256  return 0; \
257  } \
258  \
259  /* Compute new capacity */ \
260  size_t newcap = (T->cap ? T->cap : 8); \
261  while (min_size * 3 > newcap * 2) { \
262  newcap *= 2; \
263  /* XXX: Check overflow in the above */ \
264  } \
265  \
266  /* Allocate new hashing table */ \
267  entrytype* newarr; \
268  unsigned char* newocc; \
269  csnip_mem_Alloc(newcap, newarr, *err); \
270  if (err && *err) return 0; \
271  csnip_mem_Alloc(newcap, newocc, *err); \
272  if (err && *err) return 0; \
273  tbltype N = { \
274  .cap = newcap, \
275  .size = T->size, \
276  .entry = newarr, \
277  .occ = newocc \
278  }; \
279  for (size_t i = 0; i < newcap; ++i) { \
280  newocc[i] = 0; \
281  } \
282  \
283  /* Copy from old to new */ \
284  for (size_t i = 0; i < T->cap; ++i) { \
285  if (T->occ[i]) { \
286  size_t l; \
287  int r; \
288  entrytype e = T->entry[i]; \
289  l = prefix##_internal_findloc(&N, \
290  (get_key), &r); \
291  assert(r == 1); \
292  newarr[l] = T->entry[i]; \
293  newocc[l] = 1; \
294  } \
295  } \
296  \
297  /* Replace old table with new one, and free */ \
298  if (T->entry) csnip_mem_Free(T->entry); \
299  if (T->occ) csnip_mem_Free(T->occ); \
300  *T = N; \
301  \
302  return 1; \
303  } \
304  \
305  /* Creation / Deletion */ \
306  scope tbltype* prefix##make(int* err) \
307  { \
308  if (err) *err = 0; \
309  \
310  tbltype* T; \
311  csnip_mem_Alloc(1, T, *err); \
312  if (err && *err) \
313  return NULL; \
314  T->cap = 0; \
315  T->size = 0; \
316  T->entry = NULL; \
317  T->occ = NULL; \
318  return T; \
319  } \
320  \
321  scope void prefix##free(tbltype* T) \
322  { \
323  if (T->entry) csnip_mem_Free(T->entry); \
324  if (T->occ) csnip_mem_Free(T->occ); \
325  csnip_mem_Free(T); \
326  } \
327  \
328  /* Element manipulation */ \
329  \
330  /* Inserts an element only if that key is not yet present.
331  * Returns 0 if nothing was inserted,
332  * returns 1 if a new element was inserted.
333  */ \
334  scope int prefix##insert(tbltype* T, int* err, entrytype e) \
335  { \
336  if (err) *err = 0; \
337  \
338  /* Grow if necessary */ \
339  prefix##_internal_grow(T, err, T->size + 1); \
340  if (err && *err) \
341  return 0; \
342  \
343  /* Insert or replace entry */ \
344  int r; \
345  keytype key = (get_key); \
346  size_t loc = prefix##_internal_findloc(T, key, &r); \
347  assert(r < 2); \
348  if (r == 1) { \
349  T->entry[loc] = e; \
350  T->occ[loc] = 1; \
351  ++T->size; \
352  } \
353  return r; \
354  } \
355  \
356  /* Replaces existing element, or inserts a new one.
357  *
358  * Returns 0 if element was replaced,
359  * and 1 if element was newly inserted.
360  */ \
361  scope int prefix##insert_or_assign(tbltype* T, \
362  int* err, \
363  entrytype e, \
364  entrytype* old) \
365  { \
366  if (err) *err = 0; \
367  \
368  /* Grow if necessary */ \
369  prefix##_internal_grow(T, err, T->size + 1); \
370  if (err && *err) \
371  return 0; \
372  \
373  /* Insert or assign entry */ \
374  int r; \
375  keytype key = (get_key); \
376  size_t loc = prefix##_internal_findloc(T, key, &r); \
377  assert(r < 2); \
378  if (r == 0) { \
379  if (old) *old = T->entry[loc]; \
380  } else { \
381  ++T->size; \
382  T->occ[loc] = 1; \
383  } \
384  T->entry[loc] = e; \
385  return r; \
386  } \
387  \
388  scope entrytype* prefix##find_or_insert(tbltype* T, \
389  int* err, \
390  entrytype entry) \
391  { \
392  if (err) *err = 0; \
393  \
394  int r; \
395  entrytype e = entry; \
396  size_t loc = prefix##_internal_findloc(T, (get_key), &r); \
397  if (r >= 1) { \
398  /* Insert */ \
399  if (prefix##_internal_grow(T, err, T->size + 1)) { \
400  /* Need to search again, since we
401  * rehashed
402  */ \
403  loc = prefix##_internal_findloc(T, \
404  (get_key), &r); \
405  assert(r == 1); \
406  } \
407  \
408  if (err && *err) \
409  return NULL; \
410  \
411  T->entry[loc] = entry; \
412  T->occ[loc] = 1; \
413  ++T->size; \
414  } \
415  return &T->entry[loc]; \
416  } \
417  \
418  scope _Bool prefix##remove(tbltype* T, int* err, keytype key) \
419  { \
420  int r; \
421  const size_t loc = prefix##_internal_findloc(T, key, &r); \
422  if (r == 0) { \
423  prefix##_internal_deleteloc(T, loc); \
424  --T->size; \
425  } \
426  return r == 0; \
427  } \
428  \
429  scope entrytype* prefix##find(const tbltype* T, keytype key) \
430  { \
431  int r; \
432  const size_t loc = prefix##_internal_findloc(T, key, &r); \
433  if (r == 0) \
434  return &T->entry[loc]; \
435  return NULL; \
436  } \
437  \
438  /* Size and capacity */ \
439  scope size_t prefix##size(const tbltype* T) \
440  { \
441  return T->size; \
442  } \
443  \
444  scope size_t prefix##capacity(const tbltype* T) \
445  { \
446  return T->cap; \
447  } \
448  \
449  /* Slot functions */ \
450  scope size_t prefix##findslot(const tbltype* T, keytype key) \
451  { \
452  int r; \
453  const size_t loc = prefix##_internal_findloc(T, key, &r); \
454  if (r == 0) \
455  return loc; \
456  return T->cap; \
457  } \
458  \
459  scope _Bool prefix##isslotoccupied(const tbltype* T, size_t i) \
460  { \
461  assert(0 <= i && i < T->cap); \
462  return T->occ[i]; \
463  } \
464  \
465  scope entrytype* prefix##getslotentryaddress( \
466  const tbltype* T, \
467  size_t i) \
468  { \
469  return &T->entry[i]; \
470  } \
471  \
472  scope size_t prefix##getslotfromentryaddress( \
473  const tbltype* T, \
474  entrytype const* entry) \
475  { \
476  return (size_t)(entry - T->entry); \
477  } \
478  \
479  scope size_t prefix##removeatslot(tbltype* T, int* err, size_t i) \
480  { \
481  if (err) *err = 0; \
482  \
483  if (T->occ[i]) { \
484  prefix##_internal_deleteloc(T, i); \
485  --T->size; \
486  if (T->occ[i]) \
487  return i; \
488  } \
489  return prefix##nextoccupiedslot(T, i); \
490  } \
491  \
492  scope size_t prefix##firstoccupiedslot(const tbltype* T) \
493  { \
494  size_t r; \
495  for (r = 0; r < T->cap; ++r) \
496  if (T->occ[r]) break; \
497  return r; \
498  } \
499  \
500  scope size_t prefix##nextoccupiedslot( \
501  const tbltype* T, \
502  size_t r) \
503  { \
504  for (++r; r < T->cap; ++r) \
505  if (T->occ[r]) break; \
506  return r; \
507  }
508 
513 #endif /* CSNIP_LPHASH_TABLE */