@@ -95,95 +95,76 @@ extension _NativeDictionary {
95
95
) -> Void
96
96
) {
97
97
self . init ( capacity: capacity)
98
- var count = 0
98
+ var initializedCount = 0
99
99
initializer (
100
100
UnsafeMutableBufferPointer ( start: _keys, count: capacity) ,
101
101
UnsafeMutableBufferPointer ( start: _values, count: capacity) ,
102
- & count )
102
+ & initializedCount )
103
103
_precondition ( count >= 0 && count <= capacity)
104
- _storage. _count = count
104
+ _storage. _count = initializedCount
105
105
106
106
// Hash initialized elements and move each of them into their correct
107
107
// buckets.
108
108
//
109
- // - The storage is separated into three regions: "before bucket", "bucket
110
- // up to count", and "count and after".
109
+ // - We have some number of unprocessed elements at the start of the
110
+ // key/value buffers -- buckets up to and including `bucket`. Everything
111
+ // this region is either unprocessed or in use. There are no uninitialized
112
+ // entries in it.
111
113
//
112
- // - Everything in the middle region, "bucket up to count" is either
113
- // unprocessed or in use. There are no uninitialized entries in it.
114
- //
115
- // - Everything in "before bucket" and "count and after" is either
116
- // uninitialized or in use. These regions work like regular dictionary
117
- // storage.
114
+ // - Everything after `bucket` is either uninitialized or in use. This
115
+ // region works exactly like regular dictionary storage.
118
116
//
119
117
// - "in use" is tracked by the bitmap in `hashTable`, the same way it would
120
118
// be for a working Dictionary.
121
119
//
122
120
// Each iteration of the loop below processes an unprocessed element, and/or
123
- // reduces the size of the "bucket up to count" region, while ensuring the
124
- // above invariants.
125
- var bucket = _HashTable. Bucket ( offset: 0 )
126
- while bucket. offset < count {
121
+ // reduces the size of the unprocessed region, while ensuring the above
122
+ // invariants.
123
+ var bucket = _HashTable. Bucket ( offset: initializedCount - 1 )
124
+ while bucket. offset >= 0 {
127
125
if hashTable. _isOccupied ( bucket) {
128
126
// We've moved an element here in a previous iteration.
129
- bucket. offset + = 1
127
+ bucket. offset - = 1
130
128
continue
131
129
}
132
130
// Find the target bucket for this entry and mark it as in use.
133
- let target = _preloadEntry (
134
- in: bucket,
135
- allowingDuplicates: allowingDuplicates)
131
+ let target : Bucket
132
+ if _isDebugAssertConfiguration ( ) || allowingDuplicates {
133
+ let ( b, found) = find ( _keys [ bucket. offset] )
134
+ if found {
135
+ _internalInvariant ( b != bucket)
136
+ _precondition ( allowingDuplicates, " Duplicate keys found " )
137
+ // Discard existing entry, then move the current entry in place of it.
138
+ uncheckedDestroy ( at: b)
139
+ _storage. _count -= 1
140
+ moveEntry ( from: bucket, to: b)
141
+ bucket. offset -= 1
142
+ continue
143
+ }
144
+ hashTable. insert ( b)
145
+ target = b
146
+ } else {
147
+ let hashValue = self . hashValue ( for: _keys [ bucket. offset] )
148
+ target = hashTable. insertNew ( hashValue: hashValue)
149
+ }
136
150
137
- if target < bucket || target. offset >= count {
138
- // The target is in either the "before bucket" or the "count and after"
139
- // region. We can simply move the entry, leaving behind an
140
- // uninitialized bucket.
151
+ if target > bucket {
152
+ // The target is outside the unprocessed region. We can simply move the
153
+ // entry, leaving behind an uninitialized bucket.
141
154
moveEntry ( from: bucket, to: target)
142
- // Restore invariants by moving the region boundary such that the bucket
143
- // becomes part of the "before bucket" region.
144
- bucket. offset += 1
155
+ // Restore invariants by lowering the region boundary.
156
+ bucket. offset -= 1
145
157
} else if target == bucket {
146
158
// Already in place.
147
- bucket. offset + = 1
159
+ bucket. offset - = 1
148
160
} else {
149
- // The target bucket is also in the middle region. Swap the current
161
+ // The target bucket is also in the unprocessed region. Swap the current
150
162
// item into place, then try again with the swapped-in value, so that we
151
163
// don't lose it.
152
164
swapEntry ( target, with: bucket)
153
165
}
154
166
}
155
- // When the middle region disappears, we're left with a valid Dictionary.
156
- }
157
-
158
- /// Find and return the correct bucket for the entry stored in `bucket`, while
159
- /// also preparing to place it there. This is for use in the bulk loading
160
- /// initializer, `init(_unsafeUninitializedCapacity:, allowingDuplicates:,
161
- /// initializingWith:)`.
162
- ///
163
- /// When this function returns, it is guaranteed that
164
- /// - the returned bucket is marked occupied in the hash table's bitmap,
165
- /// - any duplicate that was already in the returned bucket is destroyed, and
166
- /// - the original item is still in its original bucket.
167
- @inlinable
168
- @inline ( __always)
169
- internal func _preloadEntry(
170
- in bucket: Bucket ,
171
- allowingDuplicates: Bool
172
- ) -> Bucket {
173
- if _isDebugAssertConfiguration ( ) || allowingDuplicates {
174
- let ( b, found) = find ( _keys [ bucket. offset] )
175
- if found {
176
- _precondition ( allowingDuplicates, " Duplicate keys found " )
177
- // Discard existing key & value in preparation of overwriting it.
178
- uncheckedDestroy ( at: b)
179
- _storage. _count -= 1
180
- } else {
181
- hashTable. insert ( b)
182
- }
183
- return b
184
- }
185
-
186
- let hashValue = self . hashValue ( for: _keys [ bucket. offset] )
187
- return hashTable. insertNew ( hashValue: hashValue)
167
+ // When there are no more unprocessed entries, we're left with a valid
168
+ // Dictionary.
188
169
}
189
170
}
0 commit comments