Motivation
To my knowledge, Data
is a struct that abstracts a byte buffer. It references a physical area in memory, in other words: a contiguous number of bytes. Now I want to efficiently store multiple values in memory (as raw data), where the values are not all of the same type.
My definition of efficient here ≔ Store all those values without any unused buffer / gap bytes.
Storing the raw data in memory
let a: UInt8 = 39
let b: Int32 = -20001
let string: String = "How awesome is this data?!"
Now I want to store the data of all those values sequentially in memory, without any type information.
let data = [a.asData, b.asData, string.asData].concatenated()
Imagine that the .asData
property retrieves the byte representations of each instance as a [UInt8]
array and then wraps those in a Data
instance. The concetenated()
method then just concatenates these 3 Data
instances to a single Data
instance as follows:
extension Collection where Element == Data {
func concatenated() -> Data {
reduce(into: Data()) { (result, nextDataChunk) in
result.append(nextDataChunk)
}
}
}
Reading the data back from memory into the respective types
Let's assume this all worked great and I now have this single Data
instance from which I want to restore the 3 original values (with their original types). This is what I do:
var cursor = 0
let a: UInt8 = data.withUnsafeBytes { pointer in
pointer.load(fromByteOffset: cursor, as: UInt8.self)
}
cursor += MemoryLayout<UInt8>.size // +1
let b: Int32 = data.withUnsafeBytes { pointer in
pointer.load(fromByteOffset: cursor, as: Int32.self)
}
cursor += MemoryLayout<Int32>.size // +4
let string: String = data.withUnsafeBytes { pointer in
pointer.load(fromByteOffset: cursor, as: String.self)
}
cursor += MemoryLayout<String>.size // +16
The Problem
The problem is that this throws a runtime error:
Fatal error: load from misaligned raw pointer
and I know exactly why:
Int32
has an alignment of 4 (because it's 4 bytes long). In other words: When reading data with a raw pointer, the first byte of the Int32
must be at an index that is a multiple of 4. But as the first value is a UInt8
only, the data bytes for the Int32
start at index 1, which is not a multiple of 4. Thus, I get the error.
My question is this:
Can I somehow use the raw
Data
that represents instances of different types to recreate such instances without alignment errors? How?And if this is not possible, is there a way to automatically align the
Data
chunks correctly when concatenating them in the first place?