blob: c091de0edb3dcf102edf768a800c2889afef9cc8 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// This protocol is only used for compile-time checks that
/// every buffer type implements all required operations.
internal protocol _DictionaryBuffer {
associatedtype Key
associatedtype Value
associatedtype Index
var startIndex: Index { get }
var endIndex: Index { get }
func index(after i: Index) -> Index
func index(forKey key: Key) -> Index?
var count: Int { get }
func contains(_ key: Key) -> Bool
func lookup(_ key: Key) -> Value?
func lookup(_ index: Index) -> (key: Key, value: Value)
func key(at index: Index) -> Key
func value(at index: Index) -> Value
}
extension Dictionary {
@usableFromInline
@_fixed_layout
internal struct _Variant {
@usableFromInline
internal var object: _BridgeStorage<__RawDictionaryStorage>
@inlinable
@inline(__always)
init(native: __owned _NativeDictionary<Key, Value>) {
self.object = _BridgeStorage(native: native._storage)
}
@inlinable
@inline(__always)
init(dummy: Void) {
#if arch(i386) || arch(arm)
self.init(native: _NativeDictionary())
#else
self.object = _BridgeStorage(taggedPayload: 0)
#endif
}
#if _runtime(_ObjC)
@inlinable
@inline(__always)
init(cocoa: __owned __CocoaDictionary) {
self.object = _BridgeStorage(objC: cocoa.object)
}
#endif
}
}
extension Dictionary._Variant {
#if _runtime(_ObjC)
@usableFromInline @_transparent
internal var guaranteedNative: Bool {
return _canBeClass(Key.self) == 0 || _canBeClass(Value.self) == 0
}
#endif
@inlinable
internal mutating func isUniquelyReferenced() -> Bool {
return object.isUniquelyReferencedUnflaggedNative()
}
#if _runtime(_ObjC)
@usableFromInline @_transparent
internal var isNative: Bool {
if guaranteedNative { return true }
return object.isUnflaggedNative
}
#endif
@usableFromInline @_transparent
internal var asNative: _NativeDictionary<Key, Value> {
get {
return _NativeDictionary<Key, Value>(object.unflaggedNativeInstance)
}
set {
self = .init(native: newValue)
}
_modify {
var native = _NativeDictionary<Key, Value>(object.unflaggedNativeInstance)
self = .init(dummy: ())
defer { object = .init(native: native._storage) }
yield &native
}
}
#if _runtime(_ObjC)
@inlinable
internal var asCocoa: __CocoaDictionary {
return __CocoaDictionary(object.objCInstance)
}
#endif
/// 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: _NativeDictionary(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 Dictionary._Variant: _DictionaryBuffer {
@usableFromInline
internal typealias Element = (key: Key, value: Value)
@usableFromInline
internal typealias Index = Dictionary<Key, Value>.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(forKey key: Key) -> Index? {
#if _runtime(_ObjC)
guard isNative else {
let cocoaKey = _bridgeAnythingToObjectiveC(key)
guard let index = asCocoa.index(forKey: cocoaKey) else { return nil }
return Index(_cocoa: index)
}
#endif
return asNative.index(forKey: key)
}
@inlinable
internal var count: Int {
@inline(__always)
get {
#if _runtime(_ObjC)
guard isNative else {
return asCocoa.count
}
#endif
return asNative.count
}
}
@inlinable
@inline(__always)
func contains(_ key: Key) -> Bool {
#if _runtime(_ObjC)
guard isNative else {
let cocoaKey = _bridgeAnythingToObjectiveC(key)
return asCocoa.contains(cocoaKey)
}
#endif
return asNative.contains(key)
}
@inlinable
@inline(__always)
func lookup(_ key: Key) -> Value? {
#if _runtime(_ObjC)
guard isNative else {
let cocoaKey = _bridgeAnythingToObjectiveC(key)
guard let cocoaValue = asCocoa.lookup(cocoaKey) else { return nil }
return _forceBridgeFromObjectiveC(cocoaValue, Value.self)
}
#endif
return asNative.lookup(key)
}
@inlinable
@inline(__always)
func lookup(_ index: Index) -> (key: Key, value: Value) {
#if _runtime(_ObjC)
guard isNative else {
let (cocoaKey, cocoaValue) = asCocoa.lookup(index._asCocoa)
let nativeKey = _forceBridgeFromObjectiveC(cocoaKey, Key.self)
let nativeValue = _forceBridgeFromObjectiveC(cocoaValue, Value.self)
return (nativeKey, nativeValue)
}
#endif
return asNative.lookup(index)
}
@inlinable
@inline(__always)
func key(at index: Index) -> Key {
#if _runtime(_ObjC)
guard isNative else {
let cocoaKey = asCocoa.key(at: index._asCocoa)
return _forceBridgeFromObjectiveC(cocoaKey, Key.self)
}
#endif
return asNative.key(at: index)
}
@inlinable
@inline(__always)
func value(at index: Index) -> Value {
#if _runtime(_ObjC)
guard isNative else {
let cocoaValue = asCocoa.value(at: index._asCocoa)
return _forceBridgeFromObjectiveC(cocoaValue, Value.self)
}
#endif
return asNative.value(at: index)
}
}
extension Dictionary._Variant {
@inlinable
internal subscript(key: Key) -> Value? {
@inline(__always)
get {
return lookup(key)
}
@inline(__always)
_modify {
#if _runtime(_ObjC)
guard isNative else {
let cocoa = asCocoa
var native = _NativeDictionary<Key, Value>(
cocoa, capacity: cocoa.count + 1)
self = .init(native: native)
yield &native[key, isUnique: true]
return
}
#endif
let isUnique = isUniquelyReferenced()
yield &asNative[key, isUnique: isUnique]
}
}
}
extension Dictionary._Variant {
/// Same as find(_:), except assume a corresponding key/value pair will be
/// inserted if it doesn't already exist, and mutated if it does exist. When
/// this function returns, the storage is guaranteed to be native, uniquely
/// held, and with enough capacity for a single insertion (if the key isn't
/// already in the dictionary.)
@inlinable
@inline(__always)
internal mutating func mutatingFind(
_ key: Key
) -> (bucket: _NativeDictionary<Key, Value>.Bucket, found: Bool) {
#if _runtime(_ObjC)
guard isNative else {
let cocoa = asCocoa
var native = _NativeDictionary<Key, Value>(
cocoa, capacity: cocoa.count + 1)
let result = native.mutatingFind(key, isUnique: true)
self = .init(native: native)
return result
}
#endif
let isUnique = isUniquelyReferenced()
return asNative.mutatingFind(key, isUnique: isUnique)
}
@inlinable
@inline(__always)
internal mutating func ensureUniqueNative() -> _NativeDictionary<Key, Value> {
#if _runtime(_ObjC)
guard isNative else {
let native = _NativeDictionary<Key, Value>(asCocoa)
self = .init(native: native)
return native
}
#endif
let isUnique = isUniquelyReferenced()
if !isUnique {
asNative.copy()
}
return asNative
}
@inlinable
internal mutating func updateValue(
_ value: __owned Value,
forKey key: Key
) -> Value? {
#if _runtime(_ObjC)
guard isNative else {
// Make sure we have space for an extra element.
let cocoa = asCocoa
var native = _NativeDictionary<Key, Value>(
cocoa,
capacity: cocoa.count + 1)
let result = native.updateValue(value, forKey: key, isUnique: true)
self = .init(native: native)
return result
}
#endif
let isUnique = self.isUniquelyReferenced()
return asNative.updateValue(value, forKey: key, isUnique: isUnique)
}
@inlinable
internal mutating func setValue(_ value: __owned Value, forKey key: Key) {
#if _runtime(_ObjC)
if !isNative {
// Make sure we have space for an extra element.
let cocoa = asCocoa
self = .init(native: _NativeDictionary<Key, Value>(
cocoa,
capacity: cocoa.count + 1))
}
#endif
let isUnique = self.isUniquelyReferenced()
asNative.setValue(value, forKey: key, isUnique: isUnique)
}
@inlinable
@_semantics("optimize.sil.specialize.generic.size.never")
internal mutating func remove(at index: Index) -> Element {
// FIXME(performance): fuse data migration and element deletion into one
// operation.
let native = ensureUniqueNative()
let bucket = native.validatedBucket(for: index)
return asNative.uncheckedRemove(at: bucket, isUnique: true)
}
@inlinable
internal mutating func removeValue(forKey key: Key) -> Value? {
#if _runtime(_ObjC)
guard isNative else {
let cocoaKey = _bridgeAnythingToObjectiveC(key)
let cocoa = asCocoa
guard cocoa.lookup(cocoaKey) != nil else { return nil }
var native = _NativeDictionary<Key, Value>(cocoa)
let (bucket, found) = native.find(key)
_precondition(found, "Bridging did not preserve equality")
let old = native.uncheckedRemove(at: bucket, isUnique: true).value
self = .init(native: native)
return old
}
#endif
let (bucket, found) = asNative.find(key)
guard found else { return nil }
let isUnique = isUniquelyReferenced()
return asNative.uncheckedRemove(at: bucket, isUnique: isUnique).value
}
@inlinable
@_semantics("optimize.sil.specialize.generic.size.never")
internal mutating func removeAll(keepingCapacity keepCapacity: Bool) {
if !keepCapacity {
self = .init(native: _NativeDictionary())
return
}
guard count > 0 else { return }
#if _runtime(_ObjC)
guard isNative else {
self = .init(native: _NativeDictionary(capacity: asCocoa.count))
return
}
#endif
let isUnique = isUniquelyReferenced()
asNative.removeAll(isUnique: isUnique)
}
}
extension Dictionary._Variant {
/// Returns an iterator over the `(Key, Value)` pairs.
///
/// - Complexity: O(1).
@inlinable
@inline(__always)
__consuming internal func makeIterator() -> Dictionary<Key, Value>.Iterator {
#if _runtime(_ObjC)
guard isNative else {
return Dictionary.Iterator(_cocoa: asCocoa.makeIterator())
}
#endif
return Dictionary.Iterator(_native: asNative.makeIterator())
}
}
extension Dictionary._Variant {
@inlinable
internal func mapValues<T>(
_ transform: (Value) throws -> T
) rethrows -> _NativeDictionary<Key, T> {
#if _runtime(_ObjC)
guard isNative else {
return try asCocoa.mapValues(transform)
}
#endif
return try asNative.mapValues(transform)
}
@inlinable
internal mutating func merge<S: Sequence>(
_ keysAndValues: __owned S,
uniquingKeysWith combine: (Value, Value) throws -> Value
) rethrows where S.Element == (Key, Value) {
#if _runtime(_ObjC)
guard isNative else {
var native = _NativeDictionary<Key, Value>(asCocoa)
try native.merge(
keysAndValues,
isUnique: true,
uniquingKeysWith: combine)
self = .init(native: native)
return
}
#endif
let isUnique = isUniquelyReferenced()
try asNative.merge(
keysAndValues,
isUnique: isUnique,
uniquingKeysWith: combine)
}
}