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