Skip to content

Commit 101d698

Browse files
committed
Set: avoid copy during intersectInPlace
By droping down to native storage implementation, we can perform in place intersection without making a copy of the set.
1 parent 1f8397b commit 101d698

File tree

1 file changed

+33
-19
lines changed

1 file changed

+33
-19
lines changed

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,39 @@ public struct Set<Element : Hashable> :
428428
self.init(_nativeStorage: _NativeSetStorage.fromArray(elements))
429429
}
430430

431+
/// Remove any members of this set that aren't also in a finite sequence.
432+
public mutating func intersectInPlace<
433+
S : SequenceType where S.Generator.Element == Element
434+
>(sequence: S) {
435+
if case .Native(let nativeOwner) = _variantStorage {
436+
// perform this operation at a lower level
437+
// to avoid invalidating the index and avoiding a copy.
438+
439+
let other = sequence as? Set<Element> ?? Set(sequence)
440+
// FIXME(performance) take the oppurtunity to shrink the storage.
441+
let native = nativeOwner.nativeStorage
442+
for bucket in 0..<native.capacity {
443+
if native.isInitializedEntry(bucket) &&
444+
!other.contains(native.keyAt(bucket)) {
445+
native.destroyEntryAt(bucket)
446+
native.count -= 1
447+
}
448+
}
449+
} else {
450+
// Because `intersect` needs to both modify and iterate over
451+
// the left-hand side, the index may become invalidated during
452+
// traversal so an intermediate set must be created.
453+
let result = intersect(sequence)
454+
455+
// The result can only have fewer or the same number of elements.
456+
// If no elements were removed, don't perform a reassignment
457+
// as this may cause an unnecessary uniquing COW.
458+
if result.count != count {
459+
self = result
460+
}
461+
}
462+
}
463+
431464
//
432465
// APIs below this comment should be implemented strictly in terms of
433466
// *public* APIs above. `_variantStorage` should not be accessed directly.
@@ -571,25 +604,6 @@ public struct Set<Element : Hashable> :
571604
return newSet
572605
}
573606

574-
/// Remove any members of this set that aren't also in a finite sequence.
575-
public mutating func intersectInPlace<
576-
S : SequenceType where S.Generator.Element == Element
577-
>(sequence: S) {
578-
// Because `intersect` needs to both modify and iterate over
579-
// the left-hand side, the index may become invalidated during
580-
// traversal so an intermediate set must be created.
581-
//
582-
// FIXME(performance): perform this operation at a lower level
583-
// to avoid invalidating the index and avoiding a copy.
584-
let result = self.intersect(sequence)
585-
586-
// The result can only have fewer or the same number of elements.
587-
// If no elements were removed, don't perform a reassignment
588-
// as this may cause an unnecessary uniquing COW.
589-
if result.count != count {
590-
self = result
591-
}
592-
}
593607

594608
/// Return a new set with elements that are either in the set or a finite
595609
/// sequence but do not occur in both.

0 commit comments

Comments
 (0)