Skip to content

Commit 261aca3

Browse files
authored
Merge pull request #19612 from lorentey/opaque-cocoa-indices
[stdlib] Set, Dictionary: Resilient Cocoa indices, vol 2
2 parents 805f63a + df73981 commit 261aca3

File tree

6 files changed

+180
-49
lines changed

6 files changed

+180
-49
lines changed

stdlib/public/core/Dictionary.swift

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,11 @@ extension Dictionary: Collection {
647647
return _variant.index(after: i)
648648
}
649649

650+
@inlinable
651+
public func formIndex(after i: inout Index) {
652+
_variant.formIndex(after: &i)
653+
}
654+
650655
/// Returns the index for the given key.
651656
///
652657
/// If the given key is found in the dictionary, this method returns an index
@@ -1350,6 +1355,11 @@ extension Dictionary {
13501355
return _variant.index(after: i)
13511356
}
13521357

1358+
@inlinable
1359+
public func formIndex(after i: inout Index) {
1360+
_variant.formIndex(after: &i)
1361+
}
1362+
13531363
@inlinable
13541364
public subscript(position: Index) -> Element {
13551365
return _variant.key(at: position)
@@ -1456,6 +1466,11 @@ extension Dictionary {
14561466
return _variant.index(after: i)
14571467
}
14581468

1469+
@inlinable
1470+
public func formIndex(after i: inout Index) {
1471+
_variant.formIndex(after: &i)
1472+
}
1473+
14591474
@inlinable
14601475
public subscript(position: Index) -> Element {
14611476
// FIXME(accessors): Provide a _read
@@ -1827,6 +1842,14 @@ extension Dictionary.Index {
18271842
_conditionallyUnreachable()
18281843
}
18291844
}
1845+
1846+
@inlinable
1847+
@inline(__always)
1848+
internal mutating func _isUniquelyReferenced() -> Bool {
1849+
defer { _fixLifetime(self) }
1850+
var handle = _asCocoa.handleBitPattern
1851+
return handle == 0 || _isUnique_native(&handle)
1852+
}
18301853
#endif
18311854

18321855
@usableFromInline @_transparent
@@ -1843,14 +1866,27 @@ extension Dictionary.Index {
18431866
}
18441867

18451868
#if _runtime(_ObjC)
1846-
@usableFromInline @_transparent
1869+
@usableFromInline
18471870
internal var _asCocoa: _CocoaDictionary.Index {
1848-
switch _variant {
1849-
case .native:
1850-
_preconditionFailure(
1851-
"Attempting to access Dictionary elements using an invalid index")
1852-
case .cocoa(let cocoaIndex):
1853-
return cocoaIndex
1871+
@_transparent
1872+
get {
1873+
switch _variant {
1874+
case .native:
1875+
_preconditionFailure(
1876+
"Attempting to access Dictionary elements using an invalid index")
1877+
case .cocoa(let cocoaIndex):
1878+
return cocoaIndex
1879+
}
1880+
}
1881+
_modify {
1882+
guard case .cocoa(var cocoa) = _variant else {
1883+
_preconditionFailure(
1884+
"Attempting to access Dictionary elements using an invalid index")
1885+
}
1886+
let dummy = _HashTable.Index(bucket: _HashTable.Bucket(offset: 0), age: 0)
1887+
_variant = .native(dummy)
1888+
yield &cocoa
1889+
_variant = .cocoa(cocoa)
18541890
}
18551891
}
18561892
#endif
@@ -1897,8 +1933,8 @@ extension Dictionary.Index: Comparable {
18971933
}
18981934

18991935
extension Dictionary.Index: Hashable {
1900-
@_effects(readonly) // FIXME(cocoa-index): Make inlinable
1901-
public func hash(into hasher: inout Hasher) {
1936+
public // FIXME(cocoa-index): Make inlinable
1937+
func hash(into hasher: inout Hasher) {
19021938
#if _runtime(_ObjC)
19031939
switch _variant {
19041940
case .native(let nativeIndex):

stdlib/public/core/DictionaryBridging.swift

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,8 @@ extension _CocoaDictionary: _DictionaryBuffer {
471471
@usableFromInline // FIXME(cocoa-index): Should be inlinable
472472
@_effects(releasenone)
473473
internal func index(after index: Index) -> Index {
474-
var result = index
475-
formIndex(after: &result)
474+
var result = index.copy()
475+
formIndex(after: &result, isUnique: true)
476476
return result
477477
}
478478

@@ -483,11 +483,9 @@ extension _CocoaDictionary: _DictionaryBuffer {
483483
}
484484

485485
@usableFromInline // FIXME(cocoa-index): Should be inlinable
486-
@_effects(releasenone)
487-
internal func formIndex(after index: inout Index) {
486+
internal func formIndex(after index: inout Index, isUnique: Bool) {
488487
validate(index)
489-
let isUnique = index.isUniquelyReferenced()
490-
if !isUnique { index.storage = index.copy() }
488+
if !isUnique { index = index.copy() }
491489
let storage = index.storage // FIXME: rdar://problem/44863751
492490
storage.currentKeyIndex += 1
493491
}
@@ -575,17 +573,34 @@ extension _CocoaDictionary {
575573
@usableFromInline
576574
internal struct Index {
577575
@usableFromInline
578-
internal var storage: Storage
576+
internal var _object: Builtin.BridgeObject
577+
@usableFromInline
578+
internal var _storage: Builtin.BridgeObject
579+
580+
internal var object: AnyObject {
581+
@inline(__always)
582+
get {
583+
return _bridgeObject(toNonTaggedObjC: _object)
584+
}
585+
}
586+
587+
internal var storage: Storage {
588+
@inline(__always)
589+
get {
590+
let storage = _bridgeObject(toNative: _storage)
591+
return unsafeDowncast(storage, to: Storage.self)
592+
}
593+
}
579594

580595
internal init(_ storage: Storage) {
581-
self.storage = storage
596+
self._object = _bridgeObject(fromNonTaggedObjC: storage.base.object)
597+
self._storage = _bridgeObject(fromNative: storage)
582598
}
583599
}
584600
}
585601

586602
extension _CocoaDictionary.Index {
587603
// FIXME(cocoa-index): Try using an NSEnumerator to speed this up.
588-
@usableFromInline
589604
internal class Storage {
590605
// Assumption: we rely on NSDictionary.getObjects when being
591606
// repeatedly called on the same NSDictionary, returning items in the same
@@ -618,15 +633,21 @@ extension _CocoaDictionary.Index {
618633
}
619634

620635
extension _CocoaDictionary.Index {
621-
@inlinable
622-
internal mutating func isUniquelyReferenced() -> Bool {
623-
return _isUnique_native(&storage)
636+
@usableFromInline
637+
internal var handleBitPattern: UInt {
638+
@_effects(readonly)
639+
get {
640+
return unsafeBitCast(storage, to: UInt.self)
641+
}
624642
}
625643

626644
@usableFromInline
627-
internal mutating func copy() -> Storage {
645+
internal func copy() -> _CocoaDictionary.Index {
628646
let storage = self.storage
629-
return Storage(storage.base, storage.allKeys, storage.currentKeyIndex)
647+
return _CocoaDictionary.Index(Storage(
648+
storage.base,
649+
storage.allKeys,
650+
storage.currentKeyIndex))
630651
}
631652
}
632653

@@ -647,7 +668,7 @@ extension _CocoaDictionary.Index {
647668
internal var age: Int32 {
648669
@_effects(readonly)
649670
get {
650-
return _HashTable.age(for: storage.base.object)
671+
return _HashTable.age(for: object)
651672
}
652673
}
653674
}

stdlib/public/core/DictionaryVariant.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,20 @@ extension Dictionary._Variant: _DictionaryBuffer {
189189
}
190190
}
191191

192+
@inlinable
193+
internal func formIndex(after index: inout Index) {
194+
switch self {
195+
case .native(let native):
196+
index = native.index(after: index)
197+
#if _runtime(_ObjC)
198+
case .cocoa(let cocoa):
199+
cocoaPath()
200+
let isUnique = index._isUniquelyReferenced()
201+
cocoa.formIndex(after: &index._asCocoa, isUnique: isUnique)
202+
#endif
203+
}
204+
}
205+
192206
@inlinable
193207
@inline(__always)
194208
internal func index(forKey key: Key) -> Index? {

stdlib/public/core/Set.swift

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,11 @@ extension Set: Collection {
340340
return _variant.index(after: i)
341341
}
342342

343+
@inlinable
344+
public func formIndex(after i: inout Index) {
345+
_variant.formIndex(after: &i)
346+
}
347+
343348
// APINAMING: complexity docs are broadly missing in this file.
344349

345350
/// Returns the index of the given element in the set, or `nil` if the
@@ -1343,6 +1348,14 @@ extension Set.Index {
13431348
_conditionallyUnreachable()
13441349
}
13451350
}
1351+
1352+
@inlinable
1353+
@inline(__always)
1354+
internal mutating func _isUniquelyReferenced() -> Bool {
1355+
defer { _fixLifetime(self) }
1356+
var handle = _asCocoa.handleBitPattern
1357+
return handle == 0 || _isUnique_native(&handle)
1358+
}
13461359
#endif
13471360

13481361
@usableFromInline @_transparent
@@ -1359,14 +1372,27 @@ extension Set.Index {
13591372
}
13601373

13611374
#if _runtime(_ObjC)
1362-
@usableFromInline @_transparent
1375+
@usableFromInline
13631376
internal var _asCocoa: _CocoaSet.Index {
1364-
switch _variant {
1365-
case .native:
1366-
_preconditionFailure(
1367-
"Attempting to access Set elements using an invalid index")
1368-
case .cocoa(let cocoaIndex):
1369-
return cocoaIndex
1377+
@_transparent
1378+
get {
1379+
switch _variant {
1380+
case .native:
1381+
_preconditionFailure(
1382+
"Attempting to access Set elements using an invalid index")
1383+
case .cocoa(let cocoaIndex):
1384+
return cocoaIndex
1385+
}
1386+
}
1387+
_modify {
1388+
guard case .cocoa(var cocoa) = _variant else {
1389+
_preconditionFailure(
1390+
"Attempting to access Set elements using an invalid index")
1391+
}
1392+
let dummy = _HashTable.Index(bucket: _HashTable.Bucket(offset: 0), age: 0)
1393+
_variant = .native(dummy)
1394+
yield &cocoa
1395+
_variant = .cocoa(cocoa)
13701396
}
13711397
}
13721398
#endif
@@ -1418,8 +1444,8 @@ extension Set.Index: Hashable {
14181444
///
14191445
/// - Parameter hasher: The hasher to use when combining the components
14201446
/// of this instance.
1421-
@_effects(readonly) // FIXME(cocoa-index): Make inlinable
1422-
public func hash(into hasher: inout Hasher) {
1447+
public // FIXME(cocoa-index): Make inlinable
1448+
func hash(into hasher: inout Hasher) {
14231449
#if _runtime(_ObjC)
14241450
switch _variant {
14251451
case .native(let nativeIndex):

stdlib/public/core/SetBridging.swift

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,8 @@ extension _CocoaSet: _SetBuffer {
345345
@usableFromInline // FIXME(cocoa-index): Should be inlinable
346346
@_effects(releasenone)
347347
internal func index(after index: Index) -> Index {
348-
var result = index
349-
formIndex(after: &result)
348+
var result = index.copy()
349+
formIndex(after: &result, isUnique: true)
350350
return result
351351
}
352352

@@ -358,11 +358,9 @@ extension _CocoaSet: _SetBuffer {
358358
}
359359

360360
@usableFromInline // FIXME(cocoa-index): Should be inlinable
361-
@_effects(releasenone)
362-
internal func formIndex(after index: inout Index) {
361+
internal func formIndex(after index: inout Index, isUnique: Bool) {
363362
validate(index)
364-
let isUnique = index.isUniquelyReferenced()
365-
if !isUnique { index.storage = index.copy() }
363+
if !isUnique { index = index.copy() }
366364
let storage = index.storage // FIXME: rdar://problem/44863751
367365
storage.currentKeyIndex += 1
368366
}
@@ -412,17 +410,34 @@ extension _CocoaSet {
412410
@usableFromInline
413411
internal struct Index {
414412
@usableFromInline
415-
internal var storage: Storage
413+
internal var _object: Builtin.BridgeObject
414+
@usableFromInline
415+
internal var _storage: Builtin.BridgeObject
416+
417+
internal var object: AnyObject {
418+
@inline(__always)
419+
get {
420+
return _bridgeObject(toNonTaggedObjC: _object)
421+
}
422+
}
423+
424+
internal var storage: Storage {
425+
@inline(__always)
426+
get {
427+
let storage = _bridgeObject(toNative: _storage)
428+
return unsafeDowncast(storage, to: Storage.self)
429+
}
430+
}
416431

417432
internal init(_ storage: __owned Storage) {
418-
self.storage = storage
433+
self._object = _bridgeObject(fromNonTaggedObjC: storage.base.object)
434+
self._storage = _bridgeObject(fromNative: storage)
419435
}
420436
}
421437
}
422438

423439
extension _CocoaSet.Index {
424440
// FIXME(cocoa-index): Try using an NSEnumerator to speed this up.
425-
@usableFromInline
426441
internal class Storage {
427442
// Assumption: we rely on NSDictionary.getObjects when being
428443
// repeatedly called on the same NSDictionary, returning items in the same
@@ -455,15 +470,20 @@ extension _CocoaSet.Index {
455470
}
456471

457472
extension _CocoaSet.Index {
458-
@inlinable
459-
internal mutating func isUniquelyReferenced() -> Bool {
460-
return _isUnique_native(&storage)
473+
@usableFromInline
474+
internal var handleBitPattern: UInt {
475+
@_effects(readonly)
476+
get {
477+
return unsafeBitCast(storage, to: UInt.self)
478+
}
461479
}
462480

463-
@usableFromInline
464-
internal mutating func copy() -> Storage {
481+
internal func copy() -> _CocoaSet.Index {
465482
let storage = self.storage
466-
return Storage(storage.base, storage.allKeys, storage.currentKeyIndex)
483+
return _CocoaSet.Index(Storage(
484+
storage.base,
485+
storage.allKeys,
486+
storage.currentKeyIndex))
467487
}
468488
}
469489

@@ -484,7 +504,7 @@ extension _CocoaSet.Index {
484504
internal var age: Int32 {
485505
@_effects(releasenone)
486506
get {
487-
return _HashTable.age(for: storage.base.object)
507+
return _HashTable.age(for: object)
488508
}
489509
}
490510
}

0 commit comments

Comments
 (0)