Skip to content

Commit b71f768

Browse files
authored
Add an overload of append(contentsOf:) on Array that takes a Collection instead of a Sequence, and use it to accelerate wCSIA-compatible Sequences (swiftlang#77487)
1 parent ec7a91d commit b71f768

File tree

4 files changed

+159
-1
lines changed

4 files changed

+159
-1
lines changed

stdlib/public/core/Array.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,48 @@ extension Array: RangeReplaceableCollection {
12021202
_appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement)
12031203
_endMutation()
12041204
}
1205+
1206+
/// Adds the elements of a collection to the end of the array.
1207+
///
1208+
/// Use this method to append the elements of a collection to the end of this
1209+
/// array. This example appends the elements of a `Range<Int>` instance
1210+
/// to an array of integers.
1211+
///
1212+
/// var numbers = [1, 2, 3, 4, 5]
1213+
/// numbers.append(contentsOf: 10...15)
1214+
/// print(numbers)
1215+
/// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]"
1216+
///
1217+
/// - Parameter newElements: The elements to append to the array.
1218+
///
1219+
/// - Complexity: O(*m*) on average, where *m* is the length of
1220+
/// `newElements`, over many calls to `append(contentsOf:)` on the same
1221+
/// array.
1222+
@_alwaysEmitIntoClient
1223+
@_semantics("array.append_contentsOf")
1224+
@_effects(notEscaping self.value**)
1225+
public mutating func append(contentsOf newElements: __owned some Collection<Element>) {
1226+
let newElementsCount = newElements.count
1227+
defer {
1228+
_endMutation()
1229+
}
1230+
_reserveCapacityImpl(minimumCapacity: self.count + newElementsCount,
1231+
growForAppend: true)
1232+
// This check prevents a data race writing to _swiftEmptyArrayStorage
1233+
if newElementsCount == 0 {
1234+
return
1235+
}
1236+
1237+
let oldCount = _buffer.mutableCount
1238+
let startNewElements = _buffer.mutableFirstElementAddress + oldCount
1239+
let buf = UnsafeMutableBufferPointer(
1240+
start: startNewElements,
1241+
count: newElementsCount)
1242+
_debugPrecondition(buf.endIndex <= _buffer.mutableCapacity)
1243+
let end = buf.initialize(fromContentsOf: newElements)
1244+
_precondition(end == buf.endIndex)
1245+
_buffer.mutableCount = _buffer.mutableCount + newElementsCount
1246+
}
12051247

12061248
/// Adds the elements of a sequence to the end of the array.
12071249
///
@@ -1225,6 +1267,14 @@ extension Array: RangeReplaceableCollection {
12251267
public mutating func append<S: Sequence>(contentsOf newElements: __owned S)
12261268
where S.Element == Element {
12271269

1270+
let wasContiguous = newElements.withContiguousStorageIfAvailable {
1271+
append(contentsOf: $0)
1272+
return true
1273+
}
1274+
if wasContiguous != nil {
1275+
return
1276+
}
1277+
12281278
defer {
12291279
_endMutation()
12301280
}

stdlib/public/core/ArraySlice.swift

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,47 @@ extension ArraySlice: RangeReplaceableCollection {
925925
_appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement)
926926
_endMutation()
927927
}
928+
929+
/// Adds the elements of a collection to the end of the array.
930+
///
931+
/// Use this method to append the elements of a collection to the end of this
932+
/// array. This example appends the elements of a `Range<Int>` instance
933+
/// to an array of integers.
934+
///
935+
/// var numbers = [1, 2, 3, 4, 5]
936+
/// numbers.append(contentsOf: 10...15)
937+
/// print(numbers)
938+
/// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]"
939+
///
940+
/// - Parameter newElements: The elements to append to the array.
941+
///
942+
/// - Complexity: O(*m*) on average, where *m* is the length of
943+
/// `newElements`, over many calls to `append(contentsOf:)` on the same
944+
/// array.
945+
@_alwaysEmitIntoClient
946+
@_semantics("array.append_contentsOf")
947+
public mutating func append(contentsOf newElements: __owned some Collection<Element>) {
948+
let newElementsCount = newElements.count
949+
// This check prevents a data race writing to _swiftEmptyArrayStorage
950+
if newElementsCount == 0 {
951+
return
952+
}
953+
defer {
954+
_endMutation()
955+
}
956+
reserveCapacityForAppend(newElementsCount: newElementsCount)
957+
_ = _buffer.beginCOWMutation()
958+
959+
let oldCount = self.count
960+
let startNewElements = _buffer.firstElementAddress + oldCount
961+
let buf = UnsafeMutableBufferPointer(
962+
start: startNewElements,
963+
count: newElementsCount)
964+
_debugPrecondition(buf.endIndex <= self.capacity)
965+
let end = buf.initialize(fromContentsOf: newElements)
966+
_precondition(end == buf.endIndex)
967+
_buffer.count += newElementsCount
968+
}
928969

929970
/// Adds the elements of a sequence to the end of the array.
930971
///
@@ -947,6 +988,17 @@ extension ArraySlice: RangeReplaceableCollection {
947988
public mutating func append<S: Sequence>(contentsOf newElements: __owned S)
948989
where S.Element == Element {
949990

991+
let wasContiguous = newElements.withContiguousStorageIfAvailable {
992+
append(contentsOf: $0)
993+
return true
994+
}
995+
if wasContiguous != nil {
996+
return
997+
}
998+
999+
defer {
1000+
_endMutation()
1001+
}
9501002
let newElementsCount = newElements.underestimatedCount
9511003
reserveCapacityForAppend(newElementsCount: newElementsCount)
9521004
_ = _buffer.beginCOWMutation()
@@ -975,7 +1027,6 @@ extension ArraySlice: RangeReplaceableCollection {
9751027
// append them in slow sequence-only mode
9761028
_buffer._arrayAppendSequence(IteratorSequence(remainder))
9771029
}
978-
_endMutation()
9791030
}
9801031

9811032
@inlinable

stdlib/public/core/ContiguousArray.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,49 @@ extension ContiguousArray: RangeReplaceableCollection {
815815
_appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement)
816816
_endMutation()
817817
}
818+
819+
/// Adds the elements of a sequence to the end of the array.
820+
///
821+
/// Use this method to append the elements of a sequence to the end of this
822+
/// array. This example appends the elements of a `Range<Int>` instance
823+
/// to an array of integers.
824+
///
825+
/// var numbers = [1, 2, 3, 4, 5]
826+
/// numbers.append(contentsOf: 10...15)
827+
/// print(numbers)
828+
/// // Prints "[1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15]"
829+
///
830+
/// - Parameter newElements: The elements to append to the array.
831+
///
832+
/// - Complexity: O(*m*) on average, where *m* is the length of
833+
/// `newElements`, over many calls to `append(contentsOf:)` on the same
834+
/// array.
835+
@_alwaysEmitIntoClient
836+
@_semantics("array.append_contentsOf")
837+
public mutating func append(
838+
contentsOf newElements: __owned some Collection<Element>
839+
) {
840+
let newElementsCount = newElements.count
841+
defer {
842+
_endMutation()
843+
}
844+
_reserveCapacityImpl(minimumCapacity: self.count + newElementsCount,
845+
growForAppend: true)
846+
// This check prevents a data race writing to _swiftEmptyArrayStorage
847+
if newElementsCount == 0 {
848+
return
849+
}
850+
851+
let oldCount = _buffer.mutableCount
852+
let startNewElements = _buffer.mutableFirstElementAddress + oldCount
853+
let buf = UnsafeMutableBufferPointer(
854+
start: startNewElements,
855+
count: newElementsCount)
856+
_debugPrecondition(buf.endIndex <= _buffer.mutableCapacity)
857+
let end = buf.initialize(fromContentsOf: newElements)
858+
_precondition(end == buf.endIndex)
859+
_buffer.count += newElementsCount
860+
}
818861

819862
/// Adds the elements of a sequence to the end of the array.
820863
///
@@ -836,6 +879,14 @@ extension ContiguousArray: RangeReplaceableCollection {
836879
@_semantics("array.append_contentsOf")
837880
public mutating func append<S: Sequence>(contentsOf newElements: __owned S)
838881
where S.Element == Element {
882+
883+
let wasContiguous = newElements.withContiguousStorageIfAvailable {
884+
append(contentsOf: $0)
885+
return true
886+
}
887+
if wasContiguous != nil {
888+
return
889+
}
839890

840891
defer {
841892
_endMutation()

stdlib/public/core/ContiguousArrayBuffer.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,12 @@ extension Sequence {
10631063
internal func _copySequenceToContiguousArray<
10641064
S: Sequence
10651065
>(_ source: S) -> ContiguousArray<S.Element> {
1066+
let contigArray = source.withContiguousStorageIfAvailable {
1067+
_copyCollectionToContiguousArray($0)
1068+
}
1069+
if let contigArray {
1070+
return contigArray
1071+
}
10661072
let initialCapacity = source.underestimatedCount
10671073
var builder =
10681074
_UnsafePartiallyInitializedContiguousArrayBuffer<S.Element>(

0 commit comments

Comments
 (0)