mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge branch 'master' into rename-conflicting-classes-and-methods
This commit is contained in:
@@ -61,7 +61,6 @@ func _deallocateUninitializedArray<Element>(
|
||||
// Utility method for collections that wish to implement CustomStringConvertible
|
||||
// and CustomDebugStringConvertible using a bracketed list of elements,
|
||||
// like an array.
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal func _makeCollectionDescription<C: Collection>
|
||||
(for items: C, withTypeName type: String?) -> String {
|
||||
var result = ""
|
||||
|
||||
336
stdlib/public/core/Bitset.swift
Normal file
336
stdlib/public/core/Bitset.swift
Normal file
@@ -0,0 +1,336 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// A simple bitmap of a fixed number of bits, implementing a sorted set of
|
||||
/// small nonnegative Int values.
|
||||
///
|
||||
/// Because `_UnsafeBitset` implements a flat bit vector, it isn't suitable for
|
||||
/// holding arbitrarily large integers. The maximal element a bitset can store
|
||||
/// is fixed at its initialization.
|
||||
@_fixed_layout
|
||||
@usableFromInline // @testable
|
||||
internal struct _UnsafeBitset {
|
||||
@usableFromInline
|
||||
internal let words: UnsafeMutablePointer<Word>
|
||||
|
||||
@usableFromInline
|
||||
internal let wordCount: Int
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal init(words: UnsafeMutablePointer<Word>, wordCount: Int) {
|
||||
self.words = words
|
||||
self.wordCount = wordCount
|
||||
}
|
||||
}
|
||||
|
||||
extension _UnsafeBitset {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal static func word(for element: Int) -> Int {
|
||||
_sanityCheck(element >= 0)
|
||||
return element / Word.capacity
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal static func bit(for element: Int) -> Int {
|
||||
_sanityCheck(element >= 0)
|
||||
// Note: We perform on UInts to get faster unsigned math (masking).
|
||||
let element = UInt(bitPattern: element)
|
||||
let capacity = UInt(bitPattern: Word.capacity)
|
||||
return Int(bitPattern: element % capacity)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal static func split(_ element: Int) -> (word: Int, bit: Int) {
|
||||
return (word(for: element), bit(for: element))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal static func join(word: Int, bit: Int) -> Int {
|
||||
_sanityCheck(bit >= 0 && bit < Word.capacity)
|
||||
return word * Word.capacity + bit
|
||||
}
|
||||
}
|
||||
|
||||
extension _UnsafeBitset {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal static func wordCount(forCapacity capacity: Int) -> Int {
|
||||
return (capacity + Word.capacity - 1) / Word.capacity
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var capacity: Int {
|
||||
@inline(__always)
|
||||
get {
|
||||
return wordCount * Word.capacity
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func isValid(_ element: Int) -> Bool {
|
||||
return element >= 0 && element <= capacity
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func uncheckedContains(_ element: Int) -> Bool {
|
||||
_sanityCheck(isValid(element))
|
||||
let (word, bit) = _UnsafeBitset.split(element)
|
||||
return words[word].uncheckedContains(bit)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
@discardableResult
|
||||
internal func uncheckedInsert(_ element: Int) -> Bool {
|
||||
_sanityCheck(isValid(element))
|
||||
let (word, bit) = _UnsafeBitset.split(element)
|
||||
return words[word].uncheckedInsert(bit)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
@discardableResult
|
||||
internal func uncheckedRemove(_ element: Int) -> Bool {
|
||||
_sanityCheck(isValid(element))
|
||||
let (word, bit) = _UnsafeBitset.split(element)
|
||||
return words[word].uncheckedRemove(bit)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func clear() {
|
||||
words.assign(repeating: .empty, count: wordCount)
|
||||
}
|
||||
}
|
||||
|
||||
extension _UnsafeBitset: Sequence {
|
||||
@usableFromInline
|
||||
internal typealias Element = Int
|
||||
|
||||
@inlinable
|
||||
internal var count: Int {
|
||||
var count = 0
|
||||
for w in 0 ..< wordCount {
|
||||
count += words[w].count
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var underestimatedCount: Int {
|
||||
return count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func makeIterator() -> Iterator {
|
||||
return Iterator(self)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_fixed_layout
|
||||
internal struct Iterator: IteratorProtocol {
|
||||
@usableFromInline
|
||||
internal let bitset: _UnsafeBitset
|
||||
@usableFromInline
|
||||
internal var index: Int
|
||||
@usableFromInline
|
||||
internal var word: Word
|
||||
|
||||
@inlinable
|
||||
internal init(_ bitset: _UnsafeBitset) {
|
||||
self.bitset = bitset
|
||||
self.index = 0
|
||||
self.word = bitset.wordCount > 0 ? bitset.words[0] : .empty
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func next() -> Int? {
|
||||
if let bit = word.next() {
|
||||
return _UnsafeBitset.join(word: index, bit: bit)
|
||||
}
|
||||
while (index + 1) < bitset.wordCount {
|
||||
index += 1
|
||||
word = bitset.words[index]
|
||||
if let bit = word.next() {
|
||||
return _UnsafeBitset.join(word: index, bit: bit)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extension _UnsafeBitset {
|
||||
@_fixed_layout
|
||||
@usableFromInline
|
||||
internal struct Word {
|
||||
@usableFromInline
|
||||
internal var value: UInt
|
||||
|
||||
@inlinable
|
||||
internal init(_ value: UInt) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _UnsafeBitset.Word {
|
||||
@inlinable
|
||||
internal static var capacity: Int {
|
||||
@inline(__always)
|
||||
get {
|
||||
return UInt.bitWidth
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func uncheckedContains(_ bit: Int) -> Bool {
|
||||
_sanityCheck(bit >= 0 && bit < UInt.bitWidth)
|
||||
return value & (1 &<< bit) != 0
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
@discardableResult
|
||||
internal mutating func uncheckedInsert(_ bit: Int) -> Bool {
|
||||
_sanityCheck(bit >= 0 && bit < UInt.bitWidth)
|
||||
let mask: UInt = 1 &<< bit
|
||||
let inserted = value & mask == 0
|
||||
value |= mask
|
||||
return inserted
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
@discardableResult
|
||||
internal mutating func uncheckedRemove(_ bit: Int) -> Bool {
|
||||
_sanityCheck(bit >= 0 && bit < UInt.bitWidth)
|
||||
let mask: UInt = 1 &<< bit
|
||||
let removed = value & mask != 0
|
||||
value &= ~mask
|
||||
return removed
|
||||
}
|
||||
}
|
||||
|
||||
extension _UnsafeBitset.Word {
|
||||
@inlinable
|
||||
var minimum: Int? {
|
||||
@inline(__always)
|
||||
get {
|
||||
guard value != 0 else { return nil }
|
||||
return value.trailingZeroBitCount
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var maximum: Int? {
|
||||
@inline(__always)
|
||||
get {
|
||||
guard value != 0 else { return nil }
|
||||
return _UnsafeBitset.Word.capacity &- 1 &- value.leadingZeroBitCount
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
var complement: _UnsafeBitset.Word {
|
||||
@inline(__always)
|
||||
get {
|
||||
return _UnsafeBitset.Word(~value)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func subtracting(elementsBelow bit: Int) -> _UnsafeBitset.Word {
|
||||
_sanityCheck(bit >= 0 && bit < _UnsafeBitset.Word.capacity)
|
||||
let mask = UInt.max &<< bit
|
||||
return _UnsafeBitset.Word(value & mask)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func intersecting(elementsBelow bit: Int) -> _UnsafeBitset.Word {
|
||||
_sanityCheck(bit >= 0 && bit < _UnsafeBitset.Word.capacity)
|
||||
let mask: UInt = (1 as UInt &<< bit) &- 1
|
||||
return _UnsafeBitset.Word(value & mask)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func intersecting(elementsAbove bit: Int) -> _UnsafeBitset.Word {
|
||||
_sanityCheck(bit >= 0 && bit < _UnsafeBitset.Word.capacity)
|
||||
let mask = (UInt.max &<< bit) &<< 1
|
||||
return _UnsafeBitset.Word(value & mask)
|
||||
}
|
||||
}
|
||||
|
||||
extension _UnsafeBitset.Word {
|
||||
@inlinable
|
||||
internal static var empty: _UnsafeBitset.Word {
|
||||
@inline(__always)
|
||||
get {
|
||||
return _UnsafeBitset.Word(0)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal static var allBits: _UnsafeBitset.Word {
|
||||
@inline(__always)
|
||||
get {
|
||||
return _UnsafeBitset.Word(UInt.max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Word implements Sequence by using a copy of itself as its Iterator.
|
||||
// Iteration with `next()` destroys the word's value; however, this won't cause
|
||||
// problems in normal use, because `next()` is usually called on a separate
|
||||
// iterator, not the original word.
|
||||
extension _UnsafeBitset.Word: Sequence, IteratorProtocol {
|
||||
@inlinable
|
||||
internal var count: Int {
|
||||
return value.nonzeroBitCount
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var underestimatedCount: Int {
|
||||
return count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var isEmpty: Bool {
|
||||
@inline(__always)
|
||||
get {
|
||||
return value == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the index of the lowest set bit in this word,
|
||||
/// and also destructively clear it.
|
||||
@inlinable
|
||||
internal mutating func next() -> Int? {
|
||||
guard value != 0 else { return nil }
|
||||
let bit = value.trailingZeroBitCount
|
||||
value &= value &- 1 // Clear lowest nonzero bit.
|
||||
return bit
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ set(SWIFTLIB_ESSENTIAL
|
||||
Assert.swift
|
||||
AssertCommon.swift
|
||||
BidirectionalCollection.swift
|
||||
Bitset.swift
|
||||
Bool.swift
|
||||
BridgeObjectiveC.swift
|
||||
BridgeStorage.swift
|
||||
@@ -51,6 +52,11 @@ set(SWIFTLIB_ESSENTIAL
|
||||
CTypes.swift
|
||||
DebuggerSupport.swift
|
||||
Dictionary.swift
|
||||
DictionaryBridging.swift
|
||||
DictionaryBuilder.swift
|
||||
DictionaryCasting.swift
|
||||
DictionaryStorage.swift
|
||||
DictionaryVariant.swift
|
||||
DropWhile.swift
|
||||
Dump.swift
|
||||
EmptyCollection.swift
|
||||
@@ -68,9 +74,9 @@ set(SWIFTLIB_ESSENTIAL
|
||||
# if we do so, the compiler crashes.
|
||||
AnyHashable.swift
|
||||
# END WORKAROUND
|
||||
HashedCollectionsAnyHashableExtensions.swift
|
||||
Hasher.swift
|
||||
Hashing.swift
|
||||
HashTable.swift
|
||||
HeapBuffer.swift
|
||||
ICU.swift
|
||||
Indices.swift
|
||||
@@ -90,6 +96,8 @@ set(SWIFTLIB_ESSENTIAL
|
||||
Mirrors.swift.gyb
|
||||
Misc.swift
|
||||
MutableCollection.swift
|
||||
NativeDictionary.swift
|
||||
NativeSet.swift
|
||||
NewtypeWrapper.swift
|
||||
NormalizedCodeUnitIterator.swift
|
||||
ObjectIdentifier.swift
|
||||
@@ -116,6 +124,12 @@ set(SWIFTLIB_ESSENTIAL
|
||||
SequenceWrapper.swift
|
||||
Set.swift
|
||||
SetAlgebra.swift
|
||||
SetAnyHashableExtensions.swift
|
||||
SetBridging.swift
|
||||
SetBuilder.swift
|
||||
SetCasting.swift
|
||||
SetStorage.swift
|
||||
SetVariant.swift
|
||||
ShadowProtocols.swift
|
||||
Shims.swift
|
||||
Slice.swift
|
||||
@@ -158,7 +172,6 @@ set(SWIFTLIB_ESSENTIAL
|
||||
Unmanaged.swift
|
||||
UnmanagedOpaqueString.swift
|
||||
UnmanagedString.swift
|
||||
UnsafeBitMap.swift
|
||||
UnsafeBufferPointer.swift.gyb
|
||||
UnsafeRawBufferPointer.swift.gyb
|
||||
UnsafePointer.swift
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
758
stdlib/public/core/DictionaryBridging.swift
Normal file
758
stdlib/public/core/DictionaryBridging.swift
Normal file
@@ -0,0 +1,758 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
|
||||
import SwiftShims
|
||||
|
||||
/// Equivalent to `NSDictionary.allKeys`, but does not leave objects on the
|
||||
/// autorelease pool.
|
||||
@inlinable
|
||||
internal func _stdlib_NSDictionary_allKeys(
|
||||
_ nsd: _NSDictionary
|
||||
) -> _HeapBuffer<Int, AnyObject> {
|
||||
let count = nsd.count
|
||||
let storage = _HeapBuffer<Int, AnyObject>(
|
||||
_HeapBufferStorage<Int, AnyObject>.self, count, count)
|
||||
|
||||
nsd.getObjects(nil, andKeys: storage.baseAddress, count: count)
|
||||
return storage
|
||||
}
|
||||
|
||||
extension _NativeDictionary { // Bridging
|
||||
@usableFromInline
|
||||
internal func bridged() -> _NSDictionary {
|
||||
// We can zero-cost bridge if our keys are verbatim
|
||||
// or if we're the empty singleton.
|
||||
|
||||
// Temporary var for SOME type safety before a cast.
|
||||
let nsDictionary: _NSDictionaryCore
|
||||
|
||||
if _storage === _RawDictionaryStorage.empty || count == 0 {
|
||||
nsDictionary = _RawDictionaryStorage.empty
|
||||
} else if _isBridgedVerbatimToObjectiveC(Key.self),
|
||||
_isBridgedVerbatimToObjectiveC(Value.self) {
|
||||
nsDictionary = unsafeDowncast(
|
||||
_storage,
|
||||
to: _DictionaryStorage<Key, Value>.self)
|
||||
} else {
|
||||
nsDictionary = _SwiftDeferredNSDictionary(self)
|
||||
}
|
||||
|
||||
// Cast from "minimal NSDictionary" to "NSDictionary"
|
||||
// Note that if you actually ask Swift for this cast, it will fail.
|
||||
// Never trust a shadow protocol!
|
||||
return unsafeBitCast(nsDictionary, to: _NSDictionary.self)
|
||||
}
|
||||
}
|
||||
|
||||
/// An NSEnumerator that works with any _NativeDictionary of
|
||||
/// verbatim bridgeable elements. Used by the various NSDictionary impls.
|
||||
final internal class _SwiftDictionaryNSEnumerator<Key: Hashable, Value>
|
||||
: __SwiftNativeNSEnumerator, _NSEnumerator {
|
||||
|
||||
@nonobjc internal var base: _NativeDictionary<Key, Value>
|
||||
@nonobjc internal var bridgedKeys: _BridgingHashBuffer?
|
||||
@nonobjc internal var nextIndex: _NativeDictionary<Key, Value>.Index
|
||||
@nonobjc internal var endIndex: _NativeDictionary<Key, Value>.Index
|
||||
|
||||
@objc
|
||||
internal override required init() {
|
||||
_sanityCheckFailure("don't call this designated initializer")
|
||||
}
|
||||
|
||||
internal init(_ base: _NativeDictionary<Key, Value>) {
|
||||
_sanityCheck(_isBridgedVerbatimToObjectiveC(Key.self))
|
||||
self.base = base
|
||||
self.bridgedKeys = nil
|
||||
self.nextIndex = base.startIndex
|
||||
self.endIndex = base.endIndex
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal init(_ deferred: _SwiftDeferredNSDictionary<Key, Value>) {
|
||||
_sanityCheck(!_isBridgedVerbatimToObjectiveC(Key.self))
|
||||
self.base = deferred.native
|
||||
self.bridgedKeys = deferred.bridgeKeys()
|
||||
self.nextIndex = base.startIndex
|
||||
self.endIndex = base.endIndex
|
||||
}
|
||||
|
||||
private func bridgedKey(at index: _HashTable.Index) -> AnyObject {
|
||||
_sanityCheck(base.hashTable.isOccupied(index))
|
||||
if let bridgedKeys = self.bridgedKeys {
|
||||
return bridgedKeys[index]
|
||||
}
|
||||
return _bridgeAnythingToObjectiveC(base.uncheckedKey(at: index))
|
||||
}
|
||||
|
||||
@objc
|
||||
internal func nextObject() -> AnyObject? {
|
||||
if nextIndex == endIndex {
|
||||
return nil
|
||||
}
|
||||
let index = nextIndex
|
||||
nextIndex = base.index(after: nextIndex)
|
||||
return self.bridgedKey(at: index)
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>,
|
||||
count: Int
|
||||
) -> Int {
|
||||
var theState = state.pointee
|
||||
if theState.state == 0 {
|
||||
theState.state = 1 // Arbitrary non-zero value.
|
||||
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
|
||||
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
}
|
||||
|
||||
if nextIndex == endIndex {
|
||||
state.pointee = theState
|
||||
return 0
|
||||
}
|
||||
|
||||
// Return only a single element so that code can start iterating via fast
|
||||
// enumeration, terminate it, and continue via NSEnumerator.
|
||||
let unmanagedObjects = _UnmanagedAnyObjectArray(objects)
|
||||
unmanagedObjects[0] = self.bridgedKey(at: nextIndex)
|
||||
nextIndex = base.index(after: nextIndex)
|
||||
state.pointee = theState
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
/// This class exists for Objective-C bridging. It holds a reference to a
|
||||
/// _NativeDictionary, and can be upcast to NSSelf when bridging is
|
||||
/// necessary. This is the fallback implementation for situations where
|
||||
/// toll-free bridging isn't possible. On first access, a _NativeDictionary
|
||||
/// of AnyObject will be constructed containing all the bridged elements.
|
||||
final internal class _SwiftDeferredNSDictionary<Key: Hashable, Value>
|
||||
: __SwiftNativeNSDictionary, _NSDictionaryCore {
|
||||
|
||||
// This stored property must be stored at offset zero. We perform atomic
|
||||
// operations on it.
|
||||
//
|
||||
// Do not access this property directly.
|
||||
@nonobjc
|
||||
private var _bridgedKeys_DoNotUse: AnyObject?
|
||||
|
||||
// This stored property must be stored at offset one. We perform atomic
|
||||
// operations on it.
|
||||
//
|
||||
// Do not access this property directly.
|
||||
@nonobjc
|
||||
private var _bridgedValues_DoNotUse: AnyObject?
|
||||
|
||||
/// The unbridged elements.
|
||||
internal var native: _NativeDictionary<Key, Value>
|
||||
|
||||
internal init(_ native: _NativeDictionary<Key, Value>) {
|
||||
_sanityCheck(native.count > 0)
|
||||
_sanityCheck(!_isBridgedVerbatimToObjectiveC(Key.self) ||
|
||||
!_isBridgedVerbatimToObjectiveC(Value.self))
|
||||
self.native = native
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc
|
||||
internal required init(
|
||||
objects: UnsafePointer<AnyObject?>,
|
||||
forKeys: UnsafeRawPointer,
|
||||
count: Int
|
||||
) {
|
||||
_sanityCheckFailure("don't call this designated initializer")
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
private var _bridgedKeysPtr: UnsafeMutablePointer<AnyObject?> {
|
||||
return _getUnsafePointerToStoredProperties(self)
|
||||
.assumingMemoryBound(to: Optional<AnyObject>.self)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
private var _bridgedValuesPtr: UnsafeMutablePointer<AnyObject?> {
|
||||
return _bridgedKeysPtr + 1
|
||||
}
|
||||
|
||||
/// The buffer for bridged keys, if present.
|
||||
@nonobjc
|
||||
private var _bridgedKeys: _BridgingHashBuffer? {
|
||||
guard let ref = _stdlib_atomicLoadARCRef(object: _bridgedKeysPtr) else {
|
||||
return nil
|
||||
}
|
||||
return unsafeDowncast(ref, to: _BridgingHashBuffer.self)
|
||||
}
|
||||
|
||||
/// The buffer for bridged values, if present.
|
||||
@nonobjc
|
||||
private var _bridgedValues: _BridgingHashBuffer? {
|
||||
guard let ref = _stdlib_atomicLoadARCRef(object: _bridgedValuesPtr) else {
|
||||
return nil
|
||||
}
|
||||
return unsafeDowncast(ref, to: _BridgingHashBuffer.self)
|
||||
}
|
||||
|
||||
/// Attach a buffer for bridged Dictionary keys.
|
||||
@nonobjc
|
||||
private func _initializeBridgedKeys(_ storage: _BridgingHashBuffer) {
|
||||
_stdlib_atomicInitializeARCRef(object: _bridgedKeysPtr, desired: storage)
|
||||
}
|
||||
|
||||
/// Attach a buffer for bridged Dictionary values.
|
||||
@nonobjc
|
||||
private func _initializeBridgedValues(_ storage: _BridgingHashBuffer) {
|
||||
_stdlib_atomicInitializeARCRef(object: _bridgedValuesPtr, desired: storage)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func bridgeKeys() -> _BridgingHashBuffer? {
|
||||
if _isBridgedVerbatimToObjectiveC(Key.self) { return nil }
|
||||
if let bridgedKeys = _bridgedKeys { return bridgedKeys }
|
||||
|
||||
// Allocate and initialize heap storage for bridged keys.
|
||||
let bridged = _BridgingHashBuffer.allocate(
|
||||
owner: native._storage,
|
||||
hashTable: native.hashTable)
|
||||
for index in native.hashTable {
|
||||
let object = _bridgeAnythingToObjectiveC(native.uncheckedKey(at: index))
|
||||
bridged.initialize(at: index, to: object)
|
||||
}
|
||||
|
||||
// Atomically put the bridged keys in place.
|
||||
_initializeBridgedKeys(bridged)
|
||||
return _bridgedKeys!
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func bridgeValues() -> _BridgingHashBuffer? {
|
||||
if _isBridgedVerbatimToObjectiveC(Value.self) { return nil }
|
||||
if let bridgedValues = _bridgedValues { return bridgedValues }
|
||||
|
||||
// Allocate and initialize heap storage for bridged values.
|
||||
let bridged = _BridgingHashBuffer.allocate(
|
||||
owner: native._storage,
|
||||
hashTable: native.hashTable)
|
||||
for index in native.hashTable {
|
||||
let object = _bridgeAnythingToObjectiveC(native.uncheckedValue(at: index))
|
||||
bridged.initialize(at: index, to: object)
|
||||
}
|
||||
|
||||
// Atomically put the bridged values in place.
|
||||
_initializeBridgedValues(bridged)
|
||||
return _bridgedValues!
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
internal typealias Index = _HashTable.Index
|
||||
|
||||
@objc(copyWithZone:)
|
||||
internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
|
||||
// Instances of this class should be visible outside of standard library as
|
||||
// having `NSDictionary` type, which is immutable.
|
||||
return self
|
||||
}
|
||||
|
||||
@objc(objectForKey:)
|
||||
internal func object(forKey aKey: AnyObject) -> AnyObject? {
|
||||
guard let nativeKey = _conditionallyBridgeFromObjectiveC(aKey, Key.self)
|
||||
else { return nil }
|
||||
|
||||
let (index, found) = native.find(nativeKey)
|
||||
guard found else { return nil }
|
||||
if let bridgedValues = bridgeValues() {
|
||||
return bridgedValues[index]
|
||||
}
|
||||
return _bridgeAnythingToObjectiveC(native.uncheckedValue(at: index))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func _key(
|
||||
at index: Index,
|
||||
bridgedKeys: _BridgingHashBuffer?
|
||||
) -> AnyObject {
|
||||
if let bridgedKeys = bridgedKeys {
|
||||
return bridgedKeys[index]
|
||||
}
|
||||
return _bridgeAnythingToObjectiveC(native.uncheckedKey(at: index))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func _value(
|
||||
at index: Index,
|
||||
bridgedValues: _BridgingHashBuffer?
|
||||
) -> AnyObject {
|
||||
if let bridgedValues = bridgedValues {
|
||||
return bridgedValues[index]
|
||||
}
|
||||
return _bridgeAnythingToObjectiveC(native.uncheckedValue(at: index))
|
||||
}
|
||||
|
||||
@objc
|
||||
internal func keyEnumerator() -> _NSEnumerator {
|
||||
if _isBridgedVerbatimToObjectiveC(Key.self) {
|
||||
return _SwiftDictionaryNSEnumerator<Key, Value>(native)
|
||||
}
|
||||
return _SwiftDictionaryNSEnumerator<Key, Value>(self)
|
||||
}
|
||||
|
||||
@objc(getObjects:andKeys:count:)
|
||||
internal func getObjects(
|
||||
_ objects: UnsafeMutablePointer<AnyObject>?,
|
||||
andKeys keys: UnsafeMutablePointer<AnyObject>?,
|
||||
count: Int
|
||||
) {
|
||||
_precondition(count >= 0, "Invalid count")
|
||||
guard count > 0 else { return }
|
||||
let bridgedKeys = bridgeKeys()
|
||||
let bridgedValues = bridgeValues()
|
||||
var i = 0 // Current position in the output buffers
|
||||
let bucketCount = native._storage._bucketCount
|
||||
|
||||
defer { _fixLifetime(self) }
|
||||
|
||||
switch (_UnmanagedAnyObjectArray(keys), _UnmanagedAnyObjectArray(objects)) {
|
||||
case (let unmanagedKeys?, let unmanagedObjects?):
|
||||
for index in native.hashTable {
|
||||
unmanagedKeys[i] = _key(at: index, bridgedKeys: bridgedKeys)
|
||||
unmanagedObjects[i] = _value(at: index, bridgedValues: bridgedValues)
|
||||
i += 1
|
||||
guard i < count else { break }
|
||||
}
|
||||
case (let unmanagedKeys?, nil):
|
||||
for index in native.hashTable {
|
||||
unmanagedKeys[i] = _key(at: index, bridgedKeys: bridgedKeys)
|
||||
i += 1
|
||||
guard i < count else { break }
|
||||
}
|
||||
case (nil, let unmanagedObjects?):
|
||||
for index in native.hashTable {
|
||||
unmanagedObjects[i] = _value(at: index, bridgedValues: bridgedValues)
|
||||
i += 1
|
||||
guard i < count else { break }
|
||||
}
|
||||
case (nil, nil):
|
||||
// Do nothing
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@objc(enumerateKeysAndObjectsWithOptions:usingBlock:)
|
||||
internal func enumerateKeysAndObjects(
|
||||
options: Int,
|
||||
using block: @convention(block) (
|
||||
Unmanaged<AnyObject>,
|
||||
Unmanaged<AnyObject>,
|
||||
UnsafeMutablePointer<UInt8>
|
||||
) -> Void) {
|
||||
let bridgedKeys = bridgeKeys()
|
||||
let bridgedValues = bridgeValues()
|
||||
|
||||
defer { _fixLifetime(self) }
|
||||
|
||||
var stop: UInt8 = 0
|
||||
for index in native.hashTable {
|
||||
let key = _key(at: index, bridgedKeys: bridgedKeys)
|
||||
let value = _value(at: index, bridgedValues: bridgedValues)
|
||||
block(
|
||||
Unmanaged.passUnretained(key),
|
||||
Unmanaged.passUnretained(value),
|
||||
&stop)
|
||||
if stop != 0 { return }
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
internal var count: Int {
|
||||
return native.count
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>?,
|
||||
count: Int
|
||||
) -> Int {
|
||||
var theState = state.pointee
|
||||
if theState.state == 0 {
|
||||
theState.state = 1 // Arbitrary non-zero value.
|
||||
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
|
||||
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
theState.extra.0 = CUnsignedLong(native.startIndex.bucket)
|
||||
}
|
||||
|
||||
// Test 'objects' rather than 'count' because (a) this is very rare anyway,
|
||||
// and (b) the optimizer should then be able to optimize away the
|
||||
// unwrapping check below.
|
||||
if _slowPath(objects == nil) {
|
||||
return 0
|
||||
}
|
||||
|
||||
let unmanagedObjects = _UnmanagedAnyObjectArray(objects!)
|
||||
var index = _HashTable.Index(bucket: Int(theState.extra.0))
|
||||
let endIndex = native.endIndex
|
||||
_precondition(index == endIndex || native.hashTable.isOccupied(index))
|
||||
var stored = 0
|
||||
|
||||
// Only need to bridge once, so we can hoist it out of the loop.
|
||||
let bridgedKeys = bridgeKeys()
|
||||
for i in 0..<count {
|
||||
if index == endIndex { break }
|
||||
|
||||
unmanagedObjects[i] = _key(at: index, bridgedKeys: bridgedKeys)
|
||||
stored += 1
|
||||
index = native.index(after: index)
|
||||
}
|
||||
theState.extra.0 = CUnsignedLong(index.bucket)
|
||||
state.pointee = theState
|
||||
return stored
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_fixed_layout
|
||||
internal struct _CocoaDictionary {
|
||||
@usableFromInline
|
||||
internal let object: _NSDictionary
|
||||
|
||||
@inlinable
|
||||
internal init(_ object: _NSDictionary) {
|
||||
self.object = object
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaDictionary: Equatable {
|
||||
@usableFromInline
|
||||
internal static func ==(
|
||||
lhs: _CocoaDictionary,
|
||||
rhs: _CocoaDictionary
|
||||
) -> Bool {
|
||||
return _stdlib_NSObject_isEqual(lhs.object, rhs.object)
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaDictionary: _DictionaryBuffer {
|
||||
@usableFromInline
|
||||
internal typealias Key = AnyObject
|
||||
@usableFromInline
|
||||
internal typealias Value = AnyObject
|
||||
|
||||
@inlinable
|
||||
internal var startIndex: Index {
|
||||
return Index(self, startIndex: ())
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var endIndex: Index {
|
||||
return Index(self, endIndex: ())
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func index(after i: Index) -> Index {
|
||||
var i = i
|
||||
formIndex(after: &i)
|
||||
return i
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
internal func formIndex(after i: inout Index) {
|
||||
_precondition(i.base.object === self.object, "Invalid index")
|
||||
_precondition(i.currentKeyIndex < i.allKeys.value,
|
||||
"Cannot increment endIndex")
|
||||
i.currentKeyIndex += 1
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
internal func index(forKey key: Key) -> Index? {
|
||||
// Fast path that does not involve creating an array of all keys. In case
|
||||
// the key is present, this lookup is a penalty for the slow path, but the
|
||||
// potential savings are significant: we could skip a memory allocation and
|
||||
// a linear search.
|
||||
if lookup(key) == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
let allKeys = _stdlib_NSDictionary_allKeys(object)
|
||||
var keyIndex = -1
|
||||
for i in 0..<allKeys.value {
|
||||
if _stdlib_NSObject_isEqual(key, allKeys[i]) {
|
||||
keyIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
_sanityCheck(keyIndex >= 0,
|
||||
"Key was found in fast path, but not found later?")
|
||||
return Index(self, allKeys, keyIndex)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var count: Int {
|
||||
return object.count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func contains(_ key: Key) -> Bool {
|
||||
return object.object(forKey: key) != nil
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func lookup(_ key: Key) -> Value? {
|
||||
return object.object(forKey: key)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func lookup(_ index: Index) -> (key: Key, value: Value) {
|
||||
_precondition(index.base.object === self.object, "Invalid index")
|
||||
let key: Key = index.allKeys[index.currentKeyIndex]
|
||||
let value: Value = index.base.object.object(forKey: key)!
|
||||
return (key, value)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func key(at index: Index) -> Key {
|
||||
_precondition(index.base.object === self.object, "Invalid index")
|
||||
return index.allKeys[index.currentKeyIndex]
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func value(at index: Index) -> Value {
|
||||
_precondition(index.base.object === self.object, "Invalid index")
|
||||
let key = index.allKeys[index.currentKeyIndex]
|
||||
return index.base.object.object(forKey: key)!
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaDictionary {
|
||||
@inlinable
|
||||
internal func mapValues<Key: Hashable, Value, T>(
|
||||
_ transform: (Value) throws -> T
|
||||
) rethrows -> _NativeDictionary<Key, T> {
|
||||
var result = _NativeDictionary<Key, T>(capacity: self.count)
|
||||
for (cocoaKey, cocoaValue) in self {
|
||||
let key = _forceBridgeFromObjectiveC(cocoaKey, Key.self)
|
||||
let value = _forceBridgeFromObjectiveC(cocoaValue, Value.self)
|
||||
try result.insertNew(key: key, value: transform(value))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaDictionary {
|
||||
@_fixed_layout // FIXME(sil-serialize-all)
|
||||
@usableFromInline
|
||||
internal struct Index {
|
||||
// Assumption: we rely on NSDictionary.getObjects when being
|
||||
// repeatedly called on the same NSDictionary, returning items in the same
|
||||
// order every time.
|
||||
// Similarly, the same assumption holds for NSSet.allObjects.
|
||||
|
||||
/// A reference to the NSDictionary, which owns members in `allObjects`,
|
||||
/// or `allKeys`, for NSSet and NSDictionary respectively.
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal let base: _CocoaDictionary
|
||||
// FIXME: swift-3-indexing-model: try to remove the cocoa reference, but
|
||||
// make sure that we have a safety check for accessing `allKeys`. Maybe
|
||||
// move both into the dictionary/set itself.
|
||||
|
||||
/// An unowned array of keys.
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal var allKeys: _HeapBuffer<Int, AnyObject>
|
||||
|
||||
/// Index into `allKeys`
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal var currentKeyIndex: Int
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal init(_ base: _CocoaDictionary, startIndex: ()) {
|
||||
self.base = base
|
||||
self.allKeys = _stdlib_NSDictionary_allKeys(base.object)
|
||||
self.currentKeyIndex = 0
|
||||
}
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal init(_ base: _CocoaDictionary, endIndex: ()) {
|
||||
self.base = base
|
||||
self.allKeys = _stdlib_NSDictionary_allKeys(base.object)
|
||||
self.currentKeyIndex = allKeys.value
|
||||
}
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal init(
|
||||
_ base: _CocoaDictionary,
|
||||
_ allKeys: _HeapBuffer<Int, AnyObject>,
|
||||
_ currentKeyIndex: Int
|
||||
) {
|
||||
self.base = base
|
||||
self.allKeys = allKeys
|
||||
self.currentKeyIndex = currentKeyIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaDictionary.Index: Equatable {
|
||||
@inlinable
|
||||
internal static func == (
|
||||
lhs: _CocoaDictionary.Index,
|
||||
rhs: _CocoaDictionary.Index
|
||||
) -> Bool {
|
||||
_precondition(lhs.base.object === rhs.base.object,
|
||||
"Comparing indexes from different dictionaries")
|
||||
return lhs.currentKeyIndex == rhs.currentKeyIndex
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaDictionary.Index: Comparable {
|
||||
@inlinable
|
||||
internal static func < (
|
||||
lhs: _CocoaDictionary.Index,
|
||||
rhs: _CocoaDictionary.Index
|
||||
) -> Bool {
|
||||
_precondition(lhs.base.object === rhs.base.object,
|
||||
"Comparing indexes from different dictionaries")
|
||||
return lhs.currentKeyIndex < rhs.currentKeyIndex
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaDictionary: Sequence {
|
||||
@usableFromInline
|
||||
final internal class Iterator {
|
||||
// Cocoa Dictionary iterator has to be a class, otherwise we cannot
|
||||
// guarantee that the fast enumeration struct is pinned to a certain memory
|
||||
// location.
|
||||
|
||||
// This stored property should be stored at offset zero. There's code below
|
||||
// relying on this.
|
||||
internal var _fastEnumerationState: _SwiftNSFastEnumerationState =
|
||||
_makeSwiftNSFastEnumerationState()
|
||||
|
||||
// This stored property should be stored right after
|
||||
// `_fastEnumerationState`. There's code below relying on this.
|
||||
internal var _fastEnumerationStackBuf = _CocoaFastEnumerationStackBuf()
|
||||
|
||||
internal let base: _CocoaDictionary
|
||||
|
||||
internal var _fastEnumerationStatePtr:
|
||||
UnsafeMutablePointer<_SwiftNSFastEnumerationState> {
|
||||
return _getUnsafePointerToStoredProperties(self).assumingMemoryBound(
|
||||
to: _SwiftNSFastEnumerationState.self)
|
||||
}
|
||||
|
||||
internal var _fastEnumerationStackBufPtr:
|
||||
UnsafeMutablePointer<_CocoaFastEnumerationStackBuf> {
|
||||
return UnsafeMutableRawPointer(_fastEnumerationStatePtr + 1)
|
||||
.assumingMemoryBound(to: _CocoaFastEnumerationStackBuf.self)
|
||||
}
|
||||
|
||||
// These members have to be word-sized integers, they cannot be limited to
|
||||
// Int8 just because our storage holds 16 elements: fast enumeration is
|
||||
// allowed to return inner pointers to the container, which can be much
|
||||
// larger.
|
||||
internal var itemIndex: Int = 0
|
||||
internal var itemCount: Int = 0
|
||||
|
||||
internal init(_ base: _CocoaDictionary) {
|
||||
self.base = base
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
internal func makeIterator() -> Iterator {
|
||||
return Iterator(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaDictionary.Iterator: IteratorProtocol {
|
||||
@usableFromInline
|
||||
internal typealias Element = (key: AnyObject, value: AnyObject)
|
||||
|
||||
@usableFromInline
|
||||
internal func next() -> Element? {
|
||||
if itemIndex < 0 {
|
||||
return nil
|
||||
}
|
||||
let base = self.base
|
||||
if itemIndex == itemCount {
|
||||
let stackBufCount = _fastEnumerationStackBuf.count
|
||||
// We can't use `withUnsafeMutablePointer` here to get pointers to
|
||||
// properties, because doing so might introduce a writeback storage, but
|
||||
// fast enumeration relies on the pointer identity of the enumeration
|
||||
// state struct.
|
||||
itemCount = base.object.countByEnumerating(
|
||||
with: _fastEnumerationStatePtr,
|
||||
objects: UnsafeMutableRawPointer(_fastEnumerationStackBufPtr)
|
||||
.assumingMemoryBound(to: AnyObject.self),
|
||||
count: stackBufCount)
|
||||
if itemCount == 0 {
|
||||
itemIndex = -1
|
||||
return nil
|
||||
}
|
||||
itemIndex = 0
|
||||
}
|
||||
let itemsPtrUP =
|
||||
UnsafeMutableRawPointer(_fastEnumerationState.itemsPtr!)
|
||||
.assumingMemoryBound(to: AnyObject.self)
|
||||
let itemsPtr = _UnmanagedAnyObjectArray(itemsPtrUP)
|
||||
let key: AnyObject = itemsPtr[itemIndex]
|
||||
itemIndex += 1
|
||||
let value: AnyObject = base.object.object(forKey: key)!
|
||||
return (key, value)
|
||||
}
|
||||
}
|
||||
|
||||
//===--- Bridging ---------------------------------------------------------===//
|
||||
|
||||
extension Dictionary {
|
||||
@inlinable
|
||||
public func _bridgeToObjectiveCImpl() -> _NSDictionaryCore {
|
||||
switch _variant {
|
||||
case .native(let nativeDictionary):
|
||||
return nativeDictionary.bridged()
|
||||
case .cocoa(let cocoaDictionary):
|
||||
return cocoaDictionary.object
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the native Dictionary hidden inside this NSDictionary;
|
||||
/// returns nil otherwise.
|
||||
public static func _bridgeFromObjectiveCAdoptingNativeStorageOf(
|
||||
_ s: AnyObject
|
||||
) -> Dictionary<Key, Value>? {
|
||||
|
||||
// Try all three NSDictionary impls that we currently provide.
|
||||
|
||||
if let deferred = s as? _SwiftDeferredNSDictionary<Key, Value> {
|
||||
return Dictionary(_native: deferred.native)
|
||||
}
|
||||
|
||||
if let nativeStorage = s as? _DictionaryStorage<Key, Value> {
|
||||
return Dictionary(_native: _NativeDictionary(nativeStorage))
|
||||
}
|
||||
|
||||
if s === _RawDictionaryStorage.empty {
|
||||
return Dictionary()
|
||||
}
|
||||
|
||||
// FIXME: what if `s` is native storage, but for different key/value type?
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _runtime(_ObjC)
|
||||
48
stdlib/public/core/DictionaryBuilder.swift
Normal file
48
stdlib/public/core/DictionaryBuilder.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Initializes a `Dictionary` from unique members.
|
||||
///
|
||||
/// Using a builder can be faster than inserting members into an empty
|
||||
/// `Dictionary`.
|
||||
@_fixed_layout
|
||||
public // SPI(Foundation)
|
||||
struct _DictionaryBuilder<Key: Hashable, Value> {
|
||||
@usableFromInline
|
||||
internal var _target: _NativeDictionary<Key, Value>
|
||||
@usableFromInline
|
||||
internal let _requestedCount: Int
|
||||
|
||||
@inlinable
|
||||
public init(count: Int) {
|
||||
_target = _NativeDictionary(capacity: count)
|
||||
_requestedCount = count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func add(key newKey: Key, value: Value) {
|
||||
_target.insertNew(key: newKey, value: value)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func take() -> Dictionary<Key, Value> {
|
||||
_precondition(_target.capacity > 0 || _requestedCount == 0,
|
||||
"Cannot take the result twice")
|
||||
_precondition(_target.count == _requestedCount,
|
||||
"The number of members added does not match the promised count")
|
||||
|
||||
// Prevent taking the result twice.
|
||||
var result = _NativeDictionary<Key, Value>()
|
||||
swap(&result, &_target)
|
||||
return Dictionary(_native: result)
|
||||
}
|
||||
}
|
||||
108
stdlib/public/core/DictionaryCasting.swift
Normal file
108
stdlib/public/core/DictionaryCasting.swift
Normal file
@@ -0,0 +1,108 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===--- Compiler conversion/casting entry points for Dictionary<K, V> ----===//
|
||||
|
||||
/// Perform a non-bridged upcast that always succeeds.
|
||||
///
|
||||
/// - Precondition: `BaseKey` and `BaseValue` are base classes or base `@objc`
|
||||
/// protocols (such as `AnyObject`) of `DerivedKey` and `DerivedValue`,
|
||||
/// respectively.
|
||||
@inlinable
|
||||
public func _dictionaryUpCast<DerivedKey, DerivedValue, BaseKey, BaseValue>(
|
||||
_ source: Dictionary<DerivedKey, DerivedValue>
|
||||
) -> Dictionary<BaseKey, BaseValue> {
|
||||
var result = Dictionary<BaseKey, BaseValue>(minimumCapacity: source.count)
|
||||
|
||||
for (k, v) in source {
|
||||
result[k as! BaseKey] = (v as! BaseValue)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// Called by the casting machinery.
|
||||
@_silgen_name("_swift_dictionaryDownCastIndirect")
|
||||
internal func _dictionaryDownCastIndirect<SourceKey, SourceValue,
|
||||
TargetKey, TargetValue>(
|
||||
_ source: UnsafePointer<Dictionary<SourceKey, SourceValue>>,
|
||||
_ target: UnsafeMutablePointer<Dictionary<TargetKey, TargetValue>>) {
|
||||
target.initialize(to: _dictionaryDownCast(source.pointee))
|
||||
}
|
||||
|
||||
/// Implements a forced downcast. This operation should have O(1) complexity.
|
||||
///
|
||||
/// The cast can fail if bridging fails. The actual checks and bridging can be
|
||||
/// deferred.
|
||||
///
|
||||
/// - Precondition: `DerivedKey` is a subtype of `BaseKey`, `DerivedValue` is
|
||||
/// a subtype of `BaseValue`, and all of these types are reference types.
|
||||
@inlinable
|
||||
public func _dictionaryDownCast<BaseKey, BaseValue, DerivedKey, DerivedValue>(
|
||||
_ source: Dictionary<BaseKey, BaseValue>
|
||||
) -> Dictionary<DerivedKey, DerivedValue> {
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
if _isClassOrObjCExistential(BaseKey.self)
|
||||
&& _isClassOrObjCExistential(BaseValue.self)
|
||||
&& _isClassOrObjCExistential(DerivedKey.self)
|
||||
&& _isClassOrObjCExistential(DerivedValue.self) {
|
||||
|
||||
switch source._variant {
|
||||
case .native(let native):
|
||||
// Note: it is safe to treat the buffer as immutable here because
|
||||
// Dictionary will not mutate buffer with reference count greater than 1.
|
||||
return Dictionary(_immutableCocoaDictionary: native.bridged())
|
||||
case .cocoa(let cocoa):
|
||||
return Dictionary(_immutableCocoaDictionary: cocoa.object)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return _dictionaryDownCastConditional(source)!
|
||||
}
|
||||
|
||||
/// Called by the casting machinery.
|
||||
@_silgen_name("_swift_dictionaryDownCastConditionalIndirect")
|
||||
internal func _dictionaryDownCastConditionalIndirect<SourceKey, SourceValue,
|
||||
TargetKey, TargetValue>(
|
||||
_ source: UnsafePointer<Dictionary<SourceKey, SourceValue>>,
|
||||
_ target: UnsafeMutablePointer<Dictionary<TargetKey, TargetValue>>
|
||||
) -> Bool {
|
||||
if let result: Dictionary<TargetKey, TargetValue>
|
||||
= _dictionaryDownCastConditional(source.pointee) {
|
||||
target.initialize(to: result)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// Implements a conditional downcast.
|
||||
///
|
||||
/// If the cast fails, the function returns `nil`. All checks should be
|
||||
/// performed eagerly.
|
||||
///
|
||||
/// - Precondition: `DerivedKey` is a subtype of `BaseKey`, `DerivedValue` is
|
||||
/// a subtype of `BaseValue`, and all of these types are reference types.
|
||||
@inlinable
|
||||
public func _dictionaryDownCastConditional<
|
||||
BaseKey, BaseValue, DerivedKey, DerivedValue
|
||||
>(
|
||||
_ source: Dictionary<BaseKey, BaseValue>
|
||||
) -> Dictionary<DerivedKey, DerivedValue>? {
|
||||
|
||||
var result = Dictionary<DerivedKey, DerivedValue>()
|
||||
for (k, v) in source {
|
||||
guard let k1 = k as? DerivedKey, let v1 = v as? DerivedValue
|
||||
else { return nil }
|
||||
result[k1] = v1
|
||||
}
|
||||
return result
|
||||
}
|
||||
399
stdlib/public/core/DictionaryStorage.swift
Normal file
399
stdlib/public/core/DictionaryStorage.swift
Normal file
@@ -0,0 +1,399 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import SwiftShims
|
||||
|
||||
/// An instance of this class has all `Dictionary` data tail-allocated.
|
||||
/// Enough bytes are allocated to hold the bitmap for marking valid entries,
|
||||
/// keys, and values. The data layout starts with the bitmap, followed by the
|
||||
/// keys, followed by the values.
|
||||
//
|
||||
// See the docs at the top of the file for more details on this type
|
||||
//
|
||||
// NOTE: The precise layout of this type is relied on in the runtime
|
||||
// to provide a statically allocated empty singleton.
|
||||
// See stdlib/public/stubs/GlobalObjects.cpp for details.
|
||||
@_fixed_layout // FIXME(sil-serialize-all)
|
||||
@usableFromInline
|
||||
@_objc_non_lazy_realization
|
||||
internal class _RawDictionaryStorage: __SwiftNativeNSDictionary {
|
||||
/// The current number of occupied entries in this dictionary.
|
||||
@usableFromInline
|
||||
@nonobjc
|
||||
internal final var _count: Int
|
||||
|
||||
/// The maximum number of elements that can be inserted into this set without
|
||||
/// exceeding the hash table's maximum load factor.
|
||||
@usableFromInline
|
||||
@nonobjc
|
||||
internal final var _capacity: Int
|
||||
|
||||
/// The scale of this dictionary. The number of buckets is 2 raised to the
|
||||
/// power of `scale`.
|
||||
@usableFromInline
|
||||
@nonobjc
|
||||
internal final var _scale: Int
|
||||
|
||||
@usableFromInline
|
||||
internal final var _seed: Hasher._Seed
|
||||
|
||||
@usableFromInline
|
||||
@nonobjc
|
||||
internal final var _rawKeys: UnsafeMutableRawPointer
|
||||
|
||||
@usableFromInline
|
||||
@nonobjc
|
||||
internal final var _rawValues: UnsafeMutableRawPointer
|
||||
|
||||
// This type is made with allocWithTailElems, so no init is ever called.
|
||||
// But we still need to have an init to satisfy the compiler.
|
||||
@nonobjc
|
||||
internal init(_doNotCallMe: ()) {
|
||||
_sanityCheckFailure("This class cannot be directly initialized")
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@nonobjc
|
||||
internal final var _bucketCount: Int {
|
||||
@inline(__always) get { return 1 &<< _scale }
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@nonobjc
|
||||
internal final var _metadata: UnsafeMutablePointer<_HashTable.Word> {
|
||||
@inline(__always) get {
|
||||
let address = Builtin.projectTailElems(self, _HashTable.Word.self)
|
||||
return UnsafeMutablePointer(address)
|
||||
}
|
||||
}
|
||||
|
||||
// The _HashTable struct contains pointers into tail-allocated storage, so
|
||||
// this is unsafe and needs `_fixLifetime` calls in the caller.
|
||||
@inlinable
|
||||
@nonobjc
|
||||
internal final var _hashTable: _HashTable {
|
||||
@inline(__always) get {
|
||||
return _HashTable(words: _metadata, bucketCount: _bucketCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The storage class for the singleton empty set.
|
||||
/// The single instance of this class is created by the runtime.
|
||||
@_fixed_layout
|
||||
@usableFromInline
|
||||
internal class _EmptyDictionarySingleton: _RawDictionaryStorage {
|
||||
@nonobjc
|
||||
internal override init(_doNotCallMe: ()) {
|
||||
_sanityCheckFailure("This class cannot be directly initialized")
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@objc
|
||||
internal required init(
|
||||
objects: UnsafePointer<AnyObject?>,
|
||||
forKeys: UnsafeRawPointer,
|
||||
count: Int
|
||||
) {
|
||||
_sanityCheckFailure("This class cannot be directly initialized")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
extension _EmptyDictionarySingleton: _NSDictionaryCore {
|
||||
@objc(copyWithZone:)
|
||||
internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
|
||||
return self
|
||||
}
|
||||
|
||||
@objc
|
||||
internal var count: Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>?, count: Int
|
||||
) -> Int {
|
||||
// Even though we never do anything in here, we need to update the
|
||||
// state so that callers know we actually ran.
|
||||
|
||||
var theState = state.pointee
|
||||
if theState.state == 0 {
|
||||
theState.state = 1 // Arbitrary non-zero value.
|
||||
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
|
||||
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
}
|
||||
state.pointee = theState
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@objc(objectForKey:)
|
||||
internal func object(forKey aKey: AnyObject) -> AnyObject? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc(keyEnumerator)
|
||||
internal func keyEnumerator() -> _NSEnumerator {
|
||||
return _SwiftEmptyNSEnumerator()
|
||||
}
|
||||
|
||||
@objc(getObjects:andKeys:count:)
|
||||
internal func getObjects(
|
||||
_ objects: UnsafeMutablePointer<AnyObject>?,
|
||||
andKeys keys: UnsafeMutablePointer<AnyObject>?,
|
||||
count: Int) {
|
||||
// Do nothing, we're empty
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
extension _RawDictionaryStorage {
|
||||
/// The empty singleton that is used for every single Dictionary that is
|
||||
/// created without any elements. The contents of the storage should never
|
||||
/// be mutated.
|
||||
@inlinable
|
||||
@nonobjc
|
||||
internal static var empty: _EmptyDictionarySingleton {
|
||||
return Builtin.bridgeFromRawPointer(
|
||||
Builtin.addressof(&_swiftEmptyDictionarySingleton))
|
||||
}
|
||||
}
|
||||
|
||||
// See the docs at the top of this file for a description of this type
|
||||
@_fixed_layout // FIXME(sil-serialize-all)
|
||||
@usableFromInline
|
||||
final internal class _DictionaryStorage<Key: Hashable, Value>
|
||||
: _RawDictionaryStorage, _NSDictionaryCore {
|
||||
// This type is made with allocWithTailElems, so no init is ever called.
|
||||
// But we still need to have an init to satisfy the compiler.
|
||||
@nonobjc
|
||||
override internal init(_doNotCallMe: ()) {
|
||||
_sanityCheckFailure("This class cannot be directly initialized")
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@objc
|
||||
internal required init(
|
||||
objects: UnsafePointer<AnyObject?>,
|
||||
forKeys: UnsafeRawPointer,
|
||||
count: Int
|
||||
) {
|
||||
_sanityCheckFailure("This class cannot be directly initialized")
|
||||
}
|
||||
#endif
|
||||
|
||||
deinit {
|
||||
guard _count > 0 else { return }
|
||||
if !_isPOD(Key.self) {
|
||||
let keys = self._keys
|
||||
for index in _hashTable {
|
||||
(keys + index.bucket).deinitialize(count: 1)
|
||||
}
|
||||
}
|
||||
if !_isPOD(Value.self) {
|
||||
let values = self._values
|
||||
for index in _hashTable {
|
||||
(values + index.bucket).deinitialize(count: 1)
|
||||
}
|
||||
}
|
||||
_count = 0
|
||||
_fixLifetime(self)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
final internal var _keys: UnsafeMutablePointer<Key> {
|
||||
@inline(__always)
|
||||
get {
|
||||
return self._rawKeys.assumingMemoryBound(to: Key.self)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
final internal var _values: UnsafeMutablePointer<Value> {
|
||||
@inline(__always)
|
||||
get {
|
||||
return self._rawValues.assumingMemoryBound(to: Value.self)
|
||||
}
|
||||
}
|
||||
|
||||
internal var asNative: _NativeDictionary<Key, Value> {
|
||||
return _NativeDictionary(self)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
internal static func reallocate(
|
||||
original: _RawDictionaryStorage,
|
||||
capacity: Int
|
||||
) -> (storage: _DictionaryStorage, rehash: Bool) {
|
||||
_sanityCheck(capacity >= original._count)
|
||||
let scale = _HashTable.scale(forCapacity: capacity)
|
||||
let rehash = (scale != original._scale)
|
||||
let newStorage = _DictionaryStorage<Key, Value>.allocate(scale: scale)
|
||||
return (newStorage, rehash)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
static internal func allocate(capacity: Int) -> _DictionaryStorage {
|
||||
let scale = _HashTable.scale(forCapacity: capacity)
|
||||
return allocate(scale: scale)
|
||||
}
|
||||
|
||||
static internal func allocate(scale: Int) -> _DictionaryStorage {
|
||||
// The entry count must be representable by an Int value; hence the scale's
|
||||
// peculiar upper bound.
|
||||
_sanityCheck(scale >= 0 && scale < Int.bitWidth - 1)
|
||||
|
||||
let bucketCount = 1 &<< scale
|
||||
let wordCount = _UnsafeBitset.wordCount(forCapacity: bucketCount)
|
||||
let storage = Builtin.allocWithTailElems_3(
|
||||
_DictionaryStorage<Key, Value>.self,
|
||||
wordCount._builtinWordValue, _HashTable.Word.self,
|
||||
bucketCount._builtinWordValue, Key.self,
|
||||
bucketCount._builtinWordValue, Value.self)
|
||||
|
||||
let metadataAddr = Builtin.projectTailElems(storage, _HashTable.Word.self)
|
||||
let keysAddr = Builtin.getTailAddr_Word(
|
||||
metadataAddr, wordCount._builtinWordValue, _HashTable.Word.self,
|
||||
Key.self)
|
||||
let valuesAddr = Builtin.getTailAddr_Word(
|
||||
keysAddr, bucketCount._builtinWordValue, Key.self,
|
||||
Value.self)
|
||||
storage._count = 0
|
||||
storage._capacity = _HashTable.capacity(forScale: scale)
|
||||
storage._scale = scale
|
||||
storage._rawKeys = UnsafeMutableRawPointer(keysAddr)
|
||||
storage._rawValues = UnsafeMutableRawPointer(valuesAddr)
|
||||
|
||||
// We use a slightly different hash seed whenever we change the size of the
|
||||
// hash table, so that we avoid certain copy operations becoming quadratic,
|
||||
// without breaking value semantics. (For background details, see
|
||||
// https://bugs.swift.org/browse/SR-3268)
|
||||
|
||||
// FIXME: Use true per-instance seeding instead. Per-capacity seeding still
|
||||
// leaves hash values the same in same-sized tables, which may affect
|
||||
// operations on two tables at once. (E.g., union.)
|
||||
storage._seed = (
|
||||
Hasher._seed.0 ^ UInt64(truncatingIfNeeded: scale),
|
||||
Hasher._seed.1)
|
||||
// Initialize hash table metadata.
|
||||
storage._hashTable.clear()
|
||||
return storage
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@objc(copyWithZone:)
|
||||
internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
|
||||
return self
|
||||
}
|
||||
|
||||
@objc
|
||||
internal var count: Int {
|
||||
return _count
|
||||
}
|
||||
|
||||
@objc(keyEnumerator)
|
||||
internal func keyEnumerator() -> _NSEnumerator {
|
||||
return _SwiftDictionaryNSEnumerator<Key, Value>(asNative)
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>?, count: Int
|
||||
) -> Int {
|
||||
var theState = state.pointee
|
||||
if theState.state == 0 {
|
||||
theState.state = 1 // Arbitrary non-zero value.
|
||||
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
|
||||
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
theState.extra.0 = CUnsignedLong(asNative.startIndex.bucket)
|
||||
}
|
||||
|
||||
// Test 'objects' rather than 'count' because (a) this is very rare anyway,
|
||||
// and (b) the optimizer should then be able to optimize away the
|
||||
// unwrapping check below.
|
||||
if _slowPath(objects == nil) {
|
||||
return 0
|
||||
}
|
||||
|
||||
let unmanagedObjects = _UnmanagedAnyObjectArray(objects!)
|
||||
var index = _HashTable.Index(bucket: Int(theState.extra.0))
|
||||
let endIndex = asNative.endIndex
|
||||
_precondition(index == endIndex || _hashTable.isOccupied(index))
|
||||
var stored = 0
|
||||
for i in 0..<count {
|
||||
if index == endIndex { break }
|
||||
|
||||
let key = asNative.uncheckedKey(at: index)
|
||||
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(key)
|
||||
|
||||
stored += 1
|
||||
index = asNative.index(after: index)
|
||||
}
|
||||
theState.extra.0 = CUnsignedLong(index.bucket)
|
||||
state.pointee = theState
|
||||
return stored
|
||||
}
|
||||
|
||||
@objc(objectForKey:)
|
||||
internal func object(forKey aKey: AnyObject) -> AnyObject? {
|
||||
guard let nativeKey = _conditionallyBridgeFromObjectiveC(aKey, Key.self)
|
||||
else { return nil }
|
||||
|
||||
let (index, found) = asNative.find(nativeKey)
|
||||
guard found else { return nil }
|
||||
let value = asNative.uncheckedValue(at: index)
|
||||
return _bridgeAnythingToObjectiveC(value)
|
||||
}
|
||||
|
||||
@objc(getObjects:andKeys:count:)
|
||||
internal func getObjects(
|
||||
_ objects: UnsafeMutablePointer<AnyObject>?,
|
||||
andKeys keys: UnsafeMutablePointer<AnyObject>?,
|
||||
count: Int) {
|
||||
_precondition(count >= 0, "Invalid count")
|
||||
guard count > 0 else { return }
|
||||
var i = 0 // Current position in the output buffers
|
||||
switch (_UnmanagedAnyObjectArray(keys), _UnmanagedAnyObjectArray(objects)) {
|
||||
case (let unmanagedKeys?, let unmanagedObjects?):
|
||||
for (key, value) in asNative {
|
||||
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(value)
|
||||
unmanagedKeys[i] = _bridgeAnythingToObjectiveC(key)
|
||||
i += 1
|
||||
guard i < count else { break }
|
||||
}
|
||||
case (let unmanagedKeys?, nil):
|
||||
for (key, _) in asNative {
|
||||
unmanagedKeys[i] = _bridgeAnythingToObjectiveC(key)
|
||||
i += 1
|
||||
guard i < count else { break }
|
||||
}
|
||||
case (nil, let unmanagedObjects?):
|
||||
for (_, value) in asNative {
|
||||
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(value)
|
||||
i += 1
|
||||
guard i < count else { break }
|
||||
}
|
||||
case (nil, nil):
|
||||
// Do nothing.
|
||||
break
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
518
stdlib/public/core/DictionaryVariant.swift
Normal file
518
stdlib/public/core/DictionaryVariant.swift
Normal file
@@ -0,0 +1,518 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// This protocol is only used for compile-time checks that
|
||||
/// every buffer type implements all required operations.
|
||||
internal protocol _DictionaryBuffer {
|
||||
associatedtype Key
|
||||
associatedtype Value
|
||||
associatedtype Index
|
||||
|
||||
var startIndex: Index { get }
|
||||
var endIndex: Index { get }
|
||||
func index(after i: Index) -> Index
|
||||
func index(forKey key: Key) -> Index?
|
||||
var count: Int { get }
|
||||
|
||||
func contains(_ key: Key) -> Bool
|
||||
func lookup(_ key: Key) -> Value?
|
||||
func lookup(_ index: Index) -> (key: Key, value: Value)
|
||||
func key(at index: Index) -> Key
|
||||
func value(at index: Index) -> Value
|
||||
}
|
||||
|
||||
extension Dictionary {
|
||||
@usableFromInline
|
||||
@_frozen
|
||||
internal enum _Variant {
|
||||
case native(_NativeDictionary<Key, Value>)
|
||||
#if _runtime(_ObjC)
|
||||
case cocoa(_CocoaDictionary)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary._Variant {
|
||||
#if _runtime(_ObjC)
|
||||
@usableFromInline @_transparent
|
||||
internal var guaranteedNative: Bool {
|
||||
return _canBeClass(Key.self) == 0 || _canBeClass(Value.self) == 0
|
||||
}
|
||||
|
||||
// Allow the optimizer to consider the surrounding code unreachable if Element
|
||||
// is guaranteed to be native.
|
||||
@usableFromInline @_transparent
|
||||
internal func cocoaPath() {
|
||||
if guaranteedNative {
|
||||
_conditionallyUnreachable()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@inlinable
|
||||
internal mutating func isUniquelyReferenced() -> Bool {
|
||||
switch self {
|
||||
case .native:
|
||||
// Note that &self drills down through .native(_NativeDictionary) to the
|
||||
// first property in _NativeDictionary, which is the reference to the
|
||||
// storage.
|
||||
return _isUnique_native(&self)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa:
|
||||
cocoaPath()
|
||||
// Don't consider Cocoa buffer mutable, even if it is mutable and is
|
||||
// uniquely referenced.
|
||||
return false
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var asNative: _NativeDictionary<Key, Value> {
|
||||
@inline(__always)
|
||||
get {
|
||||
switch self {
|
||||
case .native(let native):
|
||||
return native
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa:
|
||||
_sanityCheckFailure("internal error: not backed by native buffer")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@inline(__always)
|
||||
set {
|
||||
self = .native(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@inlinable
|
||||
internal var asCocoa: _CocoaDictionary {
|
||||
switch self {
|
||||
case .native:
|
||||
_sanityCheckFailure("internal error: not backed by NSDictionary")
|
||||
case .cocoa(let cocoa):
|
||||
return cocoa
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Reserves enough space for the specified number of elements to be stored
|
||||
/// without reallocating additional storage.
|
||||
@inlinable
|
||||
internal mutating func reserveCapacity(_ capacity: Int) {
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = isUniquelyReferenced()
|
||||
asNative.reserveCapacity(capacity, isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let capacity = Swift.max(cocoa.count, capacity)
|
||||
self = .native(_NativeDictionary(cocoa, capacity: capacity))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of elements that can be stored without expanding the current
|
||||
/// storage.
|
||||
///
|
||||
/// For bridged storage, this is equal to the current count of the
|
||||
/// collection, since any addition will trigger a copy of the elements into
|
||||
/// newly allocated storage. For native storage, this is the element count
|
||||
/// at which adding any more elements will exceed the load factor.
|
||||
@inlinable
|
||||
internal var capacity: Int {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.capacity
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return cocoa.count
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary._Variant: _DictionaryBuffer {
|
||||
@usableFromInline
|
||||
internal typealias Element = (key: Key, value: Value)
|
||||
@usableFromInline
|
||||
internal typealias Index = Dictionary<Key, Value>.Index
|
||||
|
||||
@inlinable
|
||||
internal var startIndex: Index {
|
||||
switch self {
|
||||
case .native:
|
||||
return Index(_native: asNative.startIndex)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return Index(_cocoa: cocoa.startIndex)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var endIndex: Index {
|
||||
switch self {
|
||||
case .native:
|
||||
return Index(_native: asNative.endIndex)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return Index(_cocoa: cocoa.endIndex)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func index(after i: Index) -> Index {
|
||||
switch self {
|
||||
case .native:
|
||||
return Index(_native: asNative.index(after: i._asNative))
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return Index(_cocoa: cocoa.index(after: i._asCocoa))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func index(forKey key: Key) -> Index? {
|
||||
switch self {
|
||||
case .native:
|
||||
guard let index = asNative.index(forKey: key) else { return nil }
|
||||
return Index(_native: index)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let cocoaKey = _bridgeAnythingToObjectiveC(key)
|
||||
guard let index = cocoa.index(forKey: cocoaKey) else { return nil }
|
||||
return Index(_cocoa: index)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var count: Int {
|
||||
@inline(__always)
|
||||
get {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.count
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return cocoa.count
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func contains(_ key: Key) -> Bool {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.contains(key)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let cocoaKey = _bridgeAnythingToObjectiveC(key)
|
||||
return cocoa.contains(cocoaKey)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func lookup(_ key: Key) -> Value? {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.lookup(key)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let cocoaKey = _bridgeAnythingToObjectiveC(key)
|
||||
guard let cocoaValue = cocoa.lookup(cocoaKey) else { return nil }
|
||||
return _forceBridgeFromObjectiveC(cocoaValue, Value.self)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func lookup(_ index: Index) -> (key: Key, value: Value) {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.lookup(index._asNative)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let (cocoaKey, cocoaValue) = cocoa.lookup(index._asCocoa)
|
||||
let nativeKey = _forceBridgeFromObjectiveC(cocoaKey, Key.self)
|
||||
let nativeValue = _forceBridgeFromObjectiveC(cocoaValue, Value.self)
|
||||
return (nativeKey, nativeValue)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func key(at index: Index) -> Key {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.key(at: index._asNative)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let cocoaKey = cocoa.key(at: index._asCocoa)
|
||||
return _forceBridgeFromObjectiveC(cocoaKey, Key.self)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func value(at index: Index) -> Value {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.value(at: index._asNative)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let cocoaValue = cocoa.value(at: index._asCocoa)
|
||||
return _forceBridgeFromObjectiveC(cocoaValue, Value.self)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary._Variant {
|
||||
/// Same as find(_:), except assume a corresponding key/value pair will be
|
||||
/// inserted if it doesn't already exist, and mutated if it does exist. When
|
||||
/// this function returns, the storage is guaranteed to be native, uniquely
|
||||
/// held, and with enough capacity for a single insertion (if the key isn't
|
||||
/// already in the dictionary.)
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func mutatingFind(
|
||||
_ key: Key
|
||||
) -> (index: _NativeDictionary<Key, Value>.Index, found: Bool) {
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = isUniquelyReferenced()
|
||||
return asNative.mutatingFind(key, isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
var native = _NativeDictionary<Key, Value>(
|
||||
cocoa, capacity: cocoa.count + 1)
|
||||
let result = native.mutatingFind(key, isUnique: true)
|
||||
self = .native(native)
|
||||
return result
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure uniquely held native storage, while preserving the given index.
|
||||
/// (If the variant had bridged storage, then the returned index will be the
|
||||
/// corresponding native representation. Otherwise it's kept the same.)
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func ensureUniqueNative(
|
||||
preserving index: Index
|
||||
) -> _NativeDictionary<Key, Value>.Index {
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = isUniquelyReferenced()
|
||||
if !isUnique {
|
||||
let rehashed = asNative.copy(capacity: asNative.capacity)
|
||||
_sanityCheck(!rehashed)
|
||||
}
|
||||
return index._asNative
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
// We have to migrate the data first. But after we do so, the Cocoa
|
||||
// index becomes useless, so get the key first.
|
||||
let cocoaKey = cocoa.key(at: index._asCocoa)
|
||||
let native = _NativeDictionary<Key, Value>(cocoa)
|
||||
self = .native(native)
|
||||
let nativeKey = _forceBridgeFromObjectiveC(cocoaKey, Key.self)
|
||||
let (nativeIndex, found) = native.find(nativeKey)
|
||||
_precondition(found, "Bridging did not preserve equality")
|
||||
return nativeIndex
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func updateValue(
|
||||
_ value: Value,
|
||||
forKey key: Key
|
||||
) -> Value? {
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = self.isUniquelyReferenced()
|
||||
return asNative.updateValue(value, forKey: key, isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
// Make sure we have space for an extra element.
|
||||
var native = _NativeDictionary<Key, Value>(
|
||||
cocoa,
|
||||
capacity: cocoa.count + 1)
|
||||
let result = native.updateValue(value, forKey: key, isUnique: true)
|
||||
self = .native(native)
|
||||
return result
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func setValue(_ value: Value, forKey key: Key) {
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = self.isUniquelyReferenced()
|
||||
asNative.setValue(value, forKey: key, isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
// Make sure we have space for an extra element.
|
||||
var native = _NativeDictionary<Key, Value>(
|
||||
cocoa,
|
||||
capacity: cocoa.count + 1)
|
||||
native.setValue(value, forKey: key, isUnique: true)
|
||||
self = .native(native)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func remove(at index: Index) -> Element {
|
||||
// FIXME(performance): fuse data migration and element deletion into one
|
||||
// operation.
|
||||
let index = ensureUniqueNative(preserving: index)
|
||||
return asNative.remove(at: index, isUnique: true)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func removeValue(forKey key: Key) -> Value? {
|
||||
switch self {
|
||||
case .native:
|
||||
let (index, found) = asNative.find(key)
|
||||
guard found else { return nil }
|
||||
let isUnique = isUniquelyReferenced()
|
||||
return asNative.uncheckedRemove(at: index, isUnique: isUnique).value
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let cocoaKey = _bridgeAnythingToObjectiveC(key)
|
||||
guard cocoa.lookup(cocoaKey) != nil else { return nil }
|
||||
var native = _NativeDictionary<Key, Value>(cocoa)
|
||||
let (index, found) = native.find(key)
|
||||
_precondition(found, "Bridging did not preserve equality")
|
||||
let old = native.uncheckedRemove(at: index, isUnique: true).value
|
||||
self = .native(native)
|
||||
return old
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func removeAll(keepingCapacity keepCapacity: Bool) {
|
||||
if !keepCapacity {
|
||||
self = .native(_NativeDictionary())
|
||||
return
|
||||
}
|
||||
guard count > 0 else { return }
|
||||
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = isUniquelyReferenced()
|
||||
asNative.removeAll(isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
self = .native(_NativeDictionary(capacity: cocoa.count))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary._Variant {
|
||||
/// Returns an iterator over the `(Key, Value)` pairs.
|
||||
///
|
||||
/// - Complexity: O(1).
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func makeIterator() -> Dictionary<Key, Value>.Iterator {
|
||||
switch self {
|
||||
case .native(let native):
|
||||
return Dictionary.Iterator(_native: native.makeIterator())
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return Dictionary.Iterator(_cocoa: cocoa.makeIterator())
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary._Variant {
|
||||
@inlinable
|
||||
internal func mapValues<T>(
|
||||
_ transform: (Value) throws -> T
|
||||
) rethrows -> _NativeDictionary<Key, T> {
|
||||
switch self {
|
||||
case .native(let native):
|
||||
return try native.mapValues(transform)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return try cocoa.mapValues(transform)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func merge<S: Sequence>(
|
||||
_ keysAndValues: S,
|
||||
uniquingKeysWith combine: (Value, Value) throws -> Value
|
||||
) rethrows where S.Element == (Key, Value) {
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = isUniquelyReferenced()
|
||||
try asNative.merge(
|
||||
keysAndValues,
|
||||
isUnique: isUnique,
|
||||
uniquingKeysWith: combine)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
var native = _NativeDictionary<Key, Value>(cocoa)
|
||||
try native.merge(
|
||||
keysAndValues,
|
||||
isUnique: true,
|
||||
uniquingKeysWith: combine)
|
||||
self = .native(native)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,9 +111,22 @@
|
||||
"SliceBuffer.swift",
|
||||
"SwiftNativeNSArray.swift"],
|
||||
"HashedCollections": [
|
||||
"HashTable.swift",
|
||||
"Dictionary.swift",
|
||||
"HashedCollectionsAnyHashableExtensions.swift",
|
||||
"NativeDictionary.swift",
|
||||
"DictionaryBridging.swift",
|
||||
"DictionaryBuilder.swift",
|
||||
"DictionaryCasting.swift",
|
||||
"DictionaryStorage.swift",
|
||||
"DictionaryVariant.swift",
|
||||
"Set.swift",
|
||||
"NativeSet.swift",
|
||||
"SetAnyHashableExtensions.swift",
|
||||
"SetBridging.swift",
|
||||
"SetBuilder.swift",
|
||||
"SetCasting.swift",
|
||||
"SetStorage.swift",
|
||||
"SetVariant.swift"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -165,12 +178,16 @@
|
||||
"Playground": [
|
||||
"PlaygroundDisplay.swift"
|
||||
],
|
||||
"Misc": [
|
||||
"AnyHashable.swift",
|
||||
"Interval.swift",
|
||||
"Hashing.swift",
|
||||
"SipHash.swift",
|
||||
"Hashing": [
|
||||
"Hashable.swift",
|
||||
"Hasher.swift",
|
||||
"SipHash.swift",
|
||||
"AnyHashable.swift",
|
||||
"Hashing.swift",
|
||||
"Bitset.swift"
|
||||
],
|
||||
"Misc": [
|
||||
"Interval.swift",
|
||||
"ErrorType.swift",
|
||||
"InputStream.swift",
|
||||
"LifetimeManager.swift",
|
||||
@@ -191,11 +208,9 @@
|
||||
"CommandLine.swift",
|
||||
"Tuple.swift",
|
||||
"NewtypeWrapper.swift",
|
||||
"UnsafeBitMap.swift",
|
||||
"DebuggerSupport.swift",
|
||||
"Equatable.swift",
|
||||
"Comparable.swift",
|
||||
"Hashable.swift",
|
||||
"Codable.swift",
|
||||
"MigrationSupport.swift"
|
||||
]
|
||||
|
||||
438
stdlib/public/core/HashTable.swift
Normal file
438
stdlib/public/core/HashTable.swift
Normal file
@@ -0,0 +1,438 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@usableFromInline
|
||||
internal protocol _HashTableDelegate {
|
||||
func hashValue(at index: _HashTable.Index) -> Int
|
||||
func moveEntry(from source: _HashTable.Index, to target: _HashTable.Index)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_fixed_layout
|
||||
internal struct _HashTable {
|
||||
@usableFromInline
|
||||
internal typealias Word = _UnsafeBitset.Word
|
||||
|
||||
@usableFromInline
|
||||
internal var words: UnsafeMutablePointer<Word>
|
||||
|
||||
@usableFromInline
|
||||
internal let bucketMask: Int
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal init(words: UnsafeMutablePointer<Word>, bucketCount: Int) {
|
||||
_sanityCheck(bucketCount > 0 && bucketCount & (bucketCount - 1) == 0,
|
||||
"bucketCount must be a power of two")
|
||||
self.words = words
|
||||
// The bucket count is a power of two, so subtracting 1 will never overflow
|
||||
// and get us a nice mask.
|
||||
self.bucketMask = bucketCount &- 1
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var bucketCount: Int {
|
||||
@inline(__always) get {
|
||||
return bucketMask &+ 1
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var wordCount: Int {
|
||||
@inline(__always) get {
|
||||
return _UnsafeBitset.wordCount(forCapacity: bucketCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable {
|
||||
/// The inverse of the maximum hash table load factor.
|
||||
private static var maxLoadFactor: Double {
|
||||
@inline(__always) get { return 3 / 4 }
|
||||
}
|
||||
|
||||
internal static func capacity(forScale scale: Int) -> Int {
|
||||
let bucketCount = 1 &<< scale
|
||||
return Int(Double(bucketCount) * maxLoadFactor)
|
||||
}
|
||||
|
||||
internal static func scale(forCapacity capacity: Int) -> Int {
|
||||
let capacity = Swift.max(capacity, 1)
|
||||
// Calculate the minimum number of entries we need to allocate to satisfy
|
||||
// the maximum load factor. `capacity + 1` below ensures that we always
|
||||
// leave at least one hole.
|
||||
let minimumEntries = Swift.max(
|
||||
Int((Double(capacity) / maxLoadFactor).rounded(.up)),
|
||||
capacity + 1)
|
||||
// The actual number of entries we need to allocate is the lowest power of
|
||||
// two greater than or equal to the minimum entry count. Calculate its
|
||||
// exponent.
|
||||
let exponent = (Swift.max(minimumEntries, 2) - 1)._binaryLogarithm() + 1
|
||||
_sanityCheck(exponent >= 0 && exponent < Int.bitWidth)
|
||||
_sanityCheck(self.capacity(forScale: exponent) >= capacity)
|
||||
// The scale is the exponent corresponding to the bucket count.
|
||||
return exponent
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable {
|
||||
@_fixed_layout
|
||||
@usableFromInline
|
||||
internal struct Index {
|
||||
@usableFromInline
|
||||
internal var bucket: Int
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal init(bucket: Int) {
|
||||
_sanityCheck(bucket >= 0)
|
||||
self.bucket = bucket
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal init(word: Int, bit: Int) {
|
||||
self.bucket = _UnsafeBitset.join(word: word, bit: bit)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var word: Int {
|
||||
@inline(__always) get {
|
||||
return _UnsafeBitset.word(for: bucket)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var bit: Int {
|
||||
@inline(__always) get {
|
||||
return _UnsafeBitset.bit(for: bucket)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable.Index: Equatable {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal
|
||||
static func == (lhs: _HashTable.Index, rhs: _HashTable.Index) -> Bool {
|
||||
return lhs.bucket == rhs.bucket
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable.Index: Comparable {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal
|
||||
static func < (lhs: _HashTable.Index, rhs: _HashTable.Index) -> Bool {
|
||||
return lhs.bucket < rhs.bucket
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable: Sequence {
|
||||
@usableFromInline
|
||||
@_fixed_layout
|
||||
internal struct Iterator: IteratorProtocol {
|
||||
@usableFromInline
|
||||
let hashTable: _HashTable
|
||||
@usableFromInline
|
||||
var wordIndex: Int
|
||||
@usableFromInline
|
||||
var word: Word
|
||||
|
||||
@inlinable
|
||||
init(_ hashTable: _HashTable) {
|
||||
self.hashTable = hashTable
|
||||
self.wordIndex = 0
|
||||
self.word = hashTable.words[0]
|
||||
if hashTable.bucketCount < Word.capacity {
|
||||
self.word = self.word.intersecting(elementsBelow: hashTable.bucketCount)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func next() -> Index? {
|
||||
if let bit = word.next() {
|
||||
return Index(word: wordIndex, bit: bit)
|
||||
}
|
||||
while wordIndex + 1 < hashTable.wordCount {
|
||||
wordIndex += 1
|
||||
word = hashTable.words[wordIndex]
|
||||
if let bit = word.next() {
|
||||
return Index(word: wordIndex, bit: bit)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func makeIterator() -> Iterator {
|
||||
return Iterator(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func isValid(_ index: Index) -> Bool {
|
||||
return index.bucket >= 0 && index.bucket < bucketCount
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func _isOccupied(_ index: Index) -> Bool {
|
||||
_sanityCheck(isValid(index))
|
||||
return words[index.word].uncheckedContains(index.bit)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func isOccupied(_ index: Index) -> Bool {
|
||||
return isValid(index) && _isOccupied(index)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func checkOccupied(_ i: Index) {
|
||||
_precondition(isOccupied(i),
|
||||
"Attempting to access Collection elements using an invalid Index")
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func _firstOccupiedIndex(fromWord word: Int) -> Index {
|
||||
_sanityCheck(word >= 0 && word <= wordCount)
|
||||
var word = word
|
||||
while word < wordCount {
|
||||
if let bit = words[word].minimum {
|
||||
return Index(word: word, bit: bit)
|
||||
}
|
||||
word += 1
|
||||
}
|
||||
return endIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func index(after index: Index) -> Index {
|
||||
_sanityCheck(isValid(index))
|
||||
let word = index.word
|
||||
if let bit = words[word].intersecting(elementsAbove: index.bit).minimum {
|
||||
return Index(word: word, bit: bit)
|
||||
}
|
||||
return _firstOccupiedIndex(fromWord: word + 1)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var startIndex: Index {
|
||||
return _firstOccupiedIndex(fromWord: 0)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var endIndex: Index {
|
||||
@inline(__always)
|
||||
get {
|
||||
return Index(bucket: bucketCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func idealIndex(forHashValue hashValue: Int) -> Index {
|
||||
return Index(bucket: hashValue & bucketMask)
|
||||
}
|
||||
|
||||
/// The next bucket after `bucket`, with wraparound at the end of the table.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func index(wrappedAfter index: Index) -> Index {
|
||||
// The bucket is less than bucketCount, which is power of two less than
|
||||
// Int.max. Therefore adding 1 does not overflow.
|
||||
return Index(bucket: (index.bucket &+ 1) & bucketMask)
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable {
|
||||
@inlinable
|
||||
internal func previousHole(before index: Index) -> Index {
|
||||
_sanityCheck(isValid(index))
|
||||
// Note that if we have only a single partial word, its out-of-bounds bits
|
||||
// are guaranteed to be all set, so the formula below gives correct results.
|
||||
var word = index.word
|
||||
if let bit =
|
||||
words[word]
|
||||
.complement
|
||||
.intersecting(elementsBelow: index.bit)
|
||||
.maximum {
|
||||
return Index(word: word, bit: bit)
|
||||
}
|
||||
var wrap = false
|
||||
while true {
|
||||
word -= 1
|
||||
if word < 0 {
|
||||
_precondition(!wrap, "Hash table has no holes")
|
||||
wrap = true
|
||||
word = wordCount - 1
|
||||
}
|
||||
if let bit = words[word].complement.maximum {
|
||||
return Index(word: word, bit: bit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func nextHole(atOrAfter index: Index) -> Index {
|
||||
_sanityCheck(isValid(index))
|
||||
// Note that if we have only a single partial word, its out-of-bounds bits
|
||||
// are guaranteed to be all set, so the formula below gives correct results.
|
||||
var word = index.word
|
||||
if let bit =
|
||||
words[word]
|
||||
.complement
|
||||
.subtracting(elementsBelow: index.bit)
|
||||
.minimum {
|
||||
return Index(word: word, bit: bit)
|
||||
}
|
||||
var wrap = false
|
||||
while true {
|
||||
word += 1
|
||||
if word == wordCount {
|
||||
_precondition(!wrap, "Hash table has no holes")
|
||||
wrap = true
|
||||
word = 0
|
||||
}
|
||||
if let bit = words[word].complement.minimum {
|
||||
return Index(word: word, bit: bit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
@_effects(releasenone)
|
||||
internal func copyContents(of other: _HashTable) {
|
||||
_sanityCheck(bucketCount == other.bucketCount)
|
||||
self.words.assign(from: other.words, count: bucketCount)
|
||||
}
|
||||
|
||||
/// Insert a new entry with the specified hash value into the table.
|
||||
/// The entry must not already exist in the table -- duplicates are ignored.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func insertNew(hashValue: Int) -> Index {
|
||||
let hole = nextHole(atOrAfter: idealIndex(forHashValue: hashValue))
|
||||
insert(hole)
|
||||
return hole
|
||||
}
|
||||
|
||||
/// Insert a new entry for an element at `index`.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func insert(_ index: Index) {
|
||||
_sanityCheck(!isOccupied(index))
|
||||
words[index.word].uncheckedInsert(index.bit)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func clear() {
|
||||
if bucketCount < Word.capacity {
|
||||
// We have only a single partial word. Set all out of bounds bits, so that
|
||||
// `index(after:)` and `nextHole(atOrAfter:)` works correctly without a
|
||||
// special case.
|
||||
words[0] = Word.allBits.subtracting(elementsBelow: bucketCount)
|
||||
} else {
|
||||
words.assign(repeating: .empty, count: wordCount)
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
@inlinable
|
||||
internal func delete<D: _HashTableDelegate>(
|
||||
at index: Index,
|
||||
with delegate: D
|
||||
) {
|
||||
_sanityCheck(isOccupied(index))
|
||||
|
||||
// If we've put a hole in a chain of contiguous elements, some element after
|
||||
// the hole may belong where the new hole is.
|
||||
|
||||
var hole = index
|
||||
var candidate = self.index(wrappedAfter: hole)
|
||||
|
||||
guard _isOccupied(candidate) else {
|
||||
// Fast path: Don't get the first bucket when there's nothing to do.
|
||||
words[hole.word].uncheckedRemove(hole.bit)
|
||||
return
|
||||
}
|
||||
|
||||
// Find the first bucket in the contiguous chain that contains the entry
|
||||
// we've just deleted.
|
||||
let start = self.index(wrappedAfter: previousHole(before: index))
|
||||
|
||||
// Relocate out-of-place elements in the chain, repeating until we get to
|
||||
// the end of the chain.
|
||||
while _isOccupied(candidate) {
|
||||
let candidateHash = delegate.hashValue(at: candidate)
|
||||
let ideal = idealIndex(forHashValue: candidateHash)
|
||||
|
||||
// Does this element belong between start and hole? We need two
|
||||
// separate tests depending on whether [start, hole] wraps around the
|
||||
// end of the storage.
|
||||
let c0 = ideal >= start
|
||||
let c1 = ideal <= hole
|
||||
if start <= hole ? (c0 && c1) : (c0 || c1) {
|
||||
delegate.moveEntry(from: candidate, to: hole)
|
||||
hole = candidate
|
||||
}
|
||||
candidate = self.index(wrappedAfter: candidate)
|
||||
}
|
||||
|
||||
words[hole.word].uncheckedRemove(hole.bit)
|
||||
}
|
||||
}
|
||||
|
||||
extension _HashTable {
|
||||
/// Check for consistency and return the count of occupied entries.
|
||||
internal func _invariantCheck(with delegate: _HashTableDelegate) -> Int {
|
||||
#if INTERNAL_CHECKS_ENABLED
|
||||
_sanityCheck(bucketCount > 0 && bucketCount & (bucketCount &- 1) == 0,
|
||||
"Invalid bucketCount")
|
||||
_sanityCheck(_isValidAddress(UInt(bitPattern: words)),
|
||||
"Invalid words pointer")
|
||||
_sanityCheck(_isValidAddress(UInt(bitPattern: words + wordCount - 1)),
|
||||
"Invalid words buffer")
|
||||
|
||||
var occupiedCount = 0
|
||||
for i in self {
|
||||
occupiedCount += 1
|
||||
let hashValue = delegate.hashValue(at: i)
|
||||
var c = idealIndex(forHashValue: hashValue)
|
||||
// There must be no holes between the ideal and actual buckets for this
|
||||
// hash value.
|
||||
while c != i {
|
||||
_sanityCheck(_isOccupied(c),
|
||||
"Some hash table elements are stored outside their collision chain")
|
||||
c = index(wrappedAfter: c)
|
||||
}
|
||||
}
|
||||
return occupiedCount
|
||||
#else
|
||||
return 0
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import SwiftShims
|
||||
// rdar://problem/38549901
|
||||
@usableFromInline
|
||||
internal protocol _HasherCore {
|
||||
init(seed: (UInt64, UInt64))
|
||||
init(seed: Hasher._Seed)
|
||||
mutating func compress(_ value: UInt64)
|
||||
mutating func finalize(tailAndByteCount: UInt64) -> UInt64
|
||||
|
||||
@@ -33,7 +33,7 @@ internal protocol _HasherCore {
|
||||
/// This comes handy when type's _hash(into:) implementation needs to perform
|
||||
/// one-shot hashing for some of its components. (E.g., for commutative
|
||||
/// hashing.)
|
||||
func _generateSeed() -> (UInt64, UInt64)
|
||||
func _generateSeed() -> Hasher._Seed
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
@@ -160,7 +160,7 @@ internal struct _BufferingHasher<Core: _HasherCore> {
|
||||
private var _core: Core
|
||||
|
||||
@inline(__always)
|
||||
internal init(seed: (UInt64, UInt64)) {
|
||||
internal init(seed: Hasher._Seed) {
|
||||
self._buffer = _HasherTailBuffer()
|
||||
self._core = Core(seed: seed)
|
||||
}
|
||||
@@ -252,7 +252,7 @@ internal struct _BufferingHasher<Core: _HasherCore> {
|
||||
// Generate a seed value from the current state of this hasher.
|
||||
// FIXME(hasher): Remove
|
||||
@inline(__always)
|
||||
internal func _generateSeed() -> (UInt64, UInt64) {
|
||||
internal func _generateSeed() -> Hasher._Seed {
|
||||
return _core._generateSeed()
|
||||
}
|
||||
|
||||
@@ -296,6 +296,9 @@ public struct Hasher {
|
||||
@usableFromInline
|
||||
internal typealias Core = _BufferingHasher<RawCore>
|
||||
|
||||
@usableFromInline
|
||||
internal typealias _Seed = (UInt64, UInt64)
|
||||
|
||||
internal var _core: Core
|
||||
|
||||
/// Creates a new hasher.
|
||||
@@ -310,7 +313,7 @@ public struct Hasher {
|
||||
/// Initialize a new hasher using the specified seed value.
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
internal init(_seed seed: (UInt64, UInt64)) {
|
||||
internal init(_seed seed: _Seed) {
|
||||
self._core = Core(seed: seed)
|
||||
}
|
||||
|
||||
@@ -331,7 +334,7 @@ public struct Hasher {
|
||||
/// The 128-bit hash seed used to initialize the hasher state. Initialized
|
||||
/// once during process startup.
|
||||
@inlinable
|
||||
internal static var _seed: (UInt64, UInt64) {
|
||||
internal static var _seed: _Seed {
|
||||
@inline(__always)
|
||||
get {
|
||||
// The seed itself is defined in C++ code so that it is initialized during
|
||||
@@ -428,13 +431,13 @@ public struct Hasher {
|
||||
// FIXME(hasher): Remove
|
||||
@_effects(readnone)
|
||||
@usableFromInline
|
||||
internal func _generateSeed() -> (UInt64, UInt64) {
|
||||
internal func _generateSeed() -> Hasher._Seed {
|
||||
return _core._generateSeed()
|
||||
}
|
||||
|
||||
@_effects(readnone)
|
||||
@usableFromInline
|
||||
internal static func _hash(seed: (UInt64, UInt64), _ value: UInt64) -> Int {
|
||||
internal static func _hash(seed: _Seed, _ value: UInt64) -> Int {
|
||||
var core = RawCore(seed: seed)
|
||||
core.compress(value)
|
||||
let tbc = _HasherTailBuffer(tail: 0, byteCount: 8)
|
||||
@@ -443,7 +446,7 @@ public struct Hasher {
|
||||
|
||||
@_effects(readnone)
|
||||
@usableFromInline
|
||||
internal static func _hash(seed: (UInt64, UInt64), _ value: UInt) -> Int {
|
||||
internal static func _hash(seed: _Seed, _ value: UInt) -> Int {
|
||||
var core = RawCore(seed: seed)
|
||||
#if arch(i386) || arch(arm)
|
||||
_sanityCheck(UInt.bitWidth < UInt64.bitWidth)
|
||||
@@ -461,7 +464,7 @@ public struct Hasher {
|
||||
@_effects(readnone)
|
||||
@usableFromInline
|
||||
internal static func _hash(
|
||||
seed: (UInt64, UInt64),
|
||||
seed: _Seed,
|
||||
bytes value: UInt64,
|
||||
count: Int) -> Int {
|
||||
_sanityCheck(count >= 0 && count < 8)
|
||||
@@ -473,7 +476,7 @@ public struct Hasher {
|
||||
@_effects(readnone)
|
||||
@usableFromInline
|
||||
internal static func _hash(
|
||||
seed: (UInt64, UInt64),
|
||||
seed: _Seed,
|
||||
bytes: UnsafeRawBufferPointer) -> Int {
|
||||
var core = Core(seed: seed)
|
||||
core.combine(bytes: bytes)
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
// This file implements helpers for hashing collections.
|
||||
//
|
||||
|
||||
import SwiftShims
|
||||
|
||||
/// The inverse of the default hash table load factor. Factored out so that it
|
||||
/// can be used in multiple places in the implementation and stay consistent.
|
||||
/// Should not be used outside `Dictionary` implementation.
|
||||
@@ -27,8 +29,6 @@ internal var _hashContainerDefaultMaxLoadFactorInverse: Double {
|
||||
///
|
||||
/// This function is part of the runtime because `Bool` type is bridged to
|
||||
/// `ObjCBool`, which is in Foundation overlay.
|
||||
/// FIXME(sil-serialize-all): this should be internal
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
@_silgen_name("swift_stdlib_NSObject_isEqual")
|
||||
internal func _stdlib_NSObject_isEqual(_ lhs: AnyObject, _ rhs: AnyObject) -> Bool
|
||||
#endif
|
||||
@@ -67,3 +67,88 @@ internal struct _UnmanagedAnyObjectArray {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
/// An NSEnumerator implementation returning zero elements. This is useful when
|
||||
/// a concrete element type is not recoverable from the empty singleton.
|
||||
final internal class _SwiftEmptyNSEnumerator
|
||||
: __SwiftNativeNSEnumerator, _NSEnumerator {
|
||||
internal override required init() {}
|
||||
|
||||
@objc
|
||||
internal func nextObject() -> AnyObject? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>,
|
||||
count: Int
|
||||
) -> Int {
|
||||
// Even though we never do anything in here, we need to update the
|
||||
// state so that callers know we actually ran.
|
||||
var theState = state.pointee
|
||||
if theState.state == 0 {
|
||||
theState.state = 1 // Arbitrary non-zero value.
|
||||
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
|
||||
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
}
|
||||
state.pointee = theState
|
||||
return 0
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
/// This is a minimal class holding a single tail-allocated flat buffer,
|
||||
/// representing hash table storage for AnyObject elements. This is used to
|
||||
/// store bridged elements in deferred bridging scenarios.
|
||||
///
|
||||
/// Using a dedicated class for this rather than a _HeapBuffer makes it easy to
|
||||
/// recognize these in heap dumps etc.
|
||||
internal final class _BridgingHashBuffer
|
||||
: ManagedBuffer<_BridgingHashBuffer.Header, AnyObject> {
|
||||
struct Header {
|
||||
internal var owner: AnyObject
|
||||
internal var hashTable: _HashTable
|
||||
|
||||
init(owner: AnyObject, hashTable: _HashTable) {
|
||||
self.owner = owner
|
||||
self.hashTable = hashTable
|
||||
}
|
||||
}
|
||||
|
||||
internal static func allocate(
|
||||
owner: AnyObject,
|
||||
hashTable: _HashTable
|
||||
) -> _BridgingHashBuffer {
|
||||
let buffer = self.create(minimumCapacity: hashTable.bucketCount) { _ in
|
||||
Header(owner: owner, hashTable: hashTable)
|
||||
}
|
||||
return unsafeDowncast(buffer, to: _BridgingHashBuffer.self)
|
||||
}
|
||||
|
||||
deinit {
|
||||
for index in header.hashTable {
|
||||
(firstElementAddress + index.bucket).deinitialize(count: 1)
|
||||
}
|
||||
_fixLifetime(self)
|
||||
}
|
||||
|
||||
internal subscript(index: _HashTable.Index) -> AnyObject {
|
||||
@inline(__always) get {
|
||||
_sanityCheck(header.hashTable.isOccupied(index))
|
||||
defer { _fixLifetime(self) }
|
||||
return firstElementAddress[index.bucket]
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func initialize(at index: _HashTable.Index, to object: AnyObject) {
|
||||
_sanityCheck(header.hashTable.isOccupied(index))
|
||||
(firstElementAddress + index.bucket).initialize(to: object)
|
||||
_fixLifetime(self)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
601
stdlib/public/core/NativeDictionary.swift
Normal file
601
stdlib/public/core/NativeDictionary.swift
Normal file
@@ -0,0 +1,601 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// A wrapper around _RawDictionaryStorage that provides most of the
|
||||
/// implementation of Dictionary.
|
||||
@usableFromInline
|
||||
@_fixed_layout
|
||||
internal struct _NativeDictionary<Key: Hashable, Value> {
|
||||
@usableFromInline
|
||||
internal typealias Element = (key: Key, value: Value)
|
||||
|
||||
/// See this comments on _RawDictionaryStorage and its subclasses to
|
||||
/// understand why we store an untyped storage here.
|
||||
@usableFromInline
|
||||
internal var _storage: _RawDictionaryStorage
|
||||
|
||||
/// Constructs an instance from the empty singleton.
|
||||
@inlinable
|
||||
internal init() {
|
||||
self._storage = _RawDictionaryStorage.empty
|
||||
}
|
||||
|
||||
/// Constructs a dictionary adopting the given storage.
|
||||
@inlinable
|
||||
internal init(_ storage: _RawDictionaryStorage) {
|
||||
self._storage = storage
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
internal init(capacity: Int) {
|
||||
let scale = _HashTable.scale(forCapacity: capacity)
|
||||
self._storage = _DictionaryStorage<Key, Value>.allocate(scale: scale)
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@inlinable
|
||||
internal init(_ cocoa: _CocoaDictionary) {
|
||||
self.init(cocoa, capacity: cocoa.count)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal init(_ cocoa: _CocoaDictionary, capacity: Int) {
|
||||
_sanityCheck(cocoa.count <= capacity)
|
||||
self.init(capacity: capacity)
|
||||
for (key, value) in cocoa {
|
||||
insertNew(
|
||||
key: _forceBridgeFromObjectiveC(key, Key.self),
|
||||
value: _forceBridgeFromObjectiveC(value, Value.self))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extension _NativeDictionary { // Primitive fields
|
||||
@inlinable
|
||||
internal var capacity: Int {
|
||||
@inline(__always)
|
||||
get {
|
||||
return _assumeNonNegative(_storage._capacity)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var hashTable: _HashTable {
|
||||
@inline(__always) get {
|
||||
return _storage._hashTable
|
||||
}
|
||||
}
|
||||
|
||||
// This API is unsafe and needs a `_fixLifetime` in the caller.
|
||||
@inlinable
|
||||
internal var _keys: UnsafeMutablePointer<Key> {
|
||||
return _storage._rawKeys.assumingMemoryBound(to: Key.self)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var _values: UnsafeMutablePointer<Value> {
|
||||
return _storage._rawValues.assumingMemoryBound(to: Value.self)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeDictionary { // Low-level unchecked operations
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func uncheckedKey(at index: Index) -> Key {
|
||||
defer { _fixLifetime(self) }
|
||||
_sanityCheck(hashTable.isOccupied(index))
|
||||
return _keys[index.bucket]
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func uncheckedValue(at index: Index) -> Value {
|
||||
defer { _fixLifetime(self) }
|
||||
_sanityCheck(hashTable.isOccupied(index))
|
||||
return _values[index.bucket]
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
internal func uncheckedInitialize(
|
||||
at index: Index,
|
||||
toKey key: Key,
|
||||
value: Value) {
|
||||
defer { _fixLifetime(self) }
|
||||
_sanityCheck(hashTable.isValid(index))
|
||||
(_keys + index.bucket).initialize(to: key)
|
||||
(_values + index.bucket).initialize(to: value)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
internal func uncheckedDestroy(at index: Index) {
|
||||
defer { _fixLifetime(self) }
|
||||
_sanityCheck(hashTable.isOccupied(index))
|
||||
(_keys + index.bucket).deinitialize(count: 1)
|
||||
(_values + index.bucket).deinitialize(count: 1)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeDictionary { // Low-level lookup operations
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func hashValue(for key: Key) -> Int {
|
||||
return key._rawHashValue(seed: _storage._seed)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func find(_ key: Key) -> (index: Index, found: Bool) {
|
||||
return find(key, hashValue: self.hashValue(for: key))
|
||||
}
|
||||
|
||||
/// Search for a given element, assuming it has the specified hash value.
|
||||
///
|
||||
/// If the element is not present in this set, return the position where it
|
||||
/// could be inserted.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func find(
|
||||
_ key: Key,
|
||||
hashValue: Int
|
||||
) -> (index: Index, found: Bool) {
|
||||
let hashTable = self.hashTable
|
||||
var index = hashTable.idealIndex(forHashValue: hashValue)
|
||||
while hashTable._isOccupied(index) {
|
||||
if uncheckedKey(at: index) == key {
|
||||
return (index, true)
|
||||
}
|
||||
index = hashTable.index(wrappedAfter: index)
|
||||
}
|
||||
return (index, false)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeDictionary { // ensureUnique
|
||||
@inlinable
|
||||
internal mutating func resize(capacity: Int) {
|
||||
let capacity = Swift.max(capacity, self.capacity)
|
||||
let result = _NativeDictionary(
|
||||
_DictionaryStorage<Key, Value>.allocate(capacity: capacity))
|
||||
if count > 0 {
|
||||
for index in hashTable {
|
||||
let key = (_keys + index.bucket).move()
|
||||
let value = (_values + index.bucket).move()
|
||||
result._unsafeInsertNew(key: key, value: value)
|
||||
}
|
||||
// Clear out old storage, ensuring that its deinit won't overrelease the
|
||||
// elements we've just moved out.
|
||||
_storage._hashTable.clear()
|
||||
_storage._count = 0
|
||||
}
|
||||
_storage = result._storage
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func copy(capacity: Int) -> Bool {
|
||||
let capacity = Swift.max(capacity, self.capacity)
|
||||
let (newStorage, rehash) = _DictionaryStorage<Key, Value>.reallocate(
|
||||
original: _storage,
|
||||
capacity: capacity)
|
||||
let result = _NativeDictionary(newStorage)
|
||||
if count > 0 {
|
||||
if rehash {
|
||||
for index in hashTable {
|
||||
result._unsafeInsertNew(
|
||||
key: self.uncheckedKey(at: index),
|
||||
value: self.uncheckedValue(at: index))
|
||||
}
|
||||
} else {
|
||||
result.hashTable.copyContents(of: hashTable)
|
||||
result._storage._count = self.count
|
||||
for index in hashTable {
|
||||
let key = uncheckedKey(at: index)
|
||||
let value = uncheckedValue(at: index)
|
||||
result.uncheckedInitialize(at: index, toKey: key, value: value)
|
||||
}
|
||||
}
|
||||
}
|
||||
_storage = result._storage
|
||||
return rehash
|
||||
}
|
||||
|
||||
/// Ensure storage of self is uniquely held and can hold at least `capacity`
|
||||
/// elements. Returns true iff contents were rehashed.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func ensureUnique(isUnique: Bool, capacity: Int) -> Bool {
|
||||
if _fastPath(capacity <= self.capacity && isUnique) {
|
||||
return false
|
||||
}
|
||||
guard isUnique else {
|
||||
return copy(capacity: capacity)
|
||||
}
|
||||
resize(capacity: capacity)
|
||||
return true
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func reserveCapacity(_ capacity: Int, isUnique: Bool) {
|
||||
_ = ensureUnique(isUnique: isUnique, capacity: capacity)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeDictionary: _DictionaryBuffer {
|
||||
@usableFromInline
|
||||
internal typealias Index = _HashTable.Index
|
||||
|
||||
@inlinable
|
||||
internal var startIndex: Index {
|
||||
return hashTable.startIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var endIndex: Index {
|
||||
return hashTable.endIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func index(after index: Index) -> Index {
|
||||
return hashTable.index(after: index)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func index(forKey key: Key) -> Index? {
|
||||
if count == 0 {
|
||||
// Fast path that avoids computing the hash of the key.
|
||||
return nil
|
||||
}
|
||||
let (index, found) = find(key)
|
||||
return found ? index : nil
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var count: Int {
|
||||
@inline(__always) get {
|
||||
return _assumeNonNegative(_storage._count)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func contains(_ key: Key) -> Bool {
|
||||
return find(key).found
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func lookup(_ key: Key) -> Value? {
|
||||
if count == 0 {
|
||||
// Fast path that avoids computing the hash of the key.
|
||||
return nil
|
||||
}
|
||||
let (index, found) = self.find(key)
|
||||
return found ? self.uncheckedValue(at: index) : nil
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func lookup(_ index: Index) -> (key: Key, value: Value) {
|
||||
_precondition(hashTable.isOccupied(index),
|
||||
"Attempting to access Dictionary elements using an invalid Index")
|
||||
let key = self.uncheckedKey(at: index)
|
||||
let value = self.uncheckedValue(at: index)
|
||||
return (key, value)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func key(at index: Index) -> Key {
|
||||
_precondition(hashTable.isOccupied(index),
|
||||
"Attempting to access Dictionary elements using an invalid Index")
|
||||
return self.uncheckedKey(at: index)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
func value(at index: Index) -> Value {
|
||||
_precondition(hashTable.isOccupied(index),
|
||||
"Attempting to access Dictionary elements using an invalid Index")
|
||||
return self.uncheckedValue(at: index)
|
||||
}
|
||||
}
|
||||
|
||||
// This function has a highly visible name to make it stand out in stack traces.
|
||||
@usableFromInline
|
||||
@inline(never)
|
||||
internal func KEY_TYPE_OF_DICTIONARY_VIOLATES_HASHABLE_REQUIREMENTS(
|
||||
_ keyType: Any.Type
|
||||
) -> Never {
|
||||
_assertionFailure(
|
||||
"Fatal error",
|
||||
"""
|
||||
Duplicate keys of type '\(keyType)' were found in a Dictionary.
|
||||
This usually means either that the type violates Hashable's requirements, or
|
||||
that members of such a dictionary were mutated after insertion.
|
||||
""",
|
||||
flags: _fatalErrorFlags())
|
||||
}
|
||||
|
||||
extension _NativeDictionary { // Insertions
|
||||
/// Insert a new element into uniquely held storage.
|
||||
/// Storage must be uniquely referenced with adequate capacity.
|
||||
/// The `key` must not be already present in the Dictionary.
|
||||
@inlinable
|
||||
internal func _unsafeInsertNew(key: Key, value: Value) {
|
||||
_sanityCheck(count + 1 <= capacity)
|
||||
let hashValue = self.hashValue(for: key)
|
||||
if _isDebugAssertConfiguration() {
|
||||
// In debug builds, perform a full lookup and trap if we detect duplicate
|
||||
// elements -- these imply that the Element type violates Hashable
|
||||
// requirements. This is generally more costly than a direct insertion,
|
||||
// because we'll need to compare elements in case of hash collisions.
|
||||
let (index, found) = find(key, hashValue: hashValue)
|
||||
guard !found else {
|
||||
KEY_TYPE_OF_DICTIONARY_VIOLATES_HASHABLE_REQUIREMENTS(Key.self)
|
||||
}
|
||||
hashTable.insert(index)
|
||||
uncheckedInitialize(at: index, toKey: key, value: value)
|
||||
} else {
|
||||
let index = hashTable.insertNew(hashValue: hashValue)
|
||||
uncheckedInitialize(at: index, toKey: key, value: value)
|
||||
}
|
||||
_storage._count += 1
|
||||
}
|
||||
|
||||
/// Insert a new entry into uniquely held storage.
|
||||
/// Storage must be uniquely referenced.
|
||||
/// The `key` must not be already present in the Dictionary.
|
||||
@inlinable
|
||||
internal mutating func insertNew(key: Key, value: Value) {
|
||||
_ = ensureUnique(isUnique: true, capacity: count + 1)
|
||||
_unsafeInsertNew(key: key, value: value)
|
||||
}
|
||||
|
||||
/// Same as find(_:), except assume a corresponding key/value pair will be
|
||||
/// inserted if it doesn't already exist, and mutated if it does exist. When
|
||||
/// this function returns, the storage is guaranteed to be native, uniquely
|
||||
/// held, and with enough capacity for a single insertion (if the key isn't
|
||||
/// already in the dictionary.)
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func mutatingFind(
|
||||
_ key: Key,
|
||||
isUnique: Bool
|
||||
) -> (index: Index, found: Bool) {
|
||||
let (index, found) = find(key)
|
||||
|
||||
// Prepare storage.
|
||||
// If `key` isn't in the dictionary yet, assume that this access will end
|
||||
// up inserting it. (If we guess wrong, we might needlessly expand
|
||||
// storage; that's fine.) Otherwise this can only be a removal or an
|
||||
// in-place mutation.
|
||||
let rehashed = ensureUnique(
|
||||
isUnique: isUnique,
|
||||
capacity: count + (found ? 0 : 1))
|
||||
guard rehashed else { return (index, found) }
|
||||
let (i, f) = find(key)
|
||||
if f != found {
|
||||
KEY_TYPE_OF_DICTIONARY_VIOLATES_HASHABLE_REQUIREMENTS(Key.self)
|
||||
}
|
||||
return (i, found)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func _insert(at index: Index, key: Key, value: Value) {
|
||||
_sanityCheck(count < capacity)
|
||||
hashTable.insert(index)
|
||||
uncheckedInitialize(at: index, toKey: key, value: value)
|
||||
_storage._count += 1
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func updateValue(
|
||||
_ value: Value,
|
||||
forKey key: Key,
|
||||
isUnique: Bool
|
||||
) -> Value? {
|
||||
let (index, found) = mutatingFind(key, isUnique: isUnique)
|
||||
if found {
|
||||
let oldValue = (_values + index.bucket).move()
|
||||
(_values + index.bucket).initialize(to: value)
|
||||
// FIXME: Replacing the old key with the new is unnecessary, unintuitive,
|
||||
// and actively harmful to some usecases. We shouldn't do it.
|
||||
// rdar://problem/32144087
|
||||
(_keys + index.bucket).pointee = key
|
||||
return oldValue
|
||||
}
|
||||
_insert(at: index, key: key, value: value)
|
||||
return nil
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func setValue(
|
||||
_ value: Value,
|
||||
forKey key: Key,
|
||||
isUnique: Bool
|
||||
) {
|
||||
let (index, found) = mutatingFind(key, isUnique: isUnique)
|
||||
if found {
|
||||
(_values + index.bucket).pointee = value
|
||||
// FIXME: Replacing the old key with the new is unnecessary, unintuitive,
|
||||
// and actively harmful to some usecases. We shouldn't do it.
|
||||
// rdar://problem/32144087
|
||||
(_keys + index.bucket).pointee = key
|
||||
} else {
|
||||
_insert(at: index, key: key, value: value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeDictionary: _HashTableDelegate {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func hashValue(at index: Index) -> Int {
|
||||
return hashValue(for: uncheckedKey(at: index))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func moveEntry(from source: Index, to target: Index) {
|
||||
(_keys + target.bucket)
|
||||
.moveInitialize(from: _keys + source.bucket, count: 1)
|
||||
(_values + target.bucket)
|
||||
.moveInitialize(from: _values + source.bucket, count: 1)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeDictionary { // Deletion
|
||||
@inlinable
|
||||
internal func _delete(at index: Index) {
|
||||
hashTable.delete(at: index, with: self)
|
||||
_storage._count -= 1
|
||||
_sanityCheck(_storage._count >= 0)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func uncheckedRemove(
|
||||
at index: Index,
|
||||
isUnique: Bool
|
||||
) -> Element {
|
||||
_sanityCheck(hashTable.isOccupied(index))
|
||||
let rehashed = ensureUnique(isUnique: isUnique, capacity: capacity)
|
||||
_sanityCheck(!rehashed)
|
||||
let oldKey = (_keys + index.bucket).move()
|
||||
let oldValue = (_values + index.bucket).move()
|
||||
_delete(at: index)
|
||||
return (oldKey, oldValue)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func remove(at index: Index, isUnique: Bool) -> Element {
|
||||
_precondition(hashTable.isOccupied(index), "Invalid index")
|
||||
return uncheckedRemove(at: index, isUnique: isUnique)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
internal mutating func removeAll(isUnique: Bool) {
|
||||
guard isUnique else {
|
||||
let scale = self._storage._scale
|
||||
_storage = _DictionaryStorage<Key, Value>.allocate(scale: scale)
|
||||
return
|
||||
}
|
||||
for index in hashTable {
|
||||
(_keys + index.bucket).deinitialize(count: 1)
|
||||
(_values + index.bucket).deinitialize(count: 1)
|
||||
}
|
||||
hashTable.clear()
|
||||
_storage._count = 0
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeDictionary { // High-level operations
|
||||
@inlinable
|
||||
internal func mapValues<T>(
|
||||
_ transform: (Value) throws -> T
|
||||
) rethrows -> _NativeDictionary<Key, T> {
|
||||
let result = _NativeDictionary<Key, T>(capacity: capacity)
|
||||
// Because the keys in the current and new buffer are the same, we can
|
||||
// initialize to the same locations in the new buffer, skipping hash value
|
||||
// recalculations.
|
||||
for index in hashTable {
|
||||
let key = self.uncheckedKey(at: index)
|
||||
let value = self.uncheckedValue(at: index)
|
||||
try result._insert(at: index, key: key, value: transform(value))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func merge<S: Sequence>(
|
||||
_ keysAndValues: S,
|
||||
isUnique: Bool,
|
||||
uniquingKeysWith combine: (Value, Value) throws -> Value
|
||||
) rethrows where S.Element == (Key, Value) {
|
||||
var isUnique = isUnique
|
||||
for (key, value) in keysAndValues {
|
||||
let (index, found) = mutatingFind(key, isUnique: isUnique)
|
||||
isUnique = true
|
||||
if found {
|
||||
do {
|
||||
let v = (_values + index.bucket).move()
|
||||
let newValue = try combine(v, value)
|
||||
(_values + index.bucket).initialize(to: newValue)
|
||||
} catch _MergeError.keyCollision {
|
||||
fatalError("Duplicate values for key: '\(key)'")
|
||||
}
|
||||
} else {
|
||||
_insert(at: index, key: key, value: value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal init<S: Sequence>(
|
||||
grouping values: S,
|
||||
by keyForValue: (S.Element) throws -> Key
|
||||
) rethrows where Value == [S.Element] {
|
||||
self.init()
|
||||
for value in values {
|
||||
let key = try keyForValue(value)
|
||||
let (index, found) = mutatingFind(key, isUnique: true)
|
||||
if found {
|
||||
_values[index.bucket].append(value)
|
||||
} else {
|
||||
_insert(at: index, key: key, value: [value])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeDictionary: Sequence {
|
||||
@usableFromInline
|
||||
@_fixed_layout
|
||||
internal struct Iterator {
|
||||
// The iterator is iterating over a frozen view of the collection state, so
|
||||
// it keeps its own reference to the dictionary.
|
||||
@usableFromInline
|
||||
internal let base: _NativeDictionary
|
||||
@usableFromInline
|
||||
internal var iterator: _HashTable.Iterator
|
||||
|
||||
@inlinable
|
||||
init(_ base: _NativeDictionary) {
|
||||
self.base = base
|
||||
self.iterator = base.hashTable.makeIterator()
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func makeIterator() -> Iterator {
|
||||
return Iterator(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeDictionary.Iterator: IteratorProtocol {
|
||||
@usableFromInline
|
||||
internal typealias Element = (key: Key, value: Value)
|
||||
|
||||
@inlinable
|
||||
internal mutating func next() -> Element? {
|
||||
guard let index = iterator.next() else { return nil }
|
||||
let key = base.uncheckedKey(at: index)
|
||||
let value = base.uncheckedValue(at: index)
|
||||
return (key, value)
|
||||
}
|
||||
}
|
||||
|
||||
443
stdlib/public/core/NativeSet.swift
Normal file
443
stdlib/public/core/NativeSet.swift
Normal file
@@ -0,0 +1,443 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// A wrapper around _RawSetStorage that provides most of the
|
||||
/// implementation of Set.
|
||||
@usableFromInline
|
||||
@_fixed_layout
|
||||
internal struct _NativeSet<Element: Hashable> {
|
||||
/// See the comments on _RawSetStorage and its subclasses to understand why we
|
||||
/// store an untyped storage here.
|
||||
@usableFromInline
|
||||
internal var _storage: _RawSetStorage
|
||||
|
||||
/// Constructs an instance from the empty singleton.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal init() {
|
||||
self._storage = _RawSetStorage.empty
|
||||
}
|
||||
|
||||
/// Constructs a native set adopting the given storage.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal init(_ storage: _RawSetStorage) {
|
||||
self._storage = storage
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
internal init(capacity: Int) {
|
||||
let scale = _HashTable.scale(forCapacity: capacity)
|
||||
self._storage = _SetStorage<Element>.allocate(scale: scale)
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@inlinable
|
||||
internal init(_ cocoa: _CocoaSet) {
|
||||
self.init(cocoa, capacity: cocoa.count)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal init(_ cocoa: _CocoaSet, capacity: Int) {
|
||||
_sanityCheck(cocoa.count <= capacity)
|
||||
self.init(capacity: capacity)
|
||||
for element in cocoa {
|
||||
let nativeElement = _forceBridgeFromObjectiveC(element, Element.self)
|
||||
insertNew(nativeElement, isUnique: true)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extension _NativeSet { // Primitive fields
|
||||
@inlinable
|
||||
internal var capacity: Int {
|
||||
@inline(__always)
|
||||
get {
|
||||
return _assumeNonNegative(_storage._capacity)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var hashTable: _HashTable {
|
||||
@inline(__always) get {
|
||||
return _storage._hashTable
|
||||
}
|
||||
}
|
||||
|
||||
// This API is unsafe and needs a `_fixLifetime` in the caller.
|
||||
@inlinable
|
||||
internal var _elements: UnsafeMutablePointer<Element> {
|
||||
return _storage._rawElements.assumingMemoryBound(to: Element.self)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeSet { // Low-level unchecked operations
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func uncheckedElement(at index: Index) -> Element {
|
||||
defer { _fixLifetime(self) }
|
||||
_sanityCheck(hashTable.isOccupied(index))
|
||||
return _elements[index.bucket]
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func uncheckedInitialize(at index: Index, to element: Element) {
|
||||
_sanityCheck(hashTable.isValid(index))
|
||||
(_elements + index.bucket).initialize(to: element)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeSet { // Low-level lookup operations
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func hashValue(for element: Element) -> Int {
|
||||
return element._rawHashValue(seed: _storage._seed)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func find(_ element: Element) -> (index: Index, found: Bool) {
|
||||
return find(element, hashValue: self.hashValue(for: element))
|
||||
}
|
||||
|
||||
/// Search for a given element, assuming it has the specified hash value.
|
||||
///
|
||||
/// If the element is not present in this set, return the position where it
|
||||
/// could be inserted.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func find(
|
||||
_ element: Element,
|
||||
hashValue: Int
|
||||
) -> (index: Index, found: Bool) {
|
||||
let hashTable = self.hashTable
|
||||
var index = hashTable.idealIndex(forHashValue: hashValue)
|
||||
while hashTable._isOccupied(index) {
|
||||
if uncheckedElement(at: index) == element {
|
||||
return (index, true)
|
||||
}
|
||||
index = hashTable.index(wrappedAfter: index)
|
||||
}
|
||||
return (index, false)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeSet { // ensureUnique
|
||||
@inlinable
|
||||
internal mutating func resize(capacity: Int) {
|
||||
let capacity = Swift.max(capacity, self.capacity)
|
||||
let result = _NativeSet(_SetStorage<Element>.allocate(capacity: capacity))
|
||||
if count > 0 {
|
||||
for index in hashTable {
|
||||
let element = (self._elements + index.bucket).move()
|
||||
result._unsafeInsertNew(element)
|
||||
}
|
||||
// Clear out old storage, ensuring that its deinit won't overrelease the
|
||||
// elements we've just moved out.
|
||||
_storage._hashTable.clear()
|
||||
_storage._count = 0
|
||||
}
|
||||
_storage = result._storage
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func copy(capacity: Int) -> Bool {
|
||||
let capacity = Swift.max(capacity, self.capacity)
|
||||
let (newStorage, rehash) = _SetStorage<Element>.reallocate(
|
||||
original: _storage,
|
||||
capacity: capacity)
|
||||
let result = _NativeSet(newStorage)
|
||||
if count > 0 {
|
||||
if rehash {
|
||||
for index in hashTable {
|
||||
result._unsafeInsertNew(self.uncheckedElement(at: index))
|
||||
}
|
||||
} else {
|
||||
result.hashTable.copyContents(of: hashTable)
|
||||
result._storage._count = self.count
|
||||
for index in hashTable {
|
||||
let element = uncheckedElement(at: index)
|
||||
result.uncheckedInitialize(at: index, to: element)
|
||||
}
|
||||
}
|
||||
}
|
||||
_storage = result._storage
|
||||
return rehash
|
||||
}
|
||||
|
||||
/// Ensure storage of self is uniquely held and can hold at least `capacity`
|
||||
/// elements. Returns true iff contents were rehashed.
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func ensureUnique(isUnique: Bool, capacity: Int) -> Bool {
|
||||
if _fastPath(capacity <= self.capacity && isUnique) {
|
||||
return false
|
||||
}
|
||||
guard isUnique else {
|
||||
return copy(capacity: capacity)
|
||||
}
|
||||
resize(capacity: capacity)
|
||||
return true
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func reserveCapacity(_ capacity: Int, isUnique: Bool) {
|
||||
_ = ensureUnique(isUnique: isUnique, capacity: capacity)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeSet: _SetBuffer {
|
||||
@usableFromInline
|
||||
internal typealias Index = _HashTable.Index
|
||||
|
||||
@inlinable
|
||||
internal var startIndex: Index {
|
||||
return hashTable.startIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var endIndex: Index {
|
||||
return hashTable.endIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func index(after index: Index) -> Index {
|
||||
return hashTable.index(after: index)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func index(for element: Element) -> Index? {
|
||||
if count == 0 {
|
||||
// Fast path that avoids computing the hash of the key.
|
||||
return nil
|
||||
}
|
||||
let (index, found) = find(element)
|
||||
return found ? index : nil
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var count: Int {
|
||||
@inline(__always) get {
|
||||
return _assumeNonNegative(_storage._count)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func contains(_ member: Element) -> Bool {
|
||||
// Fast path: Don't calculate the hash if the set has no elements.
|
||||
if count == 0 { return false }
|
||||
return find(member).found
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func element(at index: Index) -> Element {
|
||||
hashTable.checkOccupied(index)
|
||||
return _elements[index.bucket]
|
||||
}
|
||||
}
|
||||
|
||||
// This function has a highly visible name to make it stand out in stack traces.
|
||||
@usableFromInline
|
||||
@inline(never)
|
||||
internal func ELEMENT_TYPE_OF_SET_VIOLATES_HASHABLE_REQUIREMENTS(
|
||||
_ elementType: Any.Type
|
||||
) -> Never {
|
||||
_assertionFailure(
|
||||
"Fatal error",
|
||||
"""
|
||||
Duplicate elements of type '\(elementType)' were found in a Set.
|
||||
This usually means either that the type violates Hashable's requirements, or
|
||||
that members of such a set were mutated after insertion.
|
||||
""",
|
||||
flags: _fatalErrorFlags())
|
||||
}
|
||||
|
||||
extension _NativeSet { // Insertions
|
||||
/// Insert a new element into uniquely held storage.
|
||||
/// Storage must be uniquely referenced with adequate capacity.
|
||||
/// The `element` must not be already present in the Set.
|
||||
@inlinable
|
||||
internal func _unsafeInsertNew(_ element: Element) {
|
||||
_sanityCheck(count + 1 <= capacity)
|
||||
let hashValue = self.hashValue(for: element)
|
||||
if _isDebugAssertConfiguration() {
|
||||
// In debug builds, perform a full lookup and trap if we detect duplicate
|
||||
// elements -- these imply that the Element type violates Hashable
|
||||
// requirements. This is generally more costly than a direct insertion,
|
||||
// because we'll need to compare elements in case of hash collisions.
|
||||
let (index, found) = find(element, hashValue: hashValue)
|
||||
guard !found else {
|
||||
ELEMENT_TYPE_OF_SET_VIOLATES_HASHABLE_REQUIREMENTS(Element.self)
|
||||
}
|
||||
hashTable.insert(index)
|
||||
uncheckedInitialize(at: index, to: element)
|
||||
} else {
|
||||
let index = hashTable.insertNew(hashValue: hashValue)
|
||||
uncheckedInitialize(at: index, to: element)
|
||||
}
|
||||
_storage._count += 1
|
||||
}
|
||||
|
||||
/// Insert a new element into uniquely held storage.
|
||||
/// Storage must be uniquely referenced.
|
||||
/// The `element` must not be already present in the Set.
|
||||
@inlinable
|
||||
internal mutating func insertNew(_ element: Element, isUnique: Bool) {
|
||||
_ = ensureUnique(isUnique: isUnique, capacity: count + 1)
|
||||
_unsafeInsertNew(element)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func _unsafeInsertNew(_ element: Element, at index: Index) {
|
||||
hashTable.insert(index)
|
||||
uncheckedInitialize(at: index, to: element)
|
||||
_storage._count += 1
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func insertNew(
|
||||
_ element: Element,
|
||||
at index: Index,
|
||||
isUnique: Bool
|
||||
) {
|
||||
_sanityCheck(!hashTable.isOccupied(index))
|
||||
var index = index
|
||||
if ensureUnique(isUnique: isUnique, capacity: count + 1) {
|
||||
let (i, f) = find(element)
|
||||
if f {
|
||||
ELEMENT_TYPE_OF_SET_VIOLATES_HASHABLE_REQUIREMENTS(Element.self)
|
||||
}
|
||||
index = i
|
||||
}
|
||||
_unsafeInsertNew(element, at: index)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func update(
|
||||
with element: Element,
|
||||
isUnique: Bool
|
||||
) -> Element? {
|
||||
var (index, found) = find(element)
|
||||
let rehashed = ensureUnique(
|
||||
isUnique: isUnique,
|
||||
capacity: count + (found ? 0 : 1))
|
||||
if rehashed {
|
||||
let (i, f) = find(element)
|
||||
if f != found {
|
||||
ELEMENT_TYPE_OF_SET_VIOLATES_HASHABLE_REQUIREMENTS(Element.self)
|
||||
}
|
||||
index = i
|
||||
}
|
||||
if found {
|
||||
let old = (_elements + index.bucket).move()
|
||||
uncheckedInitialize(at: index, to: element)
|
||||
return old
|
||||
}
|
||||
_unsafeInsertNew(element, at: index)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeSet: _HashTableDelegate {
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func hashValue(at index: Index) -> Int {
|
||||
return hashValue(for: uncheckedElement(at: index))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func moveEntry(from source: Index, to target: Index) {
|
||||
(_elements + target.bucket)
|
||||
.moveInitialize(from: _elements + source.bucket, count: 1)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeSet { // Deletion
|
||||
@inlinable
|
||||
internal mutating func _delete(at index: Index) {
|
||||
hashTable.delete(at: index, with: self)
|
||||
_storage._count -= 1
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func uncheckedRemove(
|
||||
at index: Index,
|
||||
isUnique: Bool) -> Element {
|
||||
_sanityCheck(hashTable.isOccupied(index))
|
||||
let rehashed = ensureUnique(isUnique: isUnique, capacity: capacity)
|
||||
_sanityCheck(!rehashed)
|
||||
let old = (_elements + index.bucket).move()
|
||||
_delete(at: index)
|
||||
return old
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal mutating func remove(at index: Index, isUnique: Bool) -> Element {
|
||||
_precondition(hashTable.isOccupied(index), "Invalid index")
|
||||
return uncheckedRemove(at: index, isUnique: isUnique)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
internal mutating func removeAll(isUnique: Bool) {
|
||||
guard isUnique else {
|
||||
let scale = self._storage._scale
|
||||
_storage = _SetStorage<Element>.allocate(scale: scale)
|
||||
return
|
||||
}
|
||||
for index in hashTable {
|
||||
(_elements + index.bucket).deinitialize(count: 1)
|
||||
}
|
||||
hashTable.clear()
|
||||
_storage._count = 0
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeSet: Sequence {
|
||||
@usableFromInline
|
||||
@_fixed_layout
|
||||
internal struct Iterator {
|
||||
// The iterator is iterating over a frozen view of the collection state, so
|
||||
// it keeps its own reference to the set.
|
||||
@usableFromInline
|
||||
internal let base: _NativeSet
|
||||
@usableFromInline
|
||||
internal var iterator: _HashTable.Iterator
|
||||
|
||||
@inlinable
|
||||
init(_ base: _NativeSet) {
|
||||
self.base = base
|
||||
self.iterator = base.hashTable.makeIterator()
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
__consuming internal func makeIterator() -> Iterator {
|
||||
return Iterator(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension _NativeSet.Iterator: IteratorProtocol {
|
||||
@inlinable
|
||||
internal mutating func next() -> Element? {
|
||||
guard let index = iterator.next() else { return nil }
|
||||
return base.uncheckedElement(at: index)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
582
stdlib/public/core/SetBridging.swift
Normal file
582
stdlib/public/core/SetBridging.swift
Normal file
@@ -0,0 +1,582 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
|
||||
import SwiftShims
|
||||
|
||||
@_silgen_name("swift_stdlib_CFSetGetValues")
|
||||
@usableFromInline
|
||||
internal
|
||||
func _stdlib_CFSetGetValues(_ nss: _NSSet, _: UnsafeMutablePointer<AnyObject>)
|
||||
|
||||
/// Equivalent to `NSSet.allObjects`, but does not leave objects on the
|
||||
/// autorelease pool.
|
||||
@inlinable
|
||||
internal func _stdlib_NSSet_allObjects(
|
||||
_ nss: _NSSet
|
||||
) -> _HeapBuffer<Int, AnyObject> {
|
||||
let count = nss.count
|
||||
let storage = _HeapBuffer<Int, AnyObject>(
|
||||
_HeapBufferStorage<Int, AnyObject>.self, count, count)
|
||||
_stdlib_CFSetGetValues(nss, storage.baseAddress)
|
||||
return storage
|
||||
}
|
||||
|
||||
extension _NativeSet { // Bridging
|
||||
@usableFromInline
|
||||
internal func bridged() -> _NSSet {
|
||||
// We can zero-cost bridge if our keys are verbatim
|
||||
// or if we're the empty singleton.
|
||||
|
||||
// Temporary var for SOME type safety before a cast.
|
||||
let nsSet: _NSSetCore
|
||||
|
||||
if _storage === _RawSetStorage.empty || count == 0 {
|
||||
nsSet = _RawSetStorage.empty
|
||||
} else if _isBridgedVerbatimToObjectiveC(Element.self) {
|
||||
nsSet = unsafeDowncast(_storage, to: _SetStorage<Element>.self)
|
||||
} else {
|
||||
nsSet = _SwiftDeferredNSSet(self)
|
||||
}
|
||||
|
||||
// Cast from "minimal NSSet" to "NSSet"
|
||||
// Note that if you actually ask Swift for this cast, it will fail.
|
||||
// Never trust a shadow protocol!
|
||||
return unsafeBitCast(nsSet, to: _NSSet.self)
|
||||
}
|
||||
}
|
||||
|
||||
/// An NSEnumerator that works with any _NativeSet of verbatim bridgeable
|
||||
/// elements. Used by the various NSSet impls.
|
||||
final internal class _SwiftSetNSEnumerator<Element: Hashable>
|
||||
: __SwiftNativeNSEnumerator, _NSEnumerator {
|
||||
|
||||
@nonobjc internal var base: _NativeSet<Element>
|
||||
@nonobjc internal var bridgedElements: _BridgingHashBuffer?
|
||||
@nonobjc internal var nextIndex: _NativeSet<Element>.Index
|
||||
@nonobjc internal var endIndex: _NativeSet<Element>.Index
|
||||
|
||||
@objc
|
||||
internal override required init() {
|
||||
_sanityCheckFailure("don't call this designated initializer")
|
||||
}
|
||||
|
||||
internal init(_ base: _NativeSet<Element>) {
|
||||
_sanityCheck(_isBridgedVerbatimToObjectiveC(Element.self))
|
||||
self.base = base
|
||||
self.bridgedElements = nil
|
||||
self.nextIndex = base.startIndex
|
||||
self.endIndex = base.endIndex
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal init(_ deferred: _SwiftDeferredNSSet<Element>) {
|
||||
_sanityCheck(!_isBridgedVerbatimToObjectiveC(Element.self))
|
||||
self.base = deferred.native
|
||||
self.bridgedElements = deferred.bridgeElements()
|
||||
self.nextIndex = base.startIndex
|
||||
self.endIndex = base.endIndex
|
||||
}
|
||||
|
||||
private func bridgedElement(at index: _HashTable.Index) -> AnyObject {
|
||||
_sanityCheck(base.hashTable.isOccupied(index))
|
||||
if let bridgedElements = self.bridgedElements {
|
||||
return bridgedElements[index]
|
||||
}
|
||||
return _bridgeAnythingToObjectiveC(base.element(at: index))
|
||||
}
|
||||
|
||||
//
|
||||
// NSEnumerator implementation.
|
||||
//
|
||||
// Do not call any of these methods from the standard library!
|
||||
//
|
||||
|
||||
@objc
|
||||
internal func nextObject() -> AnyObject? {
|
||||
if nextIndex == endIndex {
|
||||
return nil
|
||||
}
|
||||
let index = nextIndex
|
||||
nextIndex = base.index(after: nextIndex)
|
||||
return self.bridgedElement(at: index)
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>,
|
||||
count: Int
|
||||
) -> Int {
|
||||
var theState = state.pointee
|
||||
if theState.state == 0 {
|
||||
theState.state = 1 // Arbitrary non-zero value.
|
||||
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
|
||||
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
}
|
||||
|
||||
if nextIndex == endIndex {
|
||||
state.pointee = theState
|
||||
return 0
|
||||
}
|
||||
|
||||
// Return only a single element so that code can start iterating via fast
|
||||
// enumeration, terminate it, and continue via NSEnumerator.
|
||||
let unmanagedObjects = _UnmanagedAnyObjectArray(objects)
|
||||
unmanagedObjects[0] = self.bridgedElement(at: nextIndex)
|
||||
nextIndex = base.index(after: nextIndex)
|
||||
state.pointee = theState
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
/// This class exists for Objective-C bridging. It holds a reference to a
|
||||
/// _NativeSet, and can be upcast to NSSelf when bridging is necessary. This is
|
||||
/// the fallback implementation for situations where toll-free bridging isn't
|
||||
/// possible. On first access, a _NativeSet of AnyObject will be constructed
|
||||
/// containing all the bridged elements.
|
||||
final internal class _SwiftDeferredNSSet<Element: Hashable>
|
||||
: __SwiftNativeNSSet, _NSSetCore {
|
||||
|
||||
// This stored property must be stored at offset zero. We perform atomic
|
||||
// operations on it.
|
||||
//
|
||||
// Do not access this property directly.
|
||||
@nonobjc
|
||||
private var _bridgedElements_DoNotUse: AnyObject?
|
||||
|
||||
/// The unbridged elements.
|
||||
internal var native: _NativeSet<Element>
|
||||
|
||||
internal init(_ native: _NativeSet<Element>) {
|
||||
_sanityCheck(native.count > 0)
|
||||
_sanityCheck(!_isBridgedVerbatimToObjectiveC(Element.self))
|
||||
self.native = native
|
||||
super.init()
|
||||
}
|
||||
|
||||
/// Returns the pointer to the stored property, which contains bridged
|
||||
/// Set elements.
|
||||
@nonobjc
|
||||
private var _bridgedElementsPtr: UnsafeMutablePointer<AnyObject?> {
|
||||
return _getUnsafePointerToStoredProperties(self)
|
||||
.assumingMemoryBound(to: Optional<AnyObject>.self)
|
||||
}
|
||||
|
||||
/// The buffer for bridged Set elements, if present.
|
||||
@nonobjc
|
||||
private var _bridgedElements: _BridgingHashBuffer? {
|
||||
guard let ref = _stdlib_atomicLoadARCRef(object: _bridgedElementsPtr) else {
|
||||
return nil
|
||||
}
|
||||
return unsafeDowncast(ref, to: _BridgingHashBuffer.self)
|
||||
}
|
||||
|
||||
/// Attach a buffer for bridged Set elements.
|
||||
@nonobjc
|
||||
private func _initializeBridgedElements(_ storage: _BridgingHashBuffer) {
|
||||
_stdlib_atomicInitializeARCRef(
|
||||
object: _bridgedElementsPtr,
|
||||
desired: storage)
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func bridgeElements() -> _BridgingHashBuffer {
|
||||
if let bridgedElements = _bridgedElements { return bridgedElements }
|
||||
|
||||
// Allocate and initialize heap storage for bridged objects.
|
||||
let bridged = _BridgingHashBuffer.allocate(
|
||||
owner: native._storage,
|
||||
hashTable: native.hashTable)
|
||||
for index in native.hashTable {
|
||||
let object = _bridgeAnythingToObjectiveC(native.element(at: index))
|
||||
bridged.initialize(at: index, to: object)
|
||||
}
|
||||
|
||||
// Atomically put the bridged elements in place.
|
||||
_initializeBridgedElements(bridged)
|
||||
return _bridgedElements!
|
||||
}
|
||||
|
||||
@objc
|
||||
internal required init(objects: UnsafePointer<AnyObject?>, count: Int) {
|
||||
_sanityCheckFailure("don't call this designated initializer")
|
||||
}
|
||||
|
||||
@objc(copyWithZone:)
|
||||
internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
|
||||
// Instances of this class should be visible outside of standard library as
|
||||
// having `NSSet` type, which is immutable.
|
||||
return self
|
||||
}
|
||||
|
||||
@objc(member:)
|
||||
internal func member(_ object: AnyObject) -> AnyObject? {
|
||||
guard let element = _conditionallyBridgeFromObjectiveC(object, Element.self)
|
||||
else { return nil }
|
||||
|
||||
let (index, found) = native.find(element)
|
||||
guard found else { return nil }
|
||||
let bridged = bridgeElements()
|
||||
return bridged[index]
|
||||
}
|
||||
|
||||
@objc
|
||||
internal func objectEnumerator() -> _NSEnumerator {
|
||||
return _SwiftSetNSEnumerator<Element>(self)
|
||||
}
|
||||
|
||||
@objc
|
||||
internal var count: Int {
|
||||
return native.count
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>?,
|
||||
count: Int
|
||||
) -> Int {
|
||||
var theState = state.pointee
|
||||
if theState.state == 0 {
|
||||
theState.state = 1 // Arbitrary non-zero value.
|
||||
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
|
||||
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
theState.extra.0 = CUnsignedLong(native.startIndex.bucket)
|
||||
}
|
||||
|
||||
// Test 'objects' rather than 'count' because (a) this is very rare anyway,
|
||||
// and (b) the optimizer should then be able to optimize away the
|
||||
// unwrapping check below.
|
||||
if _slowPath(objects == nil) {
|
||||
return 0
|
||||
}
|
||||
|
||||
let unmanagedObjects = _UnmanagedAnyObjectArray(objects!)
|
||||
var index = _NativeSet<Element>.Index(bucket: Int(theState.extra.0))
|
||||
let endIndex = native.endIndex
|
||||
_precondition(index == endIndex || native.hashTable.isValid(index))
|
||||
|
||||
// Only need to bridge once, so we can hoist it out of the loop.
|
||||
let bridgedElements = bridgeElements()
|
||||
|
||||
var stored = 0
|
||||
for i in 0..<count {
|
||||
if index == endIndex { break }
|
||||
unmanagedObjects[i] = bridgedElements[index]
|
||||
stored += 1
|
||||
index = native.index(after: index)
|
||||
}
|
||||
theState.extra.0 = CUnsignedLong(index.bucket)
|
||||
state.pointee = theState
|
||||
return stored
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_fixed_layout
|
||||
internal struct _CocoaSet {
|
||||
@usableFromInline
|
||||
internal let object: _NSSet
|
||||
|
||||
@inlinable
|
||||
internal init(_ object: _NSSet) {
|
||||
self.object = object
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaSet {
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
internal func member(for index: Index) -> AnyObject {
|
||||
return index.allKeys[index.currentKeyIndex]
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func member(for element: AnyObject) -> AnyObject? {
|
||||
return object.member(element)
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaSet: Equatable {
|
||||
@usableFromInline
|
||||
internal static func ==(lhs: _CocoaSet, rhs: _CocoaSet) -> Bool {
|
||||
return _stdlib_NSObject_isEqual(lhs.object, rhs.object)
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaSet: _SetBuffer {
|
||||
@usableFromInline
|
||||
internal typealias Element = AnyObject
|
||||
|
||||
@inlinable
|
||||
internal var startIndex: Index {
|
||||
return Index(self, startIndex: ())
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var endIndex: Index {
|
||||
return Index(self, endIndex: ())
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func index(after i: Index) -> Index {
|
||||
var i = i
|
||||
formIndex(after: &i)
|
||||
return i
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
internal func formIndex(after i: inout Index) {
|
||||
_precondition(i.base.object === self.object, "Invalid index")
|
||||
_precondition(i.currentKeyIndex < i.allKeys.value,
|
||||
"Cannot increment endIndex")
|
||||
i.currentKeyIndex += 1
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
internal func index(for element: AnyObject) -> Index? {
|
||||
// Fast path that does not involve creating an array of all keys. In case
|
||||
// the key is present, this lookup is a penalty for the slow path, but the
|
||||
// potential savings are significant: we could skip a memory allocation and
|
||||
// a linear search.
|
||||
if !contains(element) {
|
||||
return nil
|
||||
}
|
||||
|
||||
let allKeys = _stdlib_NSSet_allObjects(object)
|
||||
var keyIndex = -1
|
||||
for i in 0..<allKeys.value {
|
||||
if _stdlib_NSObject_isEqual(element, allKeys[i]) {
|
||||
keyIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
_sanityCheck(keyIndex >= 0,
|
||||
"Key was found in fast path, but not found later?")
|
||||
return Index(self, allKeys, keyIndex)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var count: Int {
|
||||
return object.count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func contains(_ element: AnyObject) -> Bool {
|
||||
return object.member(element) != nil
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
internal func element(at i: Index) -> AnyObject {
|
||||
let value: AnyObject? = i.allKeys[i.currentKeyIndex]
|
||||
_sanityCheck(value != nil, "Item not found in underlying NSSet")
|
||||
return value!
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaSet {
|
||||
@_fixed_layout // FIXME(sil-serialize-all)
|
||||
@usableFromInline
|
||||
internal struct Index {
|
||||
// Assumption: we rely on NSDictionary.getObjects when being
|
||||
// repeatedly called on the same NSDictionary, returning items in the same
|
||||
// order every time.
|
||||
// Similarly, the same assumption holds for NSSet.allObjects.
|
||||
|
||||
/// A reference to the NSSet, which owns members in `allObjects`,
|
||||
/// or `allKeys`, for NSSet and NSDictionary respectively.
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal let base: _CocoaSet
|
||||
// FIXME: swift-3-indexing-model: try to remove the cocoa reference, but
|
||||
// make sure that we have a safety check for accessing `allKeys`. Maybe
|
||||
// move both into the dictionary/set itself.
|
||||
|
||||
/// An unowned array of keys.
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal var allKeys: _HeapBuffer<Int, AnyObject>
|
||||
|
||||
/// Index into `allKeys`
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal var currentKeyIndex: Int
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal init(_ base: _CocoaSet, startIndex: ()) {
|
||||
self.base = base
|
||||
self.allKeys = _stdlib_NSSet_allObjects(base.object)
|
||||
self.currentKeyIndex = 0
|
||||
}
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal init(_ base: _CocoaSet, endIndex: ()) {
|
||||
self.base = base
|
||||
self.allKeys = _stdlib_NSSet_allObjects(base.object)
|
||||
self.currentKeyIndex = allKeys.value
|
||||
}
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal init(
|
||||
_ base: _CocoaSet,
|
||||
_ allKeys: _HeapBuffer<Int, AnyObject>,
|
||||
_ currentKeyIndex: Int
|
||||
) {
|
||||
self.base = base
|
||||
self.allKeys = allKeys
|
||||
self.currentKeyIndex = currentKeyIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaSet.Index: Equatable {
|
||||
@inlinable
|
||||
internal static func == (lhs: _CocoaSet.Index, rhs: _CocoaSet.Index) -> Bool {
|
||||
_precondition(lhs.base.object === rhs.base.object,
|
||||
"Comparing indexes from different sets")
|
||||
return lhs.currentKeyIndex == rhs.currentKeyIndex
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaSet.Index: Comparable {
|
||||
@inlinable
|
||||
internal static func < (lhs: _CocoaSet.Index, rhs: _CocoaSet.Index) -> Bool {
|
||||
_precondition(lhs.base.object === rhs.base.object,
|
||||
"Comparing indexes from different sets")
|
||||
return lhs.currentKeyIndex < rhs.currentKeyIndex
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaSet: Sequence {
|
||||
@usableFromInline
|
||||
final internal class Iterator {
|
||||
// Cocoa Set iterator has to be a class, otherwise we cannot
|
||||
// guarantee that the fast enumeration struct is pinned to a certain memory
|
||||
// location.
|
||||
|
||||
// This stored property should be stored at offset zero. There's code below
|
||||
// relying on this.
|
||||
internal var _fastEnumerationState: _SwiftNSFastEnumerationState =
|
||||
_makeSwiftNSFastEnumerationState()
|
||||
|
||||
// This stored property should be stored right after
|
||||
// `_fastEnumerationState`. There's code below relying on this.
|
||||
internal var _fastEnumerationStackBuf = _CocoaFastEnumerationStackBuf()
|
||||
|
||||
internal let base: _CocoaSet
|
||||
|
||||
internal var _fastEnumerationStatePtr:
|
||||
UnsafeMutablePointer<_SwiftNSFastEnumerationState> {
|
||||
return _getUnsafePointerToStoredProperties(self).assumingMemoryBound(
|
||||
to: _SwiftNSFastEnumerationState.self)
|
||||
}
|
||||
|
||||
internal var _fastEnumerationStackBufPtr:
|
||||
UnsafeMutablePointer<_CocoaFastEnumerationStackBuf> {
|
||||
return UnsafeMutableRawPointer(_fastEnumerationStatePtr + 1)
|
||||
.assumingMemoryBound(to: _CocoaFastEnumerationStackBuf.self)
|
||||
}
|
||||
|
||||
// These members have to be word-sized integers, they cannot be limited to
|
||||
// Int8 just because our storage holds 16 elements: fast enumeration is
|
||||
// allowed to return inner pointers to the container, which can be much
|
||||
// larger.
|
||||
internal var itemIndex: Int = 0
|
||||
internal var itemCount: Int = 0
|
||||
|
||||
internal init(_ base: _CocoaSet) {
|
||||
self.base = base
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
__consuming internal func makeIterator() -> Iterator {
|
||||
return Iterator(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension _CocoaSet.Iterator: IteratorProtocol {
|
||||
@usableFromInline
|
||||
internal typealias Element = AnyObject
|
||||
|
||||
@usableFromInline
|
||||
internal func next() -> Element? {
|
||||
if itemIndex < 0 {
|
||||
return nil
|
||||
}
|
||||
let base = self.base
|
||||
if itemIndex == itemCount {
|
||||
let stackBufCount = _fastEnumerationStackBuf.count
|
||||
// We can't use `withUnsafeMutablePointer` here to get pointers to
|
||||
// properties, because doing so might introduce a writeback storage, but
|
||||
// fast enumeration relies on the pointer identity of the enumeration
|
||||
// state struct.
|
||||
itemCount = base.object.countByEnumerating(
|
||||
with: _fastEnumerationStatePtr,
|
||||
objects: UnsafeMutableRawPointer(_fastEnumerationStackBufPtr)
|
||||
.assumingMemoryBound(to: AnyObject.self),
|
||||
count: stackBufCount)
|
||||
if itemCount == 0 {
|
||||
itemIndex = -1
|
||||
return nil
|
||||
}
|
||||
itemIndex = 0
|
||||
}
|
||||
let itemsPtrUP =
|
||||
UnsafeMutableRawPointer(_fastEnumerationState.itemsPtr!)
|
||||
.assumingMemoryBound(to: AnyObject.self)
|
||||
let itemsPtr = _UnmanagedAnyObjectArray(itemsPtrUP)
|
||||
let key: AnyObject = itemsPtr[itemIndex]
|
||||
itemIndex += 1
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
||||
//===--- Bridging ---------------------------------------------------------===//
|
||||
|
||||
extension Set {
|
||||
@inlinable
|
||||
public func _bridgeToObjectiveCImpl() -> _NSSetCore {
|
||||
switch _variant {
|
||||
case .native(let nativeSet):
|
||||
return nativeSet.bridged()
|
||||
case .cocoa(let cocoaSet):
|
||||
return cocoaSet.object
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the native Dictionary hidden inside this NSDictionary;
|
||||
/// returns nil otherwise.
|
||||
public static func _bridgeFromObjectiveCAdoptingNativeStorageOf(
|
||||
_ s: AnyObject
|
||||
) -> Set<Element>? {
|
||||
|
||||
// Try all three NSSet impls that we currently provide.
|
||||
|
||||
if let deferred = s as? _SwiftDeferredNSSet<Element> {
|
||||
return Set(_native: deferred.native)
|
||||
}
|
||||
|
||||
if let nativeStorage = s as? _SetStorage<Element> {
|
||||
return Set(_native: _NativeSet(nativeStorage))
|
||||
}
|
||||
|
||||
if s === _RawSetStorage.empty {
|
||||
return Set()
|
||||
}
|
||||
|
||||
// FIXME: what if `s` is native storage, but for different key/value type?
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _runtime(_ObjC)
|
||||
50
stdlib/public/core/SetBuilder.swift
Normal file
50
stdlib/public/core/SetBuilder.swift
Normal file
@@ -0,0 +1,50 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Initializes a `Set` from unique members.
|
||||
///
|
||||
/// Using a builder can be faster than inserting members into an empty
|
||||
/// `Set`.
|
||||
@_fixed_layout
|
||||
public // SPI(Foundation)
|
||||
struct _SetBuilder<Element: Hashable> {
|
||||
@usableFromInline
|
||||
internal var _target: _NativeSet<Element>
|
||||
@usableFromInline
|
||||
internal let _requestedCount: Int
|
||||
|
||||
@inlinable
|
||||
public init(count: Int) {
|
||||
_target = _NativeSet(capacity: count)
|
||||
_requestedCount = count
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func add(member: Element) {
|
||||
_precondition(_target.count < _requestedCount,
|
||||
"Can't add more members than promised")
|
||||
_target.insertNew(member, isUnique: true)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func take() -> Set<Element> {
|
||||
_precondition(_target.capacity > 0 || _requestedCount == 0,
|
||||
"Cannot take the result twice")
|
||||
_precondition(_target.count == _requestedCount,
|
||||
"The number of members added does not match the promised count")
|
||||
|
||||
// Prevent taking the result twice.
|
||||
var result = _NativeSet<Element>()
|
||||
swap(&result, &_target)
|
||||
return Set(_native: result)
|
||||
}
|
||||
}
|
||||
95
stdlib/public/core/SetCasting.swift
Normal file
95
stdlib/public/core/SetCasting.swift
Normal file
@@ -0,0 +1,95 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===--- Compiler conversion/casting entry points for Set<Element> --------===//
|
||||
|
||||
/// Perform a non-bridged upcast that always succeeds.
|
||||
///
|
||||
/// - Precondition: `BaseValue` is a base class or base `@objc`
|
||||
/// protocol (such as `AnyObject`) of `DerivedValue`.
|
||||
@inlinable
|
||||
public func _setUpCast<DerivedValue, BaseValue>(_ source: Set<DerivedValue>)
|
||||
-> Set<BaseValue> {
|
||||
var builder = _SetBuilder<BaseValue>(count: source.count)
|
||||
for x in source {
|
||||
builder.add(member: x as! BaseValue)
|
||||
}
|
||||
return builder.take()
|
||||
}
|
||||
|
||||
/// Called by the casting machinery.
|
||||
@_silgen_name("_swift_setDownCastIndirect")
|
||||
internal func _setDownCastIndirect<SourceValue, TargetValue>(
|
||||
_ source: UnsafePointer<Set<SourceValue>>,
|
||||
_ target: UnsafeMutablePointer<Set<TargetValue>>) {
|
||||
target.initialize(to: _setDownCast(source.pointee))
|
||||
}
|
||||
|
||||
/// Implements a forced downcast. This operation should have O(1) complexity.
|
||||
///
|
||||
/// The cast can fail if bridging fails. The actual checks and bridging can be
|
||||
/// deferred.
|
||||
///
|
||||
/// - Precondition: `DerivedValue` is a subtype of `BaseValue` and both
|
||||
/// are reference types.
|
||||
@inlinable
|
||||
public func _setDownCast<BaseValue, DerivedValue>(_ source: Set<BaseValue>)
|
||||
-> Set<DerivedValue> {
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
if _isClassOrObjCExistential(BaseValue.self)
|
||||
&& _isClassOrObjCExistential(DerivedValue.self) {
|
||||
switch source._variant {
|
||||
case .native(let nativeSet):
|
||||
return Set(_immutableCocoaSet: nativeSet.bridged())
|
||||
case .cocoa(let cocoaSet):
|
||||
return Set(_immutableCocoaSet: cocoaSet.object)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return _setDownCastConditional(source)!
|
||||
}
|
||||
|
||||
/// Called by the casting machinery.
|
||||
@_silgen_name("_swift_setDownCastConditionalIndirect")
|
||||
internal func _setDownCastConditionalIndirect<SourceValue, TargetValue>(
|
||||
_ source: UnsafePointer<Set<SourceValue>>,
|
||||
_ target: UnsafeMutablePointer<Set<TargetValue>>
|
||||
) -> Bool {
|
||||
if let result: Set<TargetValue> = _setDownCastConditional(source.pointee) {
|
||||
target.initialize(to: result)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// Implements a conditional downcast.
|
||||
///
|
||||
/// If the cast fails, the function returns `nil`. All checks should be
|
||||
/// performed eagerly.
|
||||
///
|
||||
/// - Precondition: `DerivedValue` is a subtype of `BaseValue` and both
|
||||
/// are reference types.
|
||||
@inlinable
|
||||
public func _setDownCastConditional<BaseValue, DerivedValue>(
|
||||
_ source: Set<BaseValue>
|
||||
) -> Set<DerivedValue>? {
|
||||
var result = Set<DerivedValue>(minimumCapacity: source.count)
|
||||
for member in source {
|
||||
if let derivedMember = member as? DerivedValue {
|
||||
result.insert(derivedMember)
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
322
stdlib/public/core/SetStorage.swift
Normal file
322
stdlib/public/core/SetStorage.swift
Normal file
@@ -0,0 +1,322 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import SwiftShims
|
||||
|
||||
/// An instance of this class has all `Set` data tail-allocated.
|
||||
/// Enough bytes are allocated to hold the bitmap for marking valid entries,
|
||||
/// keys, and values. The data layout starts with the bitmap, followed by the
|
||||
/// keys, followed by the values.
|
||||
//
|
||||
// See the docs at the top of the file for more details on this type
|
||||
//
|
||||
// NOTE: The precise layout of this type is relied on in the runtime
|
||||
// to provide a statically allocated empty singleton.
|
||||
// See stdlib/public/stubs/GlobalObjects.cpp for details.
|
||||
@_fixed_layout // FIXME(sil-serialize-all)
|
||||
@usableFromInline
|
||||
@_objc_non_lazy_realization
|
||||
internal class _RawSetStorage: __SwiftNativeNSSet {
|
||||
/// The current number of occupied entries in this set.
|
||||
@usableFromInline
|
||||
@nonobjc
|
||||
internal final var _count: Int
|
||||
|
||||
/// The maximum number of elements that can be inserted into this set without
|
||||
/// exceeding the hash table's maximum load factor.
|
||||
@usableFromInline
|
||||
@nonobjc
|
||||
internal final var _capacity: Int
|
||||
|
||||
/// The scale of this set. The number of buckets is 2 raised to the
|
||||
/// power of `scale`.
|
||||
@usableFromInline
|
||||
@nonobjc
|
||||
internal final var _scale: Int
|
||||
|
||||
@usableFromInline
|
||||
internal final var _seed: Hasher._Seed
|
||||
|
||||
@usableFromInline
|
||||
@nonobjc
|
||||
internal final var _rawElements: UnsafeMutableRawPointer
|
||||
|
||||
// This type is made with allocWithTailElems, so no init is ever called.
|
||||
// But we still need to have an init to satisfy the compiler.
|
||||
@nonobjc
|
||||
internal init(_doNotCallMe: ()) {
|
||||
_sanityCheckFailure("This class cannot be directly initialized")
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@nonobjc
|
||||
internal final var _bucketCount: Int {
|
||||
@inline(__always) get { return 1 &<< _scale }
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@nonobjc
|
||||
internal final var _metadata: UnsafeMutablePointer<_HashTable.Word> {
|
||||
@inline(__always) get {
|
||||
let address = Builtin.projectTailElems(self, _HashTable.Word.self)
|
||||
return UnsafeMutablePointer(address)
|
||||
}
|
||||
}
|
||||
|
||||
// The _HashTable struct contains pointers into tail-allocated storage, so
|
||||
// this is unsafe and needs `_fixLifetime` calls in the caller.
|
||||
@inlinable
|
||||
@nonobjc
|
||||
internal final var _hashTable: _HashTable {
|
||||
@inline(__always) get {
|
||||
return _HashTable(words: _metadata, bucketCount: _bucketCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The storage class for the singleton empty set.
|
||||
/// The single instance of this class is created by the runtime.
|
||||
@_fixed_layout
|
||||
@usableFromInline
|
||||
internal class _EmptySetSingleton: _RawSetStorage {
|
||||
@nonobjc
|
||||
override internal init(_doNotCallMe: ()) {
|
||||
_sanityCheckFailure("This class cannot be directly initialized")
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@objc
|
||||
internal required init(objects: UnsafePointer<AnyObject?>, count: Int) {
|
||||
_sanityCheckFailure("This class cannot be directly initialized")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extension _RawSetStorage {
|
||||
/// The empty singleton that is used for every single Set that is created
|
||||
/// without any elements. The contents of the storage must never be mutated.
|
||||
@inlinable
|
||||
@nonobjc
|
||||
internal static var empty: _EmptySetSingleton {
|
||||
return Builtin.bridgeFromRawPointer(
|
||||
Builtin.addressof(&_swiftEmptySetSingleton))
|
||||
}
|
||||
}
|
||||
|
||||
extension _EmptySetSingleton: _NSSetCore {
|
||||
#if _runtime(_ObjC)
|
||||
//
|
||||
// NSSet implementation, assuming Self is the empty singleton
|
||||
//
|
||||
@objc(copyWithZone:)
|
||||
internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
|
||||
return self
|
||||
}
|
||||
|
||||
@objc
|
||||
internal var count: Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
@objc(member:)
|
||||
internal func member(_ object: AnyObject) -> AnyObject? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc
|
||||
internal func objectEnumerator() -> _NSEnumerator {
|
||||
return _SwiftEmptyNSEnumerator()
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>?, count: Int
|
||||
) -> Int {
|
||||
// Even though we never do anything in here, we need to update the
|
||||
// state so that callers know we actually ran.
|
||||
var theState = state.pointee
|
||||
if theState.state == 0 {
|
||||
theState.state = 1 // Arbitrary non-zero value.
|
||||
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
|
||||
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
}
|
||||
state.pointee = theState
|
||||
return 0
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// See the docs at the top of this file for a description of this type
|
||||
@_fixed_layout // FIXME(sil-serialize-all)
|
||||
@usableFromInline
|
||||
final internal class _SetStorage<Element: Hashable>
|
||||
: _RawSetStorage, _NSSetCore {
|
||||
// This type is made with allocWithTailElems, so no init is ever called.
|
||||
// But we still need to have an init to satisfy the compiler.
|
||||
@nonobjc
|
||||
override internal init(_doNotCallMe: ()) {
|
||||
_sanityCheckFailure("This class cannot be directly initialized")
|
||||
}
|
||||
|
||||
deinit {
|
||||
guard _count > 0 else { return }
|
||||
if !_isPOD(Element.self) {
|
||||
let elements = _elements
|
||||
for index in _hashTable {
|
||||
(elements + index.bucket).deinitialize(count: 1)
|
||||
}
|
||||
}
|
||||
_fixLifetime(self)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
final internal var _elements: UnsafeMutablePointer<Element> {
|
||||
@inline(__always)
|
||||
get {
|
||||
return self._rawElements.assumingMemoryBound(to: Element.self)
|
||||
}
|
||||
}
|
||||
|
||||
internal var asNative: _NativeSet<Element> {
|
||||
return _NativeSet(self)
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@objc
|
||||
internal required init(objects: UnsafePointer<AnyObject?>, count: Int) {
|
||||
_sanityCheckFailure("don't call this designated initializer")
|
||||
}
|
||||
|
||||
@objc(copyWithZone:)
|
||||
internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
|
||||
return self
|
||||
}
|
||||
|
||||
@objc
|
||||
internal var count: Int {
|
||||
return _count
|
||||
}
|
||||
|
||||
@objc
|
||||
internal func objectEnumerator() -> _NSEnumerator {
|
||||
return _SwiftSetNSEnumerator<Element>(asNative)
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>?, count: Int
|
||||
) -> Int {
|
||||
var theState = state.pointee
|
||||
if theState.state == 0 {
|
||||
theState.state = 1 // Arbitrary non-zero value.
|
||||
theState.itemsPtr = AutoreleasingUnsafeMutablePointer(objects)
|
||||
theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
theState.extra.0 = CUnsignedLong(asNative.startIndex.bucket)
|
||||
}
|
||||
|
||||
// Test 'objects' rather than 'count' because (a) this is very rare anyway,
|
||||
// and (b) the optimizer should then be able to optimize away the
|
||||
// unwrapping check below.
|
||||
if _slowPath(objects == nil) {
|
||||
return 0
|
||||
}
|
||||
|
||||
let unmanagedObjects = _UnmanagedAnyObjectArray(objects!)
|
||||
var index = _HashTable.Index(bucket: Int(theState.extra.0))
|
||||
let endIndex = asNative.endIndex
|
||||
_precondition(index == endIndex || _hashTable.isValid(index))
|
||||
var stored = 0
|
||||
for i in 0..<count {
|
||||
if index == endIndex { break }
|
||||
let element = _elements[index.bucket]
|
||||
unmanagedObjects[i] = _bridgeAnythingToObjectiveC(element)
|
||||
stored += 1
|
||||
index = asNative.index(after: index)
|
||||
}
|
||||
theState.extra.0 = CUnsignedLong(index.bucket)
|
||||
state.pointee = theState
|
||||
return stored
|
||||
}
|
||||
|
||||
@objc(member:)
|
||||
internal func member(_ object: AnyObject) -> AnyObject? {
|
||||
guard let native = _conditionallyBridgeFromObjectiveC(object, Element.self)
|
||||
else { return nil }
|
||||
|
||||
let (index, found) = asNative.find(native)
|
||||
guard found else { return nil }
|
||||
return _bridgeAnythingToObjectiveC(_elements[index.bucket])
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extension _SetStorage {
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
internal static func reallocate(
|
||||
original: _RawSetStorage,
|
||||
capacity: Int
|
||||
) -> (storage: _SetStorage, rehash: Bool) {
|
||||
_sanityCheck(capacity >= original._count)
|
||||
let scale = _HashTable.scale(forCapacity: capacity)
|
||||
let rehash = (scale != original._scale)
|
||||
let newStorage = _SetStorage<Element>.allocate(scale: scale)
|
||||
return (newStorage, rehash)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
@_effects(releasenone)
|
||||
static internal func allocate(capacity: Int) -> _SetStorage {
|
||||
let scale = _HashTable.scale(forCapacity: capacity)
|
||||
return allocate(scale: scale)
|
||||
}
|
||||
|
||||
static internal func allocate(scale: Int) -> _SetStorage {
|
||||
// The entry count must be representable by an Int value; hence the scale's
|
||||
// peculiar upper bound.
|
||||
_sanityCheck(scale >= 0 && scale < Int.bitWidth - 1)
|
||||
|
||||
let bucketCount = 1 &<< scale
|
||||
let wordCount = _UnsafeBitset.wordCount(forCapacity: bucketCount)
|
||||
let storage = Builtin.allocWithTailElems_2(
|
||||
_SetStorage<Element>.self,
|
||||
wordCount._builtinWordValue, _HashTable.Word.self,
|
||||
bucketCount._builtinWordValue, Element.self)
|
||||
|
||||
let metadataAddr = Builtin.projectTailElems(storage, _HashTable.Word.self)
|
||||
let elementsAddr = Builtin.getTailAddr_Word(
|
||||
metadataAddr, wordCount._builtinWordValue, _HashTable.Word.self,
|
||||
Element.self)
|
||||
storage._count = 0
|
||||
storage._capacity = _HashTable.capacity(forScale: scale)
|
||||
storage._scale = scale
|
||||
storage._rawElements = UnsafeMutableRawPointer(elementsAddr)
|
||||
|
||||
// We use a slightly different hash seed whenever we change the size of the
|
||||
// hash table, so that we avoid certain copy operations becoming quadratic,
|
||||
// without breaking value semantics. (For background details, see
|
||||
// https://bugs.swift.org/browse/SR-3268)
|
||||
|
||||
// FIXME: Use true per-instance seeding instead. Per-capacity seeding still
|
||||
// leaves hash values the same in same-sized tables, which may affect
|
||||
// operations on two tables at once. (E.g., union.)
|
||||
storage._seed = (
|
||||
Hasher._seed.0 ^ UInt64(truncatingIfNeeded: scale),
|
||||
Hasher._seed.1)
|
||||
|
||||
// Initialize hash table metadata.
|
||||
storage._hashTable.clear()
|
||||
return storage
|
||||
}
|
||||
}
|
||||
392
stdlib/public/core/SetVariant.swift
Normal file
392
stdlib/public/core/SetVariant.swift
Normal file
@@ -0,0 +1,392 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// This protocol is only used for compile-time checks that
|
||||
/// every buffer type implements all required operations.
|
||||
internal protocol _SetBuffer {
|
||||
associatedtype Element
|
||||
associatedtype Index
|
||||
|
||||
var startIndex: Index { get }
|
||||
var endIndex: Index { get }
|
||||
func index(after i: Index) -> Index
|
||||
func index(for element: Element) -> Index?
|
||||
var count: Int { get }
|
||||
|
||||
func contains(_ member: Element) -> Bool
|
||||
func element(at i: Index) -> Element
|
||||
}
|
||||
|
||||
extension Set {
|
||||
@usableFromInline
|
||||
@_frozen
|
||||
internal enum _Variant {
|
||||
case native(_NativeSet<Element>)
|
||||
#if _runtime(_ObjC)
|
||||
case cocoa(_CocoaSet)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension Set._Variant {
|
||||
#if _runtime(_ObjC)
|
||||
@usableFromInline
|
||||
@_transparent
|
||||
internal var guaranteedNative: Bool {
|
||||
return _canBeClass(Element.self) == 0
|
||||
}
|
||||
|
||||
/// Allow the optimizer to consider the surrounding code unreachable if
|
||||
/// Set<Element> is guaranteed to be native.
|
||||
@usableFromInline
|
||||
@_transparent
|
||||
internal func cocoaPath() {
|
||||
if guaranteedNative {
|
||||
_conditionallyUnreachable()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@inlinable
|
||||
internal mutating func isUniquelyReferenced() -> Bool {
|
||||
// Note that &self drills down through .native(_NativeSet) to the first
|
||||
// property in _NativeSet, which is the reference to the storage.
|
||||
switch self {
|
||||
case .native:
|
||||
return _isUnique_native(&self)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa:
|
||||
cocoaPath()
|
||||
// Don't consider Cocoa buffer mutable, even if it is mutable and is
|
||||
// uniquely referenced.
|
||||
return false
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline @_transparent
|
||||
internal var asNative: _NativeSet<Element> {
|
||||
get {
|
||||
switch self {
|
||||
case .native(let nativeSet):
|
||||
return nativeSet
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa:
|
||||
_sanityCheckFailure("internal error: not backed by native buffer")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
set {
|
||||
self = .native(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@inlinable
|
||||
internal var asCocoa: _CocoaSet {
|
||||
switch self {
|
||||
case .native:
|
||||
_sanityCheckFailure("internal error: not backed by NSSet")
|
||||
case .cocoa(let cocoa):
|
||||
return cocoa
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Reserves enough space for the specified number of elements to be stored
|
||||
/// without reallocating additional storage.
|
||||
@inlinable
|
||||
internal mutating func reserveCapacity(_ capacity: Int) {
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = isUniquelyReferenced()
|
||||
asNative.reserveCapacity(capacity, isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let capacity = Swift.max(cocoa.count, capacity)
|
||||
self = .native(_NativeSet(cocoa, capacity: capacity))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of elements that can be stored without expanding the current
|
||||
/// storage.
|
||||
///
|
||||
/// For bridged storage, this is equal to the current count of the
|
||||
/// collection, since any addition will trigger a copy of the elements into
|
||||
/// newly allocated storage. For native storage, this is the element count
|
||||
/// at which adding any more elements will exceed the load factor.
|
||||
@inlinable
|
||||
internal var capacity: Int {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.capacity
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return cocoa.count
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Set._Variant: _SetBuffer {
|
||||
@usableFromInline
|
||||
internal typealias Index = Set<Element>.Index
|
||||
|
||||
@inlinable
|
||||
internal var startIndex: Index {
|
||||
switch self {
|
||||
case .native:
|
||||
return Index(_native: asNative.startIndex)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoaSet):
|
||||
cocoaPath()
|
||||
return Index(_cocoa: cocoaSet.startIndex)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var endIndex: Index {
|
||||
switch self {
|
||||
case .native:
|
||||
return Index(_native: asNative.endIndex)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoaSet):
|
||||
cocoaPath()
|
||||
return Index(_cocoa: cocoaSet.endIndex)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal func index(after i: Index) -> Index {
|
||||
switch self {
|
||||
case .native:
|
||||
return Index(_native: asNative.index(after: i._asNative))
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoaSet):
|
||||
cocoaPath()
|
||||
return Index(_cocoa: cocoaSet.index(after: i._asCocoa))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func index(for element: Element) -> Index? {
|
||||
switch self {
|
||||
case .native:
|
||||
guard let index = asNative.index(for: element) else { return nil }
|
||||
return Index(_native: index)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let cocoaElement = _bridgeAnythingToObjectiveC(element)
|
||||
guard let index = cocoa.index(for: cocoaElement) else { return nil }
|
||||
return Index(_cocoa: index)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var count: Int {
|
||||
@inline(__always)
|
||||
get {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.count
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return cocoa.count
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func contains(_ member: Element) -> Bool {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.contains(member)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return cocoa.contains(_bridgeAnythingToObjectiveC(member))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func element(at i: Index) -> Element {
|
||||
switch self {
|
||||
case .native:
|
||||
return asNative.element(at: i._asNative)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let cocoaMember = cocoa.element(at: i._asCocoa)
|
||||
return _forceBridgeFromObjectiveC(cocoaMember, Element.self)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Set._Variant {
|
||||
@inlinable
|
||||
internal mutating func update(with value: Element) -> Element? {
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = self.isUniquelyReferenced()
|
||||
return asNative.update(with: value, isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
// Make sure we have space for an extra element.
|
||||
var native = _NativeSet<Element>(cocoa, capacity: cocoa.count + 1)
|
||||
let old = native.update(with: value, isUnique: true)
|
||||
self = .native(native)
|
||||
return old
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal mutating func insert(
|
||||
_ element: Element
|
||||
) -> (inserted: Bool, memberAfterInsert: Element) {
|
||||
switch self {
|
||||
case .native:
|
||||
let (index, found) = asNative.find(element)
|
||||
if found {
|
||||
return (false, asNative.uncheckedElement(at: index))
|
||||
}
|
||||
let isUnique = self.isUniquelyReferenced()
|
||||
asNative.insertNew(element, at: index, isUnique: isUnique)
|
||||
return (true, element)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
// Make sure we have space for an extra element.
|
||||
let cocoaMember = _bridgeAnythingToObjectiveC(element)
|
||||
if let m = cocoa.member(for: cocoaMember) {
|
||||
return (false, _forceBridgeFromObjectiveC(m, Element.self))
|
||||
}
|
||||
var native = _NativeSet<Element>(cocoa, capacity: cocoa.count + 1)
|
||||
native.insertNew(element, isUnique: true)
|
||||
self = .native(native)
|
||||
return (true, element)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@discardableResult
|
||||
internal mutating func remove(at index: Index) -> Element {
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = isUniquelyReferenced()
|
||||
return asNative.remove(at: index._asNative, isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
// We have to migrate the data first. But after we do so, the Cocoa
|
||||
// index becomes useless, so get the element first.
|
||||
let cocoaMember = cocoa.member(for: index._asCocoa)
|
||||
let nativeMember = _forceBridgeFromObjectiveC(cocoaMember, Element.self)
|
||||
return _migrateToNative(cocoa, removing: nativeMember)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@discardableResult
|
||||
internal mutating func remove(_ member: Element) -> Element? {
|
||||
switch self {
|
||||
case .native:
|
||||
let (index, found) = asNative.find(member)
|
||||
guard found else { return nil }
|
||||
let isUnique = isUniquelyReferenced()
|
||||
return asNative.uncheckedRemove(at: index, isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
let cocoaMember = _bridgeAnythingToObjectiveC(member)
|
||||
guard cocoa.contains(cocoaMember) else { return nil }
|
||||
return _migrateToNative(cocoa, removing: member)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
@inlinable
|
||||
internal mutating func _migrateToNative(
|
||||
_ cocoa: _CocoaSet,
|
||||
removing member: Element
|
||||
) -> Element {
|
||||
// FIXME(performance): fuse data migration and element deletion into one
|
||||
// operation.
|
||||
var native = _NativeSet<Element>(cocoa)
|
||||
let (index, found) = native.find(member)
|
||||
_precondition(found, "Bridging did not preserve equality")
|
||||
let old = native.remove(at: index, isUnique: true)
|
||||
_precondition(member == old, "Bridging did not preserve equality")
|
||||
self = .native(native)
|
||||
return old
|
||||
}
|
||||
#endif
|
||||
|
||||
@inlinable
|
||||
internal mutating func removeAll(keepingCapacity keepCapacity: Bool) {
|
||||
if !keepCapacity {
|
||||
self = .native(_NativeSet<Element>())
|
||||
return
|
||||
}
|
||||
guard count > 0 else { return }
|
||||
|
||||
switch self {
|
||||
case .native:
|
||||
let isUnique = isUniquelyReferenced()
|
||||
asNative.removeAll(isUnique: isUnique)
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
self = .native(_NativeSet(capacity: cocoa.count))
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Set._Variant {
|
||||
/// Returns an iterator over the elements.
|
||||
///
|
||||
/// - Complexity: O(1).
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
__consuming internal func makeIterator() -> Set<Element>.Iterator {
|
||||
switch self {
|
||||
case .native(let native):
|
||||
return Set.Iterator(_native: native.makeIterator())
|
||||
#if _runtime(_ObjC)
|
||||
case .cocoa(let cocoa):
|
||||
cocoaPath()
|
||||
return Set.Iterator(_cocoa: cocoa.makeIterator())
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public protocol _NSDictionaryCore :
|
||||
var count: Int { get }
|
||||
|
||||
@objc(objectForKey:)
|
||||
func objectFor(_ aKey: AnyObject) -> AnyObject?
|
||||
func object(forKey aKey: AnyObject) -> AnyObject?
|
||||
|
||||
func keyEnumerator() -> _NSEnumerator
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ internal struct _ThreadLocalStorage {
|
||||
//
|
||||
// private
|
||||
internal var uBreakIterator: OpaquePointer
|
||||
internal var uText: OpaquePointer
|
||||
|
||||
// TODO: Consider saving two, e.g. for character-by-character comparison
|
||||
|
||||
@@ -55,8 +56,9 @@ internal struct _ThreadLocalStorage {
|
||||
// TODO: unowned reference to string owner, base address, and _countAndFlags
|
||||
|
||||
// private: Should only be called by _initializeThreadLocalStorage
|
||||
internal init(_uBreakIterator: OpaquePointer) {
|
||||
internal init(_uBreakIterator: OpaquePointer, _uText: OpaquePointer) {
|
||||
self.uBreakIterator = _uBreakIterator
|
||||
self.uText = _uText
|
||||
}
|
||||
|
||||
// Get the current thread's TLS pointer. On first call for a given thread,
|
||||
@@ -104,19 +106,26 @@ internal func _destroyTLS(_ ptr: UnsafeMutableRawPointer?) {
|
||||
internal func _createThreadLocalStorage()
|
||||
-> UnsafeMutablePointer<_ThreadLocalStorage>
|
||||
{
|
||||
// Create and initialize one.
|
||||
// Allocate and initialize a UBreakIterator and UText.
|
||||
var err = __swift_stdlib_U_ZERO_ERROR
|
||||
let newUBreakIterator = __swift_stdlib_ubrk_open(
|
||||
/*type:*/ __swift_stdlib_UBRK_CHARACTER, /*locale:*/ nil,
|
||||
/*text:*/ nil, /*textLength:*/ 0, /*status:*/ &err)
|
||||
_precondition(err.isSuccess, "Unexpected ubrk_open failure")
|
||||
|
||||
// utext_openUTF8 needs a valid pointer, even though we won't read from it
|
||||
var a: Int8 = 0x41
|
||||
let newUText = __swift_stdlib_utext_openUTF8(
|
||||
/*ut:*/ nil, /*s:*/ &a, /*len:*/ 1, /*status:*/ &err)
|
||||
|
||||
_precondition(err.isSuccess, "Unexpected utext_openUTF8 failure")
|
||||
|
||||
let tlsPtr: UnsafeMutablePointer<_ThreadLocalStorage>
|
||||
= UnsafeMutablePointer<_ThreadLocalStorage>.allocate(
|
||||
capacity: 1
|
||||
)
|
||||
tlsPtr.initialize(
|
||||
to: _ThreadLocalStorage(_uBreakIterator: newUBreakIterator)
|
||||
)
|
||||
tlsPtr.initialize(to: _ThreadLocalStorage(
|
||||
_uBreakIterator: newUBreakIterator, _uText: newUText))
|
||||
|
||||
return tlsPtr
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// A wrapper around a bitmap storage with room for at least `bitCount` bits.
|
||||
@_fixed_layout
|
||||
@usableFromInline // @testable
|
||||
internal struct _UnsafeBitMap {
|
||||
@usableFromInline
|
||||
internal let values: UnsafeMutablePointer<UInt>
|
||||
|
||||
@usableFromInline
|
||||
internal let bitCount: Int
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal static func wordIndex(_ i: Int) -> Int {
|
||||
// Note: We perform the operation on UInts to get faster unsigned math
|
||||
// (shifts).
|
||||
return Int(bitPattern: UInt(bitPattern: i) / UInt(UInt.bitWidth))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal static func bitIndex(_ i: Int) -> UInt {
|
||||
// Note: We perform the operation on UInts to get faster unsigned math
|
||||
// (shifts).
|
||||
return UInt(bitPattern: i) % UInt(UInt.bitWidth)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal static func sizeInWords(forSizeInBits bitCount: Int) -> Int {
|
||||
return (bitCount + Int.bitWidth - 1) / Int.bitWidth
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal init(storage: UnsafeMutablePointer<UInt>, bitCount: Int) {
|
||||
self.bitCount = bitCount
|
||||
self.values = storage
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal var numberOfWords: Int {
|
||||
@inline(__always)
|
||||
get {
|
||||
return _UnsafeBitMap.sizeInWords(forSizeInBits: bitCount)
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
@inline(__always)
|
||||
internal func initializeToZero() {
|
||||
values.initialize(repeating: 0, count: numberOfWords)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
internal subscript(i: Int) -> Bool {
|
||||
@inline(__always)
|
||||
get {
|
||||
_sanityCheck(i < Int(bitCount) && i >= 0, "index out of bounds")
|
||||
let word = values[_UnsafeBitMap.wordIndex(i)]
|
||||
let bit = word & (1 << _UnsafeBitMap.bitIndex(i))
|
||||
return bit != 0
|
||||
}
|
||||
@inline(__always)
|
||||
nonmutating set {
|
||||
_sanityCheck(i < Int(bitCount) && i >= 0, "index out of bounds")
|
||||
let wordIdx = _UnsafeBitMap.wordIndex(i)
|
||||
let bitMask = (1 as UInt) &<< _UnsafeBitMap.bitIndex(i)
|
||||
if newValue {
|
||||
values[wordIdx] = values[wordIdx] | bitMask
|
||||
} else {
|
||||
values[wordIdx] = values[wordIdx] & ~bitMask
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user