Coverage Report

Created: 2026-06-02 06:39

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