22
22
#ifdef FRONTEND
23
23
#include "common/logging.h"
24
24
#endif
25
+ #include "common/hashfn.h"
25
26
#include "lib/binaryheap.h"
26
27
28
+ /*
29
+ * Define parameters for hash table code generation. The interface is *also*
30
+ * declared in binaryheap.h (to generate the types, which are externally
31
+ * visible).
32
+ */
33
+ #define SH_PREFIX bh_nodeidx
34
+ #define SH_ELEMENT_TYPE bh_nodeidx_entry
35
+ #define SH_KEY_TYPE bh_node_type
36
+ #define SH_KEY key
37
+ #define SH_HASH_KEY (tb , key ) \
38
+ hash_bytes((const unsigned char *) &key, sizeof(bh_node_type))
39
+ #define SH_EQUAL (tb , a , b ) (memcmp(&a, &b, sizeof(bh_node_type)) == 0)
40
+ #define SH_SCOPE extern
41
+ #ifdef FRONTEND
42
+ #define SH_RAW_ALLOCATOR pg_malloc0
43
+ #endif
44
+ #define SH_STORE_HASH
45
+ #define SH_GET_HASH (tb , a ) a->hash
46
+ #define SH_DEFINE
47
+ #include "lib/simplehash.h"
48
+
27
49
static void sift_down (binaryheap * heap , int node_off );
28
50
static void sift_up (binaryheap * heap , int node_off );
29
51
@@ -34,9 +56,14 @@ static void sift_up(binaryheap *heap, int node_off);
34
56
* of nodes, and with the heap property defined by the given comparator
35
57
* function, which will be invoked with the additional argument specified by
36
58
* 'arg'.
59
+ *
60
+ * If 'indexed' is true, we create a hash table to track each node's
61
+ * index in the heap, enabling to perform some operations such as
62
+ * binaryheap_remove_node_ptr() etc.
37
63
*/
38
64
binaryheap *
39
- binaryheap_allocate (int num_nodes , binaryheap_comparator compare , void * arg )
65
+ binaryheap_allocate (int num_nodes , binaryheap_comparator compare ,
66
+ bool indexed , void * arg )
40
67
{
41
68
binaryheap * heap ;
42
69
@@ -48,6 +75,17 @@ binaryheap_allocate(int num_nodes, binaryheap_comparator compare, void *arg)
48
75
heap -> bh_size = 0 ;
49
76
heap -> bh_has_heap_property = true;
50
77
heap -> bh_nodes = (bh_node_type * ) palloc (sizeof (bh_node_type ) * num_nodes );
78
+ heap -> bh_nodeidx = NULL ;
79
+
80
+ if (indexed )
81
+ {
82
+ #ifdef FRONTEND
83
+ heap -> bh_nodeidx = bh_nodeidx_create (num_nodes , NULL );
84
+ #else
85
+ heap -> bh_nodeidx = bh_nodeidx_create (CurrentMemoryContext , num_nodes ,
86
+ NULL );
87
+ #endif
88
+ }
51
89
52
90
return heap ;
53
91
}
@@ -63,6 +101,9 @@ binaryheap_reset(binaryheap *heap)
63
101
{
64
102
heap -> bh_size = 0 ;
65
103
heap -> bh_has_heap_property = true;
104
+
105
+ if (binaryheap_indexed (heap ))
106
+ bh_nodeidx_reset (heap -> bh_nodeidx );
66
107
}
67
108
68
109
/*
@@ -73,6 +114,9 @@ binaryheap_reset(binaryheap *heap)
73
114
void
74
115
binaryheap_free (binaryheap * heap )
75
116
{
117
+ if (binaryheap_indexed (heap ))
118
+ bh_nodeidx_destroy (heap -> bh_nodeidx );
119
+
76
120
pfree (heap -> bh_nodes );
77
121
pfree (heap );
78
122
}
@@ -115,6 +159,67 @@ enlarge_node_array(binaryheap *heap)
115
159
sizeof (bh_node_type ) * heap -> bh_space );
116
160
}
117
161
162
+ /*
163
+ * Set the given node at the 'index' and track it if required.
164
+ *
165
+ * Return true if the node's index is already tracked.
166
+ */
167
+ static bool
168
+ set_node (binaryheap * heap , bh_node_type node , int index )
169
+ {
170
+ bool found = false;
171
+
172
+ /* Set the node to the nodes array */
173
+ heap -> bh_nodes [index ] = node ;
174
+
175
+ if (binaryheap_indexed (heap ))
176
+ {
177
+ bh_nodeidx_entry * ent ;
178
+
179
+ /* Keep track of the node index */
180
+ ent = bh_nodeidx_insert (heap -> bh_nodeidx , node , & found );
181
+ ent -> index = index ;
182
+ }
183
+
184
+ return found ;
185
+ }
186
+
187
+ /*
188
+ * Remove the node's index from the hash table if the heap is indexed.
189
+ */
190
+ static inline void
191
+ delete_nodeidx (binaryheap * heap , bh_node_type node )
192
+ {
193
+ if (binaryheap_indexed (heap ))
194
+ bh_nodeidx_delete (heap -> bh_nodeidx , node );
195
+ }
196
+
197
+ /*
198
+ * Replace the existing node at 'idx' with the given 'new_node'. Also
199
+ * update their positions accordingly. Note that we assume the new_node's
200
+ * position is already tracked if enabled, i.e. the new_node is already
201
+ * present in the heap.
202
+ */
203
+ static void
204
+ replace_node (binaryheap * heap , int index , bh_node_type new_node )
205
+ {
206
+ bool found PG_USED_FOR_ASSERTS_ONLY ;
207
+
208
+ /* Quick return if not necessary to move */
209
+ if (heap -> bh_nodes [index ] == new_node )
210
+ return ;
211
+
212
+ /* Remove the overwritten node's index */
213
+ delete_nodeidx (heap , heap -> bh_nodes [index ]);
214
+
215
+ /*
216
+ * Replace it with the given new node. This node's position must also be
217
+ * tracked as we assume to replace the node with the existing node.
218
+ */
219
+ found = set_node (heap , new_node , index );
220
+ Assert (!binaryheap_indexed (heap ) || found );
221
+ }
222
+
118
223
/*
119
224
* binaryheap_add_unordered
120
225
*
@@ -131,7 +236,7 @@ binaryheap_add_unordered(binaryheap *heap, bh_node_type d)
131
236
enlarge_node_array (heap );
132
237
133
238
heap -> bh_has_heap_property = false;
134
- heap -> bh_nodes [ heap -> bh_size ] = d ;
239
+ set_node ( heap , d , heap -> bh_size ) ;
135
240
heap -> bh_size ++ ;
136
241
}
137
242
@@ -164,7 +269,7 @@ binaryheap_add(binaryheap *heap, bh_node_type d)
164
269
if (heap -> bh_size >= heap -> bh_space )
165
270
enlarge_node_array (heap );
166
271
167
- heap -> bh_nodes [ heap -> bh_size ] = d ;
272
+ set_node ( heap , d , heap -> bh_size ) ;
168
273
heap -> bh_size ++ ;
169
274
sift_up (heap , heap -> bh_size - 1 );
170
275
}
@@ -205,14 +310,16 @@ binaryheap_remove_first(binaryheap *heap)
205
310
if (heap -> bh_size == 1 )
206
311
{
207
312
heap -> bh_size -- ;
313
+ delete_nodeidx (heap , result );
314
+
208
315
return result ;
209
316
}
210
317
211
318
/*
212
319
* Remove the last node, placing it in the vacated root entry, and sift
213
320
* the new root node down to its correct position.
214
321
*/
215
- heap -> bh_nodes [ 0 ] = heap -> bh_nodes [-- heap -> bh_size ];
322
+ replace_node ( heap , 0 , heap -> bh_nodes [-- heap -> bh_size ]) ;
216
323
sift_down (heap , 0 );
217
324
218
325
return result ;
@@ -238,7 +345,7 @@ binaryheap_remove_node(binaryheap *heap, int n)
238
345
heap -> bh_arg );
239
346
240
347
/* remove the last node, placing it in the vacated entry */
241
- heap -> bh_nodes [ n ] = heap -> bh_nodes [heap -> bh_size ];
348
+ replace_node ( heap , n , heap -> bh_nodes [heap -> bh_size ]) ;
242
349
243
350
/* sift as needed to preserve the heap property */
244
351
if (cmp > 0 )
@@ -247,6 +354,77 @@ binaryheap_remove_node(binaryheap *heap, int n)
247
354
sift_down (heap , n );
248
355
}
249
356
357
+ /*
358
+ * binaryheap_remove_node_ptr
359
+ *
360
+ * Similar to binaryheap_remove_node() but removes the given node. The caller
361
+ * must ensure that the given node is in the heap. O(log n) worst case.
362
+ *
363
+ * This function can be used only if the heap is indexed.
364
+ */
365
+ void
366
+ binaryheap_remove_node_ptr (binaryheap * heap , bh_node_type d )
367
+ {
368
+ bh_nodeidx_entry * ent ;
369
+
370
+ Assert (!binaryheap_empty (heap ) && heap -> bh_has_heap_property );
371
+ Assert (binaryheap_indexed (heap ));
372
+
373
+ ent = bh_nodeidx_lookup (heap -> bh_nodeidx , d );
374
+ Assert (ent );
375
+
376
+ binaryheap_remove_node (heap , ent -> index );
377
+ }
378
+
379
+ /*
380
+ * Workhorse for binaryheap_update_up and binaryheap_update_down.
381
+ */
382
+ static void
383
+ resift_node (binaryheap * heap , bh_node_type node , bool sift_dir_up )
384
+ {
385
+ bh_nodeidx_entry * ent ;
386
+
387
+ Assert (!binaryheap_empty (heap ) && heap -> bh_has_heap_property );
388
+ Assert (binaryheap_indexed (heap ));
389
+
390
+ ent = bh_nodeidx_lookup (heap -> bh_nodeidx , node );
391
+ Assert (ent );
392
+ Assert (ent -> index >= 0 && ent -> index < heap -> bh_size );
393
+
394
+ if (sift_dir_up )
395
+ sift_up (heap , ent -> index );
396
+ else
397
+ sift_down (heap , ent -> index );
398
+ }
399
+
400
+ /*
401
+ * binaryheap_update_up
402
+ *
403
+ * Sift the given node up after the node's key is updated. The caller must
404
+ * ensure that the given node is in the heap. O(log n) worst case.
405
+ *
406
+ * This function can be used only if the heap is indexed.
407
+ */
408
+ void
409
+ binaryheap_update_up (binaryheap * heap , bh_node_type d )
410
+ {
411
+ resift_node (heap , d , true);
412
+ }
413
+
414
+ /*
415
+ * binaryheap_update_down
416
+ *
417
+ * Sift the given node down after the node's key is updated. The caller must
418
+ * ensure that the given node is in the heap. O(log n) worst case.
419
+ *
420
+ * This function can be used only if the heap is indexed.
421
+ */
422
+ void
423
+ binaryheap_update_down (binaryheap * heap , bh_node_type d )
424
+ {
425
+ resift_node (heap , d , false);
426
+ }
427
+
250
428
/*
251
429
* binaryheap_replace_first
252
430
*
@@ -259,7 +437,7 @@ binaryheap_replace_first(binaryheap *heap, bh_node_type d)
259
437
{
260
438
Assert (!binaryheap_empty (heap ) && heap -> bh_has_heap_property );
261
439
262
- heap -> bh_nodes [ 0 ] = d ;
440
+ replace_node ( heap , 0 , d ) ;
263
441
264
442
if (heap -> bh_size > 1 )
265
443
sift_down (heap , 0 );
@@ -301,11 +479,11 @@ sift_up(binaryheap *heap, int node_off)
301
479
* Otherwise, swap the parent value with the hole, and go on to check
302
480
* the node's new parent.
303
481
*/
304
- heap -> bh_nodes [ node_off ] = parent_val ;
482
+ set_node ( heap , parent_val , node_off ) ;
305
483
node_off = parent_off ;
306
484
}
307
485
/* Re-fill the hole */
308
- heap -> bh_nodes [ node_off ] = node_val ;
486
+ set_node ( heap , node_val , node_off ) ;
309
487
}
310
488
311
489
/*
@@ -360,9 +538,9 @@ sift_down(binaryheap *heap, int node_off)
360
538
* Otherwise, swap the hole with the child that violates the heap
361
539
* property; then go on to check its children.
362
540
*/
363
- heap -> bh_nodes [ node_off ] = heap -> bh_nodes [swap_off ];
541
+ set_node ( heap , heap -> bh_nodes [swap_off ], node_off ) ;
364
542
node_off = swap_off ;
365
543
}
366
544
/* Re-fill the hole */
367
- heap -> bh_nodes [ node_off ] = node_val ;
545
+ set_node ( heap , node_val , node_off ) ;
368
546
}
0 commit comments