我正在尝试使用Data方法将裸struct读入Swift 4 withUnsafeBytes中。问题

网络UDP数据包具有以下格式:

data: 0102 0A00 0000 0B00 0000

01           : 1 byte : majorVersion      (decimal 01)
02           : 1 byte : minorVersion      (decimal 02)
0A00 0000    : 4 bytes: applicationHostId (decimal 10)
0B00 0000    : 4 bytes: versionNumber     (decimal 11)

然后我对Data进行了扩展,该扩展需要一个start和字节的length来读取
extension Data {
    func scanValue<T>(start: Int, length: Int) -> T {
        return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
    }
}

当一一读取值时,这可以正常工作:
// correctly read as decimal "1"
let majorVersion: UInt8 = data.scanValue(start: 0, length: 1)

// correctly read as decimal "2"
let minorVersion: UInt8 = data.scanValue(start: 1, length: 1)

// correctly read as decimal "10"
let applicationHostId: UInt32 = data.scanValue(start: 2, length: 4)

// correctly read as decimal "11"
let versionNumber: UInt32 = data.scanValue(start: 6, length: 4)

然后,我创建了一个struct,代表了整个数据包,如下所示
struct XPLBeacon {
    var majorVersion: UInt8        // 1 Byte
    var minorVersion: UInt8        // 1 Byte
    var applicationHostId: UInt32  // 4 Bytes
    var versionNumber: UInt32      // 4 Bytes
}

但是,当我直接将数据读入结构时,我遇到了一些问题:
var beacon: XPLBeacon = data.scanValue(start: 0, length: data.count)

// correctly read as decimal "1"
beacon.majorVersion

// correctly read as decimal "2"
beacon.minorVersion

// not correctly read
beacon.applicationHostId

// not correctly read
beacon.versionNumber

我应该像这样解析整个结构吗?

最佳答案

从数据读取整个结构不起作用,因为
结构成员被填充到其自然边界。这struct XPLBeacon的内存布局为

 A B x x C C C C D D D D

where

 offset    member
  0        A       - majorVersion (UInt8)
  1        B       - minorVersion (UInt8)
  2        x x     - padding
  4        C C C C - applicationHostId (UInt32)
  8        D D D D - versionNumber (UInt32)

and the padding is inserted so that the UInt32 members arealigned to memory addresses which are a multiple of their size. This isalso confirmed by

print(MemoryLayout<XPLBeacon>.size) // 12

(有关Swift中对齐的更多信息,请参见
Type Layout)。

如果您将整个数据读入结构,则将分配字节
如下

01 02 0A 00 00 00 0B 00 00 00
A B x x C C C C D D D D

这就解释了为什么major/minorVersion是正确的,但是applicationHostIdversionNumber错了。将所有成员与数据分开读取是正确的解决方案。

关于swift - 在Swift中将数据读入结构,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47525922/

10-14 21:51
查看更多