5

In Swift I want to pass a data buffer (named data) of type Data to a C function (named do_something) that takes a pointer of type UnsafePointer<UInt8>.

Is the code example below correct? And if so, in this case is it OK to use assumingMemoryBound(to:) instead of bindMemory(to:capacity:)?

data.withUnsafeBytes { (unsafeBytes) in
  let bytes = unsafeBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
  do_something(bytes, unsafeBytes.count)
}
ma11hew28
  • 121,420
  • 116
  • 450
  • 651

1 Answers1

4

The correct way is to use bindMemory():

data.withUnsafeBytes { (unsafeBytes) in
    let bytes = unsafeBytes.bindMemory(to: UInt8.self).baseAddress!
    do_something(bytes, unsafeBytes.count)
}

assumingMemoryBound() must only be used if the memory is already bound to the specified type.

Some resources about this topic:

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • If I'm not mistaken isn't it easier to convert `Data` to `[UInt8]` and pass this as a pointer? – vadian Aug 24 '20 at 19:36
  • 3
    @vadian: Easier to code but less efficient at runtime. It would create an additional (temporary) array. – Martin R Aug 24 '20 at 19:37
  • Thank you, but how do you know the memory hasn't already been bound to the `UInt8` type? – ma11hew28 Aug 25 '20 at 03:31
  • @ma11hew28: bindMemory binds or rebinds the memory location, so it does not matter if it is already bound to UInt8. There is also good information here: https://stackoverflow.com/a/47942269/1187415. Note also that *“Binding memory communicates to the compiler that the memory locations are safe for typed access. Nothing happens at runtime--until someone writes a type safety sanitizer.”* – Martin R Aug 25 '20 at 16:52
  • OK. But I want my app to run fast. Are you saying that even if I (unnecessarily) rebind memory to a type that it's already bound to, then my app will run just as fast (as if I hadn't)? – ma11hew28 Aug 25 '20 at 18:16
  • I also want my code to be coherent. [UnsafeRawBufferPointer | Apple Developer Documentation](https://developer.apple.com/documentation/swift/unsaferawbufferpointer) says: "Each byte in memory is viewed as a `UInt8` value independent of the type of values held in that memory." So I don't understand why I can't assume it's already been bound to the `UInt8` type. I know it goes on to say: "To access the underlying memory through typed operations, the memory must be bound to a trivial type." But that seems like it'd only be necessary if you want to bind the memory to a type other than `UInt8`. – ma11hew28 Aug 25 '20 at 18:17
  • @ma11hew28: Speed is not affected. As mentioned in the Q&A that I referenced above, nothing happens at runtime (there is also a link to a posting from Apple Developer Andrew Trick in the Swift forum confirming that). – Martin R Aug 25 '20 at 18:46
  • @ma11hew28: A `Data` object holds the bytes in some (opaque) memory location. One can not know if or what that memory is bound to. – Martin R Aug 25 '20 at 18:54
  • OK. Thank you, @MartinR. – ma11hew28 Aug 25 '20 at 19:04