/src/php-src/ext/opcache/zend_accelerator_hash.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend OPcache | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright © The PHP Group and Contributors. | |
6 | | +----------------------------------------------------------------------+ |
7 | | | This source file is subject to the Modified BSD License that is | |
8 | | | bundled with this package in the file LICENSE, and is available | |
9 | | | through the World Wide Web at <https://www.php.net/license/>. | |
10 | | | | |
11 | | | SPDX-License-Identifier: BSD-3-Clause | |
12 | | +----------------------------------------------------------------------+ |
13 | | | Authors: Andi Gutmans <andi@php.net> | |
14 | | | Zeev Suraski <zeev@php.net> | |
15 | | | Stanislav Malyshev <stas@zend.com> | |
16 | | | Dmitry Stogov <dmitry@php.net> | |
17 | | +----------------------------------------------------------------------+ |
18 | | */ |
19 | | |
20 | | #include "ZendAccelerator.h" |
21 | | #include "zend_accelerator_hash.h" |
22 | | #include "zend_hash.h" |
23 | | #include "zend_shared_alloc.h" |
24 | | |
25 | | /* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */ |
26 | | static const 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 const uint32_t num_prime_numbers = sizeof(prime_numbers) / sizeof(uint32_t); |
29 | | |
30 | | void zend_accel_hash_clean(zend_accel_hash *accel_hash) |
31 | 0 | { |
32 | 0 | accel_hash->num_entries = 0; |
33 | 0 | accel_hash->num_direct_entries = 0; |
34 | 0 | memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); |
35 | 0 | } |
36 | | |
37 | | void zend_accel_hash_init(zend_accel_hash *accel_hash, uint32_t hash_size) |
38 | 16 | { |
39 | 16 | uint32_t i; |
40 | | |
41 | 192 | for (i=0; i<num_prime_numbers; i++) { |
42 | 192 | if (hash_size <= prime_numbers[i]) { |
43 | 16 | hash_size = prime_numbers[i]; |
44 | 16 | break; |
45 | 16 | } |
46 | 192 | } |
47 | | |
48 | 16 | accel_hash->num_entries = 0; |
49 | 16 | accel_hash->num_direct_entries = 0; |
50 | 16 | accel_hash->max_num_entries = hash_size; |
51 | | |
52 | | /* set up hash pointers table */ |
53 | 16 | accel_hash->hash_table = zend_shared_alloc(sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); |
54 | 16 | if (!accel_hash->hash_table) { |
55 | 0 | zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!"); |
56 | 0 | return; |
57 | 0 | } |
58 | | |
59 | | /* set up hash values table */ |
60 | 16 | accel_hash->hash_entries = zend_shared_alloc(sizeof(zend_accel_hash_entry)*accel_hash->max_num_entries); |
61 | 16 | if (!accel_hash->hash_entries) { |
62 | 0 | zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!"); |
63 | 0 | return; |
64 | 0 | } |
65 | 16 | memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); |
66 | 16 | } |
67 | | |
68 | | /* Returns NULL if hash is full |
69 | | * Returns pointer the actual hash entry on success |
70 | | * key needs to be already allocated as it is not copied |
71 | | */ |
72 | | zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, zend_string *key, bool indirect, void *data) |
73 | 49.4k | { |
74 | 49.4k | zend_ulong hash_value; |
75 | 49.4k | zend_ulong index; |
76 | 49.4k | zend_accel_hash_entry *entry; |
77 | 49.4k | zend_accel_hash_entry *indirect_bucket = NULL; |
78 | | |
79 | 49.4k | if (indirect) { |
80 | 0 | indirect_bucket = (zend_accel_hash_entry*)data; |
81 | 0 | while (indirect_bucket->indirect) { |
82 | 0 | indirect_bucket = (zend_accel_hash_entry*)indirect_bucket->data; |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | 49.4k | hash_value = zend_string_hash_val(key); |
87 | 49.4k | #ifndef ZEND_WIN32 |
88 | 49.4k | hash_value ^= ZCG(root_hash); |
89 | 49.4k | #endif |
90 | 49.4k | index = hash_value % accel_hash->max_num_entries; |
91 | | |
92 | | /* try to see if the element already exists in the hash */ |
93 | 49.4k | entry = accel_hash->hash_table[index]; |
94 | 49.4k | while (entry) { |
95 | 49.4k | if (entry->hash_value == hash_value |
96 | 49.4k | && zend_string_equals(entry->key, key)) { |
97 | | |
98 | 49.4k | if (entry->indirect) { |
99 | 0 | if (indirect_bucket) { |
100 | 0 | entry->data = indirect_bucket; |
101 | 0 | } else { |
102 | 0 | ((zend_accel_hash_entry*)entry->data)->data = data; |
103 | 0 | } |
104 | 49.4k | } else { |
105 | 49.4k | if (indirect_bucket) { |
106 | 0 | accel_hash->num_direct_entries--; |
107 | 0 | entry->data = indirect_bucket; |
108 | 0 | entry->indirect = 1; |
109 | 49.4k | } else { |
110 | 49.4k | entry->data = data; |
111 | 49.4k | } |
112 | 49.4k | } |
113 | 49.4k | return entry; |
114 | 49.4k | } |
115 | 0 | entry = entry->next; |
116 | 0 | } |
117 | | |
118 | | /* Does not exist, add a new entry */ |
119 | 3 | if (accel_hash->num_entries == accel_hash->max_num_entries) { |
120 | 0 | return NULL; |
121 | 0 | } |
122 | | |
123 | 3 | entry = &accel_hash->hash_entries[accel_hash->num_entries++]; |
124 | 3 | if (indirect) { |
125 | 0 | entry->data = indirect_bucket; |
126 | 0 | entry->indirect = 1; |
127 | 3 | } else { |
128 | 3 | accel_hash->num_direct_entries++; |
129 | 3 | entry->data = data; |
130 | 3 | entry->indirect = 0; |
131 | 3 | } |
132 | 3 | entry->hash_value = hash_value; |
133 | 3 | entry->key = key; |
134 | 3 | entry->next = accel_hash->hash_table[index]; |
135 | 3 | accel_hash->hash_table[index] = entry; |
136 | 3 | return entry; |
137 | 3 | } |
138 | | |
139 | | static zend_always_inline void* zend_accel_hash_find_ex(const zend_accel_hash *accel_hash, zend_string *key, bool data) |
140 | 360k | { |
141 | 360k | zend_ulong index; |
142 | 360k | zend_accel_hash_entry *entry; |
143 | 360k | zend_ulong hash_value; |
144 | | |
145 | 360k | hash_value = zend_string_hash_val(key); |
146 | 360k | #ifndef ZEND_WIN32 |
147 | 360k | hash_value ^= ZCG(root_hash); |
148 | 360k | #endif |
149 | 360k | index = hash_value % accel_hash->max_num_entries; |
150 | | |
151 | 360k | entry = accel_hash->hash_table[index]; |
152 | 360k | while (entry) { |
153 | 308k | if (entry->hash_value == hash_value |
154 | 308k | && zend_string_equals(entry->key, key)) { |
155 | 308k | if (entry->indirect) { |
156 | 0 | if (data) { |
157 | 0 | return ((zend_accel_hash_entry*)entry->data)->data; |
158 | 0 | } else { |
159 | 0 | return entry->data; |
160 | 0 | } |
161 | 308k | } else { |
162 | 308k | if (data) { |
163 | 259k | return entry->data; |
164 | 259k | } else { |
165 | 49.5k | return entry; |
166 | 49.5k | } |
167 | 308k | } |
168 | 308k | } |
169 | 0 | entry = entry->next; |
170 | 0 | } |
171 | 51.2k | return NULL; |
172 | 360k | } |
173 | | |
174 | | /* Returns the data associated with key on success |
175 | | * Returns NULL if data doesn't exist |
176 | | */ |
177 | | void* zend_accel_hash_find(const zend_accel_hash *accel_hash, zend_string *key) |
178 | 308k | { |
179 | 308k | return zend_accel_hash_find_ex(accel_hash, key, true); |
180 | 308k | } |
181 | | |
182 | | /* Returns the hash entry associated with key on success |
183 | | * Returns NULL if it doesn't exist |
184 | | */ |
185 | | zend_accel_hash_entry* zend_accel_hash_find_entry(const zend_accel_hash *accel_hash, zend_string *key) |
186 | 51.7k | { |
187 | 51.7k | return (zend_accel_hash_entry *)zend_accel_hash_find_ex(accel_hash, key, false); |
188 | 51.7k | } |
189 | | |
190 | | zend_result zend_accel_hash_unlink(zend_accel_hash *accel_hash, zend_string *key) |
191 | 0 | { |
192 | 0 | zend_ulong hash_value; |
193 | 0 | zend_ulong index; |
194 | 0 | zend_accel_hash_entry *entry, *last_entry=NULL; |
195 | |
|
196 | 0 | hash_value = zend_string_hash_val(key); |
197 | 0 | #ifndef ZEND_WIN32 |
198 | 0 | hash_value ^= ZCG(root_hash); |
199 | 0 | #endif |
200 | 0 | index = hash_value % accel_hash->max_num_entries; |
201 | |
|
202 | 0 | entry = accel_hash->hash_table[index]; |
203 | 0 | while (entry) { |
204 | 0 | if (entry->hash_value == hash_value |
205 | 0 | && zend_string_equals(entry->key, key)) { |
206 | 0 | if (!entry->indirect) { |
207 | 0 | accel_hash->num_direct_entries--; |
208 | 0 | } |
209 | 0 | if (last_entry) { |
210 | 0 | last_entry->next = entry->next; |
211 | 0 | } else { |
212 | 0 | accel_hash->hash_table[index] = entry->next; |
213 | 0 | } |
214 | 0 | return SUCCESS; |
215 | 0 | } |
216 | 0 | last_entry = entry; |
217 | 0 | entry = entry->next; |
218 | 0 | } |
219 | 0 | return FAILURE; |
220 | 0 | } |