Coverage Report

Created: 2026-04-29 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 {}