Как создать «выровненный» массив байтов и читать из него? - PullRequest
2 голосов
/ 06 августа 2020

Я хочу иметь возможность использовать метод 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))

Не могли бы вы указать выяснить, в чем заключается мое недоразумение, и привести рабочий пример?

Ответы [ 2 ]

0 голосов
/ 06 августа 2020

Один из способов гарантировать «выровненный» массив байтов - использовать тот же тип 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)

например, массив [UInt8], содержащий 2 байтовые типы

        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 голосов
/ 06 августа 2020

Есть несколько проблем:

  1. UnsafeMutablePointer не дает никаких гарантий выравнивания. Вместо этого можно использовать функции POSIX (например, posix_memalign), но это не поможет, если ...

  2. Значения находятся в произвольных байтовых смещениях в байтовом массиве. Например, если a: UInt16, b: UInt8, c: UInt32 упакованы (в этом порядке)

    aa aa bb cc cc cc cc
    

    , то нет способа выровнять этот буфер так, чтобы все целочисленные значения были правильно выровнены.

  3. К сожалению, пока нет методов для загрузки или инициализации значения из невыровненных данных. (Это было предложено ( 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...