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;