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