Changing data value in Swift Playground

In a playground, the following code initializes data using an UnsafeBufferPointer as described in the Apple Foundation Documentation

let data = Data()
let test = Array(0..<10)
let pointer = UnsafeRawPointer(test).assumingMemoryBound(to: UInt8.self)
data = Data.init(buffer: UnsafeBufferPointer(start: pointer, count: MemoryLayout.size(ofValue: test)))
data[8]

      

Running this program multiple times results in different data values ​​[8]. Why does the meaning change?

+3


source to share


1 answer


MemoryLayout.size(ofValue: test)

equivalent MemoryLayout<[Int]>.size

(this parameter is only used to infer the general type of the placeholder). This does not give you the size of the array buffer, it gives you the size of the structure itself Array

, which is currently 1 word (8 bytes on a 64 bit machine) in size since the elements are stored indirectly.

Therefore, the instance Data

you are creating contains only 8 bytes, so the access data[8]

will be read from garbage; so you get unexpected results. This out of bounds access will actually throw a runtime error in Swift 4 (starting with the version shipped with Xcode 9 beta 4).

But ignoring all of this, using UnsafeRawPointer(test)

to start with, this behavior is undefined as it uses a pointer to a buffer that is only valid for the duration of the initialization call. Swift ensures that automatically generated pointer arguments (for example, when passing an array to a constant pointer) will be valid for the entire given function call (see the Swift Team Blog post on Interacting with C Pointers ).

If you just want to flush the bytes of an array buffer into an instance Data

, you just want:

let test = Array(0 ..< 10)
let data = test.withUnsafeBufferPointer(Data.init)
// or let data = test.withUnsafeBufferPointer { Data(buffer: $0) }

print(data as NSData) // bridge to NSData to get a full print-out of bytes  

// <00000000 00000000
//  01000000 00000000
//  02000000 00000000
//  03000000 00000000
//  04000000 00000000
//  05000000 00000000
//  06000000 00000000
//  07000000 00000000
//  08000000 00000000
//  09000000 00000000>

print(data[8]) // 1

      

(64-bit small end machine)



which uses withUnsafeBufferPointer(_:)

to get the immutable kind of buffer pointer to the array buffer (and if it is not native, like wrapping NSArray

, it must be created) and Data

init(buffer:)

to create a new instance with bytes from the given buffer the pointer.

If you want bytes to correspond 1: 1 to the elements in the array, you must make each of the elements 1 byte in length.

For example, starting with [UInt8]

:

let test = [UInt8](0 ..< 10)
let data = test.withUnsafeBufferPointer(Data.init)
print(data as NSData) // <00010203 04050607 0809>
print(data[8]) // 8

      

And since you're now working with a sequence UInt8

, you can simplify initialization a bit by using the Data

sequence UInt8

initiator
:

let data = Data(test)

      

+1


source







All Articles