/src/php-src/ext/opcache/zend_accelerator_hash.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend OPcache | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright (c) 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 | | | https://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 | | |
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 const uint32_t prime_numbers[] = |
29 | | {5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 }; |
30 | | static const uint32_t num_prime_numbers = sizeof(prime_numbers) / sizeof(uint32_t); |
31 | | |
32 | | void zend_accel_hash_clean(zend_accel_hash *accel_hash) |
33 | 0 | { |
34 | 0 | accel_hash->num_entries = 0; |
35 | 0 | accel_hash->num_direct_entries = 0; |
36 | 0 | memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); |
37 | 0 | } |
38 | | |
39 | | void zend_accel_hash_init(zend_accel_hash *accel_hash, uint32_t hash_size) |
40 | 16 | { |
41 | 16 | uint32_t i; |
42 | | |
43 | 192 | for (i=0; i<num_prime_numbers; i++) { |
44 | 192 | if (hash_size <= prime_numbers[i]) { |
45 | 16 | hash_size = prime_numbers[i]; |
46 | 16 | break; |
47 | 16 | } |
48 | 192 | } |
49 | | |
50 | 16 | accel_hash->num_entries = 0; |
51 | 16 | accel_hash->num_direct_entries = 0; |
52 | 16 | accel_hash->max_num_entries = hash_size; |
53 | | |
54 | | /* set up hash pointers table */ |
55 | 16 | accel_hash->hash_table = zend_shared_alloc(sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); |
56 | 16 | if (!accel_hash->hash_table) { |
57 | 0 | zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!"); |
58 | 0 | return; |
59 | 0 | } |
60 | | |
61 | | /* set up hash values table */ |
62 | 16 | accel_hash->hash_entries = zend_shared_alloc(sizeof(zend_accel_hash_entry)*accel_hash->max_num_entries); |
63 | 16 | if (!accel_hash->hash_entries) { |
64 | 0 | zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!"); |
65 | 0 | return; |
66 | 0 | } |
67 | 16 | memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); |
68 | 16 | } |
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 | | */ |
74 | | zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, zend_string *key, bool indirect, void *data) |
75 | 60.4k | { |
76 | 60.4k | zend_ulong hash_value; |
77 | 60.4k | zend_ulong index; |
78 | 60.4k | zend_accel_hash_entry *entry; |
79 | 60.4k | zend_accel_hash_entry *indirect_bucket = NULL; |
80 | | |
81 | 60.4k | if (indirect) { |
82 | 0 | indirect_bucket = (zend_accel_hash_entry*)data; |
83 | 0 | while (indirect_bucket->indirect) { |
84 | 0 | indirect_bucket = (zend_accel_hash_entry*)indirect_bucket->data; |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | 60.4k | hash_value = zend_string_hash_val(key); |
89 | 60.4k | #ifndef ZEND_WIN32 |
90 | 60.4k | hash_value ^= ZCG(root_hash); |
91 | 60.4k | #endif |
92 | 60.4k | index = hash_value % accel_hash->max_num_entries; |
93 | | |
94 | | /* try to see if the element already exists in the hash */ |
95 | 60.4k | entry = accel_hash->hash_table[index]; |
96 | 60.4k | while (entry) { |
97 | 60.4k | if (entry->hash_value == hash_value |
98 | 60.4k | && zend_string_equals(entry->key, key)) { |
99 | | |
100 | 60.4k | if (entry->indirect) { |
101 | 0 | if (indirect_bucket) { |
102 | 0 | entry->data = indirect_bucket; |
103 | 0 | } else { |
104 | 0 | ((zend_accel_hash_entry*)entry->data)->data = data; |
105 | 0 | } |
106 | 60.4k | } else { |
107 | 60.4k | if (indirect_bucket) { |
108 | 0 | accel_hash->num_direct_entries--; |
109 | 0 | entry->data = indirect_bucket; |
110 | 0 | entry->indirect = 1; |
111 | 60.4k | } else { |
112 | 60.4k | entry->data = data; |
113 | 60.4k | } |
114 | 60.4k | } |
115 | 60.4k | return entry; |
116 | 60.4k | } |
117 | 0 | entry = entry->next; |
118 | 0 | } |
119 | | |
120 | | /* Does not exist, add a new entry */ |
121 | 3 | if (accel_hash->num_entries == accel_hash->max_num_entries) { |
122 | 0 | return NULL; |
123 | 0 | } |
124 | | |
125 | 3 | entry = &accel_hash->hash_entries[accel_hash->num_entries++]; |
126 | 3 | if (indirect) { |
127 | 0 | entry->data = indirect_bucket; |
128 | 0 | entry->indirect = 1; |
129 | 3 | } else { |
130 | 3 | accel_hash->num_direct_entries++; |
131 | 3 | entry->data = data; |
132 | 3 | entry->indirect = 0; |
133 | 3 | } |
134 | 3 | entry->hash_value = hash_value; |
135 | 3 | entry->key = key; |
136 | 3 | entry->next = accel_hash->hash_table[index]; |
137 | 3 | accel_hash->hash_table[index] = entry; |
138 | 3 | return entry; |
139 | 3 | } |
140 | | |
141 | | static zend_always_inline void* zend_accel_hash_find_ex(zend_accel_hash *accel_hash, zend_string *key, int data) |
142 | 378k | { |
143 | 378k | zend_ulong index; |
144 | 378k | zend_accel_hash_entry *entry; |
145 | 378k | zend_ulong hash_value; |
146 | | |
147 | 378k | hash_value = zend_string_hash_val(key); |
148 | 378k | #ifndef ZEND_WIN32 |
149 | 378k | hash_value ^= ZCG(root_hash); |
150 | 378k | #endif |
151 | 378k | index = hash_value % accel_hash->max_num_entries; |
152 | | |
153 | 378k | entry = accel_hash->hash_table[index]; |
154 | 378k | while (entry) { |
155 | 316k | if (entry->hash_value == hash_value |
156 | 316k | && zend_string_equals(entry->key, key)) { |
157 | 316k | if (entry->indirect) { |
158 | 0 | if (data) { |
159 | 0 | return ((zend_accel_hash_entry*)entry->data)->data; |
160 | 0 | } else { |
161 | 0 | return entry->data; |
162 | 0 | } |
163 | 316k | } else { |
164 | 316k | if (data) { |
165 | 255k | return entry->data; |
166 | 255k | } else { |
167 | 60.4k | return entry; |
168 | 60.4k | } |
169 | 316k | } |
170 | 316k | } |
171 | 0 | entry = entry->next; |
172 | 0 | } |
173 | 61.8k | return NULL; |
174 | 378k | } |
175 | | |
176 | | /* Returns the data associated with key on success |
177 | | * Returns NULL if data doesn't exist |
178 | | */ |
179 | | void* zend_accel_hash_find(zend_accel_hash *accel_hash, zend_string *key) |
180 | 315k | { |
181 | 315k | return zend_accel_hash_find_ex(accel_hash, key, 1); |
182 | 315k | } |
183 | | |
184 | | /* Returns the hash entry associated with key on success |
185 | | * Returns NULL if it doesn't exist |
186 | | */ |
187 | | zend_accel_hash_entry* zend_accel_hash_find_entry(zend_accel_hash *accel_hash, zend_string *key) |
188 | 62.7k | { |
189 | 62.7k | return (zend_accel_hash_entry *)zend_accel_hash_find_ex(accel_hash, key, 0); |
190 | 62.7k | } |
191 | | |
192 | | int zend_accel_hash_unlink(zend_accel_hash *accel_hash, zend_string *key) |
193 | 0 | { |
194 | 0 | zend_ulong hash_value; |
195 | 0 | zend_ulong index; |
196 | 0 | zend_accel_hash_entry *entry, *last_entry=NULL; |
197 | |
|
198 | 0 | hash_value = zend_string_hash_val(key); |
199 | 0 | #ifndef ZEND_WIN32 |
200 | 0 | hash_value ^= ZCG(root_hash); |
201 | 0 | #endif |
202 | 0 | index = hash_value % accel_hash->max_num_entries; |
203 | |
|
204 | 0 | entry = accel_hash->hash_table[index]; |
205 | 0 | while (entry) { |
206 | 0 | if (entry->hash_value == hash_value |
207 | 0 | && zend_string_equals(entry->key, key)) { |
208 | 0 | if (!entry->indirect) { |
209 | 0 | accel_hash->num_direct_entries--; |
210 | 0 | } |
211 | 0 | if (last_entry) { |
212 | 0 | last_entry->next = entry->next; |
213 | 0 | } else { |
214 | 0 | accel_hash->hash_table[index] = entry->next; |
215 | 0 | } |
216 | 0 | return SUCCESS; |
217 | 0 | } |
218 | 0 | last_entry = entry; |
219 | 0 | entry = entry->next; |
220 | 0 | } |
221 | 0 | return FAILURE; |
222 | 0 | } |