3

I am not sure where I got this piece of code. But here I have some code to get series of Vertex(or whatever) out of Data in swift.

struct Vertex {
    var x, y, z, w: Float16
    var r, g, b, a: Float16
}

extension Data {
    func elements<T>() -> [T] {
        return withUnsafeBytes {
            Array(UnsafeBufferPointer<T>(start: $0, count: count/MemoryLayout<T>.stride))
        }
    }
}

It works fine to me, but I have this warning. I spent some time, but I cannot figure this out. So could someone please help me out?

'withUnsafeBytes' is deprecated: use `withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R` instead

By the way, I am trying to save and load large number of data with this way with pairing of following piece of code.

extension Array {
    var data: Data {
        var value = self
        return NSData(bytes: &value, length: MemoryLayout<Element>.stride * self.count) as Data
    }
}

Thank you,

  • EDIT

Here is the code what I like to do, it works fine, but I like to get rid of warning...

struct Vertex: Equatable {
    var x, y, z, w: Float16
    var r, g, b, a: Float16
    // assumption: no precision error 
    static func == (lhs: Self, rhs: Self) -> Bool {
        return  lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w &&
                lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b && lhs.a == rhs.a
    }
}
let v0 = Vertex(x: 1, y: 2, z: 3, w: 4, r: 0, g: 0.25, b: 0.5, a: 0.75)
let v1 = Vertex(x: 5, y: 6, z: 7, w: 8, r: 0.2, g: 0.4, b: 0.6, a: 0.8)
let v2 = Vertex(x: 9, y: 0, z: 1, w: 2, r: 0.5, g: 0.75, b: 0.0, a: 1.0)
let original: [Vertex] = [v0, v1, v2]
let data = original.data
print(data as NSData)
let restored: [Vertex] = data.elements()
let w0 = restored[0]
let w1 = restored[1]
let w2 = restored[2]
print(v0 == w0)
print(v1 == w1)
print(v2 == w2)
Kaz Yoshikawa
  • 1,577
  • 1
  • 18
  • 26
  • Thank you for your comment, I checked this question before I post here. I tried some thing like this base on the answer of that choice. error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x400420040003c00). So, I am not sure this would help... ``` extension Data { func elements() -> [T] { return self.withUnsafeBytes { $0.load(as: [T].self) } } } ``` – Kaz Yoshikawa Jul 11 '21 at 15:31

2 Answers2

1

First make your struct conform to ContiguousBytes:

extension Vertex: ContiguousBytes {
    func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
        try Swift.withUnsafeBytes(of: self) { try body($0) }
    }
}

Then create a custom initializer on ContiguousBytes to allow initializing any type that conforms to it with contiguous bytes:

extension ContiguousBytes {
    init<T: ContiguousBytes>(_ bytes: T) {
        self = bytes.withUnsafeBytes { $0.load(as: Self.self) }
    }
}

To extract the bytes/data from the types that conform to it:

extension ContiguousBytes {
    var bytes: [UInt8] { withUnsafeBytes { .init($0) } }
    var data: Data { withUnsafeBytes { .init($0) } }
}

Now you can simply make the magic happen. Playground testing:

struct Vertex {
    var x, y, z, w: Float16
    var r, g, b, a: Float16
}

let vertex = Vertex(x: 1.2, y: 2.3, z: 3.4, w: 4.5, r: 0.5, g: 0.6, b: 0.7, a: 1)
let bytes = vertex.bytes   // [205, 60, 154, 64, 205, 66, 128, 68, 0, 56, 205, 56, 154, 57, 0, 60]
let loadedVertex = Vertex(bytes)
print(loadedVertex)   // Vertex(x: 1.2, y: 2.3, z: 3.4, w: 4.5, r: 0.5, g: 0.6, b: 0.7, a: 1.0)

edit/update:

to convert your bytes to a collection of vertices:

extension Array {
    var data: Data {
        var value = self
        return .init(bytes: &value, count: MemoryLayout<Element>.stride * count)
    }
}

extension ContiguousBytes {
    func objects<T>() -> [T] { withUnsafeBytes { .init($0.bindMemory(to: T.self)) } }
    var vertices: [Vertex] { objects() }
}

let vertex1 = Vertex(x: 1.2, y: 2.3, z: 3.4, w: 4.5, r: 0.5, g: 0.6, b: 0.7, a: 1)
let vertex2 = Vertex(x: 2.3, y: 3.4, z: 4.5, w: 5.6, r: 1, g: 0.8, b: 1, a: 1)
let data = [vertex1, vertex2].data
let loadedVertices = data.vertices

print(loadedVertices)   // [__lldb_expr_8.Vertex(x: 1.2, y: 2.3, z: 3.4, w: 4.5, r: 0.5, g: 0.6, b: 0.7, a: 1.0), __lldb_expr_8.Vertex(x: 2.3, y: 3.4, z: 4.5, w: 5.6, r: 1.0, g: 0.8, b: 1.0, a: 1.0)]
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • Thank you for your answer, may be my question wasn't clear enough that I wanted to deal with Array of struct, and somehow, `$0.load(as: )` will not take `[Vertex].self`, perhaps, data should be sliced into subdata to build one by one... – Kaz Yoshikawa Jul 11 '21 at 17:21
  • @KazYoshikawa check my last edit – Leo Dabus Jul 11 '21 at 17:30
  • 1
    Thanks for your update. I tried your code and it just works. But I am still scratching my head, why your extension code work. Anyway, it is a great piece of code, thank you. – Kaz Yoshikawa Jul 11 '21 at 19:09
0

To mirror what is being done in .data I believe you just want to copy the bytes directly into the Array. This is all pretty dangerous, because it's making some pretty strong assumptions around memory management (if T, or any property of T were a class or included hidden classes internally, the reference counts could be wrong). So I'd be really uncomfortable making this a generic extension like this. But for Float16 it's probably fine, assuming that you never encounter an endian issue (or an architecture that pads differently).

But to just do what you're doing, I'd recommend:

extension Data {
    func elements<T>() -> [T] {
        Array(unsafeUninitializedCapacity: count/MemoryLayout<T>.stride) { buffer, initializedCount in
            copyBytes(to: buffer)
            initializedCount = buffer.count
        }
    }
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 1
    I understand reading raw data into struct is not an ideal. But dealing with mega scale binary data is a really hassle. I used to make some utility code to deal with binary data (like link below), but I am too tired to encode/decode those by hand coding. I wish I could go for `Codable`, but its binary are way too larger than I wish. Anyway, thank you for your time. https://github.com/codelynx/DataStream – Kaz Yoshikawa Jul 11 '21 at 19:32