bring back the deprecated NIOPriorityQueue module (#250)
Motivation: We moved the priority queue implementation into the main `NIO` module for performance reasons. I initially thought we didn't expose it to external packages but we did (thanks @lukasa). Modifications: Bring back the `NIOPriorityQueue` module but deprecate it. Result: Speed & backwards compatibility :)
This commit is contained in:
parent
34e64a0820
commit
224120c175
|
@ -21,6 +21,7 @@ var targets: [PackageDescription.Target] = [
|
||||||
"CNIODarwin",
|
"CNIODarwin",
|
||||||
"NIOConcurrencyHelpers",
|
"NIOConcurrencyHelpers",
|
||||||
"CNIOAtomics",
|
"CNIOAtomics",
|
||||||
|
"NIOPriorityQueue",
|
||||||
"CNIOSHA1"]),
|
"CNIOSHA1"]),
|
||||||
.target(name: "NIOFoundationCompat", dependencies: ["NIO"]),
|
.target(name: "NIOFoundationCompat", dependencies: ["NIO"]),
|
||||||
.target(name: "CNIOAtomics", dependencies: []),
|
.target(name: "CNIOAtomics", dependencies: []),
|
||||||
|
@ -29,6 +30,8 @@ var targets: [PackageDescription.Target] = [
|
||||||
.target(name: "CNIODarwin", dependencies: []),
|
.target(name: "CNIODarwin", dependencies: []),
|
||||||
.target(name: "NIOConcurrencyHelpers",
|
.target(name: "NIOConcurrencyHelpers",
|
||||||
dependencies: ["CNIOAtomics"]),
|
dependencies: ["CNIOAtomics"]),
|
||||||
|
.target(name: "NIOPriorityQueue",
|
||||||
|
dependencies: []),
|
||||||
.target(name: "NIOHTTP1",
|
.target(name: "NIOHTTP1",
|
||||||
dependencies: ["NIO", "NIOConcurrencyHelpers", "CNIOHTTPParser", "CNIOZlib"]),
|
dependencies: ["NIO", "NIOConcurrencyHelpers", "CNIOHTTPParser", "CNIOZlib"]),
|
||||||
.target(name: "NIOEchoServer",
|
.target(name: "NIOEchoServer",
|
||||||
|
|
|
@ -18,7 +18,7 @@ import Darwin
|
||||||
import Glibc
|
import Glibc
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public enum HeapType {
|
internal enum HeapType {
|
||||||
case maxHeap
|
case maxHeap
|
||||||
case minHeap
|
case minHeap
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
public struct PriorityQueue<Element: Comparable> {
|
internal struct PriorityQueue<Element: Comparable> {
|
||||||
private var heap: Heap<Element>
|
private var heap: Heap<Element>
|
||||||
|
|
||||||
public init(ascending: Bool = false) {
|
public init(ascending: Bool = false) {
|
||||||
|
@ -75,8 +75,8 @@ extension PriorityQueue: Sequence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension PriorityQueue {
|
internal extension PriorityQueue {
|
||||||
public var count: Int {
|
var count: Int {
|
||||||
return self.heap.count
|
return self.heap.count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,284 @@
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the SwiftNIO open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
import Darwin
|
||||||
|
#else
|
||||||
|
import Glibc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
public enum HeapType {
|
||||||
|
case maxHeap
|
||||||
|
case minHeap
|
||||||
|
|
||||||
|
public func comparator<T: Comparable>(type: T.Type) -> (T, T) -> Bool {
|
||||||
|
switch self {
|
||||||
|
case .maxHeap:
|
||||||
|
return (>)
|
||||||
|
case .minHeap:
|
||||||
|
return (<)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
internal struct Heap<T: Comparable> {
|
||||||
|
internal let type: HeapType
|
||||||
|
internal private(set) var storage: ContiguousArray<T> = []
|
||||||
|
private let comparator: (T, T) -> Bool
|
||||||
|
|
||||||
|
internal init?(type: HeapType, storage: ContiguousArray<T>) {
|
||||||
|
self.comparator = type.comparator(type: T.self)
|
||||||
|
self.storage = storage
|
||||||
|
self.type = type
|
||||||
|
if !self.checkHeapProperty() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(type: HeapType) {
|
||||||
|
self.comparator = type.comparator(type: T.self)
|
||||||
|
self.type = type
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: verbatim from CLRS's (Introduction to Algorithms) Heapsort chapter with the following changes
|
||||||
|
// - added a `compare` parameter to make it either a min or a max heap
|
||||||
|
// - renamed `largest` to `root`
|
||||||
|
// - removed `max` from the method names like `maxHeapify`, `maxHeapInsert` etc
|
||||||
|
// - made the arrays 0-based
|
||||||
|
|
||||||
|
// named `PARENT` in CLRS
|
||||||
|
private static func parentIndex(_ i: Int) -> Int {
|
||||||
|
return (i-1) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// named `LEFT` in CLRS
|
||||||
|
private static func leftIndex(_ i: Int) -> Int {
|
||||||
|
return 2*i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// named `RIGHT` in CLRS
|
||||||
|
private static func rightIndex(_ i: Int) -> Int {
|
||||||
|
return 2*i + 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// named `MAX-HEAPIFY` in CLRS
|
||||||
|
private static func heapify(storage: inout ContiguousArray<T>, compare: (T, T) -> Bool, _ i: Int) {
|
||||||
|
let l = Heap<T>.leftIndex(i)
|
||||||
|
let r = Heap<T>.rightIndex(i)
|
||||||
|
|
||||||
|
var root: Int
|
||||||
|
if l <= (storage.count - 1) && compare(storage[l], storage[i]) {
|
||||||
|
root = l
|
||||||
|
} else {
|
||||||
|
root = i
|
||||||
|
}
|
||||||
|
|
||||||
|
if r <= (storage.count - 1) && compare(storage[r], storage[root]) {
|
||||||
|
root = r
|
||||||
|
}
|
||||||
|
|
||||||
|
if root != i {
|
||||||
|
storage.swapAt(i, root)
|
||||||
|
heapify(storage: &storage, compare: compare, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// named `MAX-HEAP-INSERT` in CRLS
|
||||||
|
private static func heapInsert(storage: inout ContiguousArray<T>, compare: (T, T) -> Bool, key: T) {
|
||||||
|
var i = storage.count
|
||||||
|
storage.append(key)
|
||||||
|
while i > 0 && compare(storage[i], storage[parentIndex(i)]) {
|
||||||
|
storage.swapAt(i, parentIndex(i))
|
||||||
|
i = parentIndex(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// named `HEAP-INCREASE-KEY` in CRLS
|
||||||
|
private static func heapRootify(storage: inout ContiguousArray<T>, compare: (T, T) -> Bool, index: Int, key: T) {
|
||||||
|
var index = index
|
||||||
|
if compare(storage[index], key) {
|
||||||
|
fatalError("New key must be closer to the root than current key")
|
||||||
|
}
|
||||||
|
|
||||||
|
storage[index] = key
|
||||||
|
while index > 0 && compare(storage[index], storage[parentIndex(index)]) {
|
||||||
|
storage.swapAt(index, parentIndex(index))
|
||||||
|
index = parentIndex(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Swift interface using the low-level methods above
|
||||||
|
public mutating func append(_ value: T) {
|
||||||
|
Heap<T>.heapInsert(storage: &self.storage, compare: self.comparator, key: value)
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func removeRoot() -> T? {
|
||||||
|
return self.remove(index: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func remove(value: T) -> Bool {
|
||||||
|
if let idx = self.storage.index(of: value) {
|
||||||
|
_ = self.remove(index: idx)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private mutating func remove(index: Int) -> T? {
|
||||||
|
guard self.storage.count > 0 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let element = self.storage[index]
|
||||||
|
if self.storage.count == 1 || self.storage[index] == self.storage[self.storage.count - 1] {
|
||||||
|
_ = self.storage.removeLast()
|
||||||
|
} else if !self.comparator(self.storage[index], self.storage[self.storage.count - 1]) {
|
||||||
|
Heap<T>.heapRootify(storage: &self.storage, compare: self.comparator, index: index, key: self.storage[self.storage.count - 1])
|
||||||
|
_ = self.storage.removeLast()
|
||||||
|
} else {
|
||||||
|
self.storage[index] = self.storage[self.storage.count - 1]
|
||||||
|
_ = self.storage.removeLast()
|
||||||
|
Heap<T>.heapify(storage: &self.storage, compare: self.comparator, index)
|
||||||
|
}
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func checkHeapProperty() -> Bool {
|
||||||
|
func checkHeapProperty(index: Int) -> Bool {
|
||||||
|
let li = Heap<T>.leftIndex(index)
|
||||||
|
let ri = Heap<T>.rightIndex(index)
|
||||||
|
if index >= self.storage.count {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
let me = self.storage[index]
|
||||||
|
var lCond = true
|
||||||
|
var rCond = true
|
||||||
|
if li < self.storage.count {
|
||||||
|
let l = self.storage[li]
|
||||||
|
lCond = !self.comparator(l, me)
|
||||||
|
}
|
||||||
|
if ri < self.storage.count {
|
||||||
|
let r = self.storage[ri]
|
||||||
|
rCond = !self.comparator(r, me)
|
||||||
|
}
|
||||||
|
return lCond && rCond && checkHeapProperty(index: li) && checkHeapProperty(index: ri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkHeapProperty(index: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
extension Heap: CustomDebugStringConvertible {
|
||||||
|
public var debugDescription: String {
|
||||||
|
guard self.storage.count > 0 else {
|
||||||
|
return "<empty heap>"
|
||||||
|
}
|
||||||
|
let descriptions = self.storage.map { String(describing: $0) }
|
||||||
|
let maxLen: Int = descriptions.map { $0.count }.max()!
|
||||||
|
let paddedDescs = descriptions.map { (desc: String) -> String in
|
||||||
|
var desc = desc
|
||||||
|
while desc.count < maxLen {
|
||||||
|
if desc.count % 2 == 0 {
|
||||||
|
desc = " \(desc)"
|
||||||
|
} else {
|
||||||
|
desc = "\(desc) "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return desc
|
||||||
|
}
|
||||||
|
|
||||||
|
var all = "\n"
|
||||||
|
let spacing = String(repeating: " ", count: maxLen)
|
||||||
|
func subtreeWidths(rootIndex: Int) -> (Int, Int) {
|
||||||
|
let lcIdx = Heap<T>.leftIndex(rootIndex)
|
||||||
|
let rcIdx = Heap<T>.rightIndex(rootIndex)
|
||||||
|
var leftSpace = 0
|
||||||
|
var rightSpace = 0
|
||||||
|
if lcIdx < self.storage.count {
|
||||||
|
let sws = subtreeWidths(rootIndex: lcIdx)
|
||||||
|
leftSpace += sws.0 + sws.1 + maxLen
|
||||||
|
}
|
||||||
|
if rcIdx < self.storage.count {
|
||||||
|
let sws = subtreeWidths(rootIndex: rcIdx)
|
||||||
|
rightSpace += sws.0 + sws.1 + maxLen
|
||||||
|
}
|
||||||
|
return (leftSpace, rightSpace)
|
||||||
|
}
|
||||||
|
for (index, desc) in paddedDescs.enumerated() {
|
||||||
|
let (leftWidth, rightWidth) = subtreeWidths(rootIndex: index)
|
||||||
|
all += String(repeating: " ", count: leftWidth)
|
||||||
|
all += desc
|
||||||
|
all += String(repeating: " ", count: rightWidth)
|
||||||
|
|
||||||
|
func height(index: Int) -> Int {
|
||||||
|
return Int(log2(Double(index + 1)))
|
||||||
|
}
|
||||||
|
let myHeight = height(index: index)
|
||||||
|
let nextHeight = height(index: index + 1)
|
||||||
|
if myHeight != nextHeight {
|
||||||
|
all += "\n"
|
||||||
|
} else {
|
||||||
|
all += spacing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all += "\n"
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
struct HeapIterator<T: Comparable>: IteratorProtocol {
|
||||||
|
typealias Element = T
|
||||||
|
|
||||||
|
private var heap: Heap<T>
|
||||||
|
|
||||||
|
init(heap: Heap<T>) {
|
||||||
|
self.heap = heap
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func next() -> T? {
|
||||||
|
return self.heap.removeRoot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
extension Heap: Sequence {
|
||||||
|
typealias Element = T
|
||||||
|
|
||||||
|
var startIndex: Int { return self.storage.startIndex }
|
||||||
|
var endIndex: Int { return self.storage.endIndex }
|
||||||
|
|
||||||
|
var underestimatedCount: Int {
|
||||||
|
return self.storage.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeIterator() -> HeapIterator<T> {
|
||||||
|
return HeapIterator(heap: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript(position: Int) -> T {
|
||||||
|
return self.storage[position]
|
||||||
|
}
|
||||||
|
|
||||||
|
func index(after i: Int) -> Int {
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var count: Int {
|
||||||
|
return self.storage.count
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the SwiftNIO open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
public struct PriorityQueue<Element: Comparable> {
|
||||||
|
private var heap: Heap<Element>
|
||||||
|
|
||||||
|
public init(ascending: Bool = false) {
|
||||||
|
self.heap = Heap(type: ascending ? .minHeap : .maxHeap)
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func remove(_ key: Element) {
|
||||||
|
assert(self.heap.checkHeapProperty(), "broken heap: \(self.heap.debugDescription)")
|
||||||
|
_ = self.heap.remove(value: key)
|
||||||
|
assert(self.heap.checkHeapProperty(), "broken heap: \(self.heap.debugDescription)")
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func push(_ key: Element) {
|
||||||
|
assert(self.heap.checkHeapProperty(), "broken heap: \(self.heap.debugDescription)")
|
||||||
|
self.heap.append(key)
|
||||||
|
assert(self.heap.checkHeapProperty(), "broken heap: \(self.heap.debugDescription)")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func peek() -> Element? {
|
||||||
|
assert(self.heap.checkHeapProperty(), "broken heap: \(self.heap.debugDescription)")
|
||||||
|
return self.heap.storage.first
|
||||||
|
}
|
||||||
|
|
||||||
|
public var isEmpty: Bool {
|
||||||
|
assert(self.heap.checkHeapProperty(), "broken heap: \(self.heap.debugDescription)")
|
||||||
|
return self.heap.storage.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func pop() -> Element? {
|
||||||
|
assert(self.heap.checkHeapProperty(), "broken heap: \(self.heap.debugDescription)")
|
||||||
|
return self.heap.removeRoot()
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func clear() {
|
||||||
|
self.heap = Heap(type: self.heap.type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
extension PriorityQueue: Equatable {
|
||||||
|
public static func ==(lhs: PriorityQueue, rhs: PriorityQueue) -> Bool {
|
||||||
|
return lhs.count == rhs.count && lhs.elementsEqual(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
extension PriorityQueue: Sequence {
|
||||||
|
public struct Iterator: IteratorProtocol {
|
||||||
|
|
||||||
|
private var queue: PriorityQueue<Element>
|
||||||
|
fileprivate init(queue: PriorityQueue<Element>) {
|
||||||
|
self.queue = queue
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func next() -> Element? {
|
||||||
|
return self.queue.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeIterator() -> Iterator {
|
||||||
|
return Iterator(queue: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
public extension PriorityQueue {
|
||||||
|
public var count: Int {
|
||||||
|
return self.heap.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
extension PriorityQueue: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
return "PriorityQueue(count: \(self.underestimatedCount)): \(Array(self))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
extension PriorityQueue {
|
||||||
|
@available(*, deprecated, renamed: "Element")
|
||||||
|
public typealias T = Element
|
||||||
|
@available(*, deprecated, renamed: "PriorityQueue.Iterator")
|
||||||
|
public typealias PriorityQueueIterator<T: Comparable> = PriorityQueue<T>.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, message: "The NIOPriorityQueue module is deprecated and will be removed in the next major release.")
|
||||||
|
extension PriorityQueue.Iterator {
|
||||||
|
@available(*, deprecated, renamed: "Element")
|
||||||
|
public typealias T = Element
|
||||||
|
}
|
Loading…
Reference in New Issue