Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/spl/spl_fixedarray.c
Line
Count
Source
1
/*
2
  +----------------------------------------------------------------------+
3
  | Copyright © The PHP Group and Contributors.                          |
4
  +----------------------------------------------------------------------+
5
  | This source file is subject to the Modified BSD License that is      |
6
  | bundled with this package in the file LICENSE, and is available      |
7
  | through the World Wide Web at <https://www.php.net/license/>.        |
8
  |                                                                      |
9
  | SPDX-License-Identifier: BSD-3-Clause                                |
10
  +----------------------------------------------------------------------+
11
  | Author: Antony Dovgal <tony@daylessday.org>                          |
12
  |         Etienne Kneuss <colder@php.net>                              |
13
  +----------------------------------------------------------------------+
14
*/
15
16
#ifdef HAVE_CONFIG_H
17
#include <config.h>
18
#endif
19
20
#include "php.h"
21
#include "zend_interfaces.h"
22
#include "zend_exceptions.h"
23
24
#include "spl_fixedarray_arginfo.h"
25
#include "spl_fixedarray.h"
26
#include "spl_exceptions.h"
27
#include "ext/json/php_json.h" /* For php_json_serializable_ce */
28
29
static zend_object_handlers spl_handler_SplFixedArray;
30
PHPAPI zend_class_entry *spl_ce_SplFixedArray;
31
32
/* Check if the object is an instance of a subclass of SplFixedArray that overrides method's implementation.
33
 * Expect subclassing SplFixedArray to be rare and check that first. */
34
2.60k
#define HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, method) UNEXPECTED((object)->ce != spl_ce_SplFixedArray && (object)->ce->arrayaccess_funcs_ptr->method->common.scope != spl_ce_SplFixedArray)
35
36
typedef struct _spl_fixedarray {
37
  zend_long size;
38
  /* It is possible to resize this, so this can't be combined with the object */
39
  zval *elements;
40
  /* If positive, it's a resize within a resize and the value gives the desired size. If -1, it's not. */
41
  zend_long cached_resize;
42
} spl_fixedarray;
43
44
typedef struct _spl_fixedarray_object {
45
  spl_fixedarray          array;
46
  zend_function          *fptr_count;
47
  zend_object             std;
48
} spl_fixedarray_object;
49
50
typedef struct _spl_fixedarray_it {
51
  zend_object_iterator intern;
52
  zend_long            current;
53
} spl_fixedarray_it;
54
55
27.3k
#define spl_fixed_array_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_fixedarray_object, std)
56
57
6.57k
#define Z_SPLFIXEDARRAY_P(zv)  spl_fixed_array_from_obj(Z_OBJ_P((zv)))
58
59
/* Helps enforce the invariants in debug mode:
60
 *   - if size == 0, then elements == NULL
61
 *   - if size > 0, then elements != NULL
62
 *   - size is not less than 0
63
 */
64
static bool spl_fixedarray_empty(spl_fixedarray *array)
65
7.68k
{
66
7.68k
  if (array->elements) {
67
3.86k
    ZEND_ASSERT(array->size > 0);
68
3.86k
    return false;
69
3.86k
  }
70
3.81k
  ZEND_ASSERT(array->size == 0);
71
3.81k
  return true;
72
3.81k
}
73
74
static void spl_fixedarray_default_ctor(spl_fixedarray *array)
75
17
{
76
17
  array->size = 0;
77
17
  array->elements = NULL;
78
17
  array->cached_resize = -1;
79
17
}
80
81
/* Initializes the range [from, to) to null. Does not dtor existing elements. */
82
static void spl_fixedarray_init_elems(spl_fixedarray *array, zend_long from, zend_long to)
83
998
{
84
998
  ZEND_ASSERT(from <= to);
85
998
  zval *begin = array->elements + from, *end = array->elements + to;
86
87
2.10M
  while (begin != end) {
88
2.10M
    ZVAL_NULL(begin++);
89
2.10M
  }
90
998
}
91
92
static void spl_fixedarray_init_non_empty_struct(spl_fixedarray *array, zend_long size)
93
6.50k
{
94
6.50k
  array->size = 0; /* reset size in case ecalloc() fails */
95
6.50k
  array->elements = size ? safe_emalloc(size, sizeof(zval), 0) : NULL;
96
6.50k
  array->size = size;
97
6.50k
  array->cached_resize = -1;
98
6.50k
}
99
100
static void spl_fixedarray_init(spl_fixedarray *array, zend_long size)
101
1.01k
{
102
1.01k
  if (size > 0) {
103
998
    spl_fixedarray_init_non_empty_struct(array, size);
104
998
    spl_fixedarray_init_elems(array, 0, size);
105
998
  } else {
106
17
    spl_fixedarray_default_ctor(array);
107
17
  }
108
1.01k
}
109
110
/* Copies the range [begin, end) into the fixedarray, beginning at `offset`.
111
 * Does not dtor the existing elements.
112
 */
113
static void spl_fixedarray_copy_range(spl_fixedarray *array, zend_long offset, zval *begin, zval *end)
114
0
{
115
0
  ZEND_ASSERT(offset >= 0);
116
0
  ZEND_ASSERT(array->size - offset >= end - begin);
117
118
0
  zval *to = &array->elements[offset];
119
0
  while (begin != end) {
120
0
    ZVAL_COPY(to++, begin++);
121
0
  }
122
0
}
123
124
static void spl_fixedarray_copy_ctor(spl_fixedarray *to, spl_fixedarray *from)
125
0
{
126
0
  zend_long size = from->size;
127
0
  spl_fixedarray_init(to, size);
128
0
  if (size != 0) {
129
0
    zval *begin = from->elements, *end = from->elements + size;
130
0
    spl_fixedarray_copy_range(to, 0, begin, end);
131
0
  }
132
0
}
133
134
/* Destructs the elements in the range [from, to).
135
 * Caller is expected to bounds check.
136
 */
137
static void spl_fixedarray_dtor_range(spl_fixedarray *array, zend_long from, zend_long to)
138
0
{
139
0
  array->size = from;
140
0
  zval *begin = array->elements + from, *end = array->elements + to;
141
0
  while (begin != end) {
142
0
    zval_ptr_dtor(begin++);
143
0
  }
144
0
}
145
146
/* Destructs and frees contents but not the array itself.
147
 * If you want to re-use the array then you need to re-initialize it.
148
 */
149
static void spl_fixedarray_dtor(spl_fixedarray *array)
150
6.91k
{
151
6.91k
  if (!spl_fixedarray_empty(array)) {
152
3.86k
    zval *begin = array->elements, *end = array->elements + array->size;
153
3.86k
    array->elements = NULL;
154
3.86k
    array->size = 0;
155
2.12M
    while (begin != end) {
156
2.11M
      zval_ptr_dtor(--end);
157
2.11M
    }
158
3.86k
    efree(begin);
159
3.86k
  }
160
6.91k
}
161
162
static void spl_fixedarray_resize(spl_fixedarray *array, zend_long size)
163
0
{
164
0
  if (size == array->size) {
165
    /* nothing to do */
166
0
    return;
167
0
  }
168
169
0
  if (UNEXPECTED(array->cached_resize >= 0)) {
170
    /* We're already resizing, so just remember the desired size.
171
     * The resize will happen later. */
172
0
    array->cached_resize = size;
173
0
    return;
174
0
  }
175
  /* first initialization */
176
0
  if (array->size == 0) {
177
0
    spl_fixedarray_init(array, size);
178
0
    return;
179
0
  }
180
181
0
  array->cached_resize = size;
182
183
  /* clearing the array */
184
0
  if (size == 0) {
185
0
    spl_fixedarray_dtor(array);
186
0
    array->elements = NULL;
187
0
    array->size = 0;
188
0
  } else if (size > array->size) {
189
0
    array->elements = safe_erealloc(array->elements, size, sizeof(zval), 0);
190
0
    spl_fixedarray_init_elems(array, array->size, size);
191
0
    array->size = size;
192
0
  } else { /* size < array->size */
193
    /* Size set in spl_fixedarray_dtor_range() */
194
0
    spl_fixedarray_dtor_range(array, size, array->size);
195
0
    array->elements = erealloc(array->elements, sizeof(zval) * size);
196
0
  }
197
198
  /* If resized within the destructor, take the last resize command and perform it */
199
0
  zend_long cached_resize = array->cached_resize;
200
0
  array->cached_resize = -1;
201
0
  if (cached_resize != size) {
202
0
    spl_fixedarray_resize(array, cached_resize);
203
0
  }
204
0
}
205
206
static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, int *n)
207
11.2k
{
208
11.2k
  spl_fixedarray_object *intern = spl_fixed_array_from_obj(obj);
209
210
11.2k
  *table = intern->array.elements;
211
11.2k
  *n = (int)intern->array.size;
212
213
11.2k
  if (obj->properties == NULL && obj->ce->default_properties_count == 0) {
214
9.52k
    return NULL;
215
9.52k
  } else {
216
1.74k
    return zend_std_get_properties(obj);
217
1.74k
  }
218
11.2k
}
219
220
static HashTable* spl_fixedarray_object_get_properties_for(zend_object *obj, zend_prop_purpose purpose)
221
2
{
222
  /* This has __serialize, so the purpose is not ZEND_PROP_PURPOSE_SERIALIZE, which would expect a non-null return value */
223
2
  ZEND_ASSERT(purpose != ZEND_PROP_PURPOSE_SERIALIZE);
224
225
2
  const spl_fixedarray_object *intern = spl_fixed_array_from_obj(obj);
226
  /*
227
   * SplFixedArray can be subclassed or have dynamic properties (With or without AllowDynamicProperties in subclasses).
228
   * Instances of subclasses with declared properties may have properties but not yet have a property table.
229
   */
230
2
  HashTable *source_properties = obj->properties ? obj->properties : (obj->ce->default_properties_count ? zend_std_get_properties(obj) : NULL);
231
232
2
  const zend_long size = intern->array.size;
233
2
  if (size == 0 && (!source_properties || !zend_hash_num_elements(source_properties))) {
234
0
    return NULL;
235
0
  }
236
2
  zval *const elements = intern->array.elements;
237
2
  HashTable *ht = zend_new_array(size);
238
239
  /* The array elements are not *real properties*. */
240
2
  if (purpose != ZEND_PROP_PURPOSE_GET_OBJECT_VARS) {
241
2.09M
    for (zend_long i = 0; i < size; i++) {
242
2.09M
      Z_TRY_ADDREF_P(&elements[i]);
243
2.09M
      zend_hash_next_index_insert(ht, &elements[i]);
244
2.09M
    }
245
2
  }
246
247
2
  if (source_properties && zend_hash_num_elements(source_properties) > 0) {
248
0
    zend_long nkey;
249
0
    zend_string *skey;
250
0
    zval *value;
251
0
    ZEND_HASH_MAP_FOREACH_KEY_VAL_IND(source_properties, nkey, skey, value) {
252
0
      Z_TRY_ADDREF_P(value);
253
0
      if (skey) {
254
0
        zend_hash_add_new(ht, skey, value);
255
0
      } else {
256
0
        zend_hash_index_update(ht, nkey, value);
257
0
      }
258
0
    } ZEND_HASH_FOREACH_END();
259
0
  }
260
261
2
  return ht;
262
2
}
263
264
static void spl_fixedarray_object_free_storage(zend_object *object)
265
6.91k
{
266
6.91k
  spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
267
6.91k
  spl_fixedarray_dtor(&intern->array);
268
6.91k
  zend_object_std_dtor(&intern->std);
269
6.91k
}
270
271
static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, zend_object *orig, bool clone_orig)
272
6.91k
{
273
6.91k
  spl_fixedarray_object *intern;
274
6.91k
  zend_class_entry      *parent = class_type;
275
276
6.91k
  intern = zend_object_alloc(sizeof(spl_fixedarray_object), parent);
277
278
6.91k
  zend_object_std_init(&intern->std, class_type);
279
6.91k
  object_properties_init(&intern->std, class_type);
280
281
6.91k
  if (orig && clone_orig) {
282
0
    spl_fixedarray_object *other = spl_fixed_array_from_obj(orig);
283
0
    spl_fixedarray_copy_ctor(&intern->array, &other->array);
284
0
  }
285
286
6.91k
  if (UNEXPECTED(class_type != spl_ce_SplFixedArray)) {
287
    /* Find count() method */
288
0
    zend_function *fptr_count = zend_hash_find_ptr(&class_type->function_table, ZSTR_KNOWN(ZEND_STR_COUNT));
289
0
    if (fptr_count->common.scope == spl_ce_SplFixedArray) {
290
0
      fptr_count = NULL;
291
0
    }
292
0
    intern->fptr_count = fptr_count;
293
0
  }
294
295
6.91k
  return &intern->std;
296
6.91k
}
297
298
static zend_object *spl_fixedarray_new(zend_class_entry *class_type)
299
6.91k
{
300
6.91k
  return spl_fixedarray_object_new_ex(class_type, NULL, false);
301
6.91k
}
302
303
static zend_object *spl_fixedarray_object_clone(zend_object *old_object)
304
0
{
305
0
  zend_object *new_object = spl_fixedarray_object_new_ex(old_object->ce, old_object, true);
306
307
0
  zend_objects_clone_members(new_object, old_object);
308
309
0
  return new_object;
310
0
}
311
312
static zend_never_inline zend_ulong spl_offset_convert_to_ulong_slow(const zval *offset) /* {{{ */
313
7
{
314
7
  try_again:
315
7
  switch (Z_TYPE_P(offset)) {
316
0
    case IS_STRING: {
317
0
      zend_ulong index;
318
0
      if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), index)) {
319
0
        return index;
320
0
      }
321
0
      break;
322
0
    }
323
4
    case IS_DOUBLE:
324
4
      return zend_dval_to_lval_safe(Z_DVAL_P(offset));
325
0
    case IS_LONG:
326
0
      return Z_LVAL_P(offset);
327
0
    case IS_FALSE:
328
0
      return 0;
329
3
    case IS_TRUE:
330
3
      return 1;
331
0
    case IS_REFERENCE:
332
0
      offset = Z_REFVAL_P(offset);
333
0
      goto try_again;
334
0
    case IS_RESOURCE:
335
0
      zend_use_resource_as_offset(offset);
336
0
      return Z_RES_HANDLE_P(offset);
337
7
  }
338
339
  /* Use SplFixedArray name from the CE */
340
0
  zend_illegal_container_offset(spl_ce_SplFixedArray->name, offset, BP_VAR_R);
341
0
  return 0;
342
7
}
343
344
/* Returned index is an unsigned number such that we don't have to do a negative check.
345
 * Negative numbers will be mapped at indices larger than ZEND_ULONG_MAX,
346
 * which is beyond the maximum length. */
347
static zend_always_inline zend_ulong spl_offset_convert_to_ulong(const zval *offset)
348
2.60k
{
349
2.60k
  if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
350
    /* Allow skipping exception check at call-site. */
351
2.59k
    ZEND_ASSERT(!EG(exception));
352
2.59k
    return Z_LVAL_P(offset);
353
2.59k
  } else {
354
7
    return spl_offset_convert_to_ulong_slow(offset);
355
7
  }
356
2.60k
}
357
358
static zval *spl_fixedarray_object_read_dimension_helper(spl_fixedarray_object *intern, zval *offset)
359
2.06k
{
360
  /* we have to return NULL on error here to avoid memleak because of
361
   * ZE duplicating uninitialized_zval_ptr */
362
2.06k
  if (UNEXPECTED(!offset)) {
363
0
    zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
364
0
    return NULL;
365
0
  }
366
367
2.06k
  zend_ulong index = spl_offset_convert_to_ulong(offset);
368
2.06k
  if (UNEXPECTED(EG(exception))) {
369
0
    return NULL;
370
0
  }
371
372
2.06k
  if (UNEXPECTED(index >= intern->array.size)) {
373
11
    zend_throw_exception(spl_ce_OutOfBoundsException, "Index invalid or out of range", 0);
374
11
    return NULL;
375
2.05k
  } else {
376
2.05k
    return &intern->array.elements[index];
377
2.05k
  }
378
2.06k
}
379
380
static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset, int check_empty);
381
382
static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
383
2.06k
{
384
2.06k
  if (type == BP_VAR_IS && !spl_fixedarray_object_has_dimension(object, offset, 0)) {
385
0
    return &EG(uninitialized_zval);
386
0
  }
387
388
2.06k
  if (HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetget)) {
389
0
    zval tmp;
390
0
    if (!offset) {
391
0
      ZVAL_NULL(&tmp);
392
0
      offset = &tmp;
393
0
    }
394
0
    zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetget, object, rv, offset);
395
0
    if (!Z_ISUNDEF_P(rv)) {
396
0
      return rv;
397
0
    }
398
0
    return &EG(uninitialized_zval);
399
0
  }
400
401
2.06k
  spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
402
2.06k
  return spl_fixedarray_object_read_dimension_helper(intern, offset);
403
2.06k
}
404
405
static void spl_fixedarray_object_write_dimension_helper(spl_fixedarray_object *intern, zval *offset, zval *value)
406
538
{
407
538
  if (UNEXPECTED(!offset)) {
408
    /* '$array[] = value' syntax is not supported */
409
2
    zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
410
2
    return;
411
2
  }
412
413
536
  zend_ulong index = spl_offset_convert_to_ulong(offset);
414
536
  if (UNEXPECTED(EG(exception))) {
415
0
    return;
416
0
  }
417
418
536
  if (UNEXPECTED(index >= intern->array.size)) {
419
4
    zend_throw_exception(spl_ce_OutOfBoundsException, "Index invalid or out of range", 0);
420
532
  } else {
421
    /* Fix #81429 */
422
532
    zval *ptr = &(intern->array.elements[index]);
423
    /* This should be guaranteed by the VM handler or argument parsing. */
424
532
    ZEND_ASSERT(Z_TYPE_P(value) != IS_REFERENCE);
425
532
    Z_TRY_ADDREF_P(value);
426
532
    zend_safe_assign_to_variable_noref(ptr, value);
427
532
  }
428
536
}
429
430
static void spl_fixedarray_object_write_dimension(zend_object *object, zval *offset, zval *value)
431
538
{
432
538
  if (HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetset)) {
433
0
    zval tmp;
434
435
0
    if (!offset) {
436
0
      ZVAL_NULL(&tmp);
437
0
      offset = &tmp;
438
0
    }
439
0
    zend_call_known_instance_method_with_2_params(object->ce->arrayaccess_funcs_ptr->zf_offsetset, object, NULL, offset, value);
440
0
    return;
441
0
  }
442
443
538
  spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
444
538
  spl_fixedarray_object_write_dimension_helper(intern, offset, value);
445
538
}
446
447
static void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object *intern, zval *offset)
448
0
{
449
0
  zend_ulong index = spl_offset_convert_to_ulong(offset);
450
0
  if (UNEXPECTED(EG(exception))) {
451
0
    return;
452
0
  }
453
454
0
  if (UNEXPECTED(index >= intern->array.size)) {
455
0
    zend_throw_exception(spl_ce_OutOfBoundsException, "Index invalid or out of range", 0);
456
0
  } else {
457
0
    zval null = {0};
458
0
    ZVAL_NULL(&null);
459
0
    zend_safe_assign_to_variable_noref(&intern->array.elements[index], &null);
460
0
  }
461
0
}
462
463
static void spl_fixedarray_object_unset_dimension(zend_object *object, zval *offset)
464
0
{
465
0
  if (UNEXPECTED(HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetunset))) {
466
0
    zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetunset, object, NULL, offset);
467
0
    return;
468
0
  }
469
470
0
  spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
471
0
  spl_fixedarray_object_unset_dimension_helper(intern, offset);
472
0
}
473
474
static bool spl_fixedarray_object_has_dimension_helper(spl_fixedarray_object *intern, zval *offset, bool check_empty)
475
0
{
476
0
  zend_ulong index = spl_offset_convert_to_ulong(offset);
477
0
  if (UNEXPECTED(EG(exception))) {
478
0
    return false;
479
0
  }
480
481
0
  if (index >= intern->array.size) {
482
0
    return false;
483
0
  }
484
485
0
  if (check_empty) {
486
0
    return zend_is_true(&intern->array.elements[index]);
487
0
  }
488
489
0
  return Z_TYPE(intern->array.elements[index]) != IS_NULL;
490
0
}
491
492
static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset, int check_empty)
493
0
{
494
0
  if (HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetexists)) {
495
0
    zval rv;
496
497
0
    zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetexists, object, &rv, offset);
498
0
    bool result = zend_is_true(&rv);
499
0
    zval_ptr_dtor(&rv);
500
0
    return result;
501
0
  }
502
503
0
  spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
504
505
0
  return spl_fixedarray_object_has_dimension_helper(intern, offset, check_empty);
506
0
}
507
508
static zend_result spl_fixedarray_object_count_elements(zend_object *object, zend_long *count)
509
0
{
510
0
  spl_fixedarray_object *intern;
511
512
0
  intern = spl_fixed_array_from_obj(object);
513
0
  if (UNEXPECTED(intern->fptr_count)) {
514
0
    zval rv;
515
0
    zend_call_known_instance_method_with_0_params(intern->fptr_count, object, &rv);
516
0
    if (!Z_ISUNDEF(rv)) {
517
0
      *count = zval_get_long(&rv);
518
0
      zval_ptr_dtor(&rv);
519
0
    } else {
520
0
      *count = 0;
521
0
    }
522
0
  } else {
523
0
    *count = intern->array.size;
524
0
  }
525
0
  return SUCCESS;
526
0
}
527
528
PHP_METHOD(SplFixedArray, __construct)
529
781
{
530
781
  zval *object = ZEND_THIS;
531
781
  spl_fixedarray_object *intern;
532
781
  zend_long size = 0;
533
534
781
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &size) == FAILURE) {
535
7
    RETURN_THROWS();
536
7
  }
537
538
774
  if (size < 0) {
539
1
    zend_argument_value_error(1, "must be greater than or equal to 0");
540
1
    RETURN_THROWS();
541
1
  }
542
543
773
  intern = Z_SPLFIXEDARRAY_P(object);
544
545
773
  if (!spl_fixedarray_empty(&intern->array)) {
546
    /* called __construct() twice, bail out */
547
0
    return;
548
0
  }
549
550
773
  spl_fixedarray_init(&intern->array, size);
551
773
}
552
553
PHP_METHOD(SplFixedArray, __wakeup)
554
0
{
555
0
  spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
556
0
  HashTable *intern_ht = zend_std_get_properties(Z_OBJ_P(ZEND_THIS));
557
0
  zval *data;
558
559
0
  ZEND_PARSE_PARAMETERS_NONE();
560
561
0
  if (intern->array.size == 0) {
562
0
    int index = 0;
563
0
    int size = zend_hash_num_elements(intern_ht);
564
565
0
    spl_fixedarray_init(&intern->array, size);
566
567
0
    ZEND_HASH_FOREACH_VAL(intern_ht, data) {
568
0
      ZVAL_COPY(&intern->array.elements[index], data);
569
0
      index++;
570
0
    } ZEND_HASH_FOREACH_END();
571
572
    /* Remove the unserialised properties, since we now have the elements
573
     * within the spl_fixedarray_object structure. */
574
0
    zend_hash_clean(intern_ht);
575
0
  }
576
0
}
577
578
PHP_METHOD(SplFixedArray, __serialize)
579
50
{
580
50
  spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
581
50
  zval *current;
582
50
  zend_string *key;
583
584
50
  ZEND_PARSE_PARAMETERS_NONE();
585
586
50
  HashTable *ht = zend_std_get_properties(&intern->std);
587
50
  uint32_t num_properties = zend_hash_num_elements(ht);
588
50
  array_init_size(return_value, intern->array.size + num_properties);
589
590
  /* elements */
591
134
  for (zend_long i = 0; i < intern->array.size; i++) {
592
84
    current = &intern->array.elements[i];
593
84
    zend_hash_next_index_insert(Z_ARRVAL_P(return_value), current);
594
84
    Z_TRY_ADDREF_P(current);
595
84
  }
596
597
  /* members */
598
50
  ZEND_HASH_FOREACH_STR_KEY_VAL_IND(ht, key, current) {
599
    /* If the properties table was already rebuild, it will also contain the
600
     * array elements. The array elements are already added in the above loop.
601
     * We can detect array elements by the fact that their key == NULL. */
602
50
    if (key != NULL) {
603
0
      zend_hash_add_new(Z_ARRVAL_P(return_value), key, current);
604
0
      Z_TRY_ADDREF_P(current);
605
0
    }
606
50
  } ZEND_HASH_FOREACH_END();
607
50
}
608
609
PHP_METHOD(SplFixedArray, __unserialize)
610
5.50k
{
611
5.50k
  spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
612
5.50k
  HashTable *data;
613
5.50k
  zval members_zv, *elem;
614
5.50k
  zend_string *key;
615
5.50k
  zend_long size;
616
617
5.50k
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &data) == FAILURE) {
618
0
    RETURN_THROWS();
619
0
  }
620
621
5.50k
  if (intern->array.size == 0) {
622
5.50k
    size = zend_hash_num_elements(data);
623
5.50k
    spl_fixedarray_init_non_empty_struct(&intern->array, size);
624
5.50k
    if (!size) {
625
1
      return;
626
1
    }
627
5.50k
    array_init(&members_zv);
628
629
5.50k
    intern->array.size = 0;
630
38.2k
    ZEND_HASH_FOREACH_STR_KEY_VAL(data, key, elem) {
631
38.2k
      if (key == NULL) {
632
11.1k
        ZVAL_COPY_DEREF(&intern->array.elements[intern->array.size], elem);
633
11.1k
        intern->array.size++;
634
11.1k
      } else {
635
5.26k
        Z_TRY_ADDREF_P(elem);
636
5.26k
        zend_hash_add(Z_ARRVAL(members_zv), key, elem);
637
5.26k
      }
638
38.2k
    } ZEND_HASH_FOREACH_END();
639
640
5.50k
    if (intern->array.size != size) {
641
2.63k
      if (intern->array.size) {
642
2
        intern->array.elements = erealloc(intern->array.elements, sizeof(zval) * intern->array.size);
643
2.63k
      } else {
644
2.63k
        efree(intern->array.elements);
645
2.63k
        intern->array.elements = NULL;
646
2.63k
      }
647
2.63k
    }
648
649
5.50k
    object_properties_load(&intern->std, Z_ARRVAL(members_zv));
650
5.50k
    zval_ptr_dtor(&members_zv);
651
5.50k
  }
652
5.50k
}
653
654
PHP_METHOD(SplFixedArray, count)
655
0
{
656
0
  zval *object = ZEND_THIS;
657
0
  spl_fixedarray_object *intern;
658
659
0
  ZEND_PARSE_PARAMETERS_NONE();
660
661
0
  intern = Z_SPLFIXEDARRAY_P(object);
662
0
  RETURN_LONG(intern->array.size);
663
0
}
664
665
PHP_METHOD(SplFixedArray, toArray)
666
0
{
667
0
  spl_fixedarray_object *intern;
668
669
0
  ZEND_PARSE_PARAMETERS_NONE();
670
671
0
  intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
672
673
0
  if (!spl_fixedarray_empty(&intern->array)) {
674
0
    array_init_size(return_value, intern->array.size);
675
0
    HashTable *ht = Z_ARRVAL_P(return_value);
676
0
    zend_hash_real_init_packed(ht);
677
678
0
    ZEND_HASH_FILL_PACKED(ht) {
679
0
      for (zend_long i = 0; i < intern->array.size; i++) {
680
0
        ZEND_HASH_FILL_ADD(&intern->array.elements[i]);
681
0
        Z_TRY_ADDREF(intern->array.elements[i]);
682
0
      }
683
0
    } ZEND_HASH_FILL_END();
684
0
  } else {
685
0
    RETURN_EMPTY_ARRAY();
686
0
  }
687
0
}
688
689
PHP_METHOD(SplFixedArray, fromArray)
690
245
{
691
245
  zval *data;
692
245
  spl_fixedarray array;
693
245
  spl_fixedarray_object *intern;
694
245
  int num;
695
245
  bool save_indexes = true;
696
697
245
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|b", &data, &save_indexes) == FAILURE) {
698
3
    RETURN_THROWS();
699
3
  }
700
701
242
  num = zend_hash_num_elements(Z_ARRVAL_P(data));
702
703
242
  if (num > 0 && save_indexes) {
704
242
    zval *element;
705
242
    zend_string *str_index;
706
242
    zend_ulong num_index, max_index = 0;
707
242
    zend_long tmp;
708
709
242
    if (HT_IS_PACKED(Z_ARRVAL_P(data))) {
710
      /* If there are no holes, then nNumUsed is the number of elements.
711
       * If there are holes, then nNumUsed is the index of the last element. */
712
242
      tmp = Z_ARRVAL_P(data)->nNumUsed;
713
242
    } else {
714
0
      ZEND_HASH_MAP_FOREACH_KEY(Z_ARRVAL_P(data), num_index, str_index) {
715
0
        if (str_index != NULL || (zend_long)num_index < 0) {
716
0
          zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "array must contain only positive integer keys");
717
0
          RETURN_THROWS();
718
0
        }
719
720
0
        if (num_index > max_index) {
721
0
          max_index = num_index;
722
0
        }
723
0
      } ZEND_HASH_FOREACH_END();
724
725
0
      tmp = max_index + 1;
726
0
      if (tmp <= 0) {
727
0
        zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "integer overflow detected");
728
0
        RETURN_THROWS();
729
0
      }
730
0
    }
731
732
242
    spl_fixedarray_init(&array, tmp);
733
734
10.0k
    ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(data), num_index, element) {
735
10.0k
      ZVAL_COPY_DEREF(&array.elements[num_index], element);
736
10.0k
    } ZEND_HASH_FOREACH_END();
737
738
242
  } else if (num > 0 && !save_indexes) {
739
0
    zval *element;
740
0
    zend_long i = 0;
741
742
0
    spl_fixedarray_init(&array, num);
743
744
0
    ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(data), element) {
745
0
      ZVAL_COPY_DEREF(&array.elements[i], element);
746
0
      i++;
747
0
    } ZEND_HASH_FOREACH_END();
748
0
  } else {
749
0
    spl_fixedarray_init(&array, 0);
750
0
  }
751
752
242
  object_init_ex(return_value, spl_ce_SplFixedArray);
753
754
242
  intern = Z_SPLFIXEDARRAY_P(return_value);
755
242
  intern->array = array;
756
242
}
757
758
PHP_METHOD(SplFixedArray, getSize)
759
0
{
760
0
  zval *object = ZEND_THIS;
761
0
  spl_fixedarray_object *intern;
762
763
0
  ZEND_PARSE_PARAMETERS_NONE();
764
765
0
  intern = Z_SPLFIXEDARRAY_P(object);
766
0
  RETURN_LONG(intern->array.size);
767
0
}
768
769
PHP_METHOD(SplFixedArray, setSize)
770
0
{
771
0
  zval *object = ZEND_THIS;
772
0
  spl_fixedarray_object *intern;
773
0
  zend_long size;
774
775
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &size) == FAILURE) {
776
0
    RETURN_THROWS();
777
0
  }
778
779
0
  if (size < 0) {
780
0
    zend_argument_value_error(1, "must be greater than or equal to 0");
781
0
    RETURN_THROWS();
782
0
  }
783
784
0
  intern = Z_SPLFIXEDARRAY_P(object);
785
786
0
  spl_fixedarray_resize(&intern->array, size);
787
0
  RETURN_TRUE;
788
0
}
789
790
/* Returns whether the requested $index exists. */
791
PHP_METHOD(SplFixedArray, offsetExists)
792
0
{
793
0
  zval                  *zindex;
794
0
  spl_fixedarray_object  *intern;
795
796
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
797
0
    RETURN_THROWS();
798
0
  }
799
800
0
  intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
801
802
0
  RETURN_BOOL(spl_fixedarray_object_has_dimension_helper(intern, zindex, false));
803
0
}
804
805
/* Returns the value at the specified $index. */
806
PHP_METHOD(SplFixedArray, offsetGet)
807
0
{
808
0
  zval *zindex, *value;
809
0
  spl_fixedarray_object  *intern;
810
811
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
812
0
    RETURN_THROWS();
813
0
  }
814
815
0
  intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
816
0
  value = spl_fixedarray_object_read_dimension_helper(intern, zindex);
817
818
0
  if (value) {
819
0
    RETURN_COPY(value);
820
0
  } else {
821
0
    RETURN_NULL();
822
0
  }
823
0
}
824
825
/* Sets the value at the specified $index to $newval. */
826
PHP_METHOD(SplFixedArray, offsetSet)
827
0
{
828
0
  zval                  *zindex, *value;
829
0
  spl_fixedarray_object  *intern;
830
831
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &zindex, &value) == FAILURE) {
832
0
    RETURN_THROWS();
833
0
  }
834
835
0
  intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
836
0
  spl_fixedarray_object_write_dimension_helper(intern, zindex, value);
837
838
0
}
839
840
/* Unsets the value at the specified $index. */
841
PHP_METHOD(SplFixedArray, offsetUnset)
842
0
{
843
0
  zval                  *zindex;
844
0
  spl_fixedarray_object  *intern;
845
846
0
  if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
847
0
    RETURN_THROWS();
848
0
  }
849
850
0
  intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
851
0
  spl_fixedarray_object_unset_dimension_helper(intern, zindex);
852
853
0
}
854
855
/* Create a new iterator from a SplFixedArray instance. */
856
PHP_METHOD(SplFixedArray, getIterator)
857
0
{
858
0
  ZEND_PARSE_PARAMETERS_NONE();
859
860
0
  zend_create_internal_iterator_zval(return_value, ZEND_THIS);
861
0
}
862
863
static void spl_fixedarray_it_dtor(zend_object_iterator *iter)
864
0
{
865
0
  zval_ptr_dtor(&iter->data);
866
0
}
867
868
static void spl_fixedarray_it_rewind(zend_object_iterator *iter)
869
0
{
870
0
  ((spl_fixedarray_it*)iter)->current = 0;
871
0
}
872
873
static zend_result spl_fixedarray_it_valid(zend_object_iterator *iter)
874
0
{
875
0
  spl_fixedarray_it     *iterator = (spl_fixedarray_it*)iter;
876
0
  spl_fixedarray_object *object   = Z_SPLFIXEDARRAY_P(&iter->data);
877
878
0
  if (iterator->current >= 0 && iterator->current < object->array.size) {
879
0
    return SUCCESS;
880
0
  }
881
882
0
  return FAILURE;
883
0
}
884
885
static zval *spl_fixedarray_it_get_current_data(zend_object_iterator *iter)
886
0
{
887
0
  zval zindex, *data;
888
0
  spl_fixedarray_it     *iterator = (spl_fixedarray_it*)iter;
889
0
  spl_fixedarray_object *object   = Z_SPLFIXEDARRAY_P(&iter->data);
890
891
0
  ZVAL_LONG(&zindex, iterator->current);
892
0
  data = spl_fixedarray_object_read_dimension_helper(object, &zindex);
893
894
0
  if (data == NULL) {
895
0
    data = &EG(uninitialized_zval);
896
0
  }
897
0
  return data;
898
0
}
899
900
static void spl_fixedarray_it_get_current_key(zend_object_iterator *iter, zval *key)
901
0
{
902
0
  ZVAL_LONG(key, ((spl_fixedarray_it*)iter)->current);
903
0
}
904
905
static void spl_fixedarray_it_move_forward(zend_object_iterator *iter)
906
0
{
907
0
  ((spl_fixedarray_it*)iter)->current++;
908
0
}
909
910
/* iterator handler table */
911
static const zend_object_iterator_funcs spl_fixedarray_it_funcs = {
912
  spl_fixedarray_it_dtor,
913
  spl_fixedarray_it_valid,
914
  spl_fixedarray_it_get_current_data,
915
  spl_fixedarray_it_get_current_key,
916
  spl_fixedarray_it_move_forward,
917
  spl_fixedarray_it_rewind,
918
  NULL,
919
  NULL, /* get_gc */
920
};
921
922
static zend_object_iterator *spl_fixedarray_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
923
0
{
924
0
  spl_fixedarray_it *iterator;
925
926
0
  if (by_ref) {
927
0
    zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
928
0
    return NULL;
929
0
  }
930
931
0
  iterator = emalloc(sizeof(spl_fixedarray_it));
932
933
0
  zend_iterator_init((zend_object_iterator*)iterator);
934
935
0
  ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
936
0
  iterator->intern.funcs = &spl_fixedarray_it_funcs;
937
938
0
  return &iterator->intern;
939
0
}
940
941
PHP_MINIT_FUNCTION(spl_fixedarray)
942
16
{
943
16
  spl_ce_SplFixedArray = register_class_SplFixedArray(
944
16
    zend_ce_aggregate, zend_ce_arrayaccess, zend_ce_countable, php_json_serializable_ce);
945
16
  spl_ce_SplFixedArray->create_object = spl_fixedarray_new;
946
16
  spl_ce_SplFixedArray->default_object_handlers = &spl_handler_SplFixedArray;
947
16
  spl_ce_SplFixedArray->get_iterator = spl_fixedarray_get_iterator;
948
949
16
  memcpy(&spl_handler_SplFixedArray, &std_object_handlers, sizeof(zend_object_handlers));
950
951
16
  spl_handler_SplFixedArray.offset          = offsetof(spl_fixedarray_object, std);
952
16
  spl_handler_SplFixedArray.clone_obj       = spl_fixedarray_object_clone;
953
16
  spl_handler_SplFixedArray.read_dimension  = spl_fixedarray_object_read_dimension;
954
16
  spl_handler_SplFixedArray.write_dimension = spl_fixedarray_object_write_dimension;
955
16
  spl_handler_SplFixedArray.unset_dimension = spl_fixedarray_object_unset_dimension;
956
16
  spl_handler_SplFixedArray.has_dimension   = spl_fixedarray_object_has_dimension;
957
16
  spl_handler_SplFixedArray.count_elements  = spl_fixedarray_object_count_elements;
958
16
  spl_handler_SplFixedArray.get_properties_for = spl_fixedarray_object_get_properties_for;
959
16
  spl_handler_SplFixedArray.get_gc          = spl_fixedarray_object_get_gc;
960
16
  spl_handler_SplFixedArray.free_obj        = spl_fixedarray_object_free_storage;
961
962
16
  return SUCCESS;
963
16
}