Coverage Report

Created: 2026-10-28 06:20

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