mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
4031 lines
129 KiB
Swift
4031 lines
129 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
|
|
//===--- APIs unique to Set<Element> --------------------------------------===//
|
|
|
|
/// An unordered collection of unique elements.
|
|
///
|
|
/// You use a set instead of an array when you need to test efficiently for
|
|
/// membership and you aren't concerned with the order of the elements in the
|
|
/// collection, or when you need to ensure that each element appears only once
|
|
/// in a collection.
|
|
///
|
|
/// You can create a set with any element type that conforms to the `Hashable`
|
|
/// protocol. By default, most types in the standard library are hashable,
|
|
/// including strings, numeric and Boolean types, enumeration cases without
|
|
/// associated values, and even sets themselves.
|
|
///
|
|
/// Swift makes it as easy to create a new set as to create a new array. Simply
|
|
/// assign an array literal to a variable or constant with the `Set` type
|
|
/// specified.
|
|
///
|
|
/// let ingredients: Set = ["cocoa beans", "sugar", "cocoa butter", "salt"]
|
|
/// if ingredients.contains("sugar") {
|
|
/// print("No thanks, too sweet.")
|
|
/// }
|
|
/// // Prints "No thanks, too sweet."
|
|
///
|
|
/// Set Operations
|
|
/// ==============
|
|
///
|
|
/// Sets provide a suite of mathematical set operations. For example, you can
|
|
/// efficiently test a set for membership of an element or check its
|
|
/// intersection with another set:
|
|
///
|
|
/// - Use the `contains(_:)` method to test whether a set contains a specific
|
|
/// element.
|
|
/// - Use the "equal to" operator (`==`) to test whether two sets contain the
|
|
/// same elements.
|
|
/// - Use the `isSubset(of:)` method to test whether a set contains all the
|
|
/// elements of another set or sequence.
|
|
/// - Use the `isSuperset(of:)` method to test whether all elements of a set
|
|
/// are contained in another set or sequence.
|
|
/// - Use the `isStrictSubset(of:)` and `isStrictSuperset(of:)` methods to test
|
|
/// whether a set is a subset or superset of, but not equal to, another set.
|
|
/// - Use the `isDisjoint(with:)` method to test whether a set has any elements
|
|
/// in common with another set.
|
|
///
|
|
/// You can also combine, exclude, or subtract the elements of two sets:
|
|
///
|
|
/// - Use the `union(_:)` method to create a new set with the elements of a set
|
|
/// and another set or sequence.
|
|
/// - Use the `intersection(_:)` method to create a new set with only the
|
|
/// elements common to a set and another set or sequence.
|
|
/// - Use the `symmetricDifference(_:)` method to create a new set with the
|
|
/// elements that are in either a set or another set or sequence, but not in
|
|
/// both.
|
|
/// - Use the `subtracting(_:)` method to create a new set with the elements of
|
|
/// a set that are not also in another set or sequence.
|
|
///
|
|
/// You can modify a set in place by using these methods' mutating
|
|
/// counterparts: `formUnion(_:)`, `formIntersection(_:)`,
|
|
/// `formSymmetricDifference(_:)`, and `subtract(_:)`.
|
|
///
|
|
/// Set operations are not limited to use with other sets. Instead, you can
|
|
/// perform set operations with another set, an array, or any other sequence
|
|
/// type.
|
|
///
|
|
/// var primes: Set = [2, 3, 5, 7]
|
|
///
|
|
/// // Tests whether primes is a subset of a Range<Int>
|
|
/// print(primes.isSubset(of: 0..<10))
|
|
/// // Prints "true"
|
|
///
|
|
/// // Performs an intersection with an Array<Int>
|
|
/// let favoriteNumbers = [5, 7, 15, 21]
|
|
/// print(primes.intersection(favoriteNumbers))
|
|
/// // Prints "[5, 7]"
|
|
///
|
|
/// Sequence and Collection Operations
|
|
/// ==================================
|
|
///
|
|
/// In addition to the `Set` type's set operations, you can use any nonmutating
|
|
/// sequence or collection methods with a set.
|
|
///
|
|
/// if primes.isEmpty {
|
|
/// print("No primes!")
|
|
/// } else {
|
|
/// print("We have \(primes.count) primes.")
|
|
/// }
|
|
/// // Prints "We have 4 primes."
|
|
///
|
|
/// let primesSum = primes.reduce(0, +)
|
|
/// // 'primesSum' == 17
|
|
///
|
|
/// let primeStrings = primes.sorted().map(String.init)
|
|
/// // 'primeStrings' == ["2", "3", "5", "7"]
|
|
///
|
|
/// You can iterate through a set's unordered elements with a `for`-`in` loop.
|
|
///
|
|
/// for number in primes {
|
|
/// print(number)
|
|
/// }
|
|
/// // Prints "5"
|
|
/// // Prints "7"
|
|
/// // Prints "2"
|
|
/// // Prints "3"
|
|
///
|
|
/// Many sequence and collection operations return an array or a type-erasing
|
|
/// collection wrapper instead of a set. To restore efficient set operations,
|
|
/// create a new set from the result.
|
|
///
|
|
/// let morePrimes = primes.union([11, 13, 17, 19])
|
|
///
|
|
/// let laterPrimes = morePrimes.filter { $0 > 10 }
|
|
/// // 'laterPrimes' is of type Array<Int>
|
|
///
|
|
/// let laterPrimesSet = Set(morePrimes.filter { $0 > 10 })
|
|
/// // 'laterPrimesSet' is of type Set<Int>
|
|
///
|
|
/// Bridging Between Set and NSSet
|
|
/// ==============================
|
|
///
|
|
/// You can bridge between `Set` and `NSSet` using the `as` operator. For
|
|
/// bridging to be possible, the `Element` type of a set must be a class, an
|
|
/// `@objc` protocol (a protocol imported from Objective-C or marked with the
|
|
/// `@objc` attribute), or a type that bridges to a Foundation type.
|
|
///
|
|
/// Bridging from `Set` to `NSSet` always takes O(1) time and space. When the
|
|
/// set's `Element` type is neither a class nor an `@objc` protocol, any
|
|
/// required bridging of elements occurs at the first access of each element,
|
|
/// so the first operation that uses the contents of the set (for example, a
|
|
/// membership test) can take O(*n*).
|
|
///
|
|
/// Bridging from `NSSet` to `Set` first calls the `copy(with:)` method
|
|
/// (`- copyWithZone:` in Objective-C) on the set to get an immutable copy and
|
|
/// then performs additional Swift bookkeeping work that takes O(1) time. For
|
|
/// instances of `NSSet` that are already immutable, `copy(with:)` returns the
|
|
/// same set in constant time; otherwise, the copying performance is
|
|
/// unspecified. The instances of `NSSet` and `Set` share buffer using the
|
|
/// same copy-on-write optimization that is used when two instances of `Set`
|
|
/// share buffer.
|
|
@_fixed_layout
|
|
public struct Set<Element: Hashable> {
|
|
internal typealias _VariantBuffer = _VariantSetBuffer<Element>
|
|
internal typealias _NativeBuffer = _NativeSetBuffer<Element>
|
|
|
|
@usableFromInline
|
|
internal var _variantBuffer: _VariantBuffer
|
|
}
|
|
|
|
extension Set {
|
|
/// Creates an empty set with preallocated space for at least the specified
|
|
/// number of elements.
|
|
///
|
|
/// Use this initializer to avoid intermediate reallocations of a set's
|
|
/// storage buffer when you know how many elements you'll insert into the set
|
|
/// after creation.
|
|
///
|
|
/// - Parameter minimumCapacity: The minimum number of elements that the
|
|
/// newly created set should be able to store without reallocating its
|
|
/// storage buffer.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public init(minimumCapacity: Int) {
|
|
_variantBuffer =
|
|
_VariantBuffer.native(
|
|
_NativeBuffer(minimumCapacity: minimumCapacity))
|
|
}
|
|
|
|
/// Private initializer.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_nativeBuffer: _NativeSetBuffer<Element>) {
|
|
_variantBuffer = _VariantBuffer.native(_nativeBuffer)
|
|
}
|
|
|
|
//
|
|
// All APIs below should dispatch to `_variantBuffer`, without doing any
|
|
// additional processing.
|
|
//
|
|
|
|
#if _runtime(_ObjC)
|
|
/// Private initializer used for bridging.
|
|
///
|
|
/// Only use this initializer when both conditions are true:
|
|
///
|
|
/// * it is statically known that the given `NSSet` is immutable;
|
|
/// * `Element` is bridged verbatim to Objective-C (i.e.,
|
|
/// is a reference type).
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public init(_immutableCocoaSet: _NSSet) {
|
|
_sanityCheck(_isBridgedVerbatimToObjectiveC(Element.self),
|
|
"Set can be backed by NSSet _variantBuffer only when the member type can be bridged verbatim to Objective-C")
|
|
_variantBuffer = _VariantSetBuffer.cocoa(
|
|
_CocoaSetBuffer(cocoaSet: _immutableCocoaSet))
|
|
}
|
|
#endif
|
|
}
|
|
|
|
extension Set: ExpressibleByArrayLiteral {
|
|
|
|
//
|
|
// `ExpressibleByArrayLiteral` conformance
|
|
//
|
|
/// Creates a set containing the elements of the given array literal.
|
|
///
|
|
/// Do not call this initializer directly. It is used by the compiler when
|
|
/// you use an array literal. Instead, create a new set using an array
|
|
/// literal as its value by enclosing a comma-separated list of values in
|
|
/// square brackets. You can use an array literal anywhere a set is expected
|
|
/// by the type context.
|
|
///
|
|
/// Here, a set of strings is created from an array literal holding only
|
|
/// strings.
|
|
///
|
|
/// let ingredients: Set = ["cocoa beans", "sugar", "cocoa butter", "salt"]
|
|
/// if ingredients.isSuperset(of: ["sugar", "salt"]) {
|
|
/// print("Whatever it is, it's bound to be delicious!")
|
|
/// }
|
|
/// // Prints "Whatever it is, it's bound to be delicious!"
|
|
///
|
|
/// - Parameter elements: A variadic list of elements of the new set.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public init(arrayLiteral elements: Element...) {
|
|
self.init(_nativeBuffer: _NativeSetBuffer.fromArray(elements))
|
|
}
|
|
}
|
|
|
|
extension Set: Sequence {
|
|
/// Returns an iterator over the members of the set.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
public func makeIterator() -> SetIterator<Element> {
|
|
return _variantBuffer.makeIterator()
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether the given element exists
|
|
/// in the set.
|
|
///
|
|
/// This example uses the `contains(_:)` method to test whether an integer is
|
|
/// a member of a set of prime numbers.
|
|
///
|
|
/// let primes: Set = [2, 3, 5, 7]
|
|
/// let x = 5
|
|
/// if primes.contains(x) {
|
|
/// print("\(x) is prime!")
|
|
/// } else {
|
|
/// print("\(x). Not prime.")
|
|
/// }
|
|
/// // Prints "5 is prime!"
|
|
///
|
|
/// - Parameter member: An element to look for in the set.
|
|
/// - Returns: `true` if `member` exists in the set; otherwise, `false`.
|
|
///
|
|
/// - Complexity: O(1)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func contains(_ member: Element) -> Bool {
|
|
return _variantBuffer.maybeGet(member) != nil
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func _customContainsEquatableElement(_ member: Element) -> Bool? {
|
|
return contains(member)
|
|
}
|
|
}
|
|
|
|
// This is not quite Sequence.filter, because that returns [Element], not Self
|
|
// (RangeReplaceableCollection.filter returns Self, but Set isn't an RRC)
|
|
extension Set {
|
|
/// Returns a new set containing the elements of the set that satisfy the
|
|
/// given predicate.
|
|
///
|
|
/// In this example, `filter(_:)` is used to include only names shorter than
|
|
/// five characters.
|
|
///
|
|
/// let cast: Set = ["Vivien", "Marlon", "Kim", "Karl"]
|
|
/// let shortNames = cast.filter { $0.count < 5 }
|
|
///
|
|
/// shortNames.isSubset(of: cast)
|
|
/// // true
|
|
/// shortNames.contains("Vivien")
|
|
/// // false
|
|
///
|
|
/// - Parameter isIncluded: A closure that takes an element as its argument
|
|
/// and returns a Boolean value indicating whether the element should be
|
|
/// included in the returned set.
|
|
/// - Returns: A set of the elements that `isIncluded` allows.
|
|
@inlinable
|
|
@available(swift, introduced: 4.0)
|
|
public func filter(
|
|
_ isIncluded: (Element) throws -> Bool
|
|
) rethrows -> Set {
|
|
var result = Set()
|
|
for element in self {
|
|
if try isIncluded(element) {
|
|
result.insert(element)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
}
|
|
|
|
extension Set: Collection {
|
|
/// The starting position for iterating members of the set.
|
|
///
|
|
/// If the set is empty, `startIndex` is equal to `endIndex`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var startIndex: Index {
|
|
return _variantBuffer.startIndex
|
|
}
|
|
|
|
/// The "past the end" position for the set---that is, the position one
|
|
/// greater than the last valid subscript argument.
|
|
///
|
|
/// If the set is empty, `endIndex` is equal to `startIndex`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var endIndex: Index {
|
|
return _variantBuffer.endIndex
|
|
}
|
|
|
|
/// Accesses the member at the given position.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public subscript(position: Index) -> Element {
|
|
return _variantBuffer.assertingGet(position)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func index(after i: Index) -> Index {
|
|
return _variantBuffer.index(after: i)
|
|
}
|
|
|
|
// APINAMING: complexity docs are broadly missing in this file.
|
|
|
|
/// Returns the index of the given element in the set, or `nil` if the
|
|
/// element is not a member of the set.
|
|
///
|
|
/// - Parameter member: An element to search for in the set.
|
|
/// - Returns: The index of `member` if it exists in the set; otherwise,
|
|
/// `nil`.
|
|
///
|
|
/// - Complexity: O(1)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func firstIndex(of member: Element) -> Index? {
|
|
return _variantBuffer.index(forKey: member)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func _customIndexOfEquatableElement(
|
|
_ member: Element
|
|
) -> Index?? {
|
|
return Optional(firstIndex(of: member))
|
|
}
|
|
|
|
/// The number of elements in the set.
|
|
///
|
|
/// - Complexity: O(1).
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var count: Int {
|
|
return _variantBuffer.count
|
|
}
|
|
|
|
/// A Boolean value that indicates whether the set is empty.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var isEmpty: Bool {
|
|
return count == 0
|
|
}
|
|
|
|
/// The first element of the set.
|
|
///
|
|
/// The first element of the set is not necessarily the first element added
|
|
/// to the set. Don't expect any particular ordering of set elements.
|
|
///
|
|
/// If the set is empty, the value of this property is `nil`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var first: Element? {
|
|
return count > 0 ? self[startIndex] : nil
|
|
}
|
|
}
|
|
|
|
/// Check for both subset and equality relationship between
|
|
/// a set and some sequence (which may itself be a `Set`).
|
|
///
|
|
/// (isSubset: lhs ⊂ rhs, isEqual: lhs ⊂ rhs and |lhs| = |rhs|)
|
|
@inlinable
|
|
internal func _compareSets<Element>(_ lhs: Set<Element>, _ rhs: Set<Element>)
|
|
-> (isSubset: Bool, isEqual: Bool) {
|
|
// FIXME(performance): performance could be better if we start by comparing
|
|
// counts.
|
|
for member in lhs {
|
|
if !rhs.contains(member) {
|
|
return (false, false)
|
|
}
|
|
}
|
|
return (true, lhs.count == rhs.count)
|
|
}
|
|
|
|
// FIXME: rdar://problem/23549059 (Optimize == for Set)
|
|
// Look into initially trying to compare the two sets by directly comparing the
|
|
// contents of both buffers in order. If they happen to have the exact same
|
|
// ordering we can get the `true` response without ever hashing. If the two
|
|
// buffers' contents differ at all then we have to fall back to hashing the
|
|
// rest of the elements (but we don't need to hash any prefix that did match).
|
|
extension Set: Equatable {
|
|
/// Returns a Boolean value indicating whether two sets have equal elements.
|
|
///
|
|
/// - Parameters:
|
|
/// - lhs: A set.
|
|
/// - rhs: Another set.
|
|
/// - Returns: `true` if the `lhs` and `rhs` have the same elements; otherwise,
|
|
/// `false`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public static func == (lhs: Set<Element>, rhs: Set<Element>) -> Bool {
|
|
switch (lhs._variantBuffer, rhs._variantBuffer) {
|
|
case (.native(let lhsNative), .native(let rhsNative)):
|
|
|
|
if lhsNative._storage === rhsNative._storage {
|
|
return true
|
|
}
|
|
|
|
if lhsNative.count != rhsNative.count {
|
|
return false
|
|
}
|
|
|
|
for member in lhs {
|
|
let (_, found) =
|
|
rhsNative._find(member, startBucket: rhsNative._bucket(member))
|
|
if !found {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
|
|
#if _runtime(_ObjC)
|
|
case (_VariantSetBuffer.cocoa(let lhsCocoa),
|
|
_VariantSetBuffer.cocoa(let rhsCocoa)):
|
|
return _stdlib_NSObject_isEqual(lhsCocoa.cocoaSet, rhsCocoa.cocoaSet)
|
|
|
|
case (_VariantSetBuffer.native(let lhsNative),
|
|
_VariantSetBuffer.cocoa(let rhsCocoa)):
|
|
|
|
if lhsNative.count != rhsCocoa.count {
|
|
return false
|
|
}
|
|
|
|
let endIndex = lhsNative.endIndex
|
|
var i = lhsNative.startIndex
|
|
while i != endIndex {
|
|
let key = lhsNative.assertingGet(i)
|
|
let bridgedKey: AnyObject = _bridgeAnythingToObjectiveC(key)
|
|
let optRhsValue: AnyObject? = rhsCocoa.maybeGet(bridgedKey)
|
|
if let rhsValue = optRhsValue {
|
|
if key == _forceBridgeFromObjectiveC(rhsValue, Element.self) {
|
|
i = lhsNative.index(after: i)
|
|
continue
|
|
}
|
|
}
|
|
i = lhsNative.index(after: i)
|
|
return false
|
|
}
|
|
return true
|
|
|
|
case (_VariantSetBuffer.cocoa, _VariantSetBuffer.native):
|
|
return rhs == lhs
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Set: Hashable {
|
|
/// The hash value for the set.
|
|
///
|
|
/// Two sets that are equal will always have equal hash values.
|
|
///
|
|
/// Hash values are not guaranteed to be equal across different executions of
|
|
/// your program. Do not save hash values to use during a future execution.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var hashValue: Int {
|
|
// FIXME(ABI)#177: <rdar://problem/18915294> Cache Set<T> hashValue
|
|
return _hashValue(for: self)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func _hash(into hasher: inout _Hasher) {
|
|
var hash = 0
|
|
for member in self {
|
|
hash ^= _hashValue(for: member)
|
|
}
|
|
hasher.combine(hash)
|
|
}
|
|
}
|
|
|
|
extension Set: SetAlgebra {
|
|
|
|
/// Inserts the given element in the set if it is not already present.
|
|
///
|
|
/// If an element equal to `newMember` is already contained in the set, this
|
|
/// method has no effect. In the following example, a new element is
|
|
/// inserted into `classDays`, a set of days of the week. When an existing
|
|
/// element is inserted, the `classDays` set does not change.
|
|
///
|
|
/// enum DayOfTheWeek: Int {
|
|
/// case sunday, monday, tuesday, wednesday, thursday,
|
|
/// friday, saturday
|
|
/// }
|
|
///
|
|
/// var classDays: Set<DayOfTheWeek> = [.wednesday, .friday]
|
|
/// print(classDays.insert(.monday))
|
|
/// // Prints "(true, .monday)"
|
|
/// print(classDays)
|
|
/// // Prints "[.friday, .wednesday, .monday]"
|
|
///
|
|
/// print(classDays.insert(.friday))
|
|
/// // Prints "(false, .friday)"
|
|
/// print(classDays)
|
|
/// // Prints "[.friday, .wednesday, .monday]"
|
|
///
|
|
/// - Parameter newMember: An element to insert into the set.
|
|
/// - Returns: `(true, newMember)` if `newMember` was not contained in the
|
|
/// set. If an element equal to `newMember` was already contained in the
|
|
/// set, the method returns `(false, oldMember)`, where `oldMember` is the
|
|
/// element that was equal to `newMember`. In some cases, `oldMember` may
|
|
/// be distinguishable from `newMember` by identity comparison or some
|
|
/// other means.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
public mutating func insert(
|
|
_ newMember: Element
|
|
) -> (inserted: Bool, memberAfterInsert: Element) {
|
|
return _variantBuffer.insert(newMember, forKey: newMember)
|
|
}
|
|
|
|
/// Inserts the given element into the set unconditionally.
|
|
///
|
|
/// If an element equal to `newMember` is already contained in the set,
|
|
/// `newMember` replaces the existing element. In this example, an existing
|
|
/// element is inserted into `classDays`, a set of days of the week.
|
|
///
|
|
/// enum DayOfTheWeek: Int {
|
|
/// case sunday, monday, tuesday, wednesday, thursday,
|
|
/// friday, saturday
|
|
/// }
|
|
///
|
|
/// var classDays: Set<DayOfTheWeek> = [.monday, .wednesday, .friday]
|
|
/// print(classDays.update(with: .monday))
|
|
/// // Prints "Optional(.monday)"
|
|
///
|
|
/// - Parameter newMember: An element to insert into the set.
|
|
/// - Returns: An element equal to `newMember` if the set already contained
|
|
/// such a member; otherwise, `nil`. In some cases, the returned element
|
|
/// may be distinguishable from `newMember` by identity comparison or some
|
|
/// other means.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
public mutating func update(with newMember: Element) -> Element? {
|
|
return _variantBuffer.updateValue(newMember, forKey: newMember)
|
|
}
|
|
|
|
/// Removes the specified element from the set.
|
|
///
|
|
/// This example removes the element `"sugar"` from a set of ingredients.
|
|
///
|
|
/// var ingredients: Set = ["cocoa beans", "sugar", "cocoa butter", "salt"]
|
|
/// let toRemove = "sugar"
|
|
/// if let removed = ingredients.remove(toRemove) {
|
|
/// print("The recipe is now \(removed)-free.")
|
|
/// }
|
|
/// // Prints "The recipe is now sugar-free."
|
|
///
|
|
/// - Parameter member: The element to remove from the set.
|
|
/// - Returns: The value of the `member` parameter if it was a member of the
|
|
/// set; otherwise, `nil`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
public mutating func remove(_ member: Element) -> Element? {
|
|
return _variantBuffer.removeValue(forKey: member)
|
|
}
|
|
|
|
/// Removes the element at the given index of the set.
|
|
///
|
|
/// - Parameter position: The index of the member to remove. `position` must
|
|
/// be a valid index of the set, and must not be equal to the set's end
|
|
/// index.
|
|
/// - Returns: The element that was removed from the set.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
public mutating func remove(at position: Index) -> Element {
|
|
return _variantBuffer.remove(at: position)
|
|
}
|
|
|
|
/// Removes all members from the set.
|
|
///
|
|
/// - Parameter keepingCapacity: If `true`, the set's buffer capacity is
|
|
/// preserved; if `false`, the underlying buffer is released. The
|
|
/// default is `false`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) {
|
|
_variantBuffer.removeAll(keepingCapacity: keepCapacity)
|
|
}
|
|
|
|
/// Removes the first element of the set.
|
|
///
|
|
/// Because a set is not an ordered collection, the "first" element may not
|
|
/// be the first element that was added to the set. The set must not be
|
|
/// empty.
|
|
///
|
|
/// - Complexity: Amortized O(1) if the set does not wrap a bridged `NSSet`.
|
|
/// If the set wraps a bridged `NSSet`, the performance is unspecified.
|
|
///
|
|
/// - Returns: A member of the set.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
public mutating func removeFirst() -> Element {
|
|
_precondition(!isEmpty, "Can't removeFirst from an empty Set")
|
|
return remove(at: startIndex)
|
|
}
|
|
|
|
//
|
|
// APIs below this comment should be implemented strictly in terms of
|
|
// *public* APIs above. `_variantBuffer` should not be accessed directly.
|
|
//
|
|
// This separates concerns for testing. Tests for the following APIs need
|
|
// not to concern themselves with testing correctness of behavior of
|
|
// underlying buffer (and different variants of it), only correctness of the
|
|
// API itself.
|
|
//
|
|
|
|
/// Creates an empty set.
|
|
///
|
|
/// This is equivalent to initializing with an empty array literal. For
|
|
/// example:
|
|
///
|
|
/// var emptySet = Set<Int>()
|
|
/// print(emptySet.isEmpty)
|
|
/// // Prints "true"
|
|
///
|
|
/// emptySet = []
|
|
/// print(emptySet.isEmpty)
|
|
/// // Prints "true"
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public init() {
|
|
self = Set<Element>(_nativeBuffer: _NativeBuffer())
|
|
}
|
|
|
|
/// Creates a new set from a finite sequence of items.
|
|
///
|
|
/// Use this initializer to create a new set from an existing sequence, for
|
|
/// example, an array or a range.
|
|
///
|
|
/// let validIndices = Set(0..<7).subtracting([2, 4, 5])
|
|
/// print(validIndices)
|
|
/// // Prints "[6, 0, 1, 3]"
|
|
///
|
|
/// This initializer can also be used to restore set methods after performing
|
|
/// sequence operations such as `filter(_:)` or `map(_:)` on a set. For
|
|
/// example, after filtering a set of prime numbers to remove any below 10,
|
|
/// you can create a new set by using this initializer.
|
|
///
|
|
/// let primes: Set = [2, 3, 5, 7, 11, 13, 17, 19, 23]
|
|
/// let laterPrimes = Set(primes.lazy.filter { $0 > 10 })
|
|
/// print(laterPrimes)
|
|
/// // Prints "[17, 19, 23, 11, 13]"
|
|
///
|
|
/// - Parameter sequence: The elements to use as members of the new set.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public init<Source: Sequence>(_ sequence: Source)
|
|
where Source.Element == Element {
|
|
self.init(minimumCapacity: sequence.underestimatedCount)
|
|
if let s = sequence as? Set<Element> {
|
|
// If this sequence is actually a native `Set`, then we can quickly
|
|
// adopt its native buffer and let COW handle uniquing only
|
|
// if necessary.
|
|
switch s._variantBuffer {
|
|
case .native(let buffer):
|
|
_variantBuffer = .native(buffer)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let owner):
|
|
_variantBuffer = .cocoa(owner)
|
|
#endif
|
|
}
|
|
} else {
|
|
for item in sequence {
|
|
insert(item)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether the set is a subset of the
|
|
/// given sequence.
|
|
///
|
|
/// Set *A* is a subset of another set *B* if every member of *A* is also a
|
|
/// member of *B*.
|
|
///
|
|
/// let employees = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let attendees: Set = ["Alicia", "Bethany", "Diana"]
|
|
/// print(attendees.isSubset(of: employees))
|
|
/// // Prints "true"
|
|
///
|
|
/// - Parameter possibleSuperset: A sequence of elements. `possibleSuperset`
|
|
/// must be finite.
|
|
/// - Returns: `true` if the set is a subset of `possibleSuperset`;
|
|
/// otherwise, `false`.
|
|
@inlinable
|
|
public func isSubset<S: Sequence>(of possibleSuperset: S) -> Bool
|
|
where S.Element == Element {
|
|
// FIXME(performance): isEmpty fast path, here and elsewhere.
|
|
let other = Set(possibleSuperset)
|
|
return isSubset(of: other)
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether the set is a strict subset
|
|
/// of the given sequence.
|
|
///
|
|
/// Set *A* is a strict subset of another set *B* if every member of *A* is
|
|
/// also a member of *B* and *B* contains at least one element that is not a
|
|
/// member of *A*.
|
|
///
|
|
/// let employees = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let attendees: Set = ["Alicia", "Bethany", "Diana"]
|
|
/// print(attendees.isStrictSubset(of: employees))
|
|
/// // Prints "true"
|
|
///
|
|
/// // A set is never a strict subset of itself:
|
|
/// print(attendees.isStrictSubset(of: attendees))
|
|
/// // Prints "false"
|
|
///
|
|
/// - Parameter possibleStrictSuperset: A sequence of elements.
|
|
/// `possibleStrictSuperset` must be finite.
|
|
/// - Returns: `true` is the set is strict subset of
|
|
/// `possibleStrictSuperset`; otherwise, `false`.
|
|
@inlinable
|
|
public func isStrictSubset<S: Sequence>(of possibleStrictSuperset: S) -> Bool
|
|
where S.Element == Element {
|
|
// FIXME: code duplication.
|
|
let other = Set(possibleStrictSuperset)
|
|
return isStrictSubset(of: other)
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether the set is a superset of
|
|
/// the given sequence.
|
|
///
|
|
/// Set *A* is a superset of another set *B* if every member of *B* is also a
|
|
/// member of *A*.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let attendees = ["Alicia", "Bethany", "Diana"]
|
|
/// print(employees.isSuperset(of: attendees))
|
|
/// // Prints "true"
|
|
///
|
|
/// - Parameter possibleSubset: A sequence of elements. `possibleSubset` must
|
|
/// be finite.
|
|
/// - Returns: `true` if the set is a superset of `possibleSubset`;
|
|
/// otherwise, `false`.
|
|
@inlinable
|
|
public func isSuperset<S: Sequence>(of possibleSubset: S) -> Bool
|
|
where S.Element == Element {
|
|
// FIXME(performance): Don't build a set; just ask if every element is in
|
|
// `self`.
|
|
let other = Set(possibleSubset)
|
|
return other.isSubset(of: self)
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether the set is a strict
|
|
/// superset of the given sequence.
|
|
///
|
|
/// Set *A* is a strict superset of another set *B* if every member of *B* is
|
|
/// also a member of *A* and *A* contains at least one element that is *not*
|
|
/// a member of *B*.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let attendees = ["Alicia", "Bethany", "Diana"]
|
|
/// print(employees.isStrictSuperset(of: attendees))
|
|
/// // Prints "true"
|
|
/// print(employees.isStrictSuperset(of: employees))
|
|
/// // Prints "false"
|
|
///
|
|
/// - Parameter possibleStrictSubset: A sequence of elements.
|
|
/// `possibleStrictSubset` must be finite.
|
|
/// - Returns: `true` if the set is a strict superset of
|
|
/// `possibleStrictSubset`; otherwise, `false`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func isStrictSuperset<S: Sequence>(of possibleStrictSubset: S) -> Bool
|
|
where S.Element == Element {
|
|
let other = Set(possibleStrictSubset)
|
|
return other.isStrictSubset(of: self)
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether the set has no members in
|
|
/// common with the given sequence.
|
|
///
|
|
/// In the following example, the `employees` set is disjoint with the
|
|
/// elements of the `visitors` array because no name appears in both.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let visitors = ["Marcia", "Nathaniel", "Olivia"]
|
|
/// print(employees.isDisjoint(with: visitors))
|
|
/// // Prints "true"
|
|
///
|
|
/// - Parameter other: A sequence of elements. `other` must be finite.
|
|
/// - Returns: `true` if the set has no elements in common with `other`;
|
|
/// otherwise, `false`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func isDisjoint<S: Sequence>(with other: S) -> Bool
|
|
where S.Element == Element {
|
|
// FIXME(performance): Don't need to build a set.
|
|
let otherSet = Set(other)
|
|
return isDisjoint(with: otherSet)
|
|
}
|
|
|
|
/// Returns a new set with the elements of both this set and the given
|
|
/// sequence.
|
|
///
|
|
/// In the following example, the `attendeesAndVisitors` set is made up
|
|
/// of the elements of the `attendees` set and the `visitors` array:
|
|
///
|
|
/// let attendees: Set = ["Alicia", "Bethany", "Diana"]
|
|
/// let visitors = ["Marcia", "Nathaniel"]
|
|
/// let attendeesAndVisitors = attendees.union(visitors)
|
|
/// print(attendeesAndVisitors)
|
|
/// // Prints "["Diana", "Nathaniel", "Bethany", "Alicia", "Marcia"]"
|
|
///
|
|
/// If the set already contains one or more elements that are also in
|
|
/// `other`, the existing members are kept. If `other` contains multiple
|
|
/// instances of equivalent elements, only the first instance is kept.
|
|
///
|
|
/// let initialIndices = Set(0..<5)
|
|
/// let expandedIndices = initialIndices.union([2, 3, 6, 6, 7, 7])
|
|
/// print(expandedIndices)
|
|
/// // Prints "[2, 4, 6, 7, 0, 1, 3]"
|
|
///
|
|
/// - Parameter other: A sequence of elements. `other` must be finite.
|
|
/// - Returns: A new set with the unique elements of this set and `other`.
|
|
@inlinable
|
|
public func union<S: Sequence>(_ other: S) -> Set<Element>
|
|
where S.Element == Element {
|
|
var newSet = self
|
|
newSet.formUnion(other)
|
|
return newSet
|
|
}
|
|
|
|
/// Inserts the elements of the given sequence into the set.
|
|
///
|
|
/// If the set already contains one or more elements that are also in
|
|
/// `other`, the existing members are kept. If `other` contains multiple
|
|
/// instances of equivalent elements, only the first instance is kept.
|
|
///
|
|
/// var attendees: Set = ["Alicia", "Bethany", "Diana"]
|
|
/// let visitors = ["Diana", "Marcia", "Nathaniel"]
|
|
/// attendees.formUnion(visitors)
|
|
/// print(attendees)
|
|
/// // Prints "["Diana", "Nathaniel", "Bethany", "Alicia", "Marcia"]"
|
|
///
|
|
/// - Parameter other: A sequence of elements. `other` must be finite.
|
|
@inlinable
|
|
public mutating func formUnion<S: Sequence>(_ other: S)
|
|
where S.Element == Element {
|
|
for item in other {
|
|
insert(item)
|
|
}
|
|
}
|
|
|
|
/// Returns a new set containing the elements of this set that do not occur
|
|
/// in the given sequence.
|
|
///
|
|
/// In the following example, the `nonNeighbors` set is made up of the
|
|
/// elements of the `employees` set that are not elements of `neighbors`:
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let neighbors = ["Bethany", "Eric", "Forlani", "Greta"]
|
|
/// let nonNeighbors = employees.subtracting(neighbors)
|
|
/// print(nonNeighbors)
|
|
/// // Prints "["Chris", "Diana", "Alicia"]"
|
|
///
|
|
/// - Parameter other: A sequence of elements. `other` must be finite.
|
|
/// - Returns: A new set.
|
|
@inlinable
|
|
public func subtracting<S: Sequence>(_ other: S) -> Set<Element>
|
|
where S.Element == Element {
|
|
return self._subtracting(other)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func _subtracting<S: Sequence>(_ other: S) -> Set<Element>
|
|
where S.Element == Element {
|
|
var newSet = self
|
|
newSet.subtract(other)
|
|
return newSet
|
|
}
|
|
|
|
/// Removes the elements of the given sequence from the set.
|
|
///
|
|
/// In the following example, the elements of the `employees` set that are
|
|
/// also elements of the `neighbors` array are removed. In particular, the
|
|
/// names `"Bethany"` and `"Eric"` are removed from `employees`.
|
|
///
|
|
/// var employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let neighbors = ["Bethany", "Eric", "Forlani", "Greta"]
|
|
/// employees.subtract(neighbors)
|
|
/// print(employees)
|
|
/// // Prints "["Chris", "Diana", "Alicia"]"
|
|
///
|
|
/// - Parameter other: A sequence of elements. `other` must be finite.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public mutating func subtract<S: Sequence>(_ other: S)
|
|
where S.Element == Element {
|
|
_subtract(other)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func _subtract<S: Sequence>(_ other: S)
|
|
where S.Element == Element {
|
|
for item in other {
|
|
remove(item)
|
|
}
|
|
}
|
|
|
|
/// Returns a new set with the elements that are common to both this set and
|
|
/// the given sequence.
|
|
///
|
|
/// In the following example, the `bothNeighborsAndEmployees` set is made up
|
|
/// of the elements that are in *both* the `employees` and `neighbors` sets.
|
|
/// Elements that are in only one or the other are left out of the result of
|
|
/// the intersection.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let neighbors = ["Bethany", "Eric", "Forlani", "Greta"]
|
|
/// let bothNeighborsAndEmployees = employees.intersection(neighbors)
|
|
/// print(bothNeighborsAndEmployees)
|
|
/// // Prints "["Bethany", "Eric"]"
|
|
///
|
|
/// - Parameter other: A sequence of elements. `other` must be finite.
|
|
/// - Returns: A new set.
|
|
@inlinable
|
|
public func intersection<S: Sequence>(_ other: S) -> Set<Element>
|
|
where S.Element == Element {
|
|
let otherSet = Set(other)
|
|
return intersection(otherSet)
|
|
}
|
|
|
|
/// Removes the elements of the set that aren't also in the given sequence.
|
|
///
|
|
/// In the following example, the elements of the `employees` set that are
|
|
/// not also members of the `neighbors` set are removed. In particular, the
|
|
/// names `"Alicia"`, `"Chris"`, and `"Diana"` are removed.
|
|
///
|
|
/// var employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let neighbors = ["Bethany", "Eric", "Forlani", "Greta"]
|
|
/// employees.formIntersection(neighbors)
|
|
/// print(employees)
|
|
/// // Prints "["Bethany", "Eric"]"
|
|
///
|
|
/// - Parameter other: A sequence of elements. `other` must be finite.
|
|
@inlinable
|
|
public mutating func formIntersection<S: Sequence>(_ other: S)
|
|
where S.Element == Element {
|
|
// Because `intersect` needs to both modify and iterate over
|
|
// the left-hand side, the index may become invalidated during
|
|
// traversal so an intermediate set must be created.
|
|
//
|
|
// FIXME(performance): perform this operation at a lower level
|
|
// to avoid invalidating the index and avoiding a copy.
|
|
let result = self.intersection(other)
|
|
|
|
// The result can only have fewer or the same number of elements.
|
|
// If no elements were removed, don't perform a reassignment
|
|
// as this may cause an unnecessary uniquing COW.
|
|
if result.count != count {
|
|
self = result
|
|
}
|
|
}
|
|
|
|
/// Returns a new set with the elements that are either in this set or in the
|
|
/// given sequence, but not in both.
|
|
///
|
|
/// In the following example, the `eitherNeighborsOrEmployees` set is made up
|
|
/// of the elements of the `employees` and `neighbors` sets that are not in
|
|
/// both `employees` *and* `neighbors`. In particular, the names `"Bethany"`
|
|
/// and `"Eric"` do not appear in `eitherNeighborsOrEmployees`.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Diana", "Eric"]
|
|
/// let neighbors = ["Bethany", "Eric", "Forlani"]
|
|
/// let eitherNeighborsOrEmployees = employees.symmetricDifference(neighbors)
|
|
/// print(eitherNeighborsOrEmployees)
|
|
/// // Prints "["Diana", "Forlani", "Alicia"]"
|
|
///
|
|
/// - Parameter other: A sequence of elements. `other` must be finite.
|
|
/// - Returns: A new set.
|
|
@inlinable
|
|
public func symmetricDifference<S: Sequence>(_ other: S) -> Set<Element>
|
|
where S.Element == Element {
|
|
var newSet = self
|
|
newSet.formSymmetricDifference(other)
|
|
return newSet
|
|
}
|
|
|
|
/// Replace this set with the elements contained in this set or the given
|
|
/// set, but not both.
|
|
///
|
|
/// In the following example, the elements of the `employees` set that are
|
|
/// also members of `neighbors` are removed from `employees`, while the
|
|
/// elements of `neighbors` that are not members of `employees` are added to
|
|
/// `employees`. In particular, the names `"Bethany"` and `"Eric"` are
|
|
/// removed from `employees` while the name `"Forlani"` is added.
|
|
///
|
|
/// var employees: Set = ["Alicia", "Bethany", "Diana", "Eric"]
|
|
/// let neighbors = ["Bethany", "Eric", "Forlani"]
|
|
/// employees.formSymmetricDifference(neighbors)
|
|
/// print(employees)
|
|
/// // Prints "["Diana", "Forlani", "Alicia"]"
|
|
///
|
|
/// - Parameter other: A sequence of elements. `other` must be finite.
|
|
@inlinable
|
|
public mutating func formSymmetricDifference<S: Sequence>(_ other: S)
|
|
where S.Element == Element {
|
|
let otherSet = Set(other)
|
|
formSymmetricDifference(otherSet)
|
|
}
|
|
}
|
|
|
|
extension Set: CustomStringConvertible, CustomDebugStringConvertible {
|
|
/// A string that represents the contents of the set.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var description: String {
|
|
return _makeCollectionDescription(for: self, withTypeName: nil)
|
|
}
|
|
|
|
/// A string that represents the contents of the set, suitable for debugging.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var debugDescription: String {
|
|
return _makeCollectionDescription(for: self, withTypeName: "Set")
|
|
}
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
@_silgen_name("swift_stdlib_CFSetGetValues")
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal
|
|
func _stdlib_CFSetGetValues(_ nss: _NSSet, _: UnsafeMutablePointer<AnyObject>)
|
|
|
|
/// Equivalent to `NSSet.allObjects`, but does not leave objects on the
|
|
/// autorelease pool.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
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
|
|
}
|
|
#endif
|
|
|
|
//===--- 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 // FIXME(sil-serialize-all)
|
|
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()
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
|
|
/// Implements an unconditional upcast that involves bridging.
|
|
///
|
|
/// The cast can fail if bridging fails.
|
|
///
|
|
/// - Precondition: `SwiftValue` is bridged to Objective-C
|
|
/// and requires non-trivial bridging.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func _setBridgeToObjectiveC<SwiftValue, ObjCValue>(
|
|
_ source: Set<SwiftValue>
|
|
) -> Set<ObjCValue> {
|
|
_sanityCheck(_isClassOrObjCExistential(ObjCValue.self))
|
|
_sanityCheck(!_isBridgedVerbatimToObjectiveC(SwiftValue.self))
|
|
|
|
var result = Set<ObjCValue>(minimumCapacity: source.count)
|
|
let valueBridgesDirectly =
|
|
_isBridgedVerbatimToObjectiveC(SwiftValue.self) ==
|
|
_isBridgedVerbatimToObjectiveC(ObjCValue.self)
|
|
|
|
for member in source {
|
|
var bridgedMember: ObjCValue
|
|
if valueBridgesDirectly {
|
|
bridgedMember = unsafeBitCast(member, to: ObjCValue.self)
|
|
} else {
|
|
let bridged: AnyObject = _bridgeAnythingToObjectiveC(member)
|
|
bridgedMember = unsafeBitCast(bridged, to: ObjCValue.self)
|
|
}
|
|
result.insert(bridgedMember)
|
|
}
|
|
return result
|
|
}
|
|
#endif
|
|
|
|
/// 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 // FIXME(sil-serialize-all)
|
|
public func _setDownCast<BaseValue, DerivedValue>(_ source: Set<BaseValue>)
|
|
-> Set<DerivedValue> {
|
|
|
|
#if _runtime(_ObjC)
|
|
if _isClassOrObjCExistential(BaseValue.self)
|
|
&& _isClassOrObjCExistential(DerivedValue.self) {
|
|
switch source._variantBuffer {
|
|
case _VariantSetBuffer.native(let buffer):
|
|
return Set(_immutableCocoaSet: buffer.bridged())
|
|
case _VariantSetBuffer.cocoa(let cocoaBuffer):
|
|
return Set(_immutableCocoaSet: cocoaBuffer.cocoaSet)
|
|
}
|
|
}
|
|
#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 // FIXME(sil-serialize-all)
|
|
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
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
|
|
/// Implements an unconditional downcast that involves bridging.
|
|
///
|
|
/// - Precondition: At least one of `SwiftValue` is a bridged value
|
|
/// type, and the corresponding `ObjCValue` is a reference type.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func _setBridgeFromObjectiveC<ObjCValue, SwiftValue>(
|
|
_ source: Set<ObjCValue>
|
|
) -> Set<SwiftValue> {
|
|
let result: Set<SwiftValue>? = _setBridgeFromObjectiveCConditional(source)
|
|
_precondition(result != nil, "This set cannot be bridged from Objective-C")
|
|
return result!
|
|
}
|
|
|
|
/// Implements a conditional downcast that involves bridging.
|
|
///
|
|
/// If the cast fails, the function returns `nil`. All checks should be
|
|
/// performed eagerly.
|
|
///
|
|
/// - Precondition: At least one of `SwiftValue` is a bridged value
|
|
/// type, and the corresponding `ObjCValue` is a reference type.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func _setBridgeFromObjectiveCConditional<
|
|
ObjCValue, SwiftValue
|
|
>(
|
|
_ source: Set<ObjCValue>
|
|
) -> Set<SwiftValue>? {
|
|
_sanityCheck(_isClassOrObjCExistential(ObjCValue.self))
|
|
_sanityCheck(!_isBridgedVerbatimToObjectiveC(SwiftValue.self))
|
|
|
|
let valueBridgesDirectly =
|
|
_isBridgedVerbatimToObjectiveC(SwiftValue.self) ==
|
|
_isBridgedVerbatimToObjectiveC(ObjCValue.self)
|
|
|
|
var result = Set<SwiftValue>(minimumCapacity: source.count)
|
|
for value in source {
|
|
// Downcast the value.
|
|
var resultValue: SwiftValue
|
|
if valueBridgesDirectly {
|
|
if let bridgedValue = value as? SwiftValue {
|
|
resultValue = bridgedValue
|
|
} else {
|
|
return nil
|
|
}
|
|
} else {
|
|
if let bridgedValue = _conditionallyBridgeFromObjectiveC(
|
|
_reinterpretCastToAnyObject(value), SwiftValue.self) {
|
|
resultValue = bridgedValue
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
result.insert(resultValue)
|
|
}
|
|
return result
|
|
}
|
|
#endif
|
|
|
|
extension Set {
|
|
/// Removes the elements of the given set from this set.
|
|
///
|
|
/// In the following example, the elements of the `employees` set that are
|
|
/// also members of the `neighbors` set are removed. In particular, the
|
|
/// names `"Bethany"` and `"Eric"` are removed from `employees`.
|
|
///
|
|
/// var employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let neighbors: Set = ["Bethany", "Eric", "Forlani", "Greta"]
|
|
/// employees.subtract(neighbors)
|
|
/// print(employees)
|
|
/// // Prints "["Diana", "Chris", "Alicia"]"
|
|
///
|
|
/// - Parameter other: Another set.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public mutating func subtract(_ other: Set<Element>) {
|
|
_subtract(other)
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether this set is a subset of
|
|
/// the given set.
|
|
///
|
|
/// Set *A* is a subset of another set *B* if every member of *A* is also a
|
|
/// member of *B*.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let attendees: Set = ["Alicia", "Bethany", "Diana"]
|
|
/// print(attendees.isSubset(of: employees))
|
|
/// // Prints "true"
|
|
///
|
|
/// - Parameter other: Another set.
|
|
/// - Returns: `true` if the set is a subset of `other`; otherwise, `false`.
|
|
@inlinable
|
|
public func isSubset(of other: Set<Element>) -> Bool {
|
|
let (isSubset, isEqual) = _compareSets(self, other)
|
|
return isSubset || isEqual
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether this set is a superset of
|
|
/// the given set.
|
|
///
|
|
/// Set *A* is a superset of another set *B* if every member of *B* is also a
|
|
/// member of *A*.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let attendees: Set = ["Alicia", "Bethany", "Diana"]
|
|
/// print(employees.isSuperset(of: attendees))
|
|
/// // Prints "true"
|
|
///
|
|
/// - Parameter other: Another set.
|
|
/// - Returns: `true` if the set is a superset of `other`; otherwise,
|
|
/// `false`.
|
|
@inlinable
|
|
public func isSuperset(of other: Set<Element>) -> Bool {
|
|
return other.isSubset(of: self)
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether this set has no members in
|
|
/// common with the given set.
|
|
///
|
|
/// In the following example, the `employees` set is disjoint with the
|
|
/// `visitors` set because no name appears in both sets.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let visitors: Set = ["Marcia", "Nathaniel", "Olivia"]
|
|
/// print(employees.isDisjoint(with: visitors))
|
|
/// // Prints "true"
|
|
///
|
|
/// - Parameter other: Another set.
|
|
/// - Returns: `true` if the set has no elements in common with `other`;
|
|
/// otherwise, `false`.
|
|
@inlinable
|
|
public func isDisjoint(with other: Set<Element>) -> Bool {
|
|
for member in self {
|
|
if other.contains(member) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
/// Returns a new set containing the elements of this set that do not occur
|
|
/// in the given set.
|
|
///
|
|
/// In the following example, the `nonNeighbors` set is made up of the
|
|
/// elements of the `employees` set that are not elements of `neighbors`:
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let neighbors: Set = ["Bethany", "Eric", "Forlani", "Greta"]
|
|
/// let nonNeighbors = employees.subtracting(neighbors)
|
|
/// print(nonNeighbors)
|
|
/// // Prints "["Diana", "Chris", "Alicia"]"
|
|
///
|
|
/// - Parameter other: Another set.
|
|
/// - Returns: A new set.
|
|
@inlinable
|
|
public func subtracting(_ other: Set<Element>) -> Set<Element> {
|
|
return self._subtracting(other)
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether the set is a strict
|
|
/// superset of the given sequence.
|
|
///
|
|
/// Set *A* is a strict superset of another set *B* if every member of *B* is
|
|
/// also a member of *A* and *A* contains at least one element that is *not*
|
|
/// a member of *B*.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let attendees: Set = ["Alicia", "Bethany", "Diana"]
|
|
/// print(employees.isStrictSuperset(of: attendees))
|
|
/// // Prints "true"
|
|
/// print(employees.isStrictSuperset(of: employees))
|
|
/// // Prints "false"
|
|
///
|
|
/// - Parameter other: Another set.
|
|
/// - Returns: `true` if the set is a strict superset of
|
|
/// `other`; otherwise, `false`.
|
|
@inlinable
|
|
public func isStrictSuperset(of other: Set<Element>) -> Bool {
|
|
return self.isSuperset(of: other) && self != other
|
|
}
|
|
|
|
/// Returns a Boolean value that indicates whether the set is a strict subset
|
|
/// of the given sequence.
|
|
///
|
|
/// Set *A* is a strict subset of another set *B* if every member of *A* is
|
|
/// also a member of *B* and *B* contains at least one element that is not a
|
|
/// member of *A*.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let attendees: Set = ["Alicia", "Bethany", "Diana"]
|
|
/// print(attendees.isStrictSubset(of: employees))
|
|
/// // Prints "true"
|
|
///
|
|
/// // A set is never a strict subset of itself:
|
|
/// print(attendees.isStrictSubset(of: attendees))
|
|
/// // Prints "false"
|
|
///
|
|
/// - Parameter other: Another set.
|
|
/// - Returns: `true` if the set is a strict subset of
|
|
/// `other`; otherwise, `false`.
|
|
@inlinable
|
|
public func isStrictSubset(of other: Set<Element>) -> Bool {
|
|
return other.isStrictSuperset(of: self)
|
|
}
|
|
|
|
/// Returns a new set with the elements that are common to both this set and
|
|
/// the given sequence.
|
|
///
|
|
/// In the following example, the `bothNeighborsAndEmployees` set is made up
|
|
/// of the elements that are in *both* the `employees` and `neighbors` sets.
|
|
/// Elements that are in only one or the other are left out of the result of
|
|
/// the intersection.
|
|
///
|
|
/// let employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let neighbors: Set = ["Bethany", "Eric", "Forlani", "Greta"]
|
|
/// let bothNeighborsAndEmployees = employees.intersection(neighbors)
|
|
/// print(bothNeighborsAndEmployees)
|
|
/// // Prints "["Bethany", "Eric"]"
|
|
///
|
|
/// - Parameter other: Another set.
|
|
/// - Returns: A new set.
|
|
@inlinable
|
|
public func intersection(_ other: Set<Element>) -> Set<Element> {
|
|
var newSet = Set<Element>()
|
|
for member in self {
|
|
if other.contains(member) {
|
|
newSet.insert(member)
|
|
}
|
|
}
|
|
return newSet
|
|
}
|
|
|
|
/// Removes the elements of the set that are also in the given sequence and
|
|
/// adds the members of the sequence that are not already in the set.
|
|
///
|
|
/// In the following example, the elements of the `employees` set that are
|
|
/// also members of `neighbors` are removed from `employees`, while the
|
|
/// elements of `neighbors` that are not members of `employees` are added to
|
|
/// `employees`. In particular, the names `"Alicia"`, `"Chris"`, and
|
|
/// `"Diana"` are removed from `employees` while the names `"Forlani"` and
|
|
/// `"Greta"` are added.
|
|
///
|
|
/// var employees: Set = ["Alicia", "Bethany", "Chris", "Diana", "Eric"]
|
|
/// let neighbors: Set = ["Bethany", "Eric", "Forlani", "Greta"]
|
|
/// employees.formSymmetricDifference(neighbors)
|
|
/// print(employees)
|
|
/// // Prints "["Diana", "Chris", "Forlani", "Alicia", "Greta"]"
|
|
///
|
|
/// - Parameter other: Another set.
|
|
@inlinable
|
|
public mutating func formSymmetricDifference(_ other: Set<Element>) {
|
|
for member in other {
|
|
if contains(member) {
|
|
remove(member)
|
|
} else {
|
|
insert(member)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//===--- APIs templated for Dictionary and Set ----------------------------===//
|
|
|
|
/// 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 // FIXME(sil-serialize-all)
|
|
@_objc_non_lazy_realization
|
|
internal class _RawNativeSetStorage:
|
|
_SwiftNativeNSSet, _NSSetCore
|
|
{
|
|
internal typealias RawStorage = _RawNativeSetStorage
|
|
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal final var bucketCount: Int
|
|
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal final var count: Int
|
|
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal final var initializedEntries: _UnsafeBitMap
|
|
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal final var keys: UnsafeMutableRawPointer
|
|
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal final var seed: (UInt64, UInt64)
|
|
|
|
// This API is unsafe and needs a `_fixLifetime` in the caller.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal final
|
|
var _initializedHashtableEntriesBitMapBuffer: UnsafeMutablePointer<UInt> {
|
|
return UnsafeMutablePointer(Builtin.projectTailElems(self, UInt.self))
|
|
}
|
|
|
|
/// 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 // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal static var empty: RawStorage {
|
|
return Builtin.bridgeFromRawPointer(
|
|
Builtin.addressof(&_swiftEmptySetStorage))
|
|
}
|
|
|
|
// This type is made with allocWithTailElems, so no init is ever called.
|
|
// But we still need to have an init to satisfy the compiler.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal init(_doNotCallMe: ()) {
|
|
_sanityCheckFailure("Only create this by using the `empty` singleton")
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
//
|
|
// NSSet implementation, assuming Self is the empty singleton
|
|
//
|
|
|
|
/// Get the NSEnumerator implementation for self.
|
|
/// _HashableTypedNativeSetStorage overloads this to give
|
|
/// _NativeSelfNSEnumerator proper type parameters.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal func enumerator() -> _NSEnumerator {
|
|
return _NativeSetNSEnumerator<AnyObject>(
|
|
_NativeSetBuffer(_storage: self))
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc(copyWithZone:)
|
|
internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
|
|
return self
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@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
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal required init(objects: UnsafePointer<AnyObject?>, count: Int) {
|
|
_sanityCheckFailure("don't call this designated initializer")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal func member(_ object: AnyObject) -> AnyObject? {
|
|
return nil
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal func objectEnumerator() -> _NSEnumerator {
|
|
return enumerator()
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// See the docs at the top of this file for a description of this type
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
@usableFromInline
|
|
internal class _TypedNativeSetStorage<Element>: _RawNativeSetStorage {
|
|
|
|
internal typealias Key = Element
|
|
internal typealias Value = Element
|
|
|
|
deinit {
|
|
let keys = self.keys.assumingMemoryBound(to: Key.self)
|
|
|
|
if !_isPOD(Key.self) {
|
|
for i in 0 ..< bucketCount {
|
|
if initializedEntries[i] {
|
|
(keys+i).deinitialize(count: 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
_fixLifetime(self)
|
|
}
|
|
|
|
// This type is made with allocWithTailElems, so no init is ever called.
|
|
// But we still need to have an init to satisfy the compiler.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
override internal init(_doNotCallMe: ()) {
|
|
_sanityCheckFailure("Only create this by calling Buffer's inits")
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal required init(objects: UnsafePointer<AnyObject?>, count: Int) {
|
|
_sanityCheckFailure("don't call this designated initializer")
|
|
}
|
|
#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 _HashableTypedNativeSetStorage<Element: Hashable>
|
|
: _TypedNativeSetStorage<Element> {
|
|
|
|
internal typealias FullContainer = Set<Element>
|
|
internal typealias Buffer = _NativeSetBuffer<Element>
|
|
|
|
// This type is made with allocWithTailElems, so no init is ever called.
|
|
// But we still need to have an init to satisfy the compiler.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
override internal init(_doNotCallMe: ()) {
|
|
_sanityCheckFailure("Only create this by calling Buffer's inits'")
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
// NSSet bridging:
|
|
|
|
// All actual functionality comes from buffer/full, which are
|
|
// just wrappers around a RawNativeSetStorage.
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var buffer: Buffer {
|
|
return Buffer(_storage: self)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var full: FullContainer {
|
|
return FullContainer(_nativeBuffer: buffer)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal override func enumerator() -> _NSEnumerator {
|
|
return _NativeSetNSEnumerator<Element>(
|
|
Buffer(_storage: self))
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc(countByEnumeratingWithState:objects:count:)
|
|
internal override 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(full.startIndex._nativeIndex.offset)
|
|
}
|
|
|
|
// 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 currIndex = _NativeSetIndex<Element>(
|
|
offset: Int(theState.extra.0))
|
|
let endIndex = buffer.endIndex
|
|
var stored = 0
|
|
for i in 0..<count {
|
|
if (currIndex == endIndex) {
|
|
break
|
|
}
|
|
|
|
unmanagedObjects[i] = buffer.bridgedKey(at: currIndex)
|
|
|
|
stored += 1
|
|
buffer.formIndex(after: &currIndex)
|
|
}
|
|
theState.extra.0 = CUnsignedLong(currIndex.offset)
|
|
state.pointee = theState
|
|
return stored
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal func getObjectFor(_ aKey: AnyObject) -> AnyObject? {
|
|
guard let nativeKey = _conditionallyBridgeFromObjectiveC(aKey, Key.self)
|
|
else { return nil }
|
|
|
|
let (i, found) = buffer._find(nativeKey,
|
|
startBucket: buffer._bucket(nativeKey))
|
|
|
|
if found {
|
|
return buffer.bridgedValue(at: i)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal required init(objects: UnsafePointer<AnyObject?>, count: Int) {
|
|
_sanityCheckFailure("don't call this designated initializer")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
override internal func member(_ object: AnyObject) -> AnyObject? {
|
|
return getObjectFor(object)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// A wrapper around _RawNativeSetStorage that provides most of the
|
|
/// implementation of Set.
|
|
///
|
|
/// This type and most of its functionality doesn't require Hashable at all.
|
|
/// The reason for this is to support storing AnyObject for bridging
|
|
/// with _SwiftDeferredNSSet. What functionality actually relies on
|
|
/// Hashable can be found in an extension.
|
|
@usableFromInline
|
|
@_fixed_layout
|
|
internal struct _NativeSetBuffer<Element> {
|
|
|
|
internal typealias RawStorage = _RawNativeSetStorage
|
|
internal typealias TypedStorage = _TypedNativeSetStorage<Element>
|
|
internal typealias Buffer = _NativeSetBuffer<Element>
|
|
internal typealias Index = _NativeSetIndex<Element>
|
|
|
|
internal typealias Key = Element
|
|
internal typealias Value = Element
|
|
internal typealias SequenceElementWithoutLabels = Element
|
|
|
|
/// See this comments on _RawNativeSetStorage and its subclasses to
|
|
/// understand why we store an untyped storage here.
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var _storage: RawStorage
|
|
|
|
/// Creates a Buffer with a storage that is typed, but doesn't understand
|
|
/// Hashing. Mostly for bridging; prefer `init(minimumCapacity:)`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_exactBucketCount bucketCount: Int, unhashable: ()) {
|
|
let bitmapWordCount = _UnsafeBitMap.sizeInWords(forSizeInBits: bucketCount)
|
|
let storage = Builtin.allocWithTailElems_2(TypedStorage.self,
|
|
bitmapWordCount._builtinWordValue, UInt.self,
|
|
bucketCount._builtinWordValue, Key.self)
|
|
self.init(_exactBucketCount: bucketCount, storage: storage)
|
|
}
|
|
|
|
/// Given a bucket count and uninitialized RawStorage, completes the
|
|
/// initialization and returns a Buffer.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_exactBucketCount bucketCount: Int, storage: RawStorage) {
|
|
storage.bucketCount = bucketCount
|
|
storage.count = 0
|
|
|
|
self.init(_storage: storage)
|
|
|
|
let initializedEntries = _UnsafeBitMap(
|
|
storage: _initializedHashtableEntriesBitMapBuffer,
|
|
bitCount: bucketCount)
|
|
initializedEntries.initializeToZero()
|
|
|
|
// Compute all the array offsets now, so we don't have to later
|
|
let bitmapAddr = Builtin.projectTailElems(_storage, UInt.self)
|
|
let bitmapWordCount = _UnsafeBitMap.sizeInWords(forSizeInBits: bucketCount)
|
|
let keysAddr = Builtin.getTailAddr_Word(bitmapAddr,
|
|
bitmapWordCount._builtinWordValue, UInt.self, Key.self)
|
|
|
|
// Initialize header
|
|
_storage.initializedEntries = initializedEntries
|
|
_storage.keys = UnsafeMutableRawPointer(keysAddr)
|
|
// We assign a unique hash seed to each distinct hash table size, so that we
|
|
// avoid certain copy operations becoming quadratic, without breaking value
|
|
// semantics. (See https://bugs.swift.org/browse/SR-3268)
|
|
//
|
|
// We don't need to generate a brand new seed for each table size: it's
|
|
// enough to change a single bit in the global seed by XORing the bucket
|
|
// count to it. (The bucket count is always a power of two.)
|
|
//
|
|
// FIXME: Use an approximation of true per-instance seeding. We can't just
|
|
// use the base address, because COW copies need to share the same seed.
|
|
let seed = _Hasher._seed
|
|
let perturbation = bucketCount
|
|
_storage.seed = (seed.0 ^ UInt64(truncatingIfNeeded: perturbation), seed.1)
|
|
}
|
|
|
|
// Forwarding the individual fields of the storage in various forms
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var bucketCount: Int {
|
|
return _assumeNonNegative(_storage.bucketCount)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var count: Int {
|
|
set {
|
|
_storage.count = newValue
|
|
}
|
|
get {
|
|
return _assumeNonNegative(_storage.count)
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal
|
|
var _initializedHashtableEntriesBitMapBuffer: UnsafeMutablePointer<UInt> {
|
|
return _storage._initializedHashtableEntriesBitMapBuffer
|
|
}
|
|
|
|
// This API is unsafe and needs a `_fixLifetime` in the caller.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var keys: UnsafeMutablePointer<Key> {
|
|
return _storage.keys.assumingMemoryBound(to: Key.self)
|
|
}
|
|
|
|
/// Constructs a buffer adopting the given storage.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_storage: RawStorage) {
|
|
self._storage = _storage
|
|
}
|
|
|
|
/// Constructs an instance from the empty singleton.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init() {
|
|
self._storage = RawStorage.empty
|
|
}
|
|
|
|
// Most of the implementation of the _HashBuffer protocol,
|
|
// but only the parts that don't actually rely on hashing.
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal func key(at i: Int) -> Key {
|
|
_sanityCheck(i >= 0 && i < bucketCount)
|
|
_sanityCheck(isInitializedEntry(at: i))
|
|
defer { _fixLifetime(self) }
|
|
|
|
let res = (keys + i).pointee
|
|
return res
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
/// Returns the key at the given Index, bridged.
|
|
///
|
|
/// Intended for use with verbatim bridgeable keys.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func bridgedKey(at index: Index) -> AnyObject {
|
|
let k = key(at: index.offset)
|
|
return _bridgeAnythingToObjectiveC(k)
|
|
}
|
|
|
|
/// Returns the value at the given Index, bridged.
|
|
///
|
|
/// Intended for use with verbatim bridgeable keys.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func bridgedValue(at index: Index) -> AnyObject {
|
|
let v = value(at: index.offset)
|
|
return _bridgeAnythingToObjectiveC(v)
|
|
}
|
|
#endif
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func isInitializedEntry(at i: Int) -> Bool {
|
|
_sanityCheck(i >= 0 && i < bucketCount)
|
|
defer { _fixLifetime(self) }
|
|
|
|
return _storage.initializedEntries[i]
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal func destroyEntry(at i: Int) {
|
|
_sanityCheck(isInitializedEntry(at: i))
|
|
defer { _fixLifetime(self) }
|
|
|
|
(keys + i).deinitialize(count: 1)
|
|
_storage.initializedEntries[i] = false
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal func initializeKey(_ k: Key, at i: Int) {
|
|
_sanityCheck(!isInitializedEntry(at: i))
|
|
defer { _fixLifetime(self) }
|
|
|
|
(keys + i).initialize(to: k)
|
|
_storage.initializedEntries[i] = true
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal func moveInitializeEntry(from: Buffer, at: Int, toEntryAt: Int) {
|
|
_sanityCheck(!isInitializedEntry(at: toEntryAt))
|
|
|
|
defer { _fixLifetime(self) }
|
|
|
|
(keys + toEntryAt).initialize(to: (from.keys + at).move())
|
|
from._storage.initializedEntries[at] = false
|
|
_storage.initializedEntries[toEntryAt] = true
|
|
}
|
|
|
|
/// Alias for key(at:) in Sets for better code reuse
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal func value(at i: Int) -> Value {
|
|
return key(at: i)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func setKey(_ key: Key, at i: Int) {
|
|
_sanityCheck(i >= 0 && i < bucketCount)
|
|
_sanityCheck(isInitializedEntry(at: i))
|
|
defer { _fixLifetime(self) }
|
|
|
|
(keys + i).pointee = key
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var startIndex: Index {
|
|
// We start at "index after -1" instead of "0" because we need to find the
|
|
// first occupied slot.
|
|
return index(after: Index(offset: -1))
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var endIndex: Index {
|
|
return Index(offset: bucketCount)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func index(after i: Index) -> Index {
|
|
_precondition(i != endIndex)
|
|
var idx = i.offset + 1
|
|
while idx < bucketCount && !isInitializedEntry(at: idx) {
|
|
idx += 1
|
|
}
|
|
|
|
return Index(offset: idx)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func formIndex(after i: inout Index) {
|
|
i = index(after: i)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func assertingGet(_ i: Index) -> SequenceElement {
|
|
_precondition(i.offset >= 0 && i.offset < bucketCount)
|
|
_precondition(
|
|
isInitializedEntry(at: i.offset),
|
|
"Attempting to access Set elements using an invalid Index")
|
|
let key = self.key(at: i.offset)
|
|
return key
|
|
|
|
}
|
|
}
|
|
|
|
extension _NativeSetBuffer where Element: Hashable
|
|
{
|
|
internal typealias HashTypedStorage =
|
|
_HashableTypedNativeSetStorage<Element>
|
|
internal typealias SequenceElement = Element
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal init(minimumCapacity: Int) {
|
|
let bucketCount = _NativeSetBuffer.bucketCount(
|
|
forCapacity: minimumCapacity,
|
|
maxLoadFactorInverse: _hashContainerDefaultMaxLoadFactorInverse)
|
|
self.init(bucketCount: bucketCount)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal init(bucketCount: Int) {
|
|
// Actual bucket count is the next power of 2 greater than or equal to
|
|
// bucketCount. Make sure that is representable.
|
|
_sanityCheck(bucketCount <= (Int.max >> 1) + 1)
|
|
let buckets = 1 &<< ((Swift.max(bucketCount, 2) - 1)._binaryLogarithm() + 1)
|
|
self.init(_exactBucketCount: buckets)
|
|
}
|
|
|
|
/// Create a buffer instance with room for at least 'bucketCount' entries,
|
|
/// marking all entries invalid.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_exactBucketCount bucketCount: Int) {
|
|
let bitmapWordCount = _UnsafeBitMap.sizeInWords(forSizeInBits: bucketCount)
|
|
let storage = Builtin.allocWithTailElems_2(HashTypedStorage.self,
|
|
bitmapWordCount._builtinWordValue, UInt.self,
|
|
bucketCount._builtinWordValue, Key.self)
|
|
self.init(_exactBucketCount: bucketCount, storage: storage)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
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 (_isBridgedVerbatimToObjectiveC(Key.self) &&
|
|
_isBridgedVerbatimToObjectiveC(Value.self)) ||
|
|
self._storage === RawStorage.empty {
|
|
nsSet = self._storage
|
|
} else {
|
|
nsSet = _SwiftDeferredNSSet(nativeBuffer: 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)
|
|
}
|
|
#endif
|
|
|
|
/// A textual representation of `self`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var description: String {
|
|
var result = ""
|
|
#if INTERNAL_CHECKS_ENABLED
|
|
for i in 0..<bucketCount {
|
|
if isInitializedEntry(at: i) {
|
|
let key = self.key(at: i)
|
|
result += "bucket \(i), ideal bucket = \(_bucket(key)), key = \(key)\n"
|
|
} else {
|
|
result += "bucket \(i), empty\n"
|
|
}
|
|
}
|
|
#endif
|
|
return result
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var _bucketMask: Int {
|
|
// The bucket count is not negative, therefore subtracting 1 will not
|
|
// overflow.
|
|
return bucketCount &- 1
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always) // For performance reasons.
|
|
internal func _bucket(_ k: Key) -> Int {
|
|
var hasher = _Hasher(_seed: _storage.seed)
|
|
hasher.combine(k)
|
|
return hasher.finalize() & _bucketMask
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func _index(after bucket: Int) -> Int {
|
|
// Bucket is within 0 and bucketCount. Therefore adding 1 does not overflow.
|
|
return (bucket &+ 1) & _bucketMask
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func _prev(_ bucket: Int) -> Int {
|
|
// Bucket is not negative. Therefore subtracting 1 does not overflow.
|
|
return (bucket &- 1) & _bucketMask
|
|
}
|
|
|
|
/// Search for a given key starting from the specified bucket.
|
|
///
|
|
/// If the key is not present, returns the position where it could be
|
|
/// inserted.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal func _find(_ key: Key, startBucket: Int)
|
|
-> (pos: Index, found: Bool) {
|
|
|
|
var bucket = startBucket
|
|
|
|
// The invariant guarantees there's always a hole, so we just loop
|
|
// until we find one
|
|
while true {
|
|
let isHole = !isInitializedEntry(at: bucket)
|
|
if isHole {
|
|
return (Index(offset: bucket), false)
|
|
}
|
|
if self.key(at: bucket) == key {
|
|
return (Index(offset: bucket), true)
|
|
}
|
|
bucket = _index(after: bucket)
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal static func bucketCount(
|
|
forCapacity capacity: Int,
|
|
maxLoadFactorInverse: Double
|
|
) -> Int {
|
|
// `capacity + 1` below ensures that we don't fill in the last hole
|
|
return max(Int((Double(capacity) * maxLoadFactorInverse).rounded(.up)),
|
|
capacity + 1)
|
|
}
|
|
|
|
/// Buffer should be uniquely referenced.
|
|
/// The `key` should not be present in the Set.
|
|
/// This function does *not* update `count`.
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func unsafeAddNew(key newKey: Element) {
|
|
let (i, found) = _find(newKey, startBucket: _bucket(newKey))
|
|
_precondition(
|
|
!found, "Duplicate element found in Set. Elements may have been mutated after insertion")
|
|
initializeKey(newKey, at: i.offset)
|
|
}
|
|
|
|
//
|
|
// _HashBuffer conformance
|
|
//
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal func index(forKey key: Key) -> Index? {
|
|
if count == 0 {
|
|
// Fast path that avoids computing the hash of the key.
|
|
return nil
|
|
}
|
|
let (i, found) = _find(key, startBucket: _bucket(key))
|
|
return found ? i : nil
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func assertingGet(_ key: Key) -> Value {
|
|
let (i, found) = _find(key, startBucket: _bucket(key))
|
|
_precondition(found, "Key not found")
|
|
return self.key(at: i.offset)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal func maybeGet(_ key: Key) -> Value? {
|
|
if count == 0 {
|
|
// Fast path that avoids computing the hash of the key.
|
|
return nil
|
|
}
|
|
|
|
let (i, found) = _find(key, startBucket: _bucket(key))
|
|
if found {
|
|
return self.key(at: i.offset)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal func updateValue(_ value: Value, forKey key: Key) -> Value? {
|
|
_sanityCheckFailure(
|
|
"don't call mutating methods on _NativeSetBuffer")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal func insert(
|
|
_ value: Value, forKey key: Key
|
|
) -> (inserted: Bool, memberAfterInsert: Value) {
|
|
_sanityCheckFailure(
|
|
"don't call mutating methods on _NativeSetBuffer")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal func remove(at index: Index) -> SequenceElement {
|
|
_sanityCheckFailure(
|
|
"don't call mutating methods on _NativeSetBuffer")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal func removeValue(forKey key: Key) -> Value? {
|
|
_sanityCheckFailure(
|
|
"don't call mutating methods on _NativeSetBuffer")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func removeAll(keepingCapacity keepCapacity: Bool) {
|
|
_sanityCheckFailure(
|
|
"don't call mutating methods on _NativeSetBuffer")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func fromArray(_ elements: [SequenceElementWithoutLabels])
|
|
-> Buffer
|
|
{
|
|
if elements.isEmpty {
|
|
return Buffer()
|
|
}
|
|
|
|
var nativeBuffer = Buffer(minimumCapacity: elements.count)
|
|
|
|
var count = 0
|
|
for key in elements {
|
|
let (i, found) =
|
|
nativeBuffer._find(key, startBucket: nativeBuffer._bucket(key))
|
|
if found {
|
|
continue
|
|
}
|
|
nativeBuffer.initializeKey(key, at: i.offset)
|
|
count += 1
|
|
}
|
|
nativeBuffer.count = count
|
|
|
|
return nativeBuffer
|
|
}
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
/// An NSEnumerator that works with any NativeSetBuffer of
|
|
/// verbatim bridgeable elements. Used by the various NSSet impls.
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
final internal class _NativeSetNSEnumerator<Element>
|
|
: _SwiftNativeNSEnumerator, _NSEnumerator {
|
|
|
|
internal typealias Buffer = _NativeSetBuffer<Element>
|
|
internal typealias Index = _NativeSetIndex<Element>
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal override required init() {
|
|
_sanityCheckFailure("don't call this designated initializer")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_ buffer: Buffer) {
|
|
self.buffer = buffer
|
|
nextIndex = buffer.startIndex
|
|
endIndex = buffer.endIndex
|
|
}
|
|
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var buffer: Buffer
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var nextIndex: Index
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var endIndex: Index
|
|
|
|
//
|
|
// NSEnumerator implementation.
|
|
//
|
|
// Do not call any of these methods from the standard library!
|
|
//
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal func nextObject() -> AnyObject? {
|
|
if nextIndex == endIndex {
|
|
return nil
|
|
}
|
|
let key = buffer.bridgedKey(at: nextIndex)
|
|
buffer.formIndex(after: &nextIndex)
|
|
return key
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@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 key = buffer.bridgedKey(at: nextIndex)
|
|
buffer.formIndex(after: &nextIndex)
|
|
|
|
let unmanagedObjects = _UnmanagedAnyObjectArray(objects)
|
|
unmanagedObjects[0] = key
|
|
state.pointee = theState
|
|
return 1
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
/// This class exists for Objective-C bridging. It holds a reference to a
|
|
/// NativeSetBuffer, 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 NativeSetBuffer of AnyObject will be
|
|
/// constructed containing all the bridged elements.
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
final internal class _SwiftDeferredNSSet<Element: Hashable>
|
|
: _SwiftNativeNSSet, _NSSetCore {
|
|
|
|
internal typealias NativeBuffer = _NativeSetBuffer<Element>
|
|
internal typealias BridgedBuffer = _NativeSetBuffer<AnyObject>
|
|
internal typealias NativeIndex = _NativeSetIndex<Element>
|
|
internal typealias BridgedIndex = _NativeSetIndex<AnyObject>
|
|
|
|
internal typealias Key = Element
|
|
internal typealias Value = Element
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal init(bucketCount: Int = 2) {
|
|
nativeBuffer = NativeBuffer(bucketCount: bucketCount)
|
|
super.init()
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(nativeBuffer: NativeBuffer) {
|
|
self.nativeBuffer = nativeBuffer
|
|
super.init()
|
|
}
|
|
|
|
// This stored property should be stored at offset zero. We perform atomic
|
|
// operations on it.
|
|
//
|
|
// Do not access this property directly.
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal var _heapStorageBridged_DoNotUse: AnyObject?
|
|
|
|
/// The unbridged elements.
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var nativeBuffer: NativeBuffer
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@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
|
|
}
|
|
|
|
//
|
|
// NSSet implementation.
|
|
//
|
|
// Do not call any of these methods from the standard library! Use only
|
|
// `nativeBuffer`.
|
|
//
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal required init(objects: UnsafePointer<AnyObject?>, count: Int) {
|
|
_sanityCheckFailure("don't call this designated initializer")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal func member(_ object: AnyObject) -> AnyObject? {
|
|
return bridgingObjectForKey(object)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal func objectEnumerator() -> _NSEnumerator {
|
|
return enumerator()
|
|
}
|
|
|
|
/// Returns the pointer to the stored property, which contains bridged
|
|
/// Set elements.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal var _heapStorageBridgedPtr: UnsafeMutablePointer<AnyObject?> {
|
|
return _getUnsafePointerToStoredProperties(self).assumingMemoryBound(
|
|
to: Optional<AnyObject>.self)
|
|
}
|
|
|
|
/// The buffer for bridged Set elements, if present.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal var _bridgedStorage:
|
|
BridgedBuffer.RawStorage? {
|
|
get {
|
|
if let ref = _stdlib_atomicLoadARCRef(object: _heapStorageBridgedPtr) {
|
|
return unsafeDowncast(ref, to: BridgedBuffer.RawStorage.self)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// Attach a buffer for bridged Set elements.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal func _initializeHeapStorageBridged(_ newStorage: AnyObject) {
|
|
_stdlib_atomicInitializeARCRef(
|
|
object: _heapStorageBridgedPtr, desired: newStorage)
|
|
}
|
|
|
|
/// Returns the bridged Set values.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var bridgedBuffer: BridgedBuffer {
|
|
return BridgedBuffer(_storage: _bridgedStorage!)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal func bridgeEverything() {
|
|
if _fastPath(_bridgedStorage != nil) {
|
|
return
|
|
}
|
|
|
|
// FIXME: rdar://problem/19486139 (split bridged buffers for keys and values)
|
|
// We bridge keys and values unconditionally here, even if one of them
|
|
// actually is verbatim bridgeable (e.g. Dictionary<Int, AnyObject>).
|
|
// Investigate only allocating the buffer for a Set in this case.
|
|
|
|
// Create buffer for bridged data.
|
|
let bridged = BridgedBuffer(
|
|
_exactBucketCount: nativeBuffer.bucketCount,
|
|
unhashable: ())
|
|
|
|
// Bridge everything.
|
|
for i in 0..<nativeBuffer.bucketCount {
|
|
if nativeBuffer.isInitializedEntry(at: i) {
|
|
let key = _bridgeAnythingToObjectiveC(nativeBuffer.key(at: i))
|
|
bridged.initializeKey(key, at: i)
|
|
}
|
|
}
|
|
|
|
// Atomically put the bridged elements in place.
|
|
_initializeHeapStorageBridged(bridged._storage)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal var count: Int {
|
|
return nativeBuffer.count
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@nonobjc
|
|
internal func bridgingObjectForKey(_ aKey: AnyObject)
|
|
-> AnyObject? {
|
|
guard let nativeKey = _conditionallyBridgeFromObjectiveC(aKey, Key.self)
|
|
else { return nil }
|
|
|
|
let (i, found) = nativeBuffer._find(
|
|
nativeKey, startBucket: nativeBuffer._bucket(nativeKey))
|
|
if found {
|
|
bridgeEverything()
|
|
return bridgedBuffer.value(at: i.offset)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@objc
|
|
internal func enumerator() -> _NSEnumerator {
|
|
bridgeEverything()
|
|
return _NativeSetNSEnumerator<AnyObject>(bridgedBuffer)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@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(nativeBuffer.startIndex.offset)
|
|
}
|
|
|
|
// 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 currIndex = _NativeSetIndex<Element>(
|
|
offset: Int(theState.extra.0))
|
|
let endIndex = nativeBuffer.endIndex
|
|
var stored = 0
|
|
|
|
// Only need to bridge once, so we can hoist it out of the loop.
|
|
if (currIndex != endIndex) {
|
|
bridgeEverything()
|
|
}
|
|
|
|
for i in 0..<count {
|
|
if (currIndex == endIndex) {
|
|
break
|
|
}
|
|
|
|
let bridgedKey = bridgedBuffer.key(at: currIndex.offset)
|
|
unmanagedObjects[i] = bridgedKey
|
|
stored += 1
|
|
nativeBuffer.formIndex(after: &currIndex)
|
|
}
|
|
theState.extra.0 = CUnsignedLong(currIndex.offset)
|
|
state.pointee = theState
|
|
return stored
|
|
}
|
|
}
|
|
#else
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
final internal class _SwiftDeferredNSSet<Element: Hashable> { }
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
@usableFromInline
|
|
@_fixed_layout
|
|
internal struct _CocoaSetBuffer: _HashBuffer {
|
|
@usableFromInline
|
|
internal var cocoaSet: _NSSet
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(cocoaSet: _NSSet) {
|
|
self.cocoaSet = cocoaSet
|
|
}
|
|
|
|
internal typealias Index = _CocoaSetIndex
|
|
internal typealias SequenceElement = AnyObject
|
|
internal typealias SequenceElementWithoutLabels = AnyObject
|
|
|
|
internal typealias Key = AnyObject
|
|
internal typealias Value = AnyObject
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var startIndex: Index {
|
|
return Index(cocoaSet, startIndex: ())
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var endIndex: Index {
|
|
return Index(cocoaSet, endIndex: ())
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func index(after i: Index) -> Index {
|
|
return i.successor()
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func formIndex(after i: inout Index) {
|
|
// FIXME: swift-3-indexing-model: optimize if possible.
|
|
i = i.successor()
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
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 maybeGet(key) == nil {
|
|
return nil
|
|
}
|
|
|
|
let allKeys = _stdlib_NSSet_allObjects(cocoaSet)
|
|
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(cocoaSet, allKeys, keyIndex)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func assertingGet(_ i: Index) -> SequenceElement {
|
|
let value: Value? = i.allKeys[i.currentKeyIndex]
|
|
_sanityCheck(value != nil, "Item not found in underlying NSSet")
|
|
return value!
|
|
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func assertingGet(_ key: Key) -> Value {
|
|
let value: Value? = cocoaSet.member(key)
|
|
_precondition(value != nil, "Member not found in underlying NSSet")
|
|
return value!
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal func maybeGet(_ key: Key) -> Value? {
|
|
|
|
return cocoaSet.member(key)
|
|
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal mutating func updateValue(_ value: Value, forKey key: Key) -> Value? {
|
|
_sanityCheckFailure("cannot mutate NSSet")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal mutating func insert(
|
|
_ value: Value, forKey key: Key
|
|
) -> (inserted: Bool, memberAfterInsert: Value) {
|
|
_sanityCheckFailure("cannot mutate NSSet")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal mutating func remove(at index: Index) -> SequenceElement {
|
|
_sanityCheckFailure("cannot mutate NSSet")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal mutating func removeValue(forKey key: Key) -> Value? {
|
|
_sanityCheckFailure("cannot mutate NSSet")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func removeAll(keepingCapacity keepCapacity: Bool) {
|
|
_sanityCheckFailure("cannot mutate NSSet")
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var count: Int {
|
|
return cocoaSet.count
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func fromArray(_ elements: [SequenceElementWithoutLabels])
|
|
-> _CocoaSetBuffer {
|
|
|
|
_sanityCheckFailure("this function should never be called")
|
|
}
|
|
}
|
|
#endif
|
|
|
|
@usableFromInline
|
|
@_frozen
|
|
internal enum _VariantSetBuffer<Element: Hashable>: _HashBuffer {
|
|
|
|
internal typealias NativeBuffer = _NativeSetBuffer<Element>
|
|
internal typealias NativeIndex = _NativeSetIndex<Element>
|
|
#if _runtime(_ObjC)
|
|
internal typealias CocoaBuffer = _CocoaSetBuffer
|
|
#endif
|
|
internal typealias SequenceElement = Element
|
|
internal typealias SequenceElementWithoutLabels = Element
|
|
internal typealias SelfType = _VariantSetBuffer
|
|
|
|
internal typealias Key = Element
|
|
internal typealias Value = Element
|
|
|
|
case native(NativeBuffer)
|
|
#if _runtime(_ObjC)
|
|
case cocoa(CocoaBuffer)
|
|
#endif
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal var guaranteedNative: Bool {
|
|
return _canBeClass(Key.self) == 0 || _canBeClass(Value.self) == 0
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func isUniquelyReferenced() -> Bool {
|
|
// Note that &self drills down through .native(NativeBuffer) to the first
|
|
// property in NativeBuffer, which is the reference to the storage.
|
|
if _fastPath(guaranteedNative) {
|
|
return _isUnique_native(&self)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return _isUnique_native(&self)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa:
|
|
// Don't consider Cocoa buffer mutable, even if it is mutable and is
|
|
// uniquely referenced.
|
|
return false
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var asNative: NativeBuffer {
|
|
get {
|
|
switch self {
|
|
case .native(let buffer):
|
|
return buffer
|
|
#if _runtime(_ObjC)
|
|
case .cocoa:
|
|
_sanityCheckFailure("internal error: not backed by native buffer")
|
|
#endif
|
|
}
|
|
}
|
|
set {
|
|
self = .native(newValue)
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func ensureNativeBuffer() {
|
|
#if _runtime(_ObjC)
|
|
if _fastPath(guaranteedNative) { return }
|
|
if case .cocoa(let cocoaBuffer) = self {
|
|
migrateDataToNativeBuffer(cocoaBuffer)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var asCocoa: CocoaBuffer {
|
|
switch self {
|
|
case .native:
|
|
_sanityCheckFailure("internal error: not backed by NSSet")
|
|
case .cocoa(let cocoaBuffer):
|
|
return cocoaBuffer
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// Return true if self is native.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var _isNative: Bool {
|
|
#if _runtime(_ObjC)
|
|
switch self {
|
|
case .native:
|
|
return true
|
|
case .cocoa:
|
|
return false
|
|
}
|
|
#else
|
|
return true
|
|
#endif
|
|
}
|
|
|
|
@inline(__always)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func ensureUniqueNativeBufferNative(
|
|
withBucketCount desiredBucketCount: Int
|
|
) -> (reallocated: Bool, capacityChanged: Bool) {
|
|
let oldBucketCount = asNative.bucketCount
|
|
if oldBucketCount >= desiredBucketCount && isUniquelyReferenced() {
|
|
return (reallocated: false, capacityChanged: false)
|
|
}
|
|
|
|
let oldNativeBuffer = asNative
|
|
var newNativeBuffer = NativeBuffer(bucketCount: desiredBucketCount)
|
|
let newBucketCount = newNativeBuffer.bucketCount
|
|
for i in 0..<oldBucketCount {
|
|
if oldNativeBuffer.isInitializedEntry(at: i) {
|
|
if oldBucketCount == newBucketCount {
|
|
let key = oldNativeBuffer.key(at: i)
|
|
newNativeBuffer.initializeKey(key, at: i)
|
|
} else {
|
|
let key = oldNativeBuffer.key(at: i)
|
|
newNativeBuffer.unsafeAddNew(key: key)
|
|
}
|
|
}
|
|
}
|
|
newNativeBuffer.count = oldNativeBuffer.count
|
|
|
|
self = .native(newNativeBuffer)
|
|
return (reallocated: true,
|
|
capacityChanged: oldBucketCount != newBucketCount)
|
|
}
|
|
|
|
@inline(__always)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func ensureUniqueNativeBuffer(
|
|
withCapacity minimumCapacity: Int
|
|
) -> (reallocated: Bool, capacityChanged: Bool) {
|
|
let bucketCount = NativeBuffer.bucketCount(
|
|
forCapacity: minimumCapacity,
|
|
maxLoadFactorInverse: _hashContainerDefaultMaxLoadFactorInverse)
|
|
return ensureUniqueNativeBuffer(withBucketCount: bucketCount)
|
|
}
|
|
|
|
/// Ensure this we hold a unique reference to a native buffer
|
|
/// having at least `minimumCapacity` elements.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func ensureUniqueNativeBuffer(
|
|
withBucketCount desiredBucketCount: Int
|
|
) -> (reallocated: Bool, capacityChanged: Bool) {
|
|
#if _runtime(_ObjC)
|
|
// This is a performance optimization that was put in to ensure that we did
|
|
// not make a copy of self to call _isNative over the entire if region
|
|
// causing at -Onone the uniqueness check to fail. This code used to be:
|
|
//
|
|
// if _isNative {
|
|
// return ensureUniqueNativeBufferNative(
|
|
// withBucketCount: desiredBucketCount)
|
|
// }
|
|
//
|
|
// SR-6437
|
|
let n = _isNative
|
|
if n {
|
|
return ensureUniqueNativeBufferNative(withBucketCount: desiredBucketCount)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
fatalError("This should have been handled earlier")
|
|
case .cocoa(let cocoaBuffer):
|
|
let cocoaSet = cocoaBuffer.cocoaSet
|
|
var newNativeBuffer = NativeBuffer(bucketCount: desiredBucketCount)
|
|
let oldCocoaIterator = _CocoaSetIterator(cocoaSet)
|
|
while let key = oldCocoaIterator.next() {
|
|
newNativeBuffer.unsafeAddNew(
|
|
key: _forceBridgeFromObjectiveC(key, Value.self))
|
|
}
|
|
|
|
newNativeBuffer.count = cocoaSet.count
|
|
|
|
self = .native(newNativeBuffer)
|
|
return (reallocated: true, capacityChanged: true)
|
|
}
|
|
#else
|
|
return ensureUniqueNativeBufferNative(withBucketCount: desiredBucketCount)
|
|
#endif
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(never)
|
|
internal mutating func migrateDataToNativeBuffer(
|
|
_ cocoaBuffer: _CocoaSetBuffer
|
|
) {
|
|
let allocated = ensureUniqueNativeBuffer(
|
|
withCapacity: cocoaBuffer.count).reallocated
|
|
_sanityCheck(allocated, "failed to allocate native Set buffer")
|
|
}
|
|
#endif
|
|
|
|
/// Reserves enough space for the specified number of elements to be stored
|
|
/// without reallocating additional storage.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func reserveCapacity(_ capacity: Int) {
|
|
_ = ensureUniqueNativeBuffer(withCapacity: capacity)
|
|
}
|
|
|
|
/// 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 // FIXME(sil-serialize-all)
|
|
internal var capacity: Int {
|
|
switch self {
|
|
case .native:
|
|
return Int(Double(asNative.bucketCount) /
|
|
_hashContainerDefaultMaxLoadFactorInverse)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
return cocoaBuffer.count
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//
|
|
// _HashBuffer conformance
|
|
//
|
|
|
|
internal typealias Index = SetIndex<Element>
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var startIndex: Index {
|
|
if _fastPath(guaranteedNative) {
|
|
return ._native(asNative.startIndex)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return ._native(asNative.startIndex)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
return ._cocoa(cocoaBuffer.startIndex)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var endIndex: Index {
|
|
if _fastPath(guaranteedNative) {
|
|
return ._native(asNative.endIndex)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return ._native(asNative.endIndex)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
return ._cocoa(cocoaBuffer.endIndex)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func index(after i: Index) -> Index {
|
|
if _fastPath(guaranteedNative) {
|
|
return ._native(asNative.index(after: i._nativeIndex))
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return ._native(asNative.index(after: i._nativeIndex))
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
return ._cocoa(cocoaBuffer.index(after: i._cocoaIndex))
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func formIndex(after i: inout Index) {
|
|
// FIXME: swift-3-indexing-model: optimize if possible.
|
|
i = index(after: i)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal func index(forKey key: Key) -> Index? {
|
|
if _fastPath(guaranteedNative) {
|
|
if let nativeIndex = asNative.index(forKey: key) {
|
|
return ._native(nativeIndex)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
if let nativeIndex = asNative.index(forKey: key) {
|
|
return ._native(nativeIndex)
|
|
}
|
|
return nil
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
let anyObjectKey: AnyObject = _bridgeAnythingToObjectiveC(key)
|
|
if let cocoaIndex = cocoaBuffer.index(forKey: anyObjectKey) {
|
|
return ._cocoa(cocoaIndex)
|
|
}
|
|
return nil
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func assertingGet(_ i: Index) -> SequenceElement {
|
|
if _fastPath(guaranteedNative) {
|
|
return asNative.assertingGet(i._nativeIndex)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return asNative.assertingGet(i._nativeIndex)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
let anyObjectValue: AnyObject = cocoaBuffer.assertingGet(i._cocoaIndex)
|
|
let nativeValue = _forceBridgeFromObjectiveC(anyObjectValue, Value.self)
|
|
return nativeValue
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func assertingGet(_ key: Key) -> Value {
|
|
if _fastPath(guaranteedNative) {
|
|
return asNative.assertingGet(key)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return asNative.assertingGet(key)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
// FIXME: This assumes that Key and Value are bridged verbatim.
|
|
let anyObjectKey: AnyObject = _bridgeAnythingToObjectiveC(key)
|
|
let anyObjectValue: AnyObject = cocoaBuffer.assertingGet(anyObjectKey)
|
|
return _forceBridgeFromObjectiveC(anyObjectValue, Value.self)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(never)
|
|
internal static func maybeGetFromCocoaBuffer(
|
|
_ cocoaBuffer: CocoaBuffer, forKey key: Key
|
|
) -> Value? {
|
|
let anyObjectKey: AnyObject = _bridgeAnythingToObjectiveC(key)
|
|
if let anyObjectValue = cocoaBuffer.maybeGet(anyObjectKey) {
|
|
return _forceBridgeFromObjectiveC(anyObjectValue, Value.self)
|
|
}
|
|
return nil
|
|
}
|
|
#endif
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal func maybeGet(_ key: Key) -> Value? {
|
|
if _fastPath(guaranteedNative) {
|
|
return asNative.maybeGet(key)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return asNative.maybeGet(key)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
return SelfType.maybeGetFromCocoaBuffer(cocoaBuffer, forKey: key)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func nativeUpdateValue(
|
|
_ value: Value, forKey key: Key
|
|
) -> Value? {
|
|
var (i, found) = asNative._find(key, startBucket: asNative._bucket(key))
|
|
|
|
let minBuckets = found
|
|
? asNative.bucketCount
|
|
: NativeBuffer.bucketCount(
|
|
forCapacity: asNative.count + 1,
|
|
maxLoadFactorInverse: _hashContainerDefaultMaxLoadFactorInverse)
|
|
|
|
let (_, capacityChanged) = ensureUniqueNativeBuffer(
|
|
withBucketCount: minBuckets)
|
|
if capacityChanged {
|
|
i = asNative._find(key, startBucket: asNative._bucket(key)).pos
|
|
}
|
|
|
|
let oldValue: Value? = found ? asNative.key(at: i.offset) : nil
|
|
if found {
|
|
asNative.setKey(key, at: i.offset)
|
|
} else {
|
|
asNative.initializeKey(key, at: i.offset)
|
|
asNative.count += 1
|
|
}
|
|
|
|
return oldValue
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal mutating func updateValue(
|
|
_ value: Value, forKey key: Key
|
|
) -> Value? {
|
|
|
|
if _fastPath(guaranteedNative) {
|
|
return nativeUpdateValue(value, forKey: key)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return nativeUpdateValue(value, forKey: key)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
migrateDataToNativeBuffer(cocoaBuffer)
|
|
return nativeUpdateValue(value, forKey: key)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func nativeInsert(
|
|
_ value: Value, forKey key: Key
|
|
) -> (inserted: Bool, memberAfterInsert: Value) {
|
|
|
|
var (i, found) = asNative._find(key, startBucket: asNative._bucket(key))
|
|
if found {
|
|
return (inserted: false, memberAfterInsert: asNative.key(at: i.offset))
|
|
}
|
|
|
|
let minCapacity = asNative.count + 1
|
|
let (_, capacityChanged) = ensureUniqueNativeBuffer(
|
|
withCapacity: minCapacity)
|
|
|
|
if capacityChanged {
|
|
i = asNative._find(key, startBucket: asNative._bucket(key)).pos
|
|
}
|
|
|
|
asNative.initializeKey(key, at: i.offset)
|
|
asNative.count += 1
|
|
|
|
return (inserted: true, memberAfterInsert: value)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal mutating func insert(
|
|
_ value: Value, forKey key: Key
|
|
) -> (inserted: Bool, memberAfterInsert: Value) {
|
|
ensureNativeBuffer()
|
|
return nativeInsert(value, forKey: key)
|
|
}
|
|
|
|
/// - parameter idealBucket: The ideal bucket for the element being deleted.
|
|
/// - parameter offset: The offset of the element that will be deleted.
|
|
/// Precondition: there should be an initialized entry at offset.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func nativeDelete(
|
|
_ nativeBuffer: NativeBuffer, idealBucket: Int, offset: Int
|
|
) {
|
|
_sanityCheck(
|
|
nativeBuffer.isInitializedEntry(at: offset), "expected initialized entry")
|
|
|
|
var nativeBuffer = nativeBuffer
|
|
|
|
// remove the element
|
|
nativeBuffer.destroyEntry(at: offset)
|
|
nativeBuffer.count -= 1
|
|
|
|
// 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 = offset
|
|
|
|
// Find the first bucket in the contiguous chain
|
|
var start = idealBucket
|
|
while nativeBuffer.isInitializedEntry(at: nativeBuffer._prev(start)) {
|
|
start = nativeBuffer._prev(start)
|
|
}
|
|
|
|
// Find the last bucket in the contiguous chain
|
|
var lastInChain = hole
|
|
var b = nativeBuffer._index(after: lastInChain)
|
|
while nativeBuffer.isInitializedEntry(at: b) {
|
|
lastInChain = b
|
|
b = nativeBuffer._index(after: b)
|
|
}
|
|
|
|
// Relocate out-of-place elements in the chain, repeating until
|
|
// none are found.
|
|
while hole != lastInChain {
|
|
// Walk backwards from the end of the chain looking for
|
|
// something out-of-place.
|
|
var b = lastInChain
|
|
while b != hole {
|
|
let idealBucket = nativeBuffer._bucket(nativeBuffer.key(at: b))
|
|
|
|
// 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 = idealBucket >= start
|
|
let c1 = idealBucket <= hole
|
|
if start <= hole ? (c0 && c1) : (c0 || c1) {
|
|
break // Found it
|
|
}
|
|
b = nativeBuffer._prev(b)
|
|
}
|
|
|
|
if b == hole { // No out-of-place elements found; we're done adjusting
|
|
break
|
|
}
|
|
|
|
// Move the found element into the hole
|
|
nativeBuffer.moveInitializeEntry(
|
|
from: nativeBuffer,
|
|
at: b,
|
|
toEntryAt: hole)
|
|
hole = b
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func nativeRemoveObject(forKey key: Key) -> Value? {
|
|
var idealBucket = asNative._bucket(key)
|
|
var (index, found) = asNative._find(key, startBucket: idealBucket)
|
|
|
|
// Fast path: if the key is not present, we will not mutate the set,
|
|
// so don't force unique buffer.
|
|
if !found {
|
|
return nil
|
|
}
|
|
|
|
// This is a performance optimization that was put in to ensure that we
|
|
// did not make a copy of self to call asNative.bucketCount over
|
|
// ensureUniqueNativeBefore causing at -Onone the uniqueness check to
|
|
// fail. This code used to be:
|
|
//
|
|
// ... = ensureUniqueNativeBuffer(withBucketCount: asNative.bucketCount)
|
|
//
|
|
// SR-6437
|
|
let bucketCount = asNative.bucketCount
|
|
let (_, capacityChanged) = ensureUniqueNativeBuffer(
|
|
withBucketCount: bucketCount)
|
|
let nativeBuffer = asNative
|
|
if capacityChanged {
|
|
idealBucket = nativeBuffer._bucket(key)
|
|
(index, found) = nativeBuffer._find(key, startBucket: idealBucket)
|
|
_sanityCheck(found, "key was lost during buffer migration")
|
|
}
|
|
let oldValue = nativeBuffer.key(at: index.offset)
|
|
nativeDelete(nativeBuffer, idealBucket: idealBucket,
|
|
offset: index.offset)
|
|
return oldValue
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func nativeRemove(
|
|
at nativeIndex: NativeIndex
|
|
) -> SequenceElement {
|
|
// This is a performance optimization that was put in to ensure that we did
|
|
// not make a copy of self to call asNative.bucketCount over
|
|
// ensureUniqueNativeBefore causing at -Onone the uniqueness check to
|
|
// fail. This code used to be:
|
|
//
|
|
// _ = ensureUniqueNativeBuffer(withBucketCount: asNative.bucketCount)
|
|
//
|
|
// SR-6437
|
|
let bucketCount = asNative.bucketCount
|
|
// The provided index should be valid, so we will always mutating the
|
|
// set buffer. Request unique buffer.
|
|
_ = ensureUniqueNativeBuffer(withBucketCount: bucketCount)
|
|
let nativeBuffer = asNative
|
|
|
|
let result = nativeBuffer.assertingGet(nativeIndex)
|
|
let key = result
|
|
|
|
nativeDelete(nativeBuffer, idealBucket: nativeBuffer._bucket(key),
|
|
offset: nativeIndex.offset)
|
|
return result
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal mutating func remove(at index: Index) -> SequenceElement {
|
|
if _fastPath(guaranteedNative) {
|
|
return nativeRemove(at: index._nativeIndex)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return nativeRemove(at: index._nativeIndex)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
// We have to migrate the data first. But after we do so, the Cocoa
|
|
// index becomes useless, so get the key first.
|
|
//
|
|
// FIXME(performance): fuse data migration and element deletion into one
|
|
// operation.
|
|
let index = index._cocoaIndex
|
|
let anyObjectKey: AnyObject = index.allKeys[index.currentKeyIndex]
|
|
migrateDataToNativeBuffer(cocoaBuffer)
|
|
let key = _forceBridgeFromObjectiveC(anyObjectKey, Key.self)
|
|
let value = nativeRemoveObject(forKey: key)
|
|
|
|
_sanityCheck(key == value, "bridging did not preserve equality")
|
|
return key
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@discardableResult
|
|
internal mutating func removeValue(forKey key: Key) -> Value? {
|
|
if _fastPath(guaranteedNative) {
|
|
return nativeRemoveObject(forKey: key)
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return nativeRemoveObject(forKey: key)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
let anyObjectKey: AnyObject = _bridgeAnythingToObjectiveC(key)
|
|
if cocoaBuffer.maybeGet(anyObjectKey) == nil {
|
|
return nil
|
|
}
|
|
migrateDataToNativeBuffer(cocoaBuffer)
|
|
return nativeRemoveObject(forKey: key)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func nativeRemoveAll() {
|
|
if !isUniquelyReferenced() {
|
|
asNative = NativeBuffer(_exactBucketCount: asNative.bucketCount)
|
|
return
|
|
}
|
|
|
|
// We have already checked for the empty dictionary case and unique
|
|
// reference, so we will always mutate the dictionary buffer.
|
|
var nativeBuffer = asNative
|
|
|
|
for b in 0..<nativeBuffer.bucketCount {
|
|
if nativeBuffer.isInitializedEntry(at: b) {
|
|
nativeBuffer.destroyEntry(at: b)
|
|
}
|
|
}
|
|
nativeBuffer.count = 0
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func removeAll(keepingCapacity keepCapacity: Bool) {
|
|
if count == 0 {
|
|
return
|
|
}
|
|
|
|
if !keepCapacity {
|
|
self = .native(NativeBuffer(bucketCount: 2))
|
|
return
|
|
}
|
|
|
|
if _fastPath(guaranteedNative) {
|
|
nativeRemoveAll()
|
|
return
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
nativeRemoveAll()
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
self = .native(NativeBuffer(minimumCapacity: cocoaBuffer.count))
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var count: Int {
|
|
if _fastPath(guaranteedNative) {
|
|
return asNative.count
|
|
}
|
|
|
|
switch self {
|
|
case .native:
|
|
return asNative.count
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
return cocoaBuffer.count
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// Returns an iterator over the `(Key, Value)` pairs.
|
|
///
|
|
/// - Complexity: O(1).
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
internal func makeIterator() -> SetIterator<Element> {
|
|
switch self {
|
|
case .native(let buffer):
|
|
return ._native(
|
|
start: asNative.startIndex, end: asNative.endIndex, buffer: buffer)
|
|
#if _runtime(_ObjC)
|
|
case .cocoa(let cocoaBuffer):
|
|
return ._cocoa(_CocoaSetIterator(cocoaBuffer.cocoaSet))
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func fromArray(_ elements: [SequenceElement])
|
|
-> _VariantSetBuffer<Element> {
|
|
|
|
_sanityCheckFailure("this function should never be called")
|
|
}
|
|
}
|
|
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
@usableFromInline
|
|
internal struct _NativeSetIndex<Element>: Comparable {
|
|
@usableFromInline
|
|
internal var offset: Int
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(offset: Int) {
|
|
self.offset = offset
|
|
}
|
|
}
|
|
|
|
extension _NativeSetIndex {
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func < (
|
|
lhs: _NativeSetIndex<Element>,
|
|
rhs: _NativeSetIndex<Element>
|
|
) -> Bool {
|
|
return lhs.offset < rhs.offset
|
|
}
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func <= (
|
|
lhs: _NativeSetIndex<Element>,
|
|
rhs: _NativeSetIndex<Element>
|
|
) -> Bool {
|
|
return lhs.offset <= rhs.offset
|
|
}
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func > (
|
|
lhs: _NativeSetIndex<Element>,
|
|
rhs: _NativeSetIndex<Element>
|
|
) -> Bool {
|
|
return lhs.offset > rhs.offset
|
|
}
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func >= (
|
|
lhs: _NativeSetIndex<Element>,
|
|
rhs: _NativeSetIndex<Element>
|
|
) -> Bool {
|
|
return lhs.offset >= rhs.offset
|
|
}
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func == (
|
|
lhs: _NativeSetIndex<Element>,
|
|
rhs: _NativeSetIndex<Element>
|
|
) -> Bool {
|
|
return lhs.offset == rhs.offset
|
|
}
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
@usableFromInline
|
|
internal struct _CocoaSetIndex: Comparable {
|
|
// 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 cocoaSet: _NSSet
|
|
// 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(_ cocoaSet: _NSSet, startIndex: ()) {
|
|
self.cocoaSet = cocoaSet
|
|
self.allKeys = _stdlib_NSSet_allObjects(cocoaSet)
|
|
self.currentKeyIndex = 0
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_ cocoaSet: _NSSet, endIndex: ()) {
|
|
self.cocoaSet = cocoaSet
|
|
self.allKeys = _stdlib_NSSet_allObjects(cocoaSet)
|
|
self.currentKeyIndex = allKeys.value
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_ cocoaSet: _NSSet,
|
|
_ allKeys: _HeapBuffer<Int, AnyObject>,
|
|
_ currentKeyIndex: Int
|
|
) {
|
|
self.cocoaSet = cocoaSet
|
|
self.allKeys = allKeys
|
|
self.currentKeyIndex = currentKeyIndex
|
|
}
|
|
|
|
/// Returns the next consecutive value after `self`.
|
|
///
|
|
/// - Precondition: The next value is representable.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func successor() -> _CocoaSetIndex {
|
|
// FIXME: swift-3-indexing-model: remove this method.
|
|
_precondition(
|
|
currentKeyIndex < allKeys.value, "Cannot increment endIndex")
|
|
return _CocoaSetIndex(cocoaSet, allKeys, currentKeyIndex + 1)
|
|
}
|
|
}
|
|
|
|
extension _CocoaSetIndex {
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func < (
|
|
lhs: _CocoaSetIndex,
|
|
rhs: _CocoaSetIndex
|
|
) -> Bool {
|
|
return lhs.currentKeyIndex < rhs.currentKeyIndex
|
|
}
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func <= (
|
|
lhs: _CocoaSetIndex,
|
|
rhs: _CocoaSetIndex
|
|
) -> Bool {
|
|
return lhs.currentKeyIndex <= rhs.currentKeyIndex
|
|
}
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func > (
|
|
lhs: _CocoaSetIndex,
|
|
rhs: _CocoaSetIndex
|
|
) -> Bool {
|
|
return lhs.currentKeyIndex > rhs.currentKeyIndex
|
|
}
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func >= (
|
|
lhs: _CocoaSetIndex,
|
|
rhs: _CocoaSetIndex
|
|
) -> Bool {
|
|
return lhs.currentKeyIndex >= rhs.currentKeyIndex
|
|
}
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func == (
|
|
lhs: _CocoaSetIndex,
|
|
rhs: _CocoaSetIndex
|
|
) -> Bool {
|
|
return lhs.currentKeyIndex == rhs.currentKeyIndex
|
|
}
|
|
}
|
|
#endif
|
|
|
|
@_frozen // FIXME(sil-serialize-all)
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal enum SetIndexRepresentation<Element: Hashable> {
|
|
typealias _Index = SetIndex<Element>
|
|
typealias _NativeIndex = _Index._NativeIndex
|
|
#if _runtime(_ObjC)
|
|
typealias _CocoaIndex = _Index._CocoaIndex
|
|
#endif
|
|
|
|
case _native(_NativeIndex)
|
|
#if _runtime(_ObjC)
|
|
case _cocoa(_CocoaIndex)
|
|
#endif
|
|
}
|
|
|
|
extension Set {
|
|
/// The position of an element in a set.
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
public struct Index: Comparable, Hashable {
|
|
// Index for native buffer is efficient. Index for bridged NSSet is
|
|
// not, because neither NSEnumerator nor fast enumeration support moving
|
|
// backwards. Even if they did, there is another issue: NSEnumerator does
|
|
// not support NSCopying, and fast enumeration does not document that it is
|
|
// safe to copy the state. So, we cannot implement Index that is a value
|
|
// type for bridged NSSet in terms of Cocoa enumeration facilities.
|
|
|
|
internal typealias _NativeIndex = _NativeSetIndex<Element>
|
|
#if _runtime(_ObjC)
|
|
internal typealias _CocoaIndex = _CocoaSetIndex
|
|
#endif
|
|
|
|
internal typealias Key = Element
|
|
internal typealias Value = Element
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_value: SetIndexRepresentation<Element>) {
|
|
self._value = _value
|
|
}
|
|
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var _value: SetIndexRepresentation<Element>
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func _native(_ index: _NativeIndex) -> Index {
|
|
return SetIndex(_value: ._native(index))
|
|
}
|
|
#if _runtime(_ObjC)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func _cocoa(_ index: _CocoaIndex) -> Index {
|
|
return SetIndex(_value: ._cocoa(index))
|
|
}
|
|
#endif
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal var _guaranteedNative: Bool {
|
|
return _canBeClass(Key.self) == 0 && _canBeClass(Value.self) == 0
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal var _nativeIndex: _NativeIndex {
|
|
switch _value {
|
|
case ._native(let nativeIndex):
|
|
return nativeIndex
|
|
#if _runtime(_ObjC)
|
|
case ._cocoa:
|
|
_sanityCheckFailure("internal error: does not contain a native index")
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal var _cocoaIndex: _CocoaIndex {
|
|
switch _value {
|
|
case ._native:
|
|
_sanityCheckFailure("internal error: does not contain a Cocoa index")
|
|
case ._cocoa(let cocoaIndex):
|
|
return cocoaIndex
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public typealias SetIndex<Element: Hashable> = Set<Element>.Index
|
|
|
|
extension Set.Index {
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public static func == (
|
|
lhs: Set<Element>.Index,
|
|
rhs: Set<Element>.Index
|
|
) -> Bool {
|
|
if _fastPath(lhs._guaranteedNative) {
|
|
return lhs._nativeIndex == rhs._nativeIndex
|
|
}
|
|
|
|
switch (lhs._value, rhs._value) {
|
|
case (._native(let lhsNative), ._native(let rhsNative)):
|
|
return lhsNative == rhsNative
|
|
#if _runtime(_ObjC)
|
|
case (._cocoa(let lhsCocoa), ._cocoa(let rhsCocoa)):
|
|
return lhsCocoa == rhsCocoa
|
|
default:
|
|
_preconditionFailure("Comparing indexes from different sets")
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public static func < (
|
|
lhs: Set<Element>.Index,
|
|
rhs: Set<Element>.Index
|
|
) -> Bool {
|
|
if _fastPath(lhs._guaranteedNative) {
|
|
return lhs._nativeIndex < rhs._nativeIndex
|
|
}
|
|
|
|
switch (lhs._value, rhs._value) {
|
|
case (._native(let lhsNative), ._native(let rhsNative)):
|
|
return lhsNative < rhsNative
|
|
#if _runtime(_ObjC)
|
|
case (._cocoa(let lhsCocoa), ._cocoa(let rhsCocoa)):
|
|
return lhsCocoa < rhsCocoa
|
|
default:
|
|
_preconditionFailure("Comparing indexes from different sets")
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var hashValue: Int {
|
|
if _fastPath(_guaranteedNative) {
|
|
return _nativeIndex.offset
|
|
}
|
|
|
|
switch _value {
|
|
case ._native(let nativeIndex):
|
|
return nativeIndex.offset
|
|
#if _runtime(_ObjC)
|
|
case ._cocoa(let cocoaIndex):
|
|
return cocoaIndex.currentKeyIndex
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
@usableFromInline
|
|
final internal class _CocoaSetIterator: IteratorProtocol {
|
|
internal typealias Element = AnyObject
|
|
|
|
// 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.
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var _fastEnumerationState: _SwiftNSFastEnumerationState =
|
|
_makeSwiftNSFastEnumerationState()
|
|
|
|
// This stored property should be stored right after `_fastEnumerationState`.
|
|
// There's code below relying on this.
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var _fastEnumerationStackBuf = _CocoaFastEnumerationStackBuf()
|
|
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal let cocoaSet: _NSSet
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal var _fastEnumerationStatePtr:
|
|
UnsafeMutablePointer<_SwiftNSFastEnumerationState> {
|
|
return _getUnsafePointerToStoredProperties(self).assumingMemoryBound(
|
|
to: _SwiftNSFastEnumerationState.self)
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
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.
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var itemIndex: Int = 0
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var itemCount: Int = 0
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_ cocoaSet: _NSSet) {
|
|
self.cocoaSet = cocoaSet
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal func next() -> Element? {
|
|
if itemIndex < 0 {
|
|
return nil
|
|
}
|
|
let cocoaSet = self.cocoaSet
|
|
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 = cocoaSet.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
|
|
}
|
|
}
|
|
#endif
|
|
|
|
@usableFromInline
|
|
@_frozen // FIXME(sil-serialize-all)
|
|
internal enum SetIteratorRepresentation<Element: Hashable> {
|
|
internal typealias _Iterator = SetIterator<Element>
|
|
internal typealias _NativeBuffer =
|
|
_NativeSetBuffer<Element>
|
|
internal typealias _NativeIndex = _Iterator._NativeIndex
|
|
|
|
// For native buffer, we keep two indices to keep track of the iteration
|
|
// progress and the buffer owner to make the buffer non-uniquely
|
|
// referenced.
|
|
//
|
|
// Iterator is iterating over a frozen view of the collection
|
|
// state, so it should keep its own reference to the buffer.
|
|
case _native(
|
|
start: _NativeIndex, end: _NativeIndex, buffer: _NativeBuffer)
|
|
#if _runtime(_ObjC)
|
|
case _cocoa(_CocoaSetIterator)
|
|
#endif
|
|
}
|
|
|
|
/// An iterator over the members of a `Set<Element>`.
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
public struct SetIterator<Element: Hashable>: IteratorProtocol {
|
|
// Set has a separate IteratorProtocol and Index because of efficiency
|
|
// and implementability reasons.
|
|
//
|
|
// Index for native buffer is efficient. Index for bridged NSSet is
|
|
// not.
|
|
//
|
|
// Even though fast enumeration is not suitable for implementing
|
|
// Index, which is multi-pass, it is suitable for implementing a
|
|
// IteratorProtocol, which is being consumed as iteration proceeds.
|
|
|
|
internal typealias _NativeBuffer =
|
|
_NativeSetBuffer<Element>
|
|
internal typealias _NativeIndex = _NativeSetIndex<Element>
|
|
|
|
@usableFromInline
|
|
internal var _state: SetIteratorRepresentation<Element>
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal init(_state: SetIteratorRepresentation<Element>) {
|
|
self._state = _state
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func _native(
|
|
start: _NativeIndex, end: _NativeIndex, buffer: _NativeBuffer
|
|
) -> SetIterator {
|
|
return SetIterator(
|
|
_state: ._native(start: start, end: end, buffer: buffer))
|
|
}
|
|
#if _runtime(_ObjC)
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal static func _cocoa(
|
|
_ iterator: _CocoaSetIterator
|
|
) -> SetIterator{
|
|
return SetIterator(_state: ._cocoa(iterator))
|
|
}
|
|
#endif
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@_transparent
|
|
internal var _guaranteedNative: Bool {
|
|
return _canBeClass(Element.self) == 0
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
internal mutating func _nativeNext() -> Element? {
|
|
switch _state {
|
|
case ._native(let startIndex, let endIndex, let buffer):
|
|
if startIndex == endIndex {
|
|
return nil
|
|
}
|
|
let result = buffer.assertingGet(startIndex)
|
|
_state =
|
|
._native(start: buffer.index(after: startIndex), end: endIndex, buffer: buffer)
|
|
return result
|
|
#if _runtime(_ObjC)
|
|
case ._cocoa:
|
|
_sanityCheckFailure("internal error: not backed by NSSet")
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// Advances to the next element and returns it, or `nil` if no next element
|
|
/// exists.
|
|
///
|
|
/// Once `nil` has been returned, all subsequent calls return `nil`.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
@inline(__always)
|
|
public mutating func next() -> Element? {
|
|
if _fastPath(_guaranteedNative) {
|
|
return _nativeNext()
|
|
}
|
|
|
|
switch _state {
|
|
case ._native:
|
|
return _nativeNext()
|
|
#if _runtime(_ObjC)
|
|
case ._cocoa(let cocoaIterator):
|
|
if let anyObjectElement = cocoaIterator.next() {
|
|
return _forceBridgeFromObjectiveC(anyObjectElement, Element.self)
|
|
}
|
|
return nil
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
extension SetIterator: CustomReflectable {
|
|
/// A mirror that reflects the iterator.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var customMirror: Mirror {
|
|
return Mirror(
|
|
self,
|
|
children: EmptyCollection<(label: String?, value: Any)>())
|
|
}
|
|
}
|
|
|
|
extension Set: CustomReflectable {
|
|
/// A mirror that reflects the set.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var customMirror: Mirror {
|
|
let style = Mirror.DisplayStyle.`set`
|
|
return Mirror(self, unlabeledChildren: self, displayStyle: style)
|
|
}
|
|
}
|
|
|
|
/// Initializes a `Set` from unique members.
|
|
///
|
|
/// Using a builder can be faster than inserting members into an empty
|
|
/// `Set`.
|
|
@_fixed_layout // FIXME(sil-serialize-all)
|
|
public struct _SetBuilder<Element: Hashable> {
|
|
public typealias Key = Element
|
|
public typealias Value = Element
|
|
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var _result: Set<Element>
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var _nativeBuffer: _NativeSetBuffer<Element>
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal let _requestedCount: Int
|
|
@usableFromInline // FIXME(sil-serialize-all)
|
|
internal var _actualCount: Int
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public init(count: Int) {
|
|
_result = Set<Element>(minimumCapacity: count)
|
|
_nativeBuffer = _result._variantBuffer.asNative
|
|
_requestedCount = count
|
|
_actualCount = 0
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public mutating func add(member newKey: Key) {
|
|
_nativeBuffer.unsafeAddNew(key: newKey)
|
|
_actualCount += 1
|
|
}
|
|
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public mutating func take() -> Set<Element> {
|
|
_precondition(_actualCount >= 0,
|
|
"Cannot take the result twice")
|
|
_precondition(_actualCount == _requestedCount,
|
|
"The number of members added does not match the promised count")
|
|
|
|
// Finish building the `Set`.
|
|
_nativeBuffer.count = _requestedCount
|
|
|
|
// Prevent taking the result twice.
|
|
_actualCount = -1
|
|
return _result
|
|
}
|
|
}
|
|
|
|
extension Set {
|
|
/// Removes and returns the first element of the set.
|
|
///
|
|
/// Because a set is not an ordered collection, the "first" element may not
|
|
/// be the first element that was added to the set.
|
|
///
|
|
/// - Returns: A member of the set. If the set is empty, returns `nil`.
|
|
@inlinable
|
|
public mutating func popFirst() -> Element? {
|
|
guard !isEmpty else { return nil }
|
|
return remove(at: startIndex)
|
|
}
|
|
|
|
@inlinable
|
|
@available(swift, obsoleted: 4.0)
|
|
public func filter(
|
|
_ isIncluded: (Element) throws -> Bool, obsoletedInSwift4: () = ()
|
|
) rethrows -> [Element] {
|
|
var result: [Element] = []
|
|
for x in self {
|
|
if try isIncluded(x) {
|
|
result.append(x)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
/// The total number of elements that the set can contain without
|
|
/// allocating new storage.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public var capacity: Int {
|
|
return _variantBuffer.capacity
|
|
}
|
|
|
|
/// Reserves enough space to store the specified number of elements.
|
|
///
|
|
/// If you are adding a known number of elements to a set, use this
|
|
/// method to avoid multiple reallocations. This method ensures that the
|
|
/// set has unique, mutable, contiguous storage, with space allocated
|
|
/// for at least the requested number of elements.
|
|
///
|
|
/// Calling the `reserveCapacity(_:)` method on a set with bridged
|
|
/// storage triggers a copy to contiguous storage even if the existing
|
|
/// storage has room to store `minimumCapacity` elements.
|
|
///
|
|
/// - Parameter minimumCapacity: The requested number of elements to
|
|
/// store.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public mutating func reserveCapacity(_ minimumCapacity: Int) {
|
|
_variantBuffer.reserveCapacity(minimumCapacity)
|
|
_sanityCheck(self.capacity >= minimumCapacity)
|
|
}
|
|
}
|
|
|
|
//===--- Bridging ---------------------------------------------------------===//
|
|
|
|
#if _runtime(_ObjC)
|
|
extension Set {
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public func _bridgeToObjectiveCImpl() -> _NSSetCore {
|
|
switch _variantBuffer {
|
|
case _VariantSetBuffer.native(let buffer):
|
|
return buffer.bridged()
|
|
case _VariantSetBuffer.cocoa(let cocoaBuffer):
|
|
return cocoaBuffer.cocoaSet
|
|
}
|
|
}
|
|
|
|
/// Returns the native Dictionary hidden inside this NSDictionary;
|
|
/// returns nil otherwise.
|
|
@inlinable // FIXME(sil-serialize-all)
|
|
public static func _bridgeFromObjectiveCAdoptingNativeStorageOf(
|
|
_ s: AnyObject
|
|
) -> Set<Element>? {
|
|
|
|
// Try all three NSSet impls that we currently provide.
|
|
|
|
if let deferredBuffer = s as? _SwiftDeferredNSSet<Element> {
|
|
return Set(_nativeBuffer: deferredBuffer.nativeBuffer)
|
|
}
|
|
|
|
if let nativeStorage = s as? _HashableTypedNativeSetStorage<Element> {
|
|
return Set(_nativeBuffer:
|
|
_NativeSetBuffer(_storage: nativeStorage))
|
|
}
|
|
|
|
if s === _RawNativeSetStorage.empty {
|
|
return Set()
|
|
}
|
|
|
|
// FIXME: what if `s` is native storage, but for different key/value type?
|
|
return nil
|
|
}
|
|
}
|
|
#endif
|