Как читать байты структуры в Swift - PullRequest
0 голосов
/ 25 сентября 2018

Я работаю с различными структурами в Swift, которые мне нужны, чтобы иметь возможность непосредственно просматривать память.

Как посмотреть байт структуры для байта?

Например:

struct AwesomeStructure {
  var index: Int32
  var id: UInt16
  var stuff: UInt8
  // etc.
}

Компилятор не позволит мне сделать это:

func scopeOfAwesomeStruct() {
    withUnsafePointer(to: &self, { (ptr: UnsafePointer<Int8>) in
    })
}

Очевидно, что withUnsafePointer является шаблонной функцией, которая требует, чтобы UnsafePointer был того же типа, что и self.

Итак, как я могу разбить self (мои структуры) на8 кусочков?Да, я хочу иметь возможность просматривать index в 4-х, 8-битных частях и т. Д.

(В этом случае я пытаюсь перенести алгоритм CRC из C #, ноЯ был смущен этой проблемой и по другим причинам.)

Ответы [ 3 ]

0 голосов
/ 25 сентября 2018

Вы можете использовать withUnsafeBytes(_:) прямо так:

mutating func scopeOfAwesomeStruct() {
    withUnsafeBytes(of: &self) {rbp in
        let ptr = rbp.baseAddress!.assumingMemoryBound(to: UInt8.self)
        //...
    }
}

Как уже отмечалось, не экспортируйте ptr за пределы замыкания.

И это не такбезопасно, даже если у вас есть функция, которая знает длину структуры.Стабильность Swift API пока не объявлена.Любые детали компоновки структур не гарантируются, включая порядок свойств и порядок размещения отступов.Это может отличаться от структур C # и может дать результат, отличный от C #.

Я (и многие другие разработчики) верю и ожидаю, что текущая стратегия компоновки не изменится вближайшее будущее, поэтому я бы написал такой код, как ваш.Но я не думаю, что это безопасно.Помните, что Swift - это не C.

(Хотя все равно, если вы копируете содержимое структуры в Data.)

Если вы хотите строго точную компоновку с C,Вы можете написать структуру C и импортировать ее в свой проект Swift.

0 голосов
/ 25 сентября 2018
struct AwesomeStructure {
    let index: Int32
    let id: UInt16
    let stuff: UInt8
}

extension AwesomeStructure {
    init(data: Data) {
        self.index = data[0...3].withUnsafeBytes { $0.pointee }
        self.id    = data[4...5].withUnsafeBytes { $0.pointee }
        self.stuff = data[6...6].withUnsafeBytes { $0.pointee }
    }
    var data: Data {
        return index.data + id.data + stuff.data
    }
}

extension Numeric {
    var data: Data {
        var source = self
        return Data(bytes: &source, count: MemoryLayout<Self>.size)
    }
}

let awesomeStructure = AwesomeStructure(index: 1, id: 2, stuff: 3)
let data = awesomeStructure.data
print(data)  //  7 bytes

let structFromData = AwesomeStructure(data: data)
print(structFromData)   // "AwesomeStructure(index: 1, id: 2, stuff: 3)\n"

Другой вариант, если вы хотите избежать ручного ввода диапазонов, вы можете создать универсальный метод для возврата объекта изконкретное местоположение ваших данных, как указано ниже:

extension Data {
    func object<T>(at index: Index) -> T {
        return self[index..<index+MemoryLayout<T>.size].withUnsafeBytes { $0.pointee }
    }
}

И используйте это так:

extension AwesomeStructure {
    init(data: Data) {
        var idx = data.startIndex
        self.index = data.object(at: idx)
        idx += MemoryLayout.size(ofValue: index)
        self.id = data.object(at: idx)
        idx += MemoryLayout.size(ofValue: id)
        self.stuff = data.object(at: idx)
    }
}
0 голосов
/ 25 сентября 2018

Вот приличное первое приближение.Хитрость заключается в том, чтобы использовать Swift.withUnsafeBytes(_:), чтобы получить UnsafeRawBufferPointer, который затем можно легко преобразовать в Data с помощью Data.init<SourceType>(buffer: UnsafeMutableBufferPointer<SourceType>).

Это приводит к копированию памяти, поэтому вам не нужно беспокоиться о каких-либо проблемах с висящими указателями.

import Foundation

struct AwesomeStructure {
    let index: Int32 = 0x56
    let id: UInt16 = 0x34
    let stuff: UInt8 = 0x12
}

func toData<T>(_ input: inout T) -> Data {
    var data = withUnsafeBytes(of: &input, Data.init)
    let alignment = MemoryLayout<T>.alignment
    let remainder = data.count % alignment

    if remainder == 0 {
        return data
    }
    else {
        let paddingByteCount = alignment - remainder
        return data + Data(count: paddingByteCount)
    }
}

extension Data {
    var prettyString: String {
        return self.enumerated()
            .lazy
            .map { byteNumber, byte in String(format:"/* %02i */ 0x%02X", byteNumber, byte) }
            .joined(separator: "\n")
    }
}

var x = AwesomeStructure()
let d = toData(&x)
print(d.prettyString)
...