Coverage Report

Created: 2026-04-01 06:49

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