Как читать байты структуры в Swift
Я работаю с различными структурами в 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 ответа
Ты можешь использовать withUnsafeBytes(_:)
прямо так:
mutating func scopeOfAwesomeStruct() {
withUnsafeBytes(of: &self) {rbp in
let ptr = rbp.baseAddress!.assumingMemoryBound(to: UInt8.self)
//...
}
}
Как уже отмечалось, не экспортировать ptr
за пределами закрытия.
И это небезопасно, даже если у вас есть функция, которая знает длину структуры. Стабильность Swift API пока не объявлена. Любые детали компоновки структур не гарантируются, включая порядок свойств и порядок размещения отступов. Который может отличаться от структур C# и может генерировать результат, отличный от C#.
Я (и многие другие разработчики) верю и ожидаю, что текущая стратегия верстки не изменится в ближайшем будущем, поэтому я бы написал такой код, как ваш. Но я не думаю, что это безопасно. Помните, что Свифт не C.
(Хотя все равно, если вы копируете содержимое структуры в Data
.)
Если вам нужна строго точная компоновка с C, вы можете написать структуру C и импортировать ее в ваш проект Swift.
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)
}
}
Вот достойное первое приближение. Хитрость заключается в использовании 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)