I suspect this is really easy but it is evading me.
I have a file representing a disk image read into memory as type Data. I want to be able to address this image as an array of Sectors and, in turn, address Sectors as UInt16 (Word) and UInt8 (Byte) arrays .. a structure like:
| .. bytes from a disk image ..
+-----------------------+-----------------------+-----------------------+- - - -
| disk sector .. | disk sector .. | ⬅︎ sector array
+-----------------------+-----------------------+
| | | | | | | ⬅︎ word array
+---+---+---+---+---+---+
| | | | | | | | | | | | | ⬅︎ byte array
+-+-+-+-+-+-+-+-+-+-+-+-+
.. so I can refer to parts of the disk image via:
let header = sector[1]
let word = header[24]
let byte = header[48] // indexing the same memory location (as 8 bits)
.. with the presumption that slicing etc will avoid any more copying after the whole disk image is in memory. My first pass over this will read-only but I'm sure I'll go mutable before I'm done.
This is addressed, in part, by several StackOverflow entries but a frightening number of them are Swift 3 and earlier usages and unsafe memory cleverness has progressed a long way since then.
With thanks for guidance ..
1 Like
I can at least point you to a couple fundamental APIs. Maybe someone else can point to relevant example code:
- To work with raw bytes managed by Data
Data.withUnsafeBytes<ResultType>(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType
This fundamental API does not show up in the index of of Data's methods. And the URL above doesn't contain any documentation, so here it is from the source:
/// Calls the given closure with the contents of underlying storage.
///
/// - note: Calling `withUnsafeBytes` multiple times does not guarantee that
/// the same buffer pointer will be passed in every time.
/// - warning: The buffer argument to the body should not be stored or used
/// outside of the lifetime of the call to the closure.
func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R
It has a disclaimer about memory moving from underneath you because Data is a copy-on-write data structure. Presumably you're careful not to trigger a copy though. This API itself won't copy any bytes.
- To reinterpret raw bytes
UnsafeRawBufferPointer.load(fromByteOffset:as:)
UnsafeRawBufferPointer.loadUnaligned(fromByteOffset:as:)
This is safe as long as the memory holds a valid bit pattern and the loaded type doesn't contain object references.
You can build interesting abstractions on top of these two APIs.
Here's a little reference implementation to give you a sense of how you might build a "UnsafeBufferView" on top of a raw pointer:
1 Like