如何创建“对齐”的字节数组并从中读取?

问题描述 投票:0回答:3

我希望能够使用 UnsafeRawBufferPointer.load(fromByteOffset:as:) 方法从 [UInt8] 数组中读取多个字节,并在每次读取时将其相应的类型作为

UnsignedInteger
FixedWidthInteger
读取。

在接下来的两种方法中,都会引发“致命错误:从未对齐的原始指针加载”异常,因为

load
期望基础数据在内存中对齐。

我尝试过使用

ContiguousArray


var words: ContiguousArray<UInt8> = [0x01, 0x00, 0x03, 0x0a, 0x00, 0x01, 0x00, 0xec, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x20, 0x00, 0x00, 0xe0, 0x88, 0x47, 0xa3, 0xd6, 0x6b, 0xd6, 0x01, 0x4c, 0xff, 0x08] var offset = 0 let byte = words.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt8.self) } offset += MemoryLayout<UInt8>.size let bytes = words.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt16.self) } XCTAssertEqual(byte, UInt8(littleEndian: 0x01)) XCTAssertEqual(bytes, UInt16(littleEndian: 0x0003))
分配并初始化一个 

UnsafeMutablePointer

var words: [UInt8] = [0x01, 0x00, 0x03, 0x0a, 0x00, 0x01, 0x00, 0xec, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x20, 0x00, 0x00, 0xe0, 0x88, 0x47, 0xa3, 0xd6, 0x6b, 0xd6, 0x01, 0x4c, 0xff, 0x08] let uint8Pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: words.count) uint8Pointer.initialize(from: &words, count: words.count) let rawPointer = UnsafeMutableRawPointer(uint8Pointer) var offset = 0 let byte = UInt8(bigEndian: rawPointer.load(fromByteOffset: offset, as: UInt8.self)) offset += MemoryLayout<UInt8>.size let bytes = UInt16(bigEndian: rawPointer.load(fromByteOffset: offset, as: UInt16.self)) rawPointer.deallocate() uint8Pointer.deinitialize(count: words.count) uint8Pointer.deallocate() XCTAssertEqual(byte, UInt8(littleEndian: 0x01)) XCTAssertEqual(bytes, UInt16(littleEndian: 0x0003))
您能否指出我的误解所在并提供一个可行的例子?

swift pointers memory
3个回答
2
投票
存在多个问题:

  1. UnsafeMutablePointer

     不保证对齐。人们可以使用 POSIX 函数(如 
    posix_memalign
    )来代替,但这并没有真正的帮助,如果......

  2. 这些值位于字节数组中的任意字节偏移处。举个例子,如果

    a: UInt16

    b: UInt8
    c: UInt32
    (按此顺序)

    aa aa bb cc cc cc cc
    那么就无法对齐此缓冲区以使所有整数值正确对齐。

  3. 不幸的是,还没有方法可以从未对齐的数据加载或初始化值。 (这已被建议(

    12)但尚未实施)。使用 Array

    ContiguousArray
    Data
     作为字节源都没有关系。

最安全的方法仍然是加载字节并使用位移位将它们组合到值中:

let byteArray: Array<UInt8> = [0x01, 0x00, 0x03, 0x0a] var offset = 0 let ui8 = byteArray[offset] print(ui8) // 1 offset += MemoryLayout<UInt8>.size let ui16 = UInt16(byteArray[offset]) << 8 + UInt16(byteArray[offset + 1]) print(ui16) // 3
另一种方法是

复制字节(并进行字节顺序转换):

let byteArray: Array<UInt8> = [0x01, 0x00, 0x03, 0x0a] var offset = 0 var ui8 = UInt8(0) _ = withUnsafeMutableBytes(of: &ui8, { byteArray.copyBytes(to: $0, from: offset...) } ) print(ui8) // 1 offset += MemoryLayout<UInt8>.size var ui16 = UInt16(0) _ = withUnsafeMutableBytes(of: &ui16, { byteArray.copyBytes(to: $0, from: offset...) } ) print(UInt16(bigEndian: ui16)) // 3
    

1
投票
保证字节数组“对齐”的一种方法是使用相同的

UnsignedInteger

FixedWidthInteger
 类型。  由于数组是同质的,因此通过每种类型的大小来保证对齐。

例如保存字节类型的 [UInt8] 数组

        var array: [UInt8] = [0x01, 0x00, 0x03]         var offset = 0         let byte = array.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt8.self) }         offset += MemoryLayout<UInt8>.size         let bytee = array.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt8.self) }         offset += MemoryLayout<UInt8>.size         let byteee = array.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt8.self) }         XCTAssertEqual(byte, 0x01)         XCTAssertEqual(bytee, 0x00)         XCTAssertEqual(byteee, 0x03)
例如一个包含 2 个字节类型的 [UInt8] 数组

        var array: [UInt8] = [0x01, 0x00, 0x03, 0x0a, 0x00, 0x01]         var offset = 0         let bytes = UInt16(bigEndian: array.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt16.self) })         offset += MemoryLayout<UInt16>.size         let bytess = UInt16(bigEndian: array.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt16.self) })         offset += MemoryLayout<UInt16>.size         let bytesss = UInt16(bigEndian: array.withUnsafeBytes { $0.load(fromByteOffset: offset, as: UInt16.self) })         XCTAssertEqual(bytes, 0x0100)         XCTAssertEqual(bytess, 0x030a)         XCTAssertEqual(bytesss, 0x0001)
误解在于,当存储在数组中的类型由于大小不匹配而无法对齐时,就不可能有一个“对齐”的字节数组。

UInt8 的大小为 1 个字节,UInt16 的大小为 2 个字节,在这种情况下创建了一个

UnsafeRawBufferPointer.load(fromByteOffset:as:)

 不支持的未对齐数组。


0
投票
您现在可以使用

loadUnaligned(fromByteOffset:as:) :

var words: [UInt8] = [0x01, 0x00, 0x03, 0x0a, 0x00, 0x01, 0x00, 0xec, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x20, 0x00, 0x00, 0xe0, 0x88, 0x47, 0xa3, 0xd6, 0x6b, 0xd6, 0x01, 0x4c, 0xff, 0x08] var offset = 0 let byte = words[0] // doesn't need an unaligned access offset += 1 let bytes = words.withUnsafeBytes { UInt16(bigEndian: $0.loadUnaligned(fromByteOffset: offset, as: UInt16.self)) } precondition(byte == UInt8(littleEndian: 0x01)) precondition(bytes == UInt16(littleEndian: 0x0003))
    
© www.soinside.com 2019 - 2024. All rights reserved.