-
Notifications
You must be signed in to change notification settings - Fork 10.5k
SE-0138: Add UnsafeRawBufferPointer and UnsafeMutableRawBufferPointer… #4969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SE-0138: Add UnsafeRawBufferPointer and UnsafeMutableRawBufferPointer… #4969
Conversation
@dabrahams Please review for Swift 3. |
@swift-ci test OS X. |
|
||
extension ${Self} { | ||
/// Calls a closure with a view of the array's underlying bytes of memory as a | ||
/// Collection of `UInt8`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doc comment summaries are single sentence fragment.
_ body: (inout UnsafeMutableRawBufferPointer) throws -> R | ||
) rethrows -> R { | ||
return try self.withUnsafeMutableBufferPointer { | ||
var bytes = UnsafeMutableRawBufferPointer($0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make this look like the one below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh... this is actually really interesting.
The closure argument was 'inout' in order to allow the closure body to invoke a subscript-by-range setter. This was a bit of nonsense inherited from UnsafeMutableBufferPointer, which requires the subscript setter to be mutating. I think it should be nonmutating (because a pointer has reference semantics):
public subscript(bounds: Range<Int>) -> UnsafeMutableRawBufferPointer {
nonmutating set { ... }
}
This way withUnsafeBytes() does not need to take an inout
argument. This is an easy fix for raw buffer pointer. We may want to later revisit whether typed buffer pointer's subscript-by-range should also be nonmutating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I agree with you about the nonmutating thing. Hopefully we can implement a mutating requirement with a nonmutating function...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You ought to be able to. If you can't, that's a bug.
% Mutable = 'Mutable' if mutable else '' | ||
|
||
/// A non-owning view over a region of memory as a Collection of bytes | ||
/// independent of the type of values held in that memory. Each 8-bit byte in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary: single fragment
} | ||
|
||
/// The number of bytes in the buffer. | ||
public var count: Int { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you don't need this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like UnsafeBufferPointer, var endIndex
is defined in terms of count
. I think the implementations should be consistent so they have the same performance behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, you have to define one in terms of the other I guess. The difference is that count has a default implementation so if we made them both store a pair of pointers instead of a pointer + length we'd get it with less code.
} | ||
} | ||
// Mutable view of array2's bytes. | ||
array2.withUnsafeMutableBytes { bytes2 in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation of this tricky and it doesn't look like you're testing the tricky bits: ensuring uniqueness, and preventing undefined behavior if the user violates inout rules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been working on more satisfactory testing, and noticed some issues with UnsafeBufferPointer testing in the process. Once those changes are in master I'll update this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
|
||
// Verify basic Sequence/Iterator functionality. | ||
UnsafeRawBufferPointerTestSuite.test("Sequence") { | ||
var array1: [Int32] = [0, 1, 2, 3] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use prepackaged test components from StdlibUnittest to check for RandomAccessCollection
#4954) https://github.com/apple/swift-evolution/blob/master/proposals/0138-unsaferawbufferpointer.md Unsafe[Mutable]RawBufferPointer is a non-owning view over a region of memory as a Collection of bytes independent of the type of values held in that memory. Each 8-bit byte in memory is viewed as a `UInt8` value. Reads and writes on memory via `Unsafe[Mutable]RawBufferPointer` are untyped operations. Accessing this Collection's bytes does not bind the underlying memory to `UInt8`. The underlying memory must be bound to some trivial type whenever it is accessed via a typed operation. In addition to the `Collection` interface, the following methods from `Unsafe[Mutable]RawPointer`'s interface to raw memory are provided with debug mode bounds checks: `load(fromByteOffset:as:)`, `storeBytes(of:toByteOffset:as:)`, and `copyBytes(from:count:)`. This is only a view into memory and does not own the memory. Copying a value of type `UnsafeMutableRawBufferPointer` does not copy the underlying memory. Assigning an `Unsafe[Mutable]RawBufferPointer` into a value-based collection, such as `[UInt8]` copies bytes out of memory. Assigning into a subscript range of UnsafeMutableRawBufferPointer copies into memory.
Fix some badly written tests with false positives. Generalize the testing to cover raw buffer pointers.
The withUnsafeMutableBytes closure argument should not be `inout`. Improve testing, fix comments. Addresses DaveA's review.
There are several checks related to accessing a slice of an UnsafeBufferPointer. Which tests are active depend on the level of optimization. A raw buffer's checks are also stricter in some cases. This test was originally designed to either crash or not for each input range without regard to the nuances of when bounds checks are enabled. When an input range was marked as crashing, that forced the test case to crash which was self-fullfilling--nothing was really being tested in that case. In my previous checkin, I enabled crash checking to be effective but missed some of the nuances of different bounds checking modes. This commit adds logic to the test to account for these nuances.
@dabrahams The |
@swift-ci please test. |
––– CCC Information ––– |
DaveA signed off on this for 3.0. |
@swift-ci smoke test OS X platform. |
Swift OS X platform test passed yesterday: https://ci.swift.org/view/Pull%20Request/job/swift-PR-osx/3845/ |
@swift-ci test |
…. (#4954)
https://github.com/apple/swift-evolution/blob/master/proposals/0138-unsaferawbufferpointer.md
Unsafe[Mutable]RawBufferPointer is a non-owning view over a region of memory as
a Collection of bytes independent of the type of values held in that
memory. Each 8-bit byte in memory is viewed as a
UInt8
value.Reads and writes on memory via
Unsafe[Mutable]RawBufferPointer
are untypedoperations. Accessing this Collection's bytes does not bind the
underlying memory to
UInt8
. The underlying memory must be boundto some trivial type whenever it is accessed via a typed operation.
In addition to the
Collection
interface, the following methods fromUnsafe[Mutable]RawPointer
's interface to raw memory areprovided with debug mode bounds checks:
load(fromByteOffset:as:)
,storeBytes(of:toByteOffset:as:)
, andcopyBytes(from:count:)
.This is only a view into memory and does not own the memory. Copying a value of
type
UnsafeMutableRawBufferPointer
does not copy the underlyingmemory. Assigning an
Unsafe[Mutable]RawBufferPointer
into a value-basedcollection, such as
[UInt8]
copies bytes out of memory. Assigning into asubscript range of UnsafeMutableRawBufferPointer copies into memory.