hsearch.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #define _GNU_SOURCE
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <search.h>
  5. /*
  6. open addressing hash table with 2^n table size
  7. quadratic probing is used in case of hash collision
  8. tab indices and hash are size_t
  9. after resize fails with ENOMEM the state of tab is still usable
  10. with the posix api items cannot be iterated and length cannot be queried
  11. */
  12. #define MINSIZE 8
  13. #define MAXSIZE ((size_t)-1/2 + 1)
  14. struct __tab {
  15. ENTRY *entries;
  16. size_t mask;
  17. size_t used;
  18. };
  19. static struct hsearch_data htab;
  20. static int __hcreate_r(size_t, struct hsearch_data *);
  21. static void __hdestroy_r(struct hsearch_data *);
  22. static int __hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *);
  23. static size_t keyhash(char *k)
  24. {
  25. unsigned char *p = (void *)k;
  26. size_t h = 0;
  27. while (*p)
  28. h = 31*h + *p++;
  29. return h;
  30. }
  31. static int resize(size_t nel, struct hsearch_data *htab)
  32. {
  33. size_t newsize;
  34. size_t i, j;
  35. ENTRY *e, *newe;
  36. ENTRY *oldtab = htab->__tab->entries;
  37. ENTRY *oldend = htab->__tab->entries + htab->__tab->mask + 1;
  38. if (nel > MAXSIZE)
  39. nel = MAXSIZE;
  40. for (newsize = MINSIZE; newsize < nel; newsize *= 2);
  41. htab->__tab->entries = calloc(newsize, sizeof *htab->__tab->entries);
  42. if (!htab->__tab->entries) {
  43. htab->__tab->entries = oldtab;
  44. return 0;
  45. }
  46. htab->__tab->mask = newsize - 1;
  47. if (!oldtab)
  48. return 1;
  49. for (e = oldtab; e < oldend; e++)
  50. if (e->key) {
  51. for (i=keyhash(e->key),j=1; ; i+=j++) {
  52. newe = htab->__tab->entries + (i & htab->__tab->mask);
  53. if (!newe->key)
  54. break;
  55. }
  56. *newe = *e;
  57. }
  58. free(oldtab);
  59. return 1;
  60. }
  61. int hcreate(size_t nel)
  62. {
  63. return __hcreate_r(nel, &htab);
  64. }
  65. void hdestroy(void)
  66. {
  67. __hdestroy_r(&htab);
  68. }
  69. static ENTRY *lookup(char *key, size_t hash, struct hsearch_data *htab)
  70. {
  71. size_t i, j;
  72. ENTRY *e;
  73. for (i=hash,j=1; ; i+=j++) {
  74. e = htab->__tab->entries + (i & htab->__tab->mask);
  75. if (!e->key || strcmp(e->key, key) == 0)
  76. break;
  77. }
  78. return e;
  79. }
  80. ENTRY *hsearch(ENTRY item, ACTION action)
  81. {
  82. ENTRY *e;
  83. __hsearch_r(item, action, &e, &htab);
  84. return e;
  85. }
  86. static int __hcreate_r(size_t nel, struct hsearch_data *htab)
  87. {
  88. int r;
  89. htab->__tab = calloc(1, sizeof *htab->__tab);
  90. if (!htab->__tab)
  91. return 0;
  92. r = resize(nel, htab);
  93. if (r == 0) {
  94. free(htab->__tab);
  95. htab->__tab = 0;
  96. }
  97. return r;
  98. }
  99. weak_alias(__hcreate_r, hcreate_r);
  100. static void __hdestroy_r(struct hsearch_data *htab)
  101. {
  102. if (htab->__tab) free(htab->__tab->entries);
  103. free(htab->__tab);
  104. htab->__tab = 0;
  105. }
  106. weak_alias(__hdestroy_r, hdestroy_r);
  107. static int __hsearch_r(ENTRY item, ACTION action, ENTRY **retval, struct hsearch_data *htab)
  108. {
  109. size_t hash = keyhash(item.key);
  110. ENTRY *e = lookup(item.key, hash, htab);
  111. if (e->key) {
  112. *retval = e;
  113. return 1;
  114. }
  115. if (action == FIND) {
  116. *retval = 0;
  117. return 0;
  118. }
  119. *e = item;
  120. if (++htab->__tab->used > htab->__tab->mask - htab->__tab->mask/4) {
  121. if (!resize(2*htab->__tab->used, htab)) {
  122. htab->__tab->used--;
  123. e->key = 0;
  124. *retval = 0;
  125. return 0;
  126. }
  127. e = lookup(item.key, hash, htab);
  128. }
  129. *retval = e;
  130. return 1;
  131. }
  132. weak_alias(__hsearch_r, hsearch_r);