Skip to content

Commit d7ee6a5

Browse files
authored
Merge pull request #1747 from jere8184/fix_paring_heap_UB
fix paring heap UB
2 parents 0ce621f + aef6cf0 commit d7ee6a5

File tree

2 files changed

+199
-4
lines changed

2 files changed

+199
-4
lines changed

libopenage/datastructure/pairing_heap.h

+190-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2014-2024 the openage authors. See copying.md for legal info.
1+
// Copyright 2014-2025 the openage authors. See copying.md for legal info.
22

33
#pragma once
44

@@ -17,6 +17,7 @@
1717
*/
1818

1919
#include <functional>
20+
#include <iterator>
2021
#include <memory>
2122
#include <type_traits>
2223
#include <unordered_set>
@@ -36,13 +37,17 @@ template <typename T,
3637
typename heapnode_t>
3738
class PairingHeap;
3839

40+
template <typename T, typename compare>
41+
class PairingHeapIterator;
42+
3943

4044
template <typename T, typename compare = std::less<T>>
4145
class PairingHeapNode {
4246
public:
4347
using this_type = PairingHeapNode<T, compare>;
4448

4549
friend PairingHeap<T, compare, this_type>;
50+
friend PairingHeapIterator<T, compare>;
4651

4752
T data;
4853
compare cmp;
@@ -186,6 +191,166 @@ class PairingHeapNode {
186191
};
187192

188193

194+
/**
195+
* @brief Iterator class for PairingHeap.
196+
*
197+
* This class provides a bidirectional iterator for the PairingHeap data structure.
198+
* It allows traversal of the heap in both forward and backward directions.
199+
* It is depth-first traversal.
200+
*
201+
* @tparam T The type of elements stored in the heap.
202+
* @tparam compare The comparison functor used to order the elements.
203+
*/
204+
template <typename T, typename compare = std::less<T>>
205+
class PairingHeapIterator {
206+
public:
207+
using iterator_category = std::bidirectional_iterator_tag;
208+
using value_type = T;
209+
using difference_type = std::ptrdiff_t;
210+
using pointer = T *;
211+
using reference = T &;
212+
213+
/**
214+
* @brief Constructs an iterator starting at the given node.
215+
*
216+
* @param node The starting node for the iterator.
217+
*/
218+
PairingHeapIterator(PairingHeapNode<T, compare> *node) :
219+
current(node) {}
220+
221+
/**
222+
* @brief Dereference operator.
223+
*
224+
* @return A reference to the data stored in the current node.
225+
*/
226+
reference operator*() const {
227+
return current->data;
228+
}
229+
230+
/**
231+
* @brief Member access operator.
232+
*
233+
* @return A pointer to the data stored in the current node.
234+
*/
235+
pointer operator->() const {
236+
return &(current->data);
237+
}
238+
239+
240+
/**
241+
* @brief Get current node.
242+
*
243+
* @return The current node.
244+
*/
245+
PairingHeapNode<T, compare> *node() {
246+
return current;
247+
}
248+
249+
250+
/**
251+
* @brief Pre-increment operator.
252+
*
253+
* Moves the iterator to the next node in the heap.
254+
*
255+
* @return A reference to the incremented iterator.
256+
*/
257+
PairingHeapIterator &operator++() {
258+
if (current->first_child) {
259+
current = current->first_child;
260+
}
261+
else if (current->next_sibling) {
262+
current = current->next_sibling;
263+
}
264+
else {
265+
while (current->parent && !current->parent->next_sibling) {
266+
current = current->parent;
267+
}
268+
if (current->parent) {
269+
current = current->parent->next_sibling;
270+
}
271+
else {
272+
current = nullptr;
273+
}
274+
}
275+
return *this;
276+
}
277+
278+
/**
279+
* @brief Post-increment operator.
280+
*
281+
* Moves the iterator to the next node in the heap.
282+
*
283+
* @return A copy of the iterator before incrementing.
284+
*/
285+
PairingHeapIterator operator++(int) {
286+
PairingHeapIterator tmp = *this;
287+
++(*this);
288+
return tmp;
289+
}
290+
291+
/**
292+
* @brief Pre-decrement operator.
293+
*
294+
* Moves the iterator to the previous node in the heap.
295+
*
296+
* @return A reference to the decremented iterator.
297+
*/
298+
PairingHeapIterator &operator--() {
299+
if (current->prev_sibling) {
300+
current = current->prev_sibling;
301+
while (current->first_child) {
302+
current = current->first_child;
303+
while (current->next_sibling) {
304+
current = current->next_sibling;
305+
}
306+
}
307+
}
308+
else if (current->parent) {
309+
current = current->parent;
310+
}
311+
return *this;
312+
}
313+
314+
/**
315+
* @brief Post-decrement operator.
316+
*
317+
* Moves the iterator to the previous node in the heap.
318+
*
319+
* @return A copy of the iterator before decrementing.
320+
*/
321+
PairingHeapIterator operator--(int) {
322+
PairingHeapIterator tmp = *this;
323+
--(*this);
324+
return tmp;
325+
}
326+
327+
/**
328+
* @brief Equality comparison operator.
329+
*
330+
* @param a The first iterator to compare.
331+
* @param b The second iterator to compare.
332+
* @return True if both iterators point to the same node, false otherwise.
333+
*/
334+
friend bool operator==(const PairingHeapIterator &a, const PairingHeapIterator &b) {
335+
return a.current == b.current;
336+
}
337+
338+
/**
339+
* @brief Inequality comparison operator.
340+
*
341+
* @param a The first iterator to compare.
342+
* @param b The second iterator to compare.
343+
* @return True if the iterators point to different nodes, false otherwise.
344+
*/
345+
friend bool operator!=(const PairingHeapIterator &a, const PairingHeapIterator &b) {
346+
return a.current != b.current;
347+
}
348+
349+
private:
350+
PairingHeapNode<T, compare> *current; ///< Pointer to the current node in the heap.
351+
};
352+
353+
189354
/**
190355
* (Quite) efficient heap implementation.
191356
*/
@@ -196,6 +361,7 @@ class PairingHeap final {
196361
public:
197362
using element_t = heapnode_t *;
198363
using this_type = PairingHeap<T, compare, heapnode_t>;
364+
using iterator = PairingHeapIterator<T, compare>;
199365

200366
/**
201367
* create a empty heap.
@@ -404,11 +570,24 @@ class PairingHeap final {
404570
* erase all elements on the heap.
405571
*/
406572
void clear() {
407-
auto delete_node = [](element_t node) { delete node; };
408-
this->iter_all<true>(delete_node);
573+
std::vector<element_t> to_delete;
574+
to_delete.reserve(this->size());
575+
576+
// collect all node pointers to delete
577+
for (iterator it = this->begin(); it != this->end(); it++) {
578+
to_delete.push_back(it.node());
579+
}
580+
581+
// delete all nodes
582+
for (element_t node : to_delete) {
583+
delete node;
584+
}
585+
586+
// reset heap state to empty
409587
this->root_node = nullptr;
410588
this->node_count = 0;
411589
#if OPENAGE_PAIRINGHEAP_DEBUG
590+
// clear the node set for debugging
412591
this->nodes.clear();
413592
#endif
414593
}
@@ -590,6 +769,14 @@ class PairingHeap final {
590769
this->walk_tree<reverse>(this->root_node, func);
591770
}
592771

772+
iterator begin() const {
773+
return iterator(this->root_node);
774+
}
775+
776+
iterator end() const {
777+
return iterator(nullptr);
778+
}
779+
593780
private:
594781
/**
595782
* Apply the given function to all nodes in the tree.

libopenage/datastructure/tests.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2014-2024 the openage authors. See copying.md for legal info.
1+
// Copyright 2014-2025 the openage authors. See copying.md for legal info.
22

33
#include "tests.h"
44

@@ -135,6 +135,14 @@ void pairing_heap_3() {
135135
heap.push(heap_elem{3});
136136
heap.push(heap_elem{4});
137137
heap.push(heap_elem{5});
138+
139+
size_t i = 0;
140+
std::array<int, 6> expected{0, 5, 4, 3, 2, 1};
141+
for (auto &elem : heap) {
142+
TESTEQUALS(elem.data, expected.at(i));
143+
i++;
144+
}
145+
138146
heap.pop(); // trigger pairing
139147

140148
heap.clear();

0 commit comments

Comments
 (0)