Skip to content

Commit ad50a39

Browse files
[stdlib] Add withContiguous{Mutable}StorageIfAvailable (swiftlang#21092)
* Add MutableCollection.withContiguousMutableStorageIfAvailable * Add withContiguousMutableStorageIfAvailable impls * Add tests on concrete types * Add Sequence.withContiguousStorageIfAvailable * Implement withContiguousStorageIfAvailable in concrete types
1 parent adf4efe commit ad50a39

13 files changed

+257
-16
lines changed

stdlib/private/StdlibCollectionUnittest/CheckMutableCollectionType.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,13 +438,13 @@ if resiliencyChecks.subscriptRangeOnOutOfBoundsRangesBehavior != .none {
438438
}
439439

440440
//===----------------------------------------------------------------------===//
441-
// _withUnsafeMutableBufferPointerIfSupported()
441+
// withContiguousMutableStorageIfAvailable()
442442
//===----------------------------------------------------------------------===//
443443

444-
self.test("\(testNamePrefix)._withUnsafeMutableBufferPointerIfSupported()/semantics") {
444+
self.test("\(testNamePrefix).withContiguousMutableStorageIfAvailable()/semantics") {
445445
for test in subscriptRangeTests {
446446
var c = makeWrappedCollection(test.collection)
447-
var result = c._withUnsafeMutableBufferPointerIfSupported {
447+
var result = c.withContiguousMutableStorageIfAvailable {
448448
(bufferPointer) -> OpaqueValue<Array<OpaqueValue<Int>>> in
449449
let value = OpaqueValue(bufferPointer.map(extractValue))
450450
return value

stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ public class SequenceLog {
112112
public static var _withUnsafeMutableBufferPointerIfSupported = TypeIndexed(0)
113113
public static var _withUnsafeMutableBufferPointerIfSupportedNonNilReturns =
114114
TypeIndexed(0)
115+
public static var withContiguousMutableStorageIfAvailable = TypeIndexed(0)
116+
public static var withContiguousMutableStorageIfAvailableNonNilReturns =
117+
TypeIndexed(0)
115118
// RangeReplaceableCollection
116119
public static var init_ = TypeIndexed(0)
117120
public static var initRepeating = TypeIndexed(0)
@@ -385,6 +388,17 @@ extension LoggingMutableCollection: MutableCollection {
385388
return result
386389
}
387390

391+
public mutating func withContiguousMutableStorageIfAvailable<R>(
392+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
393+
) rethrows -> R? {
394+
MutableCollectionLog.withContiguousMutableStorageIfAvailable[selfType] += 1
395+
let result = try base.withContiguousMutableStorageIfAvailable(body)
396+
if result != nil {
397+
Log.withContiguousMutableStorageIfAvailable[selfType] += 1
398+
}
399+
return result
400+
}
401+
388402
}
389403

390404
public typealias LoggingMutableBidirectionalCollection<
@@ -501,10 +515,10 @@ public typealias LoggingRangeReplaceableRandomAccessCollection<
501515
> = LoggingRangeReplaceableCollection<Base>
502516

503517
//===----------------------------------------------------------------------===//
504-
// Collections that count calls to `_withUnsafeMutableBufferPointerIfSupported`
518+
// Collections that count calls to `withContiguousMutableStorageIfAvailable`
505519
//===----------------------------------------------------------------------===//
506520

507-
/// Interposes between `_withUnsafeMutableBufferPointerIfSupported` method calls
521+
/// Interposes between `withContiguousMutableStorageIfAvailable` method calls
508522
/// to increment a counter. Calls to this method from within dispatched methods
509523
/// are uncounted by the standard logging collection wrapper.
510524
public struct BufferAccessLoggingMutableCollection<
@@ -587,6 +601,17 @@ extension BufferAccessLoggingMutableCollection: MutableCollection {
587601
}
588602
return result
589603
}
604+
605+
public mutating func withContiguousMutableStorageIfAvailable<R>(
606+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
607+
) rethrows -> R? {
608+
Log.withContiguousMutableStorageIfAvailable[selfType] += 1
609+
let result = try base.withContiguousMutableStorageIfAvailable(body)
610+
if result != nil {
611+
Log.withContiguousMutableStorageIfAvailable[selfType] += 1
612+
}
613+
return result
614+
}
590615
}
591616

592617
public typealias BufferAccessLoggingMutableBidirectionalCollection<

stdlib/public/core/Array.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,26 @@ extension Array: RangeReplaceableCollection {
12811281
}
12821282
}
12831283

1284+
@inlinable
1285+
public mutating func withContiguousMutableStorageIfAvailable<R>(
1286+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
1287+
) rethrows -> R? {
1288+
return try withUnsafeMutableBufferPointer {
1289+
(bufferPointer) -> R in
1290+
return try body(&bufferPointer)
1291+
}
1292+
}
1293+
1294+
@inlinable
1295+
public func withContiguousStorageIfAvailable<R>(
1296+
_ body: (UnsafeBufferPointer<Element>) throws -> R
1297+
) rethrows -> R? {
1298+
return try withUnsafeBufferPointer {
1299+
(bufferPointer) -> R in
1300+
return try body(bufferPointer)
1301+
}
1302+
}
1303+
12841304
@inlinable
12851305
public __consuming func _copyToContiguousArray() -> ContiguousArray<Element> {
12861306
if let n = _buffer.requestNativeBuffer() {

stdlib/public/core/ArraySlice.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,26 @@ extension ArraySlice: RangeReplaceableCollection {
10751075
}
10761076
}
10771077

1078+
@inlinable
1079+
public mutating func withContiguousMutableStorageIfAvailable<R>(
1080+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
1081+
) rethrows -> R? {
1082+
return try withUnsafeMutableBufferPointer {
1083+
(bufferPointer) -> R in
1084+
return try body(&bufferPointer)
1085+
}
1086+
}
1087+
1088+
@inlinable
1089+
public func withContiguousStorageIfAvailable<R>(
1090+
_ body: (UnsafeBufferPointer<Element>) throws -> R
1091+
) rethrows -> R? {
1092+
return try withUnsafeBufferPointer {
1093+
(bufferPointer) -> R in
1094+
return try body(bufferPointer)
1095+
}
1096+
}
1097+
10781098
@inlinable
10791099
public __consuming func _copyToContiguousArray() -> ContiguousArray<Element> {
10801100
if let n = _buffer.requestNativeBuffer() {

stdlib/public/core/ContiguousArray.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,26 @@ extension ContiguousArray: RangeReplaceableCollection {
923923
}
924924
}
925925

926+
@inlinable
927+
public mutating func withContiguousMutableStorageIfAvailable<R>(
928+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
929+
) rethrows -> R? {
930+
return try withUnsafeMutableBufferPointer {
931+
(bufferPointer) -> R in
932+
return try body(&bufferPointer)
933+
}
934+
}
935+
936+
@inlinable
937+
public func withContiguousStorageIfAvailable<R>(
938+
_ body: (UnsafeBufferPointer<Element>) throws -> R
939+
) rethrows -> R? {
940+
return try withUnsafeBufferPointer {
941+
(bufferPointer) -> R in
942+
return try body(bufferPointer)
943+
}
944+
}
945+
926946
@inlinable
927947
public __consuming func _copyToContiguousArray() -> ContiguousArray<Element> {
928948
if let n = _buffer.requestNativeBuffer() {

stdlib/public/core/MutableCollection.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,20 @@ where SubSequence: MutableCollection
180180
mutating func _withUnsafeMutableBufferPointerIfSupported<R>(
181181
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
182182
) rethrows -> R?
183+
184+
/// Call `body(p)`, where `p` is a pointer to the collection's
185+
/// mutable contiguous storage. If no such storage exists, it is
186+
/// first created. If the collection does not support an internal
187+
/// representation in a form of mutable contiguous storage, `body` is not
188+
/// called and `nil` is returned.
189+
///
190+
/// Often, the optimizer can eliminate bounds- and uniqueness-checks
191+
/// within an algorithm, but when that fails, invoking the
192+
/// same algorithm on `body`\ 's argument lets you trade safety for
193+
/// speed.
194+
mutating func withContiguousMutableStorageIfAvailable<R>(
195+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
196+
) rethrows -> R?
183197
}
184198

185199
// TODO: swift-3-indexing-model - review the following
@@ -191,6 +205,13 @@ extension MutableCollection {
191205
return nil
192206
}
193207

208+
@inlinable
209+
public mutating func withContiguousMutableStorageIfAvailable<R>(
210+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
211+
) rethrows -> R? {
212+
return nil
213+
}
214+
194215
/// Accesses a contiguous subrange of the collection's elements.
195216
///
196217
/// The accessed slice uses the same indices for the same elements as the

stdlib/public/core/Sequence.swift

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,11 @@ public protocol Sequence {
362362
__consuming func _copyContents(
363363
initializing ptr: UnsafeMutableBufferPointer<Element>
364364
) -> (Iterator,UnsafeMutableBufferPointer<Element>.Index)
365+
366+
@inlinable
367+
func withContiguousStorageIfAvailable<R>(
368+
_ body: (UnsafeBufferPointer<Element>) throws -> R
369+
) rethrows -> R?
365370
}
366371

367372
// Provides a default associated type witness for Iterator when the
@@ -1092,17 +1097,24 @@ extension Sequence {
10921097
public __consuming func _copyContents(
10931098
initializing buffer: UnsafeMutableBufferPointer<Element>
10941099
) -> (Iterator,UnsafeMutableBufferPointer<Element>.Index) {
1095-
var it = self.makeIterator()
1096-
guard var ptr = buffer.baseAddress else { return (it,buffer.startIndex) }
1097-
for idx in buffer.startIndex..<buffer.count {
1098-
guard let x = it.next() else {
1099-
return (it, idx)
1100-
}
1101-
ptr.initialize(to: x)
1102-
ptr += 1
1100+
var it = self.makeIterator()
1101+
guard var ptr = buffer.baseAddress else { return (it,buffer.startIndex) }
1102+
for idx in buffer.startIndex..<buffer.count {
1103+
guard let x = it.next() else {
1104+
return (it, idx)
11031105
}
1104-
return (it,buffer.endIndex)
1106+
ptr.initialize(to: x)
1107+
ptr += 1
11051108
}
1109+
return (it,buffer.endIndex)
1110+
}
1111+
1112+
@inlinable
1113+
public func withContiguousStorageIfAvailable<R>(
1114+
_ body: (UnsafeBufferPointer<Element>) throws -> R
1115+
) rethrows -> R? {
1116+
return nil
1117+
}
11061118
}
11071119

11081120
// FIXME(ABI)#182

stdlib/public/core/UnsafeBufferPointer.swift.gyb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,25 @@ extension Unsafe${Mutable}BufferPointer {
432432
return try body(&self)
433433
}
434434

435+
@inlinable
436+
public mutating func withContiguousMutableStorageIfAvailable<R>(
437+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
438+
) rethrows -> R? {
439+
let (oldBase, oldCount) = (self.baseAddress, self.count)
440+
defer {
441+
_debugPrecondition((oldBase, oldCount) == (self.baseAddress, self.count),
442+
"UnsafeMutableBufferPointer.withUnsafeMutableBufferPointer: replacing the buffer is not allowed")
443+
}
444+
return try body(&self)
445+
}
446+
447+
@inlinable
448+
public func withContiguousStorageIfAvailable<R>(
449+
_ body: (UnsafeBufferPointer<Element>) throws -> R
450+
) rethrows -> R? {
451+
return try body(UnsafeBufferPointer(self))
452+
}
453+
435454
% else:
436455

437456
/// Creates an immutable typed buffer pointer referencing the same memory as the
@@ -444,6 +463,13 @@ extension Unsafe${Mutable}BufferPointer {
444463
count = other.count
445464
}
446465
466+
@inlinable
467+
public func withContiguousStorageIfAvailable<R>(
468+
_ body: (UnsafeBufferPointer<Element>) throws -> R
469+
) rethrows -> R? {
470+
return try body(self)
471+
}
472+
447473
% end
448474

449475
% if not Mutable:

test/api-digester/Outputs/stability-stdlib-abi.swift.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,3 +601,6 @@ Var Dictionary.values has been removed
601601
Var FixedWidthInteger.allZeros has been removed (deprecated)
602602
Var String.Index._offset has been removed (deprecated)
603603
Var String.Index._utf16Index has been removed (deprecated)
604+
605+
Func MutableCollection.withContiguousMutableStorageIfAvailable(_:) has been added as a protocol requirement
606+
Func Sequence.withContiguousStorageIfAvailable(_:) has been added as a protocol requirement

test/api-digester/Outputs/stability-stdlib-source.swift.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,6 @@ Func LazyCollectionProtocol.reversed() has been removed
213213
Var LazyCollectionProtocol.lazy has declared type change from LazyCollection<Self.Elements> to LazySequence<Self.Elements>
214214

215215
Func Sequence.reduce(into:_:) has parameter 0 changing from Default to Owned
216+
217+
Func MutableCollection.withContiguousMutableStorageIfAvailable(_:) has been added as a protocol requirement
218+
Func Sequence.withContiguousStorageIfAvailable(_:) has been added as a protocol requirement

0 commit comments

Comments
 (0)