@@ -785,6 +785,60 @@ extension Dictionary {
785
785
removeValue ( forKey: key)
786
786
}
787
787
}
788
+ _modify {
789
+ // FIXME: This code should be moved to _variant, with Dictionary.subscript
790
+ // yielding `&_variant[key]`.
791
+
792
+ // Look up (empty or occupied) bucket corresponding to the given key.
793
+ let isUnique = _variant. isUniquelyReferenced ( )
794
+ var idealBucket = _variant. asNative. _bucket ( key)
795
+ var ( pos, found) = _variant. asNative. _find ( key, startBucket: idealBucket)
796
+
797
+ // Prepare storage.
798
+ // If `key` isn't in the dictionary yet, assume that this access will end
799
+ // up inserting it. Otherwise this can only be a removal or an in-place
800
+ // mutation. (If we guess wrong, we might needlessly rehash; that's fine.)
801
+ let ( _, rehashed) = _variant. ensureUniqueNative (
802
+ withCapacity: self . capacity + ( found ? 0 : 1 ) ,
803
+ isUnique: isUnique)
804
+ // FIXME: we should be able to make this a let; however, some of the
805
+ // low-level operations below are (incorrectly) marked as mutating.
806
+ var native = _variant. asNative
807
+ if rehashed {
808
+ // Key needs to be hashed again if storage has been resized.
809
+ _sanityCheck ( !found)
810
+ idealBucket = native. _bucket ( key)
811
+ ( pos, found) = native. _find ( key, startBucket: idealBucket)
812
+ _sanityCheck ( !found)
813
+ }
814
+ // FIXME: Mark this entry as being modified in hash table metadata
815
+ // so that lldb can recognize it's not valid.
816
+
817
+ // Move the old value (if any) out of storage, wrapping it into an
818
+ // optional before yielding it.
819
+ var value : Value ? = found ? native. moveValue ( at: pos. bucket) : nil
820
+ yield & value
821
+
822
+ // Value is now potentially different. Check which one of the four
823
+ // possible cases apply.
824
+ switch ( found, value != nil ) {
825
+ case ( true , true ) : // Mutation
826
+ // Initialize storage to new value.
827
+ ( native. values + pos. bucket) . initialize ( to: value!)
828
+ case ( true , false ) : // Removal
829
+ // We've already removed the value; deinitialize and remove the key too.
830
+ native. destroyHalfEntry ( at: pos. bucket)
831
+ native. _deleteDestroyed ( idealBucket: idealBucket, bucket: pos. bucket)
832
+ case ( false , true ) : // Insertion
833
+ // Insert the new entry at the correct place.
834
+ // We've already ensured we have enough capacity.
835
+ native. initializeKey ( key, value: value!, at: pos. bucket)
836
+ native. count += 1
837
+ case ( false , false ) : // Noop
838
+ // Easy!
839
+ break
840
+ }
841
+ }
788
842
}
789
843
}
790
844
@@ -2369,6 +2423,21 @@ internal struct _NativeDictionary<Key, Value> {
2369
2423
_storage. initializedEntries [ i] = false
2370
2424
}
2371
2425
2426
+ @inlinable
2427
+ internal func moveValue( at bucket: Int ) -> Value {
2428
+ defer { _fixLifetime ( self ) }
2429
+ return ( values + bucket) . move ( )
2430
+ }
2431
+
2432
+ // This assumes the value is already deinitialized.
2433
+ @inlinable
2434
+ internal func destroyHalfEntry( at bucket: Int ) {
2435
+ _sanityCheck ( isInitializedEntry ( at: bucket) )
2436
+ defer { _fixLifetime ( self ) }
2437
+ ( keys + bucket) . deinitialize ( count: 1 )
2438
+ _storage. initializedEntries [ bucket] = false
2439
+ }
2440
+
2372
2441
@usableFromInline @_transparent
2373
2442
internal func initializeKey( _ k: Key , value v: Value , at i: Int ) {
2374
2443
_sanityCheck ( !isInitializedEntry( at: i) )
@@ -2633,6 +2702,13 @@ extension _NativeDictionary where Key: Hashable {
2633
2702
2634
2703
// remove the element
2635
2704
destroyEntry ( at: bucket)
2705
+ _deleteDestroyed ( idealBucket: idealBucket, bucket: bucket)
2706
+ }
2707
+
2708
+ @inlinable // FIXME(sil-serialize-all)
2709
+ internal mutating func _deleteDestroyed( idealBucket: Int , bucket: Int ) {
2710
+ _sanityCheck ( !isInitializedEntry( at: bucket) , " expected initialized entry " )
2711
+
2636
2712
self . count -= 1
2637
2713
2638
2714
// If we've put a hole in a chain of contiguous elements, some
@@ -3283,14 +3359,26 @@ extension Dictionary._Variant: _DictionaryBuffer {
3283
3359
internal mutating func _ensureUniqueNative(
3284
3360
withBucketCount desiredBucketCount: Int
3285
3361
) -> ( reallocated: Bool , capacityChanged: Bool ) {
3286
- let oldBucketCount = asNative. bucketCount
3287
3362
let isUnique = isUniquelyReferenced ( )
3363
+ return _ensureUniqueNative (
3364
+ withBucketCount: desiredBucketCount,
3365
+ isUnique: isUnique)
3366
+ }
3367
+
3368
+ @inline ( __always)
3369
+ @inlinable // FIXME(sil-serialize-all)
3370
+ internal mutating func _ensureUniqueNative(
3371
+ withBucketCount desiredBucketCount: Int ,
3372
+ isUnique: Bool
3373
+ ) -> ( reallocated: Bool , capacityChanged: Bool ) {
3374
+ let oldBucketCount = asNative. bucketCount
3288
3375
if oldBucketCount >= desiredBucketCount && isUnique {
3289
3376
return ( reallocated: false , capacityChanged: false )
3290
3377
}
3291
3378
3292
3379
let oldDictionary = asNative
3293
- var newDictionary = _NativeDictionary < Key , Value > ( bucketCount: desiredBucketCount)
3380
+ var newDictionary = _NativeDictionary < Key , Value > (
3381
+ bucketCount: desiredBucketCount)
3294
3382
let newBucketCount = newDictionary. bucketCount
3295
3383
for i in 0 ..< oldBucketCount {
3296
3384
if oldDictionary. isInitializedEntry ( at: i) {
@@ -3309,7 +3397,9 @@ extension Dictionary._Variant: _DictionaryBuffer {
3309
3397
newDictionary. count = oldDictionary. count
3310
3398
3311
3399
self = . native( newDictionary)
3312
- return ( reallocated: true , capacityChanged: oldBucketCount != newBucketCount)
3400
+ return (
3401
+ reallocated: true ,
3402
+ capacityChanged: oldBucketCount != newBucketCount)
3313
3403
}
3314
3404
3315
3405
@inline ( __always)
@@ -3323,6 +3413,18 @@ extension Dictionary._Variant: _DictionaryBuffer {
3323
3413
return ensureUniqueNative ( withBucketCount: bucketCount)
3324
3414
}
3325
3415
3416
+ @inline ( __always)
3417
+ @inlinable // FIXME(sil-serialize-all)
3418
+ internal mutating func ensureUniqueNative(
3419
+ withCapacity minimumCapacity: Int ,
3420
+ isUnique: Bool
3421
+ ) -> ( reallocated: Bool , capacityChanged: Bool ) {
3422
+ let bucketCount = _NativeDictionary < Key , Value > . bucketCount (
3423
+ forCapacity: minimumCapacity,
3424
+ maxLoadFactorInverse: _hashContainerDefaultMaxLoadFactorInverse)
3425
+ return _ensureUniqueNative ( withBucketCount: bucketCount, isUnique: isUnique)
3426
+ }
3427
+
3326
3428
/// Ensure this we hold a unique reference to a native dictionary
3327
3429
/// having at least `minimumCapacity` elements.
3328
3430
@inlinable // FIXME(sil-serialize-all)
0 commit comments