如何读取 Swift 中的结构字节
How to read bytes of struct in 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 位等片段。
(在这种情况下,我试图从 C# 移植 CRC 算法,但由于其他原因我也被这个问题弄糊涂了。)
这是一个不错的一阶近似值。诀窍是使用 Swift.withUnsafeBytes(_:)
to get a UnsafeRawBufferPointer
, which can then be easily converted to Data
using 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)
你可以像这样直接使用withUnsafeBytes(_:)
:
mutating func scopeOfAwesomeStruct() {
withUnsafeBytes(of: &self) {rbp in
let ptr = rbp.baseAddress!.assumingMemoryBound(to: UInt8.self)
//...
}
}
如前所述,不要在闭包之外导出 ptr
。
而且即使你有一个知道结构长度的函数也不安全。 Swift API 稳定性尚未宣布。不保证结构的任何布局细节,包括属性的顺序以及它们如何放置填充。这可能与 C# 结构不同,并且可能生成与 C# 不同的结果。
我(和许多其他开发人员)相信并期望 current layout strategy 在不久的将来不会改变,所以我会像您一样编写一些代码。但我认为这不安全。记住 Swift 不是 C.
(不过,如果将结构的内容复制到 Data
中,一切都一样。)
如果您想要使用 C 进行严格精确的布局,您可以编写一个 C 结构并将其导入到您的 Swift 项目中。
edit/update: Xcode 12.5 • Swift 5.4
extension ContiguousBytes {
func object<T>() -> T { withUnsafeBytes { [=10=].load(as: T.self) } }
}
extension Data {
func subdata<R: RangeExpression>(in range: R) -> Self where R.Bound == Index {
subdata(in: range.relative(to: self) )
}
func object<T>(at offset: Int) -> T { subdata(in: offset...).object() }
}
extension Numeric {
var data: Data {
var source = self
return Data(bytes: &source, count: MemoryLayout<Self>.size)
}
}
struct AwesomeStructure {
let index: Int32
let id: UInt16
let stuff: UInt8
}
extension AwesomeStructure {
init(data: Data) {
index = data.object()
id = data.object(at: 4)
stuff = data.object(at: 6)
}
var data: Data { index.data + id.data + stuff.data }
}
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"
我正在使用 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 位等片段。
(在这种情况下,我试图从 C# 移植 CRC 算法,但由于其他原因我也被这个问题弄糊涂了。)
这是一个不错的一阶近似值。诀窍是使用 Swift.withUnsafeBytes(_:)
to get a UnsafeRawBufferPointer
, which can then be easily converted to Data
using 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)
你可以像这样直接使用withUnsafeBytes(_:)
:
mutating func scopeOfAwesomeStruct() {
withUnsafeBytes(of: &self) {rbp in
let ptr = rbp.baseAddress!.assumingMemoryBound(to: UInt8.self)
//...
}
}
如前所述,不要在闭包之外导出 ptr
。
而且即使你有一个知道结构长度的函数也不安全。 Swift API 稳定性尚未宣布。不保证结构的任何布局细节,包括属性的顺序以及它们如何放置填充。这可能与 C# 结构不同,并且可能生成与 C# 不同的结果。
我(和许多其他开发人员)相信并期望 current layout strategy 在不久的将来不会改变,所以我会像您一样编写一些代码。但我认为这不安全。记住 Swift 不是 C.
(不过,如果将结构的内容复制到 Data
中,一切都一样。)
如果您想要使用 C 进行严格精确的布局,您可以编写一个 C 结构并将其导入到您的 Swift 项目中。
edit/update: Xcode 12.5 • Swift 5.4
extension ContiguousBytes {
func object<T>() -> T { withUnsafeBytes { [=10=].load(as: T.self) } }
}
extension Data {
func subdata<R: RangeExpression>(in range: R) -> Self where R.Bound == Index {
subdata(in: range.relative(to: self) )
}
func object<T>(at offset: Int) -> T { subdata(in: offset...).object() }
}
extension Numeric {
var data: Data {
var source = self
return Data(bytes: &source, count: MemoryLayout<Self>.size)
}
}
struct AwesomeStructure {
let index: Int32
let id: UInt16
let stuff: UInt8
}
extension AwesomeStructure {
init(data: Data) {
index = data.object()
id = data.object(at: 4)
stuff = data.object(at: 6)
}
var data: Data { index.data + id.data + stuff.data }
}
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"