Могу ли я переназначить [UInt8] в [UInt16], используя UnsafePointer.withMemoryRebound? - PullRequest
0 голосов
/ 02 июля 2018

Я пытаюсь перенести большой [UInt8] в [UInt16], и пока мое решение работает так:

//Function that converts [UInt8] into [UInt16]
func foo(arr: [UInt8])->[UInt16]{
    //Split into even and odds
    let even = stride(from: 0, to: arr.count, by: 2).map { arr[$0] }
    let odd = stride(from: 1, to: arr.count, by: 2).map { arr[$0] }
    //pair each even with the next odd
    let paired=Array(zip(even, odd))
    //reduce UInt8 pairs to UInt16

    return paired.reduce([UInt16]()) { (acc, curr) -> [UInt16] in
        let u16 = UnsafePointer([curr.0, curr.1]).withMemoryRebound(to: UInt16.self, capacity: 1) {
            $0.pointee
        }
        var newV = acc
        newV.append(u16)
        return newV
    }
}

Выше работает, но очень неэффективно. Функция Reduce - это место, где происходит большая часть времени вычислений. Мне было интересно, можно ли напрямую использовать переопределение UnsafePointer.withMemoryRebound. Я попробовал:

let test : [UInt16] = UnsafePointer([UInt8(0), UInt8(1),UInt8(0), UInt8(1)]).withMemoryRebound(to: [UInt16].self, capacity: 2) { 
    $0.pointee 
} 

в результате:

Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)
Process 57635 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x00007fff7d038d5f libobjc.A.dylib`objc_retain + 15
libobjc.A.dylib`objc_retain:
->  0x7fff7d038d5f <+15>: movq   (%rdi), %rax
    0x7fff7d038d62 <+18>: movabsq $0x7ffffffffff8, %rcx     ; imm = 0x7FFFFFFFFFF8 
    0x7fff7d038d6c <+28>: andq   %rax, %rcx
    0x7fff7d038d6f <+31>: testb  $0x2, 0x20(%rcx)
Target 0: (repl_swift) stopped.

Может быть, я неправильно понял, как это должно работать. Это можно сделать? Есть ли лучший способ сделать это?

Ответы [ 2 ]

0 голосов
/ 09 февраля 2019

Доступ к хранилищу элементов массива осуществляется с помощью метода withUnsafeBytes(), который вызывает замыкание с UnsafeRawBufferPointer. Этот указатель теперь может быть привязан к указателю буфера нужного типа UInt16. Наконец, создается новый массив - это единственное место, где копируются данные.

let uint8Array: [UInt8] = [1, 2, 3, 4]
let uint16Array = uint8Array.withUnsafeBytes { ptr in
    Array(ptr.bindMemory(to: UInt16.self))
}

print(uint16Array) // [513, 1027]
0 голосов
/ 02 июля 2018

Я предлагаю создать экземпляр Data и переназначить байты с помощью withUnsafeBytes

let arr : [UInt8] = [0x31, 0x32, 0x33, 0x34]
let data = Data(arr)
let u16 = data.withUnsafeBytes {
    [UInt16](UnsafeBufferPointer(start: $0, count: data.count/MemoryLayout<UInt16>.stride))
} // [12849, 13363]
...