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:
Johannes Weiß 2018-03-28 17:06:05 +01:00 committed by GitHub
parent 34e64a0820
commit 224120c175
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 398 additions and 4 deletions

View File

@ -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",

View File

@ -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

View File

@ -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
} }
} }

View File

@ -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
}
}

View File

@ -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
}