/src/swift-nio/Sources/_NIODataStructures/Heap.swift
Line | Count | Source |
1 | | //===----------------------------------------------------------------------===// |
2 | | // |
3 | | // This source file is part of the SwiftNIO open source project |
4 | | // |
5 | | // Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors |
6 | | // Licensed under Apache License v2.0 |
7 | | // |
8 | | // See LICENSE.txt for license information |
9 | | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors |
10 | | // |
11 | | // SPDX-License-Identifier: Apache-2.0 |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | #if canImport(Darwin) |
15 | | import Darwin.C |
16 | | #elseif canImport(Glibc) |
17 | | @preconcurrency import Glibc |
18 | | #elseif canImport(Musl) |
19 | | @preconcurrency import Musl |
20 | | #elseif canImport(WASILibc) |
21 | | @preconcurrency import WASILibc |
22 | | #elseif os(Windows) |
23 | | import ucrt |
24 | | #elseif canImport(Bionic) |
25 | | @preconcurrency import Bionic |
26 | | #else |
27 | | #error("The Heap module was unable to identify your C library.") |
28 | | #endif |
29 | | |
30 | | @usableFromInline |
31 | | internal struct Heap<Element: Comparable> { |
32 | | @usableFromInline |
33 | | internal private(set) var storage: [Element] |
34 | | |
35 | | @inlinable |
36 | 510k | internal init() { |
37 | 510k | self.storage = [] |
38 | 510k | } |
39 | | |
40 | | @inlinable |
41 | 0 | internal func comparator(_ lhs: Element, _ rhs: Element) -> Bool { |
42 | 0 | // This heap is always a min-heap. |
43 | 0 | lhs < rhs |
44 | 0 | } |
45 | | |
46 | | // named `PARENT` in CLRS |
47 | | @inlinable |
48 | 0 | internal func parentIndex(_ i: Int) -> Int { |
49 | 0 | (i - 1) / 2 |
50 | 0 | } |
51 | | |
52 | | // named `LEFT` in CLRS |
53 | | @inlinable |
54 | 0 | internal func leftIndex(_ i: Int) -> Int { |
55 | 0 | 2 * i + 1 |
56 | 0 | } |
57 | | |
58 | | // named `RIGHT` in CLRS |
59 | | @inlinable |
60 | 0 | internal func rightIndex(_ i: Int) -> Int { |
61 | 0 | 2 * i + 2 |
62 | 0 | } |
63 | | |
64 | | // named `MAX-HEAPIFY` in CLRS |
65 | | @inlinable |
66 | 0 | mutating func _heapify(_ index: Int) { |
67 | 0 | let left = self.leftIndex(index) |
68 | 0 | let right = self.rightIndex(index) |
69 | 0 |
|
70 | 0 | var root: Int |
71 | 0 | if left <= (self.storage.count - 1) && self.comparator(storage[left], storage[index]) { |
72 | 0 | root = left |
73 | 0 | } else { |
74 | 0 | root = index |
75 | 0 | } |
76 | 0 |
|
77 | 0 | if right <= (self.storage.count - 1) && self.comparator(storage[right], storage[root]) { |
78 | 0 | root = right |
79 | 0 | } |
80 | 0 |
|
81 | 0 | if root != index { |
82 | 0 | self.storage.swapAt(index, root) |
83 | 0 | self._heapify(root) |
84 | 0 | } |
85 | 0 | } |
86 | | |
87 | | // named `HEAP-INCREASE-KEY` in CRLS |
88 | | @inlinable |
89 | 0 | mutating func _heapRootify(index: Int, key: Element) { |
90 | 0 | var index = index |
91 | 0 | if self.comparator(storage[index], key) { |
92 | 0 | fatalError("New key must be closer to the root than current key") |
93 | 0 | } |
94 | 0 |
|
95 | 0 | self.storage[index] = key |
96 | 0 | while index > 0 && self.comparator(self.storage[index], self.storage[self.parentIndex(index)]) { |
97 | 0 | self.storage.swapAt(index, self.parentIndex(index)) |
98 | 0 | index = self.parentIndex(index) |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | @inlinable |
103 | 68.4k | internal mutating func append(_ value: Element) { |
104 | 68.4k | var i = self.storage.count |
105 | 68.4k | self.storage.append(value) |
106 | 68.4k | while i > 0 && self.comparator(self.storage[i], self.storage[self.parentIndex(i)]) { |
107 | 0 | self.storage.swapAt(i, self.parentIndex(i)) |
108 | 0 | i = self.parentIndex(i) |
109 | 68.4k | } |
110 | 68.4k | } |
111 | | |
112 | | @discardableResult |
113 | | @inlinable |
114 | 233k | internal mutating func removeRoot() -> Element? { |
115 | 233k | self._remove(index: 0) |
116 | 233k | } |
117 | | |
118 | | @discardableResult |
119 | | @inlinable |
120 | 0 | internal mutating func remove(value: Element) -> Bool { |
121 | 0 | if let idx = self.storage.firstIndex(of: value) { |
122 | 0 | self._remove(index: idx) |
123 | 0 | return true |
124 | 0 | } else { |
125 | 0 | return false |
126 | 0 | } |
127 | 0 | } |
128 | | |
129 | | @discardableResult |
130 | | @inlinable |
131 | 0 | internal mutating func removeFirst(where shouldBeRemoved: (Element) throws -> Bool) rethrows -> Element? { |
132 | 0 | guard self.storage.count > 0 else { |
133 | 0 | return nil |
134 | 0 | } |
135 | 0 |
|
136 | 0 | guard let index = try self.storage.firstIndex(where: shouldBeRemoved) else { |
137 | 0 | return nil |
138 | 0 | } |
139 | 0 |
|
140 | 0 | return self._remove(index: index) |
141 | 0 | } |
142 | | |
143 | | @discardableResult |
144 | | @inlinable |
145 | 136k | mutating func _remove(index: Int) -> Element? { |
146 | 136k | guard self.storage.count > 0 else { |
147 | 68.4k | return nil |
148 | 68.4k | } |
149 | 68.4k | let element = self.storage[index] |
150 | 68.4k | if self.storage.count == 1 || self.storage[index] == self.storage[self.storage.count - 1] { |
151 | 68.4k | self.storage.removeLast() |
152 | 68.4k | } else if !self.comparator(self.storage[index], self.storage[self.storage.count - 1]) { |
153 | 0 | self._heapRootify(index: index, key: self.storage[self.storage.count - 1]) |
154 | 0 | self.storage.removeLast() |
155 | 0 | } else { |
156 | 0 | self.storage[index] = self.storage[self.storage.count - 1] |
157 | 0 | self.storage.removeLast() |
158 | 0 | self._heapify(index) |
159 | 0 | } |
160 | 68.4k | return element |
161 | 136k | } |
162 | | } |
163 | | |
164 | | extension Heap: CustomDebugStringConvertible { |
165 | | @inlinable |
166 | 0 | var debugDescription: String { |
167 | 0 | guard self.storage.count > 0 else { |
168 | 0 | return "<empty heap>" |
169 | 0 | } |
170 | 0 | let descriptions = self.storage.map { String(describing: $0) } |
171 | 0 | let maxLen: Int = descriptions.map { $0.count }.max()! // storage checked non-empty above |
172 | 0 | let paddedDescs = descriptions.map { (desc: String) -> String in |
173 | 0 | var desc = desc |
174 | 0 | while desc.count < maxLen { |
175 | 0 | if desc.count % 2 == 0 { |
176 | 0 | desc = " \(desc)" |
177 | 0 | } else { |
178 | 0 | desc = "\(desc) " |
179 | 0 | } |
180 | 0 | } |
181 | 0 | return desc |
182 | 0 | } |
183 | 0 |
|
184 | 0 | var all = "\n" |
185 | 0 | let spacing = String(repeating: " ", count: maxLen) |
186 | 0 | func subtreeWidths(rootIndex: Int) -> (Int, Int) { |
187 | 0 | let lcIdx = self.leftIndex(rootIndex) |
188 | 0 | let rcIdx = self.rightIndex(rootIndex) |
189 | 0 | var leftSpace = 0 |
190 | 0 | var rightSpace = 0 |
191 | 0 | if lcIdx < self.storage.count { |
192 | 0 | let sws = subtreeWidths(rootIndex: lcIdx) |
193 | 0 | leftSpace += sws.0 + sws.1 + maxLen |
194 | 0 | } |
195 | 0 | if rcIdx < self.storage.count { |
196 | 0 | let sws = subtreeWidths(rootIndex: rcIdx) |
197 | 0 | rightSpace += sws.0 + sws.1 + maxLen |
198 | 0 | } |
199 | 0 | return (leftSpace, rightSpace) |
200 | 0 | } |
201 | 0 | for (index, desc) in paddedDescs.enumerated() { |
202 | 0 | let (leftWidth, rightWidth) = subtreeWidths(rootIndex: index) |
203 | 0 | all += String(repeating: " ", count: leftWidth) |
204 | 0 | all += desc |
205 | 0 | all += String(repeating: " ", count: rightWidth) |
206 | 0 |
|
207 | 0 | func height(index: Int) -> Int { |
208 | 0 | Int(log2(Double(index + 1))) |
209 | 0 | } |
210 | 0 | let myHeight = height(index: index) |
211 | 0 | let nextHeight = height(index: index + 1) |
212 | 0 | if myHeight != nextHeight { |
213 | 0 | all += "\n" |
214 | 0 | } else { |
215 | 0 | all += spacing |
216 | 0 | } |
217 | 0 | } |
218 | 0 | all += "\n" |
219 | 0 | return all |
220 | 0 | } |
221 | | } |
222 | | |
223 | | @usableFromInline |
224 | | struct HeapIterator<Element: Comparable>: IteratorProtocol { |
225 | | @usableFromInline |
226 | | var _heap: Heap<Element> |
227 | | |
228 | | @inlinable |
229 | 0 | init(heap: Heap<Element>) { |
230 | 0 | self._heap = heap |
231 | 0 | } |
232 | | |
233 | | @inlinable |
234 | 0 | mutating func next() -> Element? { |
235 | 0 | self._heap.removeRoot() |
236 | 0 | } |
237 | | } |
238 | | |
239 | | extension Heap: Sequence { |
240 | | @inlinable |
241 | 0 | var startIndex: Int { |
242 | 0 | self.storage.startIndex |
243 | 0 | } |
244 | | |
245 | | @inlinable |
246 | 0 | var endIndex: Int { |
247 | 0 | self.storage.endIndex |
248 | 0 | } |
249 | | |
250 | | @inlinable |
251 | 0 | var underestimatedCount: Int { |
252 | 0 | self.storage.count |
253 | 0 | } |
254 | | |
255 | | @inlinable |
256 | 0 | func makeIterator() -> HeapIterator<Element> { |
257 | 0 | HeapIterator(heap: self) |
258 | 0 | } |
259 | | |
260 | | @inlinable |
261 | 0 | subscript(position: Int) -> Element { |
262 | 0 | self.storage[position] |
263 | 0 | } |
264 | | |
265 | | @inlinable |
266 | 0 | func index(after i: Int) -> Int { |
267 | 0 | i + 1 |
268 | 0 | } |
269 | | |
270 | | @inlinable |
271 | 0 | var count: Int { |
272 | 0 | self.storage.count |
273 | 0 | } |
274 | | } |
275 | | |
276 | | extension Heap: Sendable where Element: Sendable {} |
277 | | extension HeapIterator: Sendable where Element: Sendable {} |