UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
hashtable.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "hashtable.h"
27 
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <stddef.h>
31 #include <string.h>
32 
33 #include "common.h"
34 #include "mem.h"
35 
36 /*
37  The memory structure being build by the hash table looks like this:
38 
39  T = hashTable_s
40  B = hashBucket_s
41  I = hashItem_s
42  0 = NULL
43 
44  [T]
45  ...
46  [0]
47  [B]
48  [I]->[0]
49  [B]
50  [I]->[0]
51  [0]
52  ...
53  [B]
54  [I]->[I]->[I]->[0]
55  [0]
56  ...
57 */
58 
59 // the hash table index size */
60 #define HASH_TABLE_SIZE 256
61 
62 // forward declarations
63 struct hashBucket_s;
64 
68 struct hashItem_s {
70  void* key;
72  int nkey;
74  void* value;
76  int nvalue;
81 };
82 
86 struct hashBucket_s {
88  int count;
91 };
92 
96 struct hashTable_s {
104  bool ownsKeys;
116 };
117 
118 #ifdef __DEBUG__
119 static unsigned long _num_allocs = 0u;
120 #endif // __DEBUG__
121 
122 static void* _hash_alloc (memPool_t* pool, int n, size_t s) {
123  #ifdef __DEBUG__
124  _num_allocs++;
125  #endif // __DEBUG__
126  if (pool) return Mem_PoolAlloc (n * s, pool, 0);
127  return Mem_Alloc(n * s);
128 }
129 
130 static void _hash_free (void* p) {
131  #ifdef __DEBUG__
132  _num_allocs--;
133  #endif // __DEBUG__
134  Mem_Free(p);
135 }
136 
143 static HASH_INDEX default_hash(const void* key, int len) {
144  const unsigned char* p = (const unsigned char*) key;
145  unsigned int h = 2166136261u;
146  int i;
147 
148  for (i = 0; i < len; i++) {
149  h = (h*16777619u) ^ p[i];
150  }
151 
152  return (h % HASH_TABLE_SIZE);
153 }
154 
165 static int default_compare(const void* key1, int len1, const void* key2, int len2) {
166  int n = (len1 < len2) ? len1 : len2;
167  return memcmp(key1, key2, n);
168 }
169 
181 static hashItem_s* _find_bucket_entry (hashBucket_s* b, hashTable_compare c, const void* key, int nkey, hashItem_s** prev) {
182  hashItem_s* p = NULL; // points to preceding item in linked list or NULL if there is none
183  hashItem_s* i = b->root; // points to current item in linked list
184  while (i) {
185  if (c(key, nkey, i->key, i->nkey) == 0) {
186  // return preceding item only if reference for this value is supplied
187  if (prev) (*prev) = p;
188  return i;
189  }
190  p = i;
191  i = i->next;
192  }
193  if (prev) (*prev) = NULL;
194  return NULL;
195 }
196 
200 static void _release_entry (hashItem_s** i, bool ownsKeys, bool ownsValues) {
201  if (ownsKeys) _hash_free ((*i)->key);
202  if (ownsValues) _hash_free ((*i)->value);
203  _hash_free (*i);
204  (*i)=NULL;
205 }
206 
210 static void _release_bucket (hashBucket_s** b, bool ownsKeys, bool ownsValues) {
211  // delete entries
212  hashItem_s* i = (*b)->root;
213  while (i) {
214  hashItem_s* p=i;
215  i = i->next;
216  _release_entry(&p, ownsKeys, ownsValues);
217  }
218  (*b)->root = NULL;
219  // delete the bucket
220  _hash_free (*b);
221  (*b) = NULL;
222 }
223 
229 static void _release_table (hashTable_s** t, bool full) {
230  // delete buckets
231  for (int i=0; i<HASH_TABLE_SIZE; i++) {
232  if ((*t)->table[i]) _release_bucket(&((*t)->table[i]), (*t)->ownsKeys, (*t)->ownsValues);
233  }
234  if (full) {
235  // delete table
236  _hash_free ((*t));
237  (*t) = NULL;
238  }
239 }
245  if (t) {
246  for (int ti = 0; ti < HASH_TABLE_SIZE; ti++) {
247  if (t->table[ti]) return t->table[ti]->root;
248  }
249  }
250  return nullptr;
251 }
252 
258  if (i) {
259  if (i->next) {
260  return i->next;
261  }
262  // compute the hash key of the last item iterated
263  int h = t->hash(i->key, i->nkey);
264  // now get the next filled bucket in the array
265  h++;
266  while (h < HASH_TABLE_SIZE) {
267  hashBucket_s* b = (t->table)[h];
268  if (b) {
269  return b->root;
270  }
271  h++;
272  }
273  }
274  return nullptr;
275 }
276 
287 hashTable_s* HASH_NewTable (bool ownsKeys, bool ownsValues, bool duplicateOverwrite) {
288  return HASH_NewTable (ownsKeys, ownsValues, duplicateOverwrite, &default_hash, &default_compare);
289 }
290 
291 
303 hashTable_s* HASH_NewTable (bool ownsKeys, bool ownsValues, bool duplicateOverwrite, hashTable_hash h, hashTable_compare c) {
304  return HASH_NewTable(ownsKeys, ownsValues, duplicateOverwrite, h, c, nullptr, nullptr, nullptr);
305 }
306 
322 hashTable_s* HASH_NewTable (bool ownsKeys, bool ownsValues, bool duplicateOverwrite, hashTable_hash h, hashTable_compare c, memPool_t* keys, memPool_t* values, memPool_t* table) {
323  // allocate table
324  hashTable_s* t = (hashTable_s*) _hash_alloc (table, 1, sizeof(hashTable_s));
325  t->hash = h;
326  t->compare = c;
327  t->ownsKeys = ownsKeys;
328  t->ownsValues = ownsValues;
329  t->duplicateOverwrite = duplicateOverwrite;
330  t->keyPool = keys;
331  t->valuePool = values;
332  t->internalPool = table;
333  return t;
334 }
335 
337  hashTable_s* t = HASH_NewTable(source->ownsKeys, source->ownsValues, source->duplicateOverwrite, source->hash, source->compare);
338 
339  hashItem_s* item = _iterator_first (source);
340  while (item) {
341  HASH_Insert (t, item->key, item->nkey, item->value, item->nvalue);
342  item = _iterator_next (source, item);
343  }
344  return t;
345 }
346 
352  _release_table (t, true);
353 }
354 
369 bool HASH_Insert (hashTable_s* t, const void* key, int nkey, const void* value, int nvalue) {
370  // compute hash index
371  int idx=t->hash(key, nkey);
372  // find the bucket, if no bucket available create a new one
373  hashBucket_s* b = t->table[idx];
374  if (b == NULL) {
375  t->table[idx] = b = (hashBucket_s*)_hash_alloc (t->internalPool, 1, sizeof(hashBucket_s));
376  }
377  // find entry
378  hashItem_s* i = _find_bucket_entry(b, t->compare, key, nkey, NULL);
379  if (i) {
380  // entry already found, so we are overwriting the current value here
381  // check table settings to see if we need to assert here
382  if (!t->duplicateOverwrite) {
383  assert(false);
384  return false;
385  }
386  // if the table owns the items, create a copy
387  if (t->ownsValues) {
388  i->value = _hash_alloc (t->valuePool, 1, nvalue);
389  memcpy (i->value, value, nvalue);
390  }
391  else {
392  i->value = const_cast<void*>(value);
393  }
394  i->nvalue = nvalue;
395  }
396  else {
397  // create a new entry, add it to the top of the list
398  i = (hashItem_s*) _hash_alloc (t->internalPool, 1, sizeof(hashItem_s));
399  if (t->ownsKeys) {
400  i->key = _hash_alloc (t->keyPool, 1, nkey);
401  memcpy (i->key, key, nkey);
402  }
403  else {
404  i->key = const_cast<void*>(key);
405  }
406  i->nkey = nkey;
407  if (t->ownsValues) {
408  i->value = _hash_alloc (t->valuePool, 1, nvalue);
409  memcpy (i->value, value, nvalue);
410  }
411  else {
412  i->value = const_cast<void*>(value);
413  }
414  i->nvalue = nvalue;
415 
416  i->next = b->root;
417  b->root = i;
418  }
419  // increase the count of the bucket
420  b->count++;
421 
422  return true;
423 }
424 
429 void* HASH_Remove (hashTable_s* t, const void* key, int nkey) {
430  // return value
431  void* v = NULL;
432  // compute index
433  int idx = t->hash (key, nkey);
434  if (t->table[idx]) {
435  // find hash entry
436  hashItem_s* prev;
437  hashItem_s* i=_find_bucket_entry (t->table[idx], t->compare, key, nkey, &prev);
438  if (i) {
439  if (!t->ownsValues) v = i->value;
440  // link prev item with next item (effectively unchaining i from the linked list)
441  if (prev) {
442  prev->next = i->next;
443  _release_entry (&i, t->ownsKeys, t->ownsValues);
444  }
445  else {
446  // special case: the entry found was the root entry
447  t->table[idx]->root = i->next;
448  _release_entry (&i, t->ownsKeys, t->ownsValues);
449  }
450  // drop the count in this bucket by one
451  t->table[idx]->count--;
452  }
453  }
454  return v;
455 }
456 
461  _release_table (&t, false);
462 }
463 
467 void* HASH_Get (hashTable_s* t, const void* key, int nkey) {
468  // compute bucket index
469  int idx = t->hash(key, nkey);
470  if (t->table[idx]) {
471  // from this bucket, scan for exact match
472  hashItem_s* i = _find_bucket_entry(t->table[idx], t->compare, key, nkey, NULL);
473  if (i) return i->value;
474  }
475  return NULL;
476 }
477 
482  // iteratie the table
483  int cnt=0;
484  for (int i=0; i<HASH_TABLE_SIZE;i++) {
485  if (t->table[i]) cnt += (t->table[i]->count);
486  }
487  return cnt;
488 }
489 
490 #ifdef __DEBUG__
491 
492 #include <time.h>
493 #include <stdlib.h>
494 #include <stdio.h>
495 
499 bool HASH_test () {
500  // return value
501  bool result = true;
502 
503  /* test 1: create the hash table, delete the hash table */
504  hashTable_s* table = HASH_NewTable(true, true, true);
505  HASH_DeleteTable (&table);
506  /* check table pointer is correctly set to nil */
507  result = result && (table == NULL);
508  if (!result) return false;
509  /* check alloc total */
510  result = result && (_num_allocs == 0);
511  if (!result) return false;
512 
513  /* test 2: create the hash table, insert 3 values, delete the hash table */
514  table = HASH_NewTable(true, true, true);
515  HASH_Insert (table, "AAA", 4, "AAA", 4);
516  HASH_Insert (table, "BBB", 4, "BBB", 4);
517  HASH_Insert (table, "CCC", 4, "CCC", 4);
518  if (HASH_Count(table) != 3) return false;
519  HASH_Clear (table);
520  if (HASH_Count(table) != 0) return false;
521  HASH_DeleteTable (&table);
522  /* check table pointer is correctly set to nil */
523  result = result && (table == NULL);
524  if (!result) return false;
525  /* check alloc total */
526  result = result && (_num_allocs == 0);
527  if (!result) return false;
528 
529  /* test 3: create the hash table, insert/remove 3 values, delete the hash table */
530  table = HASH_NewTable(true, true, true);
531  HASH_Insert (table, "AAA", 4, "AAA", 4);
532  HASH_Remove (table, "AAA", 4);
533  HASH_Insert (table, "BBB", 4, "BBB", 4);
534  HASH_Remove (table, "BBB", 4);
535  HASH_Insert (table, "CCC", 4, "CCC", 4);
536  HASH_Remove (table, "CCC", 4);
537  HASH_DeleteTable (&table);
538  /* check table pointer is correctly set to nil */
539  result = result && (table == NULL);
540  if (!result) return false;
541  /* check alloc total */
542  result = result && (_num_allocs == 0);
543  if (!result) return false;
544 
545  /* test 4: create the hash table, insert/count/search/delete 3 values, delete the hash table */
546  table = HASH_NewTable(true, true, true);
547  HASH_Insert (table, "AAA", 4, "AAA", 4);
548  HASH_Insert (table, "BBB", 4, "BBB", 4);
549  HASH_Insert (table, "CCC", 4, "CCC", 4);
550  /* check count items */
551  if (HASH_Count(table) != 3) return false;
552  char* aaa = (char*)HASH_Get(table, "AAA", 4);
553  if (strncmp (aaa, "AAA", 4) != 0) return false;
554  char* bbb = (char*)HASH_Get(table, "BBB", 4);
555  if (strncmp (bbb, "BBB", 4) != 0) return false;
556  char* ccc = (char*)HASH_Get(table, "CCC", 4);
557  if (strncmp (ccc, "CCC", 4) != 0) return false;
558  HASH_Remove (table, "AAA", 4);
559  HASH_Remove (table, "BBB", 4);
560  HASH_Remove (table, "CCC", 4);
561  if (HASH_Count(table) != 0) return false;
562  HASH_DeleteTable (&table);
563  /* check table pointer is correctly set to nil */
564  result = result && (table == NULL);
565  if (!result) return false;
566  /* check alloc total */
567  result = result && (_num_allocs == 0);
568  if (!result) return false;
569 
570  /* test 5: hash function test */
571  const char AZ[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
572  char buffer[26];
573  /* compute the averag of the hash index of 400 random keys */
574  int total = 0;
575  srand(time(NULL));
576  for(int i=0; i < 4000; i++) {
577  /* create random key of random length between 4..23 charachters */
578  int len = 4 + (rand() % 20);
579  memset (buffer, 0, sizeof(buffer));
580  for (int j=0; j < len; j++) {
581  int v = rand() % sizeof(AZ);
582  buffer[j]=AZ[v];
583  }
584  /* compute the hash index */
585  int idx = default_hash (buffer, len);
586  /* sum index */
587  total += idx;
588  }
589  /* now compute the average of the indices */
590  int avg = (total / 4000);
591  /* the average idx should be somewhere halfway, allow a %10 error margin */
592  int idx_low = (HASH_TABLE_SIZE/2) - (HASH_TABLE_SIZE/10);
593  int idx_high = (HASH_TABLE_SIZE/2) + (HASH_TABLE_SIZE/10);
594  if ( !((idx_low <= avg) && (idx_high >= avg)) ) return false;
595 
596  /* test 6: hash table without ownership test */
597  table = HASH_NewTable(true, false, true);
598  char item1[] = "AAA";
599  char item2[] = "BBB";
600  char item3[] = "CCC";
601  HASH_Insert (table, item1, 4, item1, 4);
602  HASH_Insert (table, item2, 4, item2, 4);
603  HASH_Insert (table, item3, 4, item3, 4);
604  /* check if we get the correct value pointers */
605  aaa = (char*)HASH_Get(table, "AAA", 4);
606  if (aaa != item1) return false;
607  bbb = (char*)HASH_Get(table, "BBB", 4);
608  if (bbb != item2) return false;
609  ccc = (char*)HASH_Get(table, "CCC", 4);
610  if (ccc != item3) return false;
611  HASH_DeleteTable (&table);
612  /* check table pointer is correctly set to nil */
613  result = result && (table == NULL);
614  if (!result) return false;
615  /* check alloc total */
616  result = result && (_num_allocs == 0);
617  if (!result) return false;
618 
619 
620  /* end of unit test, everything OK */
621  return true;
622 }
623 
624 #endif // __DEBUG__
#define HASH_TABLE_SIZE
Definition: hashtable.cpp:60
bool duplicateOverwrite
Definition: hashtable.cpp:109
unsigned short int(* hashTable_hash)(const void *key, int len)
Definition: hashtable.h:51
static void * _hash_alloc(memPool_t *pool, int n, size_t s)
Definition: hashtable.cpp:122
memPool_t * internalPool
Definition: hashtable.cpp:115
Memory handling with sentinel checking and pools with tags for grouped free'ing.
static hashItem_s * _iterator_next(hashTable_s *t, hashItem_s *i)
iterate to next item
Definition: hashtable.cpp:257
hashTable_hash hash
Definition: hashtable.cpp:100
unsigned short int HASH_INDEX
Definition: hashtable.h:46
static void _release_entry(hashItem_s **i, bool ownsKeys, bool ownsValues)
Internal function, release an entry including key and value if owned.
Definition: hashtable.cpp:200
memPool_t * valuePool
Definition: hashtable.cpp:113
static void _release_table(hashTable_s **t, bool full)
Internal function, releases entire table.
Definition: hashtable.cpp:229
#define Mem_PoolAlloc(size, pool, tagNum)
Definition: mem.h:41
void HASH_DeleteTable(hashTable_s **t)
Deletes a hash table and sets the pointer to NULL.
Definition: hashtable.cpp:351
The hash bucket structure, contains a linked list of items.
Definition: hashtable.cpp:86
bool ownsValues
Definition: hashtable.cpp:106
hashTable_s * HASH_NewTable(bool ownsKeys, bool ownsValues, bool duplicateOverwrite)
Creates a new hash table and sets it initial capacity.
Definition: hashtable.cpp:287
static HASH_INDEX default_hash(const void *key, int len)
Default hash function to map keys into a 0..255 index.
Definition: hashtable.cpp:143
hashBucket_s * table[HASH_TABLE_SIZE]
Definition: hashtable.cpp:98
The hash table structure, contains an array of buckets being indexed by the hash function.
Definition: hashtable.cpp:96
void * key
Definition: hashtable.cpp:70
unsigned int key
Definition: cl_input.cpp:68
static hashItem_s * _find_bucket_entry(hashBucket_s *b, hashTable_compare c, const void *key, int nkey, hashItem_s **prev)
Internal function to scan the list of entries in the bucket for an exact match.
Definition: hashtable.cpp:181
void * HASH_Remove(hashTable_s *t, const void *key, int nkey)
Removes an existing value with given key from the hash table.
Definition: hashtable.cpp:429
static hashItem_s * _iterator_first(hashTable_s *t)
start iterating with the first item.
Definition: hashtable.cpp:244
hashItem_s * next
Definition: hashtable.cpp:78
int HASH_Count(hashTable_s *t)
Returns the number of entries in the hash table.
Definition: hashtable.cpp:481
#define Mem_Alloc(size)
Definition: mem.h:40
void HASH_Clear(hashTable_s *t)
Clears the hash table.
Definition: hashtable.cpp:460
void * HASH_Get(hashTable_s *t, const void *key, int nkey)
Returns the value for a given key.
Definition: hashtable.cpp:467
hashBucket_s * root
Definition: hashtable.cpp:80
static int default_compare(const void *key1, int len1, const void *key2, int len2)
Default compare function for comparing key values.
Definition: hashtable.cpp:165
static void _hash_free(void *p)
Definition: hashtable.cpp:130
memPool_t * keyPool
Definition: hashtable.cpp:111
QGL_EXTERN GLint i
Definition: r_gl.h:113
QGL_EXTERN GLuint GLchar GLuint * len
Definition: r_gl.h:99
The linked list item.
Definition: hashtable.cpp:68
#define Mem_Free(ptr)
Definition: mem.h:35
hashTable_s * HASH_CloneTable(hashTable_s *source)
Definition: hashtable.cpp:336
static void _release_bucket(hashBucket_s **b, bool ownsKeys, bool ownsValues)
Internal function, releases bucket including entries.
Definition: hashtable.cpp:210
int(* hashTable_compare)(const void *key1, int len1, const void *key2, int len2)
Definition: hashtable.h:53
definitions common between client and server, but not game lib
void * value
Definition: hashtable.cpp:74
hashTable_compare compare
Definition: hashtable.cpp:102
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
Header file for hashtable.
hashItem_s * root
Definition: hashtable.cpp:90
bool HASH_Insert(hashTable_s *t, const void *key, int nkey, const void *value, int nvalue)
Inserts a new value with given key into the hash table.
Definition: hashtable.cpp:369