hsearch.c 3.0 KB

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