1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Dmitry Stogov <dmitry@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "zend.h"
20 #include "zend_globals.h"
21
22 #ifdef HAVE_VALGRIND
23 # include "valgrind/callgrind.h"
24 #endif
25
26 ZEND_API zend_new_interned_string_func_t zend_new_interned_string;
27 ZEND_API zend_string_init_interned_func_t zend_string_init_interned;
28
29 static zend_string* ZEND_FASTCALL zend_new_interned_string_permanent(zend_string *str);
30 static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *str);
31 static zend_string* ZEND_FASTCALL zend_string_init_interned_permanent(const char *str, size_t size, int permanent);
32 static zend_string* ZEND_FASTCALL zend_string_init_interned_request(const char *str, size_t size, int permanent);
33
34 /* Any strings interned in the startup phase. Common to all the threads,
35 won't be free'd until process exit. If we want an ability to
36 add permanent strings even after startup, it would be still
37 possible on costs of locking in the thread safe builds. */
38 static HashTable interned_strings_permanent;
39
40 static zend_new_interned_string_func_t interned_string_request_handler = zend_new_interned_string_request;
41 static zend_string_init_interned_func_t interned_string_init_request_handler = zend_string_init_interned_request;
42 static zend_string_copy_storage_func_t interned_string_copy_storage = NULL;
43 static zend_string_copy_storage_func_t interned_string_restore_storage = NULL;
44
45 ZEND_API zend_string *zend_empty_string = NULL;
46 ZEND_API zend_string *zend_one_char_string[256];
47 ZEND_API zend_string **zend_known_strings = NULL;
48
zend_string_hash_func(zend_string * str)49 ZEND_API zend_ulong ZEND_FASTCALL zend_string_hash_func(zend_string *str)
50 {
51 return ZSTR_H(str) = zend_hash_func(ZSTR_VAL(str), ZSTR_LEN(str));
52 }
53
zend_hash_func(const char * str,size_t len)54 ZEND_API zend_ulong ZEND_FASTCALL zend_hash_func(const char *str, size_t len)
55 {
56 return zend_inline_hash_func(str, len);
57 }
58
_str_dtor(zval * zv)59 static void _str_dtor(zval *zv)
60 {
61 zend_string *str = Z_STR_P(zv);
62 pefree(str, GC_FLAGS(str) & IS_STR_PERSISTENT);
63 }
64
65 static const char *known_strings[] = {
66 #define _ZEND_STR_DSC(id, str) str,
67 ZEND_KNOWN_STRINGS(_ZEND_STR_DSC)
68 #undef _ZEND_STR_DSC
69 NULL
70 };
71
zend_init_interned_strings_ht(HashTable * interned_strings,int permanent)72 static void zend_init_interned_strings_ht(HashTable *interned_strings, int permanent)
73 {
74 zend_hash_init(interned_strings, 1024, NULL, _str_dtor, permanent);
75 zend_hash_real_init_mixed(interned_strings);
76 }
77
zend_interned_strings_init(void)78 ZEND_API void zend_interned_strings_init(void)
79 {
80 char s[2];
81 int i;
82 zend_string *str;
83
84 interned_string_request_handler = zend_new_interned_string_request;
85 interned_string_init_request_handler = zend_string_init_interned_request;
86 interned_string_copy_storage = NULL;
87 interned_string_restore_storage = NULL;
88
89 zend_empty_string = NULL;
90 zend_known_strings = NULL;
91
92 zend_init_interned_strings_ht(&interned_strings_permanent, 1);
93
94 zend_new_interned_string = zend_new_interned_string_permanent;
95 zend_string_init_interned = zend_string_init_interned_permanent;
96
97 /* interned empty string */
98 str = zend_string_alloc(sizeof("")-1, 1);
99 ZSTR_VAL(str)[0] = '\000';
100 zend_empty_string = zend_new_interned_string_permanent(str);
101
102 s[1] = 0;
103 for (i = 0; i < 256; i++) {
104 s[0] = i;
105 zend_one_char_string[i] = zend_new_interned_string_permanent(zend_string_init(s, 1, 1));
106 }
107
108 /* known strings */
109 zend_known_strings = pemalloc(sizeof(zend_string*) * ((sizeof(known_strings) / sizeof(known_strings[0]) - 1)), 1);
110 for (i = 0; i < (sizeof(known_strings) / sizeof(known_strings[0])) - 1; i++) {
111 str = zend_string_init(known_strings[i], strlen(known_strings[i]), 1);
112 zend_known_strings[i] = zend_new_interned_string_permanent(str);
113 }
114 }
115
zend_interned_strings_dtor(void)116 ZEND_API void zend_interned_strings_dtor(void)
117 {
118 zend_hash_destroy(&interned_strings_permanent);
119
120 free(zend_known_strings);
121 zend_known_strings = NULL;
122 }
123
zend_interned_string_ht_lookup_ex(zend_ulong h,const char * str,size_t size,HashTable * interned_strings)124 static zend_always_inline zend_string *zend_interned_string_ht_lookup_ex(zend_ulong h, const char *str, size_t size, HashTable *interned_strings)
125 {
126 uint32_t nIndex;
127 uint32_t idx;
128 Bucket *p;
129
130 nIndex = h | interned_strings->nTableMask;
131 idx = HT_HASH(interned_strings, nIndex);
132 while (idx != HT_INVALID_IDX) {
133 p = HT_HASH_TO_BUCKET(interned_strings, idx);
134 if ((p->h == h) && (ZSTR_LEN(p->key) == size)) {
135 if (!memcmp(ZSTR_VAL(p->key), str, size)) {
136 return p->key;
137 }
138 }
139 idx = Z_NEXT(p->val);
140 }
141
142 return NULL;
143 }
144
zend_interned_string_ht_lookup(zend_string * str,HashTable * interned_strings)145 static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_string *str, HashTable *interned_strings)
146 {
147 zend_ulong h = ZSTR_H(str);
148 uint32_t nIndex;
149 uint32_t idx;
150 Bucket *p;
151
152 nIndex = h | interned_strings->nTableMask;
153 idx = HT_HASH(interned_strings, nIndex);
154 while (idx != HT_INVALID_IDX) {
155 p = HT_HASH_TO_BUCKET(interned_strings, idx);
156 if ((p->h == h) && zend_string_equal_content(p->key, str)) {
157 return p->key;
158 }
159 idx = Z_NEXT(p->val);
160 }
161
162 return NULL;
163 }
164
165 /* This function might be not thread safe at least because it would update the
166 hash val in the passed string. Be sure it is called in the appropriate context. */
zend_add_interned_string(zend_string * str,HashTable * interned_strings,uint32_t flags)167 static zend_always_inline zend_string *zend_add_interned_string(zend_string *str, HashTable *interned_strings, uint32_t flags)
168 {
169 zval val;
170
171 GC_SET_REFCOUNT(str, 1);
172 GC_ADD_FLAGS(str, IS_STR_INTERNED | flags);
173
174 ZVAL_INTERNED_STR(&val, str);
175
176 zend_hash_add_new(interned_strings, str, &val);
177
178 return str;
179 }
180
zend_interned_string_find_permanent(zend_string * str)181 ZEND_API zend_string* ZEND_FASTCALL zend_interned_string_find_permanent(zend_string *str)
182 {
183 zend_string_hash_val(str);
184 return zend_interned_string_ht_lookup(str, &interned_strings_permanent);
185 }
186
zend_new_interned_string_permanent(zend_string * str)187 static zend_string* ZEND_FASTCALL zend_new_interned_string_permanent(zend_string *str)
188 {
189 zend_string *ret;
190
191 if (ZSTR_IS_INTERNED(str)) {
192 return str;
193 }
194
195 zend_string_hash_val(str);
196 ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent);
197 if (ret) {
198 zend_string_release(str);
199 return ret;
200 }
201
202 ZEND_ASSERT(GC_FLAGS(str) & GC_PERSISTENT);
203 if (GC_REFCOUNT(str) > 1) {
204 zend_ulong h = ZSTR_H(str);
205 zend_string_delref(str);
206 str = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 1);
207 ZSTR_H(str) = h;
208 }
209
210 return zend_add_interned_string(str, &interned_strings_permanent, IS_STR_PERMANENT);
211 }
212
zend_new_interned_string_request(zend_string * str)213 static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *str)
214 {
215 zend_string *ret;
216
217 if (ZSTR_IS_INTERNED(str)) {
218 return str;
219 }
220
221 zend_string_hash_val(str);
222
223 /* Check for permanent strings, the table is readonly at this point. */
224 ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent);
225 if (ret) {
226 zend_string_release(str);
227 return ret;
228 }
229
230 ret = zend_interned_string_ht_lookup(str, &CG(interned_strings));
231 if (ret) {
232 zend_string_release(str);
233 return ret;
234 }
235
236 /* Create a short living interned, freed after the request. */
237 #if ZEND_RC_DEBUG
238 if (zend_rc_debug) {
239 /* PHP shouldn't create persistent interned string during request,
240 * but at least dl() may do this */
241 ZEND_ASSERT(!(GC_FLAGS(str) & GC_PERSISTENT));
242 }
243 #endif
244 if (GC_REFCOUNT(str) > 1) {
245 zend_ulong h = ZSTR_H(str);
246 zend_string_delref(str);
247 str = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
248 ZSTR_H(str) = h;
249 }
250
251 ret = zend_add_interned_string(str, &CG(interned_strings), 0);
252
253 return ret;
254 }
255
zend_string_init_interned_permanent(const char * str,size_t size,int permanent)256 static zend_string* ZEND_FASTCALL zend_string_init_interned_permanent(const char *str, size_t size, int permanent)
257 {
258 zend_string *ret;
259 zend_ulong h = zend_inline_hash_func(str, size);
260
261 ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent);
262 if (ret) {
263 return ret;
264 }
265
266 ZEND_ASSERT(permanent);
267 ret = zend_string_init(str, size, permanent);
268 ZSTR_H(ret) = h;
269 return zend_add_interned_string(ret, &interned_strings_permanent, IS_STR_PERMANENT);
270 }
271
zend_string_init_interned_request(const char * str,size_t size,int permanent)272 static zend_string* ZEND_FASTCALL zend_string_init_interned_request(const char *str, size_t size, int permanent)
273 {
274 zend_string *ret;
275 zend_ulong h = zend_inline_hash_func(str, size);
276
277 /* Check for permanent strings, the table is readonly at this point. */
278 ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent);
279 if (ret) {
280 return ret;
281 }
282
283 ret = zend_interned_string_ht_lookup_ex(h, str, size, &CG(interned_strings));
284 if (ret) {
285 return ret;
286 }
287
288 #if ZEND_RC_DEBUG
289 if (zend_rc_debug) {
290 /* PHP shouldn't create persistent interned string during request,
291 * but at least dl() may do this */
292 ZEND_ASSERT(!permanent);
293 }
294 #endif
295 ret = zend_string_init(str, size, permanent);
296 ZSTR_H(ret) = h;
297
298 /* Create a short living interned, freed after the request. */
299 return zend_add_interned_string(ret, &CG(interned_strings), 0);
300 }
301
zend_interned_strings_activate(void)302 ZEND_API void zend_interned_strings_activate(void)
303 {
304 zend_init_interned_strings_ht(&CG(interned_strings), 0);
305 }
306
zend_interned_strings_deactivate(void)307 ZEND_API void zend_interned_strings_deactivate(void)
308 {
309 zend_hash_destroy(&CG(interned_strings));
310 }
311
zend_interned_strings_set_request_storage_handlers(zend_new_interned_string_func_t handler,zend_string_init_interned_func_t init_handler)312 ZEND_API void zend_interned_strings_set_request_storage_handlers(zend_new_interned_string_func_t handler, zend_string_init_interned_func_t init_handler)
313 {
314 interned_string_request_handler = handler;
315 interned_string_init_request_handler = init_handler;
316 }
317
zend_interned_strings_set_permanent_storage_copy_handlers(zend_string_copy_storage_func_t copy_handler,zend_string_copy_storage_func_t restore_handler)318 ZEND_API void zend_interned_strings_set_permanent_storage_copy_handlers(zend_string_copy_storage_func_t copy_handler, zend_string_copy_storage_func_t restore_handler)
319 {
320 interned_string_copy_storage = copy_handler;
321 interned_string_restore_storage = restore_handler;
322 }
323
zend_interned_strings_switch_storage(zend_bool request)324 ZEND_API void zend_interned_strings_switch_storage(zend_bool request)
325 {
326 if (request) {
327 if (interned_string_copy_storage) {
328 interned_string_copy_storage();
329 }
330 zend_new_interned_string = interned_string_request_handler;
331 zend_string_init_interned = interned_string_init_request_handler;
332 } else {
333 zend_new_interned_string = zend_new_interned_string_permanent;
334 zend_string_init_interned = zend_string_init_interned_permanent;
335 if (interned_string_restore_storage) {
336 interned_string_restore_storage();
337 }
338 }
339 }
340
341 #if defined(__GNUC__) && defined(__i386__)
zend_string_equal_val(zend_string * s1,zend_string * s2)342 ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2)
343 {
344 char *ptr = ZSTR_VAL(s1);
345 size_t delta = (char*)s2 - (char*)s1;
346 size_t len = ZSTR_LEN(s1);
347 zend_ulong ret;
348
349 __asm__ (
350 ".LL0%=:\n\t"
351 "movl (%2,%3), %0\n\t"
352 "xorl (%2), %0\n\t"
353 "jne .LL1%=\n\t"
354 "addl $0x4, %2\n\t"
355 "subl $0x4, %1\n\t"
356 "ja .LL0%=\n\t"
357 "movl $0x1, %0\n\t"
358 "jmp .LL3%=\n\t"
359 ".LL1%=:\n\t"
360 "cmpl $0x4,%1\n\t"
361 "jb .LL2%=\n\t"
362 "xorl %0, %0\n\t"
363 "jmp .LL3%=\n\t"
364 ".LL2%=:\n\t"
365 "negl %1\n\t"
366 "lea 0x20(,%1,8), %1\n\t"
367 "shll %b1, %0\n\t"
368 "sete %b0\n\t"
369 "movzbl %b0, %0\n\t"
370 ".LL3%=:\n"
371 : "=&a"(ret),
372 "+c"(len),
373 "+r"(ptr)
374 : "r"(delta)
375 : "cc");
376 return ret;
377 }
378
379 #ifdef HAVE_VALGRIND
I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)380 ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2)
381 {
382 size_t len = ZSTR_LEN(s1);
383 char *ptr1 = ZSTR_VAL(s1);
384 char *ptr2 = ZSTR_VAL(s2);
385 zend_ulong ret;
386
387 __asm__ (
388 "test %1, %1\n\t"
389 "jnz .LL1%=\n\t"
390 "movl $0x1, %0\n\t"
391 "jmp .LL2%=\n\t"
392 ".LL1%=:\n\t"
393 "cld\n\t"
394 "rep\n\t"
395 "cmpsb\n\t"
396 "sete %b0\n\t"
397 "movzbl %b0, %0\n\t"
398 ".LL2%=:\n"
399 : "=a"(ret),
400 "+c"(len),
401 "+D"(ptr1),
402 "+S"(ptr2)
403 :
404 : "cc");
405 return ret;
406 }
407 #endif
408
409 #elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__)
zend_string_equal_val(zend_string * s1,zend_string * s2)410 ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2)
411 {
412 char *ptr = ZSTR_VAL(s1);
413 size_t delta = (char*)s2 - (char*)s1;
414 size_t len = ZSTR_LEN(s1);
415 zend_ulong ret;
416
417 __asm__ (
418 ".LL0%=:\n\t"
419 "movq (%2,%3), %0\n\t"
420 "xorq (%2), %0\n\t"
421 "jne .LL1%=\n\t"
422 "addq $0x8, %2\n\t"
423 "subq $0x8, %1\n\t"
424 "ja .LL0%=\n\t"
425 "movq $0x1, %0\n\t"
426 "jmp .LL3%=\n\t"
427 ".LL1%=:\n\t"
428 "cmpq $0x8,%1\n\t"
429 "jb .LL2%=\n\t"
430 "xorq %0, %0\n\t"
431 "jmp .LL3%=\n\t"
432 ".LL2%=:\n\t"
433 "negq %1\n\t"
434 "lea 0x40(,%1,8), %1\n\t"
435 "shlq %b1, %0\n\t"
436 "sete %b0\n\t"
437 "movzbq %b0, %0\n\t"
438 ".LL3%=:\n"
439 : "=&a"(ret),
440 "+c"(len),
441 "+r"(ptr)
442 : "r"(delta)
443 : "cc");
444 return ret;
445 }
446
447 #ifdef HAVE_VALGRIND
I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)448 ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2)
449 {
450 size_t len = ZSTR_LEN(s1);
451 char *ptr1 = ZSTR_VAL(s1);
452 char *ptr2 = ZSTR_VAL(s2);
453 zend_ulong ret;
454
455 __asm__ (
456 "test %1, %1\n\t"
457 "jnz .LL1%=\n\t"
458 "movq $0x1, %0\n\t"
459 "jmp .LL2%=\n\t"
460 ".LL1%=:\n\t"
461 "cld\n\t"
462 "rep\n\t"
463 "cmpsb\n\t"
464 "sete %b0\n\t"
465 "movzbq %b0, %0\n\t"
466 ".LL2%=:\n"
467 : "=a"(ret),
468 "+c"(len),
469 "+D"(ptr1),
470 "+S"(ptr2)
471 :
472 : "cc");
473 return ret;
474 }
475 #endif
476
477 #endif
478
479 /*
480 * Local variables:
481 * tab-width: 4
482 * c-basic-offset: 4
483 * indent-tabs-mode: t
484 * End:
485 * vim600: sw=4 ts=4 fdm=marker
486 * vim<600: sw=4 ts=4
487 */
488