diff options
Diffstat (limited to 'Zend/zend_string.c')
-rw-r--r-- | Zend/zend_string.c | 286 |
1 files changed, 134 insertions, 152 deletions
diff --git a/Zend/zend_string.c b/Zend/zend_string.c index 1e18ab40e8..e4edc92e47 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -22,48 +22,33 @@ #include "zend_globals.h" ZEND_API zend_string *(*zend_new_interned_string)(zend_string *str); -ZEND_API void (*zend_interned_strings_snapshot)(void); -ZEND_API void (*zend_interned_strings_restore)(void); -static zend_string *zend_new_interned_string_int(zend_string *str); -static void zend_interned_strings_snapshot_int(void); -static void zend_interned_strings_restore_int(void); +static zend_string *zend_new_interned_string_permanent(zend_string *str); +static zend_string *zend_new_interned_string_request(zend_string *str); + +/* 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_copy_storage_func_t interned_string_copy_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) { return zend_inline_hash_func(str, len); } -#ifndef ZTS static void _str_dtor(zval *zv) { zend_string *str = Z_STR_P(zv); pefree(str, GC_FLAGS(str) & IS_STR_PERSISTENT); } -#endif - -/* Readonly, so assigned also per thread. */ -static const zend_string **known_interned_strings = NULL; -static uint32_t known_interned_strings_count = 0; - -ZEND_API uint32_t zend_intern_known_strings(const char **strings, uint32_t count) -{ - uint32_t i, old_count = known_interned_strings_count; - - known_interned_strings = perealloc(known_interned_strings, sizeof(char*) * (old_count + count), 1); - for (i = 0; i < count; i++) { -#ifndef ZTS - zend_string *str = zend_string_init(strings[i], strlen(strings[i]), 1); - known_interned_strings[known_interned_strings_count + i] = - zend_new_interned_string_int(str); -#else - known_interned_strings[known_interned_strings_count + i] = - zend_zts_interned_string_init(strings[i], strlen(strings[i])); -#endif - } - known_interned_strings_count = old_count + count; - return old_count; -} static const char *known_strings[] = { #define _ZEND_STR_DSC(id, str) str, @@ -72,175 +57,170 @@ ZEND_KNOWN_STRINGS(_ZEND_STR_DSC) NULL }; -void zend_known_interned_strings_init(zend_string ***strings, uint32_t *count) +static void zend_init_interned_strings_ht(HashTable *interned_strings, int permanent) { - *strings = (zend_string **)known_interned_strings; - *count = known_interned_strings_count; + zend_hash_init(interned_strings, 1024, NULL, _str_dtor, permanent); + zend_hash_real_init(interned_strings, 0); } -void zend_interned_strings_init(void) +ZEND_API void zend_interned_strings_init(void) { -#ifndef ZTS + char s[2]; + int i; zend_string *str; - zend_hash_init(&CG(interned_strings), 1024, NULL, _str_dtor, 1); + interned_string_request_handler = zend_new_interned_string_request; + interned_string_copy_storage = NULL; + + zend_empty_string = NULL; + zend_known_strings = NULL; - CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize; - HT_SET_DATA_ADDR(&CG(interned_strings), pemalloc(HT_SIZE(&CG(interned_strings)), 1)); - HT_HASH_RESET(&CG(interned_strings)); - CG(interned_strings).u.flags |= HASH_FLAG_INITIALIZED; + zend_init_interned_strings_ht(&interned_strings_permanent, 1); + + zend_new_interned_string = zend_new_interned_string_permanent; /* interned empty string */ str = zend_string_alloc(sizeof("")-1, 1); ZSTR_VAL(str)[0] = '\000'; - CG(empty_string) = zend_new_interned_string_int(str); -#endif + zend_empty_string = zend_new_interned_string_permanent(str); - /* one char strings (the actual interned strings are going to be created by ext/opcache) */ - memset(CG(one_char_string), 0, sizeof(CG(one_char_string))); + 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_intern_known_strings(known_strings, (sizeof(known_strings) / sizeof(known_strings[0])) - 1); - zend_known_interned_strings_init(&CG(known_strings), &CG(known_strings_count)); - - zend_new_interned_string = zend_new_interned_string_int; - zend_interned_strings_snapshot = zend_interned_strings_snapshot_int; - zend_interned_strings_restore = zend_interned_strings_restore_int; + 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); + } } -void zend_interned_strings_dtor(void) +ZEND_API void zend_interned_strings_dtor(void) { -#ifndef ZTS - zend_hash_destroy(&CG(interned_strings)); -#else - uint32_t i; + zend_hash_destroy(&interned_strings_permanent); - for (i = 0; i < CG(known_strings_count); i++) { - zend_zts_interned_string_free(&CG(known_strings)[i]); - } -#endif - free(CG(known_strings)); - CG(known_strings) = NULL; - CG(known_strings_count) = 0; - known_interned_strings = NULL; - known_interned_strings_count = 0; + free(zend_known_strings); + zend_known_strings = NULL; } -static zend_string *zend_new_interned_string_int(zend_string *str) +static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_string *str, HashTable *interned_strings) { -#ifndef ZTS zend_ulong h; - uint nIndex; - uint idx; + uint32_t nIndex; + uint32_t idx; Bucket *p; - if (ZSTR_IS_INTERNED(str)) { - return str; - } - h = zend_string_hash_val(str); - nIndex = h | CG(interned_strings).nTableMask; - idx = HT_HASH(&CG(interned_strings), nIndex); + nIndex = h | interned_strings->nTableMask; + idx = HT_HASH(interned_strings, nIndex); while (idx != HT_INVALID_IDX) { - p = HT_HASH_TO_BUCKET(&CG(interned_strings), 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))) { - zend_string_release(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; - - if (CG(interned_strings).nNumUsed >= CG(interned_strings).nTableSize) { - if (CG(interned_strings).nTableSize < HT_MAX_SIZE) { /* Let's double the table size */ - void *new_data; - void *old_data = HT_GET_DATA_ADDR(&CG(interned_strings)); - Bucket *old_buckets = CG(interned_strings).arData; - - CG(interned_strings).nTableSize += CG(interned_strings).nTableSize; - CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize; - new_data = malloc(HT_SIZE(&CG(interned_strings))); - - if (new_data) { - HT_SET_DATA_ADDR(&CG(interned_strings), new_data); - memcpy(CG(interned_strings).arData, old_buckets, sizeof(Bucket) * CG(interned_strings).nNumUsed); - free(old_data); - zend_hash_rehash(&CG(interned_strings)); - } else { - CG(interned_strings).nTableSize = CG(interned_strings).nTableSize >> 1; - CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize; - } - } - } + GC_FLAGS(str) |= IS_STR_INTERNED | flags; - idx = CG(interned_strings).nNumUsed++; - CG(interned_strings).nNumOfElements++; - p = CG(interned_strings).arData + idx; - p->h = h; - p->key = str; - Z_STR(p->val) = str; - Z_TYPE_INFO(p->val) = IS_INTERNED_STRING_EX; - nIndex = h | CG(interned_strings).nTableMask; - Z_NEXT(p->val) = HT_HASH(&CG(interned_strings), nIndex); - HT_HASH(&CG(interned_strings), nIndex) = HT_IDX_TO_HASH(idx); + ZVAL_INTERNED_STR(&val, str); + + zend_hash_add_new(interned_strings, str, &val); return str; -#else - return str; -#endif } -static void zend_interned_strings_snapshot_int(void) +ZEND_API zend_string *zend_interned_string_find_permanent(zend_string *str) { -#ifndef ZTS - uint idx; - Bucket *p; + return zend_interned_string_ht_lookup(str, &interned_strings_permanent); +} + + +static zend_string *zend_new_interned_string_permanent(zend_string *str) +{ + zend_string *ret; - idx = CG(interned_strings).nNumUsed; - while (idx > 0) { - idx--; - p = CG(interned_strings).arData + idx; - ZEND_ASSERT(GC_FLAGS(p->key) & IS_STR_PERSISTENT); - GC_FLAGS(p->key) |= IS_STR_PERMANENT; + if (ZSTR_IS_INTERNED(str)) { + return str; + } + + ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent); + if (ret) { + zend_string_release(str); + return ret; } -#endif + + return zend_add_interned_string(str, &interned_strings_permanent, IS_STR_PERMANENT); } -static void zend_interned_strings_restore_int(void) +static zend_string *zend_new_interned_string_request(zend_string *str) { -#ifndef ZTS - uint nIndex; - uint idx; - Bucket *p; + zend_string *ret; + + if (ZSTR_IS_INTERNED(str)) { + return 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. */ + ret = zend_add_interned_string(str, &CG(interned_strings), 0); + + 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)); +} - idx = CG(interned_strings).nNumUsed; - while (idx > 0) { - idx--; - p = CG(interned_strings).arData + idx; - if (GC_FLAGS(p->key) & IS_STR_PERMANENT) break; - CG(interned_strings).nNumUsed--; - CG(interned_strings).nNumOfElements--; - - GC_FLAGS(p->key) &= ~IS_STR_INTERNED; - GC_REFCOUNT(p->key) = 1; - zend_string_free(p->key); - - nIndex = p->h | CG(interned_strings).nTableMask; - if (HT_HASH(&CG(interned_strings), nIndex) == HT_IDX_TO_HASH(idx)) { - HT_HASH(&CG(interned_strings), nIndex) = Z_NEXT(p->val); - } else { - uint32_t prev = HT_HASH(&CG(interned_strings), nIndex); - while (Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val) != idx) { - prev = Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val); - } - Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val) = Z_NEXT(p->val); - } - } -#endif +ZEND_API void zend_interned_strings_set_request_storage_handler(zend_new_interned_string_func_t handler) +{ + interned_string_request_handler = handler; +} + +ZEND_API void zend_interned_strings_set_permanent_storage_copy_handler(zend_string_copy_storage_func_t handler) +{ + interned_string_copy_storage = handler; +} + +ZEND_API void zend_interned_strings_switch_storage(void) +{ + if (interned_string_copy_storage) { + interned_string_copy_storage(); + } + zend_new_interned_string = interned_string_request_handler; } /* @@ -249,4 +229,6 @@ static void zend_interned_strings_restore_int(void) * c-basic-offset: 4 * indent-tabs-mode: t * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 */ |