Coverage Report

Created: 2025-12-14 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/zend_hash.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend Engine                                                          |
4
   +----------------------------------------------------------------------+
5
   | Copyright (c) Zend Technologies Ltd. (http://www.zend.com)           |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to version 2.00 of the Zend license,     |
8
   | that is bundled with this package in the file LICENSE, and is        |
9
   | available through the world-wide-web at the following url:           |
10
   | http://www.zend.com/license/2_00.txt.                                |
11
   | If you did not receive a copy of the Zend license and are unable to  |
12
   | obtain it through the world-wide-web, please send a note to          |
13
   | license@zend.com so we can mail you a copy immediately.              |
14
   +----------------------------------------------------------------------+
15
   | Authors: Andi Gutmans <andi@php.net>                                 |
16
   |          Zeev Suraski <zeev@php.net>                                 |
17
   |          Dmitry Stogov <dmitry@php.net>                              |
18
   +----------------------------------------------------------------------+
19
*/
20
21
#include "zend.h"
22
#include "zend_globals.h"
23
#include "zend_variables.h"
24
25
#if defined(__aarch64__) || defined(_M_ARM64)
26
# include <arm_neon.h>
27
#endif
28
29
/* Prefer to use AVX2 instructions for better latency and throughput */
30
#if defined(__AVX2__)
31
# include <immintrin.h>
32
#elif defined( __SSE2__)
33
# include <emmintrin.h>
34
#endif
35
36
#if ZEND_DEBUG
37
# define HT_ASSERT(ht, expr) \
38
2.05G
  ZEND_ASSERT((expr) || (HT_FLAGS(ht) & HASH_FLAG_ALLOW_COW_VIOLATION))
39
#else
40
# define HT_ASSERT(ht, expr)
41
#endif
42
43
2.03G
#define HT_ASSERT_RC1(ht) HT_ASSERT(ht, GC_REFCOUNT(ht) == 1)
44
45
1.17k
#define HT_POISONED_PTR ((HashTable *) (intptr_t) -1)
46
47
#if ZEND_DEBUG
48
49
3.10G
#define HT_OK         0x00
50
0
#define HT_IS_DESTROYING    0x01
51
0
#define HT_DESTROYED      0x02
52
0
#define HT_CLEANING       0x03
53
54
static void _zend_is_inconsistent(const HashTable *ht, const char *file, int line)
55
3.10G
{
56
3.10G
  if ((HT_FLAGS(ht) & HASH_FLAG_CONSISTENCY) == HT_OK) {
57
3.10G
    return;
58
3.10G
  }
59
0
  switch (HT_FLAGS(ht) & HASH_FLAG_CONSISTENCY) {
60
0
    case HT_IS_DESTROYING:
61
0
      zend_output_debug_string(1, "%s(%d) : ht=%p is being destroyed", file, line, ht);
62
0
      break;
63
0
    case HT_DESTROYED:
64
0
      zend_output_debug_string(1, "%s(%d) : ht=%p is already destroyed", file, line, ht);
65
0
      break;
66
0
    case HT_CLEANING:
67
0
      zend_output_debug_string(1, "%s(%d) : ht=%p is being cleaned", file, line, ht);
68
0
      break;
69
0
    default:
70
0
      zend_output_debug_string(1, "%s(%d) : ht=%p is inconsistent", file, line, ht);
71
0
      break;
72
0
  }
73
0
  ZEND_UNREACHABLE();
74
0
}
75
3.10G
#define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
76
12.6M
#define SET_INCONSISTENT(n) do { \
77
12.6M
    HT_FLAGS(ht) = (HT_FLAGS(ht) & ~HASH_FLAG_CONSISTENCY) | (n); \
78
12.6M
  } while (0)
79
#else
80
#define IS_CONSISTENT(a)
81
#define SET_INCONSISTENT(n)
82
#endif
83
84
#define ZEND_HASH_IF_FULL_DO_RESIZE(ht)       \
85
1.02G
  if ((ht)->nNumUsed >= (ht)->nTableSize) {   \
86
99.0k
    zend_hash_do_resize(ht);          \
87
99.0k
  }
88
89
213k
ZEND_API void *zend_hash_str_find_ptr_lc(const HashTable *ht, const char *str, size_t len) {
90
213k
  void *result;
91
213k
  char *lc_str;
92
93
  /* Stack allocate small strings to improve performance */
94
213k
  ALLOCA_FLAG(use_heap)
95
96
213k
  lc_str = zend_str_tolower_copy(do_alloca(len + 1, use_heap), str, len);
97
213k
  result = zend_hash_str_find_ptr(ht, lc_str, len);
98
213k
  free_alloca(lc_str, use_heap);
99
100
213k
  return result;
101
213k
}
102
103
4.55k
ZEND_API void *zend_hash_find_ptr_lc(const HashTable *ht, zend_string *key) {
104
4.55k
  void *result;
105
4.55k
  zend_string *lc_key = zend_string_tolower(key);
106
4.55k
  result = zend_hash_find_ptr(ht, lc_key);
107
4.55k
  zend_string_release(lc_key);
108
4.55k
  return result;
109
4.55k
}
110
111
static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht);
112
113
static zend_always_inline uint32_t zend_hash_check_size(uint32_t nSize)
114
13.3M
{
115
#ifdef ZEND_WIN32
116
  unsigned long index;
117
#endif
118
119
  /* Use big enough power of 2 */
120
  /* size should be between HT_MIN_SIZE and HT_MAX_SIZE */
121
13.3M
  if (nSize <= HT_MIN_SIZE) {
122
12.2M
    return HT_MIN_SIZE;
123
12.2M
  } else if (UNEXPECTED(nSize > HT_MAX_SIZE)) {
124
0
    zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", nSize, sizeof(Bucket), sizeof(Bucket));
125
0
  }
126
127
#ifdef ZEND_WIN32
128
  if (BitScanReverse(&index, nSize - 1)) {
129
    return 0x2u << ((31 - index) ^ 0x1f);
130
  } else {
131
    /* nSize is ensured to be in the valid range, fall back to it
132
       rather than using an undefined bis scan result. */
133
    return nSize;
134
  }
135
#elif (defined(__GNUC__) || __has_builtin(__builtin_clz))  && defined(PHP_HAVE_BUILTIN_CLZ)
136
1.16M
  return 0x2u << (__builtin_clz(nSize - 1) ^ 0x1f);
137
#else
138
  nSize -= 1;
139
  nSize |= (nSize >> 1);
140
  nSize |= (nSize >> 2);
141
  nSize |= (nSize >> 4);
142
  nSize |= (nSize >> 8);
143
  nSize |= (nSize >> 16);
144
  return nSize + 1;
145
#endif
146
13.3M
}
147
148
static zend_always_inline void zend_hash_real_init_packed_ex(HashTable *ht)
149
3.76M
{
150
3.76M
  void *data;
151
152
3.76M
  if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {
153
1.16k
    data = pemalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK), 1);
154
3.75M
  } else if (EXPECTED(ht->nTableSize == HT_MIN_SIZE)) {
155
    /* Use specialized API with constant allocation amount for a particularly common case. */
156
3.74M
    data = emalloc(HT_PACKED_SIZE_EX(HT_MIN_SIZE, HT_MIN_MASK));
157
3.74M
  } else {
158
15.5k
    data = emalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK));
159
15.5k
  }
160
3.76M
  HT_SET_DATA_ADDR(ht, data);
161
  /* Don't overwrite iterator count. */
162
3.76M
  ht->u.v.flags = HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;
163
3.76M
  HT_HASH_RESET_PACKED(ht);
164
3.76M
}
165
166
static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht)
167
4.80M
{
168
4.80M
  void *data;
169
4.80M
  uint32_t nSize = ht->nTableSize;
170
171
4.80M
  ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
172
173
4.80M
  if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {
174
4.79k
    data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), 1);
175
4.79M
  } else if (EXPECTED(nSize == HT_MIN_SIZE)) {
176
4.16M
    data = emalloc(HT_SIZE_EX(HT_MIN_SIZE, HT_SIZE_TO_MASK(HT_MIN_SIZE)));
177
4.16M
    ht->nTableMask = HT_SIZE_TO_MASK(HT_MIN_SIZE);
178
4.16M
    HT_SET_DATA_ADDR(ht, data);
179
    /* Don't overwrite iterator count. */
180
4.16M
    ht->u.v.flags = HASH_FLAG_STATIC_KEYS;
181
#if defined(__AVX2__)
182
    do {
183
      __m256i ymm0 = _mm256_setzero_si256();
184
      ymm0 = _mm256_cmpeq_epi64(ymm0, ymm0);
185
      _mm256_storeu_si256((__m256i*)&HT_HASH_EX(data,  0), ymm0);
186
      _mm256_storeu_si256((__m256i*)&HT_HASH_EX(data,  8), ymm0);
187
    } while(0);
188
#elif defined (__SSE2__)
189
4.16M
    do {
190
4.16M
      __m128i xmm0 = _mm_setzero_si128();
191
4.16M
      xmm0 = _mm_cmpeq_epi8(xmm0, xmm0);
192
4.16M
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  0), xmm0);
193
4.16M
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  4), xmm0);
194
4.16M
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  8), xmm0);
195
4.16M
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data, 12), xmm0);
196
4.16M
    } while (0);
197
#elif defined(__aarch64__) || defined(_M_ARM64)
198
    do {
199
      int32x4_t t = vdupq_n_s32(-1);
200
      vst1q_s32((int32_t*)&HT_HASH_EX(data,  0), t);
201
      vst1q_s32((int32_t*)&HT_HASH_EX(data,  4), t);
202
      vst1q_s32((int32_t*)&HT_HASH_EX(data,  8), t);
203
      vst1q_s32((int32_t*)&HT_HASH_EX(data, 12), t);
204
    } while (0);
205
#else
206
    HT_HASH_EX(data,  0) = -1;
207
    HT_HASH_EX(data,  1) = -1;
208
    HT_HASH_EX(data,  2) = -1;
209
    HT_HASH_EX(data,  3) = -1;
210
    HT_HASH_EX(data,  4) = -1;
211
    HT_HASH_EX(data,  5) = -1;
212
    HT_HASH_EX(data,  6) = -1;
213
    HT_HASH_EX(data,  7) = -1;
214
    HT_HASH_EX(data,  8) = -1;
215
    HT_HASH_EX(data,  9) = -1;
216
    HT_HASH_EX(data, 10) = -1;
217
    HT_HASH_EX(data, 11) = -1;
218
    HT_HASH_EX(data, 12) = -1;
219
    HT_HASH_EX(data, 13) = -1;
220
    HT_HASH_EX(data, 14) = -1;
221
    HT_HASH_EX(data, 15) = -1;
222
#endif
223
4.16M
    return;
224
4.16M
  } else {
225
633k
    data = emalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)));
226
633k
  }
227
638k
  ht->nTableMask = HT_SIZE_TO_MASK(nSize);
228
638k
  HT_SET_DATA_ADDR(ht, data);
229
638k
  HT_FLAGS(ht) = HASH_FLAG_STATIC_KEYS;
230
638k
  HT_HASH_RESET(ht);
231
638k
}
232
233
static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, bool packed)
234
30.2k
{
235
30.2k
  HT_ASSERT_RC1(ht);
236
30.2k
  ZEND_ASSERT(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED);
237
30.2k
  if (packed) {
238
82
    zend_hash_real_init_packed_ex(ht);
239
30.1k
  } else {
240
30.1k
    zend_hash_real_init_mixed_ex(ht);
241
30.1k
  }
242
30.2k
}
243
244
static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
245
  {HT_INVALID_IDX, HT_INVALID_IDX};
246
247
ZEND_API const HashTable zend_empty_array = {
248
  .gc.refcount = 2,
249
  .gc.u.type_info = IS_ARRAY | (GC_IMMUTABLE << GC_FLAGS_SHIFT),
250
  .u.flags = HASH_FLAG_UNINITIALIZED,
251
  .nTableMask = HT_MIN_MASK,
252
  {.arData = (Bucket*)&uninitialized_bucket[2]},
253
  .nNumUsed = 0,
254
  .nNumOfElements = 0,
255
  .nTableSize = HT_MIN_SIZE,
256
  .nInternalPointer = 0,
257
  .nNextFreeElement = ZEND_LONG_MIN,
258
  .pDestructor = ZVAL_PTR_DTOR
259
};
260
261
static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent)
262
13.1M
{
263
13.1M
  GC_SET_REFCOUNT(ht, 1);
264
13.1M
  GC_TYPE_INFO(ht) = GC_ARRAY | (persistent ? ((GC_PERSISTENT|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT) : 0);
265
13.1M
  HT_FLAGS(ht) = HASH_FLAG_UNINITIALIZED;
266
13.1M
  ht->nTableMask = HT_MIN_MASK;
267
13.1M
  HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
268
13.1M
  ht->nNumUsed = 0;
269
13.1M
  ht->nNumOfElements = 0;
270
13.1M
  ht->nInternalPointer = 0;
271
13.1M
  ht->nNextFreeElement = ZEND_LONG_MIN;
272
13.1M
  ht->pDestructor = pDestructor;
273
13.1M
  ht->nTableSize = zend_hash_check_size(nSize);
274
13.1M
}
275
276
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent)
277
5.27M
{
278
5.27M
  _zend_hash_init_int(ht, nSize, pDestructor, persistent);
279
5.27M
}
280
281
ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_0(void)
282
0
{
283
0
  HashTable *ht = emalloc(sizeof(HashTable));
284
0
  _zend_hash_init_int(ht, HT_MIN_SIZE, ZVAL_PTR_DTOR, false);
285
0
  return ht;
286
0
}
287
288
ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t nSize)
289
7.89M
{
290
7.89M
  HashTable *ht = emalloc(sizeof(HashTable));
291
7.89M
  _zend_hash_init_int(ht, nSize, ZVAL_PTR_DTOR, false);
292
7.89M
  return ht;
293
7.89M
}
294
295
ZEND_API HashTable* ZEND_FASTCALL zend_new_pair(const zval *val1, const zval *val2)
296
0
{
297
0
  zval *zv;
298
0
  HashTable *ht = emalloc(sizeof(HashTable));
299
0
  _zend_hash_init_int(ht, HT_MIN_SIZE, ZVAL_PTR_DTOR, false);
300
0
  ht->nNumUsed = ht->nNumOfElements = ht->nNextFreeElement = 2;
301
0
  zend_hash_real_init_packed_ex(ht);
302
303
0
  zv = ht->arPacked;
304
0
  ZVAL_COPY_VALUE(zv, val1);
305
0
  zv++;
306
0
  ZVAL_COPY_VALUE(zv, val2);
307
0
  return ht;
308
0
}
309
310
ZEND_API void ZEND_FASTCALL zend_hash_packed_grow(HashTable *ht)
311
259k
{
312
259k
  HT_ASSERT_RC1(ht);
313
259k
  if (ht->nTableSize >= HT_MAX_SIZE) {
314
0
    zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket), sizeof(Bucket));
315
0
  }
316
259k
  uint32_t newTableSize = ht->nTableSize * 2;
317
259k
  HT_SET_DATA_ADDR(ht, perealloc2(HT_GET_DATA_ADDR(ht), HT_PACKED_SIZE_EX(newTableSize, HT_MIN_MASK), HT_PACKED_USED_SIZE(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT));
318
259k
  ht->nTableSize = newTableSize;
319
259k
}
320
321
ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, bool packed)
322
30.2k
{
323
30.2k
  IS_CONSISTENT(ht);
324
325
30.2k
  HT_ASSERT_RC1(ht);
326
30.2k
  zend_hash_real_init_ex(ht, packed);
327
30.2k
}
328
329
ZEND_API void ZEND_FASTCALL zend_hash_real_init_packed(HashTable *ht)
330
580k
{
331
580k
  IS_CONSISTENT(ht);
332
333
580k
  HT_ASSERT_RC1(ht);
334
580k
  zend_hash_real_init_packed_ex(ht);
335
580k
}
336
337
ZEND_API void ZEND_FASTCALL zend_hash_real_init_mixed(HashTable *ht)
338
4.77M
{
339
4.77M
  IS_CONSISTENT(ht);
340
341
4.77M
  HT_ASSERT_RC1(ht);
342
4.77M
  zend_hash_real_init_mixed_ex(ht);
343
4.77M
}
344
345
ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht)
346
20.2k
{
347
20.2k
  void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
348
20.2k
  zval *src = ht->arPacked;
349
20.2k
  Bucket *dst;
350
20.2k
  uint32_t i;
351
20.2k
  uint32_t nSize = ht->nTableSize;
352
353
20.2k
  ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
354
355
20.2k
  HT_ASSERT_RC1(ht);
356
  // Alloc before assign to avoid inconsistencies on OOM
357
20.2k
  new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
358
20.2k
  HT_FLAGS(ht) &= ~HASH_FLAG_PACKED;
359
20.2k
  ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
360
20.2k
  HT_SET_DATA_ADDR(ht, new_data);
361
20.2k
  dst = ht->arData;
362
221k
  for (i = 0; i < ht->nNumUsed; i++) {
363
200k
    ZVAL_COPY_VALUE(&dst->val, src);
364
200k
    dst->h = i;
365
200k
    dst->key = NULL;
366
200k
    dst++;
367
200k
    src++;
368
200k
  }
369
20.2k
  pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
370
20.2k
  zend_hash_rehash(ht);
371
20.2k
}
372
373
ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht)
374
0
{
375
0
  void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
376
0
  Bucket *src = ht->arData;
377
0
  zval *dst;
378
0
  uint32_t i;
379
380
0
  HT_ASSERT_RC1(ht);
381
0
  new_data = pemalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
382
0
  HT_FLAGS(ht) |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;
383
0
  ht->nTableMask = HT_MIN_MASK;
384
0
  HT_SET_DATA_ADDR(ht, new_data);
385
0
  HT_HASH_RESET_PACKED(ht);
386
0
  dst = ht->arPacked;
387
0
  for (i = 0; i < ht->nNumUsed; i++) {
388
0
    ZVAL_COPY_VALUE(dst, &src->val);
389
0
    dst++;
390
0
    src++;
391
0
  }
392
0
  pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
393
0
}
394
395
ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, bool packed)
396
329k
{
397
329k
  HT_ASSERT_RC1(ht);
398
399
329k
  if (nSize == 0) return;
400
401
326k
  ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
402
403
326k
  if (UNEXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
404
30.2k
    if (nSize > ht->nTableSize) {
405
1.76k
      ht->nTableSize = zend_hash_check_size(nSize);
406
1.76k
    }
407
30.2k
    zend_hash_real_init(ht, packed);
408
296k
  } else {
409
296k
    if (packed) {
410
35
      ZEND_ASSERT(HT_IS_PACKED(ht));
411
35
      if (nSize > ht->nTableSize) {
412
1
        uint32_t newTableSize = zend_hash_check_size(nSize);
413
1
        HT_SET_DATA_ADDR(ht, perealloc2(HT_GET_DATA_ADDR(ht), HT_PACKED_SIZE_EX(newTableSize, HT_MIN_MASK), HT_PACKED_USED_SIZE(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT));
414
1
        ht->nTableSize = newTableSize;
415
1
      }
416
296k
    } else {
417
296k
      ZEND_ASSERT(!HT_IS_PACKED(ht));
418
296k
      if (nSize > ht->nTableSize) {
419
209k
        void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
420
209k
        Bucket *old_buckets = ht->arData;
421
209k
        nSize = zend_hash_check_size(nSize);
422
209k
        new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
423
209k
        ht->nTableSize = nSize;
424
209k
        ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
425
209k
        HT_SET_DATA_ADDR(ht, new_data);
426
209k
        memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
427
209k
        pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
428
209k
        zend_hash_rehash(ht);
429
209k
      }
430
296k
    }
431
296k
  }
432
326k
}
433
434
ZEND_API void ZEND_FASTCALL zend_hash_discard(HashTable *ht, uint32_t nNumUsed)
435
0
{
436
0
  Bucket *p, *end, *arData;
437
0
  uint32_t nIndex;
438
439
0
  ZEND_ASSERT(!HT_IS_PACKED(ht));
440
0
  arData = ht->arData;
441
0
  p = arData + ht->nNumUsed;
442
0
  end = arData + nNumUsed;
443
0
  ht->nNumUsed = nNumUsed;
444
0
  while (p != end) {
445
0
    p--;
446
0
    if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
447
0
    ht->nNumOfElements--;
448
    /* Collision pointers always directed from higher to lower buckets */
449
#if 0
450
    if (!(Z_NEXT(p->val) == HT_INVALID_IDX || HT_HASH_TO_BUCKET_EX(arData, Z_NEXT(p->val)) < p)) {
451
      abort();
452
    }
453
#endif
454
0
    nIndex = p->h | ht->nTableMask;
455
0
    HT_HASH_EX(arData, nIndex) = Z_NEXT(p->val);
456
0
  }
457
0
}
458
459
static uint32_t zend_array_recalc_elements(const HashTable *ht)
460
1.26k
{
461
1.26k
  zval *val;
462
1.26k
  uint32_t num = ht->nNumOfElements;
463
464
7.17k
  ZEND_HASH_MAP_FOREACH_VAL(ht, val) {
465
7.17k
    if (Z_TYPE_P(val) == IS_INDIRECT) {
466
2.23k
      if (UNEXPECTED(Z_TYPE_P(Z_INDIRECT_P(val)) == IS_UNDEF)) {
467
1.27k
        num--;
468
1.27k
      }
469
2.23k
    }
470
7.17k
  } ZEND_HASH_FOREACH_END();
471
1.26k
  return num;
472
1.26k
}
473
/* }}} */
474
475
ZEND_API uint32_t zend_array_count(HashTable *ht)
476
19.1k
{
477
19.1k
  uint32_t num;
478
19.1k
  if (UNEXPECTED(HT_FLAGS(ht) & HASH_FLAG_HAS_EMPTY_IND)) {
479
1.26k
    num = zend_array_recalc_elements(ht);
480
1.26k
    if (UNEXPECTED(ht->nNumOfElements == num)) {
481
248
      HT_FLAGS(ht) &= ~HASH_FLAG_HAS_EMPTY_IND;
482
248
    }
483
17.9k
  } else if (UNEXPECTED(ht == &EG(symbol_table))) {
484
0
    num = zend_array_recalc_elements(ht);
485
17.9k
  } else {
486
17.9k
    num = zend_hash_num_elements(ht);
487
17.9k
  }
488
19.1k
  return num;
489
19.1k
}
490
/* }}} */
491
492
static zend_always_inline HashPosition _zend_hash_get_valid_pos(const HashTable *ht, HashPosition pos)
493
11.0k
{
494
11.0k
  if (HT_IS_PACKED(ht)) {
495
4.36k
    while (pos < ht->nNumUsed && Z_ISUNDEF(ht->arPacked[pos])) {
496
796
      pos++;
497
796
    }
498
7.53k
  } else {
499
7.77k
    while (pos < ht->nNumUsed && Z_ISUNDEF(ht->arData[pos].val)) {
500
240
      pos++;
501
240
    }
502
7.53k
  }
503
11.0k
  return pos;
504
11.0k
}
505
506
static zend_always_inline HashPosition _zend_hash_get_current_pos(const HashTable *ht)
507
924
{
508
924
  return _zend_hash_get_valid_pos(ht, ht->nInternalPointer);
509
924
}
510
511
ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos(const HashTable *ht)
512
187
{
513
187
  return _zend_hash_get_current_pos(ht);
514
187
}
515
516
ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos_ex(const HashTable *ht, HashPosition pos)
517
0
{
518
0
  return _zend_hash_get_valid_pos(ht, pos);
519
0
}
520
521
83
static void zend_hash_remove_iterator_copies(uint32_t idx) {
522
83
  HashTableIterator *iterators = EG(ht_iterators);
523
524
83
  HashTableIterator *iter = iterators + idx;
525
83
  uint32_t next_idx = iter->next_copy;
526
566
  while (next_idx != idx) {
527
483
    uint32_t cur_idx = next_idx;
528
483
    HashTableIterator *cur_iter = iterators + cur_idx;
529
483
    next_idx = cur_iter->next_copy;
530
483
    cur_iter->next_copy = cur_idx; // avoid recursion in zend_hash_iterator_del
531
483
    zend_hash_iterator_del(cur_idx);
532
483
  }
533
83
  iter->next_copy = idx;
534
83
}
535
536
ZEND_API uint32_t ZEND_FASTCALL zend_hash_iterator_add(HashTable *ht, HashPosition pos)
537
2.36k
{
538
2.36k
  HashTableIterator *iter = EG(ht_iterators);
539
2.36k
  HashTableIterator *end  = iter + EG(ht_iterators_count);
540
2.36k
  uint32_t idx;
541
542
2.36k
  if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
543
2.36k
    HT_INC_ITERATORS_COUNT(ht);
544
2.36k
  }
545
15.9k
  while (iter != end) {
546
15.8k
    if (iter->ht == NULL) {
547
2.32k
      iter->ht = ht;
548
2.32k
      iter->pos = pos;
549
2.32k
      idx = iter - EG(ht_iterators);
550
2.32k
      iter->next_copy = idx;
551
2.32k
      if (idx + 1 > EG(ht_iterators_used)) {
552
2.32k
        EG(ht_iterators_used) = idx + 1;
553
2.32k
      }
554
2.32k
      return idx;
555
2.32k
    }
556
13.5k
    iter++;
557
13.5k
  }
558
42
  if (EG(ht_iterators) == EG(ht_iterators_slots)) {
559
6
    EG(ht_iterators) = emalloc(sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
560
6
    memcpy(EG(ht_iterators), EG(ht_iterators_slots), sizeof(HashTableIterator) * EG(ht_iterators_count));
561
36
  } else {
562
36
    EG(ht_iterators) = erealloc(EG(ht_iterators), sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
563
36
  }
564
42
  iter = EG(ht_iterators) + EG(ht_iterators_count);
565
42
  EG(ht_iterators_count) += 8;
566
42
  iter->ht = ht;
567
42
  iter->pos = pos;
568
42
  memset(iter + 1, 0, sizeof(HashTableIterator) * 7);
569
42
  idx = iter - EG(ht_iterators);
570
42
  iter->next_copy = idx;
571
42
  EG(ht_iterators_used) = idx + 1;
572
42
  return idx;
573
2.36k
}
574
575
// To avoid losing track of the HashTable when separating arrays, we track all copies at once.
576
812
static zend_always_inline bool zend_hash_iterator_find_copy_pos(uint32_t idx, HashTable *ht) {
577
812
  HashTableIterator *iter = EG(ht_iterators) + idx;
578
579
812
  uint32_t next_idx = iter->next_copy;
580
812
  if (EXPECTED(next_idx != idx)) {
581
75
    HashTableIterator *copy_iter;
582
80
    while (next_idx != idx) {
583
80
      copy_iter = EG(ht_iterators) + next_idx;
584
80
      if (copy_iter->ht == ht) {
585
        // We have found the hashtable we are actually iterating over
586
        // Now clean any intermittent copies and replace the original index by the found one
587
75
        if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR)
588
65
          && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) {
589
65
          HT_DEC_ITERATORS_COUNT(iter->ht);
590
65
        }
591
75
        if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
592
75
          HT_INC_ITERATORS_COUNT(ht);
593
75
        }
594
75
        iter->ht = copy_iter->ht;
595
75
        iter->pos = copy_iter->pos;
596
75
        zend_hash_remove_iterator_copies(idx);
597
75
        return true;
598
75
      }
599
5
      next_idx = copy_iter->next_copy;
600
5
    }
601
0
    zend_hash_remove_iterator_copies(idx);
602
0
  }
603
604
737
  return false;
605
812
}
606
607
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos(uint32_t idx, HashTable *ht)
608
1.64k
{
609
1.64k
  HashTableIterator *iter = EG(ht_iterators) + idx;
610
611
1.64k
  ZEND_ASSERT(idx != (uint32_t)-1);
612
1.64k
  if (UNEXPECTED(iter->ht != ht) && !zend_hash_iterator_find_copy_pos(idx, ht)) {
613
5
    if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR)
614
0
        && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) {
615
0
      HT_DEC_ITERATORS_COUNT(iter->ht);
616
0
    }
617
5
    if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
618
5
      HT_INC_ITERATORS_COUNT(ht);
619
5
    }
620
5
    iter->ht = ht;
621
5
    iter->pos = _zend_hash_get_current_pos(ht);
622
5
  }
623
1.64k
  return iter->pos;
624
1.64k
}
625
626
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos_ex(uint32_t idx, zval *array)
627
7.07k
{
628
7.07k
  HashTable *ht = Z_ARRVAL_P(array);
629
7.07k
  HashTableIterator *iter = EG(ht_iterators) + idx;
630
631
7.07k
  ZEND_ASSERT(idx != (uint32_t)-1);
632
7.07k
  if (UNEXPECTED(iter->ht != ht) && !zend_hash_iterator_find_copy_pos(idx, ht)) {
633
732
    if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR)
634
0
        && EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
635
0
      HT_DEC_ITERATORS_COUNT(iter->ht);
636
0
    }
637
638
    /* Inlined SEPARATE_ARRAY() with updating of iterator when EG(ht_iterators) grows. */
639
732
    if (UNEXPECTED(GC_REFCOUNT(ht) > 1)) {
640
389
      ZVAL_ARR(array, zend_array_dup(ht));
641
389
      GC_TRY_DELREF(ht);
642
389
      iter = EG(ht_iterators) + idx;
643
389
      ht = Z_ARRVAL_P(array);
644
389
    }
645
646
732
    if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
647
732
      HT_INC_ITERATORS_COUNT(ht);
648
732
    }
649
732
    iter->ht = ht;
650
732
    iter->pos = _zend_hash_get_current_pos(ht);
651
732
  }
652
7.07k
  return iter->pos;
653
7.07k
}
654
655
ZEND_API void ZEND_FASTCALL zend_hash_iterator_del(uint32_t idx)
656
2.35k
{
657
2.35k
  HashTableIterator *iter = EG(ht_iterators) + idx;
658
659
2.35k
  ZEND_ASSERT(idx != (uint32_t)-1);
660
661
2.35k
  if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR)
662
1.92k
      && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) {
663
1.92k
    ZEND_ASSERT(HT_ITERATORS_COUNT(iter->ht) != 0);
664
1.92k
    HT_DEC_ITERATORS_COUNT(iter->ht);
665
1.92k
  }
666
2.35k
  iter->ht = NULL;
667
668
2.35k
  if (UNEXPECTED(iter->next_copy != idx)) {
669
8
    zend_hash_remove_iterator_copies(idx);
670
8
  }
671
672
2.35k
  if (idx == EG(ht_iterators_used) - 1) {
673
2.35k
    while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) {
674
26
      idx--;
675
26
    }
676
2.32k
    EG(ht_iterators_used) = idx;
677
2.32k
  }
678
2.35k
}
679
680
static zend_never_inline void ZEND_FASTCALL _zend_hash_iterators_remove(const HashTable *ht)
681
787
{
682
787
  HashTableIterator *iter = EG(ht_iterators);
683
787
  const HashTableIterator *end = iter + EG(ht_iterators_used);
684
685
14.8k
  while (iter != end) {
686
14.0k
    if (iter->ht == ht) {
687
1.17k
      iter->ht = HT_POISONED_PTR;
688
1.17k
    }
689
14.0k
    iter++;
690
14.0k
  }
691
787
}
692
693
static zend_always_inline void zend_hash_iterators_remove(const HashTable *ht)
694
8.87M
{
695
8.87M
  if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
696
787
    _zend_hash_iterators_remove(ht);
697
787
  }
698
8.87M
}
699
700
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(const HashTable *ht, HashPosition start)
701
1.18k
{
702
1.18k
  const HashTableIterator *iter = EG(ht_iterators);
703
1.18k
  const HashTableIterator *end = iter + EG(ht_iterators_used);
704
1.18k
  HashPosition res = ht->nNumUsed;
705
706
2.02k
  while (iter != end) {
707
837
    if (iter->ht == ht) {
708
822
      if (iter->pos >= start && iter->pos < res) {
709
219
        res = iter->pos;
710
219
      }
711
822
    }
712
837
    iter++;
713
837
  }
714
1.18k
  return res;
715
1.18k
}
716
717
ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(const HashTable *ht, HashPosition from, HashPosition to)
718
214
{
719
214
  HashTableIterator *iter = EG(ht_iterators);
720
214
  const HashTableIterator *end = iter + EG(ht_iterators_used);
721
722
481
  while (iter != end) {
723
267
    if (iter->ht == ht && iter->pos == from) {
724
186
      iter->pos = to;
725
186
    }
726
267
    iter++;
727
267
  }
728
214
}
729
730
ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(const HashTable *ht, HashPosition step)
731
7
{
732
7
  HashTableIterator *iter = EG(ht_iterators);
733
7
  const HashTableIterator *end = iter + EG(ht_iterators_used);
734
735
14
  while (iter != end) {
736
7
    if (iter->ht == ht) {
737
7
      iter->pos += step;
738
7
    }
739
7
    iter++;
740
7
  }
741
7
}
742
743
/* Hash must be known and precomputed before */
744
static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, const zend_string *key)
745
50.7M
{
746
50.7M
  uint32_t nIndex;
747
50.7M
  uint32_t idx;
748
50.7M
  Bucket *p, *arData;
749
750
50.7M
  ZEND_ASSERT(ZSTR_H(key) != 0 && "Hash must be known");
751
752
50.7M
  arData = ht->arData;
753
50.7M
  nIndex = ZSTR_H(key) | ht->nTableMask;
754
50.7M
  idx = HT_HASH_EX(arData, nIndex);
755
756
50.7M
  if (UNEXPECTED(idx == HT_INVALID_IDX)) {
757
21.8M
    return NULL;
758
21.8M
  }
759
28.8M
  p = HT_HASH_TO_BUCKET_EX(arData, idx);
760
28.8M
  if (EXPECTED(p->key == key)) { /* check for the same interned string */
761
18.6M
    return p;
762
18.6M
  }
763
764
11.8M
  while (1) {
765
11.8M
    if (p->h == ZSTR_H(key) &&
766
3.10M
        EXPECTED(p->key) &&
767
3.10M
        zend_string_equal_content(p->key, key)) {
768
3.06M
      return p;
769
3.06M
    }
770
8.79M
    idx = Z_NEXT(p->val);
771
8.79M
    if (idx == HT_INVALID_IDX) {
772
7.01M
      return NULL;
773
7.01M
    }
774
1.77M
    p = HT_HASH_TO_BUCKET_EX(arData, idx);
775
1.77M
    if (p->key == key) { /* check for the same interned string */
776
131k
      return p;
777
131k
    }
778
1.77M
  }
779
10.2M
}
780
781
static zend_always_inline Bucket *zend_hash_str_find_bucket(const HashTable *ht, const char *str, size_t len, zend_ulong h)
782
5.68M
{
783
5.68M
  uint32_t nIndex;
784
5.68M
  uint32_t idx;
785
5.68M
  Bucket *p, *arData;
786
787
5.68M
  arData = ht->arData;
788
5.68M
  nIndex = h | ht->nTableMask;
789
5.68M
  idx = HT_HASH_EX(arData, nIndex);
790
5.91M
  while (idx != HT_INVALID_IDX) {
791
1.45M
    ZEND_ASSERT(idx < HT_IDX_TO_HASH(ht->nTableSize));
792
1.45M
    p = HT_HASH_TO_BUCKET_EX(arData, idx);
793
1.45M
    if ((p->h == h)
794
1.22M
       && p->key
795
1.22M
       && zend_string_equals_cstr(p->key, str, len)) {
796
1.22M
      return p;
797
1.22M
    }
798
229k
    idx = Z_NEXT(p->val);
799
229k
  }
800
4.45M
  return NULL;
801
5.68M
}
802
803
static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *ht, zend_ulong h)
804
2.03G
{
805
2.03G
  uint32_t nIndex;
806
2.03G
  uint32_t idx;
807
2.03G
  Bucket *p, *arData;
808
809
2.03G
  arData = ht->arData;
810
2.03G
  nIndex = h | ht->nTableMask;
811
2.03G
  idx = HT_HASH_EX(arData, nIndex);
812
2.38G
  while (idx != HT_INVALID_IDX) {
813
1.35G
    ZEND_ASSERT(idx < HT_IDX_TO_HASH(ht->nTableSize));
814
1.35G
    p = HT_HASH_TO_BUCKET_EX(arData, idx);
815
1.35G
    if (p->h == h && !p->key) {
816
999M
      return p;
817
999M
    }
818
351M
    idx = Z_NEXT(p->val);
819
351M
  }
820
1.03G
  return NULL;
821
2.03G
}
822
823
static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_string *key, zval *pData, uint32_t flag)
824
7.68M
{
825
7.68M
  zend_ulong h;
826
7.68M
  uint32_t nIndex;
827
7.68M
  uint32_t idx;
828
7.68M
  Bucket *p, *arData;
829
830
7.68M
  IS_CONSISTENT(ht);
831
7.68M
  HT_ASSERT_RC1(ht);
832
7.68M
  zend_string_hash_val(key);
833
834
7.68M
  if (UNEXPECTED(HT_FLAGS(ht) & (HASH_FLAG_UNINITIALIZED|HASH_FLAG_PACKED))) {
835
1.28M
    if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
836
1.27M
      zend_hash_real_init_mixed(ht);
837
1.27M
      goto add_to_hash;
838
1.27M
    } else {
839
7.62k
      zend_hash_packed_to_hash(ht);
840
7.62k
    }
841
6.40M
  } else if ((flag & HASH_ADD_NEW) == 0 || ZEND_DEBUG) {
842
6.40M
    p = zend_hash_find_bucket(ht, key);
843
844
6.40M
    if (p) {
845
791k
      zval *data;
846
847
791k
      ZEND_ASSERT((flag & HASH_ADD_NEW) == 0);
848
791k
      if (flag & HASH_LOOKUP) {
849
206k
        return &p->val;
850
584k
      } else if (flag & HASH_ADD) {
851
139k
        if (!(flag & HASH_UPDATE_INDIRECT)) {
852
138k
          return NULL;
853
138k
        }
854
917
        ZEND_ASSERT(&p->val != pData);
855
917
        data = &p->val;
856
917
        if (Z_TYPE_P(data) == IS_INDIRECT) {
857
0
          data = Z_INDIRECT_P(data);
858
0
          if (Z_TYPE_P(data) != IS_UNDEF) {
859
0
            return NULL;
860
0
          }
861
917
        } else {
862
917
          return NULL;
863
917
        }
864
445k
      } else {
865
445k
        ZEND_ASSERT(&p->val != pData);
866
445k
        data = &p->val;
867
445k
        if ((flag & HASH_UPDATE_INDIRECT) && Z_TYPE_P(data) == IS_INDIRECT) {
868
0
          data = Z_INDIRECT_P(data);
869
0
        }
870
445k
      }
871
445k
      if (ht->pDestructor) {
872
397k
        ht->pDestructor(data);
873
397k
      }
874
445k
      ZVAL_COPY_VALUE(data, pData);
875
445k
      return data;
876
791k
    }
877
6.40M
  }
878
879
5.61M
  ZEND_HASH_IF_FULL_DO_RESIZE(ht);   /* If the Hash table is full, resize it */
880
881
6.89M
add_to_hash:
882
6.89M
  if (!ZSTR_IS_INTERNED(key)) {
883
2.68M
    zend_string_addref(key);
884
2.68M
    HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS;
885
2.68M
  }
886
6.89M
  idx = ht->nNumUsed++;
887
6.89M
  ht->nNumOfElements++;
888
6.89M
  arData = ht->arData;
889
6.89M
  p = arData + idx;
890
6.89M
  p->key = key;
891
6.89M
  p->h = h = ZSTR_H(key);
892
6.89M
  nIndex = h | ht->nTableMask;
893
6.89M
  Z_NEXT(p->val) = HT_HASH_EX(arData, nIndex);
894
6.89M
  HT_HASH_EX(arData, nIndex) = HT_IDX_TO_HASH(idx);
895
6.89M
  if (flag & HASH_LOOKUP) {
896
356k
    ZVAL_NULL(&p->val);
897
6.53M
  } else {
898
6.53M
    ZVAL_COPY_VALUE(&p->val, pData);
899
6.53M
  }
900
901
6.89M
  return &p->val;
902
5.61M
}
903
904
static zend_always_inline zval *_zend_hash_str_add_or_update_i(HashTable *ht, const char *str, size_t len, zend_ulong h, zval *pData, uint32_t flag)
905
332k
{
906
332k
  zend_string *key;
907
332k
  uint32_t nIndex;
908
332k
  uint32_t idx;
909
332k
  Bucket *p;
910
911
332k
  IS_CONSISTENT(ht);
912
332k
  HT_ASSERT_RC1(ht);
913
914
332k
  if (UNEXPECTED(HT_FLAGS(ht) & (HASH_FLAG_UNINITIALIZED|HASH_FLAG_PACKED))) {
915
191k
    if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
916
191k
      zend_hash_real_init_mixed(ht);
917
191k
      goto add_to_hash;
918
191k
    } else {
919
58
      zend_hash_packed_to_hash(ht);
920
58
    }
921
191k
  } else if ((flag & HASH_ADD_NEW) == 0) {
922
140k
    p = zend_hash_str_find_bucket(ht, str, len, h);
923
924
140k
    if (p) {
925
100k
      zval *data;
926
927
100k
      if (flag & HASH_LOOKUP) {
928
0
        return &p->val;
929
100k
      } else if (flag & HASH_ADD) {
930
0
        if (!(flag & HASH_UPDATE_INDIRECT)) {
931
0
          return NULL;
932
0
        }
933
0
        ZEND_ASSERT(&p->val != pData);
934
0
        data = &p->val;
935
0
        if (Z_TYPE_P(data) == IS_INDIRECT) {
936
0
          data = Z_INDIRECT_P(data);
937
0
          if (Z_TYPE_P(data) != IS_UNDEF) {
938
0
            return NULL;
939
0
          }
940
0
        } else {
941
0
          return NULL;
942
0
        }
943
100k
      } else {
944
100k
        ZEND_ASSERT(&p->val != pData);
945
100k
        data = &p->val;
946
100k
        if ((flag & HASH_UPDATE_INDIRECT) && Z_TYPE_P(data) == IS_INDIRECT) {
947
0
          data = Z_INDIRECT_P(data);
948
0
        }
949
100k
      }
950
100k
      if (ht->pDestructor) {
951
100k
        ht->pDestructor(data);
952
100k
      }
953
100k
      ZVAL_COPY_VALUE(data, pData);
954
100k
      return data;
955
100k
    }
956
140k
  }
957
958
40.2k
  ZEND_HASH_IF_FULL_DO_RESIZE(ht);   /* If the Hash table is full, resize it */
959
960
232k
add_to_hash:
961
232k
  idx = ht->nNumUsed++;
962
232k
  ht->nNumOfElements++;
963
232k
  p = ht->arData + idx;
964
232k
  p->key = key = zend_string_init(str, len, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
965
#if ZEND_RC_DEBUG
966
  if (GC_FLAGS(ht) & GC_PERSISTENT_LOCAL) {
967
    GC_MAKE_PERSISTENT_LOCAL(key);
968
  }
969
#endif
970
232k
  p->h = ZSTR_H(key) = h;
971
232k
  HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS;
972
232k
  if (flag & HASH_LOOKUP) {
973
0
    ZVAL_NULL(&p->val);
974
232k
  } else {
975
232k
    ZVAL_COPY_VALUE(&p->val, pData);
976
232k
  }
977
232k
  nIndex = h | ht->nTableMask;
978
232k
  Z_NEXT(p->val) = HT_HASH(ht, nIndex);
979
232k
  HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
980
981
232k
  return &p->val;
982
40.2k
}
983
984
ZEND_API zval* ZEND_FASTCALL zend_hash_add_or_update(HashTable *ht, zend_string *key, zval *pData, uint32_t flag)
985
0
{
986
0
  if (flag == HASH_ADD) {
987
0
    return zend_hash_add(ht, key, pData);
988
0
  } else if (flag == HASH_ADD_NEW) {
989
0
    return zend_hash_add_new(ht, key, pData);
990
0
  } else if (flag == HASH_UPDATE) {
991
0
    return zend_hash_update(ht, key, pData);
992
0
  } else {
993
0
    ZEND_ASSERT(flag == (HASH_UPDATE|HASH_UPDATE_INDIRECT));
994
0
    return zend_hash_update_ind(ht, key, pData);
995
0
  }
996
0
}
997
998
ZEND_API zval* ZEND_FASTCALL zend_hash_add(HashTable *ht, zend_string *key, zval *pData)
999
1.00M
{
1000
1.00M
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD);
1001
1.00M
}
1002
1003
ZEND_API zval* ZEND_FASTCALL zend_hash_update(HashTable *ht, zend_string *key, zval *pData)
1004
4.15M
{
1005
4.15M
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE);
1006
4.15M
}
1007
1008
ZEND_API zval* ZEND_FASTCALL zend_hash_update_ind(HashTable *ht, zend_string *key, zval *pData)
1009
4.83k
{
1010
4.83k
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE | HASH_UPDATE_INDIRECT);
1011
4.83k
}
1012
1013
ZEND_API zval* ZEND_FASTCALL zend_hash_add_new(HashTable *ht, zend_string *key, zval *pData)
1014
1.95M
{
1015
1.95M
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD_NEW);
1016
1.95M
}
1017
1018
ZEND_API zval* ZEND_FASTCALL zend_hash_lookup(HashTable *ht, zend_string *key)
1019
563k
{
1020
563k
  return _zend_hash_add_or_update_i(ht, key, NULL, HASH_LOOKUP);
1021
563k
}
1022
1023
ZEND_API zval* ZEND_FASTCALL zend_hash_str_add_or_update(HashTable *ht, const char *str, size_t len, zval *pData, uint32_t flag)
1024
0
{
1025
0
  if (flag == HASH_ADD) {
1026
0
    return zend_hash_str_add(ht, str, len, pData);
1027
0
  } else if (flag == HASH_ADD_NEW) {
1028
0
    return zend_hash_str_add_new(ht, str, len, pData);
1029
0
  } else if (flag == HASH_UPDATE) {
1030
0
    return zend_hash_str_update(ht, str, len, pData);
1031
0
  } else {
1032
0
    ZEND_ASSERT(flag == (HASH_UPDATE|HASH_UPDATE_INDIRECT));
1033
0
    return zend_hash_str_update_ind(ht, str, len, pData);
1034
0
  }
1035
0
}
1036
1037
ZEND_API zval* ZEND_FASTCALL zend_hash_str_update(HashTable *ht, const char *str, size_t len, zval *pData)
1038
325k
{
1039
325k
  zend_ulong h = zend_hash_func(str, len);
1040
1041
325k
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_UPDATE);
1042
325k
}
1043
1044
ZEND_API zval* ZEND_FASTCALL zend_hash_str_update_ind(HashTable *ht, const char *str, size_t len, zval *pData)
1045
0
{
1046
0
  zend_ulong h = zend_hash_func(str, len);
1047
1048
0
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_UPDATE | HASH_UPDATE_INDIRECT);
1049
0
}
1050
1051
ZEND_API zval* ZEND_FASTCALL zend_hash_str_add(HashTable *ht, const char *str, size_t len, zval *pData)
1052
6.38k
{
1053
6.38k
  zend_ulong h = zend_hash_func(str, len);
1054
1055
6.38k
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_ADD);
1056
6.38k
}
1057
1058
ZEND_API zval* ZEND_FASTCALL zend_hash_str_add_new(HashTable *ht, const char *str, size_t len, zval *pData)
1059
201
{
1060
201
  zend_ulong h = zend_hash_func(str, len);
1061
1062
201
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_ADD_NEW);
1063
201
}
1064
1065
ZEND_API zval* ZEND_FASTCALL zend_hash_str_lookup(HashTable *ht, const char *str, size_t len)
1066
0
{
1067
0
  zend_ulong h = zend_hash_func(str, len);
1068
1069
0
  return _zend_hash_str_add_or_update_i(ht, str, len, h, NULL, HASH_LOOKUP);
1070
0
}
1071
1072
ZEND_API zval* ZEND_FASTCALL zend_hash_index_add_empty_element(HashTable *ht, zend_ulong h)
1073
4.62k
{
1074
4.62k
  zval dummy;
1075
1076
4.62k
  ZVAL_NULL(&dummy);
1077
4.62k
  return zend_hash_index_add(ht, h, &dummy);
1078
4.62k
}
1079
1080
ZEND_API zval* ZEND_FASTCALL zend_hash_add_empty_element(HashTable *ht, zend_string *key)
1081
504k
{
1082
504k
  zval dummy;
1083
1084
504k
  ZVAL_NULL(&dummy);
1085
504k
  return zend_hash_add(ht, key, &dummy);
1086
504k
}
1087
1088
ZEND_API zval* ZEND_FASTCALL zend_hash_str_add_empty_element(HashTable *ht, const char *str, size_t len)
1089
0
{
1090
0
  zval dummy;
1091
1092
0
  ZVAL_NULL(&dummy);
1093
0
  return zend_hash_str_add(ht, str, len, &dummy);
1094
0
}
1095
1096
static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, zend_ulong h, zval *pData, uint32_t flag)
1097
1.04G
{
1098
1.04G
  uint32_t nIndex;
1099
1.04G
  uint32_t idx;
1100
1.04G
  Bucket *p;
1101
1.04G
  zval *zv;
1102
1103
1.04G
  IS_CONSISTENT(ht);
1104
1.04G
  HT_ASSERT_RC1(ht);
1105
1106
1.04G
  if ((flag & HASH_ADD_NEXT) && h == ZEND_LONG_MIN) {
1107
3.08M
    h = 0;
1108
3.08M
  }
1109
1110
1.04G
  if (HT_IS_PACKED(ht)) {
1111
17.5M
    if ((flag & (HASH_ADD_NEW|HASH_ADD_NEXT)) != (HASH_ADD_NEW|HASH_ADD_NEXT)
1112
16.3M
     && h < ht->nNumUsed) {
1113
30.7k
      zv = ht->arPacked + h;
1114
30.7k
      if (Z_TYPE_P(zv) != IS_UNDEF) {
1115
26.4k
        if (flag & HASH_LOOKUP) {
1116
28
          return zv;
1117
28
        }
1118
49.1k
replace:
1119
49.1k
        if (flag & HASH_ADD) {
1120
18.5k
          return NULL;
1121
18.5k
        }
1122
30.5k
        if (ht->pDestructor) {
1123
30.5k
          ht->pDestructor(zv);
1124
30.5k
        }
1125
30.5k
        ZVAL_COPY_VALUE(zv, pData);
1126
30.5k
        return zv;
1127
49.1k
      } else { /* we have to keep the order :( */
1128
4.36k
        goto convert_to_hash;
1129
4.36k
      }
1130
17.5M
    } else if (EXPECTED(h < ht->nTableSize)) {
1131
20.6M
add_to_packed:
1132
20.6M
      zv = ht->arPacked + h;
1133
      /* incremental initialization of empty Buckets */
1134
20.6M
      if ((flag & (HASH_ADD_NEW|HASH_ADD_NEXT)) != (HASH_ADD_NEW|HASH_ADD_NEXT)) {
1135
17.8M
        if (h > ht->nNumUsed) {
1136
77.8k
          zval *q = ht->arPacked + ht->nNumUsed;
1137
289k
          while (q != zv) {
1138
211k
            ZVAL_UNDEF(q);
1139
211k
            q++;
1140
211k
          }
1141
77.8k
        }
1142
17.8M
      }
1143
20.6M
      ht->nNextFreeElement = ht->nNumUsed = h + 1;
1144
20.6M
      ht->nNumOfElements++;
1145
20.6M
      if (flag & HASH_LOOKUP) {
1146
13.8k
        ZVAL_NULL(zv);
1147
20.6M
      } else {
1148
20.6M
        ZVAL_COPY_VALUE(zv, pData);
1149
20.6M
      }
1150
1151
20.6M
      return zv;
1152
17.2M
    } else if ((h >> 1) < ht->nTableSize &&
1153
262k
               (ht->nTableSize >> 1) < ht->nNumOfElements) {
1154
259k
      zend_hash_packed_grow(ht);
1155
259k
      goto add_to_packed;
1156
259k
    } else {
1157
7.61k
      if (ht->nNumUsed >= ht->nTableSize) {
1158
840
        ht->nTableSize += ht->nTableSize;
1159
840
      }
1160
11.9k
convert_to_hash:
1161
11.9k
      zend_hash_packed_to_hash(ht);
1162
11.9k
    }
1163
1.02G
  } else if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
1164
3.28M
    if (h < ht->nTableSize) {
1165
3.18M
      zend_hash_real_init_packed_ex(ht);
1166
3.18M
      goto add_to_packed;
1167
3.18M
    }
1168
103k
    zend_hash_real_init_mixed(ht);
1169
1.02G
  } else {
1170
1.02G
    if ((flag & HASH_ADD_NEW) == 0 || ZEND_DEBUG) {
1171
1.02G
      p = zend_hash_index_find_bucket(ht, h);
1172
1.02G
      if (p) {
1173
338k
        if (flag & HASH_LOOKUP) {
1174
315k
          return &p->val;
1175
315k
        }
1176
22.7k
        ZEND_ASSERT((flag & HASH_ADD_NEW) == 0);
1177
22.7k
        zv = &p->val;
1178
22.7k
        goto replace;
1179
22.7k
      }
1180
1.02G
    }
1181
1.02G
    ZEND_HASH_IF_FULL_DO_RESIZE(ht);   /* If the Hash table is full, resize it */
1182
1.02G
  }
1183
1184
1.02G
  idx = ht->nNumUsed++;
1185
1.02G
  nIndex = h | ht->nTableMask;
1186
1.02G
  p = ht->arData + idx;
1187
1.02G
  Z_NEXT(p->val) = HT_HASH(ht, nIndex);
1188
1.02G
  HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
1189
1.02G
  if ((zend_long)h >= ht->nNextFreeElement) {
1190
9.43M
    ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX;
1191
9.43M
  }
1192
1.02G
  ht->nNumOfElements++;
1193
1.02G
  p->h = h;
1194
1.02G
  p->key = NULL;
1195
1.02G
  if (flag & HASH_LOOKUP) {
1196
1.32M
    ZVAL_NULL(&p->val);
1197
1.02G
  } else {
1198
1.02G
    ZVAL_COPY_VALUE(&p->val, pData);
1199
1.02G
  }
1200
1201
1.02G
  return &p->val;
1202
1.04G
}
1203
1204
ZEND_API zval* ZEND_FASTCALL zend_hash_index_add_or_update(HashTable *ht, zend_ulong h, zval *pData, uint32_t flag)
1205
0
{
1206
0
  if (flag == HASH_ADD) {
1207
0
    return zend_hash_index_add(ht, h, pData);
1208
0
  } else if (flag == (HASH_ADD|HASH_ADD_NEW)) {
1209
0
    return zend_hash_index_add_new(ht, h, pData);
1210
0
  } else if (flag == (HASH_ADD|HASH_ADD_NEXT)) {
1211
0
    ZEND_ASSERT(h == ht->nNextFreeElement);
1212
0
    return zend_hash_next_index_insert(ht, pData);
1213
0
  } else if (flag == (HASH_ADD|HASH_ADD_NEW|HASH_ADD_NEXT)) {
1214
0
    ZEND_ASSERT(h == ht->nNextFreeElement);
1215
0
    return zend_hash_next_index_insert_new(ht, pData);
1216
0
  } else {
1217
0
    ZEND_ASSERT(flag == HASH_UPDATE);
1218
0
    return zend_hash_index_update(ht, h, pData);
1219
0
  }
1220
0
}
1221
1222
ZEND_API zval* ZEND_FASTCALL zend_hash_index_add(HashTable *ht, zend_ulong h, zval *pData)
1223
44.1k
{
1224
44.1k
  return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_ADD);
1225
44.1k
}
1226
1227
ZEND_API zval* ZEND_FASTCALL zend_hash_index_add_new(HashTable *ht, zend_ulong h, zval *pData)
1228
1.02G
{
1229
1.02G
  return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_ADD | HASH_ADD_NEW);
1230
1.02G
}
1231
1232
ZEND_API zval* ZEND_FASTCALL zend_hash_index_update(HashTable *ht, zend_ulong h, zval *pData)
1233
6.13M
{
1234
6.13M
  return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_UPDATE);
1235
6.13M
}
1236
1237
ZEND_API zval* ZEND_FASTCALL zend_hash_next_index_insert(HashTable *ht, zval *pData)
1238
12.1M
{
1239
12.1M
  return _zend_hash_index_add_or_update_i(ht, ht->nNextFreeElement, pData, HASH_ADD | HASH_ADD_NEXT);
1240
12.1M
}
1241
1242
ZEND_API zval* ZEND_FASTCALL zend_hash_next_index_insert_new(HashTable *ht, zval *pData)
1243
2.85M
{
1244
2.85M
  return _zend_hash_index_add_or_update_i(ht, ht->nNextFreeElement, pData, HASH_ADD | HASH_ADD_NEW | HASH_ADD_NEXT);
1245
2.85M
}
1246
1247
ZEND_API zval* ZEND_FASTCALL zend_hash_index_lookup(HashTable *ht, zend_ulong h)
1248
1.65M
{
1249
1.65M
  return _zend_hash_index_add_or_update_i(ht, h, NULL, HASH_LOOKUP);
1250
1.65M
}
1251
1252
ZEND_API zval* ZEND_FASTCALL zend_hash_set_bucket_key(HashTable *ht, Bucket *b, zend_string *key)
1253
7.31k
{
1254
7.31k
  uint32_t nIndex;
1255
7.31k
  uint32_t idx, i;
1256
7.31k
  Bucket *p, *arData;
1257
1258
7.31k
  IS_CONSISTENT(ht);
1259
7.31k
  HT_ASSERT_RC1(ht);
1260
7.31k
  ZEND_ASSERT(!HT_IS_PACKED(ht));
1261
1262
7.31k
  (void)zend_string_hash_val(key);
1263
7.31k
  p = zend_hash_find_bucket(ht, key);
1264
7.31k
  if (UNEXPECTED(p)) {
1265
162
    return (p == b) ? &p->val : NULL;
1266
162
  }
1267
1268
7.14k
  if (!ZSTR_IS_INTERNED(key)) {
1269
265
    zend_string_addref(key);
1270
265
    HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS;
1271
265
  }
1272
1273
7.14k
  arData = ht->arData;
1274
1275
  /* del from hash */
1276
7.14k
  idx = HT_IDX_TO_HASH(b - arData);
1277
7.14k
  nIndex = b->h | ht->nTableMask;
1278
7.14k
  i = HT_HASH_EX(arData, nIndex);
1279
7.14k
  if (i == idx) {
1280
7.14k
    HT_HASH_EX(arData, nIndex) = Z_NEXT(b->val);
1281
7.14k
  } else {
1282
6
    p = HT_HASH_TO_BUCKET_EX(arData, i);
1283
6
    while (Z_NEXT(p->val) != idx) {
1284
0
      i = Z_NEXT(p->val);
1285
0
      p = HT_HASH_TO_BUCKET_EX(arData, i);
1286
0
    }
1287
6
    Z_NEXT(p->val) = Z_NEXT(b->val);
1288
6
  }
1289
7.14k
  zend_string_release(b->key);
1290
1291
  /* add to hash */
1292
7.14k
  idx = b - arData;
1293
7.14k
  b->key = key;
1294
7.14k
  b->h = ZSTR_H(key);
1295
7.14k
  nIndex = b->h | ht->nTableMask;
1296
7.14k
  idx = HT_IDX_TO_HASH(idx);
1297
7.14k
  i = HT_HASH_EX(arData, nIndex);
1298
7.14k
  if (i == HT_INVALID_IDX || i < idx) {
1299
7.14k
    Z_NEXT(b->val) = i;
1300
7.14k
    HT_HASH_EX(arData, nIndex) = idx;
1301
7.14k
  } else {
1302
7
    p = HT_HASH_TO_BUCKET_EX(arData, i);
1303
7
    while (Z_NEXT(p->val) != HT_INVALID_IDX && Z_NEXT(p->val) > idx) {
1304
0
      i = Z_NEXT(p->val);
1305
0
      p = HT_HASH_TO_BUCKET_EX(arData, i);
1306
0
    }
1307
7
    Z_NEXT(b->val) = Z_NEXT(p->val);
1308
7
    Z_NEXT(p->val) = idx;
1309
7
  }
1310
7.14k
  return &b->val;
1311
7.31k
}
1312
1313
static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht)
1314
99.0k
{
1315
1316
99.0k
  IS_CONSISTENT(ht);
1317
99.0k
  HT_ASSERT_RC1(ht);
1318
1319
99.0k
  ZEND_ASSERT(!HT_IS_PACKED(ht));
1320
99.0k
  if (ht->nNumUsed > ht->nNumOfElements + (ht->nNumOfElements >> 5)) { /* additional term is there to amortize the cost of compaction */
1321
40.6k
    zend_hash_rehash(ht);
1322
58.3k
  } else if (ht->nTableSize < HT_MAX_SIZE) { /* Let's double the table size */
1323
58.3k
    void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
1324
58.3k
    uint32_t nSize = ht->nTableSize + ht->nTableSize;
1325
58.3k
    Bucket *old_buckets = ht->arData;
1326
1327
58.3k
    ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
1328
1329
58.3k
    new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
1330
58.3k
    ht->nTableSize = nSize;
1331
58.3k
    ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
1332
58.3k
    HT_SET_DATA_ADDR(ht, new_data);
1333
58.3k
    memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
1334
58.3k
    pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
1335
58.3k
    zend_hash_rehash(ht);
1336
58.3k
  } else {
1337
0
    zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket) + sizeof(uint32_t), sizeof(Bucket));
1338
0
  }
1339
99.0k
}
1340
1341
ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
1342
329k
{
1343
329k
  Bucket *p;
1344
329k
  uint32_t nIndex, i;
1345
1346
329k
  IS_CONSISTENT(ht);
1347
1348
329k
  if (UNEXPECTED(ht->nNumOfElements == 0)) {
1349
25
    if (!(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
1350
25
      ht->nNumUsed = 0;
1351
25
      HT_HASH_RESET(ht);
1352
      /* Even if the array is empty, we still need to reset the iterator positions. */
1353
25
      ht->nInternalPointer = 0;
1354
25
      if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
1355
12
        HashTableIterator *iter = EG(ht_iterators);
1356
12
        HashTableIterator *end  = iter + EG(ht_iterators_used);
1357
24
        while (iter != end) {
1358
12
          if (iter->ht == ht) {
1359
12
            iter->pos = 0;
1360
12
          }
1361
12
          iter++;
1362
12
        }
1363
12
      }
1364
25
    }
1365
25
    return;
1366
25
  }
1367
1368
329k
  HT_HASH_RESET(ht);
1369
329k
  i = 0;
1370
329k
  p = ht->arData;
1371
329k
  if (HT_IS_WITHOUT_HOLES(ht)) {
1372
2.10M
    do {
1373
2.10M
      nIndex = p->h | ht->nTableMask;
1374
2.10M
      Z_NEXT(p->val) = HT_HASH(ht, nIndex);
1375
2.10M
      HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(i);
1376
2.10M
      p++;
1377
2.10M
    } while (++i < ht->nNumUsed);
1378
278k
  } else {
1379
50.8k
    uint32_t old_num_used = ht->nNumUsed;
1380
4.84M
    do {
1381
4.84M
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) {
1382
50.8k
        uint32_t j = i;
1383
50.8k
        Bucket *q = p;
1384
1385
50.8k
        if (EXPECTED(!HT_HAS_ITERATORS(ht))) {
1386
706M
          while (++i < ht->nNumUsed) {
1387
706M
            p++;
1388
706M
            if (EXPECTED(Z_TYPE_INFO(p->val) != IS_UNDEF)) {
1389
201M
              ZVAL_COPY_VALUE(&q->val, &p->val);
1390
201M
              q->h = p->h;
1391
201M
              nIndex = q->h | ht->nTableMask;
1392
201M
              q->key = p->key;
1393
201M
              Z_NEXT(q->val) = HT_HASH(ht, nIndex);
1394
201M
              HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j);
1395
201M
              if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) {
1396
2
                ht->nInternalPointer = j;
1397
2
              }
1398
201M
              q++;
1399
201M
              j++;
1400
201M
            }
1401
706M
          }
1402
50.8k
        } else {
1403
40
          uint32_t iter_pos = zend_hash_iterators_lower_pos(ht, i + 1);
1404
1405
321
          while (++i < ht->nNumUsed) {
1406
281
            p++;
1407
281
            if (EXPECTED(Z_TYPE_INFO(p->val) != IS_UNDEF)) {
1408
112
              ZVAL_COPY_VALUE(&q->val, &p->val);
1409
112
              q->h = p->h;
1410
112
              nIndex = q->h | ht->nTableMask;
1411
112
              q->key = p->key;
1412
112
              Z_NEXT(q->val) = HT_HASH(ht, nIndex);
1413
112
              HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j);
1414
112
              if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) {
1415
2
                ht->nInternalPointer = j;
1416
2
              }
1417
112
              if (UNEXPECTED(i >= iter_pos)) {
1418
35
                do {
1419
35
                  zend_hash_iterators_update(ht, iter_pos, j);
1420
35
                  iter_pos = zend_hash_iterators_lower_pos(ht, iter_pos + 1);
1421
35
                } while (iter_pos < i);
1422
35
              }
1423
112
              q++;
1424
112
              j++;
1425
112
            }
1426
281
          }
1427
40
        }
1428
50.8k
        ht->nNumUsed = j;
1429
50.8k
        break;
1430
50.8k
      }
1431
4.79M
      nIndex = p->h | ht->nTableMask;
1432
4.79M
      Z_NEXT(p->val) = HT_HASH(ht, nIndex);
1433
4.79M
      HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(i);
1434
4.79M
      p++;
1435
4.79M
    } while (++i < ht->nNumUsed);
1436
1437
    /* Migrate pointer to one past the end of the array to the new one past the end, so that
1438
     * newly inserted elements are picked up correctly. */
1439
50.8k
    if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
1440
40
      _zend_hash_iterators_update(ht, old_num_used, ht->nNumUsed);
1441
40
    }
1442
50.8k
  }
1443
329k
}
1444
1445
static zend_always_inline void zend_hash_iterators_clamp_max(const HashTable *ht, uint32_t max)
1446
71.7M
{
1447
71.7M
  if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
1448
954
    HashTableIterator *iter = EG(ht_iterators);
1449
954
    const HashTableIterator *end = iter + EG(ht_iterators_used);
1450
1.92k
    while (iter != end) {
1451
970
      if (iter->ht == ht) {
1452
954
        iter->pos = MIN(iter->pos, max);
1453
954
      }
1454
970
      iter++;
1455
970
    }
1456
954
  }
1457
71.7M
}
1458
1459
static zend_always_inline void _zend_hash_packed_del_val(HashTable *ht, uint32_t idx, zval *zv)
1460
11.2k
{
1461
11.2k
  idx = HT_HASH_TO_IDX(idx);
1462
11.2k
  ht->nNumOfElements--;
1463
11.2k
  if (ht->nNumUsed - 1 == idx) {
1464
22.8k
    do {
1465
22.8k
      ht->nNumUsed--;
1466
22.8k
    } while (ht->nNumUsed > 0 && (UNEXPECTED(Z_TYPE(ht->arPacked[ht->nNumUsed-1]) == IS_UNDEF)));
1467
9.98k
    ht->nInternalPointer = MIN(ht->nInternalPointer, ht->nNumUsed);
1468
9.98k
    zend_hash_iterators_clamp_max(ht, ht->nNumUsed);
1469
9.98k
  }
1470
11.2k
  if (ht->pDestructor) {
1471
11.2k
    zval tmp;
1472
11.2k
    ZVAL_COPY_VALUE(&tmp, zv);
1473
11.2k
    ZVAL_UNDEF(zv);
1474
11.2k
    ht->pDestructor(&tmp);
1475
11.2k
  } else {
1476
0
    ZVAL_UNDEF(zv);
1477
0
  }
1478
11.2k
}
1479
1480
static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, Bucket *p, Bucket *prev)
1481
979M
{
1482
979M
  if (prev) {
1483
93.5M
    Z_NEXT(prev->val) = Z_NEXT(p->val);
1484
886M
  } else {
1485
886M
    HT_HASH(ht, p->h | ht->nTableMask) = Z_NEXT(p->val);
1486
886M
  }
1487
979M
  idx = HT_HASH_TO_IDX(idx);
1488
979M
  ht->nNumOfElements--;
1489
979M
  if (ht->nNumUsed - 1 == idx) {
1490
248M
    do {
1491
248M
      ht->nNumUsed--;
1492
248M
    } while (ht->nNumUsed > 0 && (UNEXPECTED(Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF)));
1493
71.7M
    ht->nInternalPointer = MIN(ht->nInternalPointer, ht->nNumUsed);
1494
71.7M
    zend_hash_iterators_clamp_max(ht, ht->nNumUsed);
1495
71.7M
  }
1496
979M
  if (ht->pDestructor) {
1497
1.63M
    zval tmp;
1498
1.63M
    ZVAL_COPY_VALUE(&tmp, &p->val);
1499
1.63M
    ZVAL_UNDEF(&p->val);
1500
1.63M
    ht->pDestructor(&tmp);
1501
978M
  } else {
1502
978M
    ZVAL_UNDEF(&p->val);
1503
978M
  }
1504
979M
}
1505
1506
static zend_always_inline void _zend_hash_del_el(HashTable *ht, uint32_t idx, Bucket *p)
1507
979M
{
1508
979M
  Bucket *prev = NULL;
1509
979M
  uint32_t nIndex;
1510
979M
  uint32_t i;
1511
1512
979M
  nIndex = p->h | ht->nTableMask;
1513
979M
  i = HT_HASH(ht, nIndex);
1514
1515
979M
  if (i != idx) {
1516
93.5M
    prev = HT_HASH_TO_BUCKET(ht, i);
1517
104M
    while (Z_NEXT(prev->val) != idx) {
1518
10.6M
      i = Z_NEXT(prev->val);
1519
10.6M
      prev = HT_HASH_TO_BUCKET(ht, i);
1520
10.6M
    }
1521
93.5M
  }
1522
1523
979M
  if (p->key) {
1524
1.60M
    zend_string_release(p->key);
1525
1.60M
    p->key = NULL;
1526
1.60M
  }
1527
979M
  _zend_hash_del_el_ex(ht, idx, p, prev);
1528
979M
}
1529
1530
ZEND_API void ZEND_FASTCALL zend_hash_packed_del_val(HashTable *ht, zval *zv)
1531
1.44k
{
1532
1.44k
  IS_CONSISTENT(ht);
1533
1.44k
  HT_ASSERT_RC1(ht);
1534
1.44k
  ZEND_ASSERT(HT_IS_PACKED(ht));
1535
1.44k
  _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(zv - ht->arPacked), zv);
1536
1.44k
}
1537
1538
1539
ZEND_API void ZEND_FASTCALL zend_hash_del_bucket(HashTable *ht, Bucket *p)
1540
978M
{
1541
978M
  IS_CONSISTENT(ht);
1542
978M
  HT_ASSERT_RC1(ht);
1543
978M
  ZEND_ASSERT(!HT_IS_PACKED(ht));
1544
978M
  _zend_hash_del_el(ht, HT_IDX_TO_HASH(p - ht->arData), p);
1545
978M
}
1546
1547
ZEND_API zend_result ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key)
1548
323k
{
1549
323k
  zend_ulong h;
1550
323k
  uint32_t nIndex;
1551
323k
  uint32_t idx;
1552
323k
  Bucket *p;
1553
323k
  Bucket *prev = NULL;
1554
1555
323k
  IS_CONSISTENT(ht);
1556
323k
  HT_ASSERT_RC1(ht);
1557
1558
323k
  h = zend_string_hash_val(key);
1559
323k
  nIndex = h | ht->nTableMask;
1560
1561
323k
  idx = HT_HASH(ht, nIndex);
1562
334k
  while (idx != HT_INVALID_IDX) {
1563
264k
    p = HT_HASH_TO_BUCKET(ht, idx);
1564
264k
    if ((p->key == key) ||
1565
11.5k
      (p->h == h &&
1566
3.13k
         p->key &&
1567
253k
         zend_string_equal_content(p->key, key))) {
1568
253k
      zend_string_release(p->key);
1569
253k
      p->key = NULL;
1570
253k
      _zend_hash_del_el_ex(ht, idx, p, prev);
1571
253k
      return SUCCESS;
1572
253k
    }
1573
11.0k
    prev = p;
1574
11.0k
    idx = Z_NEXT(p->val);
1575
11.0k
  }
1576
70.5k
  return FAILURE;
1577
323k
}
1578
1579
ZEND_API zend_result ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key)
1580
189
{
1581
189
  zend_ulong h;
1582
189
  uint32_t nIndex;
1583
189
  uint32_t idx;
1584
189
  Bucket *p;
1585
189
  Bucket *prev = NULL;
1586
1587
189
  IS_CONSISTENT(ht);
1588
189
  HT_ASSERT_RC1(ht);
1589
1590
189
  h = zend_string_hash_val(key);
1591
189
  nIndex = h | ht->nTableMask;
1592
1593
189
  idx = HT_HASH(ht, nIndex);
1594
189
  while (idx != HT_INVALID_IDX) {
1595
129
    p = HT_HASH_TO_BUCKET(ht, idx);
1596
129
    if ((p->key == key) ||
1597
1
      (p->h == h &&
1598
1
         p->key &&
1599
129
         zend_string_equal_content(p->key, key))) {
1600
129
      if (Z_TYPE(p->val) == IS_INDIRECT) {
1601
47
        zval *data = Z_INDIRECT(p->val);
1602
1603
47
        if (UNEXPECTED(Z_TYPE_P(data) == IS_UNDEF)) {
1604
0
          return FAILURE;
1605
47
        } else {
1606
47
          if (ht->pDestructor) {
1607
47
            zval tmp;
1608
47
            ZVAL_COPY_VALUE(&tmp, data);
1609
47
            ZVAL_UNDEF(data);
1610
47
            ht->pDestructor(&tmp);
1611
47
          } else {
1612
0
            ZVAL_UNDEF(data);
1613
0
          }
1614
47
          HT_FLAGS(ht) |= HASH_FLAG_HAS_EMPTY_IND;
1615
47
        }
1616
82
      } else {
1617
82
        zend_string_release(p->key);
1618
82
        p->key = NULL;
1619
82
        _zend_hash_del_el_ex(ht, idx, p, prev);
1620
82
      }
1621
129
      return SUCCESS;
1622
129
    }
1623
0
    prev = p;
1624
0
    idx = Z_NEXT(p->val);
1625
0
  }
1626
60
  return FAILURE;
1627
189
}
1628
1629
ZEND_API zend_result ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const char *str, size_t len)
1630
0
{
1631
0
  zend_ulong h;
1632
0
  uint32_t nIndex;
1633
0
  uint32_t idx;
1634
0
  Bucket *p;
1635
0
  Bucket *prev = NULL;
1636
1637
0
  IS_CONSISTENT(ht);
1638
0
  HT_ASSERT_RC1(ht);
1639
1640
0
  h = zend_inline_hash_func(str, len);
1641
0
  nIndex = h | ht->nTableMask;
1642
1643
0
  idx = HT_HASH(ht, nIndex);
1644
0
  while (idx != HT_INVALID_IDX) {
1645
0
    p = HT_HASH_TO_BUCKET(ht, idx);
1646
0
    if ((p->h == h)
1647
0
       && p->key
1648
0
       && zend_string_equals_cstr(p->key, str, len)) {
1649
0
      if (Z_TYPE(p->val) == IS_INDIRECT) {
1650
0
        zval *data = Z_INDIRECT(p->val);
1651
1652
0
        if (UNEXPECTED(Z_TYPE_P(data) == IS_UNDEF)) {
1653
0
          return FAILURE;
1654
0
        } else {
1655
0
          if (ht->pDestructor) {
1656
0
            ht->pDestructor(data);
1657
0
          }
1658
0
          ZVAL_UNDEF(data);
1659
0
          HT_FLAGS(ht) |= HASH_FLAG_HAS_EMPTY_IND;
1660
0
        }
1661
0
      } else {
1662
0
        zend_string_release(p->key);
1663
0
        p->key = NULL;
1664
0
        _zend_hash_del_el_ex(ht, idx, p, prev);
1665
0
      }
1666
0
      return SUCCESS;
1667
0
    }
1668
0
    prev = p;
1669
0
    idx = Z_NEXT(p->val);
1670
0
  }
1671
0
  return FAILURE;
1672
0
}
1673
1674
ZEND_API zend_result ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char *str, size_t len)
1675
1.24k
{
1676
1.24k
  zend_ulong h;
1677
1.24k
  uint32_t nIndex;
1678
1.24k
  uint32_t idx;
1679
1.24k
  Bucket *p;
1680
1.24k
  Bucket *prev = NULL;
1681
1682
1.24k
  IS_CONSISTENT(ht);
1683
1.24k
  HT_ASSERT_RC1(ht);
1684
1685
1.24k
  h = zend_inline_hash_func(str, len);
1686
1.24k
  nIndex = h | ht->nTableMask;
1687
1688
1.24k
  idx = HT_HASH(ht, nIndex);
1689
1.39k
  while (idx != HT_INVALID_IDX) {
1690
1.32k
    p = HT_HASH_TO_BUCKET(ht, idx);
1691
1.32k
    if ((p->h == h)
1692
1.18k
       && p->key
1693
1.18k
       && zend_string_equals_cstr(p->key, str, len)) {
1694
1.18k
      zend_string_release(p->key);
1695
1.18k
      p->key = NULL;
1696
1.18k
      _zend_hash_del_el_ex(ht, idx, p, prev);
1697
1.18k
      return SUCCESS;
1698
1.18k
    }
1699
144
    prev = p;
1700
144
    idx = Z_NEXT(p->val);
1701
144
  }
1702
64
  return FAILURE;
1703
1.24k
}
1704
1705
ZEND_API zend_result ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h)
1706
202k
{
1707
202k
  uint32_t nIndex;
1708
202k
  uint32_t idx;
1709
202k
  Bucket *p;
1710
202k
  Bucket *prev = NULL;
1711
1712
202k
  IS_CONSISTENT(ht);
1713
202k
  HT_ASSERT_RC1(ht);
1714
1715
202k
  if (HT_IS_PACKED(ht)) {
1716
9.07k
    if (h < ht->nNumUsed) {
1717
9.03k
      zval *zv = ht->arPacked + h;
1718
9.03k
      if (Z_TYPE_P(zv) != IS_UNDEF) {
1719
9.00k
        _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(h), zv);
1720
9.00k
        return SUCCESS;
1721
9.00k
      }
1722
9.03k
    }
1723
68
    return FAILURE;
1724
9.07k
  }
1725
193k
  nIndex = h | ht->nTableMask;
1726
1727
193k
  idx = HT_HASH(ht, nIndex);
1728
865k
  while (idx != HT_INVALID_IDX) {
1729
739k
    p = HT_HASH_TO_BUCKET(ht, idx);
1730
739k
    if ((p->h == h) && (p->key == NULL)) {
1731
67.6k
      _zend_hash_del_el_ex(ht, idx, p, prev);
1732
67.6k
      return SUCCESS;
1733
67.6k
    }
1734
672k
    prev = p;
1735
672k
    idx = Z_NEXT(p->val);
1736
672k
  }
1737
125k
  return FAILURE;
1738
193k
}
1739
1740
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
1741
3.38M
{
1742
3.38M
  IS_CONSISTENT(ht);
1743
3.38M
  HT_ASSERT(ht, GC_REFCOUNT(ht) <= 1);
1744
1745
3.38M
  if (ht->nNumUsed) {
1746
994k
    if (HT_IS_PACKED(ht)) {
1747
252k
      if (ht->pDestructor) {
1748
251k
        zval *zv = ht->arPacked;
1749
251k
        zval *end = zv + ht->nNumUsed;
1750
1751
251k
        SET_INCONSISTENT(HT_IS_DESTROYING);
1752
251k
        if (HT_IS_WITHOUT_HOLES(ht)) {
1753
1.04M
          do {
1754
1.04M
            ht->pDestructor(zv);
1755
1.04M
          } while (++zv != end);
1756
250k
        } else {
1757
3.92k
          do {
1758
3.92k
            if (EXPECTED(Z_TYPE_P(zv) != IS_UNDEF)) {
1759
1.17k
              ht->pDestructor(zv);
1760
1.17k
            }
1761
3.92k
          } while (++zv != end);
1762
995
        }
1763
251k
        SET_INCONSISTENT(HT_DESTROYED);
1764
251k
      }
1765
252k
      zend_hash_iterators_remove(ht);
1766
742k
    } else {
1767
742k
      Bucket *p = ht->arData;
1768
742k
      Bucket *end = p + ht->nNumUsed;
1769
1770
742k
      if (ht->pDestructor) {
1771
57.3k
        SET_INCONSISTENT(HT_IS_DESTROYING);
1772
1773
57.3k
        if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1774
36.1k
          if (HT_IS_WITHOUT_HOLES(ht)) {
1775
145k
            do {
1776
145k
              ht->pDestructor(&p->val);
1777
145k
            } while (++p != end);
1778
36.1k
          } else {
1779
0
            do {
1780
0
              if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1781
0
                ht->pDestructor(&p->val);
1782
0
              }
1783
0
            } while (++p != end);
1784
0
          }
1785
36.1k
        } else if (HT_IS_WITHOUT_HOLES(ht)) {
1786
31.3k
          do {
1787
31.3k
            ht->pDestructor(&p->val);
1788
31.3k
            if (EXPECTED(p->key)) {
1789
30.9k
              zend_string_release(p->key);
1790
30.9k
            }
1791
31.3k
          } while (++p != end);
1792
21.2k
        } else {
1793
0
          do {
1794
0
            if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1795
0
              ht->pDestructor(&p->val);
1796
0
              if (EXPECTED(p->key)) {
1797
0
                zend_string_release(p->key);
1798
0
              }
1799
0
            }
1800
0
          } while (++p != end);
1801
0
        }
1802
1803
57.3k
        SET_INCONSISTENT(HT_DESTROYED);
1804
685k
      } else {
1805
685k
        if (!HT_HAS_STATIC_KEYS_ONLY(ht)) {
1806
2.05M
          do {
1807
2.05M
            if (EXPECTED(p->key)) {
1808
2.04M
              zend_string_release(p->key);
1809
2.04M
            }
1810
2.05M
          } while (++p != end);
1811
322k
        }
1812
685k
      }
1813
742k
      zend_hash_iterators_remove(ht);
1814
742k
    }
1815
2.39M
  } else if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
1816
2.25M
    return;
1817
2.25M
  }
1818
1.12M
  pefree(HT_GET_DATA_ADDR(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
1819
1.12M
}
1820
1821
ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht)
1822
7.88M
{
1823
7.88M
  IS_CONSISTENT(ht);
1824
7.88M
  HT_ASSERT(ht, GC_REFCOUNT(ht) <= 1);
1825
1826
  /* break possible cycles */
1827
7.88M
  GC_REMOVE_FROM_BUFFER(ht);
1828
7.88M
  GC_TYPE_INFO(ht) = GC_NULL /*???| (GC_WHITE << 16)*/;
1829
1830
7.88M
  if (ht->nNumUsed) {
1831
    /* In some rare cases destructors of regular arrays may be changed */
1832
5.48M
    if (UNEXPECTED(ht->pDestructor != ZVAL_PTR_DTOR)) {
1833
21.3k
      zend_hash_destroy(ht);
1834
21.3k
      goto free_ht;
1835
21.3k
    }
1836
1837
5.46M
    SET_INCONSISTENT(HT_IS_DESTROYING);
1838
1839
5.46M
    if (HT_IS_PACKED(ht)) {
1840
2.38M
      zval *zv = ht->arPacked;
1841
2.38M
      zval *end = zv + ht->nNumUsed;
1842
1843
20.3M
      do {
1844
20.3M
        i_zval_ptr_dtor(zv);
1845
20.3M
      } while (++zv != end);
1846
3.08M
    } else {
1847
3.08M
      Bucket *p = ht->arData;
1848
3.08M
      Bucket *end = p + ht->nNumUsed;
1849
1850
3.08M
      if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1851
9.82M
        do {
1852
9.82M
          i_zval_ptr_dtor(&p->val);
1853
9.82M
        } while (++p != end);
1854
2.38M
      } else if (HT_IS_WITHOUT_HOLES(ht)) {
1855
2.37M
        do {
1856
2.37M
          i_zval_ptr_dtor(&p->val);
1857
2.37M
          if (EXPECTED(p->key)) {
1858
2.08M
            zend_string_release_ex(p->key, 0);
1859
2.08M
          }
1860
2.37M
        } while (++p != end);
1861
695k
      } else {
1862
10
        do {
1863
10
          if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1864
5
            i_zval_ptr_dtor(&p->val);
1865
5
            if (EXPECTED(p->key)) {
1866
5
              zend_string_release_ex(p->key, 0);
1867
5
            }
1868
5
          }
1869
10
        } while (++p != end);
1870
5
      }
1871
3.08M
    }
1872
5.46M
  } else if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
1873
1.89M
    goto free_ht;
1874
1.89M
  }
1875
5.96M
  SET_INCONSISTENT(HT_DESTROYED);
1876
5.96M
  efree(HT_GET_DATA_ADDR(ht));
1877
7.88M
free_ht:
1878
7.88M
  zend_hash_iterators_remove(ht);
1879
7.88M
  FREE_HASHTABLE(ht);
1880
7.88M
}
1881
1882
ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht)
1883
777k
{
1884
777k
  IS_CONSISTENT(ht);
1885
777k
  HT_ASSERT_RC1(ht);
1886
1887
777k
  if (ht->nNumUsed) {
1888
217k
    if (HT_IS_PACKED(ht)) {
1889
2.27k
      zval *zv = ht->arPacked;
1890
2.27k
      zval *end = zv + ht->nNumUsed;
1891
1892
2.27k
      if (ht->pDestructor) {
1893
0
        if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1894
0
          if (HT_IS_WITHOUT_HOLES(ht)) {
1895
0
            do {
1896
0
              ht->pDestructor(zv);
1897
0
            } while (++zv != end);
1898
0
          } else {
1899
0
            do {
1900
0
              if (EXPECTED(Z_TYPE_P(zv) != IS_UNDEF)) {
1901
0
                ht->pDestructor(zv);
1902
0
              }
1903
0
            } while (++zv != end);
1904
0
          }
1905
0
        }
1906
0
      }
1907
215k
    } else {
1908
215k
      Bucket *p = ht->arData;
1909
215k
      Bucket *end = p + ht->nNumUsed;
1910
1911
215k
      if (ht->pDestructor) {
1912
440
        if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1913
440
          if (HT_IS_WITHOUT_HOLES(ht)) {
1914
769
            do {
1915
769
              ht->pDestructor(&p->val);
1916
769
            } while (++p != end);
1917
415
          } else {
1918
105
            do {
1919
105
              if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1920
70
                ht->pDestructor(&p->val);
1921
70
              }
1922
105
            } while (++p != end);
1923
25
          }
1924
440
        } else if (HT_IS_WITHOUT_HOLES(ht)) {
1925
0
          do {
1926
0
            ht->pDestructor(&p->val);
1927
0
            if (EXPECTED(p->key)) {
1928
0
              zend_string_release(p->key);
1929
0
            }
1930
0
          } while (++p != end);
1931
0
        } else {
1932
0
          do {
1933
0
            if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1934
0
              ht->pDestructor(&p->val);
1935
0
              if (EXPECTED(p->key)) {
1936
0
                zend_string_release(p->key);
1937
0
              }
1938
0
            }
1939
0
          } while (++p != end);
1940
0
        }
1941
214k
      } else {
1942
214k
        if (!HT_HAS_STATIC_KEYS_ONLY(ht)) {
1943
651k
          do {
1944
651k
            if (EXPECTED(p->key)) {
1945
558k
              zend_string_release(p->key);
1946
558k
            }
1947
651k
          } while (++p != end);
1948
96.2k
        }
1949
214k
      }
1950
215k
      HT_HASH_RESET(ht);
1951
215k
    }
1952
217k
  }
1953
777k
  ht->nNumUsed = 0;
1954
777k
  ht->nNumOfElements = 0;
1955
777k
  ht->nNextFreeElement = ZEND_LONG_MIN;
1956
777k
  ht->nInternalPointer = 0;
1957
777k
}
1958
1959
ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht)
1960
2.55k
{
1961
2.55k
  Bucket *p, *end;
1962
1963
2.55k
  IS_CONSISTENT(ht);
1964
2.55k
  HT_ASSERT_RC1(ht);
1965
1966
2.55k
  if (ht->nNumUsed) {
1967
2.38k
    ZEND_ASSERT(!HT_IS_PACKED(ht));
1968
2.38k
    p = ht->arData;
1969
2.38k
    end = p + ht->nNumUsed;
1970
2.38k
    if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1971
8.36k
      do {
1972
8.36k
        i_zval_ptr_dtor(&p->val);
1973
8.36k
      } while (++p != end);
1974
1.92k
    } else if (HT_IS_WITHOUT_HOLES(ht)) {
1975
4.78k
      do {
1976
4.78k
        i_zval_ptr_dtor(&p->val);
1977
4.78k
        if (EXPECTED(p->key)) {
1978
4.78k
          zend_string_release(p->key);
1979
4.78k
        }
1980
4.78k
      } while (++p != end);
1981
460
    } else {
1982
0
      do {
1983
0
        if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1984
0
          i_zval_ptr_dtor(&p->val);
1985
0
          if (EXPECTED(p->key)) {
1986
0
            zend_string_release(p->key);
1987
0
          }
1988
0
        }
1989
0
      } while (++p != end);
1990
0
    }
1991
2.38k
    HT_HASH_RESET(ht);
1992
2.38k
  }
1993
2.55k
  ht->nNumUsed = 0;
1994
2.02k
  ht->nNumOfElements = 0;
1995
2.02k
  ht->nNextFreeElement = ZEND_LONG_MIN;
1996
2.02k
  ht->nInternalPointer = 0;
1997
2.02k
}
1998
1999
ZEND_API void ZEND_FASTCALL zend_hash_graceful_destroy(HashTable *ht)
2000
0
{
2001
0
  uint32_t idx;
2002
2003
0
  IS_CONSISTENT(ht);
2004
0
  HT_ASSERT_RC1(ht);
2005
2006
0
  if (HT_IS_PACKED(ht)) {
2007
0
    zval *zv = ht->arPacked;
2008
2009
0
    for (idx = 0; idx < ht->nNumUsed; idx++, zv++) {
2010
0
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2011
0
      _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(idx), zv);
2012
0
    }
2013
0
  } else {
2014
0
    Bucket *p = ht->arData;
2015
2016
0
    for (idx = 0; idx < ht->nNumUsed; idx++, p++) {
2017
0
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2018
0
      _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2019
0
    }
2020
0
  }
2021
0
  if (!(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
2022
0
    pefree(HT_GET_DATA_ADDR(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
2023
0
  }
2024
2025
0
  SET_INCONSISTENT(HT_DESTROYED);
2026
0
}
2027
2028
ZEND_API void ZEND_FASTCALL zend_hash_graceful_reverse_destroy(HashTable *ht)
2029
613k
{
2030
613k
  uint32_t idx;
2031
2032
613k
  IS_CONSISTENT(ht);
2033
613k
  HT_ASSERT_RC1(ht);
2034
2035
613k
  idx = ht->nNumUsed;
2036
613k
  if (HT_IS_PACKED(ht)) {
2037
6.30k
    zval *zv = ht->arPacked + ht->nNumUsed;
2038
2039
7.25k
    while (idx > 0) {
2040
947
      idx--;
2041
947
      zv--;
2042
947
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2043
813
      _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(idx), zv);
2044
813
    }
2045
607k
  } else {
2046
607k
    Bucket *p = ht->arData + ht->nNumUsed;
2047
2048
2.16M
    while (idx > 0) {
2049
1.55M
      idx--;
2050
1.55M
      p--;
2051
1.55M
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2052
1.53M
      _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2053
1.53M
    }
2054
607k
  }
2055
2056
613k
  if (!(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
2057
313k
    pefree(HT_GET_DATA_ADDR(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
2058
313k
  }
2059
2060
613k
  SET_INCONSISTENT(HT_DESTROYED);
2061
613k
}
2062
2063
/* This is used to recurse elements and selectively delete certain entries
2064
 * from a hashtable. apply_func() receives the data and decides if the entry
2065
 * should be deleted or recursion should be stopped. The following three
2066
 * return codes are possible:
2067
 * ZEND_HASH_APPLY_KEEP   - continue
2068
 * ZEND_HASH_APPLY_STOP   - stop iteration
2069
 * ZEND_HASH_APPLY_REMOVE - delete the element, combinable with the former
2070
 */
2071
2072
ZEND_API void ZEND_FASTCALL zend_hash_apply(HashTable *ht, apply_func_t apply_func)
2073
263
{
2074
263
  uint32_t idx;
2075
263
  int result;
2076
2077
263
  IS_CONSISTENT(ht);
2078
263
  if (HT_IS_PACKED(ht)) {
2079
924
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2080
677
      zval *zv = ht->arPacked + idx;
2081
2082
677
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2083
677
      result = apply_func(zv);
2084
2085
677
      if (result & ZEND_HASH_APPLY_REMOVE) {
2086
0
        HT_ASSERT_RC1(ht);
2087
0
        _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(idx), zv);
2088
0
      }
2089
677
      if (result & ZEND_HASH_APPLY_STOP) {
2090
0
        break;
2091
0
      }
2092
677
    }
2093
247
  } else {
2094
224
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2095
208
      Bucket *p = ht->arData + idx;
2096
2097
208
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2098
208
      result = apply_func(&p->val);
2099
2100
208
      if (result & ZEND_HASH_APPLY_REMOVE) {
2101
0
        HT_ASSERT_RC1(ht);
2102
0
        _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2103
0
      }
2104
208
      if (result & ZEND_HASH_APPLY_STOP) {
2105
0
        break;
2106
0
      }
2107
208
    }
2108
16
  }
2109
263
}
2110
2111
2112
ZEND_API void ZEND_FASTCALL zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument)
2113
0
{
2114
0
  uint32_t idx;
2115
0
  int result;
2116
2117
0
  IS_CONSISTENT(ht);
2118
0
  if (HT_IS_PACKED(ht)) {
2119
0
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2120
0
      zval *zv = ht->arPacked + idx;
2121
0
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2122
0
      result = apply_func(zv, argument);
2123
2124
0
      if (result & ZEND_HASH_APPLY_REMOVE) {
2125
0
        HT_ASSERT_RC1(ht);
2126
0
        _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(idx), zv);
2127
0
      }
2128
0
      if (result & ZEND_HASH_APPLY_STOP) {
2129
0
        break;
2130
0
      }
2131
0
    }
2132
0
  } else {
2133
0
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2134
0
      Bucket *p = ht->arData + idx;
2135
0
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2136
0
      result = apply_func(&p->val, argument);
2137
2138
0
      if (result & ZEND_HASH_APPLY_REMOVE) {
2139
0
        HT_ASSERT_RC1(ht);
2140
0
        _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2141
0
      }
2142
0
      if (result & ZEND_HASH_APPLY_STOP) {
2143
0
        break;
2144
0
      }
2145
0
    }
2146
0
  }
2147
0
}
2148
2149
2150
ZEND_API void zend_hash_apply_with_arguments(HashTable *ht, apply_func_args_t apply_func, int num_args, ...)
2151
0
{
2152
0
  uint32_t idx;
2153
0
  va_list args;
2154
0
  zend_hash_key hash_key;
2155
0
  int result;
2156
2157
0
  IS_CONSISTENT(ht);
2158
2159
0
  if (HT_IS_PACKED(ht)) {
2160
0
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2161
0
      zval *zv = ht->arPacked + idx;
2162
2163
0
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2164
0
      va_start(args, num_args);
2165
0
      hash_key.h = idx;
2166
0
      hash_key.key = NULL;
2167
2168
0
      result = apply_func(zv, num_args, args, &hash_key);
2169
2170
0
      if (result & ZEND_HASH_APPLY_REMOVE) {
2171
0
        HT_ASSERT_RC1(ht);
2172
0
        _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(idx), zv);
2173
0
      }
2174
0
      if (result & ZEND_HASH_APPLY_STOP) {
2175
0
        va_end(args);
2176
0
        break;
2177
0
      }
2178
0
      va_end(args);
2179
0
    }
2180
0
  } else {
2181
0
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2182
0
      Bucket *p = ht->arData + idx;
2183
2184
0
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2185
0
      va_start(args, num_args);
2186
0
      hash_key.h = p->h;
2187
0
      hash_key.key = p->key;
2188
2189
0
      result = apply_func(&p->val, num_args, args, &hash_key);
2190
2191
0
      if (result & ZEND_HASH_APPLY_REMOVE) {
2192
0
        HT_ASSERT_RC1(ht);
2193
0
        _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2194
0
      }
2195
0
      if (result & ZEND_HASH_APPLY_STOP) {
2196
0
        va_end(args);
2197
0
        break;
2198
0
      }
2199
0
      va_end(args);
2200
0
    }
2201
0
  }
2202
0
}
2203
2204
2205
ZEND_API void ZEND_FASTCALL zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func)
2206
335k
{
2207
335k
  uint32_t idx;
2208
335k
  int result;
2209
2210
335k
  IS_CONSISTENT(ht);
2211
2212
335k
  idx = ht->nNumUsed;
2213
335k
  if (HT_IS_PACKED(ht)) {
2214
0
    zval *zv;
2215
2216
0
    while (idx > 0) {
2217
0
      idx--;
2218
0
      zv = ht->arPacked + idx;
2219
0
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2220
2221
0
      result = apply_func(zv);
2222
2223
0
      if (result & ZEND_HASH_APPLY_REMOVE) {
2224
0
        HT_ASSERT_RC1(ht);
2225
0
        _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(idx), zv);
2226
0
      }
2227
0
      if (result & ZEND_HASH_APPLY_STOP) {
2228
0
        break;
2229
0
      }
2230
0
    }
2231
335k
  } else {
2232
335k
    Bucket *p;
2233
2234
2.27M
    while (idx > 0) {
2235
1.93M
      idx--;
2236
1.93M
      p = ht->arData + idx;
2237
1.93M
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2238
2239
1.89M
      result = apply_func(&p->val);
2240
2241
1.89M
      if (result & ZEND_HASH_APPLY_REMOVE) {
2242
38.4k
        HT_ASSERT_RC1(ht);
2243
38.4k
        _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2244
38.4k
      }
2245
1.89M
      if (result & ZEND_HASH_APPLY_STOP) {
2246
52
        break;
2247
52
      }
2248
1.89M
    }
2249
335k
  }
2250
335k
}
2251
2252
2253
ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor)
2254
399k
{
2255
399k
  uint32_t idx;
2256
399k
  zval *new_entry, *data;
2257
2258
399k
  IS_CONSISTENT(source);
2259
399k
  IS_CONSISTENT(target);
2260
399k
  HT_ASSERT_RC1(target);
2261
2262
399k
  if (HT_IS_PACKED(source)) {
2263
0
    for (idx = 0; idx < source->nNumUsed; idx++) {
2264
0
      zval *zv = source->arPacked + idx;
2265
0
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2266
2267
0
      new_entry = zend_hash_index_update(target, idx, zv);
2268
0
      if (pCopyConstructor) {
2269
0
        pCopyConstructor(new_entry);
2270
0
      }
2271
0
    }
2272
0
    return;
2273
0
  }
2274
2275
2.52M
  for (idx = 0; idx < source->nNumUsed; idx++) {
2276
2.12M
    Bucket *p = source->arData + idx;
2277
2278
2.12M
    if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2279
2280
    /* INDIRECT element may point to UNDEF-ined slots */
2281
2.11M
    data = &p->val;
2282
2.11M
    if (Z_TYPE_P(data) == IS_INDIRECT) {
2283
0
      data = Z_INDIRECT_P(data);
2284
0
      if (UNEXPECTED(Z_TYPE_P(data) == IS_UNDEF)) {
2285
0
        continue;
2286
0
      }
2287
0
    }
2288
2.11M
    if (p->key) {
2289
2.11M
      new_entry = zend_hash_update(target, p->key, data);
2290
2.11M
    } else {
2291
28
      new_entry = zend_hash_index_update(target, p->h, data);
2292
28
    }
2293
2.11M
    if (pCopyConstructor) {
2294
16
      pCopyConstructor(new_entry);
2295
16
    }
2296
2.11M
  }
2297
399k
}
2298
2299
2300
static zend_always_inline bool zend_array_dup_value(const HashTable *source, zval *data, zval *dest, bool packed, bool with_holes)
2301
59.8k
{
2302
59.8k
  if (with_holes) {
2303
2.25k
    if (!packed && Z_TYPE_INFO_P(data) == IS_INDIRECT) {
2304
0
      data = Z_INDIRECT_P(data);
2305
0
    }
2306
2.25k
    if (UNEXPECTED(Z_TYPE_INFO_P(data) == IS_UNDEF)) {
2307
1.33k
      return 0;
2308
1.33k
    }
2309
57.6k
  } else if (!packed) {
2310
    /* INDIRECT element may point to UNDEF-ined slots */
2311
39.0k
    if (Z_TYPE_INFO_P(data) == IS_INDIRECT) {
2312
11.5k
      data = Z_INDIRECT_P(data);
2313
11.5k
      if (UNEXPECTED(Z_TYPE_INFO_P(data) == IS_UNDEF)) {
2314
7.94k
        return 0;
2315
7.94k
      }
2316
11.5k
    }
2317
39.0k
  }
2318
2319
50.6k
  do {
2320
50.6k
    if (Z_OPT_REFCOUNTED_P(data)) {
2321
24.8k
      if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1 &&
2322
115
          (Z_TYPE_P(Z_REFVAL_P(data)) != IS_ARRAY ||
2323
102
            Z_ARRVAL_P(Z_REFVAL_P(data)) != source)) {
2324
102
        data = Z_REFVAL_P(data);
2325
102
        if (!Z_OPT_REFCOUNTED_P(data)) {
2326
102
          break;
2327
102
        }
2328
102
      }
2329
24.7k
      Z_ADDREF_P(data);
2330
24.7k
    }
2331
50.6k
  } while (0);
2332
50.6k
  ZVAL_COPY_VALUE(dest, data);
2333
2334
50.6k
  return 1;
2335
59.8k
}
2336
2337
static zend_always_inline bool zend_array_dup_element(const HashTable *source, HashTable *target, uint32_t idx, Bucket *p, Bucket *q, bool packed, bool static_keys, bool with_holes)
2338
39.0k
{
2339
39.0k
  if (!zend_array_dup_value(source, &p->val, &q->val, packed, with_holes)) {
2340
7.96k
    return 0;
2341
7.96k
  }
2342
2343
31.1k
  if (!packed) {
2344
31.1k
    uint32_t nIndex;
2345
2346
31.1k
    q->h = p->h;
2347
31.1k
    q->key = p->key;
2348
31.1k
    if (!static_keys && q->key) {
2349
9.56k
      zend_string_addref(q->key);
2350
9.56k
    }
2351
2352
31.1k
    nIndex = q->h | target->nTableMask;
2353
31.1k
    Z_NEXT(q->val) = HT_HASH(target, nIndex);
2354
31.1k
    HT_HASH(target, nIndex) = HT_IDX_TO_HASH(idx);
2355
31.1k
  }
2356
31.1k
  return 1;
2357
39.0k
}
2358
2359
// We need to duplicate iterators to be able to search through all copy-on-write copies to find the actually iterated HashTable and position back
2360
483
static void zend_array_dup_ht_iterators(const HashTable *source, HashTable *target) {
2361
483
  uint32_t iter_index = 0;
2362
483
  uint32_t end_index = EG(ht_iterators_used);
2363
2364
13.8k
  while (iter_index != end_index) {
2365
13.3k
    HashTableIterator *iter = &EG(ht_iterators)[iter_index];
2366
13.3k
    if (iter->ht == source) {
2367
483
      uint32_t copy_idx = zend_hash_iterator_add(target, iter->pos);
2368
      /* Refetch iter because the memory may be reallocated. */
2369
483
      iter = &EG(ht_iterators)[iter_index];
2370
483
      HashTableIterator *copy_iter = EG(ht_iterators) + copy_idx;
2371
483
      copy_iter->next_copy = iter->next_copy;
2372
483
      iter->next_copy = copy_idx;
2373
483
    }
2374
13.3k
    iter_index++;
2375
13.3k
  }
2376
483
}
2377
2378
static zend_always_inline void zend_array_dup_packed_elements(const HashTable *source, HashTable *target, bool with_holes)
2379
3.27k
{
2380
3.27k
  zval *p = source->arPacked;
2381
3.27k
  zval *q = target->arPacked;
2382
3.27k
  const zval *end = p + source->nNumUsed;
2383
2384
20.7k
  do {
2385
20.7k
    if (!zend_array_dup_value(source, p, q, true, with_holes)) {
2386
1.31k
      if (with_holes) {
2387
1.31k
        ZVAL_UNDEF(q);
2388
1.31k
      }
2389
1.31k
    }
2390
20.7k
    p++; q++;
2391
20.7k
  } while (p != end);
2392
2393
3.27k
  if (UNEXPECTED(HT_HAS_ITERATORS(source))) {
2394
429
    zend_array_dup_ht_iterators(source, target);
2395
429
  }
2396
3.27k
}
2397
2398
static zend_always_inline uint32_t zend_array_dup_elements(const HashTable *source, HashTable *target, bool static_keys, bool with_holes)
2399
6.78k
{
2400
6.78k
  uint32_t idx = 0;
2401
6.78k
  Bucket *p = source->arData;
2402
6.78k
  Bucket *q = target->arData;
2403
6.78k
  const Bucket *end = p + source->nNumUsed;
2404
2405
6.78k
  if (UNEXPECTED(HT_HAS_ITERATORS(source))) {
2406
54
    zend_array_dup_ht_iterators(source, target);
2407
54
  }
2408
2409
31.5k
  do {
2410
31.5k
    if (!zend_array_dup_element(source, target, idx, p, q, false, static_keys, with_holes)) {
2411
1.16k
      uint32_t target_idx = idx;
2412
2413
1.16k
      idx++; p++;
2414
1.16k
      if (EXPECTED(!HT_HAS_ITERATORS(target))) {
2415
8.67k
        while (p != end) {
2416
7.52k
          if (zend_array_dup_element(source, target, target_idx, p, q, false, static_keys, with_holes)) {
2417
722
            if (source->nInternalPointer == idx) {
2418
0
              target->nInternalPointer = target_idx;
2419
0
            }
2420
722
            target_idx++; q++;
2421
722
          }
2422
7.52k
          idx++; p++;
2423
7.52k
        }
2424
1.15k
      } else {
2425
10
        target->nNumUsed = source->nNumUsed;
2426
10
        uint32_t iter_pos = zend_hash_iterators_lower_pos(target, idx);
2427
2428
30
        while (p != end) {
2429
20
          if (zend_array_dup_element(source, target, target_idx, p, q, false, static_keys, with_holes)) {
2430
15
            if (source->nInternalPointer == idx) {
2431
0
              target->nInternalPointer = target_idx;
2432
0
            }
2433
15
            if (UNEXPECTED(idx >= iter_pos)) {
2434
5
              do {
2435
5
                zend_hash_iterators_update(target, iter_pos, target_idx);
2436
5
                iter_pos = zend_hash_iterators_lower_pos(target, iter_pos + 1);
2437
5
              } while (iter_pos < idx);
2438
5
            }
2439
15
            target_idx++; q++;
2440
15
          }
2441
20
          idx++; p++;
2442
20
        }
2443
10
      }
2444
1.16k
      return target_idx;
2445
1.16k
    }
2446
30.3k
    idx++; p++; q++;
2447
30.3k
  } while (p != end);
2448
5.62k
  return idx;
2449
6.78k
}
2450
2451
ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(const HashTable *source)
2452
25.3k
{
2453
25.3k
  uint32_t idx;
2454
25.3k
  HashTable *target;
2455
2456
25.3k
  IS_CONSISTENT(source);
2457
2458
25.3k
  ALLOC_HASHTABLE(target);
2459
25.3k
  GC_SET_REFCOUNT(target, 1);
2460
25.3k
  GC_TYPE_INFO(target) = GC_ARRAY;
2461
2462
25.3k
  target->pDestructor = ZVAL_PTR_DTOR;
2463
2464
25.3k
  if (source->nNumOfElements == 0) {
2465
7.51k
    HT_FLAGS(target) = HASH_FLAG_UNINITIALIZED;
2466
7.51k
    target->nTableMask = HT_MIN_MASK;
2467
7.51k
    target->nNumUsed = 0;
2468
7.51k
    target->nNumOfElements = 0;
2469
7.51k
    target->nNextFreeElement = source->nNextFreeElement;
2470
7.51k
    target->nInternalPointer = 0;
2471
7.51k
    target->nTableSize = HT_MIN_SIZE;
2472
7.51k
    HT_SET_DATA_ADDR(target, &uninitialized_bucket);
2473
17.8k
  } else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) {
2474
7.80k
    ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_HAS_EMPTY_IND));
2475
7.80k
    HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK;
2476
7.80k
    target->nTableMask = source->nTableMask;
2477
7.80k
    target->nNumUsed = source->nNumUsed;
2478
7.80k
    target->nNumOfElements = source->nNumOfElements;
2479
7.80k
    target->nNextFreeElement = source->nNextFreeElement;
2480
7.80k
    target->nTableSize = source->nTableSize;
2481
7.80k
    if (HT_IS_PACKED(source)) {
2482
4.03k
      HT_SET_DATA_ADDR(target, emalloc(HT_PACKED_SIZE(target)));
2483
4.03k
      target->nInternalPointer = source->nInternalPointer;
2484
4.03k
      memcpy(HT_GET_DATA_ADDR(target), HT_GET_DATA_ADDR(source), HT_PACKED_USED_SIZE(source));
2485
4.03k
    } else {
2486
3.77k
      HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target)));
2487
3.77k
      target->nInternalPointer = source->nInternalPointer;
2488
3.77k
      memcpy(HT_GET_DATA_ADDR(target), HT_GET_DATA_ADDR(source), HT_USED_SIZE(source));
2489
3.77k
    }
2490
10.0k
  } else if (HT_IS_PACKED(source)) {
2491
3.27k
    ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_HAS_EMPTY_IND));
2492
3.27k
    HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK;
2493
3.27k
    target->nTableMask = HT_MIN_MASK;
2494
3.27k
    target->nNumUsed = source->nNumUsed;
2495
3.27k
    target->nNumOfElements = source->nNumOfElements;
2496
3.27k
    target->nNextFreeElement = source->nNextFreeElement;
2497
3.27k
    target->nTableSize = source->nTableSize;
2498
3.27k
    HT_SET_DATA_ADDR(target, emalloc(HT_PACKED_SIZE_EX(target->nTableSize, HT_MIN_MASK)));
2499
3.27k
    target->nInternalPointer =
2500
3.27k
      (source->nInternalPointer < source->nNumUsed) ?
2501
3.27k
        source->nInternalPointer : 0;
2502
2503
3.27k
    HT_HASH_RESET_PACKED(target);
2504
2505
3.27k
    if (HT_IS_WITHOUT_HOLES(target)) {
2506
3.05k
      zend_array_dup_packed_elements(source, target, false);
2507
3.05k
    } else {
2508
216
      zend_array_dup_packed_elements(source, target, true);
2509
216
    }
2510
6.78k
  } else {
2511
    /* Indirects are removed during duplication, remove HASH_FLAG_HAS_EMPTY_IND accordingly. */
2512
6.78k
    HT_FLAGS(target) = HT_FLAGS(source) & (HASH_FLAG_MASK & ~HASH_FLAG_HAS_EMPTY_IND);
2513
6.78k
    target->nTableMask = source->nTableMask;
2514
6.78k
    target->nNextFreeElement = source->nNextFreeElement;
2515
6.78k
    target->nInternalPointer =
2516
6.78k
      (source->nInternalPointer < source->nNumUsed) ?
2517
6.78k
        source->nInternalPointer : 0;
2518
2519
6.78k
    target->nTableSize = source->nTableSize;
2520
6.78k
    HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target)));
2521
6.78k
    HT_HASH_RESET(target);
2522
2523
6.78k
    if (HT_HAS_STATIC_KEYS_ONLY(target)) {
2524
5.22k
      if (HT_IS_WITHOUT_HOLES(source)) {
2525
5.20k
        idx = zend_array_dup_elements(source, target, true, false);
2526
5.20k
      } else {
2527
16
        idx = zend_array_dup_elements(source, target, true, true);
2528
16
      }
2529
5.22k
    } else {
2530
1.56k
      if (HT_IS_WITHOUT_HOLES(source)) {
2531
1.56k
        idx = zend_array_dup_elements(source, target, false, false);
2532
1.56k
      } else {
2533
0
        idx = zend_array_dup_elements(source, target, false, true);
2534
0
      }
2535
1.56k
    }
2536
6.78k
    target->nNumUsed = idx;
2537
6.78k
    target->nNumOfElements = idx;
2538
6.78k
  }
2539
25.3k
  return target;
2540
25.3k
}
2541
2542
ZEND_API HashTable* zend_array_to_list(const HashTable *source)
2543
0
{
2544
0
  HashTable *result = _zend_new_array(zend_hash_num_elements(source));
2545
0
  zend_hash_real_init_packed(result);
2546
2547
0
  ZEND_HASH_FILL_PACKED(result) {
2548
0
    zval *entry;
2549
2550
0
    ZEND_HASH_FOREACH_VAL(source, entry) {
2551
0
      if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) {
2552
0
        entry = Z_REFVAL_P(entry);
2553
0
      }
2554
0
      Z_TRY_ADDREF_P(entry);
2555
0
      ZEND_HASH_FILL_ADD(entry);
2556
0
    } ZEND_HASH_FOREACH_END();
2557
0
  } ZEND_HASH_FILL_END();
2558
2559
0
  return result;
2560
0
}
2561
2562
2563
ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite)
2564
8.12k
{
2565
8.12k
  uint32_t idx;
2566
8.12k
  Bucket *p;
2567
8.12k
  zval *t, *s;
2568
2569
8.12k
  IS_CONSISTENT(source);
2570
8.12k
  IS_CONSISTENT(target);
2571
8.12k
  HT_ASSERT_RC1(target);
2572
2573
8.12k
  if (overwrite) {
2574
0
    if (HT_IS_PACKED(source)) {
2575
0
      for (idx = 0; idx < source->nNumUsed; idx++) {
2576
0
        s = source->arPacked + idx;
2577
0
        if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2578
0
          continue;
2579
0
        }
2580
0
        t = zend_hash_index_update(target, idx, s);
2581
0
        if (pCopyConstructor) {
2582
0
          pCopyConstructor(t);
2583
0
        }
2584
0
      }
2585
0
      return;
2586
0
    }
2587
2588
0
    for (idx = 0; idx < source->nNumUsed; idx++) {
2589
0
      p = source->arData + idx;
2590
0
      s = &p->val;
2591
0
      if (UNEXPECTED(Z_TYPE_P(s) == IS_INDIRECT)) {
2592
0
        s = Z_INDIRECT_P(s);
2593
0
      }
2594
0
      if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2595
0
        continue;
2596
0
      }
2597
0
      if (p->key) {
2598
0
        t = _zend_hash_add_or_update_i(target, p->key, s, HASH_UPDATE | HASH_UPDATE_INDIRECT);
2599
0
        if (pCopyConstructor) {
2600
0
          pCopyConstructor(t);
2601
0
        }
2602
0
      } else {
2603
0
        t = zend_hash_index_update(target, p->h, s);
2604
0
        if (pCopyConstructor) {
2605
0
          pCopyConstructor(t);
2606
0
        }
2607
0
      }
2608
0
    }
2609
8.12k
  } else {
2610
8.12k
    if (HT_IS_PACKED(source)) {
2611
17.4k
      for (idx = 0; idx < source->nNumUsed; idx++) {
2612
14.5k
        s = source->arPacked + idx;
2613
14.5k
        if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2614
2.24k
          continue;
2615
2.24k
        }
2616
12.3k
        t = zend_hash_index_add(target, idx, s);
2617
12.3k
        if (t && pCopyConstructor) {
2618
9.94k
          pCopyConstructor(t);
2619
9.94k
        }
2620
12.3k
      }
2621
2.83k
      return;
2622
2.83k
    }
2623
2624
16.0k
    for (idx = 0; idx < source->nNumUsed; idx++) {
2625
10.7k
      p = source->arData + idx;
2626
10.7k
      s = &p->val;
2627
10.7k
      if (UNEXPECTED(Z_TYPE_P(s) == IS_INDIRECT)) {
2628
0
        s = Z_INDIRECT_P(s);
2629
0
      }
2630
10.7k
      if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2631
0
        continue;
2632
0
      }
2633
10.7k
      if (p->key) {
2634
1.38k
        t = _zend_hash_add_or_update_i(target, p->key, s, HASH_ADD | HASH_UPDATE_INDIRECT);
2635
1.38k
        if (t && pCopyConstructor) {
2636
465
          pCopyConstructor(t);
2637
465
        }
2638
9.35k
      } else {
2639
9.35k
        t = zend_hash_index_add(target, p->h, s);
2640
9.35k
        if (t && pCopyConstructor) {
2641
6.36k
          pCopyConstructor(t);
2642
6.36k
        }
2643
9.35k
      }
2644
10.7k
    }
2645
5.29k
  }
2646
8.12k
}
2647
2648
2649
static bool ZEND_FASTCALL zend_hash_replace_checker_wrapper(HashTable *target, zval *source_data, zend_ulong h, zend_string *key, void *pParam, merge_checker_func_t merge_checker_func)
2650
0
{
2651
0
  zend_hash_key hash_key;
2652
2653
0
  hash_key.h = h;
2654
0
  hash_key.key = key;
2655
0
  return merge_checker_func(target, source_data, &hash_key, pParam);
2656
0
}
2657
2658
2659
ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam)
2660
0
{
2661
0
  uint32_t idx;
2662
0
  Bucket *p;
2663
0
  zval *t;
2664
2665
0
  IS_CONSISTENT(source);
2666
0
  IS_CONSISTENT(target);
2667
0
  HT_ASSERT_RC1(target);
2668
2669
0
  ZEND_ASSERT(!HT_IS_PACKED(source));
2670
0
  for (idx = 0; idx < source->nNumUsed; idx++) {
2671
0
    p = source->arData + idx;
2672
0
    if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2673
0
    if (zend_hash_replace_checker_wrapper(target, &p->val, p->h, p->key, pParam, pMergeSource)) {
2674
0
      t = zend_hash_update(target, p->key, &p->val);
2675
0
      if (pCopyConstructor) {
2676
0
        pCopyConstructor(t);
2677
0
      }
2678
0
    }
2679
0
  }
2680
0
}
2681
2682
2683
/* Returns the hash table data if found and NULL if not. */
2684
ZEND_API zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key)
2685
42.8M
{
2686
42.8M
  Bucket *p;
2687
2688
42.8M
  IS_CONSISTENT(ht);
2689
2690
42.8M
  (void)zend_string_hash_val(key);
2691
42.8M
  p = zend_hash_find_bucket(ht, key);
2692
42.8M
  return p ? &p->val : NULL;
2693
42.8M
}
2694
2695
ZEND_API zval* ZEND_FASTCALL zend_hash_find_known_hash(const HashTable *ht, const zend_string *key)
2696
1.49M
{
2697
1.49M
  Bucket *p;
2698
2699
1.49M
  IS_CONSISTENT(ht);
2700
2701
1.49M
  p = zend_hash_find_bucket(ht, key);
2702
1.49M
  return p ? &p->val : NULL;
2703
1.49M
}
2704
2705
ZEND_API zval* ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
2706
5.54M
{
2707
5.54M
  zend_ulong h;
2708
5.54M
  Bucket *p;
2709
2710
5.54M
  IS_CONSISTENT(ht);
2711
2712
5.54M
  h = zend_inline_hash_func(str, len);
2713
5.54M
  p = zend_hash_str_find_bucket(ht, str, len, h);
2714
5.54M
  return p ? &p->val : NULL;
2715
5.54M
}
2716
2717
ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h)
2718
1.00G
{
2719
1.00G
  Bucket *p;
2720
2721
1.00G
  IS_CONSISTENT(ht);
2722
2723
1.00G
  if (HT_IS_PACKED(ht)) {
2724
28.6k
    if (h < ht->nNumUsed) {
2725
24.2k
      zval *zv = ht->arPacked + h;
2726
2727
24.2k
      if (Z_TYPE_P(zv) != IS_UNDEF) {
2728
23.8k
        return zv;
2729
23.8k
      }
2730
24.2k
    }
2731
4.78k
    return NULL;
2732
28.6k
  }
2733
2734
1.00G
  p = zend_hash_index_find_bucket(ht, h);
2735
1.00G
  return p ? &p->val : NULL;
2736
1.00G
}
2737
2738
ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulong h)
2739
3.15k
{
2740
3.15k
  Bucket *p;
2741
2742
3.15k
  IS_CONSISTENT(ht);
2743
3.15k
  ZEND_ASSERT(!HT_IS_PACKED(ht));
2744
2745
3.15k
  p = zend_hash_index_find_bucket(ht, h);
2746
3.15k
  return p ? &p->val : NULL;
2747
3.15k
}
2748
2749
ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(const HashTable *ht, HashPosition *pos)
2750
2.67k
{
2751
2.67k
  IS_CONSISTENT(ht);
2752
2.67k
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2753
2.67k
  *pos = _zend_hash_get_valid_pos(ht, 0);
2754
2.67k
}
2755
2756
2757
/* This function will be extremely optimized by remembering
2758
 * the end of the list
2759
 */
2760
ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(const HashTable *ht, HashPosition *pos)
2761
39
{
2762
39
  uint32_t idx;
2763
2764
39
  IS_CONSISTENT(ht);
2765
39
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2766
2767
39
  idx = ht->nNumUsed;
2768
39
  if (HT_IS_PACKED(ht)) {
2769
21
    while (idx > 0) {
2770
21
      idx--;
2771
21
      if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) {
2772
21
        *pos = idx;
2773
21
        return;
2774
21
      }
2775
21
    }
2776
21
  } else {
2777
18
    while (idx > 0) {
2778
18
      idx--;
2779
18
      if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
2780
18
        *pos = idx;
2781
18
        return;
2782
18
      }
2783
18
    }
2784
18
  }
2785
0
  *pos = ht->nNumUsed;
2786
0
}
2787
2788
2789
ZEND_API zend_result ZEND_FASTCALL zend_hash_move_forward_ex(const HashTable *ht, HashPosition *pos)
2790
1.65k
{
2791
1.65k
  uint32_t idx;
2792
2793
1.65k
  IS_CONSISTENT(ht);
2794
1.65k
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2795
2796
1.65k
  idx = _zend_hash_get_valid_pos(ht, *pos);
2797
1.65k
  if (idx < ht->nNumUsed) {
2798
1.65k
    if (HT_IS_PACKED(ht)) {
2799
532
      while (1) {
2800
532
        idx++;
2801
532
        if (idx >= ht->nNumUsed) {
2802
160
          *pos = ht->nNumUsed;
2803
160
          return SUCCESS;
2804
160
        }
2805
372
        if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) {
2806
369
          *pos = idx;
2807
369
          return SUCCESS;
2808
369
        }
2809
372
      }
2810
1.12k
    } else {
2811
1.13k
      while (1) {
2812
1.13k
        idx++;
2813
1.13k
        if (idx >= ht->nNumUsed) {
2814
527
          *pos = ht->nNumUsed;
2815
527
          return SUCCESS;
2816
527
        }
2817
609
        if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
2818
601
          *pos = idx;
2819
601
          return SUCCESS;
2820
601
        }
2821
609
      }
2822
1.12k
    }
2823
1.65k
  } else {
2824
0
    return FAILURE;
2825
0
  }
2826
1.65k
}
2827
2828
ZEND_API zend_result ZEND_FASTCALL zend_hash_move_backwards_ex(const HashTable *ht, HashPosition *pos)
2829
22
{
2830
22
  uint32_t idx = *pos;
2831
2832
22
  IS_CONSISTENT(ht);
2833
22
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2834
2835
22
  if (idx < ht->nNumUsed) {
2836
22
    if (HT_IS_PACKED(ht)) {
2837
6
      while (idx > 0) {
2838
6
        idx--;
2839
6
        if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) {
2840
6
          *pos = idx;
2841
6
          return SUCCESS;
2842
6
        }
2843
6
      }
2844
16
    } else {
2845
16
      while (idx > 0) {
2846
5
        idx--;
2847
5
        if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
2848
5
          *pos = idx;
2849
5
          return SUCCESS;
2850
5
        }
2851
5
      }
2852
16
    }
2853
11
    *pos = ht->nNumUsed;
2854
11
    return SUCCESS;
2855
22
  } else {
2856
0
    return FAILURE;
2857
0
  }
2858
22
}
2859
2860
2861
ZEND_API zend_hash_key_type ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, zend_ulong *num_index, const HashPosition *pos)
2862
898
{
2863
898
  uint32_t idx;
2864
898
  Bucket *p;
2865
2866
898
  IS_CONSISTENT(ht);
2867
898
  idx = _zend_hash_get_valid_pos(ht, *pos);
2868
898
  if (idx < ht->nNumUsed) {
2869
598
    if (HT_IS_PACKED(ht)) {
2870
0
      *num_index = idx;
2871
0
      return HASH_KEY_IS_LONG;
2872
0
    }
2873
598
    p = ht->arData + idx;
2874
598
    if (p->key) {
2875
492
      *str_index = p->key;
2876
492
      return HASH_KEY_IS_STRING;
2877
492
    } else {
2878
106
      *num_index = p->h;
2879
106
      return HASH_KEY_IS_LONG;
2880
106
    }
2881
598
  }
2882
300
  return HASH_KEY_NON_EXISTENT;
2883
898
}
2884
2885
ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, const HashPosition *pos)
2886
581
{
2887
581
  uint32_t idx;
2888
581
  Bucket *p;
2889
2890
581
  IS_CONSISTENT(ht);
2891
581
  idx = _zend_hash_get_valid_pos(ht, *pos);
2892
581
  if (idx >= ht->nNumUsed) {
2893
0
    ZVAL_NULL(key);
2894
581
  } else {
2895
581
    if (HT_IS_PACKED(ht)) {
2896
277
      ZVAL_LONG(key, idx);
2897
277
      return;
2898
277
    }
2899
304
    p = ht->arData + idx;
2900
304
    if (p->key) {
2901
230
      ZVAL_STR_COPY(key, p->key);
2902
230
    } else {
2903
74
      ZVAL_LONG(key, p->h);
2904
74
    }
2905
304
  }
2906
581
}
2907
2908
ZEND_API zend_hash_key_type ZEND_FASTCALL zend_hash_get_current_key_type_ex(const HashTable *ht, const HashPosition *pos)
2909
1.53k
{
2910
1.53k
  uint32_t idx;
2911
1.53k
  Bucket *p;
2912
2913
1.53k
  IS_CONSISTENT(ht);
2914
1.53k
  idx = _zend_hash_get_valid_pos(ht, *pos);
2915
1.53k
  if (idx < ht->nNumUsed) {
2916
985
    if (HT_IS_PACKED(ht)) {
2917
267
      return HASH_KEY_IS_LONG;
2918
267
    }
2919
718
    p = ht->arData + idx;
2920
718
    if (p->key) {
2921
575
      return HASH_KEY_IS_STRING;
2922
575
    } else {
2923
143
      return HASH_KEY_IS_LONG;
2924
143
    }
2925
718
  }
2926
552
  return HASH_KEY_NON_EXISTENT;
2927
1.53k
}
2928
2929
2930
ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(const HashTable *ht, const HashPosition *pos)
2931
2.82k
{
2932
2.82k
  uint32_t idx;
2933
2.82k
  Bucket *p;
2934
2935
2.82k
  IS_CONSISTENT(ht);
2936
2.82k
  idx = _zend_hash_get_valid_pos(ht, *pos);
2937
2.82k
  if (idx < ht->nNumUsed) {
2938
2.64k
    if (HT_IS_PACKED(ht)) {
2939
475
      return &ht->arPacked[idx];
2940
475
    }
2941
2.16k
    p = ht->arData + idx;
2942
2.16k
    return &p->val;
2943
2.64k
  } else {
2944
186
    return NULL;
2945
186
  }
2946
2.82k
}
2947
2948
ZEND_API void zend_hash_bucket_swap(Bucket *p, Bucket *q)
2949
4.85k
{
2950
4.85k
  zval val;
2951
4.85k
  zend_ulong h;
2952
4.85k
  zend_string *key;
2953
2954
4.85k
  val = p->val;
2955
4.85k
  h = p->h;
2956
4.85k
  key = p->key;
2957
2958
4.85k
  p->val = q->val;
2959
4.85k
  p->h = q->h;
2960
4.85k
  p->key = q->key;
2961
2962
4.85k
  q->val = val;
2963
4.85k
  q->h = h;
2964
4.85k
  q->key = key;
2965
4.85k
}
2966
2967
ZEND_API void zend_hash_bucket_renum_swap(Bucket *p, Bucket *q)
2968
93.8k
{
2969
93.8k
  zval val;
2970
2971
93.8k
  val = p->val;
2972
93.8k
  p->val = q->val;
2973
93.8k
  q->val = val;
2974
93.8k
}
2975
2976
ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q)
2977
0
{
2978
0
  zval val;
2979
0
  zend_ulong h;
2980
2981
0
  val = p->val;
2982
0
  h = p->h;
2983
2984
0
  p->val = q->val;
2985
0
  p->h = q->h;
2986
2987
0
  q->val = val;
2988
0
  q->h = h;
2989
0
}
2990
2991
static void zend_hash_sort_internal(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
2992
736
{
2993
736
  Bucket *p;
2994
736
  uint32_t i, j;
2995
2996
736
  IS_CONSISTENT(ht);
2997
2998
736
  if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) {
2999
    /* Doesn't require sorting */
3000
12
    return;
3001
12
  }
3002
3003
724
  if (HT_IS_PACKED(ht)) {
3004
0
    zend_hash_packed_to_hash(ht); // TODO: ???
3005
0
  }
3006
3007
724
  if (HT_IS_WITHOUT_HOLES(ht)) {
3008
    /* Store original order of elements in extra space to allow stable sorting. */
3009
35.2k
    for (i = 0; i < ht->nNumUsed; i++) {
3010
34.5k
      Z_EXTRA(ht->arData[i].val) = i;
3011
34.5k
    }
3012
724
  } else {
3013
    /* Remove holes and store original order. */
3014
0
    for (j = 0, i = 0; j < ht->nNumUsed; j++) {
3015
0
      p = ht->arData + j;
3016
0
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
3017
0
      if (i != j) {
3018
0
        ht->arData[i] = *p;
3019
0
      }
3020
0
      Z_EXTRA(ht->arData[i].val) = i;
3021
0
      i++;
3022
0
    }
3023
0
    ht->nNumUsed = i;
3024
0
  }
3025
3026
724
  if (!HT_IS_PACKED(ht)) {
3027
    /* We broke the hash collisions chains overriding Z_NEXT() by Z_EXTRA().
3028
     * Reset the hash headers table as well to avoid possible inconsistent
3029
     * access on recursive data structures.
3030
       *
3031
       * See Zend/tests/bug63882_2.phpt
3032
     */
3033
724
    HT_HASH_RESET(ht);
3034
724
  }
3035
3036
724
  sort((void *)ht->arData, ht->nNumUsed, sizeof(Bucket), (compare_func_t) compar,
3037
724
      (swap_func_t)(renumber? zend_hash_bucket_renum_swap :
3038
724
        (HT_IS_PACKED(ht) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap)));
3039
3040
724
  ht->nInternalPointer = 0;
3041
3042
724
  if (renumber) {
3043
32.4k
    for (j = 0; j < i; j++) {
3044
31.8k
      p = ht->arData + j;
3045
31.8k
      p->h = j;
3046
31.8k
      if (p->key) {
3047
222
        zend_string_release(p->key);
3048
222
        p->key = NULL;
3049
222
      }
3050
31.8k
    }
3051
3052
612
    ht->nNextFreeElement = i;
3053
612
  }
3054
724
  if (HT_IS_PACKED(ht)) {
3055
0
    if (!renumber) {
3056
0
      zend_hash_packed_to_hash(ht);
3057
0
    }
3058
724
  } else {
3059
724
    if (renumber) {
3060
612
      void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
3061
612
      Bucket *old_buckets = ht->arData;
3062
612
      zval *zv;
3063
3064
612
      new_data = pemalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK), (GC_FLAGS(ht) & IS_ARRAY_PERSISTENT));
3065
612
      HT_FLAGS(ht) |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;
3066
612
      ht->nTableMask = HT_MIN_MASK;
3067
612
      HT_SET_DATA_ADDR(ht, new_data);
3068
612
      p = old_buckets;
3069
612
      zv = ht->arPacked;
3070
46.6k
      for (i = 0; i < ht->nTableSize; i++) {
3071
46.0k
        ZVAL_COPY_VALUE(zv, &p->val);
3072
46.0k
        zv++;
3073
46.0k
        p++;
3074
46.0k
      }
3075
612
      pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
3076
612
      HT_HASH_RESET_PACKED(ht);
3077
612
    } else {
3078
112
      zend_hash_rehash(ht);
3079
112
    }
3080
724
  }
3081
724
}
3082
3083
ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
3084
32
{
3085
32
  HT_ASSERT_RC1(ht);
3086
32
  zend_hash_sort_internal(ht, sort, compar, renumber);
3087
32
}
3088
3089
ZEND_API void ZEND_FASTCALL zend_array_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
3090
704
{
3091
704
  HT_ASSERT_RC1(ht);
3092
3093
  /* Unpack the array early to avoid RCn assertion failures. */
3094
704
  if (HT_IS_PACKED(ht)) {
3095
593
    zend_hash_packed_to_hash(ht);
3096
593
  }
3097
3098
  /* Adding a refcount prevents the array from going away. */
3099
704
  GC_ADDREF(ht);
3100
3101
704
  zend_hash_sort_internal(ht, sort, compar, renumber);
3102
3103
704
  if (UNEXPECTED(GC_DELREF(ht) == 0)) {
3104
5
    zend_array_destroy(ht);
3105
699
  } else {
3106
699
    gc_check_possible_root((zend_refcounted *)ht);
3107
699
  }
3108
704
}
3109
3110
5.74k
static zend_always_inline int zend_hash_compare_impl(const HashTable *ht1, const HashTable *ht2, compare_func_t compar, bool ordered) {
3111
5.74k
  uint32_t idx1, idx2;
3112
5.74k
  zend_string *key1, *key2;
3113
5.74k
  zend_ulong h1, h2;
3114
5.74k
  zval *pData1, *pData2;;
3115
5.74k
  int result;
3116
3117
5.74k
  if (ht1->nNumOfElements != ht2->nNumOfElements) {
3118
1.61k
    return ht1->nNumOfElements > ht2->nNumOfElements ? 1 : -1;
3119
1.61k
  }
3120
3121
6.17k
  for (idx1 = 0, idx2 = 0; idx1 < ht1->nNumUsed; idx1++) {
3122
4.62k
    if (HT_IS_PACKED(ht1)) {
3123
2.72k
      pData1 = ht1->arPacked + idx1;
3124
2.72k
      h1 = idx1;
3125
2.72k
      key1 = NULL;
3126
2.72k
    } else {
3127
1.89k
      Bucket *p = ht1->arData + idx1;
3128
1.89k
      pData1 = &p->val;
3129
1.89k
      h1 = p->h;
3130
1.89k
      key1 = p->key;
3131
1.89k
    }
3132
3133
4.62k
    if (Z_TYPE_P(pData1) == IS_UNDEF) continue;
3134
3.46k
    if (ordered) {
3135
2.26k
      if (HT_IS_PACKED(ht2)) {
3136
1.78k
        while (1) {
3137
1.78k
          ZEND_ASSERT(idx2 != ht2->nNumUsed);
3138
1.78k
          pData2 = ht2->arPacked + idx2;
3139
1.78k
          h2 = idx2;
3140
1.78k
          key2 = NULL;
3141
1.78k
          if (Z_TYPE_P(pData2) != IS_UNDEF) break;
3142
1.27k
          idx2++;
3143
1.27k
        }
3144
1.75k
      } else {
3145
1.75k
        while (1) {
3146
1.75k
          Bucket *p;
3147
1.75k
          ZEND_ASSERT(idx2 != ht2->nNumUsed);
3148
1.75k
          p = ht2->arData + idx2;
3149
1.75k
          pData2 = &p->val;
3150
1.75k
          h2 = p->h;
3151
1.75k
          key2 = p->key;
3152
1.75k
          if (Z_TYPE_P(pData2) != IS_UNDEF) break;
3153
0
          idx2++;
3154
0
        }
3155
1.75k
      }
3156
2.26k
      if (key1 == NULL && key2 == NULL) { /* numeric indices */
3157
716
        if (h1 != h2) {
3158
159
          return h1 > h2 ? 1 : -1;
3159
159
        }
3160
1.54k
      } else if (key1 != NULL && key2 != NULL) { /* string indices */
3161
941
        if (ZSTR_LEN(key1) != ZSTR_LEN(key2)) {
3162
252
          return ZSTR_LEN(key1) > ZSTR_LEN(key2) ? 1 : -1;
3163
252
        }
3164
3165
689
        result = memcmp(ZSTR_VAL(key1), ZSTR_VAL(key2), ZSTR_LEN(key1));
3166
689
        if (result != 0) {
3167
240
          return result;
3168
240
        }
3169
689
      } else {
3170
        /* Mixed key types: A string key is considered as larger */
3171
603
        return key1 != NULL ? 1 : -1;
3172
603
      }
3173
1.00k
      idx2++;
3174
1.20k
    } else {
3175
1.20k
      if (key1 == NULL) { /* numeric index */
3176
645
        pData2 = zend_hash_index_find(ht2, h1);
3177
645
        if (pData2 == NULL) {
3178
296
          return 1;
3179
296
        }
3180
645
      } else { /* string index */
3181
559
        pData2 = zend_hash_find(ht2, key1);
3182
559
        if (pData2 == NULL) {
3183
369
          return 1;
3184
369
        }
3185
559
      }
3186
1.20k
    }
3187
3188
1.54k
    if (Z_TYPE_P(pData1) == IS_INDIRECT) {
3189
44
      pData1 = Z_INDIRECT_P(pData1);
3190
44
    }
3191
1.54k
    if (Z_TYPE_P(pData2) == IS_INDIRECT) {
3192
44
      pData2 = Z_INDIRECT_P(pData2);
3193
44
    }
3194
3195
1.54k
    if (Z_TYPE_P(pData1) == IS_UNDEF) {
3196
17
      if (Z_TYPE_P(pData2) != IS_UNDEF) {
3197
0
        return -1;
3198
0
      }
3199
1.52k
    } else if (Z_TYPE_P(pData2) == IS_UNDEF) {
3200
0
      return 1;
3201
1.52k
    } else {
3202
1.52k
      result = compar(pData1, pData2);
3203
1.52k
      if (result != 0) {
3204
663
        return result;
3205
663
      }
3206
1.52k
    }
3207
1.54k
  }
3208
3209
1.55k
  return 0;
3210
4.13k
}
3211
3212
ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered)
3213
5.76k
{
3214
5.76k
  int result;
3215
5.76k
  IS_CONSISTENT(ht1);
3216
5.76k
  IS_CONSISTENT(ht2);
3217
3218
5.76k
  if (ht1 == ht2) {
3219
0
    return 0;
3220
0
  }
3221
3222
  /* It's enough to protect only one of the arrays.
3223
   * The second one may be referenced from the first and this may cause
3224
   * false recursion detection.
3225
   */
3226
5.76k
  if (UNEXPECTED(GC_IS_RECURSIVE(ht1))) {
3227
14
    zend_throw_error(NULL, "Nesting level too deep - recursive dependency?");
3228
14
    return ZEND_UNCOMPARABLE;
3229
14
  }
3230
3231
5.74k
  GC_TRY_PROTECT_RECURSION(ht1);
3232
5.74k
  result = zend_hash_compare_impl(ht1, ht2, compar, ordered);
3233
5.74k
  GC_TRY_UNPROTECT_RECURSION(ht1);
3234
3235
5.74k
  return result;
3236
5.76k
}
3237
3238
3239
ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag)
3240
0
{
3241
0
  uint32_t idx;
3242
0
  zval *res;
3243
3244
0
  IS_CONSISTENT(ht);
3245
3246
0
  if (ht->nNumOfElements == 0 ) {
3247
0
    return NULL;
3248
0
  }
3249
3250
0
  if (HT_IS_PACKED(ht)) {
3251
0
    zval *zv;
3252
3253
0
    idx = 0;
3254
0
    while (1) {
3255
0
      if (idx == ht->nNumUsed) {
3256
0
        return NULL;
3257
0
      }
3258
0
      if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) break;
3259
0
      idx++;
3260
0
    }
3261
0
    res = ht->arPacked + idx;
3262
0
    for (; idx < ht->nNumUsed; idx++) {
3263
0
      zv = ht->arPacked + idx;
3264
0
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
3265
3266
0
      if (flag) {
3267
0
        if (compar(res, zv) < 0) { /* max */
3268
0
          res = zv;
3269
0
        }
3270
0
      } else {
3271
0
        if (compar(res, zv) > 0) { /* min */
3272
0
          res = zv;
3273
0
        }
3274
0
      }
3275
0
    }
3276
0
  } else {
3277
0
    Bucket *p;
3278
3279
0
    idx = 0;
3280
0
    while (1) {
3281
0
      if (idx == ht->nNumUsed) {
3282
0
        return NULL;
3283
0
      }
3284
0
      if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) break;
3285
0
      idx++;
3286
0
    }
3287
0
    res = &ht->arData[idx].val;
3288
0
    for (; idx < ht->nNumUsed; idx++) {
3289
0
      p = ht->arData + idx;
3290
0
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
3291
3292
0
      if (flag) {
3293
0
        if (compar(res, &p->val) < 0) { /* max */
3294
0
          res = &p->val;
3295
0
        }
3296
0
      } else {
3297
0
        if (compar(res, &p->val) > 0) { /* min */
3298
0
          res = &p->val;
3299
0
        }
3300
0
      }
3301
0
    }
3302
0
  }
3303
0
  return res;
3304
0
}
3305
3306
ZEND_API bool ZEND_FASTCALL _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx)
3307
96.2k
{
3308
96.2k
  const char *tmp = key;
3309
3310
96.2k
  const char *end = key + length;
3311
3312
96.2k
  if (*tmp == '-') {
3313
6.96k
    tmp++;
3314
6.96k
  }
3315
3316
96.2k
  if ((*tmp == '0' && length > 1) /* numbers with leading zeros */
3317
85.7k
   || (end - tmp > MAX_LENGTH_OF_LONG - 1) /* number too long */
3318
0
   || (SIZEOF_ZEND_LONG == 4 &&
3319
0
       end - tmp == MAX_LENGTH_OF_LONG - 1 &&
3320
13.8k
       *tmp > '2')) { /* overflow */
3321
13.8k
    return 0;
3322
13.8k
  }
3323
82.3k
  *idx = (*tmp - '0');
3324
163k
  while (1) {
3325
163k
    ++tmp;
3326
163k
    if (tmp == end) {
3327
57.8k
      if (*key == '-') {
3328
3.51k
        if (*idx-1 > ZEND_LONG_MAX) { /* overflow */
3329
264
          return 0;
3330
264
        }
3331
3.24k
        *idx = 0 - *idx;
3332
54.3k
      } else if (*idx > ZEND_LONG_MAX) { /* overflow */
3333
484
        return 0;
3334
484
      }
3335
57.0k
      return 1;
3336
57.8k
    }
3337
105k
    if (*tmp <= '9' && *tmp >= '0') {
3338
81.1k
      *idx = (*idx * 10) + (*tmp - '0');
3339
81.1k
    } else {
3340
24.5k
      return 0;
3341
24.5k
    }
3342
105k
  }
3343
82.3k
}
3344
3345
/* Takes a "symtable" hashtable (contains integer and non-numeric string keys)
3346
 * and converts it to a "proptable" (contains only string keys).
3347
 * If the symtable didn't need duplicating, its refcount is incremented.
3348
 */
3349
ZEND_API HashTable* ZEND_FASTCALL zend_symtable_to_proptable(HashTable *ht)
3350
279
{
3351
279
  zend_ulong num_key;
3352
279
  zend_string *str_key;
3353
279
  zval *zv;
3354
3355
279
  if (UNEXPECTED(HT_IS_PACKED(ht))) {
3356
30
    goto convert;
3357
30
  }
3358
3359
1.25k
  ZEND_HASH_MAP_FOREACH_STR_KEY(ht, str_key) {
3360
1.25k
    if (!str_key) {
3361
7
      goto convert;
3362
7
    }
3363
1.25k
  } ZEND_HASH_FOREACH_END();
3364
3365
242
  if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
3366
52
    GC_ADDREF(ht);
3367
52
  }
3368
3369
242
  return ht;
3370
3371
37
convert:
3372
37
  {
3373
37
    HashTable *new_ht = zend_new_array(zend_hash_num_elements(ht));
3374
3375
243
    ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) {
3376
243
      if (!str_key) {
3377
95
        str_key = zend_long_to_str(num_key);
3378
95
        zend_string_delref(str_key);
3379
95
      }
3380
243
      do {
3381
102
        if (Z_OPT_REFCOUNTED_P(zv)) {
3382
2
          if (Z_ISREF_P(zv) && Z_REFCOUNT_P(zv) == 1) {
3383
0
            zv = Z_REFVAL_P(zv);
3384
0
            if (!Z_OPT_REFCOUNTED_P(zv)) {
3385
0
              break;
3386
0
            }
3387
0
          }
3388
2
          Z_ADDREF_P(zv);
3389
2
        }
3390
102
      } while (0);
3391
243
      zend_hash_update(new_ht, str_key, zv);
3392
243
    } ZEND_HASH_FOREACH_END();
3393
3394
37
    return new_ht;
3395
249
  }
3396
249
}
3397
3398
/* Takes a "proptable" hashtable (contains only string keys) and converts it to
3399
 * a "symtable" (contains integer and non-numeric string keys).
3400
 * If the proptable didn't need duplicating, its refcount is incremented.
3401
 */
3402
ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, bool always_duplicate)
3403
1.75k
{
3404
1.75k
  zend_ulong num_key;
3405
1.75k
  zend_string *str_key;
3406
1.75k
  zval *zv;
3407
3408
1.75k
  if (!HT_IS_PACKED(ht)) {
3409
39.2k
    ZEND_HASH_MAP_FOREACH_STR_KEY(ht, str_key) {
3410
      /* The `str_key &&` here might seem redundant: property tables should
3411
       * only have string keys. Unfortunately, this isn't true, at the very
3412
       * least because of ArrayObject, which stores a symtable where the
3413
       * property table should be.
3414
       */
3415
39.2k
      if (str_key && ZEND_HANDLE_NUMERIC(str_key, num_key)) {
3416
52
        goto convert;
3417
52
      }
3418
39.2k
    } ZEND_HASH_FOREACH_END();
3419
1.75k
  }
3420
3421
1.69k
  if (always_duplicate) {
3422
1.69k
    return zend_array_dup(ht);
3423
1.69k
  }
3424
3425
7
  if (EXPECTED(!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE))) {
3426
7
    GC_ADDREF(ht);
3427
7
  }
3428
3429
7
  return ht;
3430
3431
52
convert:
3432
52
  {
3433
52
    HashTable *new_ht = zend_new_array(zend_hash_num_elements(ht));
3434
3435
1.07k
    ZEND_HASH_MAP_FOREACH_KEY_VAL_IND(ht, num_key, str_key, zv) {
3436
1.07k
      do {
3437
466
        if (Z_OPT_REFCOUNTED_P(zv)) {
3438
221
          if (Z_ISREF_P(zv) && Z_REFCOUNT_P(zv) == 1) {
3439
12
            zv = Z_REFVAL_P(zv);
3440
12
            if (!Z_OPT_REFCOUNTED_P(zv)) {
3441
0
              break;
3442
0
            }
3443
12
          }
3444
221
          Z_ADDREF_P(zv);
3445
221
        }
3446
466
      } while (0);
3447
      /* Again, thank ArrayObject for `!str_key ||`. */
3448
1.07k
      if (!str_key || ZEND_HANDLE_NUMERIC(str_key, num_key)) {
3449
84
        zend_hash_index_update(new_ht, num_key, zv);
3450
382
      } else {
3451
382
        zend_hash_update(new_ht, str_key, zv);
3452
382
      }
3453
1.07k
    } ZEND_HASH_FOREACH_END();
3454
3455
52
    return new_ht;
3456
52
  }
3457
52
}