An emulator, assembler, and disassembler for the Sega Game Gear
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

144 lines
3.9 KiB

  1. /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. Released under the terms of the MIT License. See LICENSE for details. */
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include "hash_table.h"
  6. #include "../logging.h"
  7. #define INITIAL_BUCKETS 127
  8. #define GET_FIELD_(obj, offset, type) (*((type*) (((char*) obj) + offset)))
  9. #define NODE_KEY(tab, node) GET_FIELD_(node, tab->key_offset, char*)
  10. #define NEXT_NODE(tab, node) GET_FIELD_(node, tab->next_offset, HashNode*)
  11. /* Internal structs */
  12. struct HashNode {
  13. char *key;
  14. HashNode *next;
  15. };
  16. /*
  17. Hash a string key into a hash table bucket index.
  18. This uses the djb2 algorithm: http://www.cse.yorku.ca/~oz/hash.html
  19. */
  20. static inline size_t hash_key(const HashTable *table, const char *key)
  21. {
  22. size_t hash = 5381;
  23. while (*key)
  24. hash = ((hash << 5) + hash) + *(key++);
  25. return hash % table->buckets;
  26. }
  27. /*
  28. Create and return a new HashTable.
  29. These HashTables are designed to be generic, a sort of poor-man's C++
  30. template. They can store any kind of node data as long as they are structs
  31. with a char* field (for the node key), and a pointer to its own type (to
  32. implement separate chaining).
  33. key_offset is the (byte) offset of the key field, and next_offset is the
  34. offset of the self-pointer. The callback function is called on a node when
  35. it is removed from the table. Typically, it should free() the node's
  36. members and then free() the node itself.
  37. The hash_table_NEW macro can be used to call this function more easily.
  38. */
  39. HashTable* hash_table_new(
  40. size_t key_offset, size_t next_offset, HashFreeCallback callback)
  41. {
  42. HashTable *table;
  43. if (!(table = malloc(sizeof(HashTable))))
  44. OUT_OF_MEMORY()
  45. if (!(table->nodes = calloc(INITIAL_BUCKETS, sizeof(HashNode*))))
  46. OUT_OF_MEMORY()
  47. table->buckets = INITIAL_BUCKETS;
  48. table->key_offset = key_offset;
  49. table->next_offset = next_offset;
  50. table->free = callback;
  51. return table;
  52. }
  53. /*
  54. Deallocate a HashTable. This function does nothing if the table is NULL.
  55. */
  56. void hash_table_free(HashTable *table)
  57. {
  58. if (!table)
  59. return;
  60. for (size_t bucket = 0; bucket < table->buckets; bucket++) {
  61. HashNode *node = table->nodes[bucket];
  62. while (node) {
  63. HashNode *temp = NEXT_NODE(table, node);
  64. table->free(node);
  65. node = temp;
  66. }
  67. }
  68. free(table);
  69. }
  70. /*
  71. Search for a key in the hash table.
  72. Return the corresponding node on success and NULL on failure.
  73. */
  74. const HashNode* hash_table_find(const HashTable *table, const char *key)
  75. {
  76. HashNode *node = table->nodes[hash_key(table, key)];
  77. while (node) {
  78. if (!strcmp(key, NODE_KEY(table, node)))
  79. return node;
  80. node = NEXT_NODE(table, node);
  81. }
  82. return NULL;
  83. }
  84. /*
  85. Insert a node into the table.
  86. This doesn't prevent inserting duplicate keys. If a duplicate key is
  87. inserted, the table acts like a stack; the new one will shadow the old one,
  88. and hash_table_remove() will remove the most-recently inserted key to
  89. reveal the second-most recent.
  90. */
  91. void hash_table_insert(HashTable *table, HashNode *node)
  92. {
  93. size_t index = hash_key(table, NODE_KEY(table, node));
  94. NEXT_NODE(table, node) = table->nodes[index];
  95. table->nodes[index] = node;
  96. }
  97. /*
  98. (Try to) remove a node with the given key from the table.
  99. Return true if the node was removed, or false if it was not found.
  100. */
  101. bool hash_table_remove(HashTable *table, const char *key)
  102. {
  103. size_t index = hash_key(table, key);
  104. HashNode *node = table->nodes[index], *prev = NULL, *next;
  105. while (node) {
  106. next = NEXT_NODE(table, node);
  107. if (!strcmp(key, NODE_KEY(table, node))) {
  108. if (prev)
  109. NEXT_NODE(table, prev) = next;
  110. else
  111. table->nodes[index] = next;
  112. table->free(node);
  113. return true;
  114. }
  115. prev = node;
  116. node = next;
  117. }
  118. return false;
  119. }