Coverage Report

Created: 2026-06-02 06:36

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