12

Is it possible to create a [UInt8] from an UnsafeMutablePointer<UInt8> without copying the bytes?

In the NSData world I could simply call

let data = NSData(bytesNoCopy: p, length: n, freeWhenDone: false)

and just wrap the pointer.

tidwall
  • 6,881
  • 2
  • 36
  • 47
  • I think you can do something like `UnsafeMutableBufferPointer(ptr, count: *the number of elements here*)` which can then be made to an array. With Swift 2 you probably don't even need to make an array out of it, since almost all methods apply to the BufferPointer as well – Kametrixom Jun 29 '15 at 01:22
  • @ago When it's made into an array, do the bytes get copied? – tidwall Jun 29 '15 at 01:33
  • Arrays have an initializer for UnsafeMutableBufferPointers so I'm farily certain that the bytes don't get xopied. If you're unsure, just do a test with loads of data and check it with Instruments – Kametrixom Jun 29 '15 at 06:39
  • I meant when the UnsafeMutableBufferPointer is converted to the [UInt8], are the bytes copied? It appears so. – tidwall Jun 29 '15 at 13:26

2 Answers2

14

As already mentioned in the comments, you can create an UnsafeMutableBufferPointer from the pointer:

let a = UnsafeMutableBufferPointer(start: p, count: n)

This does not copy the data, which means that you have to ensure that the pointed-to data is valid as long as a is used. Unsafe (mutable) buffer pointers have similar access methods like arrays, such as subscripting:

for i in 0 ..< a.count {
    print(a[i])
}

or enumeration:

for elem in a {
    print(elem)
}

You can create a "real" array from the buffer pointer with

let b = Array(a)

but this will copy the data.

Here is a complete example demonstrating the above statements:

func test(_ p : UnsafeMutablePointer<UInt8>, _ n : Int) {

    // Mutable buffer pointer from data:
    let a = UnsafeMutableBufferPointer(start: p, count: n)
    // Array from mutable buffer pointer
    let b = Array(a)

    // Modify the given data:
    p[2] = 17

    // Printing elements of a shows the modified data: 1, 2, 17, 4
    for elem in a {
        print(elem)
    }

    // Printing b shows the orignal (copied) data: 1, 2, 3, 4
    print(b)

}

var bytes : [UInt8] = [ 1, 2, 3, 4 ]
test(&bytes, bytes.count)
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 1
    Thanks for providing a workaround and for the thorough example. It doesn't exaclty answer my original question of having a toll-free conversion from UnsafeMutablePointer to [UInt8]. But perhaps there is no way without memory copy. – tidwall Jun 29 '15 at 13:30
  • @tidwall: I am pretty sure that there is no way. Swift Array manages its own backing store for the elements. – Martin R Jun 29 '15 at 13:35
  • Thanks to the anonymous downvoter, who reminded me to update the code for Swift 3/4 :) – Martin R Apr 17 '18 at 19:17
1

You can use UnsafeBufferPointer + map to achieve your goal.
KEEP IN MIND: pointer boundary should be maintained by yourself, while arrays will do by itself
let bytesArray = UnsafeBufferPointer(start: bts, count: bytes.count).map{$0}
Swift 5, Xcode 11:

var bytes: [UInt8] = [1, 3, 5, 7]
let count = bytes.count
print(bytes)

// MARK: - [UInt8] -> UnsafePointer<UInt8>

// producing dangling pointer warning bug?
let bts1: UnsafePointer<UInt8> = UnsafePointer(bytes)
print(bts1, bts1[0], bts1[1], bts1[2], bts1[3], bts1[4])

let bts = withUnsafePointer(to: &bytes[0]) {
    $0.withMemoryRebound(to: UInt8.self, capacity: count) { $0 }
}
print(bts, bts[0], bts[1], bts[4])

// MARK: - UnsafePointer<UInt8> -> [UInt8]
let bytesArray = UnsafeBufferPointer(start: bts, count: bytes.count).map{$0}
print(bytesArray, bytesArray[0], bytesArray[1]/*, bytesArray[4]*/)

output:

[1, 3, 5, 7]
0x0000600001946840 1 3 5 7 0
0x0000600001946840 1 3 0
[1, 3, 5, 7] 1 3

bad samples:

/// invalid sample 1
let testA = withUnsafePointer(to: &bytes) {
    $0.withMemoryRebound(to: UInt8.self, capacity: count) { $0 }
}
print(testA, testA[0], testA[1], testA[4])

/// invalid sample 2
let testB = withUnsafePointer(to: bytes[0]) {
    $0.withMemoryRebound(to: UInt8.self, capacity: count) { $0 }
}
print(testB, testB[0], testB[1], testB[4])

output:

0x0000000102b4f520 32 104 0
0x00007ffeed203ac0 192 58 254
Roger
  • 973
  • 10
  • 15