Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you create an "aligned" array of bytes and read from it?

I want to be able to use the UnsafeRawBufferPointer.load(fromByteOffset:as:) method to read a number of bytes from a [UInt8] array and their corresponding type as an UnsignedInteger, FixedWidthInteger at each read.

In both approaches that follow, a "Fatal error: load from misaligned raw pointer" exception is raised, since load expects the underlying data to be aligned in memory.

I have tried using a 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))

Allocating and initialising a 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))

Can you please point out where my misunderstanding lies in each and provide a working example?

like image 348
qnoid Avatar asked Dec 14 '25 14:12

qnoid


1 Answers

There are multiple problems:

  1. UnsafeMutablePointer makes no alignment guarantees. One could use POSIX functions (like posix_memalign) instead, but that does not really help if ...

  2. The values are at arbitrarily byte offsets in the byte array. As an example, if a: UInt16, b: UInt8, c: UInt32 are packed (in this order)

    aa aa bb cc cc cc cc
    

    then there is no way to align this buffer such that all integer values are properly aligned.

  3. Unfortunately, there are no methods yet to load or initialize a value from unaligned data. (This has been suggested (1, 2) but not yet implemented). It does not matter if you use Array, ContiguousArray, or Data as the byte source.

The safest way is still to load the bytes and use bit shifting to combine them to the values:

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

An alternative is to copy the bytes (and do the byte-order conversion):

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
like image 62
Martin R Avatar answered Dec 16 '25 23:12

Martin R



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!