Skip to content

Commit 454da8f

Browse files
committed
[Heap] Rework removals
1 parent 612c391 commit 454da8f

File tree

3 files changed

+57
-50
lines changed

3 files changed

+57
-50
lines changed

Sources/PriorityQueueModule/Heap+UnsafeHandle.swift

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ extension Heap._UnsafeHandle {
6363
internal func swapAt(_ i: _Node, _ j: _Node) {
6464
buffer.swapAt(i.offset, j.offset)
6565
}
66+
67+
/// Swaps the element at the given node with the supplied value.
68+
@inlinable @inline(__always)
69+
internal func swapAt(_ i: _Node, with value: inout Element) {
70+
let p = buffer.baseAddress.unsafelyUnwrapped + i.offset
71+
swap(&p.pointee, &value)
72+
}
6673
}
6774

6875
extension Heap._UnsafeHandle {
@@ -132,19 +139,10 @@ extension Heap._UnsafeHandle {
132139
}
133140

134141
extension Heap._UnsafeHandle {
135-
@inline(__always)
136-
@inlinable
137-
internal func trickleDown(_ node: _Node) {
138-
if node.isMinLevel {
139-
trickleDownMin(node)
140-
} else {
141-
trickleDownMax(node)
142-
}
143-
}
144-
145-
@inline(__always)
142+
@_effects(releasenone)
146143
@inlinable
147144
internal func trickleDownMin(_ node: _Node) {
145+
assert(node.isMinLevel)
148146
var node = node
149147

150148
while let minDescendant = minChildOrGrandchild(of: node) {
@@ -167,9 +165,10 @@ extension Heap._UnsafeHandle {
167165
}
168166
}
169167

170-
@inline(__always)
168+
@_effects(releasenone)
171169
@inlinable
172170
internal func trickleDownMax(_ node: _Node) {
171+
assert(!node.isMinLevel)
173172
var node = node
174173

175174
while let maxDescendant = maxChildOrGrandchild(of: node) {

Sources/PriorityQueueModule/Heap.swift

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -106,28 +106,47 @@ public struct Heap<Element: Comparable> {
106106
/// - Complexity: O(log `count`)
107107
@inlinable
108108
public mutating func popMin() -> Element? {
109-
defer { _checkInvariants() }
110-
return _remove(.root)
109+
guard _storage.count > 0 else { return nil }
110+
111+
var removed = _storage.removeLast()
112+
113+
if _storage.count > 0 {
114+
_update { handle in
115+
let minNode = _Node.root
116+
handle.swapAt(minNode, with: &removed)
117+
handle.trickleDownMin(minNode)
118+
}
119+
}
120+
121+
_checkInvariants()
122+
return removed
111123
}
112124

113125
/// Removes and returns the element with the highest priority, if available.
114126
///
115127
/// - Complexity: O(log `count`)
116128
@inlinable
117129
public mutating func popMax() -> Element? {
118-
defer { _checkInvariants() }
119-
guard count > 2 else {
120-
// If count is 0, `popLast` will return `nil`
121-
// If count is 1, the last (and only) item is the max
122-
// If count is 2, the last item is the max (as it's the only item in the
123-
// first max level)
124-
return _storage.popLast()
125-
}
126-
// The max item is the larger of the two items in the first max level
127-
let max = _storage.withUnsafeBufferPointer { buffer in
128-
_Node(offset: buffer[2] > buffer[1] ? 2 : 1, level: 1)
130+
guard _storage.count > 2 else { return _storage.popLast() }
131+
132+
var removed = _storage.removeLast()
133+
134+
_update { handle in
135+
if handle.count == 2 {
136+
if handle.buffer[1] > removed {
137+
handle.swapAt(.leftMax, with: &removed)
138+
}
139+
} else {
140+
let maxNode = handle.buffer[2] > handle.buffer[1]
141+
? _Node.rightMax
142+
: _Node.leftMax
143+
handle.swapAt(maxNode, with: &removed)
144+
handle.trickleDownMax(maxNode)
145+
}
129146
}
130-
return _remove(max)
147+
148+
_checkInvariants()
149+
return removed
131150
}
132151

133152
/// Removes and returns the element with the lowest priority.
@@ -149,30 +168,6 @@ public struct Heap<Element: Comparable> {
149168
public mutating func removeMax() -> Element {
150169
return popMax()!
151170
}
152-
153-
// MARK: -
154-
155-
@discardableResult
156-
@inline(__always)
157-
@inlinable
158-
internal mutating func _remove(_ node: _Node) -> Element? {
159-
guard _storage.count > node.offset else {
160-
return nil
161-
}
162-
163-
var removed = _storage.removeLast()
164-
165-
if node.offset < _storage.count {
166-
_update { handle in
167-
let p = handle.buffer.baseAddress.unsafelyUnwrapped
168-
swap(&removed, &(p + node.offset).pointee)
169-
//swap(&removed, &handle[node])
170-
handle.trickleDown(node)
171-
}
172-
}
173-
174-
return removed
175-
}
176171
}
177172

178173
// MARK: -

Sources/PriorityQueueModule/_Node.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,24 @@ extension _Node {
7777
}
7878

7979
extension _Node {
80+
/// The root node in the heap.
8081
@inlinable @inline(__always)
8182
internal static var root: Self {
8283
Self.init(offset: 0, level: 0)
8384
}
8485

86+
/// The first max node in the heap. (I.e., the left child of the root.)
87+
@inlinable @inline(__always)
88+
internal static var leftMax: Self {
89+
Self.init(offset: 1, level: 1)
90+
}
91+
92+
/// The second max node in the heap. (I.e., the right child of the root.)
93+
@inlinable @inline(__always)
94+
internal static var rightMax: Self {
95+
Self.init(offset: 2, level: 1)
96+
}
97+
8598
@inlinable
8699
internal var isMinLevel: Bool {
87100
Self.isMinLevel(level)

0 commit comments

Comments
 (0)