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 | Authors: Etienne Kneuss <colder@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
20
21 #include "php.h"
22 #include "zend_exceptions.h"
23
24 #include "php_spl.h"
25 #include "spl_functions.h"
26 #include "spl_engine.h"
27 #include "spl_iterators.h"
28 #include "spl_heap.h"
29 #include "spl_heap_arginfo.h"
30 #include "spl_exceptions.h"
31
32 #define PTR_HEAP_BLOCK_SIZE 64
33
34 #define SPL_HEAP_CORRUPTED 0x00000001
35 #define SPL_HEAP_WRITE_LOCKED 0x00000002
36
37 zend_object_handlers spl_handler_SplHeap;
38 zend_object_handlers spl_handler_SplPriorityQueue;
39
40 PHPAPI zend_class_entry *spl_ce_SplHeap;
41 PHPAPI zend_class_entry *spl_ce_SplMaxHeap;
42 PHPAPI zend_class_entry *spl_ce_SplMinHeap;
43 PHPAPI zend_class_entry *spl_ce_SplPriorityQueue;
44
45
46 typedef void (*spl_ptr_heap_dtor_func)(void *);
47 typedef void (*spl_ptr_heap_ctor_func)(void *);
48 typedef int (*spl_ptr_heap_cmp_func)(void *, void *, zval *);
49
50 typedef struct _spl_ptr_heap {
51 void *elements;
52 spl_ptr_heap_ctor_func ctor;
53 spl_ptr_heap_dtor_func dtor;
54 spl_ptr_heap_cmp_func cmp;
55 int count;
56 int flags;
57 size_t max_size;
58 size_t elem_size;
59 } spl_ptr_heap;
60
61 typedef struct _spl_heap_object spl_heap_object;
62 typedef struct _spl_heap_it spl_heap_it;
63
64 struct _spl_heap_object {
65 spl_ptr_heap *heap;
66 int flags;
67 zend_function *fptr_cmp;
68 zend_function *fptr_count;
69 zend_object std;
70 };
71
72 typedef struct _spl_pqueue_elem {
73 zval data;
74 zval priority;
75 } spl_pqueue_elem;
76
spl_heap_from_obj(zend_object * obj)77 static inline spl_heap_object *spl_heap_from_obj(zend_object *obj) /* {{{ */ {
78 return (spl_heap_object*)((char*)(obj) - XtOffsetOf(spl_heap_object, std));
79 }
80 /* }}} */
81
82 #define Z_SPLHEAP_P(zv) spl_heap_from_obj(Z_OBJ_P((zv)))
83
spl_heap_elem(spl_ptr_heap * heap,size_t i)84 static zend_always_inline void *spl_heap_elem(spl_ptr_heap *heap, size_t i) {
85 return (void *) ((char *) heap->elements + heap->elem_size * i);
86 }
87
spl_heap_elem_copy(spl_ptr_heap * heap,void * to,void * from)88 static zend_always_inline void spl_heap_elem_copy(spl_ptr_heap *heap, void *to, void *from) {
89 assert(to != from);
90
91 /* Specialized for cases of heap and priority queue. With the size being
92 * constant known at compile time the compiler can fully inline calls to memcpy. */
93 if (heap->elem_size == sizeof(spl_pqueue_elem)) {
94 memcpy(to, from, sizeof(spl_pqueue_elem));
95 } else {
96 ZEND_ASSERT(heap->elem_size == sizeof(zval));
97 memcpy(to, from, sizeof(zval));
98 }
99 }
100
spl_ptr_heap_zval_dtor(void * elem)101 static void spl_ptr_heap_zval_dtor(void *elem) { /* {{{ */
102 zval_ptr_dtor((zval *) elem);
103 }
104 /* }}} */
105
spl_ptr_heap_zval_ctor(void * elem)106 static void spl_ptr_heap_zval_ctor(void *elem) { /* {{{ */
107 Z_TRY_ADDREF_P((zval *) elem);
108 }
109 /* }}} */
110
spl_ptr_heap_pqueue_elem_dtor(void * elem)111 static void spl_ptr_heap_pqueue_elem_dtor(void *elem) { /* {{{ */
112 spl_pqueue_elem *pq_elem = elem;
113 zval_ptr_dtor(&pq_elem->data);
114 zval_ptr_dtor(&pq_elem->priority);
115 }
116 /* }}} */
117
spl_ptr_heap_pqueue_elem_ctor(void * elem)118 static void spl_ptr_heap_pqueue_elem_ctor(void *elem) { /* {{{ */
119 spl_pqueue_elem *pq_elem = elem;
120 Z_TRY_ADDREF_P(&pq_elem->data);
121 Z_TRY_ADDREF_P(&pq_elem->priority);
122 }
123 /* }}} */
124
spl_ptr_heap_cmp_cb_helper(zval * object,spl_heap_object * heap_object,zval * a,zval * b,zend_long * result)125 static int spl_ptr_heap_cmp_cb_helper(zval *object, spl_heap_object *heap_object, zval *a, zval *b, zend_long *result) { /* {{{ */
126 zval zresult;
127
128 zend_call_method_with_2_params(Z_OBJ_P(object), heap_object->std.ce, &heap_object->fptr_cmp, "compare", &zresult, a, b);
129
130 if (EG(exception)) {
131 return FAILURE;
132 }
133
134 *result = zval_get_long(&zresult);
135 zval_ptr_dtor(&zresult);
136
137 return SUCCESS;
138 }
139 /* }}} */
140
spl_pqueue_extract_helper(zval * result,spl_pqueue_elem * elem,int flags)141 static void spl_pqueue_extract_helper(zval *result, spl_pqueue_elem *elem, int flags) /* {{{ */
142 {
143 if ((flags & SPL_PQUEUE_EXTR_BOTH) == SPL_PQUEUE_EXTR_BOTH) {
144 array_init(result);
145 Z_TRY_ADDREF(elem->data);
146 add_assoc_zval_ex(result, "data", sizeof("data") - 1, &elem->data);
147 Z_TRY_ADDREF(elem->priority);
148 add_assoc_zval_ex(result, "priority", sizeof("priority") - 1, &elem->priority);
149 return;
150 }
151
152 if (flags & SPL_PQUEUE_EXTR_DATA) {
153 ZVAL_COPY(result, &elem->data);
154 return;
155 }
156
157 if (flags & SPL_PQUEUE_EXTR_PRIORITY) {
158 ZVAL_COPY(result, &elem->priority);
159 return;
160 }
161
162 ZEND_UNREACHABLE();
163 }
164 /* }}} */
165
spl_ptr_heap_zval_max_cmp(void * x,void * y,zval * object)166 static int spl_ptr_heap_zval_max_cmp(void *x, void *y, zval *object) { /* {{{ */
167 zval *a = x, *b = y;
168
169 if (EG(exception)) {
170 return 0;
171 }
172
173 if (object) {
174 spl_heap_object *heap_object = Z_SPLHEAP_P(object);
175 if (heap_object->fptr_cmp) {
176 zend_long lval = 0;
177 if (spl_ptr_heap_cmp_cb_helper(object, heap_object, a, b, &lval) == FAILURE) {
178 /* exception or call failure */
179 return 0;
180 }
181 return ZEND_NORMALIZE_BOOL(lval);
182 }
183 }
184
185 return zend_compare(a, b);
186 }
187 /* }}} */
188
spl_ptr_heap_zval_min_cmp(void * x,void * y,zval * object)189 static int spl_ptr_heap_zval_min_cmp(void *x, void *y, zval *object) { /* {{{ */
190 zval *a = x, *b = y;
191
192 if (EG(exception)) {
193 return 0;
194 }
195
196 if (object) {
197 spl_heap_object *heap_object = Z_SPLHEAP_P(object);
198 if (heap_object->fptr_cmp) {
199 zend_long lval = 0;
200 if (spl_ptr_heap_cmp_cb_helper(object, heap_object, a, b, &lval) == FAILURE) {
201 /* exception or call failure */
202 return 0;
203 }
204 return ZEND_NORMALIZE_BOOL(lval);
205 }
206 }
207
208 return zend_compare(b, a);
209 }
210 /* }}} */
211
spl_ptr_pqueue_elem_cmp(void * x,void * y,zval * object)212 static int spl_ptr_pqueue_elem_cmp(void *x, void *y, zval *object) { /* {{{ */
213 spl_pqueue_elem *a = x;
214 spl_pqueue_elem *b = y;
215 zval *a_priority_p = &a->priority;
216 zval *b_priority_p = &b->priority;
217
218 if (EG(exception)) {
219 return 0;
220 }
221
222 if (object) {
223 spl_heap_object *heap_object = Z_SPLHEAP_P(object);
224 if (heap_object->fptr_cmp) {
225 zend_long lval = 0;
226 if (spl_ptr_heap_cmp_cb_helper(object, heap_object, a_priority_p, b_priority_p, &lval) == FAILURE) {
227 /* exception or call failure */
228 return 0;
229 }
230 return ZEND_NORMALIZE_BOOL(lval);
231 }
232 }
233
234 return zend_compare(a_priority_p, b_priority_p);
235 }
236 /* }}} */
237
238 /* Specialized comparator used when we are absolutely sure an instance of the
239 * not inherited SplPriorityQueue class contains only priorities as longs. This
240 * fact is tracked during insertion into the queue. */
spl_ptr_pqueue_elem_cmp_long(void * x,void * y,zval * object)241 static int spl_ptr_pqueue_elem_cmp_long(void *x, void *y, zval *object) {
242 zend_long a = Z_LVAL(((spl_pqueue_elem*) x)->priority);
243 zend_long b = Z_LVAL(((spl_pqueue_elem*) y)->priority);
244 return a>b ? 1 : (a<b ? -1 : 0);
245 }
246
247 /* same as spl_ptr_pqueue_elem_cmp_long */
spl_ptr_pqueue_elem_cmp_double(void * x,void * y,zval * object)248 static int spl_ptr_pqueue_elem_cmp_double(void *x, void *y, zval *object) {
249 double a = Z_DVAL(((spl_pqueue_elem*) x)->priority);
250 double b = Z_DVAL(((spl_pqueue_elem*) y)->priority);
251 return ZEND_NORMALIZE_BOOL(a - b);
252 }
253
spl_ptr_heap_init(spl_ptr_heap_cmp_func cmp,spl_ptr_heap_ctor_func ctor,spl_ptr_heap_dtor_func dtor,size_t elem_size)254 static spl_ptr_heap *spl_ptr_heap_init(spl_ptr_heap_cmp_func cmp, spl_ptr_heap_ctor_func ctor, spl_ptr_heap_dtor_func dtor, size_t elem_size) /* {{{ */
255 {
256 spl_ptr_heap *heap = emalloc(sizeof(spl_ptr_heap));
257
258 heap->dtor = dtor;
259 heap->ctor = ctor;
260 heap->cmp = cmp;
261 heap->elements = ecalloc(PTR_HEAP_BLOCK_SIZE, elem_size);
262 heap->max_size = PTR_HEAP_BLOCK_SIZE;
263 heap->count = 0;
264 heap->flags = 0;
265 heap->elem_size = elem_size;
266
267 return heap;
268 }
269 /* }}} */
270
spl_ptr_heap_insert(spl_ptr_heap * heap,void * elem,void * cmp_userdata)271 static void spl_ptr_heap_insert(spl_ptr_heap *heap, void *elem, void *cmp_userdata) { /* {{{ */
272 int i;
273
274 if (heap->count+1 > heap->max_size) {
275 size_t alloc_size = heap->max_size * heap->elem_size;
276 /* we need to allocate more memory */
277 heap->elements = safe_erealloc(heap->elements, 2, alloc_size, 0);
278 memset((char *) heap->elements + alloc_size, 0, alloc_size);
279 heap->max_size *= 2;
280 }
281
282 heap->flags |= SPL_HEAP_WRITE_LOCKED;
283
284 /* sifting up */
285 for (i = heap->count; i > 0 && heap->cmp(spl_heap_elem(heap, (i-1)/2), elem, cmp_userdata) < 0; i = (i-1)/2) {
286 spl_heap_elem_copy(heap, spl_heap_elem(heap, i), spl_heap_elem(heap, (i-1)/2));
287 }
288 heap->count++;
289
290 heap->flags &= ~SPL_HEAP_WRITE_LOCKED;
291
292 if (EG(exception)) {
293 /* exception thrown during comparison */
294 heap->flags |= SPL_HEAP_CORRUPTED;
295 }
296
297 spl_heap_elem_copy(heap, spl_heap_elem(heap, i), elem);
298 }
299 /* }}} */
300
spl_ptr_heap_top(spl_ptr_heap * heap)301 static void *spl_ptr_heap_top(spl_ptr_heap *heap) { /* {{{ */
302 if (heap->count == 0) {
303 return NULL;
304 }
305
306 return heap->elements;
307 }
308 /* }}} */
309
spl_ptr_heap_delete_top(spl_ptr_heap * heap,void * elem,void * cmp_userdata)310 static int spl_ptr_heap_delete_top(spl_ptr_heap *heap, void *elem, void *cmp_userdata) { /* {{{ */
311 int i, j;
312 const int limit = (heap->count-1)/2;
313 void *bottom;
314
315 if (heap->count == 0) {
316 return FAILURE;
317 }
318
319 heap->flags |= SPL_HEAP_WRITE_LOCKED;
320
321 if (elem) {
322 spl_heap_elem_copy(heap, elem, spl_heap_elem(heap, 0));
323 } else {
324 heap->dtor(spl_heap_elem(heap, 0));
325 }
326
327 bottom = spl_heap_elem(heap, --heap->count);
328
329 for (i = 0; i < limit; i = j) {
330 /* Find smaller child */
331 j = i * 2 + 1;
332 if (j != heap->count && heap->cmp(spl_heap_elem(heap, j+1), spl_heap_elem(heap, j), cmp_userdata) > 0) {
333 j++; /* next child is bigger */
334 }
335
336 /* swap elements between two levels */
337 if(heap->cmp(bottom, spl_heap_elem(heap, j), cmp_userdata) < 0) {
338 spl_heap_elem_copy(heap, spl_heap_elem(heap, i), spl_heap_elem(heap, j));
339 } else {
340 break;
341 }
342 }
343
344 heap->flags &= ~SPL_HEAP_WRITE_LOCKED;
345
346 if (EG(exception)) {
347 /* exception thrown during comparison */
348 heap->flags |= SPL_HEAP_CORRUPTED;
349 }
350
351 void *to = spl_heap_elem(heap, i);
352 if (to != bottom) {
353 spl_heap_elem_copy(heap, to, bottom);
354 }
355 return SUCCESS;
356 }
357 /* }}} */
358
spl_ptr_heap_clone(spl_ptr_heap * from)359 static spl_ptr_heap *spl_ptr_heap_clone(spl_ptr_heap *from) { /* {{{ */
360 int i;
361
362 spl_ptr_heap *heap = emalloc(sizeof(spl_ptr_heap));
363
364 heap->dtor = from->dtor;
365 heap->ctor = from->ctor;
366 heap->cmp = from->cmp;
367 heap->max_size = from->max_size;
368 heap->count = from->count;
369 heap->flags = from->flags;
370 heap->elem_size = from->elem_size;
371
372 heap->elements = safe_emalloc(from->elem_size, from->max_size, 0);
373 memcpy(heap->elements, from->elements, from->elem_size * from->max_size);
374
375 for (i = 0; i < heap->count; ++i) {
376 heap->ctor(spl_heap_elem(heap, i));
377 }
378
379 return heap;
380 }
381 /* }}} */
382
spl_ptr_heap_destroy(spl_ptr_heap * heap)383 static void spl_ptr_heap_destroy(spl_ptr_heap *heap) { /* {{{ */
384 int i;
385
386 heap->flags |= SPL_HEAP_WRITE_LOCKED;
387
388 for (i = 0; i < heap->count; ++i) {
389 heap->dtor(spl_heap_elem(heap, i));
390 }
391
392 heap->flags &= ~SPL_HEAP_WRITE_LOCKED;
393
394 efree(heap->elements);
395 efree(heap);
396 }
397 /* }}} */
398
spl_ptr_heap_count(spl_ptr_heap * heap)399 static int spl_ptr_heap_count(spl_ptr_heap *heap) { /* {{{ */
400 return heap->count;
401 }
402 /* }}} */
403
spl_heap_object_free_storage(zend_object * object)404 static void spl_heap_object_free_storage(zend_object *object) /* {{{ */
405 {
406 spl_heap_object *intern = spl_heap_from_obj(object);
407
408 zend_object_std_dtor(&intern->std);
409
410 spl_ptr_heap_destroy(intern->heap);
411 }
412 /* }}} */
413
spl_heap_object_new_ex(zend_class_entry * class_type,zend_object * orig,int clone_orig)414 static zend_object *spl_heap_object_new_ex(zend_class_entry *class_type, zend_object *orig, int clone_orig) /* {{{ */
415 {
416 spl_heap_object *intern;
417 zend_class_entry *parent = class_type;
418 int inherited = 0;
419
420 intern = zend_object_alloc(sizeof(spl_heap_object), parent);
421
422 zend_object_std_init(&intern->std, class_type);
423 object_properties_init(&intern->std, class_type);
424
425 if (orig) {
426 spl_heap_object *other = spl_heap_from_obj(orig);
427 intern->std.handlers = other->std.handlers;
428
429 if (clone_orig) {
430 intern->heap = spl_ptr_heap_clone(other->heap);
431 } else {
432 intern->heap = other->heap;
433 }
434
435 intern->flags = other->flags;
436 intern->fptr_cmp = other->fptr_cmp;
437 intern->fptr_count = other->fptr_count;
438 return &intern->std;
439 }
440
441 while (parent) {
442 if (parent == spl_ce_SplPriorityQueue) {
443 intern->heap = spl_ptr_heap_init(spl_ptr_pqueue_elem_cmp, spl_ptr_heap_pqueue_elem_ctor, spl_ptr_heap_pqueue_elem_dtor, sizeof(spl_pqueue_elem));
444 intern->std.handlers = &spl_handler_SplPriorityQueue;
445 intern->flags = SPL_PQUEUE_EXTR_DATA;
446 break;
447 }
448
449 if (parent == spl_ce_SplMinHeap || parent == spl_ce_SplMaxHeap
450 || parent == spl_ce_SplHeap) {
451 intern->heap = spl_ptr_heap_init(
452 parent == spl_ce_SplMinHeap ? spl_ptr_heap_zval_min_cmp : spl_ptr_heap_zval_max_cmp,
453 spl_ptr_heap_zval_ctor, spl_ptr_heap_zval_dtor, sizeof(zval));
454 intern->std.handlers = &spl_handler_SplHeap;
455 break;
456 }
457
458 parent = parent->parent;
459 inherited = 1;
460 }
461
462 ZEND_ASSERT(parent);
463
464 if (inherited) {
465 intern->fptr_cmp = zend_hash_str_find_ptr(&class_type->function_table, "compare", sizeof("compare") - 1);
466 if (intern->fptr_cmp->common.scope == parent) {
467 intern->fptr_cmp = NULL;
468 }
469 intern->fptr_count = zend_hash_str_find_ptr(&class_type->function_table, "count", sizeof("count") - 1);
470 if (intern->fptr_count->common.scope == parent) {
471 intern->fptr_count = NULL;
472 }
473 }
474
475 return &intern->std;
476 }
477 /* }}} */
478
spl_heap_object_new(zend_class_entry * class_type)479 static zend_object *spl_heap_object_new(zend_class_entry *class_type) /* {{{ */
480 {
481 return spl_heap_object_new_ex(class_type, NULL, 0);
482 }
483 /* }}} */
484
spl_heap_object_clone(zend_object * old_object)485 static zend_object *spl_heap_object_clone(zend_object *old_object) /* {{{ */
486 {
487 zend_object *new_object = spl_heap_object_new_ex(old_object->ce, old_object, 1);
488
489 zend_objects_clone_members(new_object, old_object);
490
491 return new_object;
492 }
493 /* }}} */
494
spl_heap_object_count_elements(zend_object * object,zend_long * count)495 static zend_result spl_heap_object_count_elements(zend_object *object, zend_long *count) /* {{{ */
496 {
497 spl_heap_object *intern = spl_heap_from_obj(object);
498
499 if (intern->fptr_count) {
500 zval rv;
501 zend_call_method_with_0_params(object, intern->std.ce, &intern->fptr_count, "count", &rv);
502 if (!Z_ISUNDEF(rv)) {
503 *count = zval_get_long(&rv);
504 zval_ptr_dtor(&rv);
505 return SUCCESS;
506 }
507 *count = 0;
508 return FAILURE;
509 }
510
511 *count = spl_ptr_heap_count(intern->heap);
512
513 return SUCCESS;
514 }
515 /* }}} */
516
spl_heap_object_get_debug_info(zend_class_entry * ce,zend_object * obj)517 static HashTable* spl_heap_object_get_debug_info(zend_class_entry *ce, zend_object *obj) { /* {{{ */
518 spl_heap_object *intern = spl_heap_from_obj(obj);
519 zval tmp, heap_array;
520 zend_string *pnstr;
521 HashTable *debug_info;
522 int i;
523
524 if (!intern->std.properties) {
525 rebuild_object_properties(&intern->std);
526 }
527
528 debug_info = zend_new_array(zend_hash_num_elements(intern->std.properties) + 1);
529 zend_hash_copy(debug_info, intern->std.properties, (copy_ctor_func_t) zval_add_ref);
530
531 pnstr = spl_gen_private_prop_name(ce, "flags", sizeof("flags")-1);
532 ZVAL_LONG(&tmp, intern->flags);
533 zend_hash_update(debug_info, pnstr, &tmp);
534 zend_string_release_ex(pnstr, 0);
535
536 pnstr = spl_gen_private_prop_name(ce, "isCorrupted", sizeof("isCorrupted")-1);
537 ZVAL_BOOL(&tmp, intern->heap->flags&SPL_HEAP_CORRUPTED);
538 zend_hash_update(debug_info, pnstr, &tmp);
539 zend_string_release_ex(pnstr, 0);
540
541 array_init(&heap_array);
542
543 for (i = 0; i < intern->heap->count; ++i) {
544 if (ce == spl_ce_SplPriorityQueue) {
545 spl_pqueue_elem *pq_elem = spl_heap_elem(intern->heap, i);
546 zval elem;
547 spl_pqueue_extract_helper(&elem, pq_elem, SPL_PQUEUE_EXTR_BOTH);
548 add_index_zval(&heap_array, i, &elem);
549 } else {
550 zval *elem = spl_heap_elem(intern->heap, i);
551 add_index_zval(&heap_array, i, elem);
552 Z_TRY_ADDREF_P(elem);
553 }
554 }
555
556 pnstr = spl_gen_private_prop_name(ce, "heap", sizeof("heap")-1);
557 zend_hash_update(debug_info, pnstr, &heap_array);
558 zend_string_release_ex(pnstr, 0);
559
560 return debug_info;
561 }
562 /* }}} */
563
spl_heap_object_get_gc(zend_object * obj,zval ** gc_data,int * gc_data_count)564 static HashTable *spl_heap_object_get_gc(zend_object *obj, zval **gc_data, int *gc_data_count) /* {{{ */
565 {
566 spl_heap_object *intern = spl_heap_from_obj(obj);
567 *gc_data = (zval *) intern->heap->elements;
568 *gc_data_count = intern->heap->count;
569
570 return zend_std_get_properties(obj);
571 }
572 /* }}} */
573
spl_pqueue_object_get_gc(zend_object * obj,zval ** gc_data,int * gc_data_count)574 static HashTable *spl_pqueue_object_get_gc(zend_object *obj, zval **gc_data, int *gc_data_count) /* {{{ */
575 {
576 spl_heap_object *intern = spl_heap_from_obj(obj);
577 *gc_data = (zval *) intern->heap->elements;
578 /* Two zvals (value and priority) per pqueue entry */
579 *gc_data_count = 2 * intern->heap->count;
580
581 return zend_std_get_properties(obj);
582 }
583 /* }}} */
584
585 /* {{{ Return the number of elements in the heap. */
PHP_METHOD(SplHeap,count)586 PHP_METHOD(SplHeap, count)
587 {
588 zend_long count;
589 spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
590
591 if (zend_parse_parameters_none() == FAILURE) {
592 RETURN_THROWS();
593 }
594
595 count = spl_ptr_heap_count(intern->heap);
596 RETURN_LONG(count);
597 }
598 /* }}} */
599
600 /* {{{ Return true if the heap is empty. */
PHP_METHOD(SplHeap,isEmpty)601 PHP_METHOD(SplHeap, isEmpty)
602 {
603 spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
604
605 if (zend_parse_parameters_none() == FAILURE) {
606 RETURN_THROWS();
607 }
608
609 RETURN_BOOL(spl_ptr_heap_count(intern->heap) == 0);
610 }
611 /* }}} */
612
spl_heap_consistency_validations(const spl_heap_object * intern,bool write)613 static zend_result spl_heap_consistency_validations(const spl_heap_object *intern, bool write)
614 {
615 if (intern->heap->flags & SPL_HEAP_CORRUPTED) {
616 zend_throw_exception(spl_ce_RuntimeException, "Heap is corrupted, heap properties are no longer ensured.", 0);
617 return FAILURE;
618 }
619
620 if (write && (intern->heap->flags & SPL_HEAP_WRITE_LOCKED)) {
621 zend_throw_exception(spl_ce_RuntimeException, "Heap cannot be changed when it is already being modified.", 0);
622 return FAILURE;
623 }
624
625 return SUCCESS;
626 }
627
628 /* {{{ Push $value on the heap */
PHP_METHOD(SplHeap,insert)629 PHP_METHOD(SplHeap, insert)
630 {
631 zval *value;
632 spl_heap_object *intern;
633
634 ZEND_PARSE_PARAMETERS_START(1, 1)
635 Z_PARAM_ZVAL(value);
636 ZEND_PARSE_PARAMETERS_END();
637
638 intern = Z_SPLHEAP_P(ZEND_THIS);
639
640 if (UNEXPECTED(spl_heap_consistency_validations(intern, true) != SUCCESS)) {
641 RETURN_THROWS();
642 }
643
644 Z_TRY_ADDREF_P(value);
645 spl_ptr_heap_insert(intern->heap, value, ZEND_THIS);
646
647 RETURN_TRUE;
648 }
649 /* }}} */
650
651 /* {{{ extract the element out of the top of the heap */
PHP_METHOD(SplHeap,extract)652 PHP_METHOD(SplHeap, extract)
653 {
654 spl_heap_object *intern;
655
656 if (zend_parse_parameters_none() == FAILURE) {
657 RETURN_THROWS();
658 }
659
660 intern = Z_SPLHEAP_P(ZEND_THIS);
661
662 if (UNEXPECTED(spl_heap_consistency_validations(intern, true) != SUCCESS)) {
663 RETURN_THROWS();
664 }
665
666 if (spl_ptr_heap_delete_top(intern->heap, return_value, ZEND_THIS) == FAILURE) {
667 zend_throw_exception(spl_ce_RuntimeException, "Can't extract from an empty heap", 0);
668 RETURN_THROWS();
669 }
670 }
671 /* }}} */
672
673 /* {{{ Push $value with the priority $priodiry on the priorityqueue */
PHP_METHOD(SplPriorityQueue,insert)674 PHP_METHOD(SplPriorityQueue, insert)
675 {
676 zval *data, *priority;
677 spl_heap_object *intern;
678 spl_pqueue_elem elem;
679
680 ZEND_PARSE_PARAMETERS_START(2, 2)
681 Z_PARAM_ZVAL(data);
682 Z_PARAM_ZVAL(priority);
683 ZEND_PARSE_PARAMETERS_END();
684
685 intern = Z_SPLHEAP_P(ZEND_THIS);
686
687 if (UNEXPECTED(spl_heap_consistency_validations(intern, true) != SUCCESS)) {
688 RETURN_THROWS();
689 }
690
691 ZVAL_COPY(&elem.data, data);
692 ZVAL_COPY(&elem.priority, priority);
693
694 /* If we know this call came from non inherited SplPriorityQueue it's
695 * possible to do specialization on the type of the priority parameter. */
696 if (!intern->fptr_cmp) {
697 int type = Z_TYPE(elem.priority);
698 spl_ptr_heap_cmp_func new_cmp =
699 (type == IS_LONG) ? spl_ptr_pqueue_elem_cmp_long :
700 ((type == IS_DOUBLE) ? spl_ptr_pqueue_elem_cmp_double : spl_ptr_pqueue_elem_cmp);
701
702 if (intern->heap->count == 0) { /* Specialize empty queue */
703 intern->heap->cmp = new_cmp;
704 } else if (new_cmp != intern->heap->cmp) { /* Despecialize on type conflict. */
705 intern->heap->cmp = spl_ptr_pqueue_elem_cmp;
706 }
707 }
708
709 spl_ptr_heap_insert(intern->heap, &elem, ZEND_THIS);
710
711 RETURN_TRUE;
712 }
713 /* }}} */
714
715 /* {{{ extract the element out of the top of the priority queue */
PHP_METHOD(SplPriorityQueue,extract)716 PHP_METHOD(SplPriorityQueue, extract)
717 {
718 spl_pqueue_elem elem;
719 spl_heap_object *intern;
720
721 if (zend_parse_parameters_none() == FAILURE) {
722 RETURN_THROWS();
723 }
724
725 intern = Z_SPLHEAP_P(ZEND_THIS);
726
727 if (UNEXPECTED(spl_heap_consistency_validations(intern, true) != SUCCESS)) {
728 RETURN_THROWS();
729 }
730
731 if (spl_ptr_heap_delete_top(intern->heap, &elem, ZEND_THIS) == FAILURE) {
732 zend_throw_exception(spl_ce_RuntimeException, "Can't extract from an empty heap", 0);
733 RETURN_THROWS();
734 }
735
736 spl_pqueue_extract_helper(return_value, &elem, intern->flags);
737 spl_ptr_heap_pqueue_elem_dtor(&elem);
738 }
739 /* }}} */
740
741 /* {{{ Peek at the top element of the priority queue */
PHP_METHOD(SplPriorityQueue,top)742 PHP_METHOD(SplPriorityQueue, top)
743 {
744 spl_heap_object *intern;
745 spl_pqueue_elem *elem;
746
747 if (zend_parse_parameters_none() == FAILURE) {
748 RETURN_THROWS();
749 }
750
751 intern = Z_SPLHEAP_P(ZEND_THIS);
752
753 if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
754 RETURN_THROWS();
755 }
756
757 elem = spl_ptr_heap_top(intern->heap);
758
759 if (!elem) {
760 zend_throw_exception(spl_ce_RuntimeException, "Can't peek at an empty heap", 0);
761 RETURN_THROWS();
762 }
763
764 spl_pqueue_extract_helper(return_value, elem, intern->flags);
765 }
766 /* }}} */
767
768
769 /* {{{ Set the flags of extraction*/
PHP_METHOD(SplPriorityQueue,setExtractFlags)770 PHP_METHOD(SplPriorityQueue, setExtractFlags)
771 {
772 zend_long value;
773 spl_heap_object *intern;
774
775 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &value) == FAILURE) {
776 RETURN_THROWS();
777 }
778
779 value &= SPL_PQUEUE_EXTR_MASK;
780 if (!value) {
781 zend_throw_exception(spl_ce_RuntimeException, "Must specify at least one extract flag", 0);
782 RETURN_THROWS();
783 }
784
785 intern = Z_SPLHEAP_P(ZEND_THIS);
786 intern->flags = value;
787 RETURN_LONG(intern->flags);
788 }
789 /* }}} */
790
791 /* {{{ Get the flags of extraction*/
PHP_METHOD(SplPriorityQueue,getExtractFlags)792 PHP_METHOD(SplPriorityQueue, getExtractFlags)
793 {
794 spl_heap_object *intern;
795
796 if (zend_parse_parameters_none() == FAILURE) {
797 RETURN_THROWS();
798 }
799
800 intern = Z_SPLHEAP_P(ZEND_THIS);
801
802 RETURN_LONG(intern->flags);
803 }
804 /* }}} */
805
806 /* {{{ Recover from a corrupted state*/
PHP_METHOD(SplHeap,recoverFromCorruption)807 PHP_METHOD(SplHeap, recoverFromCorruption)
808 {
809 spl_heap_object *intern;
810
811 if (zend_parse_parameters_none() == FAILURE) {
812 RETURN_THROWS();
813 }
814
815 intern = Z_SPLHEAP_P(ZEND_THIS);
816
817 intern->heap->flags = intern->heap->flags & ~SPL_HEAP_CORRUPTED;
818
819 RETURN_TRUE;
820 }
821 /* }}} */
822
823 /* {{{ Tells if the heap is in a corrupted state*/
PHP_METHOD(SplHeap,isCorrupted)824 PHP_METHOD(SplHeap, isCorrupted)
825 {
826 spl_heap_object *intern;
827
828 if (zend_parse_parameters_none() == FAILURE) {
829 RETURN_THROWS();
830 }
831
832 intern = Z_SPLHEAP_P(ZEND_THIS);
833
834 RETURN_BOOL(intern->heap->flags & SPL_HEAP_CORRUPTED);
835 }
836 /* }}} */
837
838 /* {{{ compare the priorities */
PHP_METHOD(SplPriorityQueue,compare)839 PHP_METHOD(SplPriorityQueue, compare)
840 {
841 zval *a, *b;
842
843 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a, &b) == FAILURE) {
844 RETURN_THROWS();
845 }
846
847 RETURN_LONG(spl_ptr_heap_zval_max_cmp(a, b, NULL));
848 }
849 /* }}} */
850
851 /* {{{ Peek at the top element of the heap */
PHP_METHOD(SplHeap,top)852 PHP_METHOD(SplHeap, top)
853 {
854 zval *value;
855 spl_heap_object *intern;
856
857 if (zend_parse_parameters_none() == FAILURE) {
858 RETURN_THROWS();
859 }
860
861 intern = Z_SPLHEAP_P(ZEND_THIS);
862
863 if (UNEXPECTED(spl_heap_consistency_validations(intern, false) != SUCCESS)) {
864 RETURN_THROWS();
865 }
866
867 value = spl_ptr_heap_top(intern->heap);
868
869 if (!value) {
870 zend_throw_exception(spl_ce_RuntimeException, "Can't peek at an empty heap", 0);
871 RETURN_THROWS();
872 }
873
874 RETURN_COPY_DEREF(value);
875 }
876 /* }}} */
877
878 /* {{{ compare the values */
PHP_METHOD(SplMinHeap,compare)879 PHP_METHOD(SplMinHeap, compare)
880 {
881 zval *a, *b;
882
883 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a, &b) == FAILURE) {
884 RETURN_THROWS();
885 }
886
887 RETURN_LONG(spl_ptr_heap_zval_min_cmp(a, b, NULL));
888 }
889 /* }}} */
890
891 /* {{{ compare the values */
PHP_METHOD(SplMaxHeap,compare)892 PHP_METHOD(SplMaxHeap, compare)
893 {
894 zval *a, *b;
895
896 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &a, &b) == FAILURE) {
897 RETURN_THROWS();
898 }
899
900 RETURN_LONG(spl_ptr_heap_zval_max_cmp(a, b, NULL));
901 }
902 /* }}} */
903
spl_heap_it_dtor(zend_object_iterator * iter)904 static void spl_heap_it_dtor(zend_object_iterator *iter) /* {{{ */
905 {
906 zend_user_it_invalidate_current(iter);
907 zval_ptr_dtor(&iter->data);
908 }
909 /* }}} */
910
spl_heap_it_rewind(zend_object_iterator * iter)911 static void spl_heap_it_rewind(zend_object_iterator *iter) /* {{{ */
912 {
913 /* do nothing, the iterator always points to the top element */
914 }
915 /* }}} */
916
spl_heap_it_valid(zend_object_iterator * iter)917 static int spl_heap_it_valid(zend_object_iterator *iter) /* {{{ */
918 {
919 return ((Z_SPLHEAP_P(&iter->data))->heap->count != 0 ? SUCCESS : FAILURE);
920 }
921 /* }}} */
922
spl_heap_it_get_current_data(zend_object_iterator * iter)923 static zval *spl_heap_it_get_current_data(zend_object_iterator *iter) /* {{{ */
924 {
925 spl_heap_object *object = Z_SPLHEAP_P(&iter->data);
926
927 if (UNEXPECTED(spl_heap_consistency_validations(object, false) != SUCCESS)) {
928 return NULL;
929 }
930
931 if (object->heap->count == 0) {
932 return NULL;
933 } else {
934 return spl_heap_elem(object->heap, 0);
935 }
936 }
937 /* }}} */
938
spl_pqueue_it_get_current_data(zend_object_iterator * iter)939 static zval *spl_pqueue_it_get_current_data(zend_object_iterator *iter) /* {{{ */
940 {
941 zend_user_iterator *user_it = (zend_user_iterator *) iter;
942 spl_heap_object *object = Z_SPLHEAP_P(&iter->data);
943
944 if (UNEXPECTED(spl_heap_consistency_validations(object, false) != SUCCESS)) {
945 return NULL;
946 }
947
948 if (object->heap->count == 0) {
949 return NULL;
950 }
951
952 if (Z_ISUNDEF(user_it->value)) {
953 spl_pqueue_elem *elem = spl_heap_elem(object->heap, 0);
954 spl_pqueue_extract_helper(&user_it->value, elem, object->flags);
955 }
956 return &user_it->value;
957 }
958 /* }}} */
959
spl_heap_it_get_current_key(zend_object_iterator * iter,zval * key)960 static void spl_heap_it_get_current_key(zend_object_iterator *iter, zval *key) /* {{{ */
961 {
962 spl_heap_object *object = Z_SPLHEAP_P(&iter->data);
963
964 ZVAL_LONG(key, object->heap->count - 1);
965 }
966 /* }}} */
967
spl_heap_it_move_forward(zend_object_iterator * iter)968 static void spl_heap_it_move_forward(zend_object_iterator *iter) /* {{{ */
969 {
970 spl_heap_object *object = Z_SPLHEAP_P(&iter->data);
971
972 if (UNEXPECTED(spl_heap_consistency_validations(object, false) != SUCCESS)) {
973 return;
974 }
975
976 spl_ptr_heap_delete_top(object->heap, NULL, &iter->data);
977 zend_user_it_invalidate_current(iter);
978 }
979 /* }}} */
980
981 /* {{{ Return current array key */
PHP_METHOD(SplHeap,key)982 PHP_METHOD(SplHeap, key)
983 {
984 spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
985
986 if (zend_parse_parameters_none() == FAILURE) {
987 RETURN_THROWS();
988 }
989
990 RETURN_LONG(intern->heap->count - 1);
991 }
992 /* }}} */
993
994 /* {{{ Move to next entry */
PHP_METHOD(SplHeap,next)995 PHP_METHOD(SplHeap, next)
996 {
997 spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
998
999 if (zend_parse_parameters_none() == FAILURE) {
1000 RETURN_THROWS();
1001 }
1002
1003 spl_ptr_heap_delete_top(intern->heap, NULL, ZEND_THIS);
1004 }
1005 /* }}} */
1006
1007 /* {{{ Check whether the datastructure contains more entries */
PHP_METHOD(SplHeap,valid)1008 PHP_METHOD(SplHeap, valid)
1009 {
1010 spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1011
1012 if (zend_parse_parameters_none() == FAILURE) {
1013 RETURN_THROWS();
1014 }
1015
1016 RETURN_BOOL(intern->heap->count != 0);
1017 }
1018 /* }}} */
1019
1020 /* {{{ Rewind the datastructure back to the start */
PHP_METHOD(SplHeap,rewind)1021 PHP_METHOD(SplHeap, rewind)
1022 {
1023 if (zend_parse_parameters_none() == FAILURE) {
1024 RETURN_THROWS();
1025 }
1026 /* do nothing, the iterator always points to the top element */
1027 }
1028 /* }}} */
1029
1030 /* {{{ Return current datastructure entry */
PHP_METHOD(SplHeap,current)1031 PHP_METHOD(SplHeap, current)
1032 {
1033 spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1034
1035 if (zend_parse_parameters_none() == FAILURE) {
1036 RETURN_THROWS();
1037 }
1038
1039 if (!intern->heap->count) {
1040 RETURN_NULL();
1041 } else {
1042 zval *element = spl_heap_elem(intern->heap, 0);
1043 RETURN_COPY_DEREF(element);
1044 }
1045 }
1046 /* }}} */
1047
1048 /* {{{ Return current datastructure entry */
PHP_METHOD(SplPriorityQueue,current)1049 PHP_METHOD(SplPriorityQueue, current)
1050 {
1051 spl_heap_object *intern = Z_SPLHEAP_P(ZEND_THIS);
1052
1053 if (zend_parse_parameters_none() == FAILURE) {
1054 RETURN_THROWS();
1055 }
1056
1057 if (!intern->heap->count) {
1058 RETURN_NULL();
1059 } else {
1060 spl_pqueue_elem *elem = spl_heap_elem(intern->heap, 0);
1061 spl_pqueue_extract_helper(return_value, elem, intern->flags);
1062 }
1063 }
1064 /* }}} */
1065
1066 /* {{{ */
PHP_METHOD(SplHeap,__debugInfo)1067 PHP_METHOD(SplHeap, __debugInfo)
1068 {
1069 if (zend_parse_parameters_none() == FAILURE) {
1070 RETURN_THROWS();
1071 }
1072
1073 RETURN_ARR(spl_heap_object_get_debug_info(spl_ce_SplHeap, Z_OBJ_P(ZEND_THIS)));
1074 } /* }}} */
1075
1076 /* {{{ */
PHP_METHOD(SplPriorityQueue,__debugInfo)1077 PHP_METHOD(SplPriorityQueue, __debugInfo)
1078 {
1079 if (zend_parse_parameters_none() == FAILURE) {
1080 RETURN_THROWS();
1081 }
1082
1083 RETURN_ARR(spl_heap_object_get_debug_info(spl_ce_SplPriorityQueue, Z_OBJ_P(ZEND_THIS)));
1084 } /* }}} */
1085
1086 /* iterator handler table */
1087 static const zend_object_iterator_funcs spl_heap_it_funcs = {
1088 spl_heap_it_dtor,
1089 spl_heap_it_valid,
1090 spl_heap_it_get_current_data,
1091 spl_heap_it_get_current_key,
1092 spl_heap_it_move_forward,
1093 spl_heap_it_rewind,
1094 NULL,
1095 NULL, /* get_gc */
1096 };
1097
1098 static const zend_object_iterator_funcs spl_pqueue_it_funcs = {
1099 spl_heap_it_dtor,
1100 spl_heap_it_valid,
1101 spl_pqueue_it_get_current_data,
1102 spl_heap_it_get_current_key,
1103 spl_heap_it_move_forward,
1104 spl_heap_it_rewind,
1105 NULL,
1106 NULL, /* get_gc */
1107 };
1108
spl_heap_get_iterator(zend_class_entry * ce,zval * object,int by_ref)1109 zend_object_iterator *spl_heap_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
1110 {
1111 if (by_ref) {
1112 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
1113 return NULL;
1114 }
1115
1116 zend_user_iterator *iterator = emalloc(sizeof(zend_user_iterator));
1117 zend_iterator_init(&iterator->it);
1118
1119 ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
1120 iterator->it.funcs = &spl_heap_it_funcs;
1121 iterator->ce = ce;
1122 ZVAL_UNDEF(&iterator->value);
1123
1124 return &iterator->it;
1125 }
1126 /* }}} */
1127
spl_pqueue_get_iterator(zend_class_entry * ce,zval * object,int by_ref)1128 zend_object_iterator *spl_pqueue_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
1129 {
1130 if (by_ref) {
1131 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
1132 return NULL;
1133 }
1134
1135 zend_user_iterator *iterator = emalloc(sizeof(zend_user_iterator));
1136 zend_iterator_init(&iterator->it);
1137
1138 ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
1139 iterator->it.funcs = &spl_pqueue_it_funcs;
1140 iterator->ce = ce;
1141 ZVAL_UNDEF(&iterator->value);
1142
1143 return &iterator->it;
1144 }
1145 /* }}} */
1146
PHP_MINIT_FUNCTION(spl_heap)1147 PHP_MINIT_FUNCTION(spl_heap) /* {{{ */
1148 {
1149 spl_ce_SplHeap = register_class_SplHeap(zend_ce_iterator, zend_ce_countable);
1150 spl_ce_SplHeap->create_object = spl_heap_object_new;
1151 spl_ce_SplHeap->get_iterator = spl_heap_get_iterator;
1152
1153 memcpy(&spl_handler_SplHeap, &std_object_handlers, sizeof(zend_object_handlers));
1154
1155 spl_handler_SplHeap.offset = XtOffsetOf(spl_heap_object, std);
1156 spl_handler_SplHeap.clone_obj = spl_heap_object_clone;
1157 spl_handler_SplHeap.count_elements = spl_heap_object_count_elements;
1158 spl_handler_SplHeap.get_gc = spl_heap_object_get_gc;
1159 spl_handler_SplHeap.free_obj = spl_heap_object_free_storage;
1160
1161 spl_ce_SplMinHeap = register_class_SplMinHeap(spl_ce_SplHeap);
1162 spl_ce_SplMinHeap->create_object = spl_heap_object_new;
1163 spl_ce_SplMinHeap->get_iterator = spl_heap_get_iterator;
1164
1165 spl_ce_SplMaxHeap = register_class_SplMaxHeap(spl_ce_SplHeap);
1166 spl_ce_SplMaxHeap->create_object = spl_heap_object_new;
1167 spl_ce_SplMaxHeap->get_iterator = spl_heap_get_iterator;
1168
1169 spl_ce_SplPriorityQueue = register_class_SplPriorityQueue(zend_ce_iterator, zend_ce_countable);
1170 spl_ce_SplPriorityQueue->create_object = spl_heap_object_new;
1171 spl_ce_SplPriorityQueue->get_iterator = spl_pqueue_get_iterator;
1172
1173 memcpy(&spl_handler_SplPriorityQueue, &std_object_handlers, sizeof(zend_object_handlers));
1174
1175 spl_handler_SplPriorityQueue.offset = XtOffsetOf(spl_heap_object, std);
1176 spl_handler_SplPriorityQueue.clone_obj = spl_heap_object_clone;
1177 spl_handler_SplPriorityQueue.count_elements = spl_heap_object_count_elements;
1178 spl_handler_SplPriorityQueue.get_gc = spl_pqueue_object_get_gc;
1179 spl_handler_SplPriorityQueue.free_obj = spl_heap_object_free_storage;
1180
1181 return SUCCESS;
1182 }
1183 /* }}} */
1184