0

Time to time, I need to deal with numerical data in swift, and I wrote this Array extension to pack and unpack numbers with data.

extension Array {
    var data: Data {
        var array = self
        let data = Data(buffer: UnsafeBufferPointer<Element>(start: &array, count: self.count))
        return data
    }
    init(data: Data) {
        self = data.withUnsafeBytes { [Element]($0.bindMemory(to: Element.self)) }
    }
}

It works fine as follows

let values1: [Double] = [1, 2, 3, 5, 7, 11]
let values2 = [Double](data: values1.data)
print(values1) // [1.0, 2.0, 3.0, 5.0, 7.0, 11.0]
print(values2) // [1.0, 2.0, 3.0, 5.0, 7.0, 11.0]

as well as structs

struct Vertex {
    var x: Float
    var y: Float
    var z: Float
    var w: Float
    var r: UInt8
    var g: UInt8
    var b: UInt8
    var a: UInt8
}

var points1: [Vertex] = [
    Vertex(x: 11, y: 12, z: 13, w: 14, r: 15, g: 16, b: 17, a: 18),
    Vertex(x: 21, y: 22, z: 23, w: 24, r: 25, g: 26, b: 27, a: 28),
    Vertex(x: 31, y: 32, z: 33, w: 34, r: 35, g: 36, b: 37, a: 38),
]
let data2 = points1.data
let points2 = [Vertex](data: data2)
print(points1) // [__lldb_expr_17.Vertex(x: 11.0, y: 12.0, z: 13.0, w: 14.0, r: 15, g: 16, b: 17, a: 18), __lldb_expr_17.Vertex(x: 21.0, y: 22.0, z: 23.0, w: 24.0, r: 25, g: 26, b: 27, a: 28), __lldb_expr_17.Vertex(x: 31.0, y: 32.0, z: 33.0, w: 34.0, r: 35, g: 36, b: 37, a: 38)]
print(points2) // [__lldb_expr_17.Vertex(x: 11.0, y: 12.0, z: 13.0, w: 14.0, r: 15, g: 16, b: 17, a: 18), __lldb_expr_17.Vertex(x: 21.0, y: 22.0, z: 23.0, w: 24.0, r: 25, g: 26, b: 27, a: 28), __lldb_expr_17.Vertex(x: 31.0, y: 32.0, z: 33.0, w: 34.0, r: 35, g: 36, b: 37, a: 38)]

Then I have three questions.

  1. How to fix warnings around UnsafeBufferPointer.

    Initialization of 'UnsafeBufferPointer' results in a dangling buffer pointer

  2. I like to prevent none struct type, such as classes to use this extension. Is there any way to do this like using 'where Element is struct' (of cause error).

  3. Why following code works? String isn't a fixed length data, is it?

    let strings1: [String] = ["Tokyo", "New York", "London"]
    print(strings1) // ["Tokyo", "New York", "London"]
    let data3 = strings1.data
    let strings3 = [String](data: data3)
    print(strings3) // ["Tokyo", "New York", "London"]
Kaz Yoshikawa
  • 1,577
  • 1
  • 18
  • 26
  • You're rebinding existing memory to convert between `Data` and `Array`. This doesn't really work, in the general case, because you're at the mercy of the memory layout the compiler generates for you. That memory layout isn't guaranteed to be stable across compiler versions or platforms. It also wouldn't work between platforms with different endianness. You should read the contents data in a define manner, to account for these things. – Alexander Jul 07 '20 at 16:12
  • 1
    Possibly helpful: [round trip Swift number types to/from Data](https://stackoverflow.com/q/38023838/1187415). – Martin R Jul 07 '20 at 16:15
  • 1
    The answer to your first question `var data: Data { withUnsafeBytes { Data($0) } }` – Leo Dabus Jul 07 '20 at 16:25
  • @Alexander-ReinstateMonica In this situation, I am only save and load data with only iPad, but yes, thinking of alignment and others, it's good to keep in mind. thank. – Kaz Yoshikawa Jul 07 '20 at 17:54
  • @MartinR thanks, I count reached to that question before I post. – Kaz Yoshikawa Jul 07 '20 at 17:57
  • @KazYoshikawa I've heard that before. Inevitably you move this to another platform at some point in the future, forget that this vulnerability exists, and have everything be strangely fucked up :p – Alexander Jul 07 '20 at 18:28

0 Answers0