zend_accelerator_hash.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend OPcache |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1998-2018 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Andi Gutmans <andi@php.net> |
  16. | Zeev Suraski <zeev@php.net> |
  17. | Stanislav Malyshev <stas@zend.com> |
  18. | Dmitry Stogov <dmitry@php.net> |
  19. +----------------------------------------------------------------------+
  20. */
  21. #include "ZendAccelerator.h"
  22. #include "zend_accelerator_hash.h"
  23. #include "zend_hash.h"
  24. #include "zend_shared_alloc.h"
  25. /* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */
  26. static uint32_t prime_numbers[] =
  27. {5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 };
  28. static uint32_t num_prime_numbers = sizeof(prime_numbers) / sizeof(uint32_t);
  29. void zend_accel_hash_clean(zend_accel_hash *accel_hash)
  30. {
  31. accel_hash->num_entries = 0;
  32. accel_hash->num_direct_entries = 0;
  33. memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
  34. }
  35. void zend_accel_hash_init(zend_accel_hash *accel_hash, uint32_t hash_size)
  36. {
  37. uint32_t i;
  38. for (i=0; i<num_prime_numbers; i++) {
  39. if (hash_size <= prime_numbers[i]) {
  40. hash_size = prime_numbers[i];
  41. break;
  42. }
  43. }
  44. accel_hash->num_entries = 0;
  45. accel_hash->num_direct_entries = 0;
  46. accel_hash->max_num_entries = hash_size;
  47. /* set up hash pointers table */
  48. accel_hash->hash_table = zend_shared_alloc(sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
  49. if (!accel_hash->hash_table) {
  50. zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
  51. return;
  52. }
  53. /* set up hash values table */
  54. accel_hash->hash_entries = zend_shared_alloc(sizeof(zend_accel_hash_entry)*accel_hash->max_num_entries);
  55. if (!accel_hash->hash_entries) {
  56. zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
  57. return;
  58. }
  59. memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
  60. }
  61. /* Returns NULL if hash is full
  62. * Returns pointer the actual hash entry on success
  63. * key needs to be already allocated as it is not copied
  64. */
  65. zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, const char *key, uint32_t key_length, zend_bool indirect, void *data)
  66. {
  67. zend_ulong hash_value;
  68. zend_ulong index;
  69. zend_accel_hash_entry *entry;
  70. zend_accel_hash_entry *indirect_bucket = NULL;
  71. if (indirect) {
  72. indirect_bucket = (zend_accel_hash_entry*)data;
  73. while (indirect_bucket->indirect) {
  74. indirect_bucket = (zend_accel_hash_entry*)indirect_bucket->data;
  75. }
  76. }
  77. hash_value = zend_inline_hash_func(key, key_length);
  78. #ifndef ZEND_WIN32
  79. hash_value ^= ZCG(root_hash);
  80. #endif
  81. index = hash_value % accel_hash->max_num_entries;
  82. /* try to see if the element already exists in the hash */
  83. entry = accel_hash->hash_table[index];
  84. while (entry) {
  85. if (entry->hash_value == hash_value
  86. && entry->key_length == key_length
  87. && !memcmp(entry->key, key, key_length)) {
  88. if (entry->indirect) {
  89. if (indirect_bucket) {
  90. entry->data = indirect_bucket;
  91. } else {
  92. ((zend_accel_hash_entry*)entry->data)->data = data;
  93. }
  94. } else {
  95. if (indirect_bucket) {
  96. accel_hash->num_direct_entries--;
  97. entry->data = indirect_bucket;
  98. entry->indirect = 1;
  99. } else {
  100. entry->data = data;
  101. }
  102. }
  103. return entry;
  104. }
  105. entry = entry->next;
  106. }
  107. /* Does not exist, add a new entry */
  108. if (accel_hash->num_entries == accel_hash->max_num_entries) {
  109. return NULL;
  110. }
  111. entry = &accel_hash->hash_entries[accel_hash->num_entries++];
  112. if (indirect) {
  113. entry->data = indirect_bucket;
  114. entry->indirect = 1;
  115. } else {
  116. accel_hash->num_direct_entries++;
  117. entry->data = data;
  118. entry->indirect = 0;
  119. }
  120. entry->hash_value = hash_value;
  121. entry->key = key;
  122. entry->key_length = key_length;
  123. entry->next = accel_hash->hash_table[index];
  124. accel_hash->hash_table[index] = entry;
  125. return entry;
  126. }
  127. static zend_always_inline void* zend_accel_hash_find_ex(zend_accel_hash *accel_hash, const char *key, uint32_t key_length, zend_ulong hash_value, int data)
  128. {
  129. zend_ulong index;
  130. zend_accel_hash_entry *entry;
  131. #ifndef ZEND_WIN32
  132. hash_value ^= ZCG(root_hash);
  133. #endif
  134. index = hash_value % accel_hash->max_num_entries;
  135. entry = accel_hash->hash_table[index];
  136. while (entry) {
  137. if (entry->hash_value == hash_value
  138. && entry->key_length == key_length
  139. && !memcmp(entry->key, key, key_length)) {
  140. if (entry->indirect) {
  141. if (data) {
  142. return ((zend_accel_hash_entry*)entry->data)->data;
  143. } else {
  144. return entry->data;
  145. }
  146. } else {
  147. if (data) {
  148. return entry->data;
  149. } else {
  150. return entry;
  151. }
  152. }
  153. }
  154. entry = entry->next;
  155. }
  156. return NULL;
  157. }
  158. /* Returns the data associated with key on success
  159. * Returns NULL if data doesn't exist
  160. */
  161. void* zend_accel_hash_find(zend_accel_hash *accel_hash, zend_string *key)
  162. {
  163. return zend_accel_hash_find_ex(
  164. accel_hash,
  165. ZSTR_VAL(key),
  166. ZSTR_LEN(key),
  167. zend_string_hash_val(key),
  168. 1);
  169. }
  170. /* Returns the hash entry associated with key on success
  171. * Returns NULL if it doesn't exist
  172. */
  173. zend_accel_hash_entry* zend_accel_hash_find_entry(zend_accel_hash *accel_hash, zend_string *key)
  174. {
  175. return (zend_accel_hash_entry *)zend_accel_hash_find_ex(
  176. accel_hash,
  177. ZSTR_VAL(key),
  178. ZSTR_LEN(key),
  179. zend_string_hash_val(key),
  180. 0);
  181. }
  182. /* Returns the data associated with key on success
  183. * Returns NULL if data doesn't exist
  184. */
  185. void* zend_accel_hash_str_find(zend_accel_hash *accel_hash, const char *key, uint32_t key_length)
  186. {
  187. return zend_accel_hash_find_ex(
  188. accel_hash,
  189. key,
  190. key_length,
  191. zend_inline_hash_func(key, key_length),
  192. 1);
  193. }
  194. /* Returns the hash entry associated with key on success
  195. * Returns NULL if it doesn't exist
  196. */
  197. zend_accel_hash_entry* zend_accel_hash_str_find_entry(zend_accel_hash *accel_hash, const char *key, uint32_t key_length)
  198. {
  199. return (zend_accel_hash_entry *)zend_accel_hash_find_ex(
  200. accel_hash,
  201. key,
  202. key_length,
  203. zend_inline_hash_func(key, key_length),
  204. 0);
  205. }
  206. int zend_accel_hash_unlink(zend_accel_hash *accel_hash, const char *key, uint32_t key_length)
  207. {
  208. zend_ulong hash_value;
  209. zend_ulong index;
  210. zend_accel_hash_entry *entry, *last_entry=NULL;
  211. hash_value = zend_inline_hash_func(key, key_length);
  212. #ifndef ZEND_WIN32
  213. hash_value ^= ZCG(root_hash);
  214. #endif
  215. index = hash_value % accel_hash->max_num_entries;
  216. entry = accel_hash->hash_table[index];
  217. while (entry) {
  218. if (entry->hash_value == hash_value
  219. && entry->key_length == key_length
  220. && !memcmp(entry->key, key, key_length)) {
  221. if (!entry->indirect) {
  222. accel_hash->num_direct_entries--;
  223. }
  224. if (last_entry) {
  225. last_entry->next = entry->next;
  226. } else {
  227. accel_hash->hash_table[index] = entry->next;
  228. }
  229. return SUCCESS;
  230. }
  231. last_entry = entry;
  232. entry = entry->next;
  233. }
  234. return FAILURE;
  235. }