我希望能够使用 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))
分配并初始化一个 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))
您能否指出我的误解所在并提供一个可行的例子?
UnsafeMutablePointer
不保证对齐。人们可以使用 POSIX 函数(如
posix_memalign
)来代替,但这并没有真正的帮助,如果......
a: UInt16
、
b: UInt8
、
c: UInt32
(按此顺序)
aa aa bb cc cc cc cc
那么就无法对齐此缓冲区以使所有整数值正确对齐。
1,2)但尚未实施)。使用 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
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:)
不支持的未对齐数组。
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))