xref: /PHP-8.0/ext/spl/spl_fixedarray.c (revision cd1c6f0b)
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