Coverage Report

Created: 2026-06-02 06:40

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