Coverage Report

Created: 2026-09-14 06:25

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
1.77G
  ZEND_ASSERT((expr) || (HT_FLAGS(ht) & HASH_FLAG_ALLOW_COW_VIOLATION))
38
#else
39
# define HT_ASSERT(ht, expr)
40
#endif
41
42
1.76G
#define HT_ASSERT_RC1(ht) HT_ASSERT(ht, GC_REFCOUNT(ht) == 1)
43
44
1.99k
#define HT_POISONED_PTR ((HashTable *) (intptr_t) -1)
45
46
#if ZEND_DEBUG
47
48
2.76G
#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
2.76G
{
55
2.76G
  if ((HT_FLAGS(ht) & HASH_FLAG_CONSISTENCY) == HT_OK) {
56
2.76G
    return;
57
2.76G
  }
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
2.76G
#define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
75
12.4M
#define SET_INCONSISTENT(n) do { \
76
12.4M
    HT_FLAGS(ht) = (HT_FLAGS(ht) & ~HASH_FLAG_CONSISTENCY) | (n); \
77
12.4M
  } 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
888M
  if ((ht)->nNumUsed >= (ht)->nTableSize) {   \
85
67.1k
    zend_hash_do_resize(ht);          \
86
67.1k
  }
87
88
156k
ZEND_API void *zend_hash_str_find_ptr_lc(const HashTable *ht, const char *str, size_t len) {
89
156k
  void *result;
90
156k
  char *lc_str;
91
92
  /* Stack allocate small strings to improve performance */
93
156k
  ALLOCA_FLAG(use_heap)
94
95
156k
  lc_str = zend_str_tolower_copy(do_alloca(len + 1, use_heap), str, len);
96
156k
  result = zend_hash_str_find_ptr(ht, lc_str, len);
97
156k
  free_alloca(lc_str, use_heap);
98
99
156k
  return result;
100
156k
}
101
102
21.6k
ZEND_API void *zend_hash_find_ptr_lc(const HashTable *ht, zend_string *key) {
103
21.6k
  void *result;
104
21.6k
  zend_string *lc_key = zend_string_tolower(key);
105
21.6k
  result = zend_hash_find_ptr(ht, lc_key);
106
21.6k
  zend_string_release(lc_key);
107
21.6k
  return result;
108
21.6k
}
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
13.7M
{
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
13.7M
  if (nSize <= HT_MIN_SIZE) {
121
12.6M
    return HT_MIN_SIZE;
122
12.6M
  } 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
1.11M
  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
13.7M
}
146
147
static zend_always_inline void zend_hash_real_init_packed_ex(HashTable *ht)
148
3.41M
{
149
3.41M
  void *data;
150
151
3.41M
  if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {
152
1.56k
    data = pemalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK), 1);
153
3.40M
  } else if (EXPECTED(ht->nTableSize == HT_MIN_SIZE)) {
154
    /* Use specialized API with constant allocation amount for a particularly common case. */
155
3.39M
    data = emalloc(HT_PACKED_SIZE_EX(HT_MIN_SIZE, HT_MIN_MASK));
156
3.39M
  } else {
157
16.0k
    data = emalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK));
158
16.0k
  }
159
3.41M
  HT_SET_DATA_ADDR(ht, data);
160
  /* Don't overwrite iterator count. */
161
3.41M
  ht->u.v.flags = HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;
162
3.41M
  HT_HASH_RESET_PACKED(ht);
163
3.41M
}
164
165
static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht)
166
4.78M
{
167
4.78M
  void *data;
168
4.78M
  uint32_t nSize = ht->nTableSize;
169
170
4.78M
  ZEND_ASSERT(HT_SIZE_TO_MASK(nSize) != 0);
171
172
4.78M
  if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {
173
5.93k
    data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), 1);
174
4.77M
  } else if (EXPECTED(nSize == HT_MIN_SIZE)) {
175
4.17M
    data = emalloc(HT_SIZE_EX(HT_MIN_SIZE, HT_SIZE_TO_MASK(HT_MIN_SIZE)));
176
4.17M
    ht->nTableMask = HT_SIZE_TO_MASK(HT_MIN_SIZE);
177
4.17M
    HT_SET_DATA_ADDR(ht, data);
178
    /* Don't overwrite iterator count. */
179
4.17M
    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
4.17M
    do {
189
4.17M
      __m128i xmm0 = _mm_setzero_si128();
190
4.17M
      xmm0 = _mm_cmpeq_epi8(xmm0, xmm0);
191
4.17M
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  0), xmm0);
192
4.17M
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  4), xmm0);
193
4.17M
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  8), xmm0);
194
4.17M
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data, 12), xmm0);
195
4.17M
    } 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
4.17M
    return;
223
4.17M
  } else {
224
601k
    data = emalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)));
225
601k
  }
226
607k
  ht->nTableMask = HT_SIZE_TO_MASK(nSize);
227
607k
  HT_SET_DATA_ADDR(ht, data);
228
607k
  HT_FLAGS(ht) = HASH_FLAG_STATIC_KEYS;
229
607k
  HT_HASH_RESET(ht);
230
607k
}
231
232
static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, bool packed)
233
38.0k
{
234
38.0k
  HT_ASSERT_RC1(ht);
235
38.0k
  ZEND_ASSERT(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED);
236
38.0k
  if (packed) {
237
126
    zend_hash_real_init_packed_ex(ht);
238
37.9k
  } else {
239
37.9k
    zend_hash_real_init_mixed_ex(ht);
240
37.9k
  }
241
38.0k
}
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
13.5M
{
262
13.5M
  GC_SET_REFCOUNT(ht, 1);
263
13.5M
  GC_TYPE_INFO(ht) = GC_ARRAY | (persistent ? ((GC_PERSISTENT|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT) : 0);
264
13.5M
  HT_FLAGS(ht) = HASH_FLAG_UNINITIALIZED;
265
13.5M
  ht->nTableMask = HT_MIN_MASK;
266
13.5M
  HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
267
13.5M
  ht->nNumUsed = 0;
268
13.5M
  ht->nNumOfElements = 0;
269
13.5M
  ht->nInternalPointer = 0;
270
13.5M
  ht->nNextFreeElement = ZEND_LONG_MIN;
271
13.5M
  ht->pDestructor = pDestructor;
272
13.5M
  ht->nTableSize = zend_hash_check_size(nSize);
273
13.5M
}
274
275
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent)
276
5.78M
{
277
5.78M
  _zend_hash_init_int(ht, nSize, pDestructor, persistent);
278
5.78M
}
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
7.78M
{
289
7.78M
  HashTable *ht = emalloc(sizeof(HashTable));
290
7.78M
  _zend_hash_init_int(ht, nSize, ZVAL_PTR_DTOR, false);
291
7.78M
  return ht;
292
7.78M
}
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
223k
{
311
223k
  HT_ASSERT_RC1(ht);
312
223k
  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
223k
  uint32_t newTableSize = ht->nTableSize * 2;
316
223k
  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
223k
  ht->nTableSize = newTableSize;
318
223k
}
319
320
ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, bool packed)
321
38.0k
{
322
38.0k
  IS_CONSISTENT(ht);
323
324
38.0k
  HT_ASSERT_RC1(ht);
325
38.0k
  zend_hash_real_init_ex(ht, packed);
326
38.0k
}
327
328
ZEND_API void ZEND_FASTCALL zend_hash_real_init_packed(HashTable *ht)
329
643k
{
330
643k
  IS_CONSISTENT(ht);
331
332
643k
  HT_ASSERT_RC1(ht);
333
643k
  zend_hash_real_init_packed_ex(ht);
334
643k
}
335
336
ZEND_API void ZEND_FASTCALL zend_hash_real_init_mixed(HashTable *ht)
337
4.74M
{
338
4.74M
  IS_CONSISTENT(ht);
339
340
4.74M
  HT_ASSERT_RC1(ht);
341
4.74M
  zend_hash_real_init_mixed_ex(ht);
342
4.74M
}
343
344
ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht)
345
22.1k
{
346
22.1k
  void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
347
22.1k
  zval *src = ht->arPacked;
348
22.1k
  Bucket *dst;
349
22.1k
  uint32_t i;
350
22.1k
  uint32_t nSize = ht->nTableSize;
351
352
22.1k
  ZEND_ASSERT(HT_SIZE_TO_MASK(nSize) != 0);
353
354
22.1k
  HT_ASSERT_RC1(ht);
355
  // Alloc before assign to avoid inconsistencies on OOM
356
22.1k
  new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
357
22.1k
  HT_FLAGS(ht) &= ~HASH_FLAG_PACKED;
358
22.1k
  ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
359
22.1k
  HT_SET_DATA_ADDR(ht, new_data);
360
22.1k
  dst = ht->arData;
361
281k
  for (i = 0; i < ht->nNumUsed; i++) {
362
259k
    ZVAL_COPY_VALUE(&dst->val, src);
363
259k
    dst->h = i;
364
259k
    dst->key = NULL;
365
259k
    dst++;
366
259k
    src++;
367
259k
  }
368
22.1k
  pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
369
22.1k
  zend_hash_rehash(ht);
370
22.1k
}
371
372
ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht)
373
2
{
374
2
  void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
375
2
  Bucket *src = ht->arData;
376
2
  zval *dst;
377
2
  uint32_t i;
378
379
2
  HT_ASSERT_RC1(ht);
380
2
  new_data = pemalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
381
2
  HT_FLAGS(ht) |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;
382
2
  ht->nTableMask = HT_MIN_MASK;
383
2
  HT_SET_DATA_ADDR(ht, new_data);
384
2
  HT_HASH_RESET_PACKED(ht);
385
2
  dst = ht->arPacked;
386
5
  for (i = 0; i < ht->nNumUsed; i++) {
387
3
    ZVAL_COPY_VALUE(dst, &src->val);
388
3
    dst++;
389
3
    src++;
390
3
  }
391
2
  pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
392
2
}
393
394
ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, bool packed)
395
365k
{
396
365k
  HT_ASSERT_RC1(ht);
397
398
365k
  if (nSize == 0) return;
399
400
362k
  ZEND_ASSERT(HT_SIZE_TO_MASK(nSize) != 0);
401
402
362k
  if (UNEXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
403
38.0k
    if (nSize > ht->nTableSize) {
404
2.78k
      ht->nTableSize = zend_hash_check_size(nSize);
405
2.78k
    }
406
38.0k
    zend_hash_real_init(ht, packed);
407
324k
  } else {
408
324k
    if (packed) {
409
52
      ZEND_ASSERT(HT_IS_PACKED(ht));
410
52
      if (nSize > ht->nTableSize) {
411
5
        uint32_t newTableSize = zend_hash_check_size(nSize);
412
5
        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
5
        ht->nTableSize = newTableSize;
414
5
      }
415
324k
    } else {
416
324k
      ZEND_ASSERT(!HT_IS_PACKED(ht));
417
324k
      if (nSize > ht->nTableSize) {
418
195k
        void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
419
195k
        Bucket *old_buckets = ht->arData;
420
195k
        nSize = zend_hash_check_size(nSize);
421
195k
        new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
422
195k
        ht->nTableSize = nSize;
423
195k
        ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
424
195k
        HT_SET_DATA_ADDR(ht, new_data);
425
195k
        memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
426
195k
        pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
427
195k
        zend_hash_rehash(ht);
428
195k
      }
429
324k
    }
430
324k
  }
431
362k
}
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
1.37k
{
460
1.37k
  zval *val;
461
1.37k
  uint32_t num = ht->nNumOfElements;
462
463
7.78k
  ZEND_HASH_MAP_FOREACH_VAL(ht, val) {
464
7.78k
    if (Z_TYPE_P(val) == IS_INDIRECT) {
465
2.39k
      if (UNEXPECTED(Z_TYPE_P(Z_INDIRECT_P(val)) == IS_UNDEF)) {
466
1.42k
        num--;
467
1.42k
      }
468
2.39k
    }
469
7.78k
  } ZEND_HASH_FOREACH_END();
470
1.37k
  return num;
471
1.37k
}
472
/* }}} */
473
474
ZEND_API uint32_t zend_array_count(HashTable *ht)
475
41.8k
{
476
41.8k
  uint32_t num;
477
41.8k
  if (UNEXPECTED(HT_FLAGS(ht) & HASH_FLAG_HAS_EMPTY_IND)) {
478
1.37k
    num = zend_array_recalc_elements(ht);
479
1.37k
    if (UNEXPECTED(ht->nNumOfElements == num)) {
480
249
      HT_FLAGS(ht) &= ~HASH_FLAG_HAS_EMPTY_IND;
481
249
    }
482
40.4k
  } else if (UNEXPECTED(ht == &EG(symbol_table))) {
483
0
    num = zend_array_recalc_elements(ht);
484
40.4k
  } else {
485
40.4k
    num = zend_hash_num_elements(ht);
486
40.4k
  }
487
41.8k
  return num;
488
41.8k
}
489
/* }}} */
490
491
static zend_always_inline HashPosition _zend_hash_get_valid_pos(const HashTable *ht, HashPosition pos)
492
17.1k
{
493
17.1k
  if (HT_IS_PACKED(ht)) {
494
8.74k
    while (pos < ht->nNumUsed && Z_ISUNDEF(ht->arPacked[pos])) {
495
1.56k
      pos++;
496
1.56k
    }
497
9.99k
  } else {
498
10.4k
    while (pos < ht->nNumUsed && Z_ISUNDEF(ht->arData[pos].val)) {
499
409
      pos++;
500
409
    }
501
9.99k
  }
502
17.1k
  return pos;
503
17.1k
}
504
505
static zend_always_inline HashPosition _zend_hash_get_current_pos(const HashTable *ht)
506
1.36k
{
507
1.36k
  return _zend_hash_get_valid_pos(ht, ht->nInternalPointer);
508
1.36k
}
509
510
ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos(const HashTable *ht)
511
284
{
512
284
  return _zend_hash_get_current_pos(ht);
513
284
}
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
268
static void zend_hash_remove_iterator_copies(uint32_t idx) {
521
268
  HashTableIterator *iterators = EG(ht_iterators);
522
523
268
  HashTableIterator *iter = iterators + idx;
524
268
  uint32_t next_idx = iter->next_copy;
525
1.17k
  while (next_idx != idx) {
526
906
    uint32_t cur_idx = next_idx;
527
906
    HashTableIterator *cur_iter = iterators + cur_idx;
528
906
    next_idx = cur_iter->next_copy;
529
906
    cur_iter->next_copy = cur_idx; // avoid recursion in zend_hash_iterator_del
530
906
    zend_hash_iterator_del(cur_idx);
531
906
  }
532
268
  iter->next_copy = idx;
533
268
}
534
535
ZEND_API uint32_t ZEND_FASTCALL zend_hash_iterator_add(HashTable *ht, HashPosition pos)
536
3.99k
{
537
3.99k
  HashTableIterator *iter = EG(ht_iterators);
538
3.99k
  HashTableIterator *end  = iter + EG(ht_iterators_count);
539
3.99k
  uint32_t idx;
540
541
3.99k
  if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
542
3.99k
    HT_INC_ITERATORS_COUNT(ht);
543
3.99k
  }
544
31.1k
  while (iter != end) {
545
31.0k
    if (iter->ht == NULL) {
546
3.91k
      iter->ht = ht;
547
3.91k
      iter->pos = pos;
548
3.91k
      idx = iter - EG(ht_iterators);
549
3.91k
      iter->next_copy = idx;
550
3.91k
      if (idx + 1 > EG(ht_iterators_used)) {
551
3.91k
        EG(ht_iterators_used) = idx + 1;
552
3.91k
      }
553
3.91k
      return idx;
554
3.91k
    }
555
27.1k
    iter++;
556
27.1k
  }
557
77
  if (EG(ht_iterators) == EG(ht_iterators_slots)) {
558
13
    EG(ht_iterators) = emalloc(sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
559
13
    memcpy(EG(ht_iterators), EG(ht_iterators_slots), sizeof(HashTableIterator) * EG(ht_iterators_count));
560
64
  } else {
561
64
    EG(ht_iterators) = erealloc(EG(ht_iterators), sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
562
64
  }
563
77
  iter = EG(ht_iterators) + EG(ht_iterators_count);
564
77
  EG(ht_iterators_count) += 8;
565
77
  iter->ht = ht;
566
77
  iter->pos = pos;
567
77
  memset(iter + 1, 0, sizeof(HashTableIterator) * 7);
568
77
  idx = iter - EG(ht_iterators);
569
77
  iter->next_copy = idx;
570
77
  EG(ht_iterators_used) = idx + 1;
571
77
  return idx;
572
3.99k
}
573
574
// To avoid losing track of the HashTable when separating arrays, we track all copies at once.
575
1.31k
static zend_always_inline bool zend_hash_iterator_find_copy_pos(uint32_t idx, HashTable *ht) {
576
1.31k
  HashTableIterator *iter = EG(ht_iterators) + idx;
577
578
1.31k
  uint32_t next_idx = iter->next_copy;
579
1.31k
  if (EXPECTED(next_idx != idx)) {
580
235
    HashTableIterator *copy_iter;
581
246
    while (next_idx != idx) {
582
246
      copy_iter = EG(ht_iterators) + next_idx;
583
246
      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
235
        if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR)
587
135
          && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) {
588
135
          HT_DEC_ITERATORS_COUNT(iter->ht);
589
135
        }
590
235
        if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
591
235
          HT_INC_ITERATORS_COUNT(ht);
592
235
        }
593
235
        iter->ht = copy_iter->ht;
594
235
        iter->pos = copy_iter->pos;
595
235
        zend_hash_remove_iterator_copies(idx);
596
235
        return true;
597
235
      }
598
11
      next_idx = copy_iter->next_copy;
599
11
    }
600
0
    zend_hash_remove_iterator_copies(idx);
601
0
  }
602
603
1.08k
  return false;
604
1.31k
}
605
606
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos(uint32_t idx, HashTable *ht)
607
4.01k
{
608
4.01k
  HashTableIterator *iter = EG(ht_iterators) + idx;
609
610
4.01k
  ZEND_ASSERT(idx != (uint32_t)-1);
611
4.01k
  if (UNEXPECTED(iter->ht != ht) && !zend_hash_iterator_find_copy_pos(idx, ht)) {
612
10
    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
10
    if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
617
10
      HT_INC_ITERATORS_COUNT(ht);
618
10
    }
619
10
    iter->ht = ht;
620
10
    iter->pos = _zend_hash_get_current_pos(ht);
621
10
  }
622
4.01k
  return iter->pos;
623
4.01k
}
624
625
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos_ex(uint32_t idx, zval *array)
626
10.1k
{
627
10.1k
  HashTable *ht = Z_ARRVAL_P(array);
628
10.1k
  HashTableIterator *iter = EG(ht_iterators) + idx;
629
630
10.1k
  ZEND_ASSERT(idx != (uint32_t)-1);
631
10.1k
  if (UNEXPECTED(iter->ht != ht) && !zend_hash_iterator_find_copy_pos(idx, ht)) {
632
1.07k
    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
1.07k
    if (UNEXPECTED(GC_REFCOUNT(ht) > 1)) {
639
814
      ZVAL_ARR(array, zend_array_dup(ht));
640
814
      GC_TRY_DELREF(ht);
641
814
      iter = EG(ht_iterators) + idx;
642
814
      ht = Z_ARRVAL_P(array);
643
814
    }
644
645
1.07k
    if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
646
1.07k
      HT_INC_ITERATORS_COUNT(ht);
647
1.07k
    }
648
1.07k
    iter->ht = ht;
649
1.07k
    iter->pos = _zend_hash_get_current_pos(ht);
650
1.07k
  }
651
10.1k
  return iter->pos;
652
10.1k
}
653
654
ZEND_API void ZEND_FASTCALL zend_hash_iterator_del(uint32_t idx)
655
3.85k
{
656
3.85k
  HashTableIterator *iter = EG(ht_iterators) + idx;
657
658
3.85k
  ZEND_ASSERT(idx != (uint32_t)-1);
659
660
3.85k
  if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR)
661
3.16k
      && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) {
662
3.16k
    ZEND_ASSERT(HT_ITERATORS_COUNT(iter->ht) != 0);
663
3.16k
    HT_DEC_ITERATORS_COUNT(iter->ht);
664
3.16k
  }
665
3.85k
  iter->ht = NULL;
666
667
3.85k
  if (UNEXPECTED(iter->next_copy != idx)) {
668
33
    zend_hash_remove_iterator_copies(idx);
669
33
  }
670
671
3.85k
  if (idx == EG(ht_iterators_used) - 1) {
672
3.85k
    while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) {
673
87
      idx--;
674
87
    }
675
3.76k
    EG(ht_iterators_used) = idx;
676
3.76k
  }
677
3.85k
}
678
679
static zend_never_inline void ZEND_FASTCALL _zend_hash_iterators_remove(const HashTable *ht)
680
1.23k
{
681
1.23k
  HashTableIterator *iter = EG(ht_iterators);
682
1.23k
  const HashTableIterator *end = iter + EG(ht_iterators_used);
683
684
28.6k
  while (iter != end) {
685
27.4k
    if (iter->ht == ht) {
686
1.99k
      iter->ht = HT_POISONED_PTR;
687
1.99k
    }
688
27.4k
    iter++;
689
27.4k
  }
690
1.23k
}
691
692
static zend_always_inline void zend_hash_iterators_remove(const HashTable *ht)
693
8.61M
{
694
8.61M
  if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
695
1.23k
    _zend_hash_iterators_remove(ht);
696
1.23k
  }
697
8.61M
}
698
699
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(const HashTable *ht, HashPosition start)
700
1.33k
{
701
1.33k
  const HashTableIterator *iter = EG(ht_iterators);
702
1.33k
  const HashTableIterator *end = iter + EG(ht_iterators_used);
703
1.33k
  HashPosition res = ht->nNumUsed;
704
705
2.51k
  while (iter != end) {
706
1.18k
    if (iter->ht == ht) {
707
1.08k
      if (iter->pos >= start && iter->pos < res) {
708
319
        res = iter->pos;
709
319
      }
710
1.08k
    }
711
1.18k
    iter++;
712
1.18k
  }
713
1.33k
  return res;
714
1.33k
}
715
716
ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(const HashTable *ht, HashPosition from, HashPosition to)
717
380
{
718
380
  HashTableIterator *iter = EG(ht_iterators);
719
380
  const HashTableIterator *end = iter + EG(ht_iterators_used);
720
721
993
  while (iter != end) {
722
613
    if (iter->ht == ht && iter->pos == from) {
723
278
      iter->pos = to;
724
278
    }
725
613
    iter++;
726
613
  }
727
380
}
728
729
ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(const HashTable *ht, HashPosition step)
730
11
{
731
11
  HashTableIterator *iter = EG(ht_iterators);
732
11
  const HashTableIterator *end = iter + EG(ht_iterators_used);
733
734
22
  while (iter != end) {
735
11
    if (iter->ht == ht) {
736
11
      iter->pos += step;
737
11
    }
738
11
    iter++;
739
11
  }
740
11
}
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
98.1M
{
745
98.1M
  uint32_t nIndex;
746
98.1M
  uint32_t idx;
747
98.1M
  Bucket *p, *arData;
748
749
98.1M
  ZEND_ASSERT(ZSTR_H(key) != 0 && "Hash must be known");
750
751
98.1M
  arData = ht->arData;
752
98.1M
  nIndex = ZSTR_H(key) | ht->nTableMask;
753
98.1M
  idx = HT_HASH_EX(arData, nIndex);
754
755
98.1M
  if (UNEXPECTED(idx == HT_INVALID_IDX)) {
756
19.1M
    return NULL;
757
19.1M
  }
758
78.9M
  p = HT_HASH_TO_BUCKET_EX(arData, idx);
759
78.9M
  if (EXPECTED(p->key == key)) { /* check for the same interned string */
760
69.4M
    return p;
761
69.4M
  }
762
763
10.6M
  while (1) {
764
10.6M
    if (p->h == ZSTR_H(key) &&
765
3.23M
        EXPECTED(p->key) &&
766
3.23M
        zend_string_equal_content(p->key, key)) {
767
3.19M
      return p;
768
3.19M
    }
769
7.43M
    idx = Z_NEXT(p->val);
770
7.43M
    if (idx == HT_INVALID_IDX) {
771
6.21M
      return NULL;
772
6.21M
    }
773
1.21M
    p = HT_HASH_TO_BUCKET_EX(arData, idx);
774
1.21M
    if (p->key == key) { /* check for the same interned string */
775
143k
      return p;
776
143k
    }
777
1.21M
  }
778
9.55M
}
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
5.72M
{
782
5.72M
  uint32_t nIndex;
783
5.72M
  uint32_t idx;
784
5.72M
  Bucket *p, *arData;
785
786
5.72M
  arData = ht->arData;
787
5.72M
  nIndex = h | ht->nTableMask;
788
5.72M
  idx = HT_HASH_EX(arData, nIndex);
789
6.01M
  while (idx != HT_INVALID_IDX) {
790
1.50M
    ZEND_ASSERT(idx < HT_IDX_TO_HASH(ht->nTableSize));
791
1.50M
    p = HT_HASH_TO_BUCKET_EX(arData, idx);
792
1.50M
    if ((p->h == h)
793
1.21M
       && p->key
794
1.21M
       && zend_string_equals_cstr(p->key, str, len)) {
795
1.21M
      return p;
796
1.21M
    }
797
288k
    idx = Z_NEXT(p->val);
798
288k
  }
799
4.50M
  return NULL;
800
5.72M
}
801
802
static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *ht, zend_ulong h)
803
1.77G
{
804
1.77G
  uint32_t nIndex;
805
1.77G
  uint32_t idx;
806
1.77G
  Bucket *p, *arData;
807
808
1.77G
  arData = ht->arData;
809
1.77G
  nIndex = h | ht->nTableMask;
810
1.77G
  idx = HT_HASH_EX(arData, nIndex);
811
2.07G
  while (idx != HT_INVALID_IDX) {
812
1.17G
    ZEND_ASSERT(idx < HT_IDX_TO_HASH(ht->nTableSize));
813
1.17G
    p = HT_HASH_TO_BUCKET_EX(arData, idx);
814
1.17G
    if (p->h == h && !p->key) {
815
877M
      return p;
816
877M
    }
817
301M
    idx = Z_NEXT(p->val);
818
301M
  }
819
899M
  return NULL;
820
1.77G
}
821
822
static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_string *key, zval *pData, uint32_t flag)
823
7.58M
{
824
7.58M
  zend_ulong h;
825
7.58M
  uint32_t nIndex;
826
7.58M
  uint32_t idx;
827
7.58M
  Bucket *p, *arData;
828
829
7.58M
  IS_CONSISTENT(ht);
830
7.58M
  HT_ASSERT_RC1(ht);
831
7.58M
  zend_string_hash_val(key);
832
833
7.58M
  if (UNEXPECTED(HT_FLAGS(ht) & (HASH_FLAG_UNINITIALIZED|HASH_FLAG_PACKED))) {
834
1.24M
    if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
835
1.22M
      zend_hash_real_init_mixed(ht);
836
1.22M
      goto add_to_hash;
837
1.22M
    } else {
838
10.8k
      zend_hash_packed_to_hash(ht);
839
10.8k
    }
840
6.34M
  } else if ((flag & HASH_ADD_NEW) == 0 || ZEND_DEBUG) {
841
6.34M
    p = zend_hash_find_bucket(ht, key);
842
843
6.34M
    if (p) {
844
814k
      zval *data;
845
846
814k
      ZEND_ASSERT((flag & HASH_ADD_NEW) == 0);
847
814k
      if (flag & HASH_LOOKUP) {
848
243k
        return &p->val;
849
571k
      } else if (flag & HASH_ADD) {
850
126k
        if (!(flag & HASH_UPDATE_INDIRECT)) {
851
125k
          return NULL;
852
125k
        }
853
772
        ZEND_ASSERT(&p->val != pData);
854
772
        data = &p->val;
855
772
        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
772
        } else {
861
772
          return NULL;
862
772
        }
863
445k
      } else {
864
445k
        ZEND_ASSERT(&p->val != pData);
865
445k
        data = &p->val;
866
445k
        if ((flag & HASH_UPDATE_INDIRECT) && Z_TYPE_P(data) == IS_INDIRECT) {
867
0
          data = Z_INDIRECT_P(data);
868
0
        }
869
445k
      }
870
445k
      if (ht->pDestructor) {
871
401k
        ht->pDestructor(data);
872
401k
      }
873
445k
      ZVAL_COPY_VALUE(data, pData);
874
445k
      return data;
875
814k
    }
876
6.34M
  }
877
878
5.53M
  ZEND_HASH_IF_FULL_DO_RESIZE(ht);   /* If the Hash table is full, resize it */
879
880
6.76M
add_to_hash:
881
6.76M
  if (!ZSTR_IS_INTERNED(key)) {
882
2.62M
    zend_string_addref(key);
883
2.62M
    HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS;
884
2.62M
  }
885
6.76M
  idx = ht->nNumUsed++;
886
6.76M
  ht->nNumOfElements++;
887
6.76M
  arData = ht->arData;
888
6.76M
  p = arData + idx;
889
6.76M
  p->key = key;
890
6.76M
  p->h = h = ZSTR_H(key);
891
6.76M
  nIndex = h | ht->nTableMask;
892
6.76M
  Z_NEXT(p->val) = HT_HASH_EX(arData, nIndex);
893
6.76M
  HT_HASH_EX(arData, nIndex) = HT_IDX_TO_HASH(idx);
894
6.76M
  if (flag & HASH_LOOKUP) {
895
397k
    ZVAL_NULL(&p->val);
896
6.37M
  } else {
897
6.37M
    ZVAL_COPY_VALUE(&p->val, pData);
898
6.37M
  }
899
900
6.76M
  return &p->val;
901
5.53M
}
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
360k
{
905
360k
  zend_string *key;
906
360k
  uint32_t nIndex;
907
360k
  uint32_t idx;
908
360k
  Bucket *p;
909
910
360k
  IS_CONSISTENT(ht);
911
360k
  HT_ASSERT_RC1(ht);
912
913
360k
  if (UNEXPECTED(HT_FLAGS(ht) & (HASH_FLAG_UNINITIALIZED|HASH_FLAG_PACKED))) {
914
152k
    if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
915
152k
      zend_hash_real_init_mixed(ht);
916
152k
      goto add_to_hash;
917
152k
    } else {
918
66
      zend_hash_packed_to_hash(ht);
919
66
    }
920
208k
  } else if ((flag & HASH_ADD_NEW) == 0) {
921
208k
    p = zend_hash_str_find_bucket(ht, str, len, h);
922
923
208k
    if (p) {
924
125k
      zval *data;
925
926
125k
      if (flag & HASH_LOOKUP) {
927
0
        return &p->val;
928
125k
      } 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
125k
      } else {
943
125k
        ZEND_ASSERT(&p->val != pData);
944
125k
        data = &p->val;
945
125k
        if ((flag & HASH_UPDATE_INDIRECT) && Z_TYPE_P(data) == IS_INDIRECT) {
946
0
          data = Z_INDIRECT_P(data);
947
0
        }
948
125k
      }
949
125k
      if (ht->pDestructor) {
950
125k
        ht->pDestructor(data);
951
125k
      }
952
125k
      ZVAL_COPY_VALUE(data, pData);
953
125k
      return data;
954
125k
    }
955
208k
  }
956
957
82.6k
  ZEND_HASH_IF_FULL_DO_RESIZE(ht);   /* If the Hash table is full, resize it */
958
959
235k
add_to_hash:
960
235k
  idx = ht->nNumUsed++;
961
235k
  ht->nNumOfElements++;
962
235k
  p = ht->arData + idx;
963
235k
  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
235k
  p->h = ZSTR_H(key) = h;
970
235k
  HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS;
971
235k
  if (flag & HASH_LOOKUP) {
972
0
    ZVAL_NULL(&p->val);
973
235k
  } else {
974
235k
    ZVAL_COPY_VALUE(&p->val, pData);
975
235k
  }
976
235k
  nIndex = h | ht->nTableMask;
977
235k
  Z_NEXT(p->val) = HT_HASH(ht, nIndex);
978
235k
  HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
979
980
235k
  return &p->val;
981
82.6k
}
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
1.00M
{
999
1.00M
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD);
1000
1.00M
}
1001
1002
ZEND_API zval* ZEND_FASTCALL zend_hash_update(HashTable *ht, zend_string *key, zval *pData)
1003
3.87M
{
1004
3.87M
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE);
1005
3.87M
}
1006
1007
ZEND_API zval* ZEND_FASTCALL zend_hash_update_ind(HashTable *ht, zend_string *key, zval *pData)
1008
4.98k
{
1009
4.98k
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE | HASH_UPDATE_INDIRECT);
1010
4.98k
}
1011
1012
ZEND_API zval* ZEND_FASTCALL zend_hash_add_new(HashTable *ht, zend_string *key, zval *pData)
1013
2.05M
{
1014
2.05M
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD_NEW);
1015
2.05M
}
1016
1017
ZEND_API zval* ZEND_FASTCALL zend_hash_lookup(HashTable *ht, zend_string *key)
1018
640k
{
1019
640k
  return _zend_hash_add_or_update_i(ht, key, NULL, HASH_LOOKUP);
1020
640k
}
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
354k
{
1038
354k
  zend_ulong h = zend_hash_func(str, len);
1039
1040
354k
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_UPDATE);
1041
354k
}
1042
1043
ZEND_API zval* ZEND_FASTCALL zend_hash_str_update_ind(HashTable *ht, const char *str, size_t len, zval *pData)
1044
1
{
1045
1
  zend_ulong h = zend_hash_func(str, len);
1046
1047
1
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_UPDATE | HASH_UPDATE_INDIRECT);
1048
1
}
1049
1050
ZEND_API zval* ZEND_FASTCALL zend_hash_str_add(HashTable *ht, const char *str, size_t len, zval *pData)
1051
6.08k
{
1052
6.08k
  zend_ulong h = zend_hash_func(str, len);
1053
1054
6.08k
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_ADD);
1055
6.08k
}
1056
1057
ZEND_API zval* ZEND_FASTCALL zend_hash_str_add_new(HashTable *ht, const char *str, size_t len, zval *pData)
1058
260
{
1059
260
  zend_ulong h = zend_hash_func(str, len);
1060
1061
260
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_ADD_NEW);
1062
260
}
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
2.72k
{
1073
2.72k
  zval dummy;
1074
1075
2.72k
  ZVAL_NULL(&dummy);
1076
2.72k
  return zend_hash_index_add(ht, h, &dummy);
1077
2.72k
}
1078
1079
ZEND_API zval* ZEND_FASTCALL zend_hash_add_empty_element(HashTable *ht, zend_string *key)
1080
412k
{
1081
412k
  zval dummy;
1082
1083
412k
  ZVAL_NULL(&dummy);
1084
412k
  return zend_hash_add(ht, key, &dummy);
1085
412k
}
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
905M
{
1097
905M
  uint32_t nIndex;
1098
905M
  uint32_t idx;
1099
905M
  Bucket *p;
1100
905M
  zval *zv;
1101
1102
905M
  IS_CONSISTENT(ht);
1103
905M
  HT_ASSERT_RC1(ht);
1104
1105
905M
  if ((flag & HASH_ADD_NEXT) && h == ZEND_LONG_MIN) {
1106
2.65M
    h = 0;
1107
2.65M
  }
1108
1109
905M
  if (HT_IS_PACKED(ht)) {
1110
19.6M
    if ((flag & (HASH_ADD_NEW|HASH_ADD_NEXT)) != (HASH_ADD_NEW|HASH_ADD_NEXT)
1111
18.5M
     && h < ht->nNumUsed) {
1112
32.0k
      zv = ht->arPacked + h;
1113
32.0k
      if (flag & HASH_ADD_NEW) {
1114
609
        ZEND_ASSERT(Z_TYPE_P(zv) == IS_UNDEF);
1115
609
        goto convert_to_hash;
1116
31.4k
      } else if (Z_TYPE_P(zv) != IS_UNDEF) {
1117
27.8k
        if (flag & HASH_LOOKUP) {
1118
33
          return zv;
1119
33
        }
1120
65.9k
replace:
1121
65.9k
        if (flag & HASH_ADD) {
1122
13.4k
          return NULL;
1123
13.4k
        }
1124
52.5k
        if (ht->pDestructor) {
1125
52.5k
          ht->pDestructor(zv);
1126
52.5k
        }
1127
52.5k
        ZVAL_COPY_VALUE(zv, pData);
1128
52.5k
        return zv;
1129
65.9k
      } else { /* we have to keep the order :( */
1130
3.56k
        goto convert_to_hash;
1131
3.56k
      }
1132
19.6M
    } else if (EXPECTED(h < ht->nTableSize)) {
1133
22.4M
add_to_packed:
1134
22.4M
      zv = ht->arPacked + h;
1135
      /* incremental initialization of empty Buckets */
1136
22.4M
      if ((flag & (HASH_ADD_NEW|HASH_ADD_NEXT)) != (HASH_ADD_NEW|HASH_ADD_NEXT)) {
1137
19.7M
        if (h > ht->nNumUsed) {
1138
75.5k
          zval *q = ht->arPacked + ht->nNumUsed;
1139
272k
          while (q != zv) {
1140
196k
            ZVAL_UNDEF(q);
1141
196k
            q++;
1142
196k
          }
1143
75.5k
        }
1144
19.7M
      }
1145
22.4M
      ht->nNextFreeElement = ht->nNumUsed = h + 1;
1146
22.4M
      ht->nNumOfElements++;
1147
22.4M
      if (flag & HASH_LOOKUP) {
1148
18.5k
        ZVAL_NULL(zv);
1149
22.3M
      } else {
1150
22.3M
        ZVAL_COPY_VALUE(zv, pData);
1151
22.3M
      }
1152
1153
22.4M
      return zv;
1154
19.4M
    } else if ((h >> 1) < ht->nTableSize &&
1155
224k
               (ht->nTableSize >> 1) < ht->nNumOfElements) {
1156
223k
      zend_hash_packed_grow(ht);
1157
223k
      goto add_to_packed;
1158
223k
    } else {
1159
6.22k
      if (ht->nNumUsed >= ht->nTableSize) {
1160
1.22k
        ht->nTableSize += ht->nTableSize;
1161
1.22k
      }
1162
10.4k
convert_to_hash:
1163
10.4k
      zend_hash_packed_to_hash(ht);
1164
10.4k
    }
1165
885M
  } else if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
1166
2.91M
    if (h < ht->nTableSize) {
1167
2.76M
      zend_hash_real_init_packed_ex(ht);
1168
2.76M
      goto add_to_packed;
1169
2.76M
    }
1170
146k
    zend_hash_real_init_mixed(ht);
1171
883M
  } else {
1172
883M
    if ((flag & HASH_ADD_NEW) == 0 || ZEND_DEBUG) {
1173
883M
      p = zend_hash_index_find_bucket(ht, h);
1174
883M
      if (p) {
1175
388k
        if (flag & HASH_LOOKUP) {
1176
350k
          return &p->val;
1177
350k
        }
1178
38.1k
        ZEND_ASSERT((flag & HASH_ADD_NEW) == 0);
1179
38.1k
        zv = &p->val;
1180
38.1k
        goto replace;
1181
38.1k
      }
1182
883M
    }
1183
882M
    ZEND_HASH_IF_FULL_DO_RESIZE(ht);   /* If the Hash table is full, resize it */
1184
882M
  }
1185
1186
882M
  idx = ht->nNumUsed++;
1187
882M
  nIndex = h | ht->nTableMask;
1188
882M
  p = ht->arData + idx;
1189
882M
  Z_NEXT(p->val) = HT_HASH(ht, nIndex);
1190
882M
  HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
1191
882M
  if ((zend_long)h >= ht->nNextFreeElement) {
1192
5.52M
    ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX;
1193
5.52M
  }
1194
882M
  ht->nNumOfElements++;
1195
882M
  p->h = h;
1196
882M
  p->key = NULL;
1197
882M
  if (flag & HASH_LOOKUP) {
1198
1.15M
    ZVAL_NULL(&p->val);
1199
881M
  } else {
1200
881M
    ZVAL_COPY_VALUE(&p->val, pData);
1201
881M
  }
1202
1203
882M
  return &p->val;
1204
905M
}
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
29.1k
{
1226
29.1k
  return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_ADD);
1227
29.1k
}
1228
1229
ZEND_API zval* ZEND_FASTCALL zend_hash_index_add_new(HashTable *ht, zend_ulong h, zval *pData)
1230
881M
{
1231
881M
  return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_ADD | HASH_ADD_NEW);
1232
881M
}
1233
1234
ZEND_API zval* ZEND_FASTCALL zend_hash_index_update(HashTable *ht, zend_ulong h, zval *pData)
1235
8.44M
{
1236
8.44M
  return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_UPDATE);
1237
8.44M
}
1238
1239
ZEND_API zval* ZEND_FASTCALL zend_hash_next_index_insert(HashTable *ht, zval *pData)
1240
11.9M
{
1241
11.9M
  return _zend_hash_index_add_or_update_i(ht, ht->nNextFreeElement, pData, HASH_ADD | HASH_ADD_NEXT);
1242
11.9M
}
1243
1244
ZEND_API zval* ZEND_FASTCALL zend_hash_next_index_insert_new(HashTable *ht, zval *pData)
1245
2.70M
{
1246
2.70M
  return _zend_hash_index_add_or_update_i(ht, ht->nNextFreeElement, pData, HASH_ADD | HASH_ADD_NEW | HASH_ADD_NEXT);
1247
2.70M
}
1248
1249
ZEND_API zval* ZEND_FASTCALL zend_hash_index_lookup(HashTable *ht, zend_ulong h)
1250
1.52M
{
1251
1.52M
  return _zend_hash_index_add_or_update_i(ht, h, NULL, HASH_LOOKUP);
1252
1.52M
}
1253
1254
ZEND_API zval* ZEND_FASTCALL zend_hash_set_bucket_key(HashTable *ht, Bucket *b, zend_string *key)
1255
8.43k
{
1256
8.43k
  uint32_t nIndex;
1257
8.43k
  uint32_t idx, i;
1258
8.43k
  Bucket *p, *arData;
1259
1260
8.43k
  IS_CONSISTENT(ht);
1261
8.43k
  HT_ASSERT_RC1(ht);
1262
8.43k
  ZEND_ASSERT(!HT_IS_PACKED(ht));
1263
1264
8.43k
  (void)zend_string_hash_val(key);
1265
8.43k
  p = zend_hash_find_bucket(ht, key);
1266
8.43k
  if (UNEXPECTED(p)) {
1267
164
    return (p == b) ? &p->val : NULL;
1268
164
  }
1269
1270
8.26k
  if (!ZSTR_IS_INTERNED(key)) {
1271
530
    zend_string_addref(key);
1272
530
    HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS;
1273
530
  }
1274
1275
8.26k
  arData = ht->arData;
1276
1277
  /* del from hash */
1278
8.26k
  idx = HT_IDX_TO_HASH(b - arData);
1279
8.26k
  nIndex = b->h | ht->nTableMask;
1280
8.26k
  i = HT_HASH_EX(arData, nIndex);
1281
8.26k
  if (i == idx) {
1282
8.25k
    HT_HASH_EX(arData, nIndex) = Z_NEXT(b->val);
1283
8.25k
  } else {
1284
10
    p = HT_HASH_TO_BUCKET_EX(arData, i);
1285
10
    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
10
    Z_NEXT(p->val) = Z_NEXT(b->val);
1290
10
  }
1291
8.26k
  zend_string_release(b->key);
1292
1293
  /* add to hash */
1294
8.26k
  idx = b - arData;
1295
8.26k
  b->key = key;
1296
8.26k
  b->h = ZSTR_H(key);
1297
8.26k
  nIndex = b->h | ht->nTableMask;
1298
8.26k
  idx = HT_IDX_TO_HASH(idx);
1299
8.26k
  i = HT_HASH_EX(arData, nIndex);
1300
8.26k
  if (i == HT_INVALID_IDX || i < idx) {
1301
8.26k
    Z_NEXT(b->val) = i;
1302
8.26k
    HT_HASH_EX(arData, nIndex) = idx;
1303
8.26k
  } else {
1304
3
    p = HT_HASH_TO_BUCKET_EX(arData, i);
1305
3
    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
3
    Z_NEXT(b->val) = Z_NEXT(p->val);
1310
3
    Z_NEXT(p->val) = idx;
1311
3
  }
1312
8.26k
  return &b->val;
1313
8.43k
}
1314
1315
static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht)
1316
67.1k
{
1317
1318
67.1k
  IS_CONSISTENT(ht);
1319
67.1k
  HT_ASSERT_RC1(ht);
1320
1321
67.1k
  ZEND_ASSERT(!HT_IS_PACKED(ht));
1322
67.1k
  if (ht->nNumUsed > ht->nNumOfElements + (ht->nNumOfElements >> 5)) { /* additional term is there to amortize the cost of compaction */
1323
18.4k
    zend_hash_rehash(ht);
1324
48.6k
  } else if (ht->nTableSize < HT_MAX_SIZE) { /* Let's double the table size */
1325
48.6k
    void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
1326
48.6k
    uint32_t nSize = ht->nTableSize + ht->nTableSize;
1327
48.6k
    Bucket *old_buckets = ht->arData;
1328
1329
48.6k
    ZEND_ASSERT(HT_SIZE_TO_MASK(nSize) != 0);
1330
1331
48.6k
    new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
1332
48.6k
    ht->nTableSize = nSize;
1333
48.6k
    ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
1334
48.6k
    HT_SET_DATA_ADDR(ht, new_data);
1335
48.6k
    memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
1336
48.6k
    pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
1337
48.6k
    zend_hash_rehash(ht);
1338
48.6k
  } 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
67.1k
}
1342
1343
ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
1344
285k
{
1345
285k
  Bucket *p;
1346
285k
  uint32_t nIndex, i;
1347
1348
285k
  IS_CONSISTENT(ht);
1349
1350
285k
  if (UNEXPECTED(ht->nNumOfElements == 0)) {
1351
43
    if (!(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
1352
43
      ht->nNumUsed = 0;
1353
43
      HT_HASH_RESET(ht);
1354
      /* Even if the array is empty, we still need to reset the iterator positions. */
1355
43
      ht->nInternalPointer = 0;
1356
43
      if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
1357
12
        HashTableIterator *iter = EG(ht_iterators);
1358
12
        HashTableIterator *end  = iter + EG(ht_iterators_used);
1359
24
        while (iter != end) {
1360
12
          if (iter->ht == ht) {
1361
12
            iter->pos = 0;
1362
12
          }
1363
12
          iter++;
1364
12
        }
1365
12
      }
1366
43
    }
1367
43
    return;
1368
43
  }
1369
1370
285k
  HT_HASH_RESET(ht);
1371
285k
  i = 0;
1372
285k
  p = ht->arData;
1373
285k
  if (HT_IS_WITHOUT_HOLES(ht)) {
1374
2.30M
    do {
1375
2.30M
      nIndex = p->h | ht->nTableMask;
1376
2.30M
      Z_NEXT(p->val) = HT_HASH(ht, nIndex);
1377
2.30M
      HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(i);
1378
2.30M
      p++;
1379
2.30M
    } while (++i < ht->nNumUsed);
1380
255k
  } else {
1381
29.8k
    uint32_t old_num_used = ht->nNumUsed;
1382
3.27M
    do {
1383
3.27M
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) {
1384
29.8k
        uint32_t j = i;
1385
29.8k
        Bucket *q = p;
1386
1387
29.8k
        if (EXPECTED(!HT_HAS_ITERATORS(ht))) {
1388
458M
          while (++i < ht->nNumUsed) {
1389
458M
            p++;
1390
458M
            if (EXPECTED(Z_TYPE_INFO(p->val) != IS_UNDEF)) {
1391
85.2M
              ZVAL_COPY_VALUE(&q->val, &p->val);
1392
85.2M
              q->h = p->h;
1393
85.2M
              nIndex = q->h | ht->nTableMask;
1394
85.2M
              q->key = p->key;
1395
85.2M
              Z_NEXT(q->val) = HT_HASH(ht, nIndex);
1396
85.2M
              HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j);
1397
85.2M
              if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) {
1398
8
                ht->nInternalPointer = j;
1399
8
              }
1400
85.2M
              q++;
1401
85.2M
              j++;
1402
85.2M
            }
1403
458M
          }
1404
29.7k
        } else {
1405
70
          uint32_t iter_pos = zend_hash_iterators_lower_pos(ht, i + 1);
1406
1407
609
          while (++i < ht->nNumUsed) {
1408
539
            p++;
1409
539
            if (EXPECTED(Z_TYPE_INFO(p->val) != IS_UNDEF)) {
1410
251
              ZVAL_COPY_VALUE(&q->val, &p->val);
1411
251
              q->h = p->h;
1412
251
              nIndex = q->h | ht->nTableMask;
1413
251
              q->key = p->key;
1414
251
              Z_NEXT(q->val) = HT_HASH(ht, nIndex);
1415
251
              HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j);
1416
251
              if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) {
1417
4
                ht->nInternalPointer = j;
1418
4
              }
1419
251
              if (UNEXPECTED(i >= iter_pos)) {
1420
91
                do {
1421
91
                  zend_hash_iterators_update(ht, iter_pos, j);
1422
91
                  iter_pos = zend_hash_iterators_lower_pos(ht, iter_pos + 1);
1423
91
                } while (iter_pos <= i);
1424
81
              }
1425
251
              q++;
1426
251
              j++;
1427
251
            }
1428
539
          }
1429
70
        }
1430
29.8k
        ht->nNumUsed = j;
1431
29.8k
        break;
1432
29.8k
      }
1433
3.24M
      nIndex = p->h | ht->nTableMask;
1434
3.24M
      Z_NEXT(p->val) = HT_HASH(ht, nIndex);
1435
3.24M
      HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(i);
1436
3.24M
      p++;
1437
3.24M
    } 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
29.8k
    if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
1442
70
      _zend_hash_iterators_update(ht, old_num_used, ht->nNumUsed);
1443
70
    }
1444
29.8k
  }
1445
285k
}
1446
1447
static zend_always_inline void zend_hash_iterators_clamp_max(const HashTable *ht, uint32_t max)
1448
64.5M
{
1449
64.5M
  if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
1450
1.11k
    HashTableIterator *iter = EG(ht_iterators);
1451
1.11k
    const HashTableIterator *end = iter + EG(ht_iterators_used);
1452
2.24k
    while (iter != end) {
1453
1.13k
      if (iter->ht == ht) {
1454
1.11k
        iter->pos = MIN(iter->pos, max);
1455
1.11k
      }
1456
1.13k
      iter++;
1457
1.13k
    }
1458
1.11k
  }
1459
64.5M
}
1460
1461
static zend_always_inline void _zend_hash_packed_del_val(HashTable *ht, uint32_t idx, zval *zv)
1462
13.6k
{
1463
13.6k
  idx = HT_HASH_TO_IDX(idx);
1464
13.6k
  ht->nNumOfElements--;
1465
13.6k
  if (ht->nNumUsed - 1 == idx) {
1466
26.5k
    do {
1467
26.5k
      ht->nNumUsed--;
1468
26.5k
    } while (ht->nNumUsed > 0 && (UNEXPECTED(Z_TYPE(ht->arPacked[ht->nNumUsed-1]) == IS_UNDEF)));
1469
11.3k
    ht->nInternalPointer = MIN(ht->nInternalPointer, ht->nNumUsed);
1470
11.3k
    zend_hash_iterators_clamp_max(ht, ht->nNumUsed);
1471
11.3k
  }
1472
13.6k
  if (ht->pDestructor) {
1473
13.6k
    zval tmp;
1474
13.6k
    ZVAL_COPY_VALUE(&tmp, zv);
1475
13.6k
    ZVAL_UNDEF(zv);
1476
13.6k
    ht->pDestructor(&tmp);
1477
13.6k
  } else {
1478
0
    ZVAL_UNDEF(zv);
1479
0
  }
1480
13.6k
}
1481
1482
static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, Bucket *p, Bucket *prev)
1483
845M
{
1484
845M
  if (prev) {
1485
57.4M
    Z_NEXT(prev->val) = Z_NEXT(p->val);
1486
787M
  } else {
1487
787M
    HT_HASH(ht, p->h | ht->nTableMask) = Z_NEXT(p->val);
1488
787M
  }
1489
845M
  idx = HT_HASH_TO_IDX(idx);
1490
845M
  ht->nNumOfElements--;
1491
845M
  if (ht->nNumUsed - 1 == idx) {
1492
231M
    do {
1493
231M
      ht->nNumUsed--;
1494
231M
    } while (ht->nNumUsed > 0 && (UNEXPECTED(Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF)));
1495
64.5M
    ht->nInternalPointer = MIN(ht->nInternalPointer, ht->nNumUsed);
1496
64.5M
    zend_hash_iterators_clamp_max(ht, ht->nNumUsed);
1497
64.5M
  }
1498
845M
  if (ht->pDestructor) {
1499
1.54M
    zval tmp;
1500
1.54M
    ZVAL_COPY_VALUE(&tmp, &p->val);
1501
1.54M
    ZVAL_UNDEF(&p->val);
1502
1.54M
    ht->pDestructor(&tmp);
1503
843M
  } else {
1504
843M
    ZVAL_UNDEF(&p->val);
1505
843M
  }
1506
845M
}
1507
1508
static zend_always_inline void _zend_hash_del_el(HashTable *ht, uint32_t idx, Bucket *p)
1509
845M
{
1510
845M
  Bucket *prev = NULL;
1511
845M
  uint32_t nIndex;
1512
845M
  uint32_t i;
1513
1514
845M
  nIndex = p->h | ht->nTableMask;
1515
845M
  i = HT_HASH(ht, nIndex);
1516
1517
845M
  if (i != idx) {
1518
57.4M
    prev = HT_HASH_TO_BUCKET(ht, i);
1519
61.0M
    while (Z_NEXT(prev->val) != idx) {
1520
3.59M
      i = Z_NEXT(prev->val);
1521
3.59M
      prev = HT_HASH_TO_BUCKET(ht, i);
1522
3.59M
    }
1523
57.4M
  }
1524
1525
845M
  if (p->key) {
1526
1.52M
    zend_string_release(p->key);
1527
1.52M
    p->key = NULL;
1528
1.52M
  }
1529
845M
  _zend_hash_del_el_ex(ht, idx, p, prev);
1530
845M
}
1531
1532
ZEND_API void ZEND_FASTCALL zend_hash_packed_del_val(HashTable *ht, zval *zv)
1533
1.92k
{
1534
1.92k
  IS_CONSISTENT(ht);
1535
1.92k
  HT_ASSERT_RC1(ht);
1536
1.92k
  ZEND_ASSERT(HT_IS_PACKED(ht));
1537
1.92k
  _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(zv - ht->arPacked), zv);
1538
1.92k
}
1539
1540
1541
ZEND_API void ZEND_FASTCALL zend_hash_del_bucket(HashTable *ht, Bucket *p)
1542
843M
{
1543
843M
  IS_CONSISTENT(ht);
1544
843M
  HT_ASSERT_RC1(ht);
1545
843M
  ZEND_ASSERT(!HT_IS_PACKED(ht));
1546
843M
  _zend_hash_del_el(ht, HT_IDX_TO_HASH(p - ht->arData), p);
1547
843M
}
1548
1549
ZEND_API zend_result ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key)
1550
249k
{
1551
249k
  zend_ulong h;
1552
249k
  uint32_t nIndex;
1553
249k
  uint32_t idx;
1554
249k
  Bucket *p;
1555
249k
  Bucket *prev = NULL;
1556
1557
249k
  IS_CONSISTENT(ht);
1558
249k
  HT_ASSERT_RC1(ht);
1559
1560
249k
  h = zend_string_hash_val(key);
1561
249k
  nIndex = h | ht->nTableMask;
1562
1563
249k
  idx = HT_HASH(ht, nIndex);
1564
257k
  while (idx != HT_INVALID_IDX) {
1565
215k
    p = HT_HASH_TO_BUCKET(ht, idx);
1566
215k
    if ((p->key == key) ||
1567
7.46k
      (p->h == h &&
1568
1.21k
         p->key &&
1569
208k
         zend_string_equal_content(p->key, key))) {
1570
208k
      zend_string_release(p->key);
1571
208k
      p->key = NULL;
1572
208k
      _zend_hash_del_el_ex(ht, idx, p, prev);
1573
208k
      return SUCCESS;
1574
208k
    }
1575
7.11k
    prev = p;
1576
7.11k
    idx = Z_NEXT(p->val);
1577
7.11k
  }
1578
41.1k
  return FAILURE;
1579
249k
}
1580
1581
ZEND_API zend_result ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key)
1582
281
{
1583
281
  zend_ulong h;
1584
281
  uint32_t nIndex;
1585
281
  uint32_t idx;
1586
281
  Bucket *p;
1587
281
  Bucket *prev = NULL;
1588
1589
281
  IS_CONSISTENT(ht);
1590
281
  HT_ASSERT_RC1(ht);
1591
1592
281
  h = zend_string_hash_val(key);
1593
281
  nIndex = h | ht->nTableMask;
1594
1595
281
  idx = HT_HASH(ht, nIndex);
1596
291
  while (idx != HT_INVALID_IDX) {
1597
200
    p = HT_HASH_TO_BUCKET(ht, idx);
1598
200
    if ((p->key == key) ||
1599
11
      (p->h == h &&
1600
1
         p->key &&
1601
190
         zend_string_equal_content(p->key, key))) {
1602
190
      if (Z_TYPE(p->val) == IS_INDIRECT) {
1603
55
        zval *data = Z_INDIRECT(p->val);
1604
1605
55
        if (UNEXPECTED(Z_TYPE_P(data) == IS_UNDEF)) {
1606
6
          return FAILURE;
1607
49
        } else {
1608
49
          if (ht->pDestructor) {
1609
49
            zval tmp;
1610
49
            ZVAL_COPY_VALUE(&tmp, data);
1611
49
            ZVAL_UNDEF(data);
1612
49
            ht->pDestructor(&tmp);
1613
49
          } else {
1614
0
            ZVAL_UNDEF(data);
1615
0
          }
1616
49
          HT_FLAGS(ht) |= HASH_FLAG_HAS_EMPTY_IND;
1617
49
        }
1618
135
      } else {
1619
135
        zend_string_release(p->key);
1620
135
        p->key = NULL;
1621
135
        _zend_hash_del_el_ex(ht, idx, p, prev);
1622
135
      }
1623
184
      return SUCCESS;
1624
190
    }
1625
10
    prev = p;
1626
10
    idx = Z_NEXT(p->val);
1627
10
  }
1628
91
  return FAILURE;
1629
281
}
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
1.25k
{
1678
1.25k
  zend_ulong h;
1679
1.25k
  uint32_t nIndex;
1680
1.25k
  uint32_t idx;
1681
1.25k
  Bucket *p;
1682
1.25k
  Bucket *prev = NULL;
1683
1684
1.25k
  IS_CONSISTENT(ht);
1685
1.25k
  HT_ASSERT_RC1(ht);
1686
1687
1.25k
  h = zend_inline_hash_func(str, len);
1688
1.25k
  nIndex = h | ht->nTableMask;
1689
1690
1.25k
  idx = HT_HASH(ht, nIndex);
1691
1.40k
  while (idx != HT_INVALID_IDX) {
1692
1.33k
    p = HT_HASH_TO_BUCKET(ht, idx);
1693
1.33k
    if ((p->h == h)
1694
1.19k
       && p->key
1695
1.19k
       && zend_string_equals_cstr(p->key, str, len)) {
1696
1.19k
      zend_string_release(p->key);
1697
1.19k
      p->key = NULL;
1698
1.19k
      _zend_hash_del_el_ex(ht, idx, p, prev);
1699
1.19k
      return SUCCESS;
1700
1.19k
    }
1701
144
    prev = p;
1702
144
    idx = Z_NEXT(p->val);
1703
144
  }
1704
64
  return FAILURE;
1705
1.25k
}
1706
1707
ZEND_API zend_result ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h)
1708
221k
{
1709
221k
  uint32_t nIndex;
1710
221k
  uint32_t idx;
1711
221k
  Bucket *p;
1712
221k
  Bucket *prev = NULL;
1713
1714
221k
  IS_CONSISTENT(ht);
1715
221k
  HT_ASSERT_RC1(ht);
1716
1717
221k
  if (HT_IS_PACKED(ht)) {
1718
11.0k
    if (h < ht->nNumUsed) {
1719
10.6k
      zval *zv = ht->arPacked + h;
1720
10.6k
      if (Z_TYPE_P(zv) != IS_UNDEF) {
1721
10.6k
        _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(h), zv);
1722
10.6k
        return SUCCESS;
1723
10.6k
      }
1724
10.6k
    }
1725
358
    return FAILURE;
1726
11.0k
  }
1727
210k
  nIndex = h | ht->nTableMask;
1728
1729
210k
  idx = HT_HASH(ht, nIndex);
1730
908k
  while (idx != HT_INVALID_IDX) {
1731
780k
    p = HT_HASH_TO_BUCKET(ht, idx);
1732
780k
    if ((p->h == h) && (p->key == NULL)) {
1733
81.7k
      _zend_hash_del_el_ex(ht, idx, p, prev);
1734
81.7k
      return SUCCESS;
1735
81.7k
    }
1736
698k
    prev = p;
1737
698k
    idx = Z_NEXT(p->val);
1738
698k
  }
1739
128k
  return FAILURE;
1740
210k
}
1741
1742
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
1743
4.10M
{
1744
4.10M
  IS_CONSISTENT(ht);
1745
4.10M
  HT_ASSERT(ht, GC_REFCOUNT(ht) <= 1);
1746
1747
4.10M
  if (ht->nNumUsed) {
1748
855k
    if (HT_IS_PACKED(ht)) {
1749
126k
      if (ht->pDestructor) {
1750
125k
        zval *zv = ht->arPacked;
1751
125k
        zval *end = zv + ht->nNumUsed;
1752
1753
125k
        SET_INCONSISTENT(HT_IS_DESTROYING);
1754
125k
        if (HT_IS_WITHOUT_HOLES(ht)) {
1755
424k
          do {
1756
424k
            ht->pDestructor(zv);
1757
424k
          } while (++zv != end);
1758
124k
        } else {
1759
4.44k
          do {
1760
4.44k
            if (EXPECTED(Z_TYPE_P(zv) != IS_UNDEF)) {
1761
1.33k
              ht->pDestructor(zv);
1762
1.33k
            }
1763
4.44k
          } while (++zv != end);
1764
1.20k
        }
1765
125k
        SET_INCONSISTENT(HT_DESTROYED);
1766
125k
      }
1767
126k
      zend_hash_iterators_remove(ht);
1768
728k
    } else {
1769
728k
      Bucket *p = ht->arData;
1770
728k
      Bucket *end = p + ht->nNumUsed;
1771
1772
728k
      if (ht->pDestructor) {
1773
160k
        SET_INCONSISTENT(HT_IS_DESTROYING);
1774
1775
160k
        if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1776
91.1k
          if (HT_IS_WITHOUT_HOLES(ht)) {
1777
405k
            do {
1778
405k
              ht->pDestructor(&p->val);
1779
405k
            } while (++p != end);
1780
91.1k
          } 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
91.1k
        } else if (HT_IS_WITHOUT_HOLES(ht)) {
1788
83.0k
          do {
1789
83.0k
            ht->pDestructor(&p->val);
1790
83.0k
            if (EXPECTED(p->key)) {
1791
82.3k
              zend_string_release(p->key);
1792
82.3k
            }
1793
83.0k
          } while (++p != end);
1794
69.3k
        } else {
1795
29
          do {
1796
29
            if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1797
17
              ht->pDestructor(&p->val);
1798
17
              if (EXPECTED(p->key)) {
1799
17
                zend_string_release(p->key);
1800
17
              }
1801
17
            }
1802
29
          } while (++p != end);
1803
5
        }
1804
1805
160k
        SET_INCONSISTENT(HT_DESTROYED);
1806
568k
      } else {
1807
568k
        if (!HT_HAS_STATIC_KEYS_ONLY(ht)) {
1808
1.94M
          do {
1809
1.94M
            if (EXPECTED(p->key)) {
1810
1.94M
              zend_string_release(p->key);
1811
1.94M
            }
1812
1.94M
          } while (++p != end);
1813
329k
        }
1814
568k
      }
1815
728k
      zend_hash_iterators_remove(ht);
1816
728k
    }
1817
3.25M
  } else if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
1818
3.10M
    return;
1819
3.10M
  }
1820
1.00M
  pefree(HT_GET_DATA_ADDR(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
1821
1.00M
}
1822
1823
ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht)
1824
7.76M
{
1825
7.76M
  IS_CONSISTENT(ht);
1826
7.76M
  HT_ASSERT(ht, GC_REFCOUNT(ht) <= 1);
1827
1828
  /* break possible cycles */
1829
7.76M
  GC_REMOVE_FROM_BUFFER(ht);
1830
7.76M
  GC_TYPE_INFO(ht) = GC_NULL /*???| (GC_WHITE << 16)*/;
1831
1832
7.76M
  if (ht->nNumUsed) {
1833
    /* In some rare cases destructors of regular arrays may be changed */
1834
5.39M
    if (UNEXPECTED(ht->pDestructor != ZVAL_PTR_DTOR)) {
1835
2.04k
      zend_hash_destroy(ht);
1836
2.04k
      goto free_ht;
1837
2.04k
    }
1838
1839
5.39M
    SET_INCONSISTENT(HT_IS_DESTROYING);
1840
1841
5.39M
    if (HT_IS_PACKED(ht)) {
1842
2.35M
      zval *zv = ht->arPacked;
1843
2.35M
      zval *end = zv + ht->nNumUsed;
1844
1845
24.3M
      do {
1846
24.3M
        i_zval_ptr_dtor(zv);
1847
24.3M
      } while (++zv != end);
1848
3.04M
    } else {
1849
3.04M
      Bucket *p = ht->arData;
1850
3.04M
      Bucket *end = p + ht->nNumUsed;
1851
1852
3.04M
      if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1853
9.69M
        do {
1854
9.69M
          i_zval_ptr_dtor(&p->val);
1855
9.69M
        } while (++p != end);
1856
2.34M
      } else if (HT_IS_WITHOUT_HOLES(ht)) {
1857
2.66M
        do {
1858
2.66M
          i_zval_ptr_dtor(&p->val);
1859
2.66M
          if (EXPECTED(p->key)) {
1860
2.36M
            zend_string_release_ex(p->key, 0);
1861
2.36M
          }
1862
2.66M
        } while (++p != end);
1863
700k
      } else {
1864
128
        do {
1865
128
          if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1866
100
            i_zval_ptr_dtor(&p->val);
1867
100
            if (EXPECTED(p->key)) {
1868
88
              zend_string_release_ex(p->key, 0);
1869
88
            }
1870
100
          }
1871
128
        } while (++p != end);
1872
25
      }
1873
3.04M
    }
1874
5.39M
  } else if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
1875
1.84M
    goto free_ht;
1876
1.84M
  }
1877
5.91M
  SET_INCONSISTENT(HT_DESTROYED);
1878
5.91M
  efree(HT_GET_DATA_ADDR(ht));
1879
7.76M
free_ht:
1880
7.76M
  zend_hash_iterators_remove(ht);
1881
7.76M
  FREE_HASHTABLE(ht);
1882
7.76M
}
1883
1884
ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht)
1885
1.04M
{
1886
1.04M
  IS_CONSISTENT(ht);
1887
1.04M
  HT_ASSERT_RC1(ht);
1888
1889
1.04M
  if (ht->nNumUsed) {
1890
228k
    if (HT_IS_PACKED(ht)) {
1891
3.37k
      zval *zv = ht->arPacked;
1892
3.37k
      zval *end = zv + ht->nNumUsed;
1893
1894
3.37k
      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
225k
    } else {
1910
225k
      Bucket *p = ht->arData;
1911
225k
      Bucket *end = p + ht->nNumUsed;
1912
1913
225k
      if (ht->pDestructor) {
1914
627
        if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1915
545
          if (HT_IS_WITHOUT_HOLES(ht)) {
1916
1.03k
            do {
1917
1.03k
              ht->pDestructor(&p->val);
1918
1.03k
            } while (++p != end);
1919
496
          } else {
1920
175
            do {
1921
175
              if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1922
94
                ht->pDestructor(&p->val);
1923
94
              }
1924
175
            } while (++p != end);
1925
49
          }
1926
545
        } else if (HT_IS_WITHOUT_HOLES(ht)) {
1927
114
          do {
1928
114
            ht->pDestructor(&p->val);
1929
114
            if (EXPECTED(p->key)) {
1930
114
              zend_string_release(p->key);
1931
114
            }
1932
114
          } while (++p != end);
1933
82
        } 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
224k
      } else {
1944
224k
        if (!HT_HAS_STATIC_KEYS_ONLY(ht)) {
1945
648k
          do {
1946
648k
            if (EXPECTED(p->key)) {
1947
553k
              zend_string_release(p->key);
1948
553k
            }
1949
648k
          } while (++p != end);
1950
101k
        }
1951
224k
      }
1952
225k
      HT_HASH_RESET(ht);
1953
225k
    }
1954
228k
  }
1955
1.04M
  ht->nNumUsed = 0;
1956
1.04M
  ht->nNumOfElements = 0;
1957
1.04M
  ht->nNextFreeElement = ZEND_LONG_MIN;
1958
1.04M
  ht->nInternalPointer = 0;
1959
1.04M
}
1960
1961
ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht)
1962
5.08k
{
1963
5.08k
  Bucket *p, *end;
1964
1965
5.08k
  IS_CONSISTENT(ht);
1966
5.08k
  HT_ASSERT_RC1(ht);
1967
1968
5.08k
  if (ht->nNumUsed) {
1969
5.00k
    ZEND_ASSERT(!HT_IS_PACKED(ht));
1970
5.00k
    p = ht->arData;
1971
5.00k
    end = p + ht->nNumUsed;
1972
5.00k
    if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1973
17.6k
      do {
1974
17.6k
        i_zval_ptr_dtor(&p->val);
1975
17.6k
      } while (++p != end);
1976
3.80k
    } else if (HT_IS_WITHOUT_HOLES(ht)) {
1977
7.96k
      do {
1978
7.96k
        i_zval_ptr_dtor(&p->val);
1979
7.96k
        if (EXPECTED(p->key)) {
1980
7.96k
          zend_string_release(p->key);
1981
7.96k
        }
1982
7.96k
      } while (++p != end);
1983
1.16k
    } else {
1984
138
      do {
1985
138
        if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) {
1986
108
          i_zval_ptr_dtor(&p->val);
1987
108
          if (EXPECTED(p->key)) {
1988
108
            zend_string_release(p->key);
1989
108
          }
1990
108
        }
1991
138
      } while (++p != end);
1992
30
    }
1993
5.00k
    HT_HASH_RESET(ht);
1994
5.00k
  }
1995
5.08k
  ht->nNumUsed = 0;
1996
5.08k
  ht->nNumOfElements = 0;
1997
5.08k
  ht->nNextFreeElement = ZEND_LONG_MIN;
1998
5.08k
  ht->nInternalPointer = 0;
1999
5.08k
}
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
591k
{
2032
591k
  uint32_t idx;
2033
2034
591k
  IS_CONSISTENT(ht);
2035
591k
  HT_ASSERT_RC1(ht);
2036
2037
591k
  idx = ht->nNumUsed;
2038
591k
  if (HT_IS_PACKED(ht)) {
2039
6.48k
    zval *zv = ht->arPacked + ht->nNumUsed;
2040
2041
7.78k
    while (idx > 0) {
2042
1.29k
      idx--;
2043
1.29k
      zv--;
2044
1.29k
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2045
1.00k
      _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(idx), zv);
2046
1.00k
    }
2047
584k
  } else {
2048
584k
    Bucket *p = ht->arData + ht->nNumUsed;
2049
2050
2.04M
    while (idx > 0) {
2051
1.45M
      idx--;
2052
1.45M
      p--;
2053
1.45M
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2054
1.43M
      _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2055
1.43M
    }
2056
584k
  }
2057
2058
591k
  if (!(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
2059
302k
    pefree(HT_GET_DATA_ADDR(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
2060
302k
  }
2061
2062
591k
  SET_INCONSISTENT(HT_DESTROYED);
2063
591k
}
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
291
{
2076
291
  uint32_t idx;
2077
291
  int result;
2078
2079
291
  IS_CONSISTENT(ht);
2080
291
  if (HT_IS_PACKED(ht)) {
2081
577
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2082
302
      zval *zv = ht->arPacked + idx;
2083
2084
302
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2085
302
      result = apply_func(zv);
2086
2087
302
      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
302
      if (result & ZEND_HASH_APPLY_STOP) {
2092
0
        break;
2093
0
      }
2094
302
    }
2095
275
  } else {
2096
224
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2097
208
      Bucket *p = ht->arData + idx;
2098
2099
208
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2100
208
      result = apply_func(&p->val);
2101
2102
208
      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
208
      if (result & ZEND_HASH_APPLY_STOP) {
2107
0
        break;
2108
0
      }
2109
208
    }
2110
16
  }
2111
291
}
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
326k
{
2209
326k
  uint32_t idx;
2210
326k
  int result;
2211
2212
326k
  IS_CONSISTENT(ht);
2213
2214
326k
  idx = ht->nNumUsed;
2215
326k
  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
326k
  } else {
2234
326k
    Bucket *p;
2235
2236
2.15M
    while (idx > 0) {
2237
1.82M
      idx--;
2238
1.82M
      p = ht->arData + idx;
2239
1.82M
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2240
2241
1.77M
      result = apply_func(&p->val);
2242
2243
1.77M
      if (result & ZEND_HASH_APPLY_REMOVE) {
2244
42.0k
        HT_ASSERT_RC1(ht);
2245
42.0k
        _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2246
42.0k
      }
2247
1.77M
      if (result & ZEND_HASH_APPLY_STOP) {
2248
44
        break;
2249
44
      }
2250
1.77M
    }
2251
326k
  }
2252
326k
}
2253
2254
2255
ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor)
2256
300k
{
2257
300k
  uint32_t idx;
2258
300k
  zval *new_entry, *data;
2259
2260
300k
  IS_CONSISTENT(source);
2261
300k
  IS_CONSISTENT(target);
2262
300k
  HT_ASSERT_RC1(target);
2263
2264
300k
  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
2.15M
  for (idx = 0; idx < source->nNumUsed; idx++) {
2278
1.84M
    Bucket *p = source->arData + idx;
2279
2280
1.84M
    if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2281
2282
    /* INDIRECT element may point to UNDEF-ined slots */
2283
1.84M
    data = &p->val;
2284
1.84M
    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
1.84M
    if (p->key) {
2291
1.84M
      new_entry = zend_hash_update(target, p->key, data);
2292
1.84M
    } else {
2293
31
      new_entry = zend_hash_index_update(target, p->h, data);
2294
31
    }
2295
1.84M
    if (pCopyConstructor) {
2296
30
      pCopyConstructor(new_entry);
2297
30
    }
2298
1.84M
  }
2299
300k
}
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
62.4k
{
2304
62.4k
  if (with_holes) {
2305
2.68k
    if (!packed && Z_TYPE_INFO_P(data) == IS_INDIRECT) {
2306
0
      data = Z_INDIRECT_P(data);
2307
0
    }
2308
2.68k
    if (UNEXPECTED(Z_TYPE_INFO_P(data) == IS_UNDEF)) {
2309
1.01k
      return 0;
2310
1.01k
    }
2311
59.7k
  } else if (!packed) {
2312
    /* INDIRECT element may point to UNDEF-ined slots */
2313
42.0k
    if (Z_TYPE_INFO_P(data) == IS_INDIRECT) {
2314
15.9k
      data = Z_INDIRECT_P(data);
2315
15.9k
      if (UNEXPECTED(Z_TYPE_INFO_P(data) == IS_UNDEF)) {
2316
9.33k
        return 0;
2317
9.33k
      }
2318
15.9k
    }
2319
42.0k
  }
2320
2321
52.1k
  do {
2322
52.1k
    if (Z_OPT_REFCOUNTED_P(data)) {
2323
22.9k
      if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1 &&
2324
595
          (Z_TYPE_P(Z_REFVAL_P(data)) != IS_ARRAY ||
2325
367
            Z_ARRVAL_P(Z_REFVAL_P(data)) != source)) {
2326
367
        data = Z_REFVAL_P(data);
2327
367
        if (!Z_OPT_REFCOUNTED_P(data)) {
2328
314
          break;
2329
314
        }
2330
367
      }
2331
22.6k
      Z_ADDREF_P(data);
2332
22.6k
    }
2333
52.1k
  } while (0);
2334
52.1k
  ZVAL_COPY_VALUE(dest, data);
2335
2336
52.1k
  return 1;
2337
62.4k
}
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
42.3k
{
2341
42.3k
  if (!zend_array_dup_value(source, &p->val, &q->val, packed, with_holes)) {
2342
9.45k
    return 0;
2343
9.45k
  }
2344
2345
32.9k
  if (!packed) {
2346
32.9k
    uint32_t nIndex;
2347
2348
32.9k
    q->h = p->h;
2349
32.9k
    q->key = p->key;
2350
32.9k
    if (!static_keys && q->key) {
2351
6.68k
      zend_string_addref(q->key);
2352
6.68k
    }
2353
2354
32.9k
    nIndex = q->h | target->nTableMask;
2355
32.9k
    Z_NEXT(q->val) = HT_HASH(target, nIndex);
2356
32.9k
    HT_HASH(target, nIndex) = HT_IDX_TO_HASH(idx);
2357
32.9k
  }
2358
32.9k
  return 1;
2359
42.3k
}
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
1.02k
static void zend_array_dup_ht_iterators(const HashTable *source, HashTable *target) {
2363
1.02k
  uint32_t iter_index = 0;
2364
1.02k
  uint32_t end_index = EG(ht_iterators_used);
2365
2366
27.6k
  while (iter_index != end_index) {
2367
26.5k
    HashTableIterator *iter = &EG(ht_iterators)[iter_index];
2368
26.5k
    if (iter->ht == source) {
2369
1.03k
      uint32_t copy_idx = zend_hash_iterator_add(target, iter->pos);
2370
      /* Refetch iter because the memory may be reallocated. */
2371
1.03k
      iter = &EG(ht_iterators)[iter_index];
2372
1.03k
      HashTableIterator *copy_iter = EG(ht_iterators) + copy_idx;
2373
1.03k
      copy_iter->next_copy = iter->next_copy;
2374
1.03k
      iter->next_copy = copy_idx;
2375
1.03k
    }
2376
26.5k
    iter_index++;
2377
26.5k
  }
2378
1.02k
}
2379
2380
static zend_always_inline void zend_array_dup_packed_elements(const HashTable *source, HashTable *target, bool with_holes)
2381
3.99k
{
2382
3.99k
  zval *p = source->arPacked;
2383
3.99k
  zval *q = target->arPacked;
2384
3.99k
  const zval *end = p + source->nNumUsed;
2385
2386
20.0k
  do {
2387
20.0k
    if (!zend_array_dup_value(source, p, q, true, with_holes)) {
2388
885
      if (with_holes) {
2389
885
        ZVAL_UNDEF(q);
2390
885
      }
2391
885
    }
2392
20.0k
    p++; q++;
2393
20.0k
  } while (p != end);
2394
2395
3.99k
  if (UNEXPECTED(HT_HAS_ITERATORS(source))) {
2396
828
    zend_array_dup_ht_iterators(source, target);
2397
828
  }
2398
3.99k
}
2399
2400
static zend_always_inline uint32_t zend_array_dup_elements(const HashTable *source, HashTable *target, bool static_keys, bool with_holes)
2401
5.93k
{
2402
5.93k
  uint32_t idx = 0;
2403
5.93k
  Bucket *p = source->arData;
2404
5.93k
  Bucket *q = target->arData;
2405
5.93k
  const Bucket *end = p + source->nNumUsed;
2406
2407
5.93k
  if (UNEXPECTED(HT_HAS_ITERATORS(source))) {
2408
197
    zend_array_dup_ht_iterators(source, target);
2409
197
  }
2410
2411
32.7k
  do {
2412
32.7k
    if (!zend_array_dup_element(source, target, idx, p, q, false, static_keys, with_holes)) {
2413
1.82k
      uint32_t target_idx = idx;
2414
2415
1.82k
      idx++; p++;
2416
1.82k
      if (EXPECTED(!HT_HAS_ITERATORS(target))) {
2417
11.2k
        while (p != end) {
2418
9.48k
          if (zend_array_dup_element(source, target, target_idx, p, q, false, static_keys, with_holes)) {
2419
1.88k
            if (UNEXPECTED(target->nInternalPointer > target_idx && target->nInternalPointer <= idx)) {
2420
11
              target->nInternalPointer = target_idx;
2421
11
            }
2422
1.88k
            target_idx++; q++;
2423
1.88k
          }
2424
9.48k
          idx++; p++;
2425
9.48k
        }
2426
1.77k
      } else {
2427
46
        target->nNumUsed = source->nNumUsed;
2428
46
        uint32_t iter_pos = zend_hash_iterators_lower_pos(target, idx);
2429
2430
211
        while (p != end) {
2431
165
          if (zend_array_dup_element(source, target, target_idx, p, q, false, static_keys, with_holes)) {
2432
126
            if (UNEXPECTED(target->nInternalPointer > target_idx && target->nInternalPointer <= idx)) {
2433
8
              target->nInternalPointer = target_idx;
2434
8
            }
2435
126
            if (UNEXPECTED(idx >= iter_pos)) {
2436
30
              do {
2437
30
                zend_hash_iterators_update(target, iter_pos, target_idx);
2438
30
                iter_pos = zend_hash_iterators_lower_pos(target, iter_pos + 1);
2439
30
              } while (iter_pos <= idx);
2440
25
            }
2441
126
            target_idx++; q++;
2442
126
          }
2443
165
          idx++; p++;
2444
165
        }
2445
        /* Move past-the-end iterators so they can pick up newly appended elements. */
2446
46
        _zend_hash_iterators_update(target, source->nNumUsed, target_idx);
2447
46
      }
2448
1.82k
      return target_idx;
2449
1.82k
    }
2450
30.9k
    idx++; p++; q++;
2451
30.9k
  } while (p != end);
2452
4.10k
  return idx;
2453
5.93k
}
2454
2455
ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(const HashTable *source)
2456
29.1k
{
2457
29.1k
  uint32_t idx;
2458
29.1k
  HashTable *target;
2459
2460
29.1k
  IS_CONSISTENT(source);
2461
2462
29.1k
  ALLOC_HASHTABLE(target);
2463
29.1k
  GC_SET_REFCOUNT(target, 1);
2464
29.1k
  GC_TYPE_INFO(target) = GC_ARRAY;
2465
2466
29.1k
  target->pDestructor = ZVAL_PTR_DTOR;
2467
2468
29.1k
  if (source->nNumOfElements == 0) {
2469
8.41k
    HT_FLAGS(target) = HASH_FLAG_UNINITIALIZED;
2470
8.41k
    target->nTableMask = HT_MIN_MASK;
2471
8.41k
    target->nNumUsed = 0;
2472
8.41k
    target->nNumOfElements = 0;
2473
8.41k
    target->nNextFreeElement = source->nNextFreeElement;
2474
8.41k
    target->nInternalPointer = 0;
2475
8.41k
    target->nTableSize = HT_MIN_SIZE;
2476
8.41k
    HT_SET_DATA_ADDR(target, &uninitialized_bucket);
2477
20.7k
  } else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) {
2478
10.8k
    ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_HAS_EMPTY_IND));
2479
10.8k
    HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK;
2480
10.8k
    target->nTableMask = source->nTableMask;
2481
10.8k
    target->nNumUsed = source->nNumUsed;
2482
10.8k
    target->nNumOfElements = source->nNumOfElements;
2483
10.8k
    target->nNextFreeElement = source->nNextFreeElement;
2484
10.8k
    target->nTableSize = source->nTableSize;
2485
10.8k
    if (HT_IS_PACKED(source)) {
2486
5.54k
      HT_SET_DATA_ADDR(target, emalloc(HT_PACKED_SIZE(target)));
2487
5.54k
      target->nInternalPointer = source->nInternalPointer;
2488
5.54k
      memcpy(HT_GET_DATA_ADDR(target), HT_GET_DATA_ADDR(source), HT_PACKED_USED_SIZE(source));
2489
5.54k
    } else {
2490
5.28k
      HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target)));
2491
5.28k
      target->nInternalPointer = source->nInternalPointer;
2492
5.28k
      memcpy(HT_GET_DATA_ADDR(target), HT_GET_DATA_ADDR(source), HT_USED_SIZE(source));
2493
5.28k
    }
2494
10.8k
  } else if (HT_IS_PACKED(source)) {
2495
3.99k
    ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_HAS_EMPTY_IND));
2496
3.99k
    HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK;
2497
3.99k
    target->nTableMask = HT_MIN_MASK;
2498
3.99k
    target->nNumUsed = source->nNumUsed;
2499
3.99k
    target->nNumOfElements = source->nNumOfElements;
2500
3.99k
    target->nNextFreeElement = source->nNextFreeElement;
2501
3.99k
    target->nTableSize = source->nTableSize;
2502
3.99k
    HT_SET_DATA_ADDR(target, emalloc(HT_PACKED_SIZE_EX(target->nTableSize, HT_MIN_MASK)));
2503
3.99k
    target->nInternalPointer =
2504
3.99k
      (source->nInternalPointer < source->nNumUsed) ?
2505
3.99k
        source->nInternalPointer : 0;
2506
2507
3.99k
    HT_HASH_RESET_PACKED(target);
2508
2509
3.99k
    if (HT_IS_WITHOUT_HOLES(target)) {
2510
3.52k
      zend_array_dup_packed_elements(source, target, false);
2511
3.52k
    } else {
2512
462
      zend_array_dup_packed_elements(source, target, true);
2513
462
    }
2514
5.93k
  } else {
2515
    /* Indirects are removed during duplication, remove HASH_FLAG_HAS_EMPTY_IND accordingly. */
2516
5.93k
    HT_FLAGS(target) = HT_FLAGS(source) & (HASH_FLAG_MASK & ~HASH_FLAG_HAS_EMPTY_IND);
2517
5.93k
    target->nTableMask = source->nTableMask;
2518
5.93k
    target->nNextFreeElement = source->nNextFreeElement;
2519
5.93k
    target->nInternalPointer =
2520
5.93k
      (source->nInternalPointer < source->nNumUsed) ?
2521
5.93k
        source->nInternalPointer : 0;
2522
2523
5.93k
    target->nTableSize = source->nTableSize;
2524
5.93k
    HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target)));
2525
5.93k
    HT_HASH_RESET(target);
2526
2527
5.93k
    if (HT_HAS_STATIC_KEYS_ONLY(target)) {
2528
4.42k
      if (HT_IS_WITHOUT_HOLES(source)) {
2529
4.37k
        idx = zend_array_dup_elements(source, target, true, false);
2530
4.37k
      } else {
2531
51
        idx = zend_array_dup_elements(source, target, true, true);
2532
51
      }
2533
4.42k
    } else {
2534
1.50k
      if (HT_IS_WITHOUT_HOLES(source)) {
2535
1.49k
        idx = zend_array_dup_elements(source, target, false, false);
2536
1.49k
      } else {
2537
6
        idx = zend_array_dup_elements(source, target, false, true);
2538
6
      }
2539
1.50k
    }
2540
5.93k
    target->nNumUsed = idx;
2541
5.93k
    target->nNumOfElements = idx;
2542
5.93k
  }
2543
29.1k
  return target;
2544
29.1k
}
2545
2546
ZEND_API HashTable* zend_array_to_list(const HashTable *source)
2547
3
{
2548
3
  HashTable *result = _zend_new_array(zend_hash_num_elements(source));
2549
3
  zend_hash_real_init_packed(result);
2550
2551
3
  ZEND_HASH_FILL_PACKED(result) {
2552
3
    zval *entry;
2553
2554
97
    ZEND_HASH_FOREACH_VAL(source, entry) {
2555
97
      if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) {
2556
0
        entry = Z_REFVAL_P(entry);
2557
0
      }
2558
97
      Z_TRY_ADDREF_P(entry);
2559
97
      ZEND_HASH_FILL_ADD(entry);
2560
97
    } ZEND_HASH_FOREACH_END();
2561
3
  } ZEND_HASH_FILL_END();
2562
2563
3
  return result;
2564
3
}
2565
2566
2567
ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite)
2568
4.63k
{
2569
4.63k
  uint32_t idx;
2570
4.63k
  Bucket *p;
2571
4.63k
  zval *t, *s;
2572
2573
4.63k
  IS_CONSISTENT(source);
2574
4.63k
  IS_CONSISTENT(target);
2575
4.63k
  HT_ASSERT_RC1(target);
2576
2577
4.63k
  if (overwrite) {
2578
0
    if (HT_IS_PACKED(source)) {
2579
0
      for (idx = 0; idx < source->nNumUsed; idx++) {
2580
0
        s = source->arPacked + idx;
2581
0
        if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2582
0
          continue;
2583
0
        }
2584
0
        t = zend_hash_index_update(target, idx, s);
2585
0
        if (pCopyConstructor) {
2586
0
          pCopyConstructor(t);
2587
0
        }
2588
0
      }
2589
0
      return;
2590
0
    }
2591
2592
0
    for (idx = 0; idx < source->nNumUsed; idx++) {
2593
0
      p = source->arData + idx;
2594
0
      s = &p->val;
2595
0
      if (UNEXPECTED(Z_TYPE_P(s) == IS_INDIRECT)) {
2596
0
        s = Z_INDIRECT_P(s);
2597
0
      }
2598
0
      if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2599
0
        continue;
2600
0
      }
2601
0
      if (p->key) {
2602
0
        t = _zend_hash_add_or_update_i(target, p->key, s, HASH_UPDATE | HASH_UPDATE_INDIRECT);
2603
0
        if (pCopyConstructor) {
2604
0
          pCopyConstructor(t);
2605
0
        }
2606
0
      } else {
2607
0
        t = zend_hash_index_update(target, p->h, s);
2608
0
        if (pCopyConstructor) {
2609
0
          pCopyConstructor(t);
2610
0
        }
2611
0
      }
2612
0
    }
2613
4.63k
  } else {
2614
4.63k
    if (HT_IS_PACKED(source)) {
2615
9.39k
      for (idx = 0; idx < source->nNumUsed; idx++) {
2616
7.62k
        s = source->arPacked + idx;
2617
7.62k
        if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2618
828
          continue;
2619
828
        }
2620
6.79k
        t = zend_hash_index_add(target, idx, s);
2621
6.79k
        if (t && pCopyConstructor) {
2622
4.79k
          pCopyConstructor(t);
2623
4.79k
        }
2624
6.79k
      }
2625
1.77k
      return;
2626
1.77k
    }
2627
2628
9.74k
    for (idx = 0; idx < source->nNumUsed; idx++) {
2629
6.87k
      p = source->arData + idx;
2630
6.87k
      s = &p->val;
2631
6.87k
      if (UNEXPECTED(Z_TYPE_P(s) == IS_INDIRECT)) {
2632
0
        s = Z_INDIRECT_P(s);
2633
0
      }
2634
6.87k
      if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2635
0
        continue;
2636
0
      }
2637
6.87k
      if (p->key) {
2638
1.26k
        t = _zend_hash_add_or_update_i(target, p->key, s, HASH_ADD | HASH_UPDATE_INDIRECT);
2639
1.26k
        if (t && pCopyConstructor) {
2640
488
          pCopyConstructor(t);
2641
488
        }
2642
5.61k
      } else {
2643
5.61k
        t = zend_hash_index_add(target, p->h, s);
2644
5.61k
        if (t && pCopyConstructor) {
2645
4.80k
          pCopyConstructor(t);
2646
4.80k
        }
2647
5.61k
      }
2648
6.87k
    }
2649
2.86k
  }
2650
4.63k
}
2651
2652
2653
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)
2654
0
{
2655
0
  zend_hash_key hash_key;
2656
2657
0
  hash_key.h = h;
2658
0
  hash_key.key = key;
2659
0
  return merge_checker_func(target, source_data, &hash_key, pParam);
2660
0
}
2661
2662
2663
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)
2664
0
{
2665
0
  uint32_t idx;
2666
0
  Bucket *p;
2667
0
  zval *t;
2668
2669
0
  IS_CONSISTENT(source);
2670
0
  IS_CONSISTENT(target);
2671
0
  HT_ASSERT_RC1(target);
2672
2673
0
  ZEND_ASSERT(!HT_IS_PACKED(source));
2674
0
  for (idx = 0; idx < source->nNumUsed; idx++) {
2675
0
    p = source->arData + idx;
2676
0
    if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2677
0
    if (zend_hash_replace_checker_wrapper(target, &p->val, p->h, p->key, pParam, pMergeSource)) {
2678
0
      t = zend_hash_update(target, p->key, &p->val);
2679
0
      if (pCopyConstructor) {
2680
0
        pCopyConstructor(t);
2681
0
      }
2682
0
    }
2683
0
  }
2684
0
}
2685
2686
2687
/* Returns the hash table data if found and NULL if not. */
2688
ZEND_API zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key)
2689
90.3M
{
2690
90.3M
  Bucket *p;
2691
2692
90.3M
  IS_CONSISTENT(ht);
2693
2694
90.3M
  (void)zend_string_hash_val(key);
2695
90.3M
  p = zend_hash_find_bucket(ht, key);
2696
90.3M
  return p ? &p->val : NULL;
2697
90.3M
}
2698
2699
ZEND_API zval* ZEND_FASTCALL zend_hash_find_known_hash(const HashTable *ht, const zend_string *key)
2700
1.47M
{
2701
1.47M
  Bucket *p;
2702
2703
1.47M
  IS_CONSISTENT(ht);
2704
2705
1.47M
  p = zend_hash_find_bucket(ht, key);
2706
1.47M
  return p ? &p->val : NULL;
2707
1.47M
}
2708
2709
ZEND_API zval* ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
2710
5.51M
{
2711
5.51M
  zend_ulong h;
2712
5.51M
  Bucket *p;
2713
2714
5.51M
  IS_CONSISTENT(ht);
2715
2716
5.51M
  h = zend_inline_hash_func(str, len);
2717
5.51M
  p = zend_hash_str_find_bucket(ht, str, len, h);
2718
5.51M
  return p ? &p->val : NULL;
2719
5.51M
}
2720
2721
ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h)
2722
893M
{
2723
893M
  Bucket *p;
2724
2725
893M
  IS_CONSISTENT(ht);
2726
2727
893M
  if (HT_IS_PACKED(ht)) {
2728
37.2k
    if (h < ht->nNumUsed) {
2729
29.9k
      zval *zv = ht->arPacked + h;
2730
2731
29.9k
      if (Z_TYPE_P(zv) != IS_UNDEF) {
2732
29.3k
        return zv;
2733
29.3k
      }
2734
29.9k
    }
2735
7.86k
    return NULL;
2736
37.2k
  }
2737
2738
893M
  p = zend_hash_index_find_bucket(ht, h);
2739
893M
  return p ? &p->val : NULL;
2740
893M
}
2741
2742
ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulong h)
2743
6.90k
{
2744
6.90k
  Bucket *p;
2745
2746
6.90k
  IS_CONSISTENT(ht);
2747
6.90k
  ZEND_ASSERT(!HT_IS_PACKED(ht));
2748
2749
6.90k
  p = zend_hash_index_find_bucket(ht, h);
2750
6.90k
  return p ? &p->val : NULL;
2751
6.90k
}
2752
2753
ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(const HashTable *ht, HashPosition *pos)
2754
3.89k
{
2755
3.89k
  IS_CONSISTENT(ht);
2756
3.89k
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2757
3.89k
  *pos = _zend_hash_get_valid_pos(ht, 0);
2758
3.89k
}
2759
2760
2761
/* This function will be extremely optimized by remembering
2762
 * the end of the list
2763
 */
2764
ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(const HashTable *ht, HashPosition *pos)
2765
52
{
2766
52
  uint32_t idx;
2767
2768
52
  IS_CONSISTENT(ht);
2769
52
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2770
2771
52
  idx = ht->nNumUsed;
2772
52
  if (HT_IS_PACKED(ht)) {
2773
16
    while (idx > 0) {
2774
16
      idx--;
2775
16
      if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) {
2776
16
        *pos = idx;
2777
16
        return;
2778
16
      }
2779
16
    }
2780
36
  } else {
2781
36
    while (idx > 0) {
2782
36
      idx--;
2783
36
      if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
2784
36
        *pos = idx;
2785
36
        return;
2786
36
      }
2787
36
    }
2788
36
  }
2789
0
  *pos = ht->nNumUsed;
2790
0
}
2791
2792
2793
ZEND_API zend_result ZEND_FASTCALL zend_hash_move_forward_ex(const HashTable *ht, HashPosition *pos)
2794
3.08k
{
2795
3.08k
  uint32_t idx;
2796
2797
3.08k
  IS_CONSISTENT(ht);
2798
3.08k
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2799
2800
3.08k
  idx = _zend_hash_get_valid_pos(ht, *pos);
2801
3.08k
  if (idx < ht->nNumUsed) {
2802
3.05k
    if (HT_IS_PACKED(ht)) {
2803
1.52k
      while (1) {
2804
1.52k
        idx++;
2805
1.52k
        if (idx >= ht->nNumUsed) {
2806
522
          *pos = ht->nNumUsed;
2807
522
          return SUCCESS;
2808
522
        }
2809
1.00k
        if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) {
2810
1.00k
          *pos = idx;
2811
1.00k
          return SUCCESS;
2812
1.00k
        }
2813
1.00k
      }
2814
1.53k
    } else {
2815
1.54k
      while (1) {
2816
1.54k
        idx++;
2817
1.54k
        if (idx >= ht->nNumUsed) {
2818
707
          *pos = ht->nNumUsed;
2819
707
          return SUCCESS;
2820
707
        }
2821
839
        if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
2822
823
          *pos = idx;
2823
823
          return SUCCESS;
2824
823
        }
2825
839
      }
2826
1.53k
    }
2827
3.05k
  } else {
2828
34
    return FAILURE;
2829
34
  }
2830
3.08k
}
2831
2832
ZEND_API zend_result ZEND_FASTCALL zend_hash_move_backwards_ex(const HashTable *ht, HashPosition *pos)
2833
43
{
2834
43
  uint32_t idx = *pos;
2835
2836
43
  IS_CONSISTENT(ht);
2837
43
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2838
2839
43
  if (idx < ht->nNumUsed) {
2840
43
    if (HT_IS_PACKED(ht)) {
2841
4
      while (idx > 0) {
2842
4
        idx--;
2843
4
        if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) {
2844
4
          *pos = idx;
2845
4
          return SUCCESS;
2846
4
        }
2847
4
      }
2848
39
    } else {
2849
39
      while (idx > 0) {
2850
13
        idx--;
2851
13
        if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
2852
13
          *pos = idx;
2853
13
          return SUCCESS;
2854
13
        }
2855
13
      }
2856
39
    }
2857
26
    *pos = ht->nNumUsed;
2858
26
    return SUCCESS;
2859
43
  } else {
2860
0
    return FAILURE;
2861
0
  }
2862
43
}
2863
2864
2865
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)
2866
1.14k
{
2867
1.14k
  uint32_t idx;
2868
1.14k
  Bucket *p;
2869
2870
1.14k
  IS_CONSISTENT(ht);
2871
1.14k
  idx = _zend_hash_get_valid_pos(ht, *pos);
2872
1.14k
  if (idx < ht->nNumUsed) {
2873
802
    if (HT_IS_PACKED(ht)) {
2874
0
      *num_index = idx;
2875
0
      return HASH_KEY_IS_LONG;
2876
0
    }
2877
802
    p = ht->arData + idx;
2878
802
    if (p->key) {
2879
592
      *str_index = p->key;
2880
592
      return HASH_KEY_IS_STRING;
2881
592
    } else {
2882
210
      *num_index = p->h;
2883
210
      return HASH_KEY_IS_LONG;
2884
210
    }
2885
802
  }
2886
339
  return HASH_KEY_NON_EXISTENT;
2887
1.14k
}
2888
2889
ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, const HashPosition *pos)
2890
1.34k
{
2891
1.34k
  uint32_t idx;
2892
1.34k
  Bucket *p;
2893
2894
1.34k
  IS_CONSISTENT(ht);
2895
1.34k
  idx = _zend_hash_get_valid_pos(ht, *pos);
2896
1.34k
  if (idx >= ht->nNumUsed) {
2897
0
    ZVAL_NULL(key);
2898
1.34k
  } else {
2899
1.34k
    if (HT_IS_PACKED(ht)) {
2900
561
      ZVAL_LONG(key, idx);
2901
561
      return;
2902
561
    }
2903
783
    p = ht->arData + idx;
2904
783
    if (p->key) {
2905
618
      ZVAL_STR_COPY(key, p->key);
2906
618
    } else {
2907
165
      ZVAL_LONG(key, p->h);
2908
165
    }
2909
783
  }
2910
1.34k
}
2911
2912
ZEND_API zend_hash_key_type ZEND_FASTCALL zend_hash_get_current_key_type_ex(const HashTable *ht, const HashPosition *pos)
2913
1.82k
{
2914
1.82k
  uint32_t idx;
2915
1.82k
  Bucket *p;
2916
2917
1.82k
  IS_CONSISTENT(ht);
2918
1.82k
  idx = _zend_hash_get_valid_pos(ht, *pos);
2919
1.82k
  if (idx < ht->nNumUsed) {
2920
1.15k
    if (HT_IS_PACKED(ht)) {
2921
451
      return HASH_KEY_IS_LONG;
2922
451
    }
2923
699
    p = ht->arData + idx;
2924
699
    if (p->key) {
2925
453
      return HASH_KEY_IS_STRING;
2926
453
    } else {
2927
246
      return HASH_KEY_IS_LONG;
2928
246
    }
2929
699
  }
2930
678
  return HASH_KEY_NON_EXISTENT;
2931
1.82k
}
2932
2933
2934
ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(const HashTable *ht, const HashPosition *pos)
2935
4.52k
{
2936
4.52k
  uint32_t idx;
2937
4.52k
  Bucket *p;
2938
2939
4.52k
  IS_CONSISTENT(ht);
2940
4.52k
  idx = _zend_hash_get_valid_pos(ht, *pos);
2941
4.52k
  if (idx < ht->nNumUsed) {
2942
4.05k
    if (HT_IS_PACKED(ht)) {
2943
985
      return &ht->arPacked[idx];
2944
985
    }
2945
3.06k
    p = ht->arData + idx;
2946
3.06k
    return &p->val;
2947
4.05k
  } else {
2948
471
    return NULL;
2949
471
  }
2950
4.52k
}
2951
2952
ZEND_API void zend_hash_bucket_swap(Bucket *p, Bucket *q)
2953
11.9k
{
2954
11.9k
  zval val;
2955
11.9k
  zend_ulong h;
2956
11.9k
  zend_string *key;
2957
2958
11.9k
  val = p->val;
2959
11.9k
  h = p->h;
2960
11.9k
  key = p->key;
2961
2962
11.9k
  p->val = q->val;
2963
11.9k
  p->h = q->h;
2964
11.9k
  p->key = q->key;
2965
2966
11.9k
  q->val = val;
2967
11.9k
  q->h = h;
2968
11.9k
  q->key = key;
2969
11.9k
}
2970
2971
ZEND_API void zend_hash_bucket_renum_swap(Bucket *p, Bucket *q)
2972
101k
{
2973
101k
  zval val;
2974
2975
101k
  val = p->val;
2976
101k
  p->val = q->val;
2977
101k
  q->val = val;
2978
101k
}
2979
2980
ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q)
2981
0
{
2982
0
  zval val;
2983
0
  zend_ulong h;
2984
2985
0
  val = p->val;
2986
0
  h = p->h;
2987
2988
0
  p->val = q->val;
2989
0
  p->h = q->h;
2990
2991
0
  q->val = val;
2992
0
  q->h = h;
2993
0
}
2994
2995
static void zend_hash_sort_internal(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
2996
1.64k
{
2997
1.64k
  Bucket *p;
2998
1.64k
  uint32_t i, j;
2999
3000
1.64k
  IS_CONSISTENT(ht);
3001
3002
1.64k
  if (ht->nNumOfElements <= 1) {
3003
25
    if (!renumber || ht->nNumOfElements == 0) {
3004
      /* Doesn't require sorting */
3005
23
      return;
3006
23
    }
3007
2
    if (sort == zend_sort && HT_IS_PACKED(ht) && HT_IS_WITHOUT_HOLES(ht)) {
3008
      /* The single element already has the expected index. */
3009
2
      ht->nInternalPointer = 0;
3010
2
      ht->nNextFreeElement = 1;
3011
2
      return;
3012
2
    }
3013
2
  }
3014
3015
1.61k
  if (HT_IS_PACKED(ht)) {
3016
0
    zend_hash_packed_to_hash(ht); // TODO: ???
3017
0
  }
3018
3019
1.61k
  if (HT_IS_WITHOUT_HOLES(ht)) {
3020
    /* Store original order of elements in extra space to allow stable sorting. */
3021
47.5k
    for (i = 0; i < ht->nNumUsed; i++) {
3022
45.8k
      Z_EXTRA(ht->arData[i].val) = i;
3023
45.8k
    }
3024
1.61k
  } else {
3025
    /* Remove holes and store original order. */
3026
0
    for (j = 0, i = 0; j < ht->nNumUsed; j++) {
3027
0
      p = ht->arData + j;
3028
0
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
3029
0
      if (i != j) {
3030
0
        ht->arData[i] = *p;
3031
0
      }
3032
0
      Z_EXTRA(ht->arData[i].val) = i;
3033
0
      i++;
3034
0
    }
3035
0
    ht->nNumUsed = i;
3036
0
  }
3037
3038
1.61k
  if (!HT_IS_PACKED(ht)) {
3039
    /* We broke the hash collisions chains overriding Z_NEXT() by Z_EXTRA().
3040
     * Reset the hash headers table as well to avoid possible inconsistent
3041
     * access on recursive data structures.
3042
       *
3043
       * See Zend/tests/bug63882_2.phpt
3044
     */
3045
1.61k
    HT_HASH_RESET(ht);
3046
1.61k
  }
3047
3048
1.61k
  sort((void *)ht->arData, ht->nNumUsed, sizeof(Bucket), (compare_func_t) compar,
3049
1.61k
      (swap_func_t)(renumber? zend_hash_bucket_renum_swap :
3050
1.61k
        (HT_IS_PACKED(ht) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap)));
3051
3052
1.61k
  ht->nInternalPointer = 0;
3053
3054
1.61k
  if (renumber) {
3055
36.9k
    for (j = 0; j < i; j++) {
3056
36.0k
      p = ht->arData + j;
3057
36.0k
      p->h = j;
3058
36.0k
      if (p->key) {
3059
856
        zend_string_release(p->key);
3060
856
        p->key = NULL;
3061
856
      }
3062
36.0k
    }
3063
3064
928
    ht->nNextFreeElement = i;
3065
928
  }
3066
1.61k
  if (HT_IS_PACKED(ht)) {
3067
0
    if (!renumber) {
3068
0
      zend_hash_packed_to_hash(ht);
3069
0
    }
3070
1.61k
  } else {
3071
1.61k
    if (renumber) {
3072
928
      void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
3073
928
      Bucket *old_buckets = ht->arData;
3074
928
      zval *zv;
3075
3076
928
      new_data = pemalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK), (GC_FLAGS(ht) & IS_ARRAY_PERSISTENT));
3077
928
      HT_FLAGS(ht) |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;
3078
928
      ht->nTableMask = HT_MIN_MASK;
3079
928
      HT_SET_DATA_ADDR(ht, new_data);
3080
928
      p = old_buckets;
3081
928
      zv = ht->arPacked;
3082
53.4k
      for (i = 0; i < ht->nTableSize; i++) {
3083
52.5k
        ZVAL_COPY_VALUE(zv, &p->val);
3084
52.5k
        zv++;
3085
52.5k
        p++;
3086
52.5k
      }
3087
928
      pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
3088
928
      HT_HASH_RESET_PACKED(ht);
3089
928
    } else {
3090
687
      zend_hash_rehash(ht);
3091
687
    }
3092
1.61k
  }
3093
1.61k
}
3094
3095
ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
3096
57
{
3097
57
  HT_ASSERT_RC1(ht);
3098
57
  zend_hash_sort_internal(ht, sort, compar, renumber);
3099
57
}
3100
3101
ZEND_API void ZEND_FASTCALL zend_array_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
3102
1.58k
{
3103
1.58k
  HT_ASSERT_RC1(ht);
3104
3105
  /* Unpack the array early to avoid RCn assertion failures. */
3106
1.58k
  if (HT_IS_PACKED(ht)) {
3107
853
    zend_hash_packed_to_hash(ht);
3108
853
  }
3109
3110
  /* Adding a refcount prevents the array from going away. */
3111
1.58k
  GC_ADDREF(ht);
3112
3113
1.58k
  zend_hash_sort_internal(ht, sort, compar, renumber);
3114
3115
1.58k
  if (UNEXPECTED(GC_DELREF(ht) == 0)) {
3116
12
    zend_array_destroy(ht);
3117
1.57k
  } else {
3118
1.57k
    gc_check_possible_root((zend_refcounted *)ht);
3119
1.57k
  }
3120
1.58k
}
3121
3122
9.27k
static zend_always_inline int zend_hash_compare_impl(const HashTable *ht1, const HashTable *ht2, compare_func_t compar, bool ordered) {
3123
9.27k
  uint32_t idx1, idx2;
3124
9.27k
  zend_string *key1, *key2;
3125
9.27k
  zend_ulong h1, h2;
3126
9.27k
  zval *pData1, *pData2;;
3127
9.27k
  int result;
3128
3129
9.27k
  if (ht1->nNumOfElements != ht2->nNumOfElements) {
3130
4.53k
    return ht1->nNumOfElements > ht2->nNumOfElements ? 1 : -1;
3131
4.53k
  }
3132
3133
11.9k
  for (idx1 = 0, idx2 = 0; idx1 < ht1->nNumUsed; idx1++) {
3134
10.8k
    if (HT_IS_PACKED(ht1)) {
3135
8.37k
      pData1 = ht1->arPacked + idx1;
3136
8.37k
      h1 = idx1;
3137
8.37k
      key1 = NULL;
3138
8.37k
    } else {
3139
2.44k
      Bucket *p = ht1->arData + idx1;
3140
2.44k
      pData1 = &p->val;
3141
2.44k
      h1 = p->h;
3142
2.44k
      key1 = p->key;
3143
2.44k
    }
3144
3145
10.8k
    if (Z_TYPE_P(pData1) == IS_UNDEF) continue;
3146
5.53k
    if (ordered) {
3147
2.77k
      if (HT_IS_PACKED(ht2)) {
3148
7.51k
        while (1) {
3149
7.51k
          ZEND_ASSERT(idx2 != ht2->nNumUsed);
3150
7.51k
          pData2 = ht2->arPacked + idx2;
3151
7.51k
          h2 = idx2;
3152
7.51k
          key2 = NULL;
3153
7.51k
          if (Z_TYPE_P(pData2) != IS_UNDEF) break;
3154
5.54k
          idx2++;
3155
5.54k
        }
3156
1.96k
      } else {
3157
809
        while (1) {
3158
809
          Bucket *p;
3159
809
          ZEND_ASSERT(idx2 != ht2->nNumUsed);
3160
809
          p = ht2->arData + idx2;
3161
809
          pData2 = &p->val;
3162
809
          h2 = p->h;
3163
809
          key2 = p->key;
3164
809
          if (Z_TYPE_P(pData2) != IS_UNDEF) break;
3165
0
          idx2++;
3166
0
        }
3167
809
      }
3168
2.77k
      if (key1 == NULL && key2 == NULL) { /* numeric indices */
3169
1.88k
        if (h1 != h2) {
3170
574
          return h1 > h2 ? 1 : -1;
3171
574
        }
3172
1.88k
      } else if (key1 != NULL && key2 != NULL) { /* string indices */
3173
601
        if (ZSTR_LEN(key1) != ZSTR_LEN(key2)) {
3174
107
          return ZSTR_LEN(key1) > ZSTR_LEN(key2) ? 1 : -1;
3175
107
        }
3176
3177
494
        result = memcmp(ZSTR_VAL(key1), ZSTR_VAL(key2), ZSTR_LEN(key1));
3178
494
        if (result != 0) {
3179
62
          return result;
3180
62
        }
3181
494
      } else {
3182
        /* Mixed key types: A string key is considered as larger */
3183
293
        return key1 != NULL ? 1 : -1;
3184
293
      }
3185
1.74k
      idx2++;
3186
2.75k
    } else {
3187
2.75k
      if (key1 == NULL) { /* numeric index */
3188
1.61k
        pData2 = zend_hash_index_find(ht2, h1);
3189
1.61k
        if (pData2 == NULL) {
3190
194
          return 1;
3191
194
        }
3192
1.61k
      } else { /* string index */
3193
1.14k
        pData2 = zend_hash_find(ht2, key1);
3194
1.14k
        if (pData2 == NULL) {
3195
175
          return 1;
3196
175
        }
3197
1.14k
      }
3198
2.75k
    }
3199
3200
4.12k
    if (Z_TYPE_P(pData1) == IS_INDIRECT) {
3201
43
      pData1 = Z_INDIRECT_P(pData1);
3202
43
    }
3203
4.12k
    if (Z_TYPE_P(pData2) == IS_INDIRECT) {
3204
43
      pData2 = Z_INDIRECT_P(pData2);
3205
43
    }
3206
3207
4.12k
    if (Z_TYPE_P(pData1) == IS_UNDEF) {
3208
15
      if (Z_TYPE_P(pData2) != IS_UNDEF) {
3209
0
        return -1;
3210
0
      }
3211
4.11k
    } else if (Z_TYPE_P(pData2) == IS_UNDEF) {
3212
0
      return 1;
3213
4.11k
    } else {
3214
4.11k
      result = compar(pData1, pData2);
3215
4.11k
      if (result != 0) {
3216
2.17k
        return result;
3217
2.17k
      }
3218
4.11k
    }
3219
4.12k
  }
3220
3221
1.16k
  return 0;
3222
4.74k
}
3223
3224
ZEND_API int zend_hash_compare(HashTable *ht1, const HashTable *ht2, compare_func_t compar, bool ordered)
3225
9.30k
{
3226
9.30k
  int result;
3227
9.30k
  IS_CONSISTENT(ht1);
3228
9.30k
  IS_CONSISTENT(ht2);
3229
3230
9.30k
  if (ht1 == ht2) {
3231
0
    return 0;
3232
0
  }
3233
3234
9.30k
#ifdef ZEND_CHECK_STACK_LIMIT
3235
9.30k
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
3236
0
    zend_throw_error(NULL, "Maximum call stack size reached during comparison");
3237
0
    return ZEND_UNCOMPARABLE;
3238
0
  }
3239
9.30k
#endif
3240
3241
  /* It's enough to protect only one of the arrays.
3242
   * The second one may be referenced from the first and this may cause
3243
   * false recursion detection.
3244
   */
3245
9.30k
  if (UNEXPECTED(GC_IS_RECURSIVE(ht1))) {
3246
22
    zend_throw_error(NULL, "Nesting level too deep - recursive dependency?");
3247
22
    return ZEND_UNCOMPARABLE;
3248
22
  }
3249
3250
9.27k
  GC_TRY_PROTECT_RECURSION(ht1);
3251
9.27k
  result = zend_hash_compare_impl(ht1, ht2, compar, ordered);
3252
9.27k
  GC_TRY_UNPROTECT_RECURSION(ht1);
3253
3254
9.27k
  return result;
3255
9.30k
}
3256
3257
3258
ZEND_API bool ZEND_FASTCALL _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx)
3259
205k
{
3260
205k
  const char *tmp = key;
3261
3262
205k
  const char *end = key + length;
3263
3264
205k
  if (*tmp == '-') {
3265
44.6k
    tmp++;
3266
44.6k
  }
3267
3268
205k
  if ((*tmp == '0' && length > 1) /* numbers with leading zeros */
3269
166k
   || (end - tmp > MAX_LENGTH_OF_LONG - 1) /* number too long */
3270
0
   || (SIZEOF_ZEND_LONG == 4 &&
3271
0
       end - tmp == MAX_LENGTH_OF_LONG - 1 &&
3272
49.7k
       *tmp > '2')) { /* overflow */
3273
49.7k
    return 0;
3274
49.7k
  }
3275
155k
  *idx = (*tmp - '0');
3276
402k
  while (1) {
3277
402k
    ++tmp;
3278
402k
    if (tmp == end) {
3279
109k
      if (*key == '-') {
3280
20.1k
        if (*idx-1 > ZEND_LONG_MAX) { /* overflow */
3281
1.17k
          return 0;
3282
1.17k
        }
3283
18.9k
        *idx = 0 - *idx;
3284
89.3k
      } else if (*idx > ZEND_LONG_MAX) { /* overflow */
3285
1.51k
        return 0;
3286
1.51k
      }
3287
106k
      return 1;
3288
109k
    }
3289
292k
    if (*tmp <= '9' && *tmp >= '0') {
3290
246k
      *idx = (*idx * 10) + (*tmp - '0');
3291
246k
    } else {
3292
45.9k
      return 0;
3293
45.9k
    }
3294
292k
  }
3295
155k
}
3296
3297
/* Takes a "symtable" hashtable (contains integer and non-numeric string keys)
3298
 * and converts it to a "proptable" (contains only string keys).
3299
 * If the symtable didn't need duplicating, its refcount is incremented.
3300
 */
3301
ZEND_API HashTable* ZEND_FASTCALL zend_symtable_to_proptable(HashTable *ht)
3302
511
{
3303
511
  zend_ulong num_key;
3304
511
  zend_string *str_key;
3305
511
  zval *zv;
3306
3307
511
  if (UNEXPECTED(HT_IS_PACKED(ht))) {
3308
57
    goto convert;
3309
57
  }
3310
3311
2.25k
  ZEND_HASH_MAP_FOREACH_STR_KEY(ht, str_key) {
3312
2.25k
    if (!str_key) {
3313
46
      goto convert;
3314
46
    }
3315
2.25k
  } ZEND_HASH_FOREACH_END();
3316
3317
408
  if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
3318
98
    GC_ADDREF(ht);
3319
98
  }
3320
3321
408
  return ht;
3322
3323
103
convert:
3324
103
  {
3325
103
    HashTable *new_ht = zend_new_array(zend_hash_num_elements(ht));
3326
3327
969
    ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) {
3328
969
      if (!str_key) {
3329
232
        str_key = zend_long_to_str(num_key);
3330
232
        zend_string_delref(str_key);
3331
232
      }
3332
969
      do {
3333
432
        if (Z_OPT_REFCOUNTED_P(zv)) {
3334
7
          if (Z_ISREF_P(zv) && Z_REFCOUNT_P(zv) == 1) {
3335
0
            zv = Z_REFVAL_P(zv);
3336
0
            if (!Z_OPT_REFCOUNTED_P(zv)) {
3337
0
              break;
3338
0
            }
3339
0
          }
3340
7
          Z_ADDREF_P(zv);
3341
7
        }
3342
432
      } while (0);
3343
969
      zend_hash_update(new_ht, str_key, zv);
3344
969
    } ZEND_HASH_FOREACH_END();
3345
3346
103
    return new_ht;
3347
454
  }
3348
454
}
3349
3350
/* Takes a "proptable" hashtable (contains only string keys) and converts it to
3351
 * a "symtable" (contains integer and non-numeric string keys).
3352
 * If the proptable didn't need duplicating, its refcount is incremented.
3353
 */
3354
ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, bool always_duplicate)
3355
2.74k
{
3356
2.74k
  zend_ulong num_key;
3357
2.74k
  zend_string *str_key;
3358
2.74k
  zval *zv;
3359
3360
2.74k
  if (!HT_IS_PACKED(ht)) {
3361
59.2k
    ZEND_HASH_MAP_FOREACH_STR_KEY(ht, str_key) {
3362
      /* The `str_key &&` here might seem redundant: property tables should
3363
       * only have string keys. Unfortunately, this isn't true, at the very
3364
       * least because of ArrayObject, which stores a symtable where the
3365
       * property table should be.
3366
       */
3367
59.2k
      if (str_key && ZEND_HANDLE_NUMERIC(str_key, num_key)) {
3368
145
        goto convert;
3369
145
      }
3370
59.2k
    } ZEND_HASH_FOREACH_END();
3371
2.74k
  }
3372
3373
2.60k
  if (always_duplicate) {
3374
2.59k
    return zend_array_dup(ht);
3375
2.59k
  }
3376
3377
9
  if (EXPECTED(!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE))) {
3378
9
    GC_ADDREF(ht);
3379
9
  }
3380
3381
9
  return ht;
3382
3383
145
convert:
3384
145
  {
3385
145
    HashTable *new_ht = zend_new_array(zend_hash_num_elements(ht));
3386
3387
4.19k
    ZEND_HASH_MAP_FOREACH_KEY_VAL_IND(ht, num_key, str_key, zv) {
3388
4.19k
      do {
3389
1.83k
        if (Z_OPT_REFCOUNTED_P(zv)) {
3390
861
          if (Z_ISREF_P(zv) && Z_REFCOUNT_P(zv) == 1) {
3391
35
            zv = Z_REFVAL_P(zv);
3392
35
            if (!Z_OPT_REFCOUNTED_P(zv)) {
3393
0
              break;
3394
0
            }
3395
35
          }
3396
861
          Z_ADDREF_P(zv);
3397
861
        }
3398
1.83k
      } while (0);
3399
      /* Again, thank ArrayObject for `!str_key ||`. */
3400
4.19k
      if (!str_key || ZEND_HANDLE_NUMERIC(str_key, num_key)) {
3401
476
        zend_hash_index_update(new_ht, num_key, zv);
3402
1.35k
      } else {
3403
1.35k
        zend_hash_update(new_ht, str_key, zv);
3404
1.35k
      }
3405
4.19k
    } ZEND_HASH_FOREACH_END();
3406
3407
145
    return new_ht;
3408
145
  }
3409
145
}