Skip to content

Commit d12beac

Browse files
committed
Add UnsafeRawBufferPointer.initialize(as:from:)
1 parent 465f243 commit d12beac

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

stdlib/public/core/UnsafeRawBufferPointer.swift.gyb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,60 @@ public struct Unsafe${Mutable}RawBufferPointer
422422
return 0
423423
}
424424

425+
% if mutable:
426+
/// Initializes memory in the buffer with the elements of
427+
/// `source` and binds the initialized memory to type `T`.
428+
///
429+
/// Returns an iterator to any elements of `source` that didn't fit in the
430+
/// buffer, and an index into the buffer one past the last byte written.
431+
///
432+
/// - Precondition: The memory in `startIndex..<(source.count *
433+
/// MemoryLayout<T>.stride)` is uninitialized or initialized to a
434+
/// trivial type.
435+
///
436+
/// - Precondition: The buffer must contain sufficient memory to
437+
/// accommodate at least `source.underestimateCount` elements.
438+
///
439+
/// - Postcondition: The memory at `self[startIndex..<returned index]
440+
/// is bound to type `T`.
441+
///
442+
/// - Postcondition: The `T` values at `self[startIndex..<returned index]`
443+
/// are initialized.
444+
///
445+
// TODO: Optimize where `C` is a `ContiguousArrayBuffer`.
446+
public func initializeMemory<S: Sequence>(
447+
as: S.Iterator.Element.Type, from source: S
448+
) -> (unwritten: S.Iterator, initializedUpTo: Index) {
449+
450+
var it = source.makeIterator()
451+
var idx = startIndex
452+
let elementStride = MemoryLayout<S.Iterator.Element>.stride
453+
454+
// This has to be a debug precondition due to the cost of walking over some collections.
455+
_debugPrecondition(source.underestimatedCount <= (count / elementStride),
456+
"insufficient space to accommodate source.underestimatedCount elements")
457+
guard let base = baseAddress else {
458+
// this can be a precondition since only an invalid argument should be costly
459+
_precondition(source.underestimatedCount == 0, "no memory available to initialize from source")
460+
return (it, startIndex)
461+
}
462+
463+
for p in stride(from: base,
464+
// only advance to as far as the last element that will fit
465+
to: base + count - elementStride + 1,
466+
by: elementStride
467+
) {
468+
// underflow is permitted – e.g. a sequence into
469+
// the spare capacity of an Array buffer
470+
guard let x = it.next() else { break }
471+
p.initializeMemory(as: S.Iterator.Element.self, to: x)
472+
formIndex(&idx, offsetBy: elementStride)
473+
}
474+
475+
return (it, idx)
476+
}
477+
% end # mutable
478+
425479
let _position, _end: Unsafe${Mutable}RawPointer?
426480
}
427481

stdlib/public/core/UnsafeRawPointer.swift.gyb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,8 @@ public struct Unsafe${Mutable}RawPointer : Strideable, Hashable, _Pointer {
675675
/// memory region referenced by `source` must not overlap the
676676
/// destination region.
677677
/// - Returns: A typed pointer to the memory referenced by this raw pointer.
678+
// This is fundamentally unsafe since collections can underreport their count.
679+
@available(*, deprecated, message: "it will be removed in Swift 4.0. Please use 'UnsafeMutableRawBufferPointer.initialize(from:)' instead")
678680
@discardableResult
679681
public func initializeMemory<C : Collection>(
680682
as type: C.Iterator.Element.Type, from source: C

test/stdlib/UnsafeRawBufferPointer.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,59 @@ UnsafeRawBufferPointerTestSuite.test("initFromArray") {
7575
expectEqual(array2, array1)
7676
}
7777

78+
UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).underflow") {
79+
let buffer = UnsafeMutableRawBufferPointer.allocate(count: 30)
80+
defer { buffer.deallocate() }
81+
let source = stride(from: 5 as Int64, to: 0, by: -1)
82+
var (it,idx) = buffer.initializeMemory(as: Int64.self, from: source)
83+
expectEqual(it.next()!, 2)
84+
expectEqual(idx, 24)
85+
([5, 4, 3] as [Int64]).withUnsafeBytes {
86+
expectEqualSequence($0,buffer[0..<idx])
87+
}
88+
}
89+
90+
UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).overflow") {
91+
let buffer = UnsafeMutableRawBufferPointer.allocate(count: 30)
92+
defer { buffer.deallocate() }
93+
let source: [Int64] = [5, 4, 3, 2, 1]
94+
if _isDebugAssertConfiguration() {
95+
expectCrashLater()
96+
}
97+
var (it, idx) = buffer.initializeMemory(as: Int64.self, from: source)
98+
expectEqual(it.next()!, 2)
99+
expectEqual(idx, 24)
100+
([5, 4, 3] as [Int64]).withUnsafeBytes {
101+
expectEqualSequence($0,buffer[0..<idx])
102+
}
103+
}
104+
105+
UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).exact") {
106+
let buffer = UnsafeMutableRawBufferPointer.allocate(count: 24)
107+
defer { buffer.deallocate() }
108+
let source: [Int64] = [5, 4, 3]
109+
var (it, idx) = buffer.initializeMemory(as: Int64.self, from: source)
110+
expectNil(it.next())
111+
expectEqual(idx, buffer.endIndex)
112+
source.withUnsafeBytes { expectEqualSequence($0,buffer) }
113+
}
114+
115+
UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).invalidNilPtr") {
116+
let buffer = UnsafeMutableRawBufferPointer(start: nil, count: 0)
117+
let source: [Int64] = [5, 4, 3, 2, 1]
118+
expectCrashLater()
119+
var (it, idx) = buffer.initializeMemory(as: Int64.self, from: source)
120+
}
121+
122+
UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).validNilPtr") {
123+
let buffer = UnsafeMutableRawBufferPointer(start: nil, count: 0)
124+
let source: [Int64] = []
125+
var (it, idx) = buffer.initializeMemory(as: Int64.self, from: source)
126+
expectNil(it.next())
127+
expectEqual(idx, source.endIndex)
128+
}
129+
130+
78131
// Directly test the byte Sequence produced by withUnsafeBytes.
79132
UnsafeRawBufferPointerTestSuite.test("withUnsafeBytes.Sequence") {
80133
let array1: [Int32] = [0, 1, 2, 3]

0 commit comments

Comments
 (0)