@@ -328,33 +328,43 @@ extension _CocoaSet: _SetBuffer {
328
328
internal var startIndex : Index {
329
329
@_effects ( releasenone)
330
330
get {
331
- return Index ( self , startIndex: ( ) )
331
+ let allKeys = _stdlib_NSSet_allObjects ( self . object)
332
+ return Index ( Index . Storage ( self , allKeys, 0 ) )
332
333
}
333
334
}
334
335
335
336
@usableFromInline // FIXME(cocoa-index): Should be inlinable
336
337
internal var endIndex : Index {
337
338
@_effects ( releasenone)
338
339
get {
339
- return Index ( self , endIndex: ( ) )
340
+ let allKeys = _stdlib_NSSet_allObjects ( self . object)
341
+ return Index ( Index . Storage ( self , allKeys, allKeys. value) )
340
342
}
341
343
}
342
344
343
345
@usableFromInline // FIXME(cocoa-index): Should be inlinable
344
346
@_effects ( releasenone)
345
- internal func index( after i: Index ) -> Index {
346
- var i = i
347
- formIndex ( after: & i)
348
- return i
347
+ internal func index( after index: Index ) -> Index {
348
+ var result = index
349
+ formIndex ( after: & result)
350
+ return result
351
+ }
352
+
353
+ internal func validate( _ index: Index ) {
354
+ _precondition ( index. storage. base. object === self . object,
355
+ " Invalid index " )
356
+ _precondition ( index. storage. currentKeyIndex < index. storage. allKeys. value,
357
+ " Attempt to access endIndex " )
349
358
}
350
359
351
360
@usableFromInline // FIXME(cocoa-index): Should be inlinable
352
361
@_effects ( releasenone)
353
- internal func formIndex( after i: inout Index ) {
354
- _precondition ( i. base. object === self . object, " Invalid index " )
355
- _precondition ( i. currentKeyIndex < i. allKeys. value,
356
- " Cannot increment endIndex " )
357
- i. currentKeyIndex += 1
362
+ internal func formIndex( after index: inout Index ) {
363
+ validate ( index)
364
+ let isUnique = index. isUniquelyReferenced ( )
365
+ if !isUnique { index. storage = index. copy ( ) }
366
+ let storage = index. storage // FIXME: rdar://problem/44863751
367
+ storage. currentKeyIndex += 1
358
368
}
359
369
360
370
@usableFromInline // FIXME(cocoa-index): Should be inlinable
@@ -369,16 +379,13 @@ extension _CocoaSet: _SetBuffer {
369
379
}
370
380
371
381
let allKeys = _stdlib_NSSet_allObjects ( object)
372
- var keyIndex = - 1
373
382
for i in 0 ..< allKeys. value {
374
383
if _stdlib_NSObject_isEqual ( element, allKeys [ i] ) {
375
- keyIndex = i
376
- break
384
+ return Index ( Index . Storage ( self , allKeys, i) )
377
385
}
378
386
}
379
- _sanityCheck ( keyIndex >= 0 ,
380
- " Key was found in fast path, but not found later? " )
381
- return Index ( self , allKeys, keyIndex)
387
+ _sanityCheckFailure (
388
+ " An NSSet member wasn't listed amongst its enumerated contents " )
382
389
}
383
390
384
391
@inlinable
@@ -401,9 +408,22 @@ extension _CocoaSet: _SetBuffer {
401
408
}
402
409
403
410
extension _CocoaSet {
404
- // FIXME(cocoa-index): Overhaul and make @_fixed_layout
411
+ @_fixed_layout
405
412
@usableFromInline
406
413
internal struct Index {
414
+ @usableFromInline
415
+ internal var storage : Storage
416
+
417
+ internal init ( _ storage: __owned Storage) {
418
+ self . storage = storage
419
+ }
420
+ }
421
+ }
422
+
423
+ extension _CocoaSet . Index {
424
+ // FIXME(cocoa-index): Try using an NSEnumerator to speed this up.
425
+ @usableFromInline
426
+ internal class Storage {
407
427
// Assumption: we rely on NSDictionary.getObjects when being
408
428
// repeatedly called on the same NSDictionary, returning items in the same
409
429
// order every time.
@@ -422,18 +442,6 @@ extension _CocoaSet {
422
442
/// Index into `allKeys`
423
443
internal var currentKeyIndex : Int
424
444
425
- internal init ( _ base: __owned _CocoaSet, startIndex: ( ) ) {
426
- self . base = base
427
- self . allKeys = _stdlib_NSSet_allObjects ( base. object)
428
- self . currentKeyIndex = 0
429
- }
430
-
431
- internal init ( _ base: __owned _CocoaSet, endIndex: ( ) ) {
432
- self . base = base
433
- self . allKeys = _stdlib_NSSet_allObjects ( base. object)
434
- self . currentKeyIndex = allKeys. value
435
- }
436
-
437
445
internal init (
438
446
_ base: __owned _CocoaSet,
439
447
_ allKeys: __owned _HeapBuffer< Int , AnyObject > ,
@@ -446,15 +454,28 @@ extension _CocoaSet {
446
454
}
447
455
}
448
456
457
+ extension _CocoaSet . Index {
458
+ @inlinable
459
+ internal mutating func isUniquelyReferenced( ) -> Bool {
460
+ return _isUnique_native ( & storage)
461
+ }
462
+
463
+ @usableFromInline
464
+ internal mutating func copy( ) -> Storage {
465
+ let storage = self . storage
466
+ return Storage ( storage. base, storage. allKeys, storage. currentKeyIndex)
467
+ }
468
+ }
469
+
449
470
extension _CocoaSet . Index {
450
471
@usableFromInline // FIXME(cocoa-index): Make inlinable
451
472
@nonobjc
452
473
internal var element : AnyObject {
453
474
@_effects ( readonly)
454
475
get {
455
- _precondition ( currentKeyIndex < allKeys. value,
476
+ _precondition ( storage . currentKeyIndex < storage . allKeys. value,
456
477
" Attempting to access Set elements using an invalid index " )
457
- return allKeys [ currentKeyIndex]
478
+ return storage . allKeys [ storage . currentKeyIndex]
458
479
}
459
480
}
460
481
@@ -463,7 +484,7 @@ extension _CocoaSet.Index {
463
484
internal var age : Int32 {
464
485
@_effects ( releasenone)
465
486
get {
466
- return _HashTable. age ( for: base. object)
487
+ return _HashTable. age ( for: storage . base. object)
467
488
}
468
489
}
469
490
}
@@ -472,19 +493,19 @@ extension _CocoaSet.Index: Equatable {
472
493
@usableFromInline // FIXME(cocoa-index): Make inlinable
473
494
@_effects ( readonly)
474
495
internal static func == ( lhs: _CocoaSet . Index , rhs: _CocoaSet . Index ) -> Bool {
475
- _precondition ( lhs. base. object === rhs. base. object,
496
+ _precondition ( lhs. storage . base. object === rhs. storage . base. object,
476
497
" Comparing indexes from different sets " )
477
- return lhs. currentKeyIndex == rhs. currentKeyIndex
498
+ return lhs. storage . currentKeyIndex == rhs. storage . currentKeyIndex
478
499
}
479
500
}
480
501
481
502
extension _CocoaSet . Index : Comparable {
482
503
@usableFromInline // FIXME(cocoa-index): Make inlinable
483
504
@_effects ( readonly)
484
505
internal static func < ( lhs: _CocoaSet . Index , rhs: _CocoaSet . Index ) -> Bool {
485
- _precondition ( lhs. base. object === rhs. base. object,
506
+ _precondition ( lhs. storage . base. object === rhs. storage . base. object,
486
507
" Comparing indexes from different sets " )
487
- return lhs. currentKeyIndex < rhs. currentKeyIndex
508
+ return lhs. storage . currentKeyIndex < rhs. storage . currentKeyIndex
488
509
}
489
510
}
490
511
0 commit comments