summaryrefslogtreecommitdiff
path: root/Zend/zend_hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_hash.c')
-rw-r--r--Zend/zend_hash.c129
1 files changed, 77 insertions, 52 deletions
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index d35d8afd53..6d065cd03e 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -68,7 +68,7 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin
zend_output_debug_string(1, "%s(%d) : ht=%p is inconsistent", file, line, ht);
break;
}
- ZEND_ASSERT(0);
+ ZEND_UNREACHABLE();
}
#define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
#define SET_INCONSISTENT(n) do { \
@@ -84,11 +84,33 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin
zend_hash_do_resize(ht); \
}
+ZEND_API void *zend_hash_str_find_ptr_lc(const HashTable *ht, const char *str, size_t len) {
+ void *result;
+ char *lc_str;
+
+ /* Stack allocate small strings to improve performance */
+ ALLOCA_FLAG(use_heap)
+
+ lc_str = zend_str_tolower_copy(do_alloca(len + 1, use_heap), str, len);
+ result = zend_hash_str_find_ptr(ht, lc_str, len);
+ free_alloca(lc_str, use_heap);
+
+ return result;
+}
+
+ZEND_API void *zend_hash_find_ptr_lc(const HashTable *ht, zend_string *key) {
+ void *result;
+ zend_string *lc_key = zend_string_tolower(key);
+ result = zend_hash_find_ptr(ht, lc_key);
+ zend_string_release(lc_key);
+ return result;
+}
+
static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht);
static zend_always_inline uint32_t zend_hash_check_size(uint32_t nSize)
{
-#if defined(ZEND_WIN32)
+#ifdef ZEND_WIN32
unsigned long index;
#endif
@@ -100,16 +122,16 @@ static zend_always_inline uint32_t zend_hash_check_size(uint32_t nSize)
zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", nSize, sizeof(Bucket), sizeof(Bucket));
}
-#if defined(ZEND_WIN32)
+#ifdef ZEND_WIN32
if (BitScanReverse(&index, nSize - 1)) {
- return 0x2 << ((31 - index) ^ 0x1f);
+ return 0x2u << ((31 - index) ^ 0x1f);
} else {
/* nSize is ensured to be in the valid range, fall back to it
rather than using an undefined bis scan result. */
return nSize;
}
#elif (defined(__GNUC__) || __has_builtin(__builtin_clz)) && defined(PHP_HAVE_BUILTIN_CLZ)
- return 0x2 << (__builtin_clz(nSize - 1) ^ 0x1f);
+ return 0x2u << (__builtin_clz(nSize - 1) ^ 0x1f);
#else
nSize -= 1;
nSize |= (nSize >> 1);
@@ -196,7 +218,7 @@ static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht)
HT_HASH_RESET(ht);
}
-static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, int packed)
+static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, bool packed)
{
HT_ASSERT_RC1(ht);
ZEND_ASSERT(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED);
@@ -227,14 +249,14 @@ ZEND_API const HashTable zend_empty_array = {
static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent)
{
GC_SET_REFCOUNT(ht, 1);
- GC_TYPE_INFO(ht) = IS_ARRAY | (persistent ? (GC_PERSISTENT << GC_FLAGS_SHIFT) : (GC_COLLECTABLE << GC_FLAGS_SHIFT));
+ GC_TYPE_INFO(ht) = GC_ARRAY | (persistent ? ((GC_PERSISTENT|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT) : 0);
HT_FLAGS(ht) = HASH_FLAG_UNINITIALIZED;
ht->nTableMask = HT_MIN_MASK;
HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
ht->nNumUsed = 0;
ht->nNumOfElements = 0;
ht->nInternalPointer = 0;
- ht->nNextFreeElement = 0;
+ ht->nNextFreeElement = ZEND_LONG_MIN;
ht->pDestructor = pDestructor;
ht->nTableSize = zend_hash_check_size(nSize);
}
@@ -964,6 +986,10 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht,
IS_CONSISTENT(ht);
HT_ASSERT_RC1(ht);
+ if ((flag & HASH_ADD_NEXT) && h == ZEND_LONG_MIN) {
+ h = 0;
+ }
+
if (HT_FLAGS(ht) & HASH_FLAG_PACKED) {
if (h < ht->nNumUsed) {
p = ht->arData + h;
@@ -1028,8 +1054,8 @@ convert_to_hash:
p = ht->arData + idx;
Z_NEXT(p->val) = HT_HASH(ht, nIndex);
HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
- if ((zend_long)h >= (zend_long)ht->nNextFreeElement) {
- ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX;
+ if ((zend_long)h >= ht->nNextFreeElement) {
+ ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX;
}
add:
ht->nNumOfElements++;
@@ -1168,7 +1194,7 @@ static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht)
}
}
-ZEND_API int ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
+ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
{
Bucket *p;
uint32_t nIndex, i;
@@ -1180,7 +1206,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
ht->nNumUsed = 0;
HT_HASH_RESET(ht);
}
- return SUCCESS;
+ return;
}
HT_HASH_RESET(ht);
@@ -1258,7 +1284,6 @@ ZEND_API int ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
_zend_hash_iterators_update(ht, old_num_used, ht->nNumUsed);
}
}
- return SUCCESS;
}
static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, Bucket *p, Bucket *prev)
@@ -1335,7 +1360,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_del_bucket(HashTable *ht, Bucket *p)
_zend_hash_del_el(ht, HT_IDX_TO_HASH(p - ht->arData), p);
}
-ZEND_API int ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key)
+ZEND_API zend_result ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key)
{
zend_ulong h;
uint32_t nIndex;
@@ -1365,7 +1390,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key)
return FAILURE;
}
-ZEND_API int ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key)
+ZEND_API zend_result ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key)
{
zend_ulong h;
uint32_t nIndex;
@@ -1413,7 +1438,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key)
return FAILURE;
}
-ZEND_API int ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const char *str, size_t len)
+ZEND_API zend_result ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const char *str, size_t len)
{
zend_ulong h;
uint32_t nIndex;
@@ -1457,7 +1482,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const char *str,
return FAILURE;
}
-ZEND_API int ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char *str, size_t len)
+ZEND_API zend_result ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char *str, size_t len)
{
zend_ulong h;
uint32_t nIndex;
@@ -1487,7 +1512,7 @@ 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 zend_result ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h)
{
uint32_t nIndex;
uint32_t idx;
@@ -1593,7 +1618,7 @@ ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht)
/* break possible cycles */
GC_REMOVE_FROM_BUFFER(ht);
- GC_TYPE_INFO(ht) = IS_NULL /*???| (GC_WHITE << 16)*/;
+ GC_TYPE_INFO(ht) = GC_NULL /*???| (GC_WHITE << 16)*/;
if (ht->nNumUsed) {
/* In some rare cases destructors of regular arrays may be changed */
@@ -1702,7 +1727,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht)
}
ht->nNumUsed = 0;
ht->nNumOfElements = 0;
- ht->nNextFreeElement = 0;
+ ht->nNextFreeElement = ZEND_LONG_MIN;
ht->nInternalPointer = 0;
}
@@ -1741,7 +1766,7 @@ ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht)
}
ht->nNumUsed = 0;
ht->nNumOfElements = 0;
- ht->nNextFreeElement = 0;
+ ht->nNextFreeElement = ZEND_LONG_MIN;
ht->nInternalPointer = 0;
}
@@ -1939,7 +1964,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source,
}
-static zend_always_inline int zend_array_dup_element(HashTable *source, HashTable *target, uint32_t idx, Bucket *p, Bucket *q, int packed, int static_keys, int with_holes)
+static zend_always_inline bool zend_array_dup_element(HashTable *source, HashTable *target, uint32_t idx, Bucket *p, Bucket *q, bool packed, bool static_keys, bool with_holes)
{
zval *data = &p->val;
@@ -1993,7 +2018,7 @@ static zend_always_inline int zend_array_dup_element(HashTable *source, HashTabl
return 1;
}
-static zend_always_inline void zend_array_dup_packed_elements(HashTable *source, HashTable *target, int with_holes)
+static zend_always_inline void zend_array_dup_packed_elements(HashTable *source, HashTable *target, bool with_holes)
{
Bucket *p = source->arData;
Bucket *q = target->arData;
@@ -2009,7 +2034,7 @@ static zend_always_inline void zend_array_dup_packed_elements(HashTable *source,
} while (p != end);
}
-static zend_always_inline uint32_t zend_array_dup_elements(HashTable *source, HashTable *target, int static_keys, int with_holes)
+static zend_always_inline uint32_t zend_array_dup_elements(HashTable *source, HashTable *target, bool static_keys, bool with_holes)
{
uint32_t idx = 0;
Bucket *p = source->arData;
@@ -2046,7 +2071,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source)
ALLOC_HASHTABLE(target);
GC_SET_REFCOUNT(target, 1);
- GC_TYPE_INFO(target) = IS_ARRAY | (GC_COLLECTABLE << GC_FLAGS_SHIFT);
+ GC_TYPE_INFO(target) = GC_ARRAY;
target->pDestructor = ZVAL_PTR_DTOR;
@@ -2304,7 +2329,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, Has
}
-ZEND_API int ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
+ZEND_API zend_result ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
{
uint32_t idx;
@@ -2329,7 +2354,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition
}
}
-ZEND_API int ZEND_FASTCALL zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
+ZEND_API zend_result ZEND_FASTCALL zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
{
uint32_t idx = *pos;
@@ -2432,15 +2457,15 @@ ZEND_API void zend_hash_bucket_swap(Bucket *p, Bucket *q)
zend_ulong h;
zend_string *key;
- ZVAL_COPY_VALUE(&val, &p->val);
+ val = p->val;
h = p->h;
key = p->key;
- ZVAL_COPY_VALUE(&p->val, &q->val);
+ p->val = q->val;
p->h = q->h;
p->key = q->key;
- ZVAL_COPY_VALUE(&q->val, &val);
+ q->val = val;
q->h = h;
q->key = key;
}
@@ -2449,9 +2474,9 @@ ZEND_API void zend_hash_bucket_renum_swap(Bucket *p, Bucket *q)
{
zval val;
- ZVAL_COPY_VALUE(&val, &p->val);
- ZVAL_COPY_VALUE(&p->val, &q->val);
- ZVAL_COPY_VALUE(&q->val, &val);
+ val = p->val;
+ p->val = q->val;
+ q->val = val;
}
ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q)
@@ -2459,17 +2484,17 @@ ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q)
zval val;
zend_ulong h;
- ZVAL_COPY_VALUE(&val, &p->val);
+ val = p->val;
h = p->h;
- ZVAL_COPY_VALUE(&p->val, &q->val);
+ p->val = q->val;
p->h = q->h;
- ZVAL_COPY_VALUE(&q->val, &val);
+ q->val = val;
q->h = h;
}
-ZEND_API int ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, compare_func_t compar, zend_bool renumber)
+ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, zend_bool renumber)
{
Bucket *p;
uint32_t i, j;
@@ -2477,28 +2502,34 @@ ZEND_API int ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, co
IS_CONSISTENT(ht);
HT_ASSERT_RC1(ht);
- if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
- return SUCCESS;
+ if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) {
+ /* Doesn't require sorting */
+ return;
}
if (HT_IS_WITHOUT_HOLES(ht)) {
- i = ht->nNumUsed;
+ /* Store original order of elements in extra space to allow stable sorting. */
+ for (i = 0; i < ht->nNumUsed; i++) {
+ Z_EXTRA(ht->arData[i].val) = i;
+ }
} else {
+ /* Remove holes and store original order. */
for (j = 0, i = 0; j < ht->nNumUsed; j++) {
p = ht->arData + j;
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
if (i != j) {
ht->arData[i] = *p;
}
+ Z_EXTRA(ht->arData[i].val) = i;
i++;
}
+ ht->nNumUsed = i;
}
- sort((void *)ht->arData, i, sizeof(Bucket), compar,
+ sort((void *)ht->arData, ht->nNumUsed, sizeof(Bucket), (compare_func_t) compar,
(swap_func_t)(renumber? zend_hash_bucket_renum_swap :
((HT_FLAGS(ht) & HASH_FLAG_PACKED) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap)));
- ht->nNumUsed = i;
ht->nInternalPointer = 0;
if (renumber) {
@@ -2533,8 +2564,6 @@ ZEND_API int ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, co
zend_hash_rehash(ht);
}
}
-
- return SUCCESS;
}
static zend_always_inline int zend_hash_compare_impl(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered) {
@@ -2633,19 +2662,15 @@ ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t co
zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");
}
- if (!(GC_FLAGS(ht1) & GC_IMMUTABLE)) {
- GC_PROTECT_RECURSION(ht1);
- }
+ GC_TRY_PROTECT_RECURSION(ht1);
result = zend_hash_compare_impl(ht1, ht2, compar, ordered);
- if (!(GC_FLAGS(ht1) & GC_IMMUTABLE)) {
- GC_UNPROTECT_RECURSION(ht1);
- }
+ GC_TRY_UNPROTECT_RECURSION(ht1);
return result;
}
-ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag)
+ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, bucket_compare_func_t compar, uint32_t flag)
{
uint32_t idx;
Bucket *p, *res;
@@ -2682,7 +2707,7 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_
return &res->val;
}
-ZEND_API int ZEND_FASTCALL _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx)
+ZEND_API bool ZEND_FASTCALL _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx)
{
register const char *tmp = key;