Coverage Report

Created: 2025-07-23 06:33

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