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.