An emulator, assembler, and disassembler for the Sega Game Gear
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

153 lignes
4.3 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 "../util.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(
  21. const HashTable *table, const char *key, ssize_t size)
  22. {
  23. size_t hash = 5381;
  24. while ((size < 0 || size-- > 0) && *key)
  25. hash = ((hash << 5) + hash) + *(key++);
  26. return hash % table->buckets;
  27. }
  28. /*
  29. Return true if the two key strings are equal, or false if they aren't.
  30. */
  31. static inline bool keyeq(const char *s1, const char *s2, ssize_t size)
  32. {
  33. return !(size >= 0 ? strncmp(s1, s2, size) : strcmp(s1, s2));
  34. }
  35. /*
  36. Create and return a new HashTable.
  37. These HashTables are designed to be generic, a sort of poor-man's C++
  38. template. They can store any kind of node data as long as they are structs
  39. with a char* field (for the node key), and a pointer to its own type (to
  40. implement separate chaining).
  41. key_offset is the (byte) offset of the key field, and next_offset is the
  42. offset of the self-pointer. The callback function is called on a node when
  43. it is removed from the table. Typically, it should free() the node's
  44. members and then free() the node itself.
  45. The hash_table_NEW macro can be used to call this function more easily.
  46. */
  47. HashTable* hash_table_new(
  48. size_t key_offset, size_t next_offset, HashFreeCallback callback)
  49. {
  50. HashTable *table = cr_malloc(sizeof(HashTable));
  51. table->nodes = cr_calloc(INITIAL_BUCKETS, sizeof(HashNode*));
  52. table->buckets = INITIAL_BUCKETS;
  53. table->key_offset = key_offset;
  54. table->next_offset = next_offset;
  55. table->free = callback;
  56. return table;
  57. }
  58. /*
  59. Deallocate a HashTable. This function does nothing if the table is NULL.
  60. */
  61. void hash_table_free(HashTable *table)
  62. {
  63. if (!table)
  64. return;
  65. for (size_t bucket = 0; bucket < table->buckets; bucket++) {
  66. HashNode *node = table->nodes[bucket];
  67. while (node) {
  68. HashNode *temp = NEXT_NODE(table, node);
  69. table->free(node);
  70. node = temp;
  71. }
  72. }
  73. free(table);
  74. }
  75. /*
  76. Search for a key in the hash table.
  77. Return the corresponding node on success and NULL on failure.
  78. If the key is null-terminated, pass size as -1.
  79. */
  80. const HashNode* hash_table_find(
  81. const HashTable *table, const char *key, ssize_t size)
  82. {
  83. HashNode *node = table->nodes[hash_key(table, key, size)];
  84. while (node) {
  85. if (keyeq(key, NODE_KEY(table, node), size))
  86. return node;
  87. node = NEXT_NODE(table, node);
  88. }
  89. return NULL;
  90. }
  91. /*
  92. Insert a node into the table.
  93. This doesn't prevent inserting duplicate keys. If a duplicate key is
  94. inserted, the table acts like a stack; the new one will shadow the old one,
  95. and hash_table_remove() will remove the most-recently inserted key to
  96. reveal the second-most recent.
  97. */
  98. void hash_table_insert(HashTable *table, HashNode *node)
  99. {
  100. size_t index = hash_key(table, NODE_KEY(table, node), -1);
  101. NEXT_NODE(table, node) = table->nodes[index];
  102. table->nodes[index] = node;
  103. }
  104. /*
  105. (Try to) remove a node with the given key from the table.
  106. Return true if the node was removed, or false if it was not found.
  107. If the key is null-terminated, pass size as -1.
  108. */
  109. bool hash_table_remove(HashTable *table, const char *key, ssize_t size)
  110. {
  111. size_t index = hash_key(table, key, size);
  112. HashNode *node = table->nodes[index], *prev = NULL, *next;
  113. while (node) {
  114. next = NEXT_NODE(table, node);
  115. if (keyeq(key, NODE_KEY(table, node), size)) {
  116. if (prev)
  117. NEXT_NODE(table, prev) = next;
  118. else
  119. table->nodes[index] = next;
  120. table->free(node);
  121. return true;
  122. }
  123. prev = node;
  124. node = next;
  125. }
  126. return false;
  127. }