123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- /*
- +----------------------------------------------------------------------+
- | Zend Engine |
- +----------------------------------------------------------------------+
- | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
- +----------------------------------------------------------------------+
- | This source file is subject to version 2.00 of the Zend license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.zend.com/license/2_00.txt. |
- | If you did not receive a copy of the Zend license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@zend.com so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Dmitry Stogov <dmitry@php.net> |
- +----------------------------------------------------------------------+
- */
- #include "zend.h"
- #include "zend_globals.h"
- #ifdef HAVE_VALGRIND
- # include "valgrind/callgrind.h"
- #endif
- ZEND_API zend_new_interned_string_func_t zend_new_interned_string;
- ZEND_API zend_string_init_interned_func_t zend_string_init_interned;
- ZEND_API zend_string_init_existing_interned_func_t zend_string_init_existing_interned;
- static zend_string* ZEND_FASTCALL zend_new_interned_string_permanent(zend_string *str);
- static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *str);
- static zend_string* ZEND_FASTCALL zend_string_init_interned_permanent(const char *str, size_t size, bool permanent);
- static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_permanent(const char *str, size_t size, bool permanent);
- static zend_string* ZEND_FASTCALL zend_string_init_interned_request(const char *str, size_t size, bool permanent);
- static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_request(const char *str, size_t size, bool permanent);
- /* Any strings interned in the startup phase. Common to all the threads,
- won't be free'd until process exit. If we want an ability to
- add permanent strings even after startup, it would be still
- possible on costs of locking in the thread safe builds. */
- static HashTable interned_strings_permanent;
- static zend_new_interned_string_func_t interned_string_request_handler = zend_new_interned_string_request;
- static zend_string_init_interned_func_t interned_string_init_request_handler = zend_string_init_interned_request;
- static zend_string_init_existing_interned_func_t interned_string_init_existing_request_handler = zend_string_init_existing_interned_request;
- ZEND_API zend_string *zend_empty_string = NULL;
- ZEND_API zend_string *zend_one_char_string[256];
- ZEND_API zend_string **zend_known_strings = NULL;
- ZEND_API zend_ulong ZEND_FASTCALL zend_string_hash_func(zend_string *str)
- {
- return ZSTR_H(str) = zend_hash_func(ZSTR_VAL(str), ZSTR_LEN(str));
- }
- ZEND_API zend_ulong ZEND_FASTCALL zend_hash_func(const char *str, size_t len)
- {
- return zend_inline_hash_func(str, len);
- }
- static void _str_dtor(zval *zv)
- {
- zend_string *str = Z_STR_P(zv);
- pefree(str, GC_FLAGS(str) & IS_STR_PERSISTENT);
- }
- static const char *known_strings[] = {
- #define _ZEND_STR_DSC(id, str) str,
- ZEND_KNOWN_STRINGS(_ZEND_STR_DSC)
- #undef _ZEND_STR_DSC
- NULL
- };
- static zend_always_inline void zend_init_interned_strings_ht(HashTable *interned_strings, bool permanent)
- {
- zend_hash_init(interned_strings, 1024, NULL, _str_dtor, permanent);
- if (permanent) {
- zend_hash_real_init_mixed(interned_strings);
- }
- }
- ZEND_API void zend_interned_strings_init(void)
- {
- char s[2];
- unsigned int i;
- zend_string *str;
- interned_string_request_handler = zend_new_interned_string_request;
- interned_string_init_request_handler = zend_string_init_interned_request;
- interned_string_init_existing_request_handler = zend_string_init_existing_interned_request;
- zend_empty_string = NULL;
- zend_known_strings = NULL;
- zend_init_interned_strings_ht(&interned_strings_permanent, 1);
- zend_new_interned_string = zend_new_interned_string_permanent;
- zend_string_init_interned = zend_string_init_interned_permanent;
- zend_string_init_existing_interned = zend_string_init_existing_interned_permanent;
- /* interned empty string */
- str = zend_string_alloc(sizeof("")-1, 1);
- ZSTR_VAL(str)[0] = '\000';
- zend_empty_string = zend_new_interned_string_permanent(str);
- s[1] = 0;
- for (i = 0; i < 256; i++) {
- s[0] = i;
- zend_one_char_string[i] = zend_new_interned_string_permanent(zend_string_init(s, 1, 1));
- }
- /* known strings */
- zend_known_strings = pemalloc(sizeof(zend_string*) * ((sizeof(known_strings) / sizeof(known_strings[0]) - 1)), 1);
- for (i = 0; i < (sizeof(known_strings) / sizeof(known_strings[0])) - 1; i++) {
- str = zend_string_init(known_strings[i], strlen(known_strings[i]), 1);
- zend_known_strings[i] = zend_new_interned_string_permanent(str);
- }
- }
- ZEND_API void zend_interned_strings_dtor(void)
- {
- zend_hash_destroy(&interned_strings_permanent);
- free(zend_known_strings);
- zend_known_strings = NULL;
- }
- static zend_always_inline zend_string *zend_interned_string_ht_lookup_ex(zend_ulong h, const char *str, size_t size, HashTable *interned_strings)
- {
- uint32_t nIndex;
- uint32_t idx;
- Bucket *p;
- nIndex = h | interned_strings->nTableMask;
- idx = HT_HASH(interned_strings, nIndex);
- while (idx != HT_INVALID_IDX) {
- p = HT_HASH_TO_BUCKET(interned_strings, idx);
- if ((p->h == h) && (ZSTR_LEN(p->key) == size)) {
- if (!memcmp(ZSTR_VAL(p->key), str, size)) {
- return p->key;
- }
- }
- idx = Z_NEXT(p->val);
- }
- return NULL;
- }
- static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_string *str, HashTable *interned_strings)
- {
- zend_ulong h = ZSTR_H(str);
- uint32_t nIndex;
- uint32_t idx;
- Bucket *p;
- nIndex = h | interned_strings->nTableMask;
- idx = HT_HASH(interned_strings, nIndex);
- while (idx != HT_INVALID_IDX) {
- p = HT_HASH_TO_BUCKET(interned_strings, idx);
- if ((p->h == h) && zend_string_equal_content(p->key, str)) {
- return p->key;
- }
- idx = Z_NEXT(p->val);
- }
- return NULL;
- }
- /* This function might be not thread safe at least because it would update the
- hash val in the passed string. Be sure it is called in the appropriate context. */
- static zend_always_inline zend_string *zend_add_interned_string(zend_string *str, HashTable *interned_strings, uint32_t flags)
- {
- zval val;
- GC_SET_REFCOUNT(str, 1);
- GC_ADD_FLAGS(str, IS_STR_INTERNED | flags);
- ZVAL_INTERNED_STR(&val, str);
- zend_hash_add_new(interned_strings, str, &val);
- return str;
- }
- ZEND_API zend_string* ZEND_FASTCALL zend_interned_string_find_permanent(zend_string *str)
- {
- zend_string_hash_val(str);
- return zend_interned_string_ht_lookup(str, &interned_strings_permanent);
- }
- static zend_string* ZEND_FASTCALL zend_new_interned_string_permanent(zend_string *str)
- {
- zend_string *ret;
- if (ZSTR_IS_INTERNED(str)) {
- return str;
- }
- zend_string_hash_val(str);
- ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent);
- if (ret) {
- zend_string_release(str);
- return ret;
- }
- ZEND_ASSERT(GC_FLAGS(str) & GC_PERSISTENT);
- if (GC_REFCOUNT(str) > 1) {
- zend_ulong h = ZSTR_H(str);
- zend_string_delref(str);
- str = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 1);
- ZSTR_H(str) = h;
- }
- return zend_add_interned_string(str, &interned_strings_permanent, IS_STR_PERMANENT);
- }
- static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *str)
- {
- zend_string *ret;
- if (ZSTR_IS_INTERNED(str)) {
- return str;
- }
- zend_string_hash_val(str);
- /* Check for permanent strings, the table is readonly at this point. */
- ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent);
- if (ret) {
- zend_string_release(str);
- return ret;
- }
- ret = zend_interned_string_ht_lookup(str, &CG(interned_strings));
- if (ret) {
- zend_string_release(str);
- return ret;
- }
- /* Create a short living interned, freed after the request. */
- #if ZEND_RC_DEBUG
- if (zend_rc_debug) {
- /* PHP shouldn't create persistent interned string during request,
- * but at least dl() may do this */
- ZEND_ASSERT(!(GC_FLAGS(str) & GC_PERSISTENT));
- }
- #endif
- if (GC_REFCOUNT(str) > 1) {
- zend_ulong h = ZSTR_H(str);
- zend_string_delref(str);
- str = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), 0);
- ZSTR_H(str) = h;
- }
- ret = zend_add_interned_string(str, &CG(interned_strings), 0);
- return ret;
- }
- static zend_string* ZEND_FASTCALL zend_string_init_interned_permanent(const char *str, size_t size, bool permanent)
- {
- zend_string *ret;
- zend_ulong h = zend_inline_hash_func(str, size);
- ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent);
- if (ret) {
- return ret;
- }
- ZEND_ASSERT(permanent);
- ret = zend_string_init(str, size, permanent);
- ZSTR_H(ret) = h;
- return zend_add_interned_string(ret, &interned_strings_permanent, IS_STR_PERMANENT);
- }
- static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_permanent(const char *str, size_t size, bool permanent)
- {
- zend_ulong h = zend_inline_hash_func(str, size);
- zend_string *ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent);
- if (ret) {
- return ret;
- }
- ZEND_ASSERT(permanent);
- ret = zend_string_init(str, size, permanent);
- ZSTR_H(ret) = h;
- return ret;
- }
- static zend_string* ZEND_FASTCALL zend_string_init_interned_request(const char *str, size_t size, bool permanent)
- {
- zend_string *ret;
- zend_ulong h = zend_inline_hash_func(str, size);
- /* Check for permanent strings, the table is readonly at this point. */
- ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent);
- if (ret) {
- return ret;
- }
- ret = zend_interned_string_ht_lookup_ex(h, str, size, &CG(interned_strings));
- if (ret) {
- return ret;
- }
- #if ZEND_RC_DEBUG
- if (zend_rc_debug) {
- /* PHP shouldn't create persistent interned string during request,
- * but at least dl() may do this */
- ZEND_ASSERT(!permanent);
- }
- #endif
- ret = zend_string_init(str, size, permanent);
- ZSTR_H(ret) = h;
- /* Create a short living interned, freed after the request. */
- return zend_add_interned_string(ret, &CG(interned_strings), 0);
- }
- static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_request(const char *str, size_t size, bool permanent)
- {
- zend_ulong h = zend_inline_hash_func(str, size);
- zend_string *ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent);
- if (ret) {
- return ret;
- }
- ret = zend_interned_string_ht_lookup_ex(h, str, size, &CG(interned_strings));
- if (ret) {
- return ret;
- }
- ZEND_ASSERT(!permanent);
- ret = zend_string_init(str, size, permanent);
- ZSTR_H(ret) = h;
- return ret;
- }
- ZEND_API void zend_interned_strings_activate(void)
- {
- zend_init_interned_strings_ht(&CG(interned_strings), 0);
- }
- ZEND_API void zend_interned_strings_deactivate(void)
- {
- zend_hash_destroy(&CG(interned_strings));
- }
- 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, zend_string_init_existing_interned_func_t init_existing_handler)
- {
- interned_string_request_handler = handler;
- interned_string_init_request_handler = init_handler;
- interned_string_init_existing_request_handler = init_existing_handler;
- }
- ZEND_API void zend_interned_strings_switch_storage(bool request)
- {
- if (request) {
- zend_new_interned_string = interned_string_request_handler;
- zend_string_init_interned = interned_string_init_request_handler;
- zend_string_init_existing_interned = interned_string_init_existing_request_handler;
- } else {
- zend_new_interned_string = zend_new_interned_string_permanent;
- zend_string_init_interned = zend_string_init_interned_permanent;
- zend_string_init_existing_interned = zend_string_init_existing_interned_permanent;
- }
- }
- /* Even if we don't build with valgrind support, include the symbol so that valgrind available
- * only at runtime will not result in false positives. */
- #ifndef I_REPLACE_SONAME_FNNAME_ZU
- # define I_REPLACE_SONAME_FNNAME_ZU(soname, fnname) _vgr00000ZU_ ## soname ## _ ## fnname
- #endif
- ZEND_API bool ZEND_FASTCALL I_REPLACE_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2)
- {
- return !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1));
- }
- #if defined(__GNUC__) && defined(__i386__)
- ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2)
- {
- char *ptr = ZSTR_VAL(s1);
- size_t delta = (char*)s2 - (char*)s1;
- size_t len = ZSTR_LEN(s1);
- zend_ulong ret;
- __asm__ (
- ".LL0%=:\n\t"
- "movl (%2,%3), %0\n\t"
- "xorl (%2), %0\n\t"
- "jne .LL1%=\n\t"
- "addl $0x4, %2\n\t"
- "subl $0x4, %1\n\t"
- "ja .LL0%=\n\t"
- "movl $0x1, %0\n\t"
- "jmp .LL3%=\n\t"
- ".LL1%=:\n\t"
- "cmpl $0x4,%1\n\t"
- "jb .LL2%=\n\t"
- "xorl %0, %0\n\t"
- "jmp .LL3%=\n\t"
- ".LL2%=:\n\t"
- "negl %1\n\t"
- "lea 0x20(,%1,8), %1\n\t"
- "shll %b1, %0\n\t"
- "sete %b0\n\t"
- "movzbl %b0, %0\n\t"
- ".LL3%=:\n"
- : "=&a"(ret),
- "+c"(len),
- "+r"(ptr)
- : "r"(delta)
- : "cc");
- return ret;
- }
- #elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__)
- ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2)
- {
- char *ptr = ZSTR_VAL(s1);
- size_t delta = (char*)s2 - (char*)s1;
- size_t len = ZSTR_LEN(s1);
- zend_ulong ret;
- __asm__ (
- ".LL0%=:\n\t"
- "movq (%2,%3), %0\n\t"
- "xorq (%2), %0\n\t"
- "jne .LL1%=\n\t"
- "addq $0x8, %2\n\t"
- "subq $0x8, %1\n\t"
- "ja .LL0%=\n\t"
- "movq $0x1, %0\n\t"
- "jmp .LL3%=\n\t"
- ".LL1%=:\n\t"
- "cmpq $0x8,%1\n\t"
- "jb .LL2%=\n\t"
- "xorq %0, %0\n\t"
- "jmp .LL3%=\n\t"
- ".LL2%=:\n\t"
- "negq %1\n\t"
- "lea 0x40(,%1,8), %1\n\t"
- "shlq %b1, %0\n\t"
- "sete %b0\n\t"
- "movzbq %b0, %0\n\t"
- ".LL3%=:\n"
- : "=&a"(ret),
- "+c"(len),
- "+r"(ptr)
- : "r"(delta)
- : "cc");
- return ret;
- }
- #endif
- ZEND_API zend_string *zend_string_concat2(
- const char *str1, size_t str1_len,
- const char *str2, size_t str2_len)
- {
- size_t len = str1_len + str2_len;
- zend_string *res = zend_string_alloc(len, 0);
- memcpy(ZSTR_VAL(res), str1, str1_len);
- memcpy(ZSTR_VAL(res) + str1_len, str2, str2_len);
- ZSTR_VAL(res)[len] = '\0';
- return res;
- }
- ZEND_API zend_string *zend_string_concat3(
- const char *str1, size_t str1_len,
- const char *str2, size_t str2_len,
- const char *str3, size_t str3_len)
- {
- size_t len = str1_len + str2_len + str3_len;
- zend_string *res = zend_string_alloc(len, 0);
- memcpy(ZSTR_VAL(res), str1, str1_len);
- memcpy(ZSTR_VAL(res) + str1_len, str2, str2_len);
- memcpy(ZSTR_VAL(res) + str1_len + str2_len, str3, str3_len);
- ZSTR_VAL(res)[len] = '\0';
- return res;
- }
|