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