Data
has a method withUnsafeBytes(_:)
which allows you to access its contents through a typed UnsafePointer<T>
, where the type T
is satisfied by the caller (there's also withUnsafeMutableBytes(_:)
that gives you mutable access to the underlying data).
However it's not documented exactly what we're allowed to substitute for the type T
. Is the following fairly innocuous looking example well-defined?
import Foundation
let data = Data([0xFF])
let i = data.withUnsafeBytes { (ptr: UnsafePointer<Int8>) in
ptr.pointee
}
print(i) // -1
Looking at the source, assumingMemoryBound(to:)
is used in order to get the typed pointer to the underlying bytes – however AFAIK that means that T
and UInt8
must be both be related types, and UInt8
must be layout compatible with T
(and for withUnsafeMutableBytes(_:)
this would need to extend to mutual layout compatibility).
From that I would conclude that the above example exhibits undefined behaviour due to the violation of strict aliasing, assuming that Int8
and UInt8
are unrelated types. Is this correct?
If so, it would be great if the documentation for both withUnsafeBytes(_:)
and withUnsafeMutableBytes(_:)
could be updated to make the user aware of this very easy to fall into pit (especially given the fact the user doesn't always have to spell out the type T
explicitly, it can be inferred).
I did notice a proposal from December 2016 that aimed to replace withUnsafeBytes(_:)
's UnsafePointer<T>
argument with an UnsafeRawBufferPointer
(note the link in the post is broken, you have to expand the body), which I would support – but it was deferred until sometime after Swift 4. Now that we're in the window for Swift 5, would now be a good time to re-visit this proposal?