Referencing memory via a variety of Arrays

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:

  1. 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.

  1. 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