1 #ifndef DASYNC_DARYHEAP_H_INCLUDED
2 #define DASYNC_DARYHEAP_H_INCLUDED
4 #include "dasynq-svec.h"
12 * Priority queue implementation based on a binary heap.
14 * Heap entry "handles" maintain an index into the heap. When the position of a node in the heap
15 * changes, its handle must be updated.
18 * P : priority type (eg int)
19 * Compare : functional object type to compare priorities
21 template <typename T, typename P, typename Compare = std::less<P>, int N = 4>
26 using handle_t_r = handle_t &;
37 heap_node(handle_t * hnd, const P &odata) : data(odata), hnd_p(hnd)
45 svector<heap_node> hvec;
47 using hindex_t = typename decltype(hvec)::size_type;
49 hindex_t num_nodes = 0;
53 // Handle to an element on the heap in the node buffer; also contains the data associated
54 // with the node. (Alternative implementation would be to store the heap data in a
55 // separate container, and have the handle be an index into that container).
59 // The data member is kept in a union so it doesn't get constructed/destructed
60 // automatically, and we can construct it lazily.
69 handle_t(const handle_t &) = delete;
73 // Initialise a handle (if it does not have a suitable constructor). Need not do anything
74 // but may store a sentinel value to mark the handle as inactive. It should not be
75 // necessary to call this, really.
76 static void init_handle(handle_t &h) noexcept
82 // Bubble a newly added timer down to the correct position
83 bool bubble_down(hindex_t pos) noexcept
85 handle_t * ohndl = hvec[pos].hnd_p;
86 P op = hvec[pos].data;
87 return bubble_down(pos, ohndl, op);
90 bool bubble_down(hindex_t pos, handle_t * ohndl, const P &op) noexcept
92 // int pos = v.size() - 1;
95 hindex_t parent = (pos - 1) / N;
96 if (! lt(op, hvec[parent].data)) {
100 hvec[pos] = hvec[parent];
101 hvec[pos].hnd_p->heap_index = pos;
105 hvec[pos].hnd_p = ohndl;
107 ohndl->heap_index = pos;
112 void bubble_up(hindex_t pos = 0) noexcept
114 P p = hvec[pos].data;
115 handle_t &h = *(hvec[pos].hnd_p);
116 bubble_up(pos, h, p);
119 void bubble_up(hindex_t pos, handle_t &h, const P &p) noexcept
121 hindex_t rmax = hvec.size() - 1;
127 hindex_t max = (rmax - 1) / N;
130 hindex_t lchild = pos * N + 1;
131 hindex_t selchild = lchild;
132 hindex_t rchild = std::min(lchild + N, rmax);
133 for (hindex_t i = lchild + 1; i < rchild; i++) {
134 if (lt(hvec[i].data, hvec[selchild].data)) {
139 if (! lt(hvec[selchild].data, p)) {
143 hvec[pos] = hvec[selchild];
144 hvec[pos].hnd_p->heap_index = pos;
148 hvec[pos].hnd_p = &h;
153 void remove_h(hindex_t hidx) noexcept
155 hvec[hidx].hnd_p->heap_index = -1;
156 if (hvec.size() != hidx + 1) {
157 bubble_up(hidx, *(hvec.back().hnd_p), hvec.back().data);
167 T & node_data(handle_t & index) noexcept
169 return index.hd_u.hd;
172 // Allocate a slot, but do not incorporate into the heap:
173 // u... : parameters for data constructor T::T(...)
174 template <typename ...U> void allocate(handle_t & hnd, U... u)
176 new (& hnd.hd_u.hd) T(u...);
178 constexpr hindex_t max_allowed = std::numeric_limits<hindex_t>::is_signed ?
179 std::numeric_limits<hindex_t>::max() : ((hindex_t) - 2);
181 if (num_nodes == max_allowed) {
182 throw std::bad_alloc();
187 if (__builtin_expect(hvec.capacity() < num_nodes, 0)) {
188 hindex_t half_point = max_allowed / 2;
190 if (__builtin_expect(num_nodes < half_point, 1)) {
191 hvec.reserve(num_nodes * 2);
194 hvec.reserve(max_allowed);
197 catch (std::bad_alloc &e) {
198 hvec.reserve(num_nodes);
204 void deallocate(handle_t & index) noexcept
209 // shrink the capacity of hvec if num_nodes is sufficiently less than
210 // its current capacity:
211 if (num_nodes < hvec.capacity() / 4) {
212 hvec.shrink_to(num_nodes * 2);
216 bool insert(handle_t & hnd) noexcept
219 return insert(hnd, pval);
222 bool insert(handle_t & hnd, const P &pval) noexcept
224 hnd.heap_index = hvec.size();
225 //hvec.emplace_back(&hnd, pval);
227 return bubble_down(hvec.size() - 1, &hnd, pval);
230 // Get the root node handle. (Returns a handle_t or reference to handle_t).
231 handle_t & get_root()
233 return * hvec[0].hnd_p;
236 P &get_root_priority()
246 void remove(handle_t & hnd)
248 remove_h(hnd.heap_index);
256 bool is_queued(handle_t & hnd)
258 return hnd.heap_index != (hindex_t) -1;
261 // Set a node priority. Returns true iff the node becomes the root node (and wasn't before).
262 bool set_priority(handle_t & hnd, const P& p)
264 int heap_index = hnd.heap_index;
267 if (lt(hvec[heap_index].data, p)) {
269 hvec[heap_index].data = p;
270 bubble_up(heap_index);
275 hvec[heap_index].data = p;
276 return bubble_down(heap_index);
282 dary_heap(const dary_heap &) = delete;