//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// /// This protocol is only used for compile-time checks that /// every buffer type implements all required operations. internal protocol _SetBuffer { associatedtype Element associatedtype Index var startIndex: Index { get } var endIndex: Index { get } func index(after i: Index) -> Index func index(for element: Element) -> Index? var count: Int { get } func contains(_ member: Element) -> Bool func element(at i: Index) -> Element } extension Set { @usableFromInline @frozen @safe internal struct _Variant { @usableFromInline internal var object: _BridgeStorage<__RawSetStorage> @inlinable @inline(__always) init(dummy: ()) { #if _pointerBitWidth(_64) && !$Embedded unsafe self.object = _BridgeStorage(taggedPayload: 0) #elseif _pointerBitWidth(_32) || $Embedded self.init(native: _NativeSet()) #else #error("Unknown platform") #endif } @inlinable @inline(__always) init(native: __owned _NativeSet) { unsafe self.object = _BridgeStorage(native: native._storage) } #if _runtime(_ObjC) @inlinable @inline(__always) init(cocoa: __owned __CocoaSet) { unsafe self.object = _BridgeStorage(objC: cocoa.object) } #endif } } extension Set._Variant { #if _runtime(_ObjC) @usableFromInline @_transparent internal var guaranteedNative: Bool { return _canBeClass(Element.self) == 0 } #endif @inlinable internal mutating func isUniquelyReferenced() -> Bool { return unsafe object.isUniquelyReferencedUnflaggedNative() } #if _runtime(_ObjC) @usableFromInline @_transparent internal var isNative: Bool { if guaranteedNative { return true } return unsafe object.isUnflaggedNative } #endif @usableFromInline @_transparent internal var asNative: _NativeSet { get { return unsafe _NativeSet(object.unflaggedNativeInstance) } set { self = .init(native: newValue) } _modify { var native = unsafe _NativeSet(object.unflaggedNativeInstance) self = .init(dummy: ()) defer { // This is in a defer block because yield might throw, and we need to // preserve Set's storage invariants when that happens. unsafe object = .init(native: native._storage) } yield &native } } #if _runtime(_ObjC) @inlinable internal var asCocoa: __CocoaSet { return unsafe __CocoaSet(object.objCInstance) } #endif @_alwaysEmitIntoClient internal var convertedToNative: _NativeSet { #if _runtime(_ObjC) guard isNative else { return _NativeSet(asCocoa) } #endif return asNative } /// Reserves enough space for the specified number of elements to be stored /// without reallocating additional storage. internal mutating func reserveCapacity(_ capacity: Int) { #if _runtime(_ObjC) guard isNative else { let cocoa = asCocoa let capacity = Swift.max(cocoa.count, capacity) self = .init(native: _NativeSet(cocoa, capacity: capacity)) return } #endif let isUnique = isUniquelyReferenced() asNative.reserveCapacity(capacity, isUnique: isUnique) } /// The number of elements that can be stored without expanding the current /// storage. /// /// For bridged storage, this is equal to the current count of the /// collection, since any addition will trigger a copy of the elements into /// newly allocated storage. For native storage, this is the element count /// at which adding any more elements will exceed the load factor. @inlinable internal var capacity: Int { #if _runtime(_ObjC) guard isNative else { return asCocoa.count } #endif return asNative.capacity } } extension Set._Variant: _SetBuffer { @usableFromInline internal typealias Index = Set.Index @inlinable internal var startIndex: Index { #if _runtime(_ObjC) guard isNative else { return Index(_cocoa: asCocoa.startIndex) } #endif return asNative.startIndex } @inlinable internal var endIndex: Index { #if _runtime(_ObjC) guard isNative else { return Index(_cocoa: asCocoa.endIndex) } #endif return asNative.endIndex } @inlinable internal func index(after index: Index) -> Index { #if _runtime(_ObjC) guard isNative else { return Index(_cocoa: asCocoa.index(after: index._asCocoa)) } #endif return asNative.index(after: index) } @inlinable internal func formIndex(after index: inout Index) { #if _runtime(_ObjC) guard isNative else { let isUnique = index._isUniquelyReferenced() asCocoa.formIndex(after: &index._asCocoa, isUnique: isUnique) return } #endif index = asNative.index(after: index) } @inlinable @inline(__always) internal func index(for element: Element) -> Index? { #if _runtime(_ObjC) guard isNative else { let cocoaElement = _bridgeAnythingToObjectiveC(element) guard let index = asCocoa.index(for: cocoaElement) else { return nil } return Index(_cocoa: index) } #endif return asNative.index(for: element) } @inlinable internal var count: Int { @inline(__always) get { #if _runtime(_ObjC) guard isNative else { return asCocoa.count } #endif return asNative.count } } @inlinable @inline(__always) internal func contains(_ member: Element) -> Bool { #if _runtime(_ObjC) guard isNative else { return asCocoa.contains(_bridgeAnythingToObjectiveC(member)) } #endif return asNative.contains(member) } @inlinable @inline(__always) internal func element(at index: Index) -> Element { #if _runtime(_ObjC) guard isNative else { let cocoaMember = asCocoa.element(at: index._asCocoa) return _forceBridgeFromObjectiveC(cocoaMember, Element.self) } #endif return asNative.element(at: index) } } extension Set._Variant { @inlinable internal mutating func update(with value: __owned Element) -> Element? { #if _runtime(_ObjC) guard isNative else { // Make sure we have space for an extra element. var native = _NativeSet(asCocoa, capacity: asCocoa.count + 1) let old = native.update(with: value, isUnique: true) self = .init(native: native) return old } #endif let isUnique = self.isUniquelyReferenced() return asNative.update(with: value, isUnique: isUnique) } @inlinable internal mutating func insert( _ element: __owned Element ) -> (inserted: Bool, memberAfterInsert: Element) { #if _runtime(_ObjC) guard isNative else { // Make sure we have space for an extra element. let cocoaMember = _bridgeAnythingToObjectiveC(element) let cocoa = asCocoa if let m = cocoa.member(for: cocoaMember) { return (false, _forceBridgeFromObjectiveC(m, Element.self)) } var native = _NativeSet(cocoa, capacity: cocoa.count + 1) native.insertNew(element, isUnique: true) self = .init(native: native) return (true, element) } #endif let (bucket, found) = asNative.find(element) if found { return (false, unsafe asNative.uncheckedElement(at: bucket)) } let isUnique = self.isUniquelyReferenced() asNative.insertNew(element, at: bucket, isUnique: isUnique) return (true, element) } @inlinable @discardableResult internal mutating func remove(at index: Index) -> Element { #if _runtime(_ObjC) guard isNative else { // We have to migrate the data first. But after we do so, the Cocoa // index becomes useless, so get the element first. let cocoa = asCocoa let cocoaMember = cocoa.member(for: index._asCocoa) let nativeMember = _forceBridgeFromObjectiveC(cocoaMember, Element.self) return _migrateToNative(cocoa, removing: nativeMember) } #endif let isUnique = isUniquelyReferenced() let bucket = asNative.validatedBucket(for: index) return unsafe asNative.uncheckedRemove(at: bucket, isUnique: isUnique) } @inlinable @discardableResult internal mutating func remove(_ member: Element) -> Element? { #if _runtime(_ObjC) guard isNative else { let cocoa = asCocoa let cocoaMember = _bridgeAnythingToObjectiveC(member) guard cocoa.contains(cocoaMember) else { return nil } return _migrateToNative(cocoa, removing: member) } #endif let (bucket, found) = asNative.find(member) guard found else { return nil } let isUnique = isUniquelyReferenced() return unsafe asNative.uncheckedRemove(at: bucket, isUnique: isUnique) } #if _runtime(_ObjC) @inlinable internal mutating func _migrateToNative( _ cocoa: __CocoaSet, removing member: Element ) -> Element { // FIXME(performance): fuse data migration and element deletion into one // operation. var native = _NativeSet(cocoa) let (bucket, found) = native.find(member) _precondition(found, "Bridging did not preserve equality") let old = unsafe native.uncheckedRemove(at: bucket, isUnique: true) _precondition(member == old, "Bridging did not preserve equality") self = .init(native: native) return old } #endif @inlinable internal mutating func removeAll(keepingCapacity keepCapacity: Bool) { if !keepCapacity { self = .init(native: _NativeSet()) return } guard count > 0 else { return } #if _runtime(_ObjC) guard isNative else { self = .init(native: _NativeSet(capacity: asCocoa.count)) return } #endif let isUnique = isUniquelyReferenced() asNative.removeAll(isUnique: isUnique) } } extension Set._Variant { /// Returns an iterator over the elements. /// /// - Complexity: O(1). @inlinable @inline(__always) internal __consuming func makeIterator() -> Set.Iterator { #if _runtime(_ObjC) guard isNative else { return Set.Iterator(_cocoa: asCocoa.makeIterator()) } #endif return Set.Iterator(_native: asNative.makeIterator()) } } extension Set._Variant { @_alwaysEmitIntoClient internal __consuming func filter( _ isIncluded: (Element) throws -> Bool ) rethrows -> _NativeSet { #if _runtime(_ObjC) guard isNative else { var result = _NativeSet() for cocoaElement in asCocoa { let nativeElement = _forceBridgeFromObjectiveC( cocoaElement, Element.self) if try isIncluded(nativeElement) { result.insertNew(nativeElement, isUnique: true) } } return result } #endif return try asNative.filter(isIncluded) } @_alwaysEmitIntoClient internal __consuming func intersection( _ other: Set ) -> _NativeSet { #if _runtime(_ObjC) switch (self.isNative, other._variant.isNative) { case (true, true): return asNative.intersection(other._variant.asNative) case (true, false): return asNative.genericIntersection(other) case (false, false): return _NativeSet(asCocoa).genericIntersection(other) case (false, true): // Note: It is tempting to implement this as `that.intersection(this)`, // but intersection isn't symmetric -- the result should only contain // elements from `self`. let that = other._variant.asNative var result = _NativeSet() for cocoaElement in asCocoa { let nativeElement = _forceBridgeFromObjectiveC( cocoaElement, Element.self) if that.contains(nativeElement) { result.insertNew(nativeElement, isUnique: true) } } return result } #else return asNative.intersection(other._variant.asNative) #endif } }