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 | http://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 "php_ini.h"
24 #include "ext/standard/info.h"
25 #include "zend_exceptions.h"
26
27 #include "php_spl.h"
28 #include "spl_fixedarray_arginfo.h"
29 #include "spl_functions.h"
30 #include "spl_engine.h"
31 #include "spl_fixedarray.h"
32 #include "spl_exceptions.h"
33 #include "spl_iterators.h"
34
35 zend_object_handlers spl_handler_SplFixedArray;
36 PHPAPI zend_class_entry *spl_ce_SplFixedArray;
37
38 #ifdef COMPILE_DL_SPL_FIXEDARRAY
39 ZEND_GET_MODULE(spl_fixedarray)
40 #endif
41
42 typedef struct _spl_fixedarray {
43 zend_long size;
44 zval *elements;
45 /* True if this was modified after the last call to get_properties or the hash table wasn't rebuilt. */
46 bool should_rebuild_properties;
47 } spl_fixedarray;
48
49 typedef struct _spl_fixedarray_object {
50 spl_fixedarray array;
51 zend_function *fptr_offset_get;
52 zend_function *fptr_offset_set;
53 zend_function *fptr_offset_has;
54 zend_function *fptr_offset_del;
55 zend_function *fptr_count;
56 zend_object std;
57 } spl_fixedarray_object;
58
59 typedef struct _spl_fixedarray_it {
60 zend_object_iterator intern;
61 zend_long current;
62 } spl_fixedarray_it;
63
spl_fixed_array_from_obj(zend_object * obj)64 static spl_fixedarray_object *spl_fixed_array_from_obj(zend_object *obj)
65 {
66 return (spl_fixedarray_object*)((char*)(obj) - XtOffsetOf(spl_fixedarray_object, std));
67 }
68
69 #define Z_SPLFIXEDARRAY_P(zv) spl_fixed_array_from_obj(Z_OBJ_P((zv)))
70
71 /* Helps enforce the invariants in debug mode:
72 * - if size == 0, then elements == NULL
73 * - if size > 0, then elements != NULL
74 * - size is not less than 0
75 */
spl_fixedarray_empty(spl_fixedarray * array)76 static bool spl_fixedarray_empty(spl_fixedarray *array)
77 {
78 if (array->elements) {
79 ZEND_ASSERT(array->size > 0);
80 return false;
81 }
82 ZEND_ASSERT(array->size == 0);
83 return true;
84 }
85
spl_fixedarray_default_ctor(spl_fixedarray * array)86 static void spl_fixedarray_default_ctor(spl_fixedarray *array)
87 {
88 array->size = 0;
89 array->elements = NULL;
90 }
91
92 /* Initializes the range [from, to) to null. Does not dtor existing elements. */
spl_fixedarray_init_elems(spl_fixedarray * array,zend_long from,zend_long to)93 static void spl_fixedarray_init_elems(spl_fixedarray *array, zend_long from, zend_long to)
94 {
95 ZEND_ASSERT(from <= to);
96 zval *begin = array->elements + from, *end = array->elements + to;
97
98 while (begin != end) {
99 ZVAL_NULL(begin++);
100 }
101 }
102
spl_fixedarray_init(spl_fixedarray * array,zend_long size)103 static void spl_fixedarray_init(spl_fixedarray *array, zend_long size)
104 {
105 if (size > 0) {
106 array->size = 0; /* reset size in case ecalloc() fails */
107 array->elements = safe_emalloc(size, sizeof(zval), 0);
108 array->size = size;
109 array->should_rebuild_properties = true;
110 spl_fixedarray_init_elems(array, 0, size);
111 } else {
112 spl_fixedarray_default_ctor(array);
113 }
114 }
115
116 /* Copies the range [begin, end) into the fixedarray, beginning at `offset`.
117 * Does not dtor the existing elements.
118 */
spl_fixedarray_copy_range(spl_fixedarray * array,zend_long offset,zval * begin,zval * end)119 static void spl_fixedarray_copy_range(spl_fixedarray *array, zend_long offset, zval *begin, zval *end)
120 {
121 ZEND_ASSERT(offset >= 0);
122 ZEND_ASSERT(array->size - offset >= end - begin);
123
124 zval *to = &array->elements[offset];
125 while (begin != end) {
126 ZVAL_COPY(to++, begin++);
127 }
128 }
129
spl_fixedarray_copy_ctor(spl_fixedarray * to,spl_fixedarray * from)130 static void spl_fixedarray_copy_ctor(spl_fixedarray *to, spl_fixedarray *from)
131 {
132 zend_long size = from->size;
133 spl_fixedarray_init(to, size);
134
135 zval *begin = from->elements, *end = from->elements + size;
136 spl_fixedarray_copy_range(to, 0, begin, end);
137 }
138
139 /* Destructs the elements in the range [from, to).
140 * Caller is expected to bounds check.
141 */
spl_fixedarray_dtor_range(spl_fixedarray * array,zend_long from,zend_long to)142 static void spl_fixedarray_dtor_range(spl_fixedarray *array, zend_long from, zend_long to)
143 {
144 zval *begin = array->elements + from, *end = array->elements + to;
145 while (begin != end) {
146 zval_ptr_dtor(begin++);
147 }
148 }
149
150 /* Destructs and frees contents but not the array itself.
151 * If you want to re-use the array then you need to re-initialize it.
152 */
spl_fixedarray_dtor(spl_fixedarray * array)153 static void spl_fixedarray_dtor(spl_fixedarray *array)
154 {
155 if (!spl_fixedarray_empty(array)) {
156 zval *begin = array->elements, *end = array->elements + array->size;
157 array->elements = NULL;
158 array->size = 0;
159 while (begin != end) {
160 zval_ptr_dtor(--end);
161 }
162 efree(begin);
163 }
164 }
165
spl_fixedarray_resize(spl_fixedarray * array,zend_long size)166 static void spl_fixedarray_resize(spl_fixedarray *array, zend_long size)
167 {
168 if (size == array->size) {
169 /* nothing to do */
170 return;
171 }
172 array->should_rebuild_properties = true;
173
174 /* first initialization */
175 if (array->size == 0) {
176 spl_fixedarray_init(array, size);
177 return;
178 }
179
180 /* clearing the array */
181 if (size == 0) {
182 spl_fixedarray_dtor(array);
183 array->elements = NULL;
184 } else if (size > array->size) {
185 array->elements = safe_erealloc(array->elements, size, sizeof(zval), 0);
186 spl_fixedarray_init_elems(array, array->size, size);
187 } else { /* size < array->size */
188 spl_fixedarray_dtor_range(array, size, array->size);
189 array->elements = erealloc(array->elements, sizeof(zval) * size);
190 }
191
192 array->size = size;
193 }
194
spl_fixedarray_object_get_gc(zend_object * obj,zval ** table,int * n)195 static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, int *n)
196 {
197 spl_fixedarray_object *intern = spl_fixed_array_from_obj(obj);
198 HashTable *ht = zend_std_get_properties(obj);
199
200 *table = intern->array.elements;
201 *n = (int)intern->array.size;
202
203 return ht;
204 }
205
spl_fixedarray_object_get_properties(zend_object * obj)206 static HashTable* spl_fixedarray_object_get_properties(zend_object *obj)
207 {
208 spl_fixedarray_object *intern = spl_fixed_array_from_obj(obj);
209 HashTable *ht = zend_std_get_properties(obj);
210
211 if (!spl_fixedarray_empty(&intern->array)) {
212 /*
213 * Usually, the reference count of the hash table is 1,
214 * except during cyclic reference cycles.
215 *
216 * Maintain the DEBUG invariant that a hash table isn't modified during iteration,
217 * and avoid unnecessary work rebuilding a hash table for unmodified properties.
218 *
219 * See https://github.com/php/php-src/issues/8079 and ext/spl/tests/fixedarray_022.phpt
220 * Also see https://github.com/php/php-src/issues/8044 for alternate considered approaches.
221 */
222 if (!intern->array.should_rebuild_properties) {
223 /* Return the same hash table so that recursion cycle detection works in internal functions. */
224 return ht;
225 }
226 intern->array.should_rebuild_properties = false;
227
228 zend_long j = zend_hash_num_elements(ht);
229
230 if (GC_REFCOUNT(ht) > 1) {
231 intern->std.properties = zend_array_dup(ht);
232 if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
233 GC_DELREF(ht);
234 }
235 }
236 for (zend_long i = 0; i < intern->array.size; i++) {
237 zend_hash_index_update(ht, i, &intern->array.elements[i]);
238 Z_TRY_ADDREF(intern->array.elements[i]);
239 }
240 if (j > intern->array.size) {
241 for (zend_long i = intern->array.size; i < j; ++i) {
242 zend_hash_index_del(ht, i);
243 }
244 }
245 }
246
247 return ht;
248 }
249
spl_fixedarray_object_free_storage(zend_object * object)250 static void spl_fixedarray_object_free_storage(zend_object *object)
251 {
252 spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
253 spl_fixedarray_dtor(&intern->array);
254 zend_object_std_dtor(&intern->std);
255 }
256
spl_fixedarray_object_new_ex(zend_class_entry * class_type,zend_object * orig,bool clone_orig)257 static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, zend_object *orig, bool clone_orig)
258 {
259 spl_fixedarray_object *intern;
260 zend_class_entry *parent = class_type;
261 bool inherited = false;
262
263 intern = zend_object_alloc(sizeof(spl_fixedarray_object), parent);
264
265 zend_object_std_init(&intern->std, class_type);
266 object_properties_init(&intern->std, class_type);
267
268 if (orig && clone_orig) {
269 spl_fixedarray_object *other = spl_fixed_array_from_obj(orig);
270 spl_fixedarray_copy_ctor(&intern->array, &other->array);
271 }
272
273 while (parent) {
274 if (parent == spl_ce_SplFixedArray) {
275 intern->std.handlers = &spl_handler_SplFixedArray;
276 break;
277 }
278
279 parent = parent->parent;
280 inherited = true;
281 }
282
283 ZEND_ASSERT(parent);
284
285 if (inherited) {
286 intern->fptr_offset_get = zend_hash_str_find_ptr(&class_type->function_table, "offsetget", sizeof("offsetget") - 1);
287 if (intern->fptr_offset_get->common.scope == parent) {
288 intern->fptr_offset_get = NULL;
289 }
290 intern->fptr_offset_set = zend_hash_str_find_ptr(&class_type->function_table, "offsetset", sizeof("offsetset") - 1);
291 if (intern->fptr_offset_set->common.scope == parent) {
292 intern->fptr_offset_set = NULL;
293 }
294 intern->fptr_offset_has = zend_hash_str_find_ptr(&class_type->function_table, "offsetexists", sizeof("offsetexists") - 1);
295 if (intern->fptr_offset_has->common.scope == parent) {
296 intern->fptr_offset_has = NULL;
297 }
298 intern->fptr_offset_del = zend_hash_str_find_ptr(&class_type->function_table, "offsetunset", sizeof("offsetunset") - 1);
299 if (intern->fptr_offset_del->common.scope == parent) {
300 intern->fptr_offset_del = NULL;
301 }
302 intern->fptr_count = zend_hash_str_find_ptr(&class_type->function_table, "count", sizeof("count") - 1);
303 if (intern->fptr_count->common.scope == parent) {
304 intern->fptr_count = NULL;
305 }
306 }
307
308 return &intern->std;
309 }
310
spl_fixedarray_new(zend_class_entry * class_type)311 static zend_object *spl_fixedarray_new(zend_class_entry *class_type)
312 {
313 return spl_fixedarray_object_new_ex(class_type, NULL, 0);
314 }
315
spl_fixedarray_object_clone(zend_object * old_object)316 static zend_object *spl_fixedarray_object_clone(zend_object *old_object)
317 {
318 zend_object *new_object = spl_fixedarray_object_new_ex(old_object->ce, old_object, 1);
319
320 zend_objects_clone_members(new_object, old_object);
321
322 return new_object;
323 }
324
spl_fixedarray_object_read_dimension_helper(spl_fixedarray_object * intern,zval * offset)325 static zval *spl_fixedarray_object_read_dimension_helper(spl_fixedarray_object *intern, zval *offset)
326 {
327 zend_long index;
328
329 /* we have to return NULL on error here to avoid memleak because of
330 * ZE duplicating uninitialized_zval_ptr */
331 if (!offset) {
332 zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
333 return NULL;
334 }
335
336 if (Z_TYPE_P(offset) != IS_LONG) {
337 index = spl_offset_convert_to_long(offset);
338 } else {
339 index = Z_LVAL_P(offset);
340 }
341
342 if (index < 0 || index >= intern->array.size) {
343 zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
344 return NULL;
345 } else {
346 return &intern->array.elements[index];
347 }
348 }
349
350 static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset, int check_empty);
351
spl_fixedarray_object_read_dimension(zend_object * object,zval * offset,int type,zval * rv)352 static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
353 {
354 spl_fixedarray_object *intern;
355
356 intern = spl_fixed_array_from_obj(object);
357
358 if (type == BP_VAR_IS && !spl_fixedarray_object_has_dimension(object, offset, 0)) {
359 return &EG(uninitialized_zval);
360 }
361
362 if (intern->fptr_offset_get) {
363 zval tmp;
364 if (!offset) {
365 ZVAL_NULL(&tmp);
366 offset = &tmp;
367 } else {
368 SEPARATE_ARG_IF_REF(offset);
369 }
370 zend_call_method_with_1_params(object, intern->std.ce, &intern->fptr_offset_get, "offsetGet", rv, offset);
371 zval_ptr_dtor(offset);
372 if (!Z_ISUNDEF_P(rv)) {
373 return rv;
374 }
375 return &EG(uninitialized_zval);
376 }
377 if (type != BP_VAR_IS && type != BP_VAR_R) {
378 intern->array.should_rebuild_properties = true;
379 }
380
381 return spl_fixedarray_object_read_dimension_helper(intern, offset);
382 }
383
spl_fixedarray_object_write_dimension_helper(spl_fixedarray_object * intern,zval * offset,zval * value)384 static void spl_fixedarray_object_write_dimension_helper(spl_fixedarray_object *intern, zval *offset, zval *value)
385 {
386 zend_long index;
387
388 if (!offset) {
389 /* '$array[] = value' syntax is not supported */
390 zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
391 return;
392 }
393
394 if (Z_TYPE_P(offset) != IS_LONG) {
395 index = spl_offset_convert_to_long(offset);
396 } else {
397 index = Z_LVAL_P(offset);
398 }
399
400 if (index < 0 || index >= intern->array.size) {
401 zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
402 return;
403 } else {
404 intern->array.should_rebuild_properties = true;
405 /* Fix #81429 */
406 zval *ptr = &(intern->array.elements[index]);
407 zval tmp;
408 ZVAL_COPY_VALUE(&tmp, ptr);
409 ZVAL_COPY_DEREF(ptr, value);
410 zval_ptr_dtor(&tmp);
411 }
412 }
413
spl_fixedarray_object_write_dimension(zend_object * object,zval * offset,zval * value)414 static void spl_fixedarray_object_write_dimension(zend_object *object, zval *offset, zval *value)
415 {
416 spl_fixedarray_object *intern;
417 zval tmp;
418
419 intern = spl_fixed_array_from_obj(object);
420
421 if (intern->fptr_offset_set) {
422 if (!offset) {
423 ZVAL_NULL(&tmp);
424 offset = &tmp;
425 } else {
426 SEPARATE_ARG_IF_REF(offset);
427 }
428 SEPARATE_ARG_IF_REF(value);
429 zend_call_method_with_2_params(object, intern->std.ce, &intern->fptr_offset_set, "offsetSet", NULL, offset, value);
430 zval_ptr_dtor(value);
431 zval_ptr_dtor(offset);
432 return;
433 }
434
435 spl_fixedarray_object_write_dimension_helper(intern, offset, value);
436 }
437
spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object * intern,zval * offset)438 static void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object *intern, zval *offset)
439 {
440 zend_long index;
441
442 if (Z_TYPE_P(offset) != IS_LONG) {
443 index = spl_offset_convert_to_long(offset);
444 } else {
445 index = Z_LVAL_P(offset);
446 }
447
448 if (index < 0 || index >= intern->array.size) {
449 zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
450 return;
451 } else {
452 intern->array.should_rebuild_properties = true;
453 zval_ptr_dtor(&(intern->array.elements[index]));
454 ZVAL_NULL(&intern->array.elements[index]);
455 }
456 }
457
spl_fixedarray_object_unset_dimension(zend_object * object,zval * offset)458 static void spl_fixedarray_object_unset_dimension(zend_object *object, zval *offset)
459 {
460 spl_fixedarray_object *intern;
461
462 intern = spl_fixed_array_from_obj(object);
463
464 if (intern->fptr_offset_del) {
465 SEPARATE_ARG_IF_REF(offset);
466 zend_call_method_with_1_params(object, intern->std.ce, &intern->fptr_offset_del, "offsetUnset", NULL, offset);
467 zval_ptr_dtor(offset);
468 return;
469 }
470
471 spl_fixedarray_object_unset_dimension_helper(intern, offset);
472 }
473
spl_fixedarray_object_has_dimension_helper(spl_fixedarray_object * intern,zval * offset,int check_empty)474 static int spl_fixedarray_object_has_dimension_helper(spl_fixedarray_object *intern, zval *offset, int check_empty)
475 {
476 zend_long index;
477 int retval;
478
479 if (Z_TYPE_P(offset) != IS_LONG) {
480 index = spl_offset_convert_to_long(offset);
481 } else {
482 index = Z_LVAL_P(offset);
483 }
484
485 if (index < 0 || index >= intern->array.size) {
486 retval = 0;
487 } else {
488 if (check_empty) {
489 retval = zend_is_true(&intern->array.elements[index]);
490 } else {
491 retval = Z_TYPE(intern->array.elements[index]) != IS_NULL;
492 }
493 }
494
495 return retval;
496 }
497
spl_fixedarray_object_has_dimension(zend_object * object,zval * offset,int check_empty)498 static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset, int check_empty)
499 {
500 spl_fixedarray_object *intern;
501
502 intern = spl_fixed_array_from_obj(object);
503
504 if (intern->fptr_offset_has) {
505 zval rv;
506 zend_bool result;
507
508 SEPARATE_ARG_IF_REF(offset);
509 zend_call_method_with_1_params(object, intern->std.ce, &intern->fptr_offset_has, "offsetExists", &rv, offset);
510 zval_ptr_dtor(offset);
511 result = zend_is_true(&rv);
512 zval_ptr_dtor(&rv);
513 return result;
514 }
515
516 return spl_fixedarray_object_has_dimension_helper(intern, offset, check_empty);
517 }
518
spl_fixedarray_object_count_elements(zend_object * object,zend_long * count)519 static int spl_fixedarray_object_count_elements(zend_object *object, zend_long *count)
520 {
521 spl_fixedarray_object *intern;
522
523 intern = spl_fixed_array_from_obj(object);
524 if (intern->fptr_count) {
525 zval rv;
526 zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv);
527 if (!Z_ISUNDEF(rv)) {
528 *count = zval_get_long(&rv);
529 zval_ptr_dtor(&rv);
530 } else {
531 *count = 0;
532 }
533 } else {
534 *count = intern->array.size;
535 }
536 return SUCCESS;
537 }
538
PHP_METHOD(SplFixedArray,__construct)539 PHP_METHOD(SplFixedArray, __construct)
540 {
541 zval *object = ZEND_THIS;
542 spl_fixedarray_object *intern;
543 zend_long size = 0;
544
545 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &size) == FAILURE) {
546 RETURN_THROWS();
547 }
548
549 if (size < 0) {
550 zend_argument_value_error(1, "must be greater than or equal to 0");
551 RETURN_THROWS();
552 }
553
554 intern = Z_SPLFIXEDARRAY_P(object);
555
556 if (!spl_fixedarray_empty(&intern->array)) {
557 /* called __construct() twice, bail out */
558 return;
559 }
560
561 spl_fixedarray_init(&intern->array, size);
562 }
563
PHP_METHOD(SplFixedArray,__wakeup)564 PHP_METHOD(SplFixedArray, __wakeup)
565 {
566 spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
567 HashTable *intern_ht = zend_std_get_properties(Z_OBJ_P(ZEND_THIS));
568 zval *data;
569
570 if (zend_parse_parameters_none() == FAILURE) {
571 RETURN_THROWS();
572 }
573
574 if (intern->array.size == 0) {
575 int index = 0;
576 int size = zend_hash_num_elements(intern_ht);
577
578 spl_fixedarray_init(&intern->array, size);
579
580 ZEND_HASH_FOREACH_VAL(intern_ht, data) {
581 ZVAL_COPY(&intern->array.elements[index], data);
582 index++;
583 } ZEND_HASH_FOREACH_END();
584
585 /* Remove the unserialised properties, since we now have the elements
586 * within the spl_fixedarray_object structure. */
587 zend_hash_clean(intern_ht);
588 }
589 }
590
PHP_METHOD(SplFixedArray,count)591 PHP_METHOD(SplFixedArray, count)
592 {
593 zval *object = ZEND_THIS;
594 spl_fixedarray_object *intern;
595
596 if (zend_parse_parameters_none() == FAILURE) {
597 RETURN_THROWS();
598 }
599
600 intern = Z_SPLFIXEDARRAY_P(object);
601 RETURN_LONG(intern->array.size);
602 }
603
PHP_METHOD(SplFixedArray,toArray)604 PHP_METHOD(SplFixedArray, toArray)
605 {
606 spl_fixedarray_object *intern;
607
608 if (zend_parse_parameters_none() == FAILURE) {
609 RETURN_THROWS();
610 }
611
612 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
613
614 if (!spl_fixedarray_empty(&intern->array)) {
615 array_init(return_value);
616 for (zend_long i = 0; i < intern->array.size; i++) {
617 zend_hash_index_update(Z_ARRVAL_P(return_value), i, &intern->array.elements[i]);
618 Z_TRY_ADDREF(intern->array.elements[i]);
619 }
620 } else {
621 RETURN_EMPTY_ARRAY();
622 }
623 }
624
PHP_METHOD(SplFixedArray,fromArray)625 PHP_METHOD(SplFixedArray, fromArray)
626 {
627 zval *data;
628 spl_fixedarray array;
629 spl_fixedarray_object *intern;
630 int num;
631 zend_bool save_indexes = 1;
632
633 if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|b", &data, &save_indexes) == FAILURE) {
634 RETURN_THROWS();
635 }
636
637 num = zend_hash_num_elements(Z_ARRVAL_P(data));
638
639 if (num > 0 && save_indexes) {
640 zval *element;
641 zend_string *str_index;
642 zend_ulong num_index, max_index = 0;
643 zend_long tmp;
644
645 ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(data), num_index, str_index) {
646 if (str_index != NULL || (zend_long)num_index < 0) {
647 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "array must contain only positive integer keys");
648 RETURN_THROWS();
649 }
650
651 if (num_index > max_index) {
652 max_index = num_index;
653 }
654 } ZEND_HASH_FOREACH_END();
655
656 tmp = max_index + 1;
657 if (tmp <= 0) {
658 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "integer overflow detected");
659 RETURN_THROWS();
660 }
661 spl_fixedarray_init(&array, tmp);
662
663 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(data), num_index, str_index, element) {
664 ZVAL_COPY_DEREF(&array.elements[num_index], element);
665 } ZEND_HASH_FOREACH_END();
666
667 } else if (num > 0 && !save_indexes) {
668 zval *element;
669 zend_long i = 0;
670
671 spl_fixedarray_init(&array, num);
672
673 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(data), element) {
674 ZVAL_COPY_DEREF(&array.elements[i], element);
675 i++;
676 } ZEND_HASH_FOREACH_END();
677 } else {
678 spl_fixedarray_init(&array, 0);
679 }
680
681 object_init_ex(return_value, spl_ce_SplFixedArray);
682
683 intern = Z_SPLFIXEDARRAY_P(return_value);
684 intern->array = array;
685 }
686
PHP_METHOD(SplFixedArray,getSize)687 PHP_METHOD(SplFixedArray, getSize)
688 {
689 zval *object = ZEND_THIS;
690 spl_fixedarray_object *intern;
691
692 if (zend_parse_parameters_none() == FAILURE) {
693 RETURN_THROWS();
694 }
695
696 intern = Z_SPLFIXEDARRAY_P(object);
697 RETURN_LONG(intern->array.size);
698 }
699
PHP_METHOD(SplFixedArray,setSize)700 PHP_METHOD(SplFixedArray, setSize)
701 {
702 zval *object = ZEND_THIS;
703 spl_fixedarray_object *intern;
704 zend_long size;
705
706 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &size) == FAILURE) {
707 RETURN_THROWS();
708 }
709
710 if (size < 0) {
711 zend_argument_value_error(1, "must be greater than or equal to 0");
712 RETURN_THROWS();
713 }
714
715 intern = Z_SPLFIXEDARRAY_P(object);
716
717 spl_fixedarray_resize(&intern->array, size);
718 RETURN_TRUE;
719 }
720
721 /* Returns whether the requested $index exists. */
PHP_METHOD(SplFixedArray,offsetExists)722 PHP_METHOD(SplFixedArray, offsetExists)
723 {
724 zval *zindex;
725 spl_fixedarray_object *intern;
726
727 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
728 RETURN_THROWS();
729 }
730
731 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
732
733 RETURN_BOOL(spl_fixedarray_object_has_dimension_helper(intern, zindex, 0));
734 }
735
736 /* Returns the value at the specified $index. */
PHP_METHOD(SplFixedArray,offsetGet)737 PHP_METHOD(SplFixedArray, offsetGet)
738 {
739 zval *zindex, *value;
740 spl_fixedarray_object *intern;
741
742 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
743 RETURN_THROWS();
744 }
745
746 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
747 value = spl_fixedarray_object_read_dimension_helper(intern, zindex);
748
749 if (value) {
750 ZVAL_COPY_DEREF(return_value, value);
751 } else {
752 RETURN_NULL();
753 }
754 }
755
756 /* Sets the value at the specified $index to $newval. */
PHP_METHOD(SplFixedArray,offsetSet)757 PHP_METHOD(SplFixedArray, offsetSet)
758 {
759 zval *zindex, *value;
760 spl_fixedarray_object *intern;
761
762 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &zindex, &value) == FAILURE) {
763 RETURN_THROWS();
764 }
765
766 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
767 spl_fixedarray_object_write_dimension_helper(intern, zindex, value);
768
769 }
770
771 /* Unsets the value at the specified $index. */
PHP_METHOD(SplFixedArray,offsetUnset)772 PHP_METHOD(SplFixedArray, offsetUnset)
773 {
774 zval *zindex;
775 spl_fixedarray_object *intern;
776
777 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
778 RETURN_THROWS();
779 }
780
781 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
782 spl_fixedarray_object_unset_dimension_helper(intern, zindex);
783
784 }
785
786 /* Create a new iterator from a SplFixedArray instance. */
PHP_METHOD(SplFixedArray,getIterator)787 PHP_METHOD(SplFixedArray, getIterator)
788 {
789 if (zend_parse_parameters_none() == FAILURE) {
790 return;
791 }
792
793 zend_create_internal_iterator_zval(return_value, ZEND_THIS);
794 }
795
spl_fixedarray_it_dtor(zend_object_iterator * iter)796 static void spl_fixedarray_it_dtor(zend_object_iterator *iter)
797 {
798 zval_ptr_dtor(&iter->data);
799 }
800
spl_fixedarray_it_rewind(zend_object_iterator * iter)801 static void spl_fixedarray_it_rewind(zend_object_iterator *iter)
802 {
803 ((spl_fixedarray_it*)iter)->current = 0;
804 }
805
spl_fixedarray_it_valid(zend_object_iterator * iter)806 static int spl_fixedarray_it_valid(zend_object_iterator *iter)
807 {
808 spl_fixedarray_it *iterator = (spl_fixedarray_it*)iter;
809 spl_fixedarray_object *object = Z_SPLFIXEDARRAY_P(&iter->data);
810
811 if (iterator->current >= 0 && iterator->current < object->array.size) {
812 return SUCCESS;
813 }
814
815 return FAILURE;
816 }
817
spl_fixedarray_it_get_current_data(zend_object_iterator * iter)818 static zval *spl_fixedarray_it_get_current_data(zend_object_iterator *iter)
819 {
820 zval zindex, *data;
821 spl_fixedarray_it *iterator = (spl_fixedarray_it*)iter;
822 spl_fixedarray_object *object = Z_SPLFIXEDARRAY_P(&iter->data);
823
824 ZVAL_LONG(&zindex, iterator->current);
825 data = spl_fixedarray_object_read_dimension_helper(object, &zindex);
826
827 if (data == NULL) {
828 data = &EG(uninitialized_zval);
829 }
830 return data;
831 }
832
spl_fixedarray_it_get_current_key(zend_object_iterator * iter,zval * key)833 static void spl_fixedarray_it_get_current_key(zend_object_iterator *iter, zval *key)
834 {
835 ZVAL_LONG(key, ((spl_fixedarray_it*)iter)->current);
836 }
837
spl_fixedarray_it_move_forward(zend_object_iterator * iter)838 static void spl_fixedarray_it_move_forward(zend_object_iterator *iter)
839 {
840 ((spl_fixedarray_it*)iter)->current++;
841 }
842
843 /* iterator handler table */
844 static const zend_object_iterator_funcs spl_fixedarray_it_funcs = {
845 spl_fixedarray_it_dtor,
846 spl_fixedarray_it_valid,
847 spl_fixedarray_it_get_current_data,
848 spl_fixedarray_it_get_current_key,
849 spl_fixedarray_it_move_forward,
850 spl_fixedarray_it_rewind,
851 NULL,
852 NULL, /* get_gc */
853 };
854
spl_fixedarray_get_iterator(zend_class_entry * ce,zval * object,int by_ref)855 zend_object_iterator *spl_fixedarray_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
856 {
857 spl_fixedarray_it *iterator;
858
859 if (by_ref) {
860 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
861 return NULL;
862 }
863
864 iterator = emalloc(sizeof(spl_fixedarray_it));
865
866 zend_iterator_init((zend_object_iterator*)iterator);
867
868 ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
869 iterator->intern.funcs = &spl_fixedarray_it_funcs;
870
871 return &iterator->intern;
872 }
873
PHP_MINIT_FUNCTION(spl_fixedarray)874 PHP_MINIT_FUNCTION(spl_fixedarray)
875 {
876 REGISTER_SPL_STD_CLASS_EX(SplFixedArray, spl_fixedarray_new, class_SplFixedArray_methods);
877 memcpy(&spl_handler_SplFixedArray, &std_object_handlers, sizeof(zend_object_handlers));
878
879 spl_handler_SplFixedArray.offset = XtOffsetOf(spl_fixedarray_object, std);
880 spl_handler_SplFixedArray.clone_obj = spl_fixedarray_object_clone;
881 spl_handler_SplFixedArray.read_dimension = spl_fixedarray_object_read_dimension;
882 spl_handler_SplFixedArray.write_dimension = spl_fixedarray_object_write_dimension;
883 spl_handler_SplFixedArray.unset_dimension = spl_fixedarray_object_unset_dimension;
884 spl_handler_SplFixedArray.has_dimension = spl_fixedarray_object_has_dimension;
885 spl_handler_SplFixedArray.count_elements = spl_fixedarray_object_count_elements;
886 spl_handler_SplFixedArray.get_properties = spl_fixedarray_object_get_properties;
887 spl_handler_SplFixedArray.get_gc = spl_fixedarray_object_get_gc;
888 spl_handler_SplFixedArray.dtor_obj = zend_objects_destroy_object;
889 spl_handler_SplFixedArray.free_obj = spl_fixedarray_object_free_storage;
890
891 REGISTER_SPL_IMPLEMENTS(SplFixedArray, Aggregate);
892 REGISTER_SPL_IMPLEMENTS(SplFixedArray, ArrayAccess);
893 REGISTER_SPL_IMPLEMENTS(SplFixedArray, Countable);
894
895 spl_ce_SplFixedArray->get_iterator = spl_fixedarray_get_iterator;
896 spl_ce_SplFixedArray->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
897
898 return SUCCESS;
899 }
900