diff options
Diffstat (limited to 'Zend/zend_string.c')
| -rw-r--r-- | Zend/zend_string.c | 289 |
1 files changed, 265 insertions, 24 deletions
diff --git a/Zend/zend_string.c b/Zend/zend_string.c index e4edc92e47..2cfe329fe4 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -21,25 +21,39 @@ #include "zend.h" #include "zend_globals.h" -ZEND_API zend_string *(*zend_new_interned_string)(zend_string *str); +#ifdef HAVE_VALGRIND +# include "valgrind/callgrind.h" +#endif -static zend_string *zend_new_interned_string_permanent(zend_string *str); -static zend_string *zend_new_interned_string_request(zend_string *str); +ZEND_API zend_new_interned_string_func_t zend_new_interned_string; +ZEND_API zend_string_init_interned_func_t zend_string_init_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, int permanent); +static zend_string* ZEND_FASTCALL zend_string_init_interned_request(const char *str, size_t size, int 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 + 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_copy_storage_func_t interned_string_copy_storage = NULL; +static zend_string_copy_storage_func_t interned_string_restore_storage = NULL; 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_hash_func(const char *str, size_t len) +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); } @@ -60,7 +74,7 @@ ZEND_KNOWN_STRINGS(_ZEND_STR_DSC) static void zend_init_interned_strings_ht(HashTable *interned_strings, int permanent) { zend_hash_init(interned_strings, 1024, NULL, _str_dtor, permanent); - zend_hash_real_init(interned_strings, 0); + zend_hash_real_init_mixed(interned_strings); } ZEND_API void zend_interned_strings_init(void) @@ -70,7 +84,9 @@ ZEND_API void zend_interned_strings_init(void) zend_string *str; interned_string_request_handler = zend_new_interned_string_request; + interned_string_init_request_handler = zend_string_init_interned_request; interned_string_copy_storage = NULL; + interned_string_restore_storage = NULL; zend_empty_string = NULL; zend_known_strings = NULL; @@ -78,6 +94,7 @@ ZEND_API void zend_interned_strings_init(void) 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; /* interned empty string */ str = zend_string_alloc(sizeof("")-1, 1); @@ -106,20 +123,18 @@ ZEND_API void zend_interned_strings_dtor(void) zend_known_strings = NULL; } -static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_string *str, HashTable *interned_strings) +static zend_always_inline zend_string *zend_interned_string_ht_lookup_ex(zend_ulong h, const char *str, size_t size, HashTable *interned_strings) { - zend_ulong h; uint32_t nIndex; uint32_t idx; Bucket *p; - h = zend_string_hash_val(str); 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) == ZSTR_LEN(str))) { - if (!memcmp(ZSTR_VAL(p->key), ZSTR_VAL(str), ZSTR_LEN(str))) { + if ((p->h == h) && (ZSTR_LEN(p->key) == size)) { + if (!memcmp(ZSTR_VAL(p->key), str, size)) { return p->key; } } @@ -129,14 +144,34 @@ static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_strin 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_REFCOUNT(str) = 1; - GC_FLAGS(str) |= IS_STR_INTERNED | flags; + GC_SET_REFCOUNT(str, 1); + GC_ADD_FLAGS(str, IS_STR_INTERNED | flags); ZVAL_INTERNED_STR(&val, str); @@ -145,13 +180,13 @@ static zend_always_inline zend_string *zend_add_interned_string(zend_string *str return str; } -ZEND_API zend_string *zend_interned_string_find_permanent(zend_string *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_new_interned_string_permanent(zend_string *str) +static zend_string* ZEND_FASTCALL zend_new_interned_string_permanent(zend_string *str) { zend_string *ret; @@ -159,16 +194,25 @@ static zend_string *zend_new_interned_string_permanent(zend_string *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_new_interned_string_request(zend_string *str) +static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *str) { zend_string *ret; @@ -176,6 +220,8 @@ static zend_string *zend_new_interned_string_request(zend_string *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) { @@ -190,11 +236,57 @@ static zend_string *zend_new_interned_string_request(zend_string *str) } /* Create a short living interned, freed after the request. */ + 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), 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, int 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; + } + + 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_interned_request(const char *str, size_t size, int 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; + } + + 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); +} + ZEND_API void zend_interned_strings_activate(void) { zend_init_interned_strings_ht(&CG(interned_strings), 0); @@ -205,24 +297,173 @@ ZEND_API void zend_interned_strings_deactivate(void) zend_hash_destroy(&CG(interned_strings)); } -ZEND_API void zend_interned_strings_set_request_storage_handler(zend_new_interned_string_func_t handler) +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) { interned_string_request_handler = handler; + interned_string_init_request_handler = init_handler; } -ZEND_API void zend_interned_strings_set_permanent_storage_copy_handler(zend_string_copy_storage_func_t handler) +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) { - interned_string_copy_storage = handler; + interned_string_copy_storage = copy_handler; + interned_string_restore_storage = restore_handler; } -ZEND_API void zend_interned_strings_switch_storage(void) +ZEND_API void zend_interned_strings_switch_storage(zend_bool request) { - if (interned_string_copy_storage) { - interned_string_copy_storage(); + if (request) { + if (interned_string_copy_storage) { + interned_string_copy_storage(); + } + zend_new_interned_string = interned_string_request_handler; + zend_string_init_interned = interned_string_init_request_handler; + } else { + zend_new_interned_string = zend_new_interned_string_permanent; + zend_string_init_interned = zend_string_init_interned_permanent; + if (interned_string_restore_storage) { + interned_string_restore_storage(); + } } - zend_new_interned_string = interned_string_request_handler; } +#if defined(__GNUC__) && defined(__i386__) +ZEND_API zend_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; +} + +#ifdef HAVE_VALGRIND +ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2) +{ + size_t len = ZSTR_LEN(s1); + char *ptr1 = ZSTR_VAL(s1); + char *ptr2 = ZSTR_VAL(s2); + zend_ulong ret; + + __asm__ ( + "test %1, %1\n\t" + "jnz .LL1%=\n\t" + "movl $0x1, %0\n\t" + "jmp .LL2%=\n\t" + ".LL1%=:\n\t" + "cld\n\t" + "rep\n\t" + "cmpsb\n\t" + "sete %b0\n\t" + "movzbl %b0, %0\n\t" + ".LL2%=:\n" + : "=a"(ret), + "+c"(len), + "+D"(ptr1), + "+S"(ptr2) + : + : "cc"); + return ret; +} +#endif + +#elif defined(__GNUC__) && defined(__x86_64__) +ZEND_API zend_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; +} + +#ifdef HAVE_VALGRIND +ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2) +{ + size_t len = ZSTR_LEN(s1); + char *ptr1 = ZSTR_VAL(s1); + char *ptr2 = ZSTR_VAL(s2); + zend_ulong ret; + + __asm__ ( + "test %1, %1\n\t" + "jnz .LL1%=\n\t" + "movq $0x1, %0\n\t" + "jmp .LL2%=\n\t" + ".LL1%=:\n\t" + "cld\n\t" + "rep\n\t" + "cmpsb\n\t" + "sete %b0\n\t" + "movzbq %b0, %0\n\t" + ".LL2%=:\n" + : "=a"(ret), + "+c"(len), + "+D"(ptr1), + "+S"(ptr2) + : + : "cc"); + return ret; +} +#endif + +#endif + /* * Local variables: * tab-width: 4 |
