How to calculate the alignment of unsafe pointer for heterogeneous data?

UnsafeRawBufferPointer

I really like @tera answer. If you want to go with UnsafeRawBufferPointer then:

import Foundation

struct FileRecord {
  /// (8 characters, 8 bytes): An identification word (`DAF/xxxx').
  let locidw: String
  /// ( 1 integer, 4 bytes): The number of double precision components in each array summary. [Address 8]
  let nd: Int32
  /// ( 1 integer, 4 bytes): The number of integer components in each array summary. [Address 12]
  let ni: Int32

  // etc…
}

func readString(_ ptr: UnsafeRawBufferPointer, offset: Int, length: Int) -> String {
  let slice = ptr[offset..<(offset + length)]
  let data = Data(slice)
  return String(data: data, encoding: .ascii)!
}

func readInt32(_ ptr: UnsafeRawBufferPointer, offset: Int) -> Int32 {
  return ptr.load(fromByteOffset: offset, as: Int32.self)
}

func parse(ptr: UnsafeRawBufferPointer) -> FileRecord {
  let locidw = readString(ptr, offset: 0, length: 8)
  let nd = readInt32(ptr, offset: 8)
  let ni = readInt32(ptr, offset: 12)
  return FileRecord(locidw: locidw, nd: nd, ni: ni)
}

// ============== TEST ==============

// This is the 1st line from 'The File Record' section of you doc.
// '0o' is means octal (because they used 'od -cbv' where -b means octal output).
// I will just use Foundation.Data instead of the file.
let data = Data([
  // 0000  D      A      F      /      S      P      K          002       \0   \0   \0   006    \0   \0   \0
           0o104, 0o101, 0o106, 0o057, 0o123, 0o120, 0o113, 0o040, 0o002, 000, 000, 000, 0o006, 000, 000, 000,
])

let result = data.withUnsafeBytes(parse(ptr:))
print(result.locidw) // DAF/SPK
print(result.nd) // 2
print(result.ni) // 6

Obviously, this is a dummy mock, I real life it will probably be more complicated.

Unit tests

If you want unit tests then you can create something like:

protocol BinaryFile {
  func read(offset: Int, count: Int) -> UnsafeRawBufferPointer
  /// Release buffer.
  func close()
}

func parse(file: BinaryFile) -> FileRecord { things and stuff }

Then in unit tests you just create BinaryFileMock that is backed by Foundation.Data (just like I did in example).

Alternative

Though the best option may be to use existing C library to do the work for you.
Swift has a really nice C interop, the code may look a little bit ugly, but it works nicely.

1 Like