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
8.88M
  ZEND_ASSERT((expr) || (HT_FLAGS(ht) & HASH_FLAG_ALLOW_COW_VIOLATION))
38
#else
39
# define HT_ASSERT(ht, expr)
40
#endif
41
42
8.78M
#define HT_ASSERT_RC1(ht) HT_ASSERT(ht, GC_REFCOUNT(ht) == 1)
43
44
0
#define HT_POISONED_PTR ((HashTable *) (intptr_t) -1)
45
46
#if ZEND_DEBUG
47
48
8.92M
#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
8.92M
{
55
8.92M
  if ((HT_FLAGS(ht) & HASH_FLAG_CONSISTENCY) == HT_OK) {
56
8.92M
    return;
57
8.92M
  }
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
8.92M
#define IS_CONSISTENT(a) _zend_is_inconsistent(a, __FILE__, __LINE__);
75
57.4k
#define SET_INCONSISTENT(n) do { \
76
57.4k
    HT_FLAGS(ht) = (HT_FLAGS(ht) & ~HASH_FLAG_CONSISTENCY) | (n); \
77
57.4k
  } 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
55.2k
  if ((ht)->nNumUsed >= (ht)->nTableSize) {   \
85
1.39k
    zend_hash_do_resize(ht);          \
86
1.39k
  }
87
88
2
ZEND_API void *zend_hash_str_find_ptr_lc(const HashTable *ht, const char *str, size_t len) {
89
2
  void *result;
90
2
  char *lc_str;
91
92
  /* Stack allocate small strings to improve performance */
93
2
  ALLOCA_FLAG(use_heap)
94
95
2
  lc_str = zend_str_tolower_copy(do_alloca(len + 1, use_heap), str, len);
96
2
  result = zend_hash_str_find_ptr(ht, lc_str, len);
97
2
  free_alloca(lc_str, use_heap);
98
99
2
  return result;
100
2
}
101
102
0
ZEND_API void *zend_hash_find_ptr_lc(const HashTable *ht, zend_string *key) {
103
0
  void *result;
104
0
  zend_string *lc_key = zend_string_tolower(key);
105
0
  result = zend_hash_find_ptr(ht, lc_key);
106
0
  zend_string_release(lc_key);
107
0
  return result;
108
0
}
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
111k
{
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
111k
  if (nSize <= HT_MIN_SIZE) {
121
97.7k
    return HT_MIN_SIZE;
122
97.7k
  } 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
13.7k
  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
111k
}
146
147
static zend_always_inline void zend_hash_real_init_packed_ex(HashTable *ht)
148
9.06k
{
149
9.06k
  void *data;
150
151
9.06k
  if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {
152
144
    data = pemalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK), 1);
153
8.92k
  } else if (EXPECTED(ht->nTableSize == HT_MIN_SIZE)) {
154
    /* Use specialized API with constant allocation amount for a particularly common case. */
155
8.92k
    data = emalloc(HT_PACKED_SIZE_EX(HT_MIN_SIZE, HT_MIN_MASK));
156
8.92k
  } else {
157
0
    data = emalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK));
158
0
  }
159
9.06k
  HT_SET_DATA_ADDR(ht, data);
160
  /* Don't overwrite iterator count. */
161
9.06k
  ht->u.v.flags = HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;
162
9.06k
  HT_HASH_RESET_PACKED(ht);
163
9.06k
}
164
165
static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht)
166
20.4k
{
167
20.4k
  void *data;
168
20.4k
  uint32_t nSize = ht->nTableSize;
169
170
20.4k
  ZEND_ASSERT(HT_SIZE_TO_MASK(nSize) != 0);
171
172
20.4k
  if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {
173
644
    data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), 1);
174
19.7k
  } else if (EXPECTED(nSize == HT_MIN_SIZE)) {
175
13.0k
    data = emalloc(HT_SIZE_EX(HT_MIN_SIZE, HT_SIZE_TO_MASK(HT_MIN_SIZE)));
176
13.0k
    ht->nTableMask = HT_SIZE_TO_MASK(HT_MIN_SIZE);
177
13.0k
    HT_SET_DATA_ADDR(ht, data);
178
    /* Don't overwrite iterator count. */
179
13.0k
    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
13.0k
    do {
189
13.0k
      __m128i xmm0 = _mm_setzero_si128();
190
13.0k
      xmm0 = _mm_cmpeq_epi8(xmm0, xmm0);
191
13.0k
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  0), xmm0);
192
13.0k
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  4), xmm0);
193
13.0k
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  8), xmm0);
194
13.0k
      _mm_storeu_si128((__m128i*)&HT_HASH_EX(data, 12), xmm0);
195
13.0k
    } 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
13.0k
    return;
223
13.0k
  } else {
224
6.77k
    data = emalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)));
225
6.77k
  }
226
7.42k
  ht->nTableMask = HT_SIZE_TO_MASK(nSize);
227
7.42k
  HT_SET_DATA_ADDR(ht, data);
228
7.42k
  HT_FLAGS(ht) = HASH_FLAG_STATIC_KEYS;
229
7.42k
  HT_HASH_RESET(ht);
230
7.42k
}
231
232
static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, bool packed)
233
214
{
234
214
  HT_ASSERT_RC1(ht);
235
214
  ZEND_ASSERT(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED);
236
214
  if (packed) {
237
0
    zend_hash_real_init_packed_ex(ht);
238
214
  } else {
239
214
    zend_hash_real_init_mixed_ex(ht);
240
214
  }
241
214
}
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
111k
{
262
111k
  GC_SET_REFCOUNT(ht, 1);
263
111k
  GC_TYPE_INFO(ht) = GC_ARRAY | (persistent ? ((GC_PERSISTENT|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT) : 0);
264
111k
  HT_FLAGS(ht) = HASH_FLAG_UNINITIALIZED;
265
111k
  ht->nTableMask = HT_MIN_MASK;
266
111k
  HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
267
111k
  ht->nNumUsed = 0;
268
111k
  ht->nNumOfElements = 0;
269
111k
  ht->nInternalPointer = 0;
270
111k
  ht->nNextFreeElement = ZEND_LONG_MIN;
271
111k
  ht->pDestructor = pDestructor;
272
111k
  ht->nTableSize = zend_hash_check_size(nSize);
273
111k
}
274
275
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent)
276
62.2k
{
277
62.2k
  _zend_hash_init_int(ht, nSize, pDestructor, persistent);
278
62.2k
}
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
49.0k
{
289
49.0k
  HashTable *ht = emalloc(sizeof(HashTable));
290
49.0k
  _zend_hash_init_int(ht, nSize, ZVAL_PTR_DTOR, false);
291
49.0k
  return ht;
292
49.0k
}
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
2.74k
{
311
2.74k
  HT_ASSERT_RC1(ht);
312
2.74k
  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
2.74k
  uint32_t newTableSize = ht->nTableSize * 2;
316
2.74k
  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
2.74k
  ht->nTableSize = newTableSize;
318
2.74k
}
319
320
ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, bool packed)
321
214
{
322
214
  IS_CONSISTENT(ht);
323
324
214
  HT_ASSERT_RC1(ht);
325
214
  zend_hash_real_init_ex(ht, packed);
326
214
}
327
328
ZEND_API void ZEND_FASTCALL zend_hash_real_init_packed(HashTable *ht)
329
0
{
330
0
  IS_CONSISTENT(ht);
331
332
0
  HT_ASSERT_RC1(ht);
333
0
  zend_hash_real_init_packed_ex(ht);
334
0
}
335
336
ZEND_API void ZEND_FASTCALL zend_hash_real_init_mixed(HashTable *ht)
337
20.2k
{
338
20.2k
  IS_CONSISTENT(ht);
339
340
20.2k
  HT_ASSERT_RC1(ht);
341
20.2k
  zend_hash_real_init_mixed_ex(ht);
342
20.2k
}
343
344
ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht)
345
1.67k
{
346
1.67k
  void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
347
1.67k
  zval *src = ht->arPacked;
348
1.67k
  Bucket *dst;
349
1.67k
  uint32_t i;
350
1.67k
  uint32_t nSize = ht->nTableSize;
351
352
1.67k
  ZEND_ASSERT(HT_SIZE_TO_MASK(nSize) != 0);
353
354
1.67k
  HT_ASSERT_RC1(ht);
355
  // Alloc before assign to avoid inconsistencies on OOM
356
1.67k
  new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
357
1.67k
  HT_FLAGS(ht) &= ~HASH_FLAG_PACKED;
358
1.67k
  ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
359
1.67k
  HT_SET_DATA_ADDR(ht, new_data);
360
1.67k
  dst = ht->arData;
361
8.38k
  for (i = 0; i < ht->nNumUsed; i++) {
362
6.71k
    ZVAL_COPY_VALUE(&dst->val, src);
363
6.71k
    dst->h = i;
364
6.71k
    dst->key = NULL;
365
6.71k
    dst++;
366
6.71k
    src++;
367
6.71k
  }
368
1.67k
  pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
369
1.67k
  zend_hash_rehash(ht);
370
1.67k
}
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
490
{
396
490
  HT_ASSERT_RC1(ht);
397
398
490
  if (nSize == 0) return;
399
400
314
  ZEND_ASSERT(HT_SIZE_TO_MASK(nSize) != 0);
401
402
314
  if (UNEXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
403
214
    if (nSize > ht->nTableSize) {
404
88
      ht->nTableSize = zend_hash_check_size(nSize);
405
88
    }
406
214
    zend_hash_real_init(ht, packed);
407
214
  } else {
408
100
    if (packed) {
409
0
      ZEND_ASSERT(HT_IS_PACKED(ht));
410
0
      if (nSize > ht->nTableSize) {
411
0
        uint32_t newTableSize = zend_hash_check_size(nSize);
412
0
        HT_SET_DATA_ADDR(ht, perealloc2(HT_GET_DATA_ADDR(ht), HT_PACKED_SIZE_EX(newTableSize, HT_MIN_MASK), HT_PACKED_USED_SIZE(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT));
413
0
        ht->nTableSize = newTableSize;
414
0
      }
415
100
    } else {
416
100
      ZEND_ASSERT(!HT_IS_PACKED(ht));
417
100
      if (nSize > ht->nTableSize) {
418
68
        void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
419
68
        Bucket *old_buckets = ht->arData;
420
68
        nSize = zend_hash_check_size(nSize);
421
68
        new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
422
68
        ht->nTableSize = nSize;
423
68
        ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
424
68
        HT_SET_DATA_ADDR(ht, new_data);
425
68
        memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
426
68
        pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
427
68
        zend_hash_rehash(ht);
428
68
      }
429
100
    }
430
100
  }
431
314
}
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
0
{
460
0
  zval *val;
461
0
  uint32_t num = ht->nNumOfElements;
462
463
0
  ZEND_HASH_MAP_FOREACH_VAL(ht, val) {
464
0
    if (Z_TYPE_P(val) == IS_INDIRECT) {
465
0
      if (UNEXPECTED(Z_TYPE_P(Z_INDIRECT_P(val)) == IS_UNDEF)) {
466
0
        num--;
467
0
      }
468
0
    }
469
0
  } ZEND_HASH_FOREACH_END();
470
0
  return num;
471
0
}
472
/* }}} */
473
474
ZEND_API uint32_t zend_array_count(HashTable *ht)
475
0
{
476
0
  uint32_t num;
477
0
  if (UNEXPECTED(HT_FLAGS(ht) & HASH_FLAG_HAS_EMPTY_IND)) {
478
0
    num = zend_array_recalc_elements(ht);
479
0
    if (UNEXPECTED(ht->nNumOfElements == num)) {
480
0
      HT_FLAGS(ht) &= ~HASH_FLAG_HAS_EMPTY_IND;
481
0
    }
482
0
  } else if (UNEXPECTED(ht == &EG(symbol_table))) {
483
0
    num = zend_array_recalc_elements(ht);
484
0
  } else {
485
0
    num = zend_hash_num_elements(ht);
486
0
  }
487
0
  return num;
488
0
}
489
/* }}} */
490
491
static zend_always_inline HashPosition _zend_hash_get_valid_pos(const HashTable *ht, HashPosition pos)
492
0
{
493
0
  if (HT_IS_PACKED(ht)) {
494
0
    while (pos < ht->nNumUsed && Z_ISUNDEF(ht->arPacked[pos])) {
495
0
      pos++;
496
0
    }
497
0
  } else {
498
0
    while (pos < ht->nNumUsed && Z_ISUNDEF(ht->arData[pos].val)) {
499
0
      pos++;
500
0
    }
501
0
  }
502
0
  return pos;
503
0
}
504
505
static zend_always_inline HashPosition _zend_hash_get_current_pos(const HashTable *ht)
506
0
{
507
0
  return _zend_hash_get_valid_pos(ht, ht->nInternalPointer);
508
0
}
509
510
ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos(const HashTable *ht)
511
0
{
512
0
  return _zend_hash_get_current_pos(ht);
513
0
}
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
0
static void zend_hash_remove_iterator_copies(uint32_t idx) {
521
0
  HashTableIterator *iterators = EG(ht_iterators);
522
523
0
  HashTableIterator *iter = iterators + idx;
524
0
  uint32_t next_idx = iter->next_copy;
525
0
  while (next_idx != idx) {
526
0
    uint32_t cur_idx = next_idx;
527
0
    HashTableIterator *cur_iter = iterators + cur_idx;
528
0
    next_idx = cur_iter->next_copy;
529
0
    cur_iter->next_copy = cur_idx; // avoid recursion in zend_hash_iterator_del
530
0
    zend_hash_iterator_del(cur_idx);
531
0
  }
532
0
  iter->next_copy = idx;
533
0
}
534
535
ZEND_API uint32_t ZEND_FASTCALL zend_hash_iterator_add(HashTable *ht, HashPosition pos)
536
0
{
537
0
  HashTableIterator *iter = EG(ht_iterators);
538
0
  HashTableIterator *end  = iter + EG(ht_iterators_count);
539
0
  uint32_t idx;
540
541
0
  if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
542
0
    HT_INC_ITERATORS_COUNT(ht);
543
0
  }
544
0
  while (iter != end) {
545
0
    if (iter->ht == NULL) {
546
0
      iter->ht = ht;
547
0
      iter->pos = pos;
548
0
      idx = iter - EG(ht_iterators);
549
0
      iter->next_copy = idx;
550
0
      if (idx + 1 > EG(ht_iterators_used)) {
551
0
        EG(ht_iterators_used) = idx + 1;
552
0
      }
553
0
      return idx;
554
0
    }
555
0
    iter++;
556
0
  }
557
0
  if (EG(ht_iterators) == EG(ht_iterators_slots)) {
558
0
    EG(ht_iterators) = emalloc(sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
559
0
    memcpy(EG(ht_iterators), EG(ht_iterators_slots), sizeof(HashTableIterator) * EG(ht_iterators_count));
560
0
  } else {
561
0
    EG(ht_iterators) = erealloc(EG(ht_iterators), sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
562
0
  }
563
0
  iter = EG(ht_iterators) + EG(ht_iterators_count);
564
0
  EG(ht_iterators_count) += 8;
565
0
  iter->ht = ht;
566
0
  iter->pos = pos;
567
0
  memset(iter + 1, 0, sizeof(HashTableIterator) * 7);
568
0
  idx = iter - EG(ht_iterators);
569
0
  iter->next_copy = idx;
570
0
  EG(ht_iterators_used) = idx + 1;
571
0
  return idx;
572
0
}
573
574
// To avoid losing track of the HashTable when separating arrays, we track all copies at once.
575
0
static zend_always_inline bool zend_hash_iterator_find_copy_pos(uint32_t idx, HashTable *ht) {
576
0
  HashTableIterator *iter = EG(ht_iterators) + idx;
577
578
0
  uint32_t next_idx = iter->next_copy;
579
0
  if (EXPECTED(next_idx != idx)) {
580
0
    HashTableIterator *copy_iter;
581
0
    while (next_idx != idx) {
582
0
      copy_iter = EG(ht_iterators) + next_idx;
583
0
      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
0
        if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR)
587
0
          && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) {
588
0
          HT_DEC_ITERATORS_COUNT(iter->ht);
589
0
        }
590
0
        if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
591
0
          HT_INC_ITERATORS_COUNT(ht);
592
0
        }
593
0
        iter->ht = copy_iter->ht;
594
0
        iter->pos = copy_iter->pos;
595
0
        zend_hash_remove_iterator_copies(idx);
596
0
        return true;
597
0
      }
598
0
      next_idx = copy_iter->next_copy;
599
0
    }
600
0
    zend_hash_remove_iterator_copies(idx);
601
0
  }
602
603
0
  return false;
604
0
}
605
606
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos(uint32_t idx, HashTable *ht)
607
0
{
608
0
  HashTableIterator *iter = EG(ht_iterators) + idx;
609
610
0
  ZEND_ASSERT(idx != (uint32_t)-1);
611
0
  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
0
  return iter->pos;
623
0
}
624
625
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos_ex(uint32_t idx, zval *array)
626
0
{
627
0
  HashTable *ht = Z_ARRVAL_P(array);
628
0
  HashTableIterator *iter = EG(ht_iterators) + idx;
629
630
0
  ZEND_ASSERT(idx != (uint32_t)-1);
631
0
  if (UNEXPECTED(iter->ht != ht) && !zend_hash_iterator_find_copy_pos(idx, ht)) {
632
0
    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
0
    if (UNEXPECTED(GC_REFCOUNT(ht) > 1)) {
639
0
      ZVAL_ARR(array, zend_array_dup(ht));
640
0
      GC_TRY_DELREF(ht);
641
0
      iter = EG(ht_iterators) + idx;
642
0
      ht = Z_ARRVAL_P(array);
643
0
    }
644
645
0
    if (EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) {
646
0
      HT_INC_ITERATORS_COUNT(ht);
647
0
    }
648
0
    iter->ht = ht;
649
0
    iter->pos = _zend_hash_get_current_pos(ht);
650
0
  }
651
0
  return iter->pos;
652
0
}
653
654
ZEND_API void ZEND_FASTCALL zend_hash_iterator_del(uint32_t idx)
655
0
{
656
0
  HashTableIterator *iter = EG(ht_iterators) + idx;
657
658
0
  ZEND_ASSERT(idx != (uint32_t)-1);
659
660
0
  if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR)
661
0
      && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) {
662
0
    ZEND_ASSERT(HT_ITERATORS_COUNT(iter->ht) != 0);
663
0
    HT_DEC_ITERATORS_COUNT(iter->ht);
664
0
  }
665
0
  iter->ht = NULL;
666
667
0
  if (UNEXPECTED(iter->next_copy != idx)) {
668
0
    zend_hash_remove_iterator_copies(idx);
669
0
  }
670
671
0
  if (idx == EG(ht_iterators_used) - 1) {
672
0
    while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) {
673
0
      idx--;
674
0
    }
675
0
    EG(ht_iterators_used) = idx;
676
0
  }
677
0
}
678
679
static zend_never_inline void ZEND_FASTCALL _zend_hash_iterators_remove(const HashTable *ht)
680
0
{
681
0
  HashTableIterator *iter = EG(ht_iterators);
682
0
  const HashTableIterator *end = iter + EG(ht_iterators_used);
683
684
0
  while (iter != end) {
685
0
    if (iter->ht == ht) {
686
0
      iter->ht = HT_POISONED_PTR;
687
0
    }
688
0
    iter++;
689
0
  }
690
0
}
691
692
static zend_always_inline void zend_hash_iterators_remove(const HashTable *ht)
693
49.0k
{
694
49.0k
  if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
695
0
    _zend_hash_iterators_remove(ht);
696
0
  }
697
49.0k
}
698
699
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(const HashTable *ht, HashPosition start)
700
0
{
701
0
  const HashTableIterator *iter = EG(ht_iterators);
702
0
  const HashTableIterator *end = iter + EG(ht_iterators_used);
703
0
  HashPosition res = ht->nNumUsed;
704
705
0
  while (iter != end) {
706
0
    if (iter->ht == ht) {
707
0
      if (iter->pos >= start && iter->pos < res) {
708
0
        res = iter->pos;
709
0
      }
710
0
    }
711
0
    iter++;
712
0
  }
713
0
  return res;
714
0
}
715
716
ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(const HashTable *ht, HashPosition from, HashPosition to)
717
0
{
718
0
  HashTableIterator *iter = EG(ht_iterators);
719
0
  const HashTableIterator *end = iter + EG(ht_iterators_used);
720
721
0
  while (iter != end) {
722
0
    if (iter->ht == ht && iter->pos == from) {
723
0
      iter->pos = to;
724
0
    }
725
0
    iter++;
726
0
  }
727
0
}
728
729
ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(const HashTable *ht, HashPosition step)
730
0
{
731
0
  HashTableIterator *iter = EG(ht_iterators);
732
0
  const HashTableIterator *end = iter + EG(ht_iterators_used);
733
734
0
  while (iter != end) {
735
0
    if (iter->ht == ht) {
736
0
      iter->pos += step;
737
0
    }
738
0
    iter++;
739
0
  }
740
0
}
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
84.4k
{
745
84.4k
  uint32_t nIndex;
746
84.4k
  uint32_t idx;
747
84.4k
  Bucket *p, *arData;
748
749
84.4k
  ZEND_ASSERT(ZSTR_H(key) != 0 && "Hash must be known");
750
751
84.4k
  arData = ht->arData;
752
84.4k
  nIndex = ZSTR_H(key) | ht->nTableMask;
753
84.4k
  idx = HT_HASH_EX(arData, nIndex);
754
755
84.4k
  if (UNEXPECTED(idx == HT_INVALID_IDX)) {
756
54.8k
    return NULL;
757
54.8k
  }
758
29.6k
  p = HT_HASH_TO_BUCKET_EX(arData, idx);
759
29.6k
  if (EXPECTED(p->key == key)) { /* check for the same interned string */
760
9.76k
    return p;
761
9.76k
  }
762
763
27.1k
  while (1) {
764
27.1k
    if (p->h == ZSTR_H(key) &&
765
10.5k
        EXPECTED(p->key) &&
766
10.1k
        zend_string_equal_content(p->key, key)) {
767
8.35k
      return p;
768
8.35k
    }
769
18.7k
    idx = Z_NEXT(p->val);
770
18.7k
    if (idx == HT_INVALID_IDX) {
771
10.7k
      return NULL;
772
10.7k
    }
773
8.01k
    p = HT_HASH_TO_BUCKET_EX(arData, idx);
774
8.01k
    if (p->key == key) { /* check for the same interned string */
775
769
      return p;
776
769
    }
777
8.01k
  }
778
19.8k
}
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
7.31k
{
782
7.31k
  uint32_t nIndex;
783
7.31k
  uint32_t idx;
784
7.31k
  Bucket *p, *arData;
785
786
7.31k
  arData = ht->arData;
787
7.31k
  nIndex = h | ht->nTableMask;
788
7.31k
  idx = HT_HASH_EX(arData, nIndex);
789
7.43k
  while (idx != HT_INVALID_IDX) {
790
7.39k
    ZEND_ASSERT(idx < HT_IDX_TO_HASH(ht->nTableSize));
791
7.39k
    p = HT_HASH_TO_BUCKET_EX(arData, idx);
792
7.39k
    if ((p->h == h)
793
7.27k
       && p->key
794
7.27k
       && zend_string_equals_cstr(p->key, str, len)) {
795
7.27k
      return p;
796
7.27k
    }
797
116
    idx = Z_NEXT(p->val);
798
116
  }
799
40
  return NULL;
800
7.31k
}
801
802
static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *ht, zend_ulong h)
803
4.63k
{
804
4.63k
  uint32_t nIndex;
805
4.63k
  uint32_t idx;
806
4.63k
  Bucket *p, *arData;
807
808
4.63k
  arData = ht->arData;
809
4.63k
  nIndex = h | ht->nTableMask;
810
4.63k
  idx = HT_HASH_EX(arData, nIndex);
811
6.63k
  while (idx != HT_INVALID_IDX) {
812
2.90k
    ZEND_ASSERT(idx < HT_IDX_TO_HASH(ht->nTableSize));
813
2.90k
    p = HT_HASH_TO_BUCKET_EX(arData, idx);
814
2.90k
    if (p->h == h && !p->key) {
815
910
      return p;
816
910
    }
817
1.99k
    idx = Z_NEXT(p->val);
818
1.99k
  }
819
3.72k
  return NULL;
820
4.63k
}
821
822
static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_string *key, zval *pData, uint32_t flag)
823
79.0k
{
824
79.0k
  zend_ulong h;
825
79.0k
  uint32_t nIndex;
826
79.0k
  uint32_t idx;
827
79.0k
  Bucket *p, *arData;
828
829
79.0k
  IS_CONSISTENT(ht);
830
79.0k
  HT_ASSERT_RC1(ht);
831
79.0k
  zend_string_hash_val(key);
832
833
79.0k
  if (UNEXPECTED(HT_FLAGS(ht) & (HASH_FLAG_UNINITIALIZED|HASH_FLAG_PACKED))) {
834
19.6k
    if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
835
19.1k
      zend_hash_real_init_mixed(ht);
836
19.1k
      goto add_to_hash;
837
19.1k
    } else {
838
456
      zend_hash_packed_to_hash(ht);
839
456
    }
840
59.4k
  } else if ((flag & HASH_ADD_NEW) == 0 || ZEND_DEBUG) {
841
59.4k
    p = zend_hash_find_bucket(ht, key);
842
843
59.4k
    if (p) {
844
8.36k
      zval *data;
845
846
8.36k
      ZEND_ASSERT((flag & HASH_ADD_NEW) == 0);
847
8.36k
      if (flag & HASH_LOOKUP) {
848
0
        return &p->val;
849
8.36k
      } else if (flag & HASH_ADD) {
850
0
        if (!(flag & HASH_UPDATE_INDIRECT)) {
851
0
          return NULL;
852
0
        }
853
0
        ZEND_ASSERT(&p->val != pData);
854
0
        data = &p->val;
855
0
        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
0
        } else {
861
0
          return NULL;
862
0
        }
863
8.36k
      } else {
864
8.36k
        ZEND_ASSERT(&p->val != pData);
865
8.36k
        data = &p->val;
866
8.36k
        if ((flag & HASH_UPDATE_INDIRECT) && Z_TYPE_P(data) == IS_INDIRECT) {
867
0
          data = Z_INDIRECT_P(data);
868
0
        }
869
8.36k
      }
870
8.36k
      if (ht->pDestructor) {
871
8.36k
        ht->pDestructor(data);
872
8.36k
      }
873
8.36k
      ZVAL_COPY_VALUE(data, pData);
874
8.36k
      return data;
875
8.36k
    }
876
59.4k
  }
877
878
51.5k
  ZEND_HASH_IF_FULL_DO_RESIZE(ht);   /* If the Hash table is full, resize it */
879
880
70.7k
add_to_hash:
881
70.7k
  if (!ZSTR_IS_INTERNED(key)) {
882
20.4k
    zend_string_addref(key);
883
20.4k
    HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS;
884
20.4k
  }
885
70.7k
  idx = ht->nNumUsed++;
886
70.7k
  ht->nNumOfElements++;
887
70.7k
  arData = ht->arData;
888
70.7k
  p = arData + idx;
889
70.7k
  p->key = key;
890
70.7k
  p->h = h = ZSTR_H(key);
891
70.7k
  nIndex = h | ht->nTableMask;
892
70.7k
  Z_NEXT(p->val) = HT_HASH_EX(arData, nIndex);
893
70.7k
  HT_HASH_EX(arData, nIndex) = HT_IDX_TO_HASH(idx);
894
70.7k
  if (flag & HASH_LOOKUP) {
895
0
    ZVAL_NULL(&p->val);
896
70.7k
  } else {
897
70.7k
    ZVAL_COPY_VALUE(&p->val, pData);
898
70.7k
  }
899
900
70.7k
  return &p->val;
901
51.5k
}
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
0
{
905
0
  zend_string *key;
906
0
  uint32_t nIndex;
907
0
  uint32_t idx;
908
0
  Bucket *p;
909
910
0
  IS_CONSISTENT(ht);
911
0
  HT_ASSERT_RC1(ht);
912
913
0
  if (UNEXPECTED(HT_FLAGS(ht) & (HASH_FLAG_UNINITIALIZED|HASH_FLAG_PACKED))) {
914
0
    if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
915
0
      zend_hash_real_init_mixed(ht);
916
0
      goto add_to_hash;
917
0
    } else {
918
0
      zend_hash_packed_to_hash(ht);
919
0
    }
920
0
  } else if ((flag & HASH_ADD_NEW) == 0) {
921
0
    p = zend_hash_str_find_bucket(ht, str, len, h);
922
923
0
    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
0
  }
956
957
0
  ZEND_HASH_IF_FULL_DO_RESIZE(ht);   /* If the Hash table is full, resize it */
958
959
0
add_to_hash:
960
0
  idx = ht->nNumUsed++;
961
0
  ht->nNumOfElements++;
962
0
  p = ht->arData + idx;
963
0
  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
0
  p->h = ZSTR_H(key) = h;
970
0
  HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS;
971
0
  if (flag & HASH_LOOKUP) {
972
0
    ZVAL_NULL(&p->val);
973
0
  } else {
974
0
    ZVAL_COPY_VALUE(&p->val, pData);
975
0
  }
976
0
  nIndex = h | ht->nTableMask;
977
0
  Z_NEXT(p->val) = HT_HASH(ht, nIndex);
978
0
  HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
979
980
0
  return &p->val;
981
0
}
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
6.64k
{
999
6.64k
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD);
1000
6.64k
}
1001
1002
ZEND_API zval* ZEND_FASTCALL zend_hash_update(HashTable *ht, zend_string *key, zval *pData)
1003
46.6k
{
1004
46.6k
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE);
1005
46.6k
}
1006
1007
ZEND_API zval* ZEND_FASTCALL zend_hash_update_ind(HashTable *ht, zend_string *key, zval *pData)
1008
0
{
1009
0
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE | HASH_UPDATE_INDIRECT);
1010
0
}
1011
1012
ZEND_API zval* ZEND_FASTCALL zend_hash_add_new(HashTable *ht, zend_string *key, zval *pData)
1013
25.8k
{
1014
25.8k
  return _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD_NEW);
1015
25.8k
}
1016
1017
ZEND_API zval* ZEND_FASTCALL zend_hash_lookup(HashTable *ht, zend_string *key)
1018
0
{
1019
0
  return _zend_hash_add_or_update_i(ht, key, NULL, HASH_LOOKUP);
1020
0
}
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
0
{
1038
0
  zend_ulong h = zend_hash_func(str, len);
1039
1040
0
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_UPDATE);
1041
0
}
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
0
{
1052
0
  zend_ulong h = zend_hash_func(str, len);
1053
1054
0
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_ADD);
1055
0
}
1056
1057
ZEND_API zval* ZEND_FASTCALL zend_hash_str_add_new(HashTable *ht, const char *str, size_t len, zval *pData)
1058
0
{
1059
0
  zend_ulong h = zend_hash_func(str, len);
1060
1061
0
  return _zend_hash_str_add_or_update_i(ht, str, len, h, pData, HASH_ADD_NEW);
1062
0
}
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
0
{
1073
0
  zval dummy;
1074
1075
0
  ZVAL_NULL(&dummy);
1076
0
  return zend_hash_index_add(ht, h, &dummy);
1077
0
}
1078
1079
ZEND_API zval* ZEND_FASTCALL zend_hash_add_empty_element(HashTable *ht, zend_string *key)
1080
0
{
1081
0
  zval dummy;
1082
1083
0
  ZVAL_NULL(&dummy);
1084
0
  return zend_hash_add(ht, key, &dummy);
1085
0
}
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
8.66M
{
1097
8.66M
  uint32_t nIndex;
1098
8.66M
  uint32_t idx;
1099
8.66M
  Bucket *p;
1100
8.66M
  zval *zv;
1101
1102
8.66M
  IS_CONSISTENT(ht);
1103
8.66M
  HT_ASSERT_RC1(ht);
1104
1105
8.66M
  if ((flag & HASH_ADD_NEXT) && h == ZEND_LONG_MIN) {
1106
7.09k
    h = 0;
1107
7.09k
  }
1108
1109
8.66M
  if (HT_IS_PACKED(ht)) {
1110
8.64M
    if ((flag & (HASH_ADD_NEW|HASH_ADD_NEXT)) != (HASH_ADD_NEW|HASH_ADD_NEXT)
1111
8.64M
     && h < ht->nNumUsed) {
1112
719
      zv = ht->arPacked + h;
1113
719
      if (flag & HASH_ADD_NEW) {
1114
0
        ZEND_ASSERT(Z_TYPE_P(zv) == IS_UNDEF);
1115
0
        goto convert_to_hash;
1116
719
      } else if (Z_TYPE_P(zv) != IS_UNDEF) {
1117
513
        if (flag & HASH_LOOKUP) {
1118
0
          return zv;
1119
0
        }
1120
1.42k
replace:
1121
1.42k
        if (flag & HASH_ADD) {
1122
0
          return NULL;
1123
0
        }
1124
1.42k
        if (ht->pDestructor) {
1125
1.42k
          ht->pDestructor(zv);
1126
1.42k
        }
1127
1.42k
        ZVAL_COPY_VALUE(zv, pData);
1128
1.42k
        return zv;
1129
1.42k
      } else { /* we have to keep the order :( */
1130
206
        goto convert_to_hash;
1131
206
      }
1132
8.64M
    } else if (EXPECTED(h < ht->nTableSize)) {
1133
8.65M
add_to_packed:
1134
8.65M
      zv = ht->arPacked + h;
1135
      /* incremental initialization of empty Buckets */
1136
8.65M
      if ((flag & (HASH_ADD_NEW|HASH_ADD_NEXT)) != (HASH_ADD_NEW|HASH_ADD_NEXT)) {
1137
8.65M
        if (h > ht->nNumUsed) {
1138
1.26k
          zval *q = ht->arPacked + ht->nNumUsed;
1139
7.04k
          while (q != zv) {
1140
5.78k
            ZVAL_UNDEF(q);
1141
5.78k
            q++;
1142
5.78k
          }
1143
1.26k
        }
1144
8.65M
      }
1145
8.65M
      ht->nNextFreeElement = ht->nNumUsed = h + 1;
1146
8.65M
      ht->nNumOfElements++;
1147
8.65M
      if (flag & HASH_LOOKUP) {
1148
0
        ZVAL_NULL(zv);
1149
8.65M
      } else {
1150
8.65M
        ZVAL_COPY_VALUE(zv, pData);
1151
8.65M
      }
1152
1153
8.65M
      return zv;
1154
8.64M
    } else if ((h >> 1) < ht->nTableSize &&
1155
3.04k
               (ht->nTableSize >> 1) < ht->nNumOfElements) {
1156
2.74k
      zend_hash_packed_grow(ht);
1157
2.74k
      goto add_to_packed;
1158
2.74k
    } else {
1159
1.01k
      if (ht->nNumUsed >= ht->nTableSize) {
1160
274
        ht->nTableSize += ht->nTableSize;
1161
274
      }
1162
1.21k
convert_to_hash:
1163
1.21k
      zend_hash_packed_to_hash(ht);
1164
1.21k
    }
1165
8.64M
  } else if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
1166
10.1k
    if (h < ht->nTableSize) {
1167
9.06k
      zend_hash_real_init_packed_ex(ht);
1168
9.06k
      goto add_to_packed;
1169
9.06k
    }
1170
1.04k
    zend_hash_real_init_mixed(ht);
1171
4.63k
  } else {
1172
4.63k
    if ((flag & HASH_ADD_NEW) == 0 || ZEND_DEBUG) {
1173
4.63k
      p = zend_hash_index_find_bucket(ht, h);
1174
4.63k
      if (p) {
1175
910
        if (flag & HASH_LOOKUP) {
1176
0
          return &p->val;
1177
0
        }
1178
910
        ZEND_ASSERT((flag & HASH_ADD_NEW) == 0);
1179
910
        zv = &p->val;
1180
910
        goto replace;
1181
910
      }
1182
4.63k
    }
1183
3.72k
    ZEND_HASH_IF_FULL_DO_RESIZE(ht);   /* If the Hash table is full, resize it */
1184
3.72k
  }
1185
1186
5.99k
  idx = ht->nNumUsed++;
1187
5.99k
  nIndex = h | ht->nTableMask;
1188
5.99k
  p = ht->arData + idx;
1189
5.99k
  Z_NEXT(p->val) = HT_HASH(ht, nIndex);
1190
5.99k
  HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
1191
5.99k
  if ((zend_long)h >= ht->nNextFreeElement) {
1192
3.06k
    ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX;
1193
3.06k
  }
1194
5.99k
  ht->nNumOfElements++;
1195
5.99k
  p->h = h;
1196
5.99k
  p->key = NULL;
1197
5.99k
  if (flag & HASH_LOOKUP) {
1198
0
    ZVAL_NULL(&p->val);
1199
5.99k
  } else {
1200
5.99k
    ZVAL_COPY_VALUE(&p->val, pData);
1201
5.99k
  }
1202
1203
5.99k
  return &p->val;
1204
8.66M
}
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
0
{
1226
0
  return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_ADD);
1227
0
}
1228
1229
ZEND_API zval* ZEND_FASTCALL zend_hash_index_add_new(HashTable *ht, zend_ulong h, zval *pData)
1230
0
{
1231
0
  return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_ADD | HASH_ADD_NEW);
1232
0
}
1233
1234
ZEND_API zval* ZEND_FASTCALL zend_hash_index_update(HashTable *ht, zend_ulong h, zval *pData)
1235
9.68k
{
1236
9.68k
  return _zend_hash_index_add_or_update_i(ht, h, pData, HASH_UPDATE);
1237
9.68k
}
1238
1239
ZEND_API zval* ZEND_FASTCALL zend_hash_next_index_insert(HashTable *ht, zval *pData)
1240
8.65M
{
1241
8.65M
  return _zend_hash_index_add_or_update_i(ht, ht->nNextFreeElement, pData, HASH_ADD | HASH_ADD_NEXT);
1242
8.65M
}
1243
1244
ZEND_API zval* ZEND_FASTCALL zend_hash_next_index_insert_new(HashTable *ht, zval *pData)
1245
0
{
1246
0
  return _zend_hash_index_add_or_update_i(ht, ht->nNextFreeElement, pData, HASH_ADD | HASH_ADD_NEW | HASH_ADD_NEXT);
1247
0
}
1248
1249
ZEND_API zval* ZEND_FASTCALL zend_hash_index_lookup(HashTable *ht, zend_ulong h)
1250
0
{
1251
0
  return _zend_hash_index_add_or_update_i(ht, h, NULL, HASH_LOOKUP);
1252
0
}
1253
1254
ZEND_API zval* ZEND_FASTCALL zend_hash_set_bucket_key(HashTable *ht, Bucket *b, zend_string *key)
1255
0
{
1256
0
  uint32_t nIndex;
1257
0
  uint32_t idx, i;
1258
0
  Bucket *p, *arData;
1259
1260
0
  IS_CONSISTENT(ht);
1261
0
  HT_ASSERT_RC1(ht);
1262
0
  ZEND_ASSERT(!HT_IS_PACKED(ht));
1263
1264
0
  (void)zend_string_hash_val(key);
1265
0
  p = zend_hash_find_bucket(ht, key);
1266
0
  if (UNEXPECTED(p)) {
1267
0
    return (p == b) ? &p->val : NULL;
1268
0
  }
1269
1270
0
  if (!ZSTR_IS_INTERNED(key)) {
1271
0
    zend_string_addref(key);
1272
0
    HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS;
1273
0
  }
1274
1275
0
  arData = ht->arData;
1276
1277
  /* del from hash */
1278
0
  idx = HT_IDX_TO_HASH(b - arData);
1279
0
  nIndex = b->h | ht->nTableMask;
1280
0
  i = HT_HASH_EX(arData, nIndex);
1281
0
  if (i == idx) {
1282
0
    HT_HASH_EX(arData, nIndex) = Z_NEXT(b->val);
1283
0
  } 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
0
  zend_string_release(b->key);
1292
1293
  /* add to hash */
1294
0
  idx = b - arData;
1295
0
  b->key = key;
1296
0
  b->h = ZSTR_H(key);
1297
0
  nIndex = b->h | ht->nTableMask;
1298
0
  idx = HT_IDX_TO_HASH(idx);
1299
0
  i = HT_HASH_EX(arData, nIndex);
1300
0
  if (i == HT_INVALID_IDX || i < idx) {
1301
0
    Z_NEXT(b->val) = i;
1302
0
    HT_HASH_EX(arData, nIndex) = idx;
1303
0
  } 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
0
  return &b->val;
1313
0
}
1314
1315
static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht)
1316
1.39k
{
1317
1318
1.39k
  IS_CONSISTENT(ht);
1319
1.39k
  HT_ASSERT_RC1(ht);
1320
1321
1.39k
  ZEND_ASSERT(!HT_IS_PACKED(ht));
1322
1.39k
  if (ht->nNumUsed > ht->nNumOfElements + (ht->nNumOfElements >> 5)) { /* additional term is there to amortize the cost of compaction */
1323
0
    zend_hash_rehash(ht);
1324
1.39k
  } else if (ht->nTableSize < HT_MAX_SIZE) { /* Let's double the table size */
1325
1.39k
    void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
1326
1.39k
    uint32_t nSize = ht->nTableSize + ht->nTableSize;
1327
1.39k
    Bucket *old_buckets = ht->arData;
1328
1329
1.39k
    ZEND_ASSERT(HT_SIZE_TO_MASK(nSize) != 0);
1330
1331
1.39k
    new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
1332
1.39k
    ht->nTableSize = nSize;
1333
1.39k
    ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);
1334
1.39k
    HT_SET_DATA_ADDR(ht, new_data);
1335
1.39k
    memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed);
1336
1.39k
    pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
1337
1.39k
    zend_hash_rehash(ht);
1338
1.39k
  } 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
1.39k
}
1342
1343
ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
1344
3.13k
{
1345
3.13k
  Bucket *p;
1346
3.13k
  uint32_t nIndex, i;
1347
1348
3.13k
  IS_CONSISTENT(ht);
1349
1350
3.13k
  if (UNEXPECTED(ht->nNumOfElements == 0)) {
1351
0
    if (!(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
1352
0
      ht->nNumUsed = 0;
1353
0
      HT_HASH_RESET(ht);
1354
      /* Even if the array is empty, we still need to reset the iterator positions. */
1355
0
      ht->nInternalPointer = 0;
1356
0
      if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
1357
0
        HashTableIterator *iter = EG(ht_iterators);
1358
0
        HashTableIterator *end  = iter + EG(ht_iterators_used);
1359
0
        while (iter != end) {
1360
0
          if (iter->ht == ht) {
1361
0
            iter->pos = 0;
1362
0
          }
1363
0
          iter++;
1364
0
        }
1365
0
      }
1366
0
    }
1367
0
    return;
1368
0
  }
1369
1370
3.13k
  HT_HASH_RESET(ht);
1371
3.13k
  i = 0;
1372
3.13k
  p = ht->arData;
1373
3.13k
  if (HT_IS_WITHOUT_HOLES(ht)) {
1374
24.8k
    do {
1375
24.8k
      nIndex = p->h | ht->nTableMask;
1376
24.8k
      Z_NEXT(p->val) = HT_HASH(ht, nIndex);
1377
24.8k
      HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(i);
1378
24.8k
      p++;
1379
24.8k
    } while (++i < ht->nNumUsed);
1380
2.15k
  } else {
1381
979
    uint32_t old_num_used = ht->nNumUsed;
1382
1.25k
    do {
1383
1.25k
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) {
1384
979
        uint32_t j = i;
1385
979
        Bucket *q = p;
1386
1387
979
        if (EXPECTED(!HT_HAS_ITERATORS(ht))) {
1388
7.16k
          while (++i < ht->nNumUsed) {
1389
6.18k
            p++;
1390
6.18k
            if (EXPECTED(Z_TYPE_INFO(p->val) != IS_UNDEF)) {
1391
2.31k
              ZVAL_COPY_VALUE(&q->val, &p->val);
1392
2.31k
              q->h = p->h;
1393
2.31k
              nIndex = q->h | ht->nTableMask;
1394
2.31k
              q->key = p->key;
1395
2.31k
              Z_NEXT(q->val) = HT_HASH(ht, nIndex);
1396
2.31k
              HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j);
1397
2.31k
              if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) {
1398
0
                ht->nInternalPointer = j;
1399
0
              }
1400
2.31k
              q++;
1401
2.31k
              j++;
1402
2.31k
            }
1403
6.18k
          }
1404
979
        } else {
1405
0
          uint32_t iter_pos = zend_hash_iterators_lower_pos(ht, i + 1);
1406
1407
0
          while (++i < ht->nNumUsed) {
1408
0
            p++;
1409
0
            if (EXPECTED(Z_TYPE_INFO(p->val) != IS_UNDEF)) {
1410
0
              ZVAL_COPY_VALUE(&q->val, &p->val);
1411
0
              q->h = p->h;
1412
0
              nIndex = q->h | ht->nTableMask;
1413
0
              q->key = p->key;
1414
0
              Z_NEXT(q->val) = HT_HASH(ht, nIndex);
1415
0
              HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j);
1416
0
              if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) {
1417
0
                ht->nInternalPointer = j;
1418
0
              }
1419
0
              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
0
              q++;
1426
0
              j++;
1427
0
            }
1428
0
          }
1429
0
        }
1430
979
        ht->nNumUsed = j;
1431
979
        break;
1432
979
      }
1433
277
      nIndex = p->h | ht->nTableMask;
1434
277
      Z_NEXT(p->val) = HT_HASH(ht, nIndex);
1435
277
      HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(i);
1436
277
      p++;
1437
277
    } 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
979
    if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
1442
0
      _zend_hash_iterators_update(ht, old_num_used, ht->nNumUsed);
1443
0
    }
1444
979
  }
1445
3.13k
}
1446
1447
static zend_always_inline void zend_hash_iterators_clamp_max(const HashTable *ht, uint32_t max)
1448
27.1k
{
1449
27.1k
  if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
1450
0
    HashTableIterator *iter = EG(ht_iterators);
1451
0
    const HashTableIterator *end = iter + EG(ht_iterators_used);
1452
0
    while (iter != end) {
1453
0
      if (iter->ht == ht) {
1454
0
        iter->pos = MIN(iter->pos, max);
1455
0
      }
1456
0
      iter++;
1457
0
    }
1458
0
  }
1459
27.1k
}
1460
1461
static zend_always_inline void _zend_hash_packed_del_val(HashTable *ht, uint32_t idx, zval *zv)
1462
0
{
1463
0
  idx = HT_HASH_TO_IDX(idx);
1464
0
  ht->nNumOfElements--;
1465
0
  if (ht->nNumUsed - 1 == idx) {
1466
0
    do {
1467
0
      ht->nNumUsed--;
1468
0
    } while (ht->nNumUsed > 0 && (UNEXPECTED(Z_TYPE(ht->arPacked[ht->nNumUsed-1]) == IS_UNDEF)));
1469
0
    ht->nInternalPointer = MIN(ht->nInternalPointer, ht->nNumUsed);
1470
0
    zend_hash_iterators_clamp_max(ht, ht->nNumUsed);
1471
0
  }
1472
0
  if (ht->pDestructor) {
1473
0
    zval tmp;
1474
0
    ZVAL_COPY_VALUE(&tmp, zv);
1475
0
    ZVAL_UNDEF(zv);
1476
0
    ht->pDestructor(&tmp);
1477
0
  } else {
1478
0
    ZVAL_UNDEF(zv);
1479
0
  }
1480
0
}
1481
1482
static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, Bucket *p, Bucket *prev)
1483
27.1k
{
1484
27.1k
  if (prev) {
1485
6
    Z_NEXT(prev->val) = Z_NEXT(p->val);
1486
27.1k
  } else {
1487
27.1k
    HT_HASH(ht, p->h | ht->nTableMask) = Z_NEXT(p->val);
1488
27.1k
  }
1489
27.1k
  idx = HT_HASH_TO_IDX(idx);
1490
27.1k
  ht->nNumOfElements--;
1491
27.1k
  if (ht->nNumUsed - 1 == idx) {
1492
27.1k
    do {
1493
27.1k
      ht->nNumUsed--;
1494
27.1k
    } while (ht->nNumUsed > 0 && (UNEXPECTED(Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF)));
1495
27.1k
    ht->nInternalPointer = MIN(ht->nInternalPointer, ht->nNumUsed);
1496
27.1k
    zend_hash_iterators_clamp_max(ht, ht->nNumUsed);
1497
27.1k
  }
1498
27.1k
  if (ht->pDestructor) {
1499
27.1k
    zval tmp;
1500
27.1k
    ZVAL_COPY_VALUE(&tmp, &p->val);
1501
27.1k
    ZVAL_UNDEF(&p->val);
1502
27.1k
    ht->pDestructor(&tmp);
1503
27.1k
  } else {
1504
0
    ZVAL_UNDEF(&p->val);
1505
0
  }
1506
27.1k
}
1507
1508
static zend_always_inline void _zend_hash_del_el(HashTable *ht, uint32_t idx, Bucket *p)
1509
27.1k
{
1510
27.1k
  Bucket *prev = NULL;
1511
27.1k
  uint32_t nIndex;
1512
27.1k
  uint32_t i;
1513
1514
27.1k
  nIndex = p->h | ht->nTableMask;
1515
27.1k
  i = HT_HASH(ht, nIndex);
1516
1517
27.1k
  if (i != idx) {
1518
0
    prev = HT_HASH_TO_BUCKET(ht, i);
1519
0
    while (Z_NEXT(prev->val) != idx) {
1520
0
      i = Z_NEXT(prev->val);
1521
0
      prev = HT_HASH_TO_BUCKET(ht, i);
1522
0
    }
1523
0
  }
1524
1525
27.1k
  if (p->key) {
1526
27.1k
    zend_string_release(p->key);
1527
27.1k
    p->key = NULL;
1528
27.1k
  }
1529
27.1k
  _zend_hash_del_el_ex(ht, idx, p, prev);
1530
27.1k
}
1531
1532
ZEND_API void ZEND_FASTCALL zend_hash_packed_del_val(HashTable *ht, zval *zv)
1533
0
{
1534
0
  IS_CONSISTENT(ht);
1535
0
  HT_ASSERT_RC1(ht);
1536
0
  ZEND_ASSERT(HT_IS_PACKED(ht));
1537
0
  _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(zv - ht->arPacked), zv);
1538
0
}
1539
1540
1541
ZEND_API void ZEND_FASTCALL zend_hash_del_bucket(HashTable *ht, Bucket *p)
1542
0
{
1543
0
  IS_CONSISTENT(ht);
1544
0
  HT_ASSERT_RC1(ht);
1545
0
  ZEND_ASSERT(!HT_IS_PACKED(ht));
1546
0
  _zend_hash_del_el(ht, HT_IDX_TO_HASH(p - ht->arData), p);
1547
0
}
1548
1549
ZEND_API zend_result ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key)
1550
0
{
1551
0
  zend_ulong h;
1552
0
  uint32_t nIndex;
1553
0
  uint32_t idx;
1554
0
  Bucket *p;
1555
0
  Bucket *prev = NULL;
1556
1557
0
  IS_CONSISTENT(ht);
1558
0
  HT_ASSERT_RC1(ht);
1559
1560
0
  h = zend_string_hash_val(key);
1561
0
  nIndex = h | ht->nTableMask;
1562
1563
0
  idx = HT_HASH(ht, nIndex);
1564
0
  while (idx != HT_INVALID_IDX) {
1565
0
    p = HT_HASH_TO_BUCKET(ht, idx);
1566
0
    if ((p->key == key) ||
1567
0
      (p->h == h &&
1568
0
         p->key &&
1569
0
         zend_string_equal_content(p->key, key))) {
1570
0
      zend_string_release(p->key);
1571
0
      p->key = NULL;
1572
0
      _zend_hash_del_el_ex(ht, idx, p, prev);
1573
0
      return SUCCESS;
1574
0
    }
1575
0
    prev = p;
1576
0
    idx = Z_NEXT(p->val);
1577
0
  }
1578
0
  return FAILURE;
1579
0
}
1580
1581
ZEND_API zend_result ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key)
1582
0
{
1583
0
  zend_ulong h;
1584
0
  uint32_t nIndex;
1585
0
  uint32_t idx;
1586
0
  Bucket *p;
1587
0
  Bucket *prev = NULL;
1588
1589
0
  IS_CONSISTENT(ht);
1590
0
  HT_ASSERT_RC1(ht);
1591
1592
0
  h = zend_string_hash_val(key);
1593
0
  nIndex = h | ht->nTableMask;
1594
1595
0
  idx = HT_HASH(ht, nIndex);
1596
0
  while (idx != HT_INVALID_IDX) {
1597
0
    p = HT_HASH_TO_BUCKET(ht, idx);
1598
0
    if ((p->key == key) ||
1599
0
      (p->h == h &&
1600
0
         p->key &&
1601
0
         zend_string_equal_content(p->key, key))) {
1602
0
      if (Z_TYPE(p->val) == IS_INDIRECT) {
1603
0
        zval *data = Z_INDIRECT(p->val);
1604
1605
0
        if (UNEXPECTED(Z_TYPE_P(data) == IS_UNDEF)) {
1606
0
          return FAILURE;
1607
0
        } else {
1608
0
          if (ht->pDestructor) {
1609
0
            zval tmp;
1610
0
            ZVAL_COPY_VALUE(&tmp, data);
1611
0
            ZVAL_UNDEF(data);
1612
0
            ht->pDestructor(&tmp);
1613
0
          } else {
1614
0
            ZVAL_UNDEF(data);
1615
0
          }
1616
0
          HT_FLAGS(ht) |= HASH_FLAG_HAS_EMPTY_IND;
1617
0
        }
1618
0
      } else {
1619
0
        zend_string_release(p->key);
1620
0
        p->key = NULL;
1621
0
        _zend_hash_del_el_ex(ht, idx, p, prev);
1622
0
      }
1623
0
      return SUCCESS;
1624
0
    }
1625
0
    prev = p;
1626
0
    idx = Z_NEXT(p->val);
1627
0
  }
1628
0
  return FAILURE;
1629
0
}
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
72
{
1678
72
  zend_ulong h;
1679
72
  uint32_t nIndex;
1680
72
  uint32_t idx;
1681
72
  Bucket *p;
1682
72
  Bucket *prev = NULL;
1683
1684
72
  IS_CONSISTENT(ht);
1685
72
  HT_ASSERT_RC1(ht);
1686
1687
72
  h = zend_inline_hash_func(str, len);
1688
72
  nIndex = h | ht->nTableMask;
1689
1690
72
  idx = HT_HASH(ht, nIndex);
1691
90
  while (idx != HT_INVALID_IDX) {
1692
82
    p = HT_HASH_TO_BUCKET(ht, idx);
1693
82
    if ((p->h == h)
1694
64
       && p->key
1695
64
       && zend_string_equals_cstr(p->key, str, len)) {
1696
64
      zend_string_release(p->key);
1697
64
      p->key = NULL;
1698
64
      _zend_hash_del_el_ex(ht, idx, p, prev);
1699
64
      return SUCCESS;
1700
64
    }
1701
18
    prev = p;
1702
18
    idx = Z_NEXT(p->val);
1703
18
  }
1704
8
  return FAILURE;
1705
72
}
1706
1707
ZEND_API zend_result ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h)
1708
0
{
1709
0
  uint32_t nIndex;
1710
0
  uint32_t idx;
1711
0
  Bucket *p;
1712
0
  Bucket *prev = NULL;
1713
1714
0
  IS_CONSISTENT(ht);
1715
0
  HT_ASSERT_RC1(ht);
1716
1717
0
  if (HT_IS_PACKED(ht)) {
1718
0
    if (h < ht->nNumUsed) {
1719
0
      zval *zv = ht->arPacked + h;
1720
0
      if (Z_TYPE_P(zv) != IS_UNDEF) {
1721
0
        _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(h), zv);
1722
0
        return SUCCESS;
1723
0
      }
1724
0
    }
1725
0
    return FAILURE;
1726
0
  }
1727
0
  nIndex = h | ht->nTableMask;
1728
1729
0
  idx = HT_HASH(ht, nIndex);
1730
0
  while (idx != HT_INVALID_IDX) {
1731
0
    p = HT_HASH_TO_BUCKET(ht, idx);
1732
0
    if ((p->h == h) && (p->key == NULL)) {
1733
0
      _zend_hash_del_el_ex(ht, idx, p, prev);
1734
0
      return SUCCESS;
1735
0
    }
1736
0
    prev = p;
1737
0
    idx = Z_NEXT(p->val);
1738
0
  }
1739
0
  return FAILURE;
1740
0
}
1741
1742
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
1743
47.4k
{
1744
47.4k
  IS_CONSISTENT(ht);
1745
47.4k
  HT_ASSERT(ht, GC_REFCOUNT(ht) <= 1);
1746
1747
47.4k
  if (ht->nNumUsed) {
1748
2
    if (HT_IS_PACKED(ht)) {
1749
2
      if (ht->pDestructor) {
1750
2
        zval *zv = ht->arPacked;
1751
2
        zval *end = zv + ht->nNumUsed;
1752
1753
2
        SET_INCONSISTENT(HT_IS_DESTROYING);
1754
2
        if (HT_IS_WITHOUT_HOLES(ht)) {
1755
2
          do {
1756
2
            ht->pDestructor(zv);
1757
2
          } while (++zv != end);
1758
2
        } else {
1759
0
          do {
1760
0
            if (EXPECTED(Z_TYPE_P(zv) != IS_UNDEF)) {
1761
0
              ht->pDestructor(zv);
1762
0
            }
1763
0
          } while (++zv != end);
1764
0
        }
1765
2
        SET_INCONSISTENT(HT_DESTROYED);
1766
2
      }
1767
2
      zend_hash_iterators_remove(ht);
1768
2
    } else {
1769
0
      Bucket *p = ht->arData;
1770
0
      Bucket *end = p + ht->nNumUsed;
1771
1772
0
      if (ht->pDestructor) {
1773
0
        SET_INCONSISTENT(HT_IS_DESTROYING);
1774
1775
0
        if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1776
0
          if (HT_IS_WITHOUT_HOLES(ht)) {
1777
0
            do {
1778
0
              ht->pDestructor(&p->val);
1779
0
            } while (++p != end);
1780
0
          } 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
0
        } else if (HT_IS_WITHOUT_HOLES(ht)) {
1788
0
          do {
1789
0
            ht->pDestructor(&p->val);
1790
0
            if (EXPECTED(p->key)) {
1791
0
              zend_string_release(p->key);
1792
0
            }
1793
0
          } while (++p != end);
1794
0
        } 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
0
        SET_INCONSISTENT(HT_DESTROYED);
1806
0
      } else {
1807
0
        if (!HT_HAS_STATIC_KEYS_ONLY(ht)) {
1808
0
          do {
1809
0
            if (EXPECTED(p->key)) {
1810
0
              zend_string_release(p->key);
1811
0
            }
1812
0
          } while (++p != end);
1813
0
        }
1814
0
      }
1815
0
      zend_hash_iterators_remove(ht);
1816
0
    }
1817
47.4k
  } else if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
1818
47.4k
    return;
1819
47.4k
  }
1820
2
  pefree(HT_GET_DATA_ADDR(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
1821
2
}
1822
1823
ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht)
1824
49.0k
{
1825
49.0k
  IS_CONSISTENT(ht);
1826
49.0k
  HT_ASSERT(ht, GC_REFCOUNT(ht) <= 1);
1827
1828
  /* break possible cycles */
1829
49.0k
  GC_REMOVE_FROM_BUFFER(ht);
1830
49.0k
  GC_TYPE_INFO(ht) = GC_NULL /*???| (GC_WHITE << 16)*/;
1831
1832
49.0k
  if (ht->nNumUsed) {
1833
    /* In some rare cases destructors of regular arrays may be changed */
1834
21.9k
    if (UNEXPECTED(ht->pDestructor != ZVAL_PTR_DTOR)) {
1835
0
      zend_hash_destroy(ht);
1836
0
      goto free_ht;
1837
0
    }
1838
1839
21.9k
    SET_INCONSISTENT(HT_IS_DESTROYING);
1840
1841
21.9k
    if (HT_IS_PACKED(ht)) {
1842
7.25k
      zval *zv = ht->arPacked;
1843
7.25k
      zval *end = zv + ht->nNumUsed;
1844
1845
8.65M
      do {
1846
8.65M
        i_zval_ptr_dtor(zv);
1847
8.65M
      } while (++zv != end);
1848
14.6k
    } else {
1849
14.6k
      Bucket *p = ht->arData;
1850
14.6k
      Bucket *end = p + ht->nNumUsed;
1851
1852
14.6k
      if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1853
10.5k
        do {
1854
10.5k
          i_zval_ptr_dtor(&p->val);
1855
10.5k
        } while (++p != end);
1856
7.49k
      } else if (HT_IS_WITHOUT_HOLES(ht)) {
1857
25.8k
        do {
1858
25.8k
          i_zval_ptr_dtor(&p->val);
1859
25.8k
          if (EXPECTED(p->key)) {
1860
22.9k
            zend_string_release_ex(p->key, 0);
1861
22.9k
          }
1862
25.8k
        } while (++p != end);
1863
7.19k
      } 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
14.6k
    }
1874
27.1k
  } else if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
1875
27.1k
    goto free_ht;
1876
27.1k
  }
1877
21.9k
  SET_INCONSISTENT(HT_DESTROYED);
1878
21.9k
  efree(HT_GET_DATA_ADDR(ht));
1879
49.0k
free_ht:
1880
49.0k
  zend_hash_iterators_remove(ht);
1881
49.0k
  FREE_HASHTABLE(ht);
1882
49.0k
}
1883
1884
ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht)
1885
6.78k
{
1886
6.78k
  IS_CONSISTENT(ht);
1887
6.78k
  HT_ASSERT_RC1(ht);
1888
1889
6.78k
  if (ht->nNumUsed) {
1890
0
    if (HT_IS_PACKED(ht)) {
1891
0
      zval *zv = ht->arPacked;
1892
0
      zval *end = zv + ht->nNumUsed;
1893
1894
0
      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
0
    } else {
1910
0
      Bucket *p = ht->arData;
1911
0
      Bucket *end = p + ht->nNumUsed;
1912
1913
0
      if (ht->pDestructor) {
1914
0
        if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1915
0
          if (HT_IS_WITHOUT_HOLES(ht)) {
1916
0
            do {
1917
0
              ht->pDestructor(&p->val);
1918
0
            } while (++p != end);
1919
0
          } 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
0
        } 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
0
      } else {
1944
0
        if (!HT_HAS_STATIC_KEYS_ONLY(ht)) {
1945
0
          do {
1946
0
            if (EXPECTED(p->key)) {
1947
0
              zend_string_release(p->key);
1948
0
            }
1949
0
          } while (++p != end);
1950
0
        }
1951
0
      }
1952
0
      HT_HASH_RESET(ht);
1953
0
    }
1954
0
  }
1955
6.78k
  ht->nNumUsed = 0;
1956
6.78k
  ht->nNumOfElements = 0;
1957
6.78k
  ht->nNextFreeElement = ZEND_LONG_MIN;
1958
6.78k
  ht->nInternalPointer = 0;
1959
6.78k
}
1960
1961
ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht)
1962
0
{
1963
0
  Bucket *p, *end;
1964
1965
0
  IS_CONSISTENT(ht);
1966
0
  HT_ASSERT_RC1(ht);
1967
1968
0
  if (ht->nNumUsed) {
1969
0
    ZEND_ASSERT(!HT_IS_PACKED(ht));
1970
0
    p = ht->arData;
1971
0
    end = p + ht->nNumUsed;
1972
0
    if (HT_HAS_STATIC_KEYS_ONLY(ht)) {
1973
0
      do {
1974
0
        i_zval_ptr_dtor(&p->val);
1975
0
      } while (++p != end);
1976
0
    } else if (HT_IS_WITHOUT_HOLES(ht)) {
1977
0
      do {
1978
0
        i_zval_ptr_dtor(&p->val);
1979
0
        if (EXPECTED(p->key)) {
1980
0
          zend_string_release(p->key);
1981
0
        }
1982
0
      } while (++p != end);
1983
0
    } 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
0
    HT_HASH_RESET(ht);
1994
0
  }
1995
0
  ht->nNumUsed = 0;
1996
0
  ht->nNumOfElements = 0;
1997
0
  ht->nNextFreeElement = ZEND_LONG_MIN;
1998
0
  ht->nInternalPointer = 0;
1999
0
}
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
13.5k
{
2032
13.5k
  uint32_t idx;
2033
2034
13.5k
  IS_CONSISTENT(ht);
2035
13.5k
  HT_ASSERT_RC1(ht);
2036
2037
13.5k
  idx = ht->nNumUsed;
2038
13.5k
  if (HT_IS_PACKED(ht)) {
2039
0
    zval *zv = ht->arPacked + ht->nNumUsed;
2040
2041
0
    while (idx > 0) {
2042
0
      idx--;
2043
0
      zv--;
2044
0
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2045
0
      _zend_hash_packed_del_val(ht, HT_IDX_TO_HASH(idx), zv);
2046
0
    }
2047
13.5k
  } else {
2048
13.5k
    Bucket *p = ht->arData + ht->nNumUsed;
2049
2050
40.6k
    while (idx > 0) {
2051
27.1k
      idx--;
2052
27.1k
      p--;
2053
27.1k
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2054
27.1k
      _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2055
27.1k
    }
2056
13.5k
  }
2057
2058
13.5k
  if (!(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
2059
6.77k
    pefree(HT_GET_DATA_ADDR(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
2060
6.77k
  }
2061
2062
13.5k
  SET_INCONSISTENT(HT_DESTROYED);
2063
13.5k
}
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
2
{
2076
2
  uint32_t idx;
2077
2
  int result;
2078
2079
2
  IS_CONSISTENT(ht);
2080
2
  if (HT_IS_PACKED(ht)) {
2081
0
    for (idx = 0; idx < ht->nNumUsed; idx++) {
2082
0
      zval *zv = ht->arPacked + idx;
2083
2084
0
      if (UNEXPECTED(Z_TYPE_P(zv) == IS_UNDEF)) continue;
2085
0
      result = apply_func(zv);
2086
2087
0
      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
0
      if (result & ZEND_HASH_APPLY_STOP) {
2092
0
        break;
2093
0
      }
2094
0
    }
2095
2
  } 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
2
}
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
6.77k
{
2209
6.77k
  uint32_t idx;
2210
6.77k
  int result;
2211
2212
6.77k
  IS_CONSISTENT(ht);
2213
2214
6.77k
  idx = ht->nNumUsed;
2215
6.77k
  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
6.77k
  } else {
2234
6.77k
    Bucket *p;
2235
2236
33.8k
    while (idx > 0) {
2237
27.1k
      idx--;
2238
27.1k
      p = ht->arData + idx;
2239
27.1k
      if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2240
2241
27.1k
      result = apply_func(&p->val);
2242
2243
27.1k
      if (result & ZEND_HASH_APPLY_REMOVE) {
2244
0
        HT_ASSERT_RC1(ht);
2245
0
        _zend_hash_del_el(ht, HT_IDX_TO_HASH(idx), p);
2246
0
      }
2247
27.1k
      if (result & ZEND_HASH_APPLY_STOP) {
2248
0
        break;
2249
0
      }
2250
27.1k
    }
2251
6.77k
  }
2252
6.77k
}
2253
2254
2255
ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor)
2256
0
{
2257
0
  uint32_t idx;
2258
0
  zval *new_entry, *data;
2259
2260
0
  IS_CONSISTENT(source);
2261
0
  IS_CONSISTENT(target);
2262
0
  HT_ASSERT_RC1(target);
2263
2264
0
  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
0
  for (idx = 0; idx < source->nNumUsed; idx++) {
2278
0
    Bucket *p = source->arData + idx;
2279
2280
0
    if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2281
2282
    /* INDIRECT element may point to UNDEF-ined slots */
2283
0
    data = &p->val;
2284
0
    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
0
    if (p->key) {
2291
0
      new_entry = zend_hash_update(target, p->key, data);
2292
0
    } else {
2293
0
      new_entry = zend_hash_index_update(target, p->h, data);
2294
0
    }
2295
0
    if (pCopyConstructor) {
2296
0
      pCopyConstructor(new_entry);
2297
0
    }
2298
0
  }
2299
0
}
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
0
{
2304
0
  if (with_holes) {
2305
0
    if (!packed && Z_TYPE_INFO_P(data) == IS_INDIRECT) {
2306
0
      data = Z_INDIRECT_P(data);
2307
0
    }
2308
0
    if (UNEXPECTED(Z_TYPE_INFO_P(data) == IS_UNDEF)) {
2309
0
      return 0;
2310
0
    }
2311
0
  } else if (!packed) {
2312
    /* INDIRECT element may point to UNDEF-ined slots */
2313
0
    if (Z_TYPE_INFO_P(data) == IS_INDIRECT) {
2314
0
      data = Z_INDIRECT_P(data);
2315
0
      if (UNEXPECTED(Z_TYPE_INFO_P(data) == IS_UNDEF)) {
2316
0
        return 0;
2317
0
      }
2318
0
    }
2319
0
  }
2320
2321
0
  do {
2322
0
    if (Z_OPT_REFCOUNTED_P(data)) {
2323
0
      if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1 &&
2324
0
          (Z_TYPE_P(Z_REFVAL_P(data)) != IS_ARRAY ||
2325
0
            Z_ARRVAL_P(Z_REFVAL_P(data)) != source)) {
2326
0
        data = Z_REFVAL_P(data);
2327
0
        if (!Z_OPT_REFCOUNTED_P(data)) {
2328
0
          break;
2329
0
        }
2330
0
      }
2331
0
      Z_ADDREF_P(data);
2332
0
    }
2333
0
  } while (0);
2334
0
  ZVAL_COPY_VALUE(dest, data);
2335
2336
0
  return 1;
2337
0
}
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
0
{
2341
0
  if (!zend_array_dup_value(source, &p->val, &q->val, packed, with_holes)) {
2342
0
    return 0;
2343
0
  }
2344
2345
0
  if (!packed) {
2346
0
    uint32_t nIndex;
2347
2348
0
    q->h = p->h;
2349
0
    q->key = p->key;
2350
0
    if (!static_keys && q->key) {
2351
0
      zend_string_addref(q->key);
2352
0
    }
2353
2354
0
    nIndex = q->h | target->nTableMask;
2355
0
    Z_NEXT(q->val) = HT_HASH(target, nIndex);
2356
0
    HT_HASH(target, nIndex) = HT_IDX_TO_HASH(idx);
2357
0
  }
2358
0
  return 1;
2359
0
}
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
0
static void zend_array_dup_ht_iterators(const HashTable *source, HashTable *target) {
2363
0
  uint32_t iter_index = 0;
2364
0
  uint32_t end_index = EG(ht_iterators_used);
2365
2366
0
  while (iter_index != end_index) {
2367
0
    HashTableIterator *iter = &EG(ht_iterators)[iter_index];
2368
0
    if (iter->ht == source) {
2369
0
      uint32_t copy_idx = zend_hash_iterator_add(target, iter->pos);
2370
      /* Refetch iter because the memory may be reallocated. */
2371
0
      iter = &EG(ht_iterators)[iter_index];
2372
0
      HashTableIterator *copy_iter = EG(ht_iterators) + copy_idx;
2373
0
      copy_iter->next_copy = iter->next_copy;
2374
0
      iter->next_copy = copy_idx;
2375
0
    }
2376
0
    iter_index++;
2377
0
  }
2378
0
}
2379
2380
static zend_always_inline void zend_array_dup_packed_elements(const HashTable *source, HashTable *target, bool with_holes)
2381
0
{
2382
0
  zval *p = source->arPacked;
2383
0
  zval *q = target->arPacked;
2384
0
  const zval *end = p + source->nNumUsed;
2385
2386
0
  do {
2387
0
    if (!zend_array_dup_value(source, p, q, true, with_holes)) {
2388
0
      if (with_holes) {
2389
0
        ZVAL_UNDEF(q);
2390
0
      }
2391
0
    }
2392
0
    p++; q++;
2393
0
  } while (p != end);
2394
2395
0
  if (UNEXPECTED(HT_HAS_ITERATORS(source))) {
2396
0
    zend_array_dup_ht_iterators(source, target);
2397
0
  }
2398
0
}
2399
2400
static zend_always_inline uint32_t zend_array_dup_elements(const HashTable *source, HashTable *target, bool static_keys, bool with_holes)
2401
0
{
2402
0
  uint32_t idx = 0;
2403
0
  Bucket *p = source->arData;
2404
0
  Bucket *q = target->arData;
2405
0
  const Bucket *end = p + source->nNumUsed;
2406
2407
0
  if (UNEXPECTED(HT_HAS_ITERATORS(source))) {
2408
0
    zend_array_dup_ht_iterators(source, target);
2409
0
  }
2410
2411
0
  do {
2412
0
    if (!zend_array_dup_element(source, target, idx, p, q, false, static_keys, with_holes)) {
2413
0
      uint32_t target_idx = idx;
2414
2415
0
      idx++; p++;
2416
0
      if (EXPECTED(!HT_HAS_ITERATORS(target))) {
2417
0
        while (p != end) {
2418
0
          if (zend_array_dup_element(source, target, target_idx, p, q, false, static_keys, with_holes)) {
2419
0
            if (source->nInternalPointer == idx) {
2420
0
              target->nInternalPointer = target_idx;
2421
0
            }
2422
0
            target_idx++; q++;
2423
0
          }
2424
0
          idx++; p++;
2425
0
        }
2426
0
      } 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
0
      return target_idx;
2447
0
    }
2448
0
    idx++; p++; q++;
2449
0
  } while (p != end);
2450
0
  return idx;
2451
0
}
2452
2453
ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(const HashTable *source)
2454
0
{
2455
0
  uint32_t idx;
2456
0
  HashTable *target;
2457
2458
0
  IS_CONSISTENT(source);
2459
2460
0
  ALLOC_HASHTABLE(target);
2461
0
  GC_SET_REFCOUNT(target, 1);
2462
0
  GC_TYPE_INFO(target) = GC_ARRAY;
2463
2464
0
  target->pDestructor = ZVAL_PTR_DTOR;
2465
2466
0
  if (source->nNumOfElements == 0) {
2467
0
    HT_FLAGS(target) = HASH_FLAG_UNINITIALIZED;
2468
0
    target->nTableMask = HT_MIN_MASK;
2469
0
    target->nNumUsed = 0;
2470
0
    target->nNumOfElements = 0;
2471
0
    target->nNextFreeElement = source->nNextFreeElement;
2472
0
    target->nInternalPointer = 0;
2473
0
    target->nTableSize = HT_MIN_SIZE;
2474
0
    HT_SET_DATA_ADDR(target, &uninitialized_bucket);
2475
0
  } 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
0
  } else if (HT_IS_PACKED(source)) {
2493
0
    ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_HAS_EMPTY_IND));
2494
0
    HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK;
2495
0
    target->nTableMask = HT_MIN_MASK;
2496
0
    target->nNumUsed = source->nNumUsed;
2497
0
    target->nNumOfElements = source->nNumOfElements;
2498
0
    target->nNextFreeElement = source->nNextFreeElement;
2499
0
    target->nTableSize = source->nTableSize;
2500
0
    HT_SET_DATA_ADDR(target, emalloc(HT_PACKED_SIZE_EX(target->nTableSize, HT_MIN_MASK)));
2501
0
    target->nInternalPointer =
2502
0
      (source->nInternalPointer < source->nNumUsed) ?
2503
0
        source->nInternalPointer : 0;
2504
2505
0
    HT_HASH_RESET_PACKED(target);
2506
2507
0
    if (HT_IS_WITHOUT_HOLES(target)) {
2508
0
      zend_array_dup_packed_elements(source, target, false);
2509
0
    } else {
2510
0
      zend_array_dup_packed_elements(source, target, true);
2511
0
    }
2512
0
  } else {
2513
    /* Indirects are removed during duplication, remove HASH_FLAG_HAS_EMPTY_IND accordingly. */
2514
0
    HT_FLAGS(target) = HT_FLAGS(source) & (HASH_FLAG_MASK & ~HASH_FLAG_HAS_EMPTY_IND);
2515
0
    target->nTableMask = source->nTableMask;
2516
0
    target->nNextFreeElement = source->nNextFreeElement;
2517
0
    target->nInternalPointer =
2518
0
      (source->nInternalPointer < source->nNumUsed) ?
2519
0
        source->nInternalPointer : 0;
2520
2521
0
    target->nTableSize = source->nTableSize;
2522
0
    HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target)));
2523
0
    HT_HASH_RESET(target);
2524
2525
0
    if (HT_HAS_STATIC_KEYS_ONLY(target)) {
2526
0
      if (HT_IS_WITHOUT_HOLES(source)) {
2527
0
        idx = zend_array_dup_elements(source, target, true, false);
2528
0
      } else {
2529
0
        idx = zend_array_dup_elements(source, target, true, true);
2530
0
      }
2531
0
    } else {
2532
0
      if (HT_IS_WITHOUT_HOLES(source)) {
2533
0
        idx = zend_array_dup_elements(source, target, false, false);
2534
0
      } else {
2535
0
        idx = zend_array_dup_elements(source, target, false, true);
2536
0
      }
2537
0
    }
2538
0
    target->nNumUsed = idx;
2539
0
    target->nNumOfElements = idx;
2540
0
  }
2541
0
  return target;
2542
0
}
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
0
{
2567
0
  uint32_t idx;
2568
0
  Bucket *p;
2569
0
  zval *t, *s;
2570
2571
0
  IS_CONSISTENT(source);
2572
0
  IS_CONSISTENT(target);
2573
0
  HT_ASSERT_RC1(target);
2574
2575
0
  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
0
  } else {
2612
0
    if (HT_IS_PACKED(source)) {
2613
0
      for (idx = 0; idx < source->nNumUsed; idx++) {
2614
0
        s = source->arPacked + idx;
2615
0
        if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2616
0
          continue;
2617
0
        }
2618
0
        t = zend_hash_index_add(target, idx, s);
2619
0
        if (t && pCopyConstructor) {
2620
0
          pCopyConstructor(t);
2621
0
        }
2622
0
      }
2623
0
      return;
2624
0
    }
2625
2626
0
    for (idx = 0; idx < source->nNumUsed; idx++) {
2627
0
      p = source->arData + idx;
2628
0
      s = &p->val;
2629
0
      if (UNEXPECTED(Z_TYPE_P(s) == IS_INDIRECT)) {
2630
0
        s = Z_INDIRECT_P(s);
2631
0
      }
2632
0
      if (UNEXPECTED(Z_TYPE_P(s) == IS_UNDEF)) {
2633
0
        continue;
2634
0
      }
2635
0
      if (p->key) {
2636
0
        t = _zend_hash_add_or_update_i(target, p->key, s, HASH_ADD | HASH_UPDATE_INDIRECT);
2637
0
        if (t && pCopyConstructor) {
2638
0
          pCopyConstructor(t);
2639
0
        }
2640
0
      } else {
2641
0
        t = zend_hash_index_add(target, p->h, s);
2642
0
        if (t && pCopyConstructor) {
2643
0
          pCopyConstructor(t);
2644
0
        }
2645
0
      }
2646
0
    }
2647
0
  }
2648
0
}
2649
2650
2651
static bool ZEND_FASTCALL zend_hash_replace_checker_wrapper(HashTable *target, zval *source_data, zend_ulong h, zend_string *key, void *pParam, merge_checker_func_t merge_checker_func)
2652
0
{
2653
0
  zend_hash_key hash_key;
2654
2655
0
  hash_key.h = h;
2656
0
  hash_key.key = key;
2657
0
  return merge_checker_func(target, source_data, &hash_key, pParam);
2658
0
}
2659
2660
2661
ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, const HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam)
2662
0
{
2663
0
  uint32_t idx;
2664
0
  Bucket *p;
2665
0
  zval *t;
2666
2667
0
  IS_CONSISTENT(source);
2668
0
  IS_CONSISTENT(target);
2669
0
  HT_ASSERT_RC1(target);
2670
2671
0
  ZEND_ASSERT(!HT_IS_PACKED(source));
2672
0
  for (idx = 0; idx < source->nNumUsed; idx++) {
2673
0
    p = source->arData + idx;
2674
0
    if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
2675
0
    if (zend_hash_replace_checker_wrapper(target, &p->val, p->h, p->key, pParam, pMergeSource)) {
2676
0
      t = zend_hash_update(target, p->key, &p->val);
2677
0
      if (pCopyConstructor) {
2678
0
        pCopyConstructor(t);
2679
0
      }
2680
0
    }
2681
0
  }
2682
0
}
2683
2684
2685
/* Returns the hash table data if found and NULL if not. */
2686
ZEND_API zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key)
2687
21.2k
{
2688
21.2k
  Bucket *p;
2689
2690
21.2k
  IS_CONSISTENT(ht);
2691
2692
21.2k
  (void)zend_string_hash_val(key);
2693
21.2k
  p = zend_hash_find_bucket(ht, key);
2694
21.2k
  return p ? &p->val : NULL;
2695
21.2k
}
2696
2697
ZEND_API zval* ZEND_FASTCALL zend_hash_find_known_hash(const HashTable *ht, const zend_string *key)
2698
3.82k
{
2699
3.82k
  Bucket *p;
2700
2701
3.82k
  IS_CONSISTENT(ht);
2702
2703
3.82k
  p = zend_hash_find_bucket(ht, key);
2704
3.82k
  return p ? &p->val : NULL;
2705
3.82k
}
2706
2707
ZEND_API zval* ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
2708
7.31k
{
2709
7.31k
  zend_ulong h;
2710
7.31k
  Bucket *p;
2711
2712
7.31k
  IS_CONSISTENT(ht);
2713
2714
7.31k
  h = zend_inline_hash_func(str, len);
2715
7.31k
  p = zend_hash_str_find_bucket(ht, str, len, h);
2716
7.31k
  return p ? &p->val : NULL;
2717
7.31k
}
2718
2719
ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h)
2720
0
{
2721
0
  Bucket *p;
2722
2723
0
  IS_CONSISTENT(ht);
2724
2725
0
  if (HT_IS_PACKED(ht)) {
2726
0
    if (h < ht->nNumUsed) {
2727
0
      zval *zv = ht->arPacked + h;
2728
2729
0
      if (Z_TYPE_P(zv) != IS_UNDEF) {
2730
0
        return zv;
2731
0
      }
2732
0
    }
2733
0
    return NULL;
2734
0
  }
2735
2736
0
  p = zend_hash_index_find_bucket(ht, h);
2737
0
  return p ? &p->val : NULL;
2738
0
}
2739
2740
ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulong h)
2741
0
{
2742
0
  Bucket *p;
2743
2744
0
  IS_CONSISTENT(ht);
2745
0
  ZEND_ASSERT(!HT_IS_PACKED(ht));
2746
2747
0
  p = zend_hash_index_find_bucket(ht, h);
2748
0
  return p ? &p->val : NULL;
2749
0
}
2750
2751
ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(const HashTable *ht, HashPosition *pos)
2752
0
{
2753
0
  IS_CONSISTENT(ht);
2754
0
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2755
0
  *pos = _zend_hash_get_valid_pos(ht, 0);
2756
0
}
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
0
{
2764
0
  uint32_t idx;
2765
2766
0
  IS_CONSISTENT(ht);
2767
0
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2768
2769
0
  idx = ht->nNumUsed;
2770
0
  if (HT_IS_PACKED(ht)) {
2771
0
    while (idx > 0) {
2772
0
      idx--;
2773
0
      if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) {
2774
0
        *pos = idx;
2775
0
        return;
2776
0
      }
2777
0
    }
2778
0
  } else {
2779
0
    while (idx > 0) {
2780
0
      idx--;
2781
0
      if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
2782
0
        *pos = idx;
2783
0
        return;
2784
0
      }
2785
0
    }
2786
0
  }
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
0
{
2793
0
  uint32_t idx;
2794
2795
0
  IS_CONSISTENT(ht);
2796
0
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2797
2798
0
  idx = _zend_hash_get_valid_pos(ht, *pos);
2799
0
  if (idx < ht->nNumUsed) {
2800
0
    if (HT_IS_PACKED(ht)) {
2801
0
      while (1) {
2802
0
        idx++;
2803
0
        if (idx >= ht->nNumUsed) {
2804
0
          *pos = ht->nNumUsed;
2805
0
          return SUCCESS;
2806
0
        }
2807
0
        if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) {
2808
0
          *pos = idx;
2809
0
          return SUCCESS;
2810
0
        }
2811
0
      }
2812
0
    } else {
2813
0
      while (1) {
2814
0
        idx++;
2815
0
        if (idx >= ht->nNumUsed) {
2816
0
          *pos = ht->nNumUsed;
2817
0
          return SUCCESS;
2818
0
        }
2819
0
        if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
2820
0
          *pos = idx;
2821
0
          return SUCCESS;
2822
0
        }
2823
0
      }
2824
0
    }
2825
0
  } else {
2826
0
    return FAILURE;
2827
0
  }
2828
0
}
2829
2830
ZEND_API zend_result ZEND_FASTCALL zend_hash_move_backwards_ex(const HashTable *ht, HashPosition *pos)
2831
0
{
2832
0
  uint32_t idx = *pos;
2833
2834
0
  IS_CONSISTENT(ht);
2835
0
  HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
2836
2837
0
  if (idx < ht->nNumUsed) {
2838
0
    if (HT_IS_PACKED(ht)) {
2839
0
      while (idx > 0) {
2840
0
        idx--;
2841
0
        if (Z_TYPE(ht->arPacked[idx]) != IS_UNDEF) {
2842
0
          *pos = idx;
2843
0
          return SUCCESS;
2844
0
        }
2845
0
      }
2846
0
    } else {
2847
0
      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
0
    }
2855
0
    *pos = ht->nNumUsed;
2856
0
    return SUCCESS;
2857
0
  } else {
2858
0
    return FAILURE;
2859
0
  }
2860
0
}
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
0
{
2865
0
  uint32_t idx;
2866
0
  Bucket *p;
2867
2868
0
  IS_CONSISTENT(ht);
2869
0
  idx = _zend_hash_get_valid_pos(ht, *pos);
2870
0
  if (idx < ht->nNumUsed) {
2871
0
    if (HT_IS_PACKED(ht)) {
2872
0
      *num_index = idx;
2873
0
      return HASH_KEY_IS_LONG;
2874
0
    }
2875
0
    p = ht->arData + idx;
2876
0
    if (p->key) {
2877
0
      *str_index = p->key;
2878
0
      return HASH_KEY_IS_STRING;
2879
0
    } else {
2880
0
      *num_index = p->h;
2881
0
      return HASH_KEY_IS_LONG;
2882
0
    }
2883
0
  }
2884
0
  return HASH_KEY_NON_EXISTENT;
2885
0
}
2886
2887
ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, const HashPosition *pos)
2888
0
{
2889
0
  uint32_t idx;
2890
0
  Bucket *p;
2891
2892
0
  IS_CONSISTENT(ht);
2893
0
  idx = _zend_hash_get_valid_pos(ht, *pos);
2894
0
  if (idx >= ht->nNumUsed) {
2895
0
    ZVAL_NULL(key);
2896
0
  } else {
2897
0
    if (HT_IS_PACKED(ht)) {
2898
0
      ZVAL_LONG(key, idx);
2899
0
      return;
2900
0
    }
2901
0
    p = ht->arData + idx;
2902
0
    if (p->key) {
2903
0
      ZVAL_STR_COPY(key, p->key);
2904
0
    } else {
2905
0
      ZVAL_LONG(key, p->h);
2906
0
    }
2907
0
  }
2908
0
}
2909
2910
ZEND_API zend_hash_key_type ZEND_FASTCALL zend_hash_get_current_key_type_ex(const HashTable *ht, const HashPosition *pos)
2911
0
{
2912
0
  uint32_t idx;
2913
0
  Bucket *p;
2914
2915
0
  IS_CONSISTENT(ht);
2916
0
  idx = _zend_hash_get_valid_pos(ht, *pos);
2917
0
  if (idx < ht->nNumUsed) {
2918
0
    if (HT_IS_PACKED(ht)) {
2919
0
      return HASH_KEY_IS_LONG;
2920
0
    }
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
0
  return HASH_KEY_NON_EXISTENT;
2929
0
}
2930
2931
2932
ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(const HashTable *ht, const HashPosition *pos)
2933
0
{
2934
0
  uint32_t idx;
2935
0
  Bucket *p;
2936
2937
0
  IS_CONSISTENT(ht);
2938
0
  idx = _zend_hash_get_valid_pos(ht, *pos);
2939
0
  if (idx < ht->nNumUsed) {
2940
0
    if (HT_IS_PACKED(ht)) {
2941
0
      return &ht->arPacked[idx];
2942
0
    }
2943
0
    p = ht->arData + idx;
2944
0
    return &p->val;
2945
0
  } else {
2946
0
    return NULL;
2947
0
  }
2948
0
}
2949
2950
ZEND_API void zend_hash_bucket_swap(Bucket *p, Bucket *q)
2951
0
{
2952
0
  zval val;
2953
0
  zend_ulong h;
2954
0
  zend_string *key;
2955
2956
0
  val = p->val;
2957
0
  h = p->h;
2958
0
  key = p->key;
2959
2960
0
  p->val = q->val;
2961
0
  p->h = q->h;
2962
0
  p->key = q->key;
2963
2964
0
  q->val = val;
2965
0
  q->h = h;
2966
0
  q->key = key;
2967
0
}
2968
2969
ZEND_API void zend_hash_bucket_renum_swap(Bucket *p, Bucket *q)
2970
0
{
2971
0
  zval val;
2972
2973
0
  val = p->val;
2974
0
  p->val = q->val;
2975
0
  q->val = val;
2976
0
}
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
2
{
2995
2
  Bucket *p;
2996
2
  uint32_t i, j;
2997
2998
2
  IS_CONSISTENT(ht);
2999
3000
2
  if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) {
3001
    /* Doesn't require sorting */
3002
0
    return;
3003
0
  }
3004
3005
2
  if (HT_IS_PACKED(ht)) {
3006
0
    zend_hash_packed_to_hash(ht); // TODO: ???
3007
0
  }
3008
3009
2
  if (HT_IS_WITHOUT_HOLES(ht)) {
3010
    /* Store original order of elements in extra space to allow stable sorting. */
3011
28
    for (i = 0; i < ht->nNumUsed; i++) {
3012
26
      Z_EXTRA(ht->arData[i].val) = i;
3013
26
    }
3014
2
  } 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
2
  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
2
    HT_HASH_RESET(ht);
3036
2
  }
3037
3038
2
  sort((void *)ht->arData, ht->nNumUsed, sizeof(Bucket), (compare_func_t) compar,
3039
2
      (swap_func_t)(renumber? zend_hash_bucket_renum_swap :
3040
2
        (HT_IS_PACKED(ht) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap)));
3041
3042
2
  ht->nInternalPointer = 0;
3043
3044
2
  if (renumber) {
3045
0
    for (j = 0; j < i; j++) {
3046
0
      p = ht->arData + j;
3047
0
      p->h = j;
3048
0
      if (p->key) {
3049
0
        zend_string_release(p->key);
3050
0
        p->key = NULL;
3051
0
      }
3052
0
    }
3053
3054
0
    ht->nNextFreeElement = i;
3055
0
  }
3056
2
  if (HT_IS_PACKED(ht)) {
3057
0
    if (!renumber) {
3058
0
      zend_hash_packed_to_hash(ht);
3059
0
    }
3060
2
  } else {
3061
2
    if (renumber) {
3062
0
      void *new_data, *old_data = HT_GET_DATA_ADDR(ht);
3063
0
      Bucket *old_buckets = ht->arData;
3064
0
      zval *zv;
3065
3066
0
      new_data = pemalloc(HT_PACKED_SIZE_EX(ht->nTableSize, HT_MIN_MASK), (GC_FLAGS(ht) & IS_ARRAY_PERSISTENT));
3067
0
      HT_FLAGS(ht) |= HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;
3068
0
      ht->nTableMask = HT_MIN_MASK;
3069
0
      HT_SET_DATA_ADDR(ht, new_data);
3070
0
      p = old_buckets;
3071
0
      zv = ht->arPacked;
3072
0
      for (i = 0; i < ht->nTableSize; i++) {
3073
0
        ZVAL_COPY_VALUE(zv, &p->val);
3074
0
        zv++;
3075
0
        p++;
3076
0
      }
3077
0
      pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
3078
0
      HT_HASH_RESET_PACKED(ht);
3079
2
    } else {
3080
2
      zend_hash_rehash(ht);
3081
2
    }
3082
2
  }
3083
2
}
3084
3085
ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
3086
2
{
3087
2
  HT_ASSERT_RC1(ht);
3088
2
  zend_hash_sort_internal(ht, sort, compar, renumber);
3089
2
}
3090
3091
ZEND_API void ZEND_FASTCALL zend_array_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber)
3092
0
{
3093
0
  HT_ASSERT_RC1(ht);
3094
3095
  /* Unpack the array early to avoid RCn assertion failures. */
3096
0
  if (HT_IS_PACKED(ht)) {
3097
0
    zend_hash_packed_to_hash(ht);
3098
0
  }
3099
3100
  /* Adding a refcount prevents the array from going away. */
3101
0
  GC_ADDREF(ht);
3102
3103
0
  zend_hash_sort_internal(ht, sort, compar, renumber);
3104
3105
0
  if (UNEXPECTED(GC_DELREF(ht) == 0)) {
3106
0
    zend_array_destroy(ht);
3107
0
  } else {
3108
0
    gc_check_possible_root((zend_refcounted *)ht);
3109
0
  }
3110
0
}
3111
3112
0
static zend_always_inline int zend_hash_compare_impl(const HashTable *ht1, const HashTable *ht2, compare_func_t compar, bool ordered) {
3113
0
  uint32_t idx1, idx2;
3114
0
  zend_string *key1, *key2;
3115
0
  zend_ulong h1, h2;
3116
0
  zval *pData1, *pData2;;
3117
0
  int result;
3118
3119
0
  if (ht1->nNumOfElements != ht2->nNumOfElements) {
3120
0
    return ht1->nNumOfElements > ht2->nNumOfElements ? 1 : -1;
3121
0
  }
3122
3123
0
  for (idx1 = 0, idx2 = 0; idx1 < ht1->nNumUsed; idx1++) {
3124
0
    if (HT_IS_PACKED(ht1)) {
3125
0
      pData1 = ht1->arPacked + idx1;
3126
0
      h1 = idx1;
3127
0
      key1 = NULL;
3128
0
    } 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
0
    if (Z_TYPE_P(pData1) == IS_UNDEF) continue;
3136
0
    if (ordered) {
3137
0
      if (HT_IS_PACKED(ht2)) {
3138
0
        while (1) {
3139
0
          ZEND_ASSERT(idx2 != ht2->nNumUsed);
3140
0
          pData2 = ht2->arPacked + idx2;
3141
0
          h2 = idx2;
3142
0
          key2 = NULL;
3143
0
          if (Z_TYPE_P(pData2) != IS_UNDEF) break;
3144
0
          idx2++;
3145
0
        }
3146
0
      } else {
3147
0
        while (1) {
3148
0
          Bucket *p;
3149
0
          ZEND_ASSERT(idx2 != ht2->nNumUsed);
3150
0
          p = ht2->arData + idx2;
3151
0
          pData2 = &p->val;
3152
0
          h2 = p->h;
3153
0
          key2 = p->key;
3154
0
          if (Z_TYPE_P(pData2) != IS_UNDEF) break;
3155
0
          idx2++;
3156
0
        }
3157
0
      }
3158
0
      if (key1 == NULL && key2 == NULL) { /* numeric indices */
3159
0
        if (h1 != h2) {
3160
0
          return h1 > h2 ? 1 : -1;
3161
0
        }
3162
0
      } else if (key1 != NULL && key2 != NULL) { /* string indices */
3163
0
        if (ZSTR_LEN(key1) != ZSTR_LEN(key2)) {
3164
0
          return ZSTR_LEN(key1) > ZSTR_LEN(key2) ? 1 : -1;
3165
0
        }
3166
3167
0
        result = memcmp(ZSTR_VAL(key1), ZSTR_VAL(key2), ZSTR_LEN(key1));
3168
0
        if (result != 0) {
3169
0
          return result;
3170
0
        }
3171
0
      } else {
3172
        /* Mixed key types: A string key is considered as larger */
3173
0
        return key1 != NULL ? 1 : -1;
3174
0
      }
3175
0
      idx2++;
3176
0
    } else {
3177
0
      if (key1 == NULL) { /* numeric index */
3178
0
        pData2 = zend_hash_index_find(ht2, h1);
3179
0
        if (pData2 == NULL) {
3180
0
          return 1;
3181
0
        }
3182
0
      } 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
0
    }
3189
3190
0
    if (Z_TYPE_P(pData1) == IS_INDIRECT) {
3191
0
      pData1 = Z_INDIRECT_P(pData1);
3192
0
    }
3193
0
    if (Z_TYPE_P(pData2) == IS_INDIRECT) {
3194
0
      pData2 = Z_INDIRECT_P(pData2);
3195
0
    }
3196
3197
0
    if (Z_TYPE_P(pData1) == IS_UNDEF) {
3198
0
      if (Z_TYPE_P(pData2) != IS_UNDEF) {
3199
0
        return -1;
3200
0
      }
3201
0
    } else if (Z_TYPE_P(pData2) == IS_UNDEF) {
3202
0
      return 1;
3203
0
    } else {
3204
0
      result = compar(pData1, pData2);
3205
0
      if (result != 0) {
3206
0
        return result;
3207
0
      }
3208
0
    }
3209
0
  }
3210
3211
0
  return 0;
3212
0
}
3213
3214
ZEND_API int zend_hash_compare(HashTable *ht1, const HashTable *ht2, compare_func_t compar, bool ordered)
3215
0
{
3216
0
  int result;
3217
0
  IS_CONSISTENT(ht1);
3218
0
  IS_CONSISTENT(ht2);
3219
3220
0
  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
0
  if (UNEXPECTED(GC_IS_RECURSIVE(ht1))) {
3229
0
    zend_throw_error(NULL, "Nesting level too deep - recursive dependency?");
3230
0
    return ZEND_UNCOMPARABLE;
3231
0
  }
3232
3233
0
  GC_TRY_PROTECT_RECURSION(ht1);
3234
0
  result = zend_hash_compare_impl(ht1, ht2, compar, ordered);
3235
0
  GC_TRY_UNPROTECT_RECURSION(ht1);
3236
3237
0
  return result;
3238
0
}
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
13.0k
{
3310
13.0k
  const char *tmp = key;
3311
3312
13.0k
  const char *end = key + length;
3313
3314
13.0k
  if (*tmp == '-') {
3315
2.11k
    tmp++;
3316
2.11k
  }
3317
3318
13.0k
  if ((*tmp == '0' && length > 1) /* numbers with leading zeros */
3319
12.3k
   || (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
1.34k
       *tmp > '2')) { /* overflow */
3323
1.34k
    return 0;
3324
1.34k
  }
3325
11.7k
  *idx = (*tmp - '0');
3326
31.5k
  while (1) {
3327
31.5k
    ++tmp;
3328
31.5k
    if (tmp == end) {
3329
10.0k
      if (*key == '-') {
3330
1.48k
        if (*idx-1 > ZEND_LONG_MAX) { /* overflow */
3331
194
          return 0;
3332
194
        }
3333
1.29k
        *idx = 0 - *idx;
3334
8.59k
      } else if (*idx > ZEND_LONG_MAX) { /* overflow */
3335
204
        return 0;
3336
204
      }
3337
9.68k
      return 1;
3338
10.0k
    }
3339
21.5k
    if (*tmp <= '9' && *tmp >= '0') {
3340
19.8k
      *idx = (*idx * 10) + (*tmp - '0');
3341
19.8k
    } else {
3342
1.63k
      return 0;
3343
1.63k
    }
3344
21.5k
  }
3345
11.7k
}
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
0
{
3353
0
  zend_ulong num_key;
3354
0
  zend_string *str_key;
3355
0
  zval *zv;
3356
3357
0
  if (UNEXPECTED(HT_IS_PACKED(ht))) {
3358
0
    goto convert;
3359
0
  }
3360
3361
0
  ZEND_HASH_MAP_FOREACH_STR_KEY(ht, str_key) {
3362
0
    if (!str_key) {
3363
0
      goto convert;
3364
0
    }
3365
0
  } ZEND_HASH_FOREACH_END();
3366
3367
0
  if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
3368
0
    GC_ADDREF(ht);
3369
0
  }
3370
3371
0
  return ht;
3372
3373
0
convert:
3374
0
  {
3375
0
    HashTable *new_ht = zend_new_array(zend_hash_num_elements(ht));
3376
3377
0
    ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) {
3378
0
      if (!str_key) {
3379
0
        str_key = zend_long_to_str(num_key);
3380
0
        zend_string_delref(str_key);
3381
0
      }
3382
0
      do {
3383
0
        if (Z_OPT_REFCOUNTED_P(zv)) {
3384
0
          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
0
          Z_ADDREF_P(zv);
3391
0
        }
3392
0
      } while (0);
3393
0
      zend_hash_update(new_ht, str_key, zv);
3394
0
    } ZEND_HASH_FOREACH_END();
3395
3396
0
    return new_ht;
3397
0
  }
3398
0
}
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
0
{
3406
0
  zend_ulong num_key;
3407
0
  zend_string *str_key;
3408
0
  zval *zv;
3409
3410
0
  if (!HT_IS_PACKED(ht)) {
3411
0
    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
0
      if (str_key && ZEND_HANDLE_NUMERIC(str_key, num_key)) {
3418
0
        goto convert;
3419
0
      }
3420
0
    } ZEND_HASH_FOREACH_END();
3421
0
  }
3422
3423
0
  if (always_duplicate) {
3424
0
    return zend_array_dup(ht);
3425
0
  }
3426
3427
0
  if (EXPECTED(!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE))) {
3428
0
    GC_ADDREF(ht);
3429
0
  }
3430
3431
0
  return ht;
3432
3433
0
convert:
3434
0
  {
3435
0
    HashTable *new_ht = zend_new_array(zend_hash_num_elements(ht));
3436
3437
0
    ZEND_HASH_MAP_FOREACH_KEY_VAL_IND(ht, num_key, str_key, zv) {
3438
0
      do {
3439
0
        if (Z_OPT_REFCOUNTED_P(zv)) {
3440
0
          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
0
          Z_ADDREF_P(zv);
3447
0
        }
3448
0
      } while (0);
3449
      /* Again, thank ArrayObject for `!str_key ||`. */
3450
0
      if (!str_key || ZEND_HANDLE_NUMERIC(str_key, num_key)) {
3451
0
        zend_hash_index_update(new_ht, num_key, zv);
3452
0
      } else {
3453
0
        zend_hash_update(new_ht, str_key, zv);
3454
0
      }
3455
0
    } ZEND_HASH_FOREACH_END();
3456
3457
0
    return new_ht;
3458
0
  }
3459
0
}