@@ -785,6 +785,61 @@ 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. (If we guess wrong, we might needlessly expand
800
+ // storage; that's fine.) Otherwise this can only be a removal or an
801
+ // in-place mutation.
802
+ let ( _, rehashed) = _variant. ensureUniqueNative (
803
+ withCapacity: self . count + ( found ? 0 : 1 ) ,
804
+ isUnique: isUnique)
805
+ // FIXME: we should be able to make this a let; however, some of the
806
+ // low-level operations below are (incorrectly) marked as mutating.
807
+ var native = _variant. asNative
808
+ if rehashed {
809
+ // Key needs to be hashed again if storage has been resized.
810
+ _sanityCheck ( !found)
811
+ idealBucket = native. _bucket ( key)
812
+ ( pos, found) = native. _find ( key, startBucket: idealBucket)
813
+ _sanityCheck ( !found)
814
+ }
815
+ // FIXME: Mark this entry as being modified in hash table metadata
816
+ // so that lldb can recognize it's not valid.
817
+
818
+ // Move the old value (if any) out of storage, wrapping it into an
819
+ // optional before yielding it.
820
+ var value : Value ? = found ? native. moveValue ( at: pos. bucket) : nil
821
+ yield & value
822
+
823
+ // Value is now potentially different. Check which one of the four
824
+ // possible cases apply.
825
+ switch ( found, value != nil ) {
826
+ case ( true , true ) : // Mutation
827
+ // Initialize storage to new value.
828
+ ( native. values + pos. bucket) . initialize ( to: value!)
829
+ case ( true , false ) : // Removal
830
+ // We've already removed the value; deinitialize and remove the key too.
831
+ native. destroyHalfEntry ( at: pos. bucket)
832
+ native. _deleteDestroyed ( idealBucket: idealBucket, bucket: pos. bucket)
833
+ case ( false , true ) : // Insertion
834
+ // Insert the new entry at the correct place.
835
+ // We've already ensured we have enough capacity.
836
+ native. initializeKey ( key, value: value!, at: pos. bucket)
837
+ native. count += 1
838
+ case ( false , false ) : // Noop
839
+ // Easy!
840
+ break
841
+ }
842
+ }
788
843
}
789
844
}
790
845
@@ -2224,6 +2279,21 @@ internal struct _NativeDictionary<Key, Value> {
2224
2279
_storage. initializedEntries [ i] = false
2225
2280
}
2226
2281
2282
+ @inlinable
2283
+ internal func moveValue( at bucket: Int ) -> Value {
2284
+ defer { _fixLifetime ( self ) }
2285
+ return ( values + bucket) . move ( )
2286
+ }
2287
+
2288
+ // This assumes the value is already deinitialized.
2289
+ @inlinable
2290
+ internal func destroyHalfEntry( at bucket: Int ) {
2291
+ _sanityCheck ( isInitializedEntry ( at: bucket) )
2292
+ defer { _fixLifetime ( self ) }
2293
+ ( keys + bucket) . deinitialize ( count: 1 )
2294
+ _storage. initializedEntries [ bucket] = false
2295
+ }
2296
+
2227
2297
@usableFromInline @_transparent
2228
2298
internal func initializeKey( _ k: Key , value v: Value , at i: Int ) {
2229
2299
_sanityCheck ( !isInitializedEntry( at: i) )
@@ -2488,6 +2558,13 @@ extension _NativeDictionary where Key: Hashable {
2488
2558
2489
2559
// remove the element
2490
2560
destroyEntry ( at: bucket)
2561
+ _deleteDestroyed ( idealBucket: idealBucket, bucket: bucket)
2562
+ }
2563
+
2564
+ @inlinable // FIXME(sil-serialize-all)
2565
+ internal mutating func _deleteDestroyed( idealBucket: Int , bucket: Int ) {
2566
+ _sanityCheck ( !isInitializedEntry( at: bucket) , " expected initialized entry " )
2567
+
2491
2568
self . count -= 1
2492
2569
2493
2570
// If we've put a hole in a chain of contiguous elements, some
@@ -3138,14 +3215,26 @@ extension Dictionary._Variant: _DictionaryBuffer {
3138
3215
internal mutating func _ensureUniqueNative(
3139
3216
withBucketCount desiredBucketCount: Int
3140
3217
) -> ( reallocated: Bool , capacityChanged: Bool ) {
3141
- let oldBucketCount = asNative. bucketCount
3142
3218
let isUnique = isUniquelyReferenced ( )
3219
+ return _ensureUniqueNative (
3220
+ withBucketCount: desiredBucketCount,
3221
+ isUnique: isUnique)
3222
+ }
3223
+
3224
+ @inline ( __always)
3225
+ @inlinable // FIXME(sil-serialize-all)
3226
+ internal mutating func _ensureUniqueNative(
3227
+ withBucketCount desiredBucketCount: Int ,
3228
+ isUnique: Bool
3229
+ ) -> ( reallocated: Bool , capacityChanged: Bool ) {
3230
+ let oldBucketCount = asNative. bucketCount
3143
3231
if oldBucketCount >= desiredBucketCount && isUnique {
3144
3232
return ( reallocated: false , capacityChanged: false )
3145
3233
}
3146
3234
3147
3235
let oldDictionary = asNative
3148
- var newDictionary = _NativeDictionary < Key , Value > ( bucketCount: desiredBucketCount)
3236
+ var newDictionary = _NativeDictionary < Key , Value > (
3237
+ bucketCount: desiredBucketCount)
3149
3238
let newBucketCount = newDictionary. bucketCount
3150
3239
for i in 0 ..< oldBucketCount {
3151
3240
if oldDictionary. isInitializedEntry ( at: i) {
@@ -3164,7 +3253,9 @@ extension Dictionary._Variant: _DictionaryBuffer {
3164
3253
newDictionary. count = oldDictionary. count
3165
3254
3166
3255
self = . native( newDictionary)
3167
- return ( reallocated: true , capacityChanged: oldBucketCount != newBucketCount)
3256
+ return (
3257
+ reallocated: true ,
3258
+ capacityChanged: oldBucketCount != newBucketCount)
3168
3259
}
3169
3260
3170
3261
@inline ( __always)
@@ -3178,6 +3269,18 @@ extension Dictionary._Variant: _DictionaryBuffer {
3178
3269
return ensureUniqueNative ( withBucketCount: bucketCount)
3179
3270
}
3180
3271
3272
+ @inline ( __always)
3273
+ @inlinable // FIXME(sil-serialize-all)
3274
+ internal mutating func ensureUniqueNative(
3275
+ withCapacity minimumCapacity: Int ,
3276
+ isUnique: Bool
3277
+ ) -> ( reallocated: Bool , capacityChanged: Bool ) {
3278
+ let bucketCount = _NativeDictionary < Key , Value > . bucketCount (
3279
+ forCapacity: minimumCapacity,
3280
+ maxLoadFactorInverse: _hashContainerDefaultMaxLoadFactorInverse)
3281
+ return _ensureUniqueNative ( withBucketCount: bucketCount, isUnique: isUnique)
3282
+ }
3283
+
3181
3284
/// Ensure this we hold a unique reference to a native dictionary
3182
3285
/// having at least `minimumCapacity` elements.
3183
3286
@inlinable // FIXME(sil-serialize-all)
0 commit comments