Skip to content

Commit d870a9f

Browse files
authored
Merge pull request #41288 from glessard/sr15433
[stdlib] Implement `withContiguousStorageIfAvailable` for raw buffer types
2 parents 9cc5d12 + 85250c7 commit d870a9f

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

stdlib/public/core/UnsafeRawBufferPointer.swift.gyb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,35 @@ extension Unsafe${Mutable}RawBufferPointer {
830830
let n = c / MemoryLayout<T>.stride
831831
return .init(start: .init(s._rawValue), count: n)
832832
}
833+
834+
% if Mutable:
835+
@inlinable
836+
@_alwaysEmitIntoClient
837+
public func withContiguousMutableStorageIfAvailable<R>(
838+
_ body: (inout UnsafeMutableBufferPointer<Element>) throws -> R
839+
) rethrows -> R? {
840+
try withMemoryRebound(to: Element.self) { b in
841+
var buffer = b
842+
defer {
843+
_debugPrecondition(
844+
(b.baseAddress, b.count) == (buffer.baseAddress, buffer.count),
845+
"UnsafeMutableRawBufferPointer.withContiguousMutableStorageIfAvailable: replacing the buffer is not allowed"
846+
)
847+
}
848+
return try body(&buffer)
849+
}
850+
}
851+
852+
% end
853+
@inlinable
854+
@_alwaysEmitIntoClient
855+
public func withContiguousStorageIfAvailable<R>(
856+
_ body: (UnsafeBufferPointer<Element>) throws -> R
857+
) rethrows -> R? {
858+
try withMemoryRebound(to: Element.self) {
859+
try body(${ 'UnsafeBufferPointer<Element>($0)' if Mutable else '$0' })
860+
}
861+
}
833862
}
834863

835864
extension Unsafe${Mutable}RawBufferPointer: CustomDebugStringConvertible {

validation-test/stdlib/UnsafeBufferPointer.swift.gyb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,31 @@ UnsafeMutableBufferPointerTestSuite.test("withContiguous(Mutable)StorageIfAvaila
361361
expectEqual(result1, result2)
362362
}
363363

364+
UnsafeMutableRawBufferPointerTestSuite.test("withContiguous(Mutable)StorageIfAvailable") {
365+
for c in [0, 1, 4] {
366+
let pointer = UnsafeMutableRawPointer.allocate(byteCount: c, alignment: c)
367+
defer { pointer.deallocate() }
368+
369+
let mutableBuffer = UnsafeMutableRawBufferPointer(start: pointer, count: c)
370+
mutableBuffer.copyBytes(from: 0..<UInt8(c))
371+
372+
let r: ()? = mutableBuffer.withContiguousMutableStorageIfAvailable {
373+
for i in $0.indices { $0[i] = 2*($0[i]+1) }
374+
}
375+
expectNotNil(r)
376+
let s = mutableBuffer.withContiguousStorageIfAvailable {
377+
$0.reduce(0, +)
378+
}
379+
expectEqual(s.map(Int.init), c*(c+1))
380+
381+
let immutableBuffer = UnsafeRawBufferPointer(mutableBuffer)
382+
let t = immutableBuffer.withContiguousStorageIfAvailable {
383+
$0.reduce(0, +)
384+
}
385+
expectEqual(s, t)
386+
}
387+
}
388+
364389
UnsafeMutableBufferPointerTestSuite.test("initialize(from: Slice)") {
365390
let o = 91
366391
let c = 1_000
@@ -386,6 +411,7 @@ UnsafeMutableBufferPointerTestSuite.test("Slice.withContiguousStorageIfAvailable
386411
let c = test.collection.map { MinimalEquatableValue($0.value) }
387412
let buffer = UnsafeMutableBufferPointer<MinimalEquatableValue>.allocate(
388413
capacity: test.collection.count)
414+
defer { buffer.deallocate() }
389415
var (it, idx) = buffer.initialize(from: c)
390416
expectEqual(buffer.endIndex, idx)
391417
expectNil(it.next())
@@ -402,6 +428,28 @@ UnsafeMutableBufferPointerTestSuite.test("Slice.withContiguousStorageIfAvailable
402428
}
403429
}
404430

431+
UnsafeMutableRawBufferPointerTestSuite.test("Slice.withContiguousStorageIfAvailable") {
432+
for test in subscriptRangeTests {
433+
let c = test.collection.map({ UInt8($0.value/1000) })
434+
let buffer = UnsafeMutableRawBufferPointer.allocate(
435+
byteCount: test.collection.count, alignment: 8
436+
)
437+
defer { buffer.deallocate() }
438+
buffer.copyBytes(from: c)
439+
440+
let expected = test.expected.map({ UInt8($0.value/1000) })
441+
let r1: ()? = buffer[test.bounds].withContiguousStorageIfAvailable {
442+
expectTrue(expected.elementsEqual($0))
443+
}
444+
expectNotNil(r1)
445+
let r2: ()? = buffer[test.bounds].withContiguousMutableStorageIfAvailable {
446+
for i in $0.indices { $0[i] += 1 }
447+
}
448+
expectNotNil(r2)
449+
expectTrue(buffer[test.bounds].elementsEqual(expected.map({ $0+1 })))
450+
}
451+
}
452+
405453
UnsafeMutableBufferPointerTestSuite.test("sort") {
406454
var values = (0..<1000).map({ _ in Int.random(in: 0..<100) })
407455
let sortedValues = values.sorted()
@@ -948,6 +996,8 @@ UnsafeMutable${'Raw' if IsRaw else ''}BufferPointerTestSuite.test("subscript/${R
948996
if _isDebugAssertConfiguration() || isOutOfBounds() {
949997
if expectedValues == nil { expectCrashLater() }
950998
}
999+
% else:
1000+
_ = isOutOfBounds
9511001
% end
9521002
let range = test.rangeSelection.${RangeName}(in: buffer)
9531003

0 commit comments

Comments
 (0)