Appu Newbie
Appu Newbie

Reputation: 133

Saving/writing swift structs as a file or binary format

I have a Swift struct of Int32, floats and data and I want to save this struct instance in a binary format - Can someone help me on how to write or save file in the binary format?

struct Another {
    let imgCount: UInt32
    let resetFlag: UInt32
    let focalX: Float
    let focalY: Float
    let somedata: Data
}

Also the MemoryLayout<Another>.size and MemoryLayout<Another>.stripe is confusing.

Upvotes: 0

Views: 2301

Answers (1)

Leo Dabus
Leo Dabus

Reputation: 236370

You will need to reserve the first 16 bytes (4 bytes for each numeric property because they are all 32 bits). The tail of the data will be used for the data property someData which can be of any size:

struct Another {
    // UInt32 - A 32-bit unsigned integer value type. You will need 4 bytes for each property
    // 4 + 4 = 8 Bytes
    let imgCount, resetFlag: UInt32
    // Float - A single-precision, floating-point value type. Again 4 bytes for each property
    // 4 + 4 = 8 Bytes
    let focalX, focalY: Float
    // the data can have any size this will be all bytes from index 16 until the end of the file
    let someData: Data
}

To convert the numeric types to Data and back you can use the following helpers:

extension Numeric {
    var data: Data {
        var source = self
        return Data(bytes: &source, count: MemoryLayout<Self>.size)
    }
}

extension Data {
    func object<T>() -> T { withUnsafeBytes { $0.load(as: T.self) } }
}

Now you just need to create a custom initializer to decode your data iterating over your data and set the correspond properties values.

extension Another {
    init(_ data: Data) {

        var startIndex = data.startIndex
        var endIndex = startIndex.advanced(by: MemoryLayout<UInt32>.size)
        let imgCountRange = startIndex..<endIndex
        self.imgCount = data[imgCountRange].object()

        startIndex = endIndex
        endIndex = startIndex.advanced(by: MemoryLayout<UInt32>.size)
        let resetFlagRange = startIndex..<endIndex
        self.resetFlag = data[resetFlagRange].object()

        startIndex = endIndex
        endIndex = startIndex.advanced(by: MemoryLayout<Float>.size)
        let focalXRange = startIndex..<endIndex
        self.focalX = data[focalXRange].object()

        startIndex = endIndex
        endIndex = startIndex.advanced(by: MemoryLayout<Float>.size)
        let focalYRange = startIndex..<endIndex
        self.focalY = data[focalYRange].object()

        self.someData = data[endIndex...]
    }
}

To encode your structure will be much easier:

extension Another {
    var data: Data {
        return imgCount.data + resetFlag.data + focalX.data + focalY.data + someData
    }
}

Testing:

let obj1 = Another(imgCount: 1, resetFlag: 2, focalX: 3.4, focalY: 5.6, someData: Data([0,1,2,3,4,5,6,7,8,9,10]))
let obj2 = Another(imgCount: 3, resetFlag: 4, focalX: 5.6, focalY: 7.8, someData: Data([10,11,12,13,14,15,16,17,18,19,20,21,22]))

let obj1Data = obj1.data
print(obj1Data)  // 27 bytes
let obj1FromData = Another(obj1Data)
print(obj1FromData)
let obj2Data = obj2.data
print(obj2Data)  // 29 bytes
let obj2FromData = Another(obj2Data)
print(obj2FromData)

This will print

27 bytes

Another(imgCount: 1, resetFlag: 2, focalX: 3.4, focalY: 5.6, someData: 11 bytes)

29 bytes

Another(imgCount: 3, resetFlag: 4, focalX: 5.6, focalY: 7.8, someData: 13 bytes)

Upvotes: 3

Related Questions