Coverage Report

Created: 2025-06-13 06:43

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