diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 72819aaced75b..941ab4f3c4d08 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -695,10 +695,10 @@ static int zend_generator_get_next_delegated_value(zend_generator *generator) /* ZVAL_COPY(&generator->value, value); zval_ptr_dtor(&generator->key); - if (p->key) { - ZVAL_STR_COPY(&generator->key, p->key); + if (zend_bucket_has_str_key(p)) { + ZVAL_STR_COPY(&generator->key, p->key.str); } else { - ZVAL_LONG(&generator->key, p->h); + ZVAL_LONG(&generator->key, p->key.num); } Z_FE_POS(generator->values) = pos; diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index ae3d91944cfb2..a9ccb5f95f8ac 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -23,6 +23,20 @@ #include "zend.h" #include "zend_globals.h" #include "zend_variables.h" +#include "zend_hash_func.h" + +#define HT_HAS_STR_KEY(p) zend_bucket_has_str_key(p) + +#define HT_KEY_MATCHES(p, _key, _h) \ + (EXPECTED((p)->h == (_h)) && \ + (EXPECTED((p)->key.str == (_key)) || \ + (EXPECTED(ZSTR_LEN((p)->key.str) == ZSTR_LEN(_key)) && \ + EXPECTED(0 == memcmp(ZSTR_VAL((p)->key.str), ZSTR_VAL(_key), ZSTR_LEN(_key)))))) +#define HT_STR_KEY_MATCHES(p, _str, _len, _h) \ + ((p)->h == (_h) && ZSTR_LEN((p)->key.str) == (_len) && \ + (0 == memcmp(ZSTR_VAL((p)->key.str), (_str), (_len)))) +#define HT_INT_KEY_MATCHES(p, _num) \ + ((p)->key.num == (_num) && !HT_HAS_STR_KEY(p)) #define HT_DEBUG 0 #if HT_DEBUG @@ -127,7 +141,17 @@ static zend_always_inline uint32_t zend_hash_check_size(uint32_t nSize) #endif } -static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, int packed) +ZEND_API unsigned char zend_siphash_key[16]; +void zend_initialize_siphash_key() +{ + /* TODO Initialize to actually random data... */ + int i = 0; + for (i = 0; i < 16; ++i) { + zend_siphash_key[i] = i; + } +} + +static void zend_always_inline zend_hash_real_init_ex(HashTable *ht, int packed) { HT_ASSERT(GC_REFCOUNT(ht) == 1); ZEND_ASSERT(!((ht)->u.flags & HASH_FLAG_INITIALIZED)); @@ -215,6 +239,16 @@ ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht) HT_SET_DATA_ADDR(ht, new_data); memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed); pefree(old_data, (ht)->u.flags & HASH_FLAG_PERSISTENT); + /* TODO: Maybe integrate into rehash loop */ + { + uint32_t i; + for (i = 0; i < ht->nNumUsed; ++i) { + Bucket *p = &ht->arData[i]; + if (!Z_ISUNDEF(p->val)) { + p->h = zend_hash_integer(p->key.num); + } + } + } zend_hash_rehash(ht); } @@ -483,12 +517,7 @@ static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zen idx = HT_HASH_EX(arData, nIndex); while (EXPECTED(idx != HT_INVALID_IDX)) { p = HT_HASH_TO_BUCKET_EX(arData, idx); - if (EXPECTED(p->key == key)) { /* check for the same interned string */ - return p; - } else if (EXPECTED(p->h == h) && - EXPECTED(p->key) && - EXPECTED(ZSTR_LEN(p->key) == ZSTR_LEN(key)) && - EXPECTED(memcmp(ZSTR_VAL(p->key), ZSTR_VAL(key), ZSTR_LEN(key)) == 0)) { + if (HT_KEY_MATCHES(p, key, h)) { return p; } idx = Z_NEXT(p->val); @@ -508,10 +537,7 @@ static zend_always_inline Bucket *zend_hash_str_find_bucket(const HashTable *ht, while (idx != HT_INVALID_IDX) { ZEND_ASSERT(idx < HT_IDX_TO_HASH(ht->nTableSize)); p = HT_HASH_TO_BUCKET_EX(arData, idx); - if ((p->h == h) - && p->key - && (ZSTR_LEN(p->key) == len) - && !memcmp(ZSTR_VAL(p->key), str, len)) { + if (HT_STR_KEY_MATCHES(p, str, len, h)) { return p; } idx = Z_NEXT(p->val); @@ -519,7 +545,7 @@ static zend_always_inline Bucket *zend_hash_str_find_bucket(const HashTable *ht, return NULL; } -static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *ht, zend_ulong h) +static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *ht, zend_ulong num, zend_ulong h) { uint32_t nIndex; uint32_t idx; @@ -531,7 +557,7 @@ static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *h while (idx != HT_INVALID_IDX) { ZEND_ASSERT(idx < HT_IDX_TO_HASH(ht->nTableSize)); p = HT_HASH_TO_BUCKET_EX(arData, idx); - if (p->h == h && !p->key) { + if (HT_INT_KEY_MATCHES(p, num)) { return p; } idx = Z_NEXT(p->val); @@ -599,7 +625,7 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s } zend_hash_iterators_update(ht, HT_INVALID_IDX, idx); p = ht->arData + idx; - p->key = key; + p->key.str = key; if (!ZSTR_IS_INTERNED(key)) { zend_string_addref(key); ht->u.flags &= ~HASH_FLAG_STATIC_KEYS; @@ -703,25 +729,26 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_str_add_empty_element(HashTable *ht, cons return zend_hash_str_add(ht, str, len, &dummy); } -static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, zend_ulong h, zval *pData, uint32_t flag ZEND_FILE_LINE_DC) +static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, zend_ulong num, zval *pData, uint32_t flag ZEND_FILE_LINE_DC) { uint32_t nIndex; uint32_t idx; Bucket *p; + zend_ulong h; IS_CONSISTENT(ht); HT_ASSERT(GC_REFCOUNT(ht) == 1); if (UNEXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { - CHECK_INIT(ht, h < ht->nTableSize); - if (h < ht->nTableSize) { - p = ht->arData + h; + CHECK_INIT(ht, num < ht->nTableSize); + if (num < ht->nTableSize) { + p = ht->arData + num; goto add_to_packed; } goto add_to_hash; } else if (ht->u.flags & HASH_FLAG_PACKED) { - if (h < ht->nNumUsed) { - p = ht->arData + h; + if (num < ht->nNumUsed) { + p = ht->arData + num; if (Z_TYPE(p->val) != IS_UNDEF) { if (flag & HASH_ADD) { return NULL; @@ -734,12 +761,12 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, } else { /* we have to keep the order :( */ goto convert_to_hash; } - } else if (EXPECTED(h < ht->nTableSize)) { - p = ht->arData + h; - } else if ((h >> 1) < ht->nTableSize && + } else if (EXPECTED(num < ht->nTableSize)) { + p = ht->arData + num; + } else if ((num >> 1) < ht->nTableSize && (ht->nTableSize >> 1) < ht->nNumOfElements) { zend_hash_packed_grow(ht); - p = ht->arData + h; + p = ht->arData + num; } else { goto convert_to_hash; } @@ -747,66 +774,72 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, add_to_packed: /* incremental initialization of empty Buckets */ if ((flag & (HASH_ADD_NEW|HASH_ADD_NEXT)) == (HASH_ADD_NEW|HASH_ADD_NEXT)) { - ht->nNumUsed = h + 1; - } else if (h >= ht->nNumUsed) { - if (h > ht->nNumUsed) { + ht->nNumUsed = num + 1; + } else if (num >= ht->nNumUsed) { + if (num > ht->nNumUsed) { Bucket *q = ht->arData + ht->nNumUsed; while (q != p) { ZVAL_UNDEF(&q->val); q++; } } - ht->nNumUsed = h + 1; + ht->nNumUsed = num + 1; } ht->nNumOfElements++; if (ht->nInternalPointer == HT_INVALID_IDX) { - ht->nInternalPointer = h; + ht->nInternalPointer = num; } - zend_hash_iterators_update(ht, HT_INVALID_IDX, h); - if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { - ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; + zend_hash_iterators_update(ht, HT_INVALID_IDX, num); + if ((zend_long) num >= (zend_long)ht->nNextFreeElement) { + ht->nNextFreeElement = num < ZEND_LONG_MAX ? num + 1 : ZEND_LONG_MAX; } - p->h = h; - p->key = NULL; + p->h = 0; + p->key.num = num; ZVAL_COPY_VALUE(&p->val, pData); return &p->val; - -convert_to_hash: - zend_hash_packed_to_hash(ht); - } else if ((flag & HASH_ADD_NEW) == 0) { - p = zend_hash_index_find_bucket(ht, h); - if (p) { - if (flag & HASH_ADD) { - return NULL; - } - ZEND_ASSERT(&p->val != pData); - if (ht->pDestructor) { - ht->pDestructor(&p->val); - } - ZVAL_COPY_VALUE(&p->val, pData); - if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { - ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; + } else { + h = zend_hash_integer(num); + if ((flag & HASH_ADD_NEW) == 0) { + p = zend_hash_index_find_bucket(ht, num, h); + if (p) { + if (flag & HASH_ADD) { + return NULL; + } + ZEND_ASSERT(&p->val != pData); + if (ht->pDestructor) { + ht->pDestructor(&p->val); + } + ZVAL_COPY_VALUE(&p->val, pData); + if ((zend_long) num >= (zend_long)ht->nNextFreeElement) { + ht->nNextFreeElement = num < ZEND_LONG_MAX ? num + 1 : ZEND_LONG_MAX; + } + return &p->val; } - return &p->val; } + ZEND_HASH_IF_FULL_DO_RESIZE(ht); } - ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */ - + if (0) { +convert_to_hash: + zend_hash_packed_to_hash(ht); + ZEND_HASH_IF_FULL_DO_RESIZE(ht); add_to_hash: + h = zend_hash_integer(num); + } + idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == HT_INVALID_IDX) { ht->nInternalPointer = idx; } zend_hash_iterators_update(ht, HT_INVALID_IDX, idx); - if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { - ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; + if ((zend_long) num >= (zend_long)ht->nNextFreeElement) { + ht->nNextFreeElement = num < ZEND_LONG_MAX ? num + 1 : ZEND_LONG_MAX; } p = ht->arData + idx; p->h = h; - p->key = NULL; + p->key.num = num; nIndex = h | ht->nTableMask; ZVAL_COPY_VALUE(&p->val, pData); Z_NEXT(p->val) = HT_HASH(ht, nIndex); @@ -987,8 +1020,8 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, } zend_hash_iterators_update(ht, idx, new_idx); } - if (p->key) { - zend_string_release(p->key); + if (HT_HAS_STR_KEY(p)) { + zend_string_release(p->key.str); } if (ht->pDestructor) { zval tmp; @@ -1044,11 +1077,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key) idx = HT_HASH(ht, nIndex); while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET(ht, idx); - if ((p->key == key) || - (p->h == h && - p->key && - ZSTR_LEN(p->key) == ZSTR_LEN(key) && - memcmp(ZSTR_VAL(p->key), ZSTR_VAL(key), ZSTR_LEN(key)) == 0)) { + if (HT_KEY_MATCHES(p, key, h)) { _zend_hash_del_el_ex(ht, idx, p, prev); return SUCCESS; } @@ -1075,11 +1104,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key) idx = HT_HASH(ht, nIndex); while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET(ht, idx); - if ((p->key == key) || - (p->h == h && - p->key && - ZSTR_LEN(p->key) == ZSTR_LEN(key) && - memcmp(ZSTR_VAL(p->key), ZSTR_VAL(key), ZSTR_LEN(key)) == 0)) { + if (HT_KEY_MATCHES(p, key, h)) { if (Z_TYPE(p->val) == IS_INDIRECT) { zval *data = Z_INDIRECT(p->val); @@ -1124,10 +1149,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const char *str, idx = HT_HASH(ht, nIndex); while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET(ht, idx); - if ((p->h == h) - && p->key - && (ZSTR_LEN(p->key) == len) - && !memcmp(ZSTR_VAL(p->key), str, len)) { + if (HT_STR_KEY_MATCHES(p, str, len, h)) { if (Z_TYPE(p->val) == IS_INDIRECT) { zval *data = Z_INDIRECT(p->val); @@ -1168,10 +1190,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char *str, siz idx = HT_HASH(ht, nIndex); while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET(ht, idx); - if ((p->h == h) - && p->key - && (ZSTR_LEN(p->key) == len) - && !memcmp(ZSTR_VAL(p->key), str, len)) { + if (HT_STR_KEY_MATCHES(p, str, len, h)) { _zend_hash_del_el_ex(ht, idx, p, prev); return SUCCESS; } @@ -1181,32 +1200,35 @@ ZEND_API int ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char *str, siz return FAILURE; } -ZEND_API int ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h) +ZEND_API int ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong num) { uint32_t nIndex; uint32_t idx; Bucket *p; Bucket *prev = NULL; + zend_ulong h; IS_CONSISTENT(ht); HT_ASSERT(GC_REFCOUNT(ht) == 1); if (ht->u.flags & HASH_FLAG_PACKED) { - if (h < ht->nNumUsed) { - p = ht->arData + h; + if (num < ht->nNumUsed) { + p = ht->arData + num; if (Z_TYPE(p->val) != IS_UNDEF) { - _zend_hash_del_el_ex(ht, HT_IDX_TO_HASH(h), p, NULL); + _zend_hash_del_el_ex(ht, HT_IDX_TO_HASH(num), p, NULL); return SUCCESS; } } return FAILURE; } + + h = zend_hash_integer(num); nIndex = h | ht->nTableMask; idx = HT_HASH(ht, nIndex); while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET(ht, idx); - if ((p->h == h) && (p->key == NULL)) { + if (HT_INT_KEY_MATCHES(p, num)) { _zend_hash_del_el_ex(ht, idx, p, prev); return SUCCESS; } @@ -1244,16 +1266,16 @@ ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht) } else if (ht->nNumUsed == ht->nNumOfElements) { do { ht->pDestructor(&p->val); - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } while (++p != end); } else { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { ht->pDestructor(&p->val); - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } } while (++p != end); @@ -1264,8 +1286,8 @@ ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht) if (!(ht->u.flags & (HASH_FLAG_PACKED|HASH_FLAG_STATIC_KEYS))) { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } } while (++p != end); @@ -1307,16 +1329,16 @@ ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht) } else if (ht->nNumUsed == ht->nNumOfElements) { do { i_zval_ptr_dtor(&p->val ZEND_FILE_LINE_CC); - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } while (++p != end); } else { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { i_zval_ptr_dtor(&p->val ZEND_FILE_LINE_CC); - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } } while (++p != end); @@ -1357,16 +1379,16 @@ ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht) } else if (ht->nNumUsed == ht->nNumOfElements) { do { ht->pDestructor(&p->val); - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } while (++p != end); } else { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { ht->pDestructor(&p->val); - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } } while (++p != end); @@ -1375,15 +1397,15 @@ ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht) if (!(ht->u.flags & (HASH_FLAG_PACKED|HASH_FLAG_STATIC_KEYS))) { if (ht->nNumUsed == ht->nNumOfElements) { do { - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } while (++p != end); } else { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } } while (++p != end); @@ -1417,16 +1439,16 @@ ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht) } else if (ht->nNumUsed == ht->nNumOfElements) { do { i_zval_ptr_dtor(&p->val ZEND_FILE_LINE_CC); - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } while (++p != end); } else { do { if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { i_zval_ptr_dtor(&p->val ZEND_FILE_LINE_CC); - if (EXPECTED(p->key)) { - zend_string_release(p->key); + if (EXPECTED(HT_HAS_STR_KEY(p))) { + zend_string_release(p->key.str); } } } while (++p != end); @@ -1560,8 +1582,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_apply_with_arguments(HashTable *ht, apply_ p = ht->arData + idx; if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; va_start(args, num_args); - hash_key.h = p->h; - hash_key.key = p->key; + ZEND_HASH_EXTRACT_KEY(p, hash_key.key, hash_key.h); result = apply_func(&p->val, num_args, args, &hash_key); @@ -1636,10 +1657,10 @@ ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source, continue; } } - if (p->key) { - new_entry = zend_hash_update(target, p->key, data); + if (HT_HAS_STR_KEY(p)) { + new_entry = zend_hash_update(target, p->key.str, data); } else { - new_entry = zend_hash_index_update(target, p->h, data); + new_entry = zend_hash_index_update(target, p->key.num, data); } if (pCopyConstructor) { pCopyConstructor(new_entry); @@ -1692,14 +1713,12 @@ static zend_always_inline int zend_array_dup_element(HashTable *source, HashTabl ZVAL_COPY_VALUE(&q->val, data); q->h = p->h; - if (packed) { - q->key = NULL; - } else { + q->key = p->key; + if (!packed) { uint32_t nIndex; - q->key = p->key; - if (!static_keys && q->key) { - zend_string_addref(q->key); + if (!static_keys && HT_HAS_STR_KEY(q)) { + zend_string_addref(q->key.str); } nIndex = q->h | target->nTableMask; @@ -1864,13 +1883,13 @@ ZEND_API void ZEND_FASTCALL _zend_hash_merge(HashTable *target, HashTable *sourc UNEXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { continue; } - if (p->key) { - t = _zend_hash_add_or_update_i(target, p->key, &p->val, HASH_UPDATE | HASH_UPDATE_INDIRECT ZEND_FILE_LINE_RELAY_CC); + if (HT_HAS_STR_KEY(p)) { + t = _zend_hash_add_or_update_i(target, p->key.str, &p->val, HASH_UPDATE | HASH_UPDATE_INDIRECT ZEND_FILE_LINE_RELAY_CC); if (t && pCopyConstructor) { pCopyConstructor(t); } } else { - t = zend_hash_index_update(target, p->h, &p->val); + t = zend_hash_index_update(target, p->key.num, &p->val); if (t && pCopyConstructor) { pCopyConstructor(t); } @@ -1884,13 +1903,13 @@ ZEND_API void ZEND_FASTCALL _zend_hash_merge(HashTable *target, HashTable *sourc UNEXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { continue; } - if (p->key) { - t = _zend_hash_add_or_update_i(target, p->key, &p->val, HASH_ADD | HASH_UPDATE_INDIRECT ZEND_FILE_LINE_RELAY_CC); + if (HT_HAS_STR_KEY(p)) { + t = _zend_hash_add_or_update_i(target, p->key.str, &p->val, HASH_ADD | HASH_UPDATE_INDIRECT ZEND_FILE_LINE_RELAY_CC); if (t && pCopyConstructor) { pCopyConstructor(t); } } else { - t = zend_hash_index_add(target, p->h, &p->val); + t = zend_hash_index_add(target, p->key.num, &p->val); if (t && pCopyConstructor) { pCopyConstructor(t); } @@ -1910,9 +1929,7 @@ ZEND_API void ZEND_FASTCALL _zend_hash_merge(HashTable *target, HashTable *sourc static zend_bool ZEND_FASTCALL zend_hash_replace_checker_wrapper(HashTable *target, zval *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func) { zend_hash_key hash_key; - - hash_key.h = p->h; - hash_key.key = p->key; + ZEND_HASH_EXTRACT_KEY(p, hash_key.key, hash_key.h); return merge_checker_func(target, source_data, &hash_key, pParam); } @@ -1931,7 +1948,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, HashTable *sou p = source->arData + idx; if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; if (zend_hash_replace_checker_wrapper(target, &p->val, p, pParam, pMergeSource)) { - t = zend_hash_update(target, p->key, &p->val); + t = zend_hash_update(target, p->key.str, &p->val); if (t && pCopyConstructor) { pCopyConstructor(t); } @@ -1992,15 +2009,15 @@ ZEND_API zend_bool ZEND_FASTCALL zend_hash_str_exists(const HashTable *ht, const return p ? 1 : 0; } -ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h) +ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong num) { Bucket *p; IS_CONSISTENT(ht); if (ht->u.flags & HASH_FLAG_PACKED) { - if (h < ht->nNumUsed) { - p = ht->arData + h; + if (num < ht->nNumUsed) { + p = ht->arData + num; if (Z_TYPE(p->val) != IS_UNDEF) { return &p->val; } @@ -2008,36 +2025,36 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulon return NULL; } - p = zend_hash_index_find_bucket(ht, h); + p = zend_hash_index_find_bucket(ht, num, zend_hash_integer(num)); return p ? &p->val : NULL; } -ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulong h) +ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulong num) { Bucket *p; IS_CONSISTENT(ht); - p = zend_hash_index_find_bucket(ht, h); + p = zend_hash_index_find_bucket(ht, num, zend_hash_integer(num)); return p ? &p->val : NULL; } -ZEND_API zend_bool ZEND_FASTCALL zend_hash_index_exists(const HashTable *ht, zend_ulong h) +ZEND_API zend_bool ZEND_FASTCALL zend_hash_index_exists(const HashTable *ht, zend_ulong num) { Bucket *p; IS_CONSISTENT(ht); if (ht->u.flags & HASH_FLAG_PACKED) { - if (h < ht->nNumUsed) { - if (Z_TYPE(ht->arData[h].val) != IS_UNDEF) { + if (num < ht->nNumUsed) { + if (Z_TYPE(ht->arData[num].val) != IS_UNDEF) { return 1; } } return 0; } - p = zend_hash_index_find_bucket(ht, h); + p = zend_hash_index_find_bucket(ht, num, zend_hash_integer(num)); return p ? 1 : 0; } @@ -2137,11 +2154,11 @@ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zen IS_CONSISTENT(ht); if (idx != HT_INVALID_IDX) { p = ht->arData + idx; - if (p->key) { - *str_index = p->key; + if (HT_HAS_STR_KEY(p)) { + *str_index = p->key.str; return HASH_KEY_IS_STRING; } else { - *num_index = p->h; + *num_index = p->key.num; return HASH_KEY_IS_LONG; } } @@ -2158,10 +2175,10 @@ ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *h ZVAL_NULL(key); } else { p = ht->arData + idx; - if (p->key) { - ZVAL_STR_COPY(key, p->key); + if (HT_HAS_STR_KEY(p)) { + ZVAL_STR_COPY(key, p->key.str); } else { - ZVAL_LONG(key, p->h); + ZVAL_LONG(key, p->key.num); } } } @@ -2174,7 +2191,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(HashTable *ht, Hash IS_CONSISTENT(ht); if (idx != HT_INVALID_IDX) { p = ht->arData + idx; - if (p->key) { + if (HT_HAS_STR_KEY(p)) { return HASH_KEY_IS_STRING; } else { return HASH_KEY_IS_LONG; @@ -2202,7 +2219,7 @@ ZEND_API void zend_hash_bucket_swap(Bucket *p, Bucket *q) { zval val; zend_ulong h; - zend_string *key; + zend_bucket_key key; ZVAL_COPY_VALUE(&val, &p->val); h = p->h; @@ -2228,17 +2245,19 @@ ZEND_API void zend_hash_bucket_renum_swap(Bucket *p, Bucket *q) ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q) { + /* Can't use this right now -- might be able to use it in the future if + * packed -> unpacked conversion requires a hash recomputation. */ zval val; - zend_ulong h; + zend_bucket_key key; ZVAL_COPY_VALUE(&val, &p->val); - h = p->h; + key = p->key; ZVAL_COPY_VALUE(&p->val, &q->val); - p->h = q->h; + p->key = q->key; ZVAL_COPY_VALUE(&q->val, &val); - q->h = h; + q->key = key; } ZEND_API int ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, compare_func_t compar, zend_bool renumber) @@ -2267,8 +2286,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, co } sort((void *)ht->arData, i, sizeof(Bucket), compar, - (swap_func_t)(renumber? zend_hash_bucket_renum_swap : - ((ht->u.flags & HASH_FLAG_PACKED) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap))); + (swap_func_t)(renumber? zend_hash_bucket_renum_swap : zend_hash_bucket_swap)); ht->nNumUsed = i; ht->nInternalPointer = 0; @@ -2276,11 +2294,11 @@ ZEND_API int ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, co if (renumber) { for (j = 0; j < i; j++) { p = ht->arData + j; - p->h = j; - if (p->key) { - zend_string_release(p->key); - p->key = NULL; + if (HT_HAS_STR_KEY(p)) { + zend_string_release(p->key.str); } + p->key.num = j; + p->h = 0; } ht->nNextFreeElement = i; @@ -2329,33 +2347,33 @@ static zend_always_inline int zend_hash_compare_impl(HashTable *ht1, HashTable * if (Z_TYPE(p2->val) != IS_UNDEF) break; idx2++; } - if (p1->key == NULL && p2->key == NULL) { /* numeric indices */ - if (p1->h != p2->h) { - return p1->h > p2->h ? 1 : -1; + if (!HT_HAS_STR_KEY(p1) && !HT_HAS_STR_KEY(p2)) { /* numeric indices */ + if (p1->key.num != p2->key.num) { + return p1->key.num > p2->key.num ? 1 : -1; } - } else if (p1->key != NULL && p2->key != NULL) { /* string indices */ - if (ZSTR_LEN(p1->key) != ZSTR_LEN(p2->key)) { - return ZSTR_LEN(p1->key) > ZSTR_LEN(p2->key) ? 1 : -1; + } else if (HT_HAS_STR_KEY(p1) && HT_HAS_STR_KEY(p2)) { /* string indices */ + if (ZSTR_LEN(p1->key.str) != ZSTR_LEN(p2->key.str)) { + return ZSTR_LEN(p1->key.str) > ZSTR_LEN(p2->key.str) ? 1 : -1; } - result = memcmp(ZSTR_VAL(p1->key), ZSTR_VAL(p2->key), ZSTR_LEN(p1->key)); + result = memcmp(ZSTR_VAL(p1->key.str), ZSTR_VAL(p2->key.str), ZSTR_LEN(p1->key.str)); if (result != 0) { return result; } } else { /* Mixed key types: A string key is considered as larger */ - return p1->key != NULL ? 1 : -1; + return HT_HAS_STR_KEY(p1) ? 1 : -1; } pData2 = &p2->val; idx2++; } else { - if (p1->key == NULL) { /* numeric index */ - pData2 = zend_hash_index_find(ht2, p1->h); + if (!HT_HAS_STR_KEY(p1)) { /* numeric index */ + pData2 = zend_hash_index_find(ht2, p1->key.num); if (pData2 == NULL) { return 1; } } else { /* string index */ - pData2 = zend_hash_find(ht2, p1->key); + pData2 = zend_hash_find(ht2, p1->key.str); if (pData2 == NULL) { return 1; } diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index b7e6f37f71fe3..8544f1cc95853 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -53,6 +53,8 @@ typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_dat BEGIN_EXTERN_C() +void zend_initialize_siphash_key(void); + /* startup/shutdown */ ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC); ZEND_API void ZEND_FASTCALL _zend_hash_init_ex(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection ZEND_FILE_LINE_DC); @@ -763,6 +765,26 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, #define zend_hash_get_current_data_ptr(ht) \ zend_hash_get_current_data_ptr_ex(ht, &(ht)->nInternalPointer) +static zend_always_inline zend_bool zend_bucket_has_str_key(const Bucket *p) { + return (p->h & HT_IS_STR_BIT) != 0; +} + +#define ZEND_HASH_EXTRACT_KEY(_p, _key, _h) \ + if (zend_bucket_has_str_key(_p)) { \ + _key = (_p)->key.str; \ + _h = 0; \ + } else { \ + _key = NULL; \ + _h = (_p)->key.num; \ + } + +#define ZEND_HASH_EXTRACT_STR_KEY(_p, _key) \ + (_key) = zend_bucket_has_str_key(_p) ? (_p)->key.str : NULL; + +#define ZEND_HASH_EXTRACT_NUM_KEY(_p, _h) \ + ZEND_ASSERT(!zend_bucket_has_str_key(_p)); \ + (_h) = (_p)->key.num; + #define ZEND_HASH_FOREACH(_ht, indirect) do { \ Bucket *_p = (_ht)->arData; \ Bucket *_end = _p + (_ht)->nNumUsed; \ @@ -805,58 +827,54 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, #define ZEND_HASH_FOREACH_NUM_KEY(ht, _h) \ ZEND_HASH_FOREACH(ht, 0); \ - _h = _p->h; + ZEND_HASH_EXTRACT_NUM_KEY(_p, _h); #define ZEND_HASH_FOREACH_STR_KEY(ht, _key) \ ZEND_HASH_FOREACH(ht, 0); \ - _key = _p->key; + ZEND_HASH_EXTRACT_STR_KEY(_p, _key); #define ZEND_HASH_FOREACH_KEY(ht, _h, _key) \ ZEND_HASH_FOREACH(ht, 0); \ - _h = _p->h; \ - _key = _p->key; + ZEND_HASH_EXTRACT_KEY(_p, _key, _h); #define ZEND_HASH_FOREACH_NUM_KEY_VAL(ht, _h, _val) \ ZEND_HASH_FOREACH(ht, 0); \ - _h = _p->h; \ + ZEND_HASH_EXTRACT_NUM_KEY(_p, _h); \ _val = _z; #define ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _key, _val) \ ZEND_HASH_FOREACH(ht, 0); \ - _key = _p->key; \ + ZEND_HASH_EXTRACT_STR_KEY(_p, _key); \ _val = _z; #define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val) \ ZEND_HASH_FOREACH(ht, 0); \ - _h = _p->h; \ - _key = _p->key; \ + ZEND_HASH_EXTRACT_KEY(_p, _key, _h); \ _val = _z; #define ZEND_HASH_FOREACH_STR_KEY_VAL_IND(ht, _key, _val) \ ZEND_HASH_FOREACH(ht, 1); \ - _key = _p->key; \ + ZEND_HASH_EXTRACT_STR_KEY(_p, _key); \ _val = _z; #define ZEND_HASH_FOREACH_KEY_VAL_IND(ht, _h, _key, _val) \ ZEND_HASH_FOREACH(ht, 1); \ - _h = _p->h; \ - _key = _p->key; \ + ZEND_HASH_EXTRACT_KEY(_p, _key, _h); \ _val = _z; #define ZEND_HASH_FOREACH_NUM_KEY_PTR(ht, _h, _ptr) \ ZEND_HASH_FOREACH(ht, 0); \ - _h = _p->h; \ + ZEND_HASH_EXTRACT_NUM_KEY(_p, _h); \ _ptr = Z_PTR_P(_z); #define ZEND_HASH_FOREACH_STR_KEY_PTR(ht, _key, _ptr) \ ZEND_HASH_FOREACH(ht, 0); \ - _key = _p->key; \ + ZEND_HASH_EXTRACT_STR_KEY(_p, _key); \ _ptr = Z_PTR_P(_z); #define ZEND_HASH_FOREACH_KEY_PTR(ht, _h, _key, _ptr) \ ZEND_HASH_FOREACH(ht, 0); \ - _h = _p->h; \ - _key = _p->key; \ + ZEND_HASH_EXTRACT_KEY(_p, _key, _h); \ _ptr = Z_PTR_P(_z); #define ZEND_HASH_REVERSE_FOREACH_BUCKET(ht, _bucket) \ @@ -877,14 +895,12 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, #define ZEND_HASH_REVERSE_FOREACH_KEY_VAL(ht, _h, _key, _val) \ ZEND_HASH_REVERSE_FOREACH(ht, 0); \ - _h = _p->h; \ - _key = _p->key; \ + ZEND_HASH_EXTRACT_KEY(_p, _key, _h); \ _val = _z; #define ZEND_HASH_REVERSE_FOREACH_KEY_VAL_IND(ht, _h, _key, _val) \ ZEND_HASH_REVERSE_FOREACH(ht, 1); \ - _h = _p->h; \ - _key = _p->key; \ + ZEND_HASH_EXTRACT_KEY(_p, _key, _h); \ _val = _z; #define ZEND_HASH_APPLY_PROTECTION(ht) \ @@ -910,8 +926,8 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, #define ZEND_HASH_FILL_ADD(_val) do { \ ZVAL_COPY_VALUE(&__fill_bkt->val, _val); \ - __fill_bkt->h = (__fill_idx); \ - __fill_bkt->key = NULL; \ + __fill_bkt->key.num = (__fill_idx); \ + __fill_bkt->h = 0; \ __fill_bkt++; \ __fill_idx++; \ } while (0) @@ -935,7 +951,7 @@ static zend_always_inline zval *_zend_hash_append(HashTable *ht, zend_string *ke zend_string_addref(key); zend_string_hash_val(key); } - p->key = key; + p->key.str = key; p->h = ZSTR_H(key); nIndex = (uint32_t)p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); @@ -957,7 +973,7 @@ static zend_always_inline zval *_zend_hash_append_ptr(HashTable *ht, zend_string zend_string_addref(key); zend_string_hash_val(key); } - p->key = key; + p->key.str = key; p->h = ZSTR_H(key); nIndex = (uint32_t)p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); @@ -979,7 +995,7 @@ static zend_always_inline void _zend_hash_append_ind(HashTable *ht, zend_string zend_string_addref(key); zend_string_hash_val(key); } - p->key = key; + p->key.str = key; p->h = ZSTR_H(key); nIndex = (uint32_t)p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); diff --git a/Zend/zend_hash_func.h b/Zend/zend_hash_func.h new file mode 100644 index 0000000000000..ade208a33fadf --- /dev/null +++ b/Zend/zend_hash_func.h @@ -0,0 +1,150 @@ +#ifndef ZEND_HASH_FUNC_H +#define ZEND_HASH_FUNC_H + +/* + Copyright (c) 2013 Marek Majkowski + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + Original location: + https://github.com/majek/csiphash/ + Solution inspired by code from: + Samuel Neves (supercop/crypto_auth/siphash24/little) + djb (supercop/crypto_auth/siphash24/little2) + Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c) +*/ + +#include "zend_types.h" + +#if WORDS_BIGENDIAN +# if defined(__APPLE__) +# include +# define _le64toh(x) OSSwapLittleToHostInt64(x) +# else +/* See: http://sourceforge.net/p/predef/wiki/Endianness/ */ +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# include +# else +# include +# endif +# endif +# define _le64toh(x) le64toh(x) +#else +# define _le64toh(x) ((uint64_t)(x)) +#endif + + +#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) ) + +#define HALF_ROUND(a,b,c,d,s,t) \ + a += b; c += d; \ + b = ROTATE(b, s) ^ a; \ + d = ROTATE(d, t) ^ c; \ + a = ROTATE(a, 32); + +#define SINGLE_ROUND(v0,v1,v2,v3) \ + HALF_ROUND(v0,v1,v2,v3,13,16); \ + HALF_ROUND(v2,v1,v0,v3,17,21); + +extern unsigned char zend_siphash_key[16]; + +static zend_always_inline uint64_t siphash13_bytes( + const unsigned char *src, size_t src_sz, const unsigned char *key) { + const uint64_t *_key = (uint64_t *) key; + uint64_t k0 = _le64toh(_key[0]); + uint64_t k1 = _le64toh(_key[1]); + uint64_t b = (uint64_t)src_sz << 56; + const uint64_t *in = (uint64_t*)src; + + uint64_t v0 = k0 ^ 0x736f6d6570736575ULL; + uint64_t v1 = k1 ^ 0x646f72616e646f6dULL; + uint64_t v2 = k0 ^ 0x6c7967656e657261ULL; + uint64_t v3 = k1 ^ 0x7465646279746573ULL; + + uint64_t t; + uint8_t *pt, *m; + + while (src_sz >= 8) { + uint64_t mi = _le64toh(*in); + in += 1; src_sz -= 8; + v3 ^= mi; + SINGLE_ROUND(v0,v1,v2,v3); + v0 ^= mi; + } + + t = 0; + pt = (uint8_t *)&t; + m = (uint8_t *)in; + switch (src_sz) { + case 7: pt[6] = m[6]; + case 6: pt[5] = m[5]; + case 5: pt[4] = m[4]; + case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break; + case 3: pt[2] = m[2]; + case 2: pt[1] = m[1]; + case 1: pt[0] = m[0]; + } + b |= _le64toh(t); + + v3 ^= b; + SINGLE_ROUND(v0,v1,v2,v3); + v0 ^= b; v2 ^= 0xff; + SINGLE_ROUND(v0,v1,v2,v3); + SINGLE_ROUND(v0,v1,v2,v3); + SINGLE_ROUND(v0,v1,v2,v3); + return (v0 ^ v1) ^ (v2 ^ v3); +} + +static zend_always_inline uint64_t siphash13_u64(uint64_t in, const unsigned char *key) { + const uint64_t *_key = (uint64_t *) key; + uint64_t k0 = _le64toh(_key[0]); + uint64_t k1 = _le64toh(_key[1]); + uint64_t b = (uint64_t)8 << 56; + + uint64_t v0 = k0 ^ 0x736f6d6570736575ULL; + uint64_t v1 = k1 ^ 0x646f72616e646f6dULL; + uint64_t v2 = k0 ^ 0x6c7967656e657261ULL; + uint64_t v3 = k1 ^ 0x7465646279746573ULL; + + /* TODO: Need _le64toh here? */ + uint64_t mi = _le64toh(in); + v3 ^= mi; + SINGLE_ROUND(v0,v1,v2,v3); + v0 ^= mi; + v3 ^= b; + SINGLE_ROUND(v0,v1,v2,v3); + v0 ^= b; + v2 ^= 0xff; + SINGLE_ROUND(v0,v1,v2,v3); + SINGLE_ROUND(v0,v1,v2,v3); + SINGLE_ROUND(v0,v1,v2,v3); + return (v0 ^ v1) ^ (v2 ^ v3); +} + +static zend_always_inline zend_ulong zend_inline_hash_func(const char *str, size_t len) +{ + uint64_t hash = siphash13_bytes((unsigned char *) str, len, zend_siphash_key); + return hash | HT_IS_STR_BIT; +} + +static zend_always_inline zend_ulong zend_hash_integer(zend_ulong h) { + uint64_t hash = siphash13_u64(h, zend_siphash_key); + return hash & ~HT_IS_STR_BIT; +} + + +#endif + diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c index d4dd6fb5b8938..8a94caa83ccf2 100644 --- a/Zend/zend_ini.c +++ b/Zend/zend_ini.c @@ -188,14 +188,16 @@ static int ini_key_compare(const void *a, const void *b) /* {{{ */ f = (const Bucket *) a; s = (const Bucket *) b; - if (!f->key && !s->key) { /* both numeric */ + if (!zend_bucket_has_str_key(f) && !zend_bucket_has_str_key(s)) { /* both numeric */ return ZEND_NORMALIZE_BOOL(f->h - s->h); - } else if (!f->key) { /* f is numeric, s is not */ + } else if (!zend_bucket_has_str_key(f)) { /* f is numeric, s is not */ return -1; - } else if (!s->key) { /* s is numeric, f is not */ + } else if (!zend_bucket_has_str_key(s)) { /* s is numeric, f is not */ return 1; } else { /* both strings */ - return zend_binary_strcasecmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key)); + return zend_binary_strcasecmp( + ZSTR_VAL(f->key.str), ZSTR_LEN(f->key.str), + ZSTR_VAL(s->key.str), ZSTR_LEN(s->key.str)); } } /* }}} */ diff --git a/Zend/zend_string.c b/Zend/zend_string.c index 612c8049ef889..1a10d9cc209f5 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -20,6 +20,7 @@ #include "zend.h" #include "zend_globals.h" +#include "zend_hash_func.h" ZEND_API zend_string *(*zend_new_interned_string)(zend_string *str); ZEND_API void (*zend_interned_strings_snapshot)(void); @@ -143,10 +144,10 @@ static zend_string *zend_new_interned_string_int(zend_string *str) idx = HT_HASH(&CG(interned_strings), nIndex); while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET(&CG(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.str) == ZSTR_LEN(str))) { + if (!memcmp(ZSTR_VAL(p->key.str), ZSTR_VAL(str), ZSTR_LEN(str))) { zend_string_release(str); - return p->key; + return p->key.str; } } idx = Z_NEXT(p->val); @@ -181,7 +182,7 @@ static zend_string *zend_new_interned_string_int(zend_string *str) CG(interned_strings).nNumOfElements++; p = CG(interned_strings).arData + idx; p->h = h; - p->key = str; + p->key.str = str; Z_STR(p->val) = str; Z_TYPE_INFO(p->val) = IS_INTERNED_STRING_EX; nIndex = h | CG(interned_strings).nTableMask; @@ -204,8 +205,8 @@ static void zend_interned_strings_snapshot_int(void) 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; + ZEND_ASSERT(GC_FLAGS(p->key.str) & IS_STR_PERSISTENT); + GC_FLAGS(p->key.str) |= IS_STR_PERMANENT; } #endif } @@ -221,13 +222,13 @@ static void zend_interned_strings_restore_int(void) while (idx > 0) { idx--; p = CG(interned_strings).arData + idx; - if (GC_FLAGS(p->key) & IS_STR_PERMANENT) break; + if (GC_FLAGS(p->key.str) & 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); + GC_FLAGS(p->key.str) &= ~IS_STR_INTERNED; + GC_REFCOUNT(p->key.str) = 1; + zend_string_free(p->key.str); nIndex = p->h | CG(interned_strings).nTableMask; if (HT_HASH(&CG(interned_strings), nIndex) == HT_IDX_TO_HASH(idx)) { diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 8a1ac6f506d4e..cf4cbcf766235 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -289,76 +289,6 @@ static zend_always_inline zend_bool zend_string_equals(zend_string *s1, zend_str #define zend_string_equals_literal(str, literal) \ (ZSTR_LEN(str) == sizeof(literal)-1 && !memcmp(ZSTR_VAL(str), literal, sizeof(literal) - 1)) -/* - * DJBX33A (Daniel J. Bernstein, Times 33 with Addition) - * - * This is Daniel J. Bernstein's popular `times 33' hash function as - * posted by him years ago on comp.lang.c. It basically uses a function - * like ``hash(i) = hash(i-1) * 33 + str[i]''. This is one of the best - * known hash functions for strings. Because it is both computed very - * fast and distributes very well. - * - * The magic of number 33, i.e. why it works better than many other - * constants, prime or not, has never been adequately explained by - * anyone. So I try an explanation: if one experimentally tests all - * multipliers between 1 and 256 (as RSE did now) one detects that even - * numbers are not useable at all. The remaining 128 odd numbers - * (except for the number 1) work more or less all equally well. They - * all distribute in an acceptable way and this way fill a hash table - * with an average percent of approx. 86%. - * - * If one compares the Chi^2 values of the variants, the number 33 not - * even has the best value. But the number 33 and a few other equally - * good numbers like 17, 31, 63, 127 and 129 have nevertheless a great - * advantage to the remaining numbers in the large set of possible - * multipliers: their multiply operation can be replaced by a faster - * operation based on just one shift plus either a single addition - * or subtraction operation. And because a hash function has to both - * distribute good _and_ has to be very fast to compute, those few - * numbers should be preferred and seems to be the reason why Daniel J. - * Bernstein also preferred it. - * - * - * -- Ralf S. Engelschall - */ - -static zend_always_inline zend_ulong zend_inline_hash_func(const char *str, size_t len) -{ - zend_ulong hash = Z_UL(5381); - - /* variant with the hash unrolled eight times */ - for (; len >= 8; len -= 8) { - hash = ((hash << 5) + hash) + *str++; - hash = ((hash << 5) + hash) + *str++; - hash = ((hash << 5) + hash) + *str++; - hash = ((hash << 5) + hash) + *str++; - hash = ((hash << 5) + hash) + *str++; - hash = ((hash << 5) + hash) + *str++; - hash = ((hash << 5) + hash) + *str++; - hash = ((hash << 5) + hash) + *str++; - } - switch (len) { - case 7: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ - case 6: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ - case 5: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ - case 4: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ - case 3: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ - case 2: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ - case 1: hash = ((hash << 5) + hash) + *str++; break; - case 0: break; -EMPTY_SWITCH_DEFAULT_CASE() - } - - /* Hash value can't be zero, so we always set the high bit */ -#if SIZEOF_ZEND_LONG == 8 - return hash | Z_UL(0x8000000000000000); -#elif SIZEOF_ZEND_LONG == 4 - return hash | Z_UL(0x80000000); -#else -# error "Unknown SIZEOF_ZEND_LONG" -#endif -} - #ifdef ZTS static zend_always_inline zend_string* zend_zts_interned_string_init(const char *val, size_t len) { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 29dec8e97f845..4b6f38cd422e4 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -166,10 +166,15 @@ struct _zend_string { char val[1]; }; +typedef union _zend_bucket_key { + zend_string *str; + zend_ulong num; +} zend_bucket_key; + typedef struct _Bucket { zval val; - zend_ulong h; /* hash value (or numeric index) */ - zend_string *key; /* string key or NULL for numerics */ + zend_ulong h; /* hash value */ + zend_bucket_key key; } Bucket; typedef struct _zend_array HashTable; @@ -218,6 +223,7 @@ struct _zend_array { #if SIZEOF_SIZE_T == 4 # define HT_MAX_SIZE 0x04000000 /* small enough to avoid overflow checks */ +# define HT_IS_STR_BIT Z_UL(0x80000000) # define HT_HASH_TO_BUCKET_EX(data, idx) \ ((Bucket*)((char*)(data) + (idx))) # define HT_IDX_TO_HASH(idx) \ @@ -226,6 +232,7 @@ struct _zend_array { ((idx) / sizeof(Bucket)) #elif SIZEOF_SIZE_T == 8 # define HT_MAX_SIZE 0x80000000 +# define HT_IS_STR_BIT Z_UL(0x8000000000000000) # define HT_HASH_TO_BUCKET_EX(data, idx) \ ((data) + (idx)) # define HT_IDX_TO_HASH(idx) \ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7bad61dd2c1f4..ba65c6f6ac16f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5760,8 +5760,8 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, JMP_ADDR) if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -5915,8 +5915,8 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR) if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -6038,10 +6038,10 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY, JMP_ADDR) } Z_FE_POS_P(array) = pos + 1; if (opline->result_type & (IS_TMP_VAR|IS_CV)) { - if (!p->key) { - ZVAL_LONG(EX_VAR(opline->result.var), p->h); + if (!zend_bucket_has_str_key(p)) { + ZVAL_LONG(EX_VAR(opline->result.var), p->key.num); } else { - ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); + ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key.str); } } } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -6074,23 +6074,23 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY, JMP_ADDR) continue; } } - if (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + if (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key.str) == SUCCESS)) { break; } pos++; p++; } if (opline->result_type & (IS_TMP_VAR|IS_CV)) { - if (UNEXPECTED(!p->key)) { - ZVAL_LONG(EX_VAR(opline->result.var), p->h); - } else if (ZSTR_VAL(p->key)[0]) { - ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); + if (UNEXPECTED(!zend_bucket_has_str_key(p))) { + ZVAL_LONG(EX_VAR(opline->result.var), p->key.num); + } else if (ZSTR_VAL(p->key.str)[0]) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key.str); } else { const char *class_name, *prop_name; size_t prop_name_len; zend_unmangle_property_name_ex( - p->key, &class_name, &prop_name, &prop_name_len); + p->key.str, &class_name, &prop_name, &prop_name_len); ZVAL_STRINGL(EX_VAR(opline->result.var), prop_name, prop_name_len); } } @@ -6104,8 +6104,8 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY, JMP_ADDR) if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key.str) == SUCCESS))) { break; } } @@ -6212,10 +6212,10 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) break; } if (opline->result_type & (IS_TMP_VAR|IS_CV)) { - if (!p->key) { - ZVAL_LONG(EX_VAR(opline->result.var), p->h); + if (!zend_bucket_has_str_key(p)) { + ZVAL_LONG(EX_VAR(opline->result.var), p->key.num); } else { - ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); + ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key.str); } } while (1) { @@ -6262,23 +6262,23 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) continue; } } - if (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + if (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key.str) == SUCCESS)) { break; } pos++; p++; } if (opline->result_type & (IS_TMP_VAR|IS_CV)) { - if (UNEXPECTED(!p->key)) { - ZVAL_LONG(EX_VAR(opline->result.var), p->h); - } else if (ZSTR_VAL(p->key)[0]) { - ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); + if (UNEXPECTED(!zend_bucket_has_str_key(p))) { + ZVAL_LONG(EX_VAR(opline->result.var), p->key.num); + } else if (ZSTR_VAL(p->key.str)[0]) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key.str); } else { const char *class_name, *prop_name; size_t prop_name_len; zend_unmangle_property_name_ex( - p->key, &class_name, &prop_name, &prop_name_len); + p->key.str, &class_name, &prop_name, &prop_name_len); ZVAL_STRINGL(EX_VAR(opline->result.var), prop_name, prop_name_len); } } @@ -6292,8 +6292,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key.str) == SUCCESS))) { break; } } @@ -7666,11 +7666,10 @@ ZEND_VM_HANDLER(168, ZEND_BIND_GLOBAL, CV, CONST) Bucket *p = EG(symbol_table).arData + idx; if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == Z_STR_P(varname)) || - (EXPECTED(p->h == ZSTR_H(Z_STR_P(varname))) && - EXPECTED(p->key != NULL) && - EXPECTED(ZSTR_LEN(p->key) == Z_STRLEN_P(varname)) && - EXPECTED(memcmp(ZSTR_VAL(p->key), Z_STRVAL_P(varname), Z_STRLEN_P(varname)) == 0)))) { + EXPECTED(p->h == ZSTR_H(Z_STR_P(varname))) && + (EXPECTED(p->key.str == Z_STR_P(varname)) || + (EXPECTED(ZSTR_LEN(p->key.str) == Z_STRLEN_P(varname)) && + EXPECTED(memcmp(ZSTR_VAL(p->key.str), Z_STRVAL_P(varname), Z_STRLEN_P(varname)) == 0)))) { value = &EG(symbol_table).arData[idx].val; ZEND_VM_C_GOTO(check_indirect); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 677a2175269dc..a96ca0d19a724 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3496,8 +3496,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER( if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -3648,8 +3648,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -12751,8 +12751,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZE if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -12904,8 +12904,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(Z if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -16312,8 +16312,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZE if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -16467,8 +16467,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(Z if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -16590,10 +16590,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE } Z_FE_POS_P(array) = pos + 1; if (opline->result_type & (IS_TMP_VAR|IS_CV)) { - if (!p->key) { - ZVAL_LONG(EX_VAR(opline->result.var), p->h); + if (!zend_bucket_has_str_key(p)) { + ZVAL_LONG(EX_VAR(opline->result.var), p->key.num); } else { - ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); + ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key.str); } } } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -16626,23 +16626,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE continue; } } - if (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + if (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key.str) == SUCCESS)) { break; } pos++; p++; } if (opline->result_type & (IS_TMP_VAR|IS_CV)) { - if (UNEXPECTED(!p->key)) { - ZVAL_LONG(EX_VAR(opline->result.var), p->h); - } else if (ZSTR_VAL(p->key)[0]) { - ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); + if (UNEXPECTED(!zend_bucket_has_str_key(p))) { + ZVAL_LONG(EX_VAR(opline->result.var), p->key.num); + } else if (ZSTR_VAL(p->key.str)[0]) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key.str); } else { const char *class_name, *prop_name; size_t prop_name_len; zend_unmangle_property_name_ex( - p->key, &class_name, &prop_name, &prop_name_len); + p->key.str, &class_name, &prop_name, &prop_name_len); ZVAL_STRINGL(EX_VAR(opline->result.var), prop_name, prop_name_len); } } @@ -16656,8 +16656,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key.str) == SUCCESS))) { break; } } @@ -16764,10 +16764,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z break; } if (opline->result_type & (IS_TMP_VAR|IS_CV)) { - if (!p->key) { - ZVAL_LONG(EX_VAR(opline->result.var), p->h); + if (!zend_bucket_has_str_key(p)) { + ZVAL_LONG(EX_VAR(opline->result.var), p->key.num); } else { - ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); + ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key.str); } } while (1) { @@ -16814,23 +16814,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z continue; } } - if (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + if (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key.str) == SUCCESS)) { break; } pos++; p++; } if (opline->result_type & (IS_TMP_VAR|IS_CV)) { - if (UNEXPECTED(!p->key)) { - ZVAL_LONG(EX_VAR(opline->result.var), p->h); - } else if (ZSTR_VAL(p->key)[0]) { - ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); + if (UNEXPECTED(!zend_bucket_has_str_key(p))) { + ZVAL_LONG(EX_VAR(opline->result.var), p->key.num); + } else if (ZSTR_VAL(p->key.str)[0]) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key.str); } else { const char *class_name, *prop_name; size_t prop_name_len; zend_unmangle_property_name_ex( - p->key, &class_name, &prop_name, &prop_name_len); + p->key.str, &class_name, &prop_name, &prop_name_len); ZVAL_STRINGL(EX_VAR(opline->result.var), prop_name, prop_name_len); } } @@ -16844,8 +16844,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array), p->key.str) == SUCCESS))) { break; } } @@ -35372,8 +35372,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEN if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -35524,8 +35524,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZE if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + (UNEXPECTED(!zend_bucket_has_str_key(p)) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key.str) == SUCCESS))) { break; } pos++; @@ -40341,11 +40341,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_GLOBAL_SPEC_CV_CONST_HAND Bucket *p = EG(symbol_table).arData + idx; if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == Z_STR_P(varname)) || - (EXPECTED(p->h == ZSTR_H(Z_STR_P(varname))) && - EXPECTED(p->key != NULL) && - EXPECTED(ZSTR_LEN(p->key) == Z_STRLEN_P(varname)) && - EXPECTED(memcmp(ZSTR_VAL(p->key), Z_STRVAL_P(varname), Z_STRLEN_P(varname)) == 0)))) { + EXPECTED(p->h == ZSTR_H(Z_STR_P(varname))) && + (EXPECTED(p->key.str == Z_STR_P(varname)) || + (EXPECTED(ZSTR_LEN(p->key.str) == Z_STRLEN_P(varname)) && + EXPECTED(memcmp(ZSTR_VAL(p->key.str), Z_STRVAL_P(varname), Z_STRLEN_P(varname)) == 0)))) { value = &EG(symbol_table).arData[idx].val; goto check_indirect; diff --git a/ext/oci8/oci8.c b/ext/oci8/oci8.c index 0527b558474b9..8751ee52cdc96 100644 --- a/ext/oci8/oci8.c +++ b/ext/oci8/oci8.c @@ -34,6 +34,7 @@ #include "ext/standard/info.h" #include "php_ini.h" #include "zend_smart_str.h" +#include "zend_hash_func.h" #if HAVE_OCI8 diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 769ce92049629..00ef37d3b208a 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -373,7 +373,7 @@ static void accel_interned_strings_restore_state(void) while (idx > 0) { idx--; p = ZCSG(interned_strings).arData + idx; - if ((char*)p->key < ZCSG(interned_strings_top)) break; + if ((char*)p->key.str < ZCSG(interned_strings_top)) break; ZCSG(interned_strings).nNumUsed--; ZCSG(interned_strings).nNumOfElements--; @@ -424,9 +424,9 @@ static zend_string *accel_find_interned_string(zend_string *str) arData = ZCSG(interned_strings).arData; while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET_EX(arData, idx); - if ((p->h == h) && (ZSTR_LEN(p->key) == ZSTR_LEN(str))) { - if (!memcmp(ZSTR_VAL(p->key), ZSTR_VAL(str), ZSTR_LEN(str))) { - return p->key; + if ((p->h == h) && (ZSTR_LEN(p->key.str) == ZSTR_LEN(str))) { + if (!memcmp(ZSTR_VAL(p->key.str), ZSTR_VAL(str), ZSTR_LEN(str))) { + return p->key.str; } } idx = Z_NEXT(p->val); @@ -463,10 +463,10 @@ zend_string *accel_new_interned_string(zend_string *str) idx = HT_HASH(&ZCSG(interned_strings), nIndex); while (idx != HT_INVALID_IDX) { p = HT_HASH_TO_BUCKET(&ZCSG(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.str) == ZSTR_LEN(str))) { + if (!memcmp(ZSTR_VAL(p->key.str), ZSTR_VAL(str), ZSTR_LEN(str))) { zend_string_release(str); - return p->key; + return p->key.str; } } idx = Z_NEXT(p->val); @@ -484,25 +484,25 @@ zend_string *accel_new_interned_string(zend_string *str) idx = ZCSG(interned_strings).nNumUsed++; ZCSG(interned_strings).nNumOfElements++; p = ZCSG(interned_strings).arData + idx; - p->key = (zend_string*) ZCSG(interned_strings_top); + p->key.str = (zend_string*) ZCSG(interned_strings_top); ZCSG(interned_strings_top) += ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(ZSTR_LEN(str))); p->h = h; - GC_REFCOUNT(p->key) = 1; + GC_REFCOUNT(p->key.str) = 1; #if 1 /* optimized single assignment */ - GC_TYPE_INFO(p->key) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << 8); + GC_TYPE_INFO(p->key.str) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << 8); #else GC_TYPE(p->key) = IS_STRING; GC_FLAGS(p->key) = IS_STR_INTERNED | IS_STR_PERMANENT; #endif - ZSTR_H(p->key) = ZSTR_H(str); - ZSTR_LEN(p->key) = ZSTR_LEN(str); - memcpy(ZSTR_VAL(p->key), ZSTR_VAL(str), ZSTR_LEN(str)); - ZVAL_INTERNED_STR(&p->val, p->key); + ZSTR_H(p->key.str) = ZSTR_H(str); + ZSTR_LEN(p->key.str) = ZSTR_LEN(str); + memcpy(ZSTR_VAL(p->key.str), ZSTR_VAL(str), ZSTR_LEN(str)); + ZVAL_INTERNED_STR(&p->val, p->key.str); Z_NEXT(p->val) = HT_HASH(&ZCSG(interned_strings), nIndex); HT_HASH(&ZCSG(interned_strings), nIndex) = HT_IDX_TO_HASH(idx); zend_string_release(str); - return p->key; + return p->key.str; #else return str; #endif @@ -531,8 +531,8 @@ static void accel_use_shm_interned_strings(void) for (idx = 0; idx < CG(function_table)->nNumUsed; idx++) { p = CG(function_table)->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (p->key) { - p->key = accel_new_interned_string(p->key); + if (zend_bucket_has_str_key(p)) { + p->key.str = accel_new_interned_string(p->key.str); } if (Z_FUNC(p->val)->common.function_name) { Z_FUNC(p->val)->common.function_name = accel_new_interned_string(Z_FUNC(p->val)->common.function_name); @@ -547,8 +547,8 @@ static void accel_use_shm_interned_strings(void) if (Z_TYPE(p->val) == IS_UNDEF) continue; ce = (zend_class_entry*)Z_PTR(p->val); - if (p->key) { - p->key = accel_new_interned_string(p->key); + if (zend_bucket_has_str_key(p)) { + p->key.str = accel_new_interned_string(p->key.str); } if (ce->name) { @@ -563,8 +563,8 @@ static void accel_use_shm_interned_strings(void) info = (zend_property_info*)Z_PTR(q->val); - if (q->key) { - q->key = accel_new_interned_string(q->key); + if (zend_bucket_has_str_key(q)) { + q->key.str = accel_new_interned_string(q->key.str); } if (info->name) { @@ -575,8 +575,8 @@ static void accel_use_shm_interned_strings(void) for (j = 0; j < ce->function_table.nNumUsed; j++) { q = ce->function_table.arData + j; if (Z_TYPE(q->val) == IS_UNDEF) continue; - if (q->key) { - q->key = accel_new_interned_string(q->key); + if (zend_bucket_has_str_key(q)) { + q->key.str = accel_new_interned_string(q->key.str); } if (Z_FUNC(q->val)->common.function_name) { Z_FUNC(q->val)->common.function_name = accel_new_interned_string(Z_FUNC(q->val)->common.function_name); @@ -586,8 +586,8 @@ static void accel_use_shm_interned_strings(void) for (j = 0; j < ce->constants_table.nNumUsed; j++) { q = ce->constants_table.arData + j; if (!Z_TYPE(q->val) == IS_UNDEF) continue; - if (q->key) { - q->key = accel_new_interned_string(q->key); + if (zend_bucket_has_str_key(q)) { + q->key.str = accel_new_interned_string(q->key.str); } } } @@ -596,8 +596,8 @@ static void accel_use_shm_interned_strings(void) for (idx = 0; idx < EG(zend_constants)->nNumUsed; idx++) { p = EG(zend_constants)->arData + idx; if (!Z_TYPE(p->val) == IS_UNDEF) continue; - if (p->key) { - p->key = accel_new_interned_string(p->key); + if (zend_bucket_has_str_key(p)) { + p->key.str = accel_new_interned_string(p->key.str); } } @@ -612,8 +612,8 @@ static void accel_use_shm_interned_strings(void) zend_string_addref(auto_global->name); auto_global->name = accel_new_interned_string(auto_global->name); - if (p->key) { - p->key = accel_new_interned_string(p->key); + if (zend_bucket_has_str_key(p)) { + p->key.str = accel_new_interned_string(p->key.str); } } } @@ -2026,8 +2026,8 @@ static void accel_reset_pcre_cache(void) ZEND_HASH_FOREACH_BUCKET(&PCRE_G(pcre_cache), p) { /* Remove PCRE cache entries with inconsistent keys */ - if (zend_accel_in_shm(p->key)) { - p->key = NULL; + if (zend_accel_in_shm(p->key.str)) { + p->h &= ~HT_IS_STR_BIT; zend_hash_del_bucket(&PCRE_G(pcre_cache), p); } } ZEND_HASH_FOREACH_END(); diff --git a/ext/opcache/zend_accelerator_hash.c b/ext/opcache/zend_accelerator_hash.c index a533d06120fe5..e5c7d783a6cce 100644 --- a/ext/opcache/zend_accelerator_hash.c +++ b/ext/opcache/zend_accelerator_hash.c @@ -23,6 +23,7 @@ #include "zend_accelerator_hash.h" #include "zend_hash.h" #include "zend_shared_alloc.h" +#include "zend_hash_func.h" /* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */ static uint prime_numbers[] = diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 61c9c15d2b10f..5129ed1b8b619 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -126,7 +126,7 @@ void zend_accel_move_user_functions(HashTable *src, HashTable *dst) zend_function *function = Z_PTR(p->val); if (EXPECTED(function->type == ZEND_USER_FUNCTION)) { - _zend_hash_append_ptr(dst, p->key, function); + _zend_hash_append_ptr(dst, p->key.str, function); zend_hash_del_bucket(src, p); } else { break; @@ -258,7 +258,7 @@ static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class /* Initialize key */ q->h = p->h; - ZEND_ASSERT(p->key != NULL); + ZEND_ASSERT(zend_bucket_has_str_key(p)); q->key = p->key; /* Copy data */ @@ -316,7 +316,7 @@ static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_cla /* Initialize key */ q->h = p->h; - ZEND_ASSERT(p->key != NULL); + ZEND_ASSERT(zend_bucket_has_str_key(p)); q->key = p->key; /* Copy data */ @@ -481,17 +481,17 @@ static void zend_accel_function_hash_copy(HashTable *target, HashTable *source) end = p + source->nNumUsed; for (; p != end; p++) { if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; - ZEND_ASSERT(p->key); - t = zend_hash_find(target, p->key); + ZEND_ASSERT(zend_bucket_has_str_key(p)); + t = zend_hash_find(target, p->key.str); if (UNEXPECTED(t != NULL)) { - if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { + if (EXPECTED(ZSTR_LEN(p->key.str) > 0) && EXPECTED(ZSTR_VAL(p->key.str)[0] == 0)) { /* Mangled key */ - t = zend_hash_update(target, p->key, &p->val); + t = zend_hash_update(target, p->key.str, &p->val); } else { goto failure; } } else { - _zend_hash_append_ptr(target, p->key, Z_PTR(p->val)); + _zend_hash_append_ptr(target, p->key.str, Z_PTR(p->val)); } } target->nInternalPointer = target->nNumOfElements ? 0 : HT_INVALID_IDX; @@ -525,17 +525,17 @@ static void zend_accel_function_hash_copy_from_shm(HashTable *target, HashTable end = p + source->nNumUsed; for (; p != end; p++) { if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; - ZEND_ASSERT(p->key); - t = zend_hash_find(target, p->key); + ZEND_ASSERT(zend_bucket_has_str_key(p)); + t = zend_hash_find(target, p->key.str); if (UNEXPECTED(t != NULL)) { - if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { + if (EXPECTED(ZSTR_LEN(p->key.str) > 0) && EXPECTED(ZSTR_VAL(p->key.str)[0] == 0)) { /* Mangled key */ - zend_hash_update_ptr(target, p->key, ARENA_REALLOC(Z_PTR(p->val))); + zend_hash_update_ptr(target, p->key.str, ARENA_REALLOC(Z_PTR(p->val))); } else { goto failure; } } else { - _zend_hash_append_ptr(target, p->key, ARENA_REALLOC(Z_PTR(p->val))); + _zend_hash_append_ptr(target, p->key.str, ARENA_REALLOC(Z_PTR(p->val))); } } target->nInternalPointer = target->nNumOfElements ? 0 : HT_INVALID_IDX; @@ -568,10 +568,10 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source, uni end = p + source->nNumUsed; for (; p != end; p++) { if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; - ZEND_ASSERT(p->key); - t = zend_hash_find(target, p->key); + ZEND_ASSERT(zend_bucket_has_str_key(p)); + t = zend_hash_find(target, p->key.str); if (UNEXPECTED(t != NULL)) { - if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { + if (EXPECTED(ZSTR_LEN(p->key.str) > 0) && EXPECTED(ZSTR_VAL(p->key.str)[0] == 0)) { /* Mangled key - ignore and wait for runtime */ continue; } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { @@ -588,7 +588,7 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source, uni continue; } } else { - t = _zend_hash_append_ptr(target, p->key, Z_PTR(p->val)); + t = _zend_hash_append_ptr(target, p->key.str, Z_PTR(p->val)); if (pCopyConstructor) { pCopyConstructor(&Z_PTR_P(t)); } diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 6d34851352a42..04f72643acf42 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -259,7 +259,7 @@ static void zend_file_cache_serialize_hash(HashTable *ht, end = p + ht->nNumUsed; while (p < end) { if (Z_TYPE(p->val) != IS_UNDEF) { - SERIALIZE_STR(p->key); + SERIALIZE_STR(p->key.str); func(&p->val, script, info, buf); } p++; @@ -845,7 +845,7 @@ static void zend_file_cache_unserialize_hash(HashTable *ht, end = p + ht->nNumUsed; while (p < end) { if (Z_TYPE(p->val) != IS_UNDEF) { - UNSERIALIZE_STR(p->key); + UNSERIALIZE_STR(p->key.str); func(&p->val, script, buf); } p++; diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index e022b40950a94..1d12ba3468ea9 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -116,8 +116,8 @@ static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement if (Z_TYPE(p->val) == IS_UNDEF) continue; /* persist bucket and key */ - if (p->key) { - zend_accel_store_interned_string(p->key); + if (zend_bucket_has_str_key(p)) { + zend_accel_store_interned_string(p->key.str); } /* persist the data itself */ @@ -144,8 +144,8 @@ static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement if (Z_TYPE(p->val) == IS_UNDEF) continue; /* persist bucket and key */ - if (p->key) { - zend_accel_store_interned_string(p->key); + if (zend_bucket_has_str_key(p)) { + zend_accel_store_interned_string(p->key.str); } /* persist the data itself */ @@ -198,8 +198,8 @@ static void zend_hash_persist_immutable(HashTable *ht) if (Z_TYPE(p->val) == IS_UNDEF) continue; /* persist bucket and key */ - if (p->key) { - zend_accel_memdup_interned_string(p->key); + if (zend_bucket_has_str_key(p)) { + zend_accel_memdup_interned_string(p->key.str); } /* persist the data itself */ @@ -223,8 +223,8 @@ static void zend_hash_persist_immutable(HashTable *ht) if (Z_TYPE(p->val) == IS_UNDEF) continue; /* persist bucket and key */ - if (p->key) { - zend_accel_memdup_interned_string(p->key); + if (zend_bucket_has_str_key(p)) { + zend_accel_memdup_interned_string(p->key.str); } /* persist the data itself */ diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 84cd417204c92..808c46d6db7d4 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -82,10 +82,10 @@ static void zend_hash_persist_calc(HashTable *ht, void (*pPersistElement)(zval * if (Z_TYPE(p->val) == IS_UNDEF) continue; /* persist bucket and key */ - if (p->key) { - zend_uchar flags = GC_FLAGS(p->key) & ~ (IS_STR_PERSISTENT | IS_STR_INTERNED | IS_STR_PERMANENT); - ADD_INTERNED_STRING(p->key, 1); - GC_FLAGS(p->key) |= flags; + if (zend_bucket_has_str_key(p)) { + zend_uchar flags = GC_FLAGS(p->key.str) & ~ (IS_STR_PERSISTENT | IS_STR_INTERNED | IS_STR_PERMANENT); + ADD_INTERNED_STRING(p->key.str, 1); + GC_FLAGS(p->key.str) |= flags; } pPersistElement(&p->val); diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index d44b3be298800..a9e3b2e536704 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -160,7 +160,9 @@ static int phar_compare_dir_name(const void *a, const void *b) /* {{{ */ f = (Bucket *) a; s = (Bucket *) b; - result = zend_binary_strcmp(ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key)); + result = zend_binary_strcmp( + ZSTR_VAL(f->key.str), ZSTR_LEN(f->key.str), + ZSTR_VAL(s->key.str), ZSTR_LEN(s->key.str)); if (result < 0) { return -1; diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 5706c2095244b..214d5556b3922 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -904,7 +904,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from uint to_len = strlen(resource_to->path+1); ZEND_HASH_FOREACH_BUCKET(&phar->manifest, b) { - str_key = b->key; + str_key = b->key.str; entry = Z_PTR(b->val); if (!entry->is_deleted && ZSTR_LEN(str_key) > from_len && @@ -925,13 +925,13 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from zend_string_release(str_key); b->h = zend_string_hash_val(new_str_key); - b->key = new_str_key; + b->key.str = new_str_key; } } ZEND_HASH_FOREACH_END(); zend_hash_rehash(&phar->manifest); ZEND_HASH_FOREACH_BUCKET(&phar->virtual_dirs, b) { - str_key = b->key; + str_key = b->key.str; if (ZSTR_LEN(str_key) >= from_len && memcmp(ZSTR_VAL(str_key), resource_from->path+1, from_len) == 0 && (ZSTR_LEN(str_key) == from_len || IS_SLASH(ZSTR_VAL(str_key)[from_len]))) { @@ -943,13 +943,13 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from zend_string_release(str_key); b->h = zend_string_hash_val(new_str_key); - b->key = new_str_key; + b->key.str = new_str_key; } } ZEND_HASH_FOREACH_END(); zend_hash_rehash(&phar->virtual_dirs); ZEND_HASH_FOREACH_BUCKET(&phar->mounted_dirs, b) { - str_key = b->key; + str_key = b->key.str; if (ZSTR_LEN(str_key) >= from_len && memcmp(ZSTR_VAL(str_key), resource_from->path+1, from_len) == 0 && (ZSTR_LEN(str_key) == from_len || IS_SLASH(ZSTR_VAL(str_key)[from_len]))) { @@ -961,7 +961,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from zend_string_release(str_key); b->h = zend_string_hash_val(new_str_key); - b->key = new_str_key; + b->key.str = new_str_key; } } ZEND_HASH_FOREACH_END(); zend_hash_rehash(&phar->mounted_dirs); diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index a8f28fbf1eca2..2bf6d58c2c81d 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -2185,8 +2185,9 @@ static inline int array_num_elements(HashTable* ht) { if (ht->nNumUsed && Z_TYPE(ht->arData[ht->nNumUsed-1].val) != IS_UNDEF && - ht->arData[ht->nNumUsed-1].key == NULL) { - return ht->arData[ht->nNumUsed-1].h - 1; + !zend_bucket_has_str_key(&ht->arData[ht->nNumUsed-1])) { + + return ht->arData[ht->nNumUsed-1].key.num - 1; } return 0; } diff --git a/ext/standard/array.c b/ext/standard/array.c index ccfb3dc7688c3..3144ad9909f14 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -48,6 +48,7 @@ #include "php_math.h" #include "zend_smart_str.h" #include "zend_bitset.h" +#include "zend_hash_func.h" #include "ext/spl/spl_array.h" /* {{{ defines */ @@ -147,12 +148,12 @@ static int php_array_key_compare(const void *a, const void *b) /* {{{ */ zend_long l1, l2; double d; - if (f->key == NULL) { - if (s->key == NULL) { - return (zend_long)f->h > (zend_long)s->h ? 1 : -1; + if (!zend_bucket_has_str_key(f)) { + if (!zend_bucket_has_str_key(s)) { + return (zend_long)f->key.num > (zend_long)s->key.num ? 1 : -1; } else { - l1 = (zend_long)f->h; - t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1); + l1 = (zend_long)f->key.num; + t = is_numeric_string(ZSTR_VAL(s->key.str), ZSTR_LEN(s->key.str), &l2, &d, 1); if (t == IS_LONG) { /* pass */ } else if (t == IS_DOUBLE) { @@ -162,11 +163,11 @@ static int php_array_key_compare(const void *a, const void *b) /* {{{ */ } } } else { - if (s->key) { - return zendi_smart_strcmp(f->key, s->key); + if (zend_bucket_has_str_key(s)) { + return zendi_smart_strcmp(f->key.str, s->key.str); } else { - l2 = (zend_long)s->h; - t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1); + l2 = (zend_long)s->key.num; + t = is_numeric_string(ZSTR_VAL(f->key.str), ZSTR_LEN(f->key.str), &l1, &d, 1); if (t == IS_LONG) { /* pass */ } else if (t == IS_DOUBLE) { @@ -191,19 +192,19 @@ static int php_array_key_compare_numeric(const void *a, const void *b) /* {{{ */ Bucket *f = (Bucket *) a; Bucket *s = (Bucket *) b; - if (f->key == NULL && s->key == NULL) { - return (zend_long)f->h > (zend_long)s->h ? 1 : -1; + if (!zend_bucket_has_str_key(f) && !zend_bucket_has_str_key(s)) { + return (zend_long)f->key.num > (zend_long)s->key.num ? 1 : -1; } else { double d1, d2; - if (f->key) { - d1 = zend_strtod(f->key->val, NULL); + if (zend_bucket_has_str_key(f)) { + d1 = zend_strtod(ZSTR_VAL(f->key.str), NULL); } else { - d1 = (double)(zend_long)f->h; + d1 = (double)(zend_long)f->key.num; } - if (s->key) { - d2 = zend_strtod(s->key->val, NULL); + if (zend_bucket_has_str_key(s)) { + d2 = zend_strtod(ZSTR_VAL(s->key.str), NULL); } else { - d2 = (double)(zend_long)s->h; + d2 = (double)(zend_long)s->key.num; } return ZEND_NORMALIZE_BOOL(d1 - d2); } @@ -225,18 +226,18 @@ static int php_array_key_compare_string_case(const void *a, const void *b) /* {{ char buf1[MAX_LENGTH_OF_LONG + 1]; char buf2[MAX_LENGTH_OF_LONG + 1]; - if (f->key) { - s1 = f->key->val; - l1 = f->key->len; + if (zend_bucket_has_str_key(f)) { + s1 = ZSTR_VAL(f->key.str); + l1 = ZSTR_LEN(f->key.str); } else { - s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->key.num); l1 = buf1 + sizeof(buf1) - 1 - s1; } - if (s->key) { - s2 = s->key->val; - l2 = s->key->len; + if (zend_bucket_has_str_key(s)) { + s2 = ZSTR_VAL(s->key.str); + l2 = ZSTR_LEN(s->key.str); } else { - s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->key.num); l2 = buf2 + sizeof(buf2) - 1 - s1; } return zend_binary_strcasecmp_l(s1, l1, s2, l2); @@ -258,18 +259,18 @@ static int php_array_key_compare_string(const void *a, const void *b) /* {{{ */ char buf1[MAX_LENGTH_OF_LONG + 1]; char buf2[MAX_LENGTH_OF_LONG + 1]; - if (f->key) { - s1 = f->key->val; - l1 = f->key->len; + if (zend_bucket_has_str_key(f)) { + s1 = ZSTR_VAL(f->key.str); + l1 = ZSTR_LEN(f->key.str); } else { - s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->key.num); l1 = buf1 + sizeof(buf1) - 1 - s1; } - if (s->key) { - s2 = s->key->val; - l2 = s->key->len; + if (zend_bucket_has_str_key(s)) { + s2 = ZSTR_VAL(s->key.str); + l2 = ZSTR_LEN(s->key.str); } else { - s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->key.num); l2 = buf2 + sizeof(buf2) - 1 - s2; } return zend_binary_strcmp(s1, l1, s2, l2); @@ -291,18 +292,18 @@ static int php_array_key_compare_string_natural_general(const void *a, const voi char buf1[MAX_LENGTH_OF_LONG + 1]; char buf2[MAX_LENGTH_OF_LONG + 1]; - if (f->key) { - s1 = f->key->val; - l1 = f->key->len; + if (zend_bucket_has_str_key(f)) { + s1 = ZSTR_VAL(f->key.str); + l1 = ZSTR_LEN(f->key.str); } else { - s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->key.num); l1 = buf1 + sizeof(buf1) - 1 - s1; } - if (s->key) { - s2 = s->key->val; - l2 = s->key->len; + if (zend_bucket_has_str_key(s)) { + s2 = ZSTR_VAL(s->key.str); + l2 = ZSTR_LEN(s->key.str); } else { - s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->key.num); l2 = buf2 + sizeof(buf2) - 1 - s1; } return strnatcmp_ex(s1, l1, s2, l2, fold_case); @@ -342,15 +343,15 @@ static int php_array_key_compare_string_locale(const void *a, const void *b) /* char buf1[MAX_LENGTH_OF_LONG + 1]; char buf2[MAX_LENGTH_OF_LONG + 1]; - if (f->key) { - s1 = f->key->val; + if (zend_bucket_has_str_key(f)) { + s1 = ZSTR_VAL(f->key.str); } else { - s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->key.num); } - if (s->key) { - s2 = s->key->val; + if (zend_bucket_has_str_key(s)) { + s2 = ZSTR_VAL(s->key.str); } else { - s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->key.num); } return strcoll(s1, s2); } @@ -1073,15 +1074,15 @@ static int php_array_user_key_compare(const void *a, const void *b) /* {{{ */ f = (Bucket *) a; s = (Bucket *) b; - if (f->key == NULL) { - ZVAL_LONG(&args[0], f->h); + if (!zend_bucket_has_str_key(f)) { + ZVAL_LONG(&args[0], f->key.num); } else { - ZVAL_STR_COPY(&args[0], f->key); + ZVAL_STR_COPY(&args[0], f->key.str); } - if (s->key == NULL) { - ZVAL_LONG(&args[1], s->h); + if (!zend_bucket_has_str_key(s)) { + ZVAL_LONG(&args[1], s->key.num); } else { - ZVAL_STR_COPY(&args[1], s->key); + ZVAL_STR_COPY(&args[1], s->key.str); } BG(user_compare_fci).param_count = 2; @@ -2004,8 +2005,9 @@ PHP_FUNCTION(array_fill) } while (num--) { ZVAL_COPY_VALUE(&p->val, val); - p->h = n++; - p->key = NULL; + p->key.num = n; + p->h = zend_hash_integer(n); + n++; p++; } } else { @@ -2351,11 +2353,11 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ for (j = 0; j < n_elems; j++) { p = hash->arData + j; - if (p->key) { - zend_string_release(p->key); + if (zend_bucket_has_str_key(p)) { + zend_string_release(p->key.str); } - p->h = j; - p->key = NULL; + p->key.num = j; + p->h = 0; } hash->nNextFreeElement = n_elems; if (!(hash->u.flags & HASH_FLAG_PACKED)) { @@ -2418,10 +2420,10 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H entry = &p->val; /* Update output hash depending on key type */ - if (p->key == NULL) { + if (!zend_bucket_has_str_key(p)) { zend_hash_next_index_insert_new(&out_hash, entry); } else { - zend_hash_add_new(&out_hash, p->key, entry); + zend_hash_add_new(&out_hash, p->key.str, entry); } if (idx == iter_pos) { if ((zend_long)idx != pos) { @@ -2442,15 +2444,15 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H if (Z_REFCOUNTED_P(entry)) { Z_ADDREF_P(entry); } - if (p->key == NULL) { + if (!zend_bucket_has_str_key(p)) { zend_hash_next_index_insert_new(removed, entry); - zend_hash_index_del(in_hash, p->h); + zend_hash_index_del(in_hash, p->key.num); } else { - zend_hash_add_new(removed, p->key, entry); + zend_hash_add_new(removed, p->key.str, entry); if (in_hash == &EG(symbol_table)) { - zend_delete_global_variable(p->key); + zend_delete_global_variable(p->key.str); } else { - zend_hash_del(in_hash, p->key); + zend_hash_del(in_hash, p->key.str); } } } @@ -2461,13 +2463,13 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H p = in_hash->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; pos2++; - if (p->key == NULL) { - zend_hash_index_del(in_hash, p->h); + if (!zend_bucket_has_str_key(p)) { + zend_hash_index_del(in_hash, p->key.num); } else { if (in_hash == &EG(symbol_table)) { - zend_delete_global_variable(p->key); + zend_delete_global_variable(p->key.str); } else { - zend_hash_del(in_hash, p->key); + zend_hash_del(in_hash, p->key.str); } } } @@ -2488,10 +2490,10 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H p = in_hash->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; entry = &p->val; - if (p->key == NULL) { + if (!zend_bucket_has_str_key(p)) { zend_hash_next_index_insert_new(&out_hash, entry); } else { - zend_hash_add_new(&out_hash, p->key, entry); + zend_hash_add_new(&out_hash, p->key.str, entry); } if (idx == iter_pos) { if ((zend_long)idx != pos) { @@ -2587,19 +2589,20 @@ PHP_FUNCTION(array_pop) ZVAL_DEREF(val); ZVAL_COPY(return_value, val); - if (!p->key && Z_ARRVAL_P(stack)->nNextFreeElement > 0 && p->h >= (zend_ulong)(Z_ARRVAL_P(stack)->nNextFreeElement - 1)) { + if (!zend_bucket_has_str_key(p) && Z_ARRVAL_P(stack)->nNextFreeElement > 0 + && p->key.num >= (zend_ulong)(Z_ARRVAL_P(stack)->nNextFreeElement - 1)) { Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1; } /* Delete the last value */ - if (p->key) { + if (zend_bucket_has_str_key(p)) { if (Z_ARRVAL_P(stack) == &EG(symbol_table)) { - zend_delete_global_variable(p->key); + zend_delete_global_variable(p->key.str); } else { - zend_hash_del(Z_ARRVAL_P(stack), p->key); + zend_hash_del(Z_ARRVAL_P(stack), p->key.str); } } else { - zend_hash_index_del(Z_ARRVAL_P(stack), p->h); + zend_hash_index_del(Z_ARRVAL_P(stack), p->key.num); } zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); @@ -2643,14 +2646,14 @@ PHP_FUNCTION(array_shift) ZVAL_COPY(return_value, val); /* Delete the first value */ - if (p->key) { + if (zend_bucket_has_str_key(p)) { if (Z_ARRVAL_P(stack) == &EG(symbol_table)) { - zend_delete_global_variable(p->key); + zend_delete_global_variable(p->key.str); } else { - zend_hash_del(Z_ARRVAL_P(stack), p->key); + zend_hash_del(Z_ARRVAL_P(stack), p->key.str); } } else { - zend_hash_index_del(Z_ARRVAL_P(stack), p->h); + zend_hash_index_del(Z_ARRVAL_P(stack), p->key.num); } /* re-index like it did before */ @@ -2663,8 +2666,8 @@ PHP_FUNCTION(array_shift) if (Z_TYPE(p->val) == IS_UNDEF) continue; if (idx != k) { Bucket *q = Z_ARRVAL_P(stack)->arData + k; - q->h = k; - q->key = NULL; + q->key.num = k; + q->h = 0; ZVAL_COPY_VALUE(&q->val, &p->val); ZVAL_UNDEF(&p->val); } @@ -2678,8 +2681,8 @@ PHP_FUNCTION(array_shift) if (Z_TYPE(p->val) == IS_UNDEF) continue; if (idx != k) { Bucket *q = Z_ARRVAL_P(stack)->arData + k; - q->h = k; - q->key = NULL; + q->key.num = k; + q->h = 0; ZVAL_COPY_VALUE(&q->val, &p->val); ZVAL_UNDEF(&p->val); if (idx == iter_pos) { @@ -2699,13 +2702,13 @@ PHP_FUNCTION(array_shift) for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { p = Z_ARRVAL_P(stack)->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (p->key == NULL) { - if (p->h != k) { - p->h = k++; + if (!zend_bucket_has_str_key(p)) { + if (p->key.num != k) { + p->key.num = k; + p->h = zend_hash_integer(k); should_rehash = 1; - } else { - k++; } + k++; } } Z_ARRVAL_P(stack)->nNextFreeElement = k; @@ -3811,13 +3814,13 @@ PHP_FUNCTION(array_unique) } else { p = &cmpdata->b; } - if (p->key == NULL) { - zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); + if (!zend_bucket_has_str_key(p)) { + zend_hash_index_del(Z_ARRVAL_P(return_value), p->key.num); } else { if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) { - zend_delete_global_variable(p->key); + zend_delete_global_variable(p->key.str); } else { - zend_hash_del(Z_ARRVAL_P(return_value), p->key); + zend_hash_del(Z_ARRVAL_P(return_value), p->key.str); } } } @@ -3914,10 +3917,10 @@ static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compa if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) { ZVAL_UNREF(val); } - if (p->key == NULL) { + if (!zend_bucket_has_str_key(p)) { ok = 1; for (i = 1; i < argc; i++) { - if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) == NULL || + if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->key.num)) == NULL || (intersect_data_compare_func && intersect_data_compare_func(val, data) != 0) ) { @@ -3929,12 +3932,12 @@ static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compa if (Z_REFCOUNTED_P(val)) { Z_ADDREF_P(val); } - zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val); + zend_hash_index_update(Z_ARRVAL_P(return_value), p->key.num, val); } } else { ok = 1; for (i = 1; i < argc; i++) { - if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) == NULL || + if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key.str)) == NULL || (intersect_data_compare_func && intersect_data_compare_func(val, data) != 0) ) { @@ -3946,7 +3949,7 @@ static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compa if (Z_REFCOUNTED_P(val)) { Z_ADDREF_P(val); } - zend_hash_update(Z_ARRVAL_P(return_value), p->key, val); + zend_hash_update(Z_ARRVAL_P(return_value), p->key.str, val); } } } @@ -4155,10 +4158,10 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int if (Z_TYPE(p->val) == IS_UNDEF) { goto out; } - if (p->key == NULL) { - zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); + if (!zend_bucket_has_str_key(p)) { + zend_hash_index_del(Z_ARRVAL_P(return_value), p->key.num); } else { - zend_hash_del(Z_ARRVAL_P(return_value), p->key); + zend_hash_del(Z_ARRVAL_P(return_value), p->key.str); } } } @@ -4171,10 +4174,10 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int /* with value < value of ptrs[i] */ for (;;) { p = ptrs[0]; - if (p->key == NULL) { - zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); + if (!zend_bucket_has_str_key(p)) { + zend_hash_index_del(Z_ARRVAL_P(return_value), p->key.num); } else { - zend_hash_del(Z_ARRVAL_P(return_value), p->key); + zend_hash_del(Z_ARRVAL_P(return_value), p->key.str); } if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) { goto out; @@ -4337,10 +4340,10 @@ static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_ty if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) { ZVAL_UNREF(val); } - if (p->key == NULL) { + if (!zend_bucket_has_str_key(p)) { ok = 1; for (i = 1; i < argc; i++) { - if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) != NULL && + if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->key.num)) != NULL && (!diff_data_compare_func || diff_data_compare_func(val, data) == 0) ) { @@ -4352,12 +4355,12 @@ static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_ty if (Z_REFCOUNTED_P(val)) { Z_ADDREF_P(val); } - zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val); + zend_hash_index_update(Z_ARRVAL_P(return_value), p->key.num, val); } } else { ok = 1; for (i = 1; i < argc; i++) { - if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) != NULL && + if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key.str)) != NULL && (!diff_data_compare_func || diff_data_compare_func(val, data) == 0) ) { @@ -4369,7 +4372,7 @@ static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_ty if (Z_REFCOUNTED_P(val)) { Z_ADDREF_P(val); } - zend_hash_update(Z_ARRVAL_P(return_value), p->key, val); + zend_hash_update(Z_ARRVAL_P(return_value), p->key.str, val); } } } @@ -4591,10 +4594,10 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ /* delete all entries with value as ptrs[0] */ for (;;) { p = ptrs[0]; - if (p->key == NULL) { - zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); + if (!zend_bucket_has_str_key(p)) { + zend_hash_index_del(Z_ARRVAL_P(return_value), p->key.num); } else { - zend_hash_del(Z_ARRVAL_P(return_value), p->key); + zend_hash_del(Z_ARRVAL_P(return_value), p->key.str); } if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) { goto out; @@ -4958,8 +4961,10 @@ PHP_FUNCTION(array_multisort) for (n = 0, k = 0; k < array_size; k++) { hash->arData[k] = indirect[k][i]; - if (hash->arData[k].key == NULL) { - hash->arData[k].h = n++; + if (!zend_bucket_has_str_key(&hash->arData[k])) { + hash->arData[k].key.num = n; + hash->arData[k].h = zend_hash_integer(n); /* TODO */ + n++; } else { repack = 0; } @@ -5029,10 +5034,10 @@ PHP_FUNCTION(array_rand) zend_long randval = php_mt_rand_range(0, ht->nNumUsed - 1); Bucket *bucket = &ht->arData[randval]; if (!Z_ISUNDEF(bucket->val)) { - if (bucket->key) { - RETURN_STR_COPY(bucket->key); + if (zend_bucket_has_str_key(bucket)) { + RETURN_STR_COPY(bucket->key.str); } else { - RETURN_LONG(bucket->h); + RETURN_LONG(bucket->key.num); } } } while (1); diff --git a/ext/standard/html.c b/ext/standard/html.c index 4eff6981ceadd..2b7bcef0f6cac 100644 --- a/ext/standard/html.c +++ b/ext/standard/html.c @@ -84,6 +84,67 @@ #define sjis_lead(c) ((c) != 0x80 && (c) != 0xA0 && (c) < 0xFD) #define sjis_trail(c) ((c) >= 0x40 && (c) != 0x7F && (c) < 0xFD) +/* + * DJBX33A (Daniel J. Bernstein, Times 33 with Addition) + * + * This is Daniel J. Bernstein's popular `times 33' hash function as + * posted by him years ago on comp.lang.c. It basically uses a function + * like ``hash(i) = hash(i-1) * 33 + str[i]''. This is one of the best + * known hash functions for strings. Because it is both computed very + * fast and distributes very well. + * + * The magic of number 33, i.e. why it works better than many other + * constants, prime or not, has never been adequately explained by + * anyone. So I try an explanation: if one experimentally tests all + * multipliers between 1 and 256 (as RSE did now) one detects that even + * numbers are not useable at all. The remaining 128 odd numbers + * (except for the number 1) work more or less all equally well. They + * all distribute in an acceptable way and this way fill a hash table + * with an average percent of approx. 86%. + * + * If one compares the Chi^2 values of the variants, the number 33 not + * even has the best value. But the number 33 and a few other equally + * good numbers like 17, 31, 63, 127 and 129 have nevertheless a great + * advantage to the remaining numbers in the large set of possible + * multipliers: their multiply operation can be replaced by a faster + * operation based on just one shift plus either a single addition + * or subtraction operation. And because a hash function has to both + * distribute good _and_ has to be very fast to compute, those few + * numbers should be preferred and seems to be the reason why Daniel J. + * Bernstein also preferred it. + * + * + * -- Ralf S. Engelschall + */ +static inline zend_ulong djbx33a(const char *str, size_t len) +{ + register zend_ulong hash = Z_UL(5381); + + /* variant with the hash unrolled eight times */ + for (; len >= 8; len -= 8) { + hash = ((hash << 5) + hash) + *str++; + hash = ((hash << 5) + hash) + *str++; + hash = ((hash << 5) + hash) + *str++; + hash = ((hash << 5) + hash) + *str++; + hash = ((hash << 5) + hash) + *str++; + hash = ((hash << 5) + hash) + *str++; + hash = ((hash << 5) + hash) + *str++; + hash = ((hash << 5) + hash) + *str++; + } + switch (len) { + case 7: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ + case 6: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ + case 5: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ + case 4: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ + case 3: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ + case 2: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ + case 1: hash = ((hash << 5) + hash) + *str++; break; + case 0: break; +EMPTY_SWITCH_DEFAULT_CASE() + } + return hash; +} + /* {{{ get_default_charset */ static char *get_default_charset(void) { @@ -857,7 +918,7 @@ static inline int process_named_entity_html(const char **buf, const char **start static inline int resolve_named_entity_html(const char *start, size_t length, const entity_ht *ht, unsigned *uni_cp1, unsigned *uni_cp2) { const entity_cp_map *s; - zend_ulong hash = zend_inline_hash_func(start, length); + zend_ulong hash = djbx33a(start, length); s = ht->buckets[hash % ht->num_elems]; while (s->entity) { diff --git a/main/SAPI.c b/main/SAPI.c index e1c22bd18243f..2ae0f76ae6a46 100644 --- a/main/SAPI.c +++ b/main/SAPI.c @@ -80,6 +80,9 @@ SAPI_API void sapi_startup(sapi_module_struct *sf) sf->ini_entries = NULL; sapi_module = *sf; + /* TODO: Check if there's a better place to do this initialization */ + zend_initialize_siphash_key(); + #ifdef ZTS ts_allocate_id(&sapi_globals_id, sizeof(sapi_globals_struct), (ts_allocate_ctor) sapi_globals_ctor, (ts_allocate_dtor) sapi_globals_dtor); # ifdef PHP_WIN32 diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c index 4cb1dae42c6b1..08c5ba19832b0 100644 --- a/sapi/phpdbg/phpdbg_bp.c +++ b/sapi/phpdbg/phpdbg_bp.c @@ -25,6 +25,7 @@ #include "phpdbg_utils.h" #include "phpdbg_opcode.h" #include "zend_globals.h" +#include "zend_hash_func.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg) diff --git a/sapi/phpdbg/phpdbg_cmd.c b/sapi/phpdbg/phpdbg_cmd.c index f89e58a13c224..801e6267440a2 100644 --- a/sapi/phpdbg/phpdbg_cmd.c +++ b/sapi/phpdbg/phpdbg_cmd.c @@ -24,6 +24,7 @@ #include "phpdbg_set.h" #include "phpdbg_prompt.h" #include "phpdbg_io.h" +#include "zend_hash_func.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg) diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c index 63af56793be54..b8bc5856ffaba 100644 --- a/sapi/phpdbg/phpdbg_watch.c +++ b/sapi/phpdbg/phpdbg_watch.c @@ -607,7 +607,7 @@ void phpdbg_watch_parent_ht(phpdbg_watch_element *element) { zend_hash_internal_pointer_end_ex(hti->ht, &pos); hti->last = hti->ht->arData + pos; - hti->last_str = hti->last->key; + hti->last_str = hti->last->key.str; // ??? hti->last_idx = hti->last->h; zend_hash_add_ptr(&hti->watches, element->name_in_parent, element); @@ -1012,7 +1012,11 @@ void phpdbg_check_watchpoint(phpdbg_watchpoint_t *watch) { return; } if (watch->type == WATCH_ON_BUCKET) { - if (watch->backup.bucket.key != watch->addr.bucket->key || (watch->backup.bucket.key != NULL && watch->backup.bucket.h != watch->addr.bucket->h)) { + zend_bool same_key = watch->backup.bucket.h == watch->addr.bucket->h + && (zend_bucket_has_str_key(&watch->backup.bucket) + ? watch->backup.bucket.key.str == watch->addr.bucket->key.str + : watch->backup.bucket.key.num == watch->addr.bucket->key.num); + if (!same_key) { phpdbg_watch_element *element; zval *new;