Unsafemutablepointer<Uint8> to [Uint8] Without Memory Copy

UnsafeMutablePointer UInt8 to [UInt8] without memory copy

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)

How do you convert a UnsafeMutablePointer Void to UInt8?

You have to convert the pointer to the correct type first
(a pointer to UInt8) and then you can access the memory it points to:

let u8 = UnsafePointer<UInt8>(theUnsafeMutablePointerVar).memory

In Swift 3, a void pointer from C is imported to Swift as
UnsafeMutableRawPointer, and one can read the pointed-to data
with

let u8 = theUnsafeMutablePointerVar.load(as: UInt8.self)

Why can I pass [UInt8] type to the UnsafePointer UInt8 type parameter?

It's just a shortcut.

An unsafe pointer is effectively a C array: what we are pointing at is the first address of a contiguous block of memory. Therefore Swift gives you some nice help for when you have a Swift array from which you want to write to the memory: this Swift array is nothing whatever like a C array, but nevertheless, you can pass a pointer to the Swift array or even the Swift array itself, including an array literal, and Swift will just do the right thing.

UnsafeMutablePointer UInt8 : What's the risk?

UnsafeMutablePointer is how you represent a C pointer in Swift. It's unsafe because the underlying memory the pointer points to could change at anytime without the Swift pointer knowing. It also has no information about the size of the memory block that it points to (thanks Martin).

If your library requires you to use C types, in this case a pointer to a uint8_t, then you must use UnsafeMutablePointer. Otherwise I if you just want to represent an array of numbers I would wrap all of the uint8_t types in an NSArray as NSNumber types (or NSData if you are pointing to a byte stream) for easier bridging.

You can avoid these risks by dereferencing the pointer (if it is non-nil) and copying the value stored at the pointer to a variable in your Swift application.

How to convert Data to UnsafePointer UInt8 ?

The correct way is to use bindMemory():

data.withUnsafeBytes { (unsafeBytes) in
let bytes = unsafeBytes.bindMemory(to: UInt8.self).baseAddress!
do_something(bytes, unsafeBytes.count)
}

assumingMemoryBound() must only be used if the memory is already bound to the specified type.

Some resources about this topic:

  • UnsafeRawPointer Migration Guide
  • UnsafeRawPointer API

Release UnsafeMutableBufferPointer UInt8 values

You can solve this by using a class for the actual implementation.
An example how this can be done is given in
Friday Q&A 2015-04-17: Let's Build Swift.Array:

Destruction can be solved by using a class, which provides deinit. The
pointer can be destroyed there. class doesn't have value semantics,
but we can solve this by using class for the implementation of the
struct, and exposing the struct as the external interface to the
array. This looks something like:

class ArrayImpl<T> {
var ptr: UnsafeMutablePointer<T>

deinit {
ptr.destroy(...)
ptr.dealloc(...)
}
}

struct Array<T> {
var impl: ArrayImpl<T>
}

You then write methods on Array that forward to implementations on
ArrayImpl, where the real work is done.

Applying this approach to your problem could roughly look like this:

private class DataImpl {
typealias Block = UInt8
var blocks: UnsafeMutableBufferPointer<Block>

init(bytes: UnsafeMutablePointer<Block>, length: Int) {
// copy bytes
let bytesCopy = UnsafeMutablePointer<Block>.alloc(length)
bytesCopy.initializeFrom(bytes, count: length)
// init byte blocks
self.blocks = UnsafeMutableBufferPointer<Block>(start: bytesCopy, count: length)
}

deinit {
print("deinit")
blocks.baseAddress.destroy(blocks.count)
blocks.baseAddress.dealloc(blocks.count)
}
}

struct Data {
typealias Block = UInt8
private var impl : DataImpl

init(bytes: UnsafeMutablePointer<Block>, length: Int) {
impl = DataImpl(bytes: bytes, length: length)
}
}

A simple test shows that this works as expected, the data is released
when the variable goes out of scope:

var bytes : [UInt8] = [1, 2, 3, 4]
do {
let data = Data(bytes: &bytes, length: bytes.count)
}
print("finished")

Output:


deinit
finished

Swift convert [UIn16] to UnsafeMutablePointer UInt16

The withXXXBytes methods get you raw pointers, but here you want a typed pointer. Use withUnsafeMutableBufferPointer instead and get the base address:

arrayOfUInt16.withUnsafeMutableBufferPointer { (bufferPointer) in
let mutablePointerOfUInt16 = bufferPointer.baseAddress
// do whatever you want with mutablePointerOfUInt16
// just don't "store or return the pointer for later use." as the documentation says
// because the pointer is only valid within this block
}

What is the correct way in swift to wrap a segment of a UInt8 array as a String?

Here's how you can do try this -

import Foundation

func readString(_ data: inout [UInt8], _ x: inout Int) -> String {
let l = 4
var slice: ArraySlice<UInt8> = data[x..<x+l] // No copy, view into existing Array
x += l

return slice.withUnsafeBytes({ pointer in
// No copy, just making compiler happy (assumption that it is bound to UInt8 is correct
if let bytes = pointer.baseAddress?.assumingMemoryBound(to: UInt8.self) {
return String(
bytesNoCopy: UnsafeMutableRawPointer(mutating: bytes), // No copy
length: slice.count,
encoding: .utf8,
freeWhenDone: false
) ?? ""
} else {
return ""
}
})
}

Test

var a: [UInt8] = [
65, 66, 67, 68,
69, 70, 71, 72
]
var x = 0

let test1 = readString(&a, &x)
print("test1 : \(test1)")
// test1 : ABCD

let test2 = readString(&a, &x)
print("test2 : \(test2)")
// test2 : EFGH


Related Topics



Leave a reply



Submit