Skip to content

Commit b92c301

Browse files
committed
[stdlib] Set, Dictionary: Clean up storage allocation
1 parent 7826c3c commit b92c301

File tree

6 files changed

+117
-87
lines changed

6 files changed

+117
-87
lines changed

stdlib/public/core/DictionaryStorage.swift

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -359,21 +359,24 @@ final internal class _DictionaryStorage<Key: Hashable, Value>
359359
extension _DictionaryStorage {
360360
@usableFromInline
361361
@_effects(releasenone)
362-
internal static func reallocate(
362+
internal static func copy(
363+
original: _RawDictionaryStorage
364+
) -> _DictionaryStorage {
365+
return allocate(
366+
scale: original._scale,
367+
age: original._age,
368+
seed: original._seed)
369+
}
370+
371+
@usableFromInline
372+
@_effects(releasenone)
373+
static internal func resize(
363374
original: _RawDictionaryStorage,
364-
capacity: Int
365-
) -> (storage: _DictionaryStorage, rehash: Bool) {
366-
_sanityCheck(capacity >= original._count)
375+
capacity: Int,
376+
move: Bool
377+
) -> _DictionaryStorage {
367378
let scale = _HashTable.scale(forCapacity: capacity)
368-
let rehash = (scale != original._scale)
369-
if rehash {
370-
return (.allocate(scale: scale, age: nil, seed: nil), rehash)
371-
}
372-
return (
373-
.allocate(scale: scale, age: original._age, seed: original._seed),
374-
rehash)
375-
}
376-
return (newStorage, rehash)
379+
return allocate(scale: scale, age: nil, seed: nil)
377380
}
378381

379382
@usableFromInline
@@ -392,7 +395,7 @@ extension _DictionaryStorage {
392395
) -> _DictionaryStorage {
393396
let scale = _HashTable.scale(forCapacity: capacity)
394397
let age = _HashTable.age(for: cocoa.object)
395-
return allocate(scale: scale, age: age)
398+
return allocate(scale: scale, age: age, seed: nil)
396399
}
397400
#endif
398401

stdlib/public/core/DictionaryVariant.swift

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -328,28 +328,22 @@ extension Dictionary._Variant {
328328
}
329329
}
330330

331-
/// Ensure uniquely held native storage, while preserving the given index.
332-
/// (If the variant had bridged storage, then the returned index will be the
333-
/// corresponding native representation. Otherwise it's kept the same.)
334331
@inlinable
335332
@inline(__always)
336333
internal mutating func ensureUniqueNative() -> _NativeDictionary<Key, Value> {
337-
switch self {
338-
case .native:
339-
let isUnique = isUniquelyReferenced()
340-
if !isUnique {
341-
let rehashed = asNative.copy(capacity: asNative.capacity)
342-
_sanityCheck(!rehashed)
343-
}
344-
return asNative
345334
#if _runtime(_ObjC)
346-
case .cocoa(let cocoa):
335+
if case .cocoa(let cocoa) = self {
347336
cocoaPath()
348337
let native = _NativeDictionary<Key, Value>(cocoa)
349338
self = .native(native)
350339
return native
340+
}
351341
#endif
342+
let isUnique = isUniquelyReferenced()
343+
if !isUnique {
344+
asNative.copy()
352345
}
346+
return asNative
353347
}
354348

355349
@inlinable

stdlib/public/core/HashTable.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ extension _HashTable {
9292

9393
internal static func hashSeed(
9494
for object: AnyObject,
95-
scale: Int
95+
scale: Int8
9696
) -> Int {
9797
#if false // FIXME: Enable per-instance seeding
9898
// We generate a new hash seed whenever a new hash table is allocated and
@@ -106,7 +106,7 @@ extension _HashTable {
106106
// When we're using deterministic hashing, the scale value as the seed is
107107
// still allowed, and it covers most cases. (Unfortunately some operations
108108
// that merge two similar-sized hash tables will still be quadratic.)
109-
return scale
109+
return Int(scale)
110110
}
111111
// Use the object address as the hash seed. This is cheaper than
112112
// SystemRandomNumberGenerator, while it has the same practical effect.
@@ -117,7 +117,7 @@ extension _HashTable {
117117
return unsafeBitCast(object, to: Int.self)
118118
#else
119119
// Use per-capacity seeding for now.
120-
return scale
120+
return Int(scale)
121121
#endif
122122
}
123123
}

stdlib/public/core/NativeDictionary.swift

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,11 @@ extension _NativeDictionary { // ensureUnique
182182
@inlinable
183183
internal mutating func resize(capacity: Int) {
184184
let capacity = Swift.max(capacity, self.capacity)
185-
let result = _NativeDictionary(
186-
_DictionaryStorage<Key, Value>.allocate(capacity: capacity))
185+
let newStorage = _DictionaryStorage<Key, Value>.resize(
186+
original: _storage,
187+
capacity: capacity,
188+
move: true)
189+
let result = _NativeDictionary(newStorage)
187190
if count > 0 {
188191
for bucket in hashTable {
189192
let key = (_keys + bucket.offset).move()
@@ -199,31 +202,40 @@ extension _NativeDictionary { // ensureUnique
199202
}
200203

201204
@inlinable
202-
internal mutating func copy(capacity: Int) -> Bool {
205+
internal mutating func copyAndResize(capacity: Int) {
203206
let capacity = Swift.max(capacity, self.capacity)
204-
let (newStorage, rehash) = _DictionaryStorage<Key, Value>.reallocate(
207+
let newStorage = _DictionaryStorage<Key, Value>.resize(
205208
original: _storage,
206-
capacity: capacity)
209+
capacity: capacity,
210+
move: false)
207211
let result = _NativeDictionary(newStorage)
208212
if count > 0 {
209-
if rehash {
210-
for bucket in hashTable {
211-
result._unsafeInsertNew(
212-
key: self.uncheckedKey(at: bucket),
213-
value: self.uncheckedValue(at: bucket))
214-
}
215-
} else {
216-
result.hashTable.copyContents(of: hashTable)
217-
result._storage._count = self.count
218-
for bucket in hashTable {
219-
let key = uncheckedKey(at: bucket)
220-
let value = uncheckedValue(at: bucket)
221-
result.uncheckedInitialize(at: bucket, toKey: key, value: value)
222-
}
213+
for bucket in hashTable {
214+
result._unsafeInsertNew(
215+
key: self.uncheckedKey(at: bucket),
216+
value: self.uncheckedValue(at: bucket))
217+
}
218+
}
219+
_storage = result._storage
220+
}
221+
222+
@inlinable
223+
internal mutating func copy() {
224+
let newStorage = _DictionaryStorage<Key, Value>.copy(original: _storage)
225+
_sanityCheck(newStorage._scale == _storage._scale)
226+
_sanityCheck(newStorage._age == _storage._age)
227+
_sanityCheck(newStorage._seed == _storage._seed)
228+
let result = _NativeDictionary(newStorage)
229+
if count > 0 {
230+
result.hashTable.copyContents(of: hashTable)
231+
result._storage._count = self.count
232+
for bucket in hashTable {
233+
let key = uncheckedKey(at: bucket)
234+
let value = uncheckedValue(at: bucket)
235+
result.uncheckedInitialize(at: bucket, toKey: key, value: value)
223236
}
224237
}
225238
_storage = result._storage
226-
return rehash
227239
}
228240

229241
/// Ensure storage of self is uniquely held and can hold at least `capacity`
@@ -234,10 +246,15 @@ extension _NativeDictionary { // ensureUnique
234246
if _fastPath(capacity <= self.capacity && isUnique) {
235247
return false
236248
}
237-
guard isUnique else {
238-
return copy(capacity: capacity)
249+
if isUnique {
250+
resize(capacity: capacity)
251+
return true
252+
}
253+
if capacity <= self.capacity {
254+
copy()
255+
return false
239256
}
240-
resize(capacity: capacity)
257+
copyAndResize(capacity: capacity)
241258
return true
242259
}
243260

@@ -571,10 +588,7 @@ extension _NativeDictionary { // High-level operations
571588
internal func mapValues<T>(
572589
_ transform: (Value) throws -> T
573590
) rethrows -> _NativeDictionary<Key, T> {
574-
let (resultStorage, rehash) = _DictionaryStorage<Key, T>.reallocate(
575-
original: _storage,
576-
capacity: capacity)
577-
_sanityCheck(!rehash)
591+
let resultStorage = _DictionaryStorage<Key, T>.copy(original: _storage)
578592
_sanityCheck(resultStorage._seed == _storage._seed)
579593
let result = _NativeDictionary<Key, T>(resultStorage)
580594
// Because the current and new buffer have the same scale and seed, we can

stdlib/public/core/NativeSet.swift

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,10 @@ extension _NativeSet { // ensureUnique
154154
@inlinable
155155
internal mutating func resize(capacity: Int) {
156156
let capacity = Swift.max(capacity, self.capacity)
157-
let result = _NativeSet(_SetStorage<Element>.allocate(capacity: capacity))
157+
let result = _NativeSet(_SetStorage<Element>.resize(
158+
original: _storage,
159+
capacity: capacity,
160+
move: true))
158161
if count > 0 {
159162
for bucket in hashTable {
160163
let element = (self._elements + bucket.offset).move()
@@ -169,28 +172,36 @@ extension _NativeSet { // ensureUnique
169172
}
170173

171174
@inlinable
172-
internal mutating func copy(capacity: Int) -> Bool {
175+
internal mutating func copyAndResize(capacity: Int) {
173176
let capacity = Swift.max(capacity, self.capacity)
174-
let (newStorage, rehash) = _SetStorage<Element>.reallocate(
175-
original: _storage,
176-
capacity: capacity)
177+
let result = _NativeSet(_SetStorage<Element>.resize(
178+
original: _storage,
179+
capacity: capacity,
180+
move: false))
181+
if count > 0 {
182+
for bucket in hashTable {
183+
result._unsafeInsertNew(self.uncheckedElement(at: bucket))
184+
}
185+
}
186+
_storage = result._storage
187+
}
188+
189+
@inlinable
190+
internal mutating func copy() {
191+
let newStorage = _SetStorage<Element>.copy(original: _storage)
192+
_sanityCheck(newStorage._scale == _storage._scale)
193+
_sanityCheck(newStorage._age == _storage._age)
194+
_sanityCheck(newStorage._seed == _storage._seed)
177195
let result = _NativeSet(newStorage)
178196
if count > 0 {
179-
if rehash {
180-
for bucket in hashTable {
181-
result._unsafeInsertNew(self.uncheckedElement(at: bucket))
182-
}
183-
} else {
184-
result.hashTable.copyContents(of: hashTable)
185-
result._storage._count = self.count
186-
for bucket in hashTable {
187-
let element = uncheckedElement(at: bucket)
188-
result.uncheckedInitialize(at: bucket, to: element)
189-
}
197+
result.hashTable.copyContents(of: hashTable)
198+
result._storage._count = self.count
199+
for bucket in hashTable {
200+
let element = uncheckedElement(at: bucket)
201+
result.uncheckedInitialize(at: bucket, to: element)
190202
}
191203
}
192204
_storage = result._storage
193-
return rehash
194205
}
195206

196207
/// Ensure storage of self is uniquely held and can hold at least `capacity`
@@ -201,10 +212,15 @@ extension _NativeSet { // ensureUnique
201212
if _fastPath(capacity <= self.capacity && isUnique) {
202213
return false
203214
}
204-
guard isUnique else {
205-
return copy(capacity: capacity)
215+
if isUnique {
216+
resize(capacity: capacity)
217+
return true
218+
}
219+
if capacity <= self.capacity {
220+
copy()
221+
return false
206222
}
207-
resize(capacity: capacity)
223+
copyAndResize(capacity: capacity)
208224
return true
209225
}
210226

stdlib/public/core/SetStorage.swift

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -285,19 +285,22 @@ final internal class _SetStorage<Element: Hashable>
285285
extension _SetStorage {
286286
@usableFromInline
287287
@_effects(releasenone)
288-
internal static func reallocate(
288+
internal static func copy(original: _RawSetStorage) -> _SetStorage {
289+
return .allocate(
290+
scale: original._scale,
291+
age: original._age,
292+
seed: original._seed)
293+
}
294+
295+
@usableFromInline
296+
@_effects(releasenone)
297+
static internal func resize(
289298
original: _RawSetStorage,
290-
capacity: Int
291-
) -> (storage: _SetStorage, rehash: Bool) {
292-
_sanityCheck(capacity >= original._count)
299+
capacity: Int,
300+
move: Bool
301+
) -> _SetStorage {
293302
let scale = _HashTable.scale(forCapacity: capacity)
294-
let rehash = (scale != original._scale)
295-
if rehash {
296-
return .allocate(scale: scale, age: nil, seed: nil)
297-
}
298-
return (
299-
.allocate(scale: scale, age: original._age, seed: original._seed),
300-
rehash)
303+
return allocate(scale: scale, age: nil, seed: nil)
301304
}
302305

303306
@usableFromInline

0 commit comments

Comments
 (0)