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