在 Swift 中将数据读入结构体 [英] Reading Data into a Struct in Swift

查看:31
本文介绍了在 Swift 中将数据读入结构体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

网络UDP数据包格式如下:

数据:0102 0A00 0000 0B00 000001:1 字节:主要版本(十进制 01)02:1 字节:minorVersion(十进制 02)0A00 0000:4 个字节:applicationHostId(十进制 10)0B00 0000 :4 个字节:versionNumber(十进制 11)

然后我在 Data 上有一个扩展,它需要一个 startlength 字节来读取

扩展数据{func scanValue(start: Int, length: Int) ->T{返回 self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }}}

当一一读取值时,这可以正常工作:

//正确读为十进制1"让majorVersion:UInt8 = data.scanValue(开始:0,长度:1)//正确读为十进制2"让小版本:UInt8 = data.scanValue(开始:1,长度:1)//正确读作十进制10"让 applicationHostId: UInt32 = data.scanValue(start: 2, length: 4)//正确读为十进制11"让 versionNumber: UInt32 = data.scanValue(start: 6, length: 4)

然后我创建了一个 struct 表示整个数据包如下

struct XPLBeacon {var majorVersion: UInt8//1 字节var minorVersion: UInt8//1 字节var applicationHostId: UInt32//4 字节var versionNumber: UInt32//4 字节}

但是当我将数据直接读入结构时,我遇到了一些问题:

var beacon: XPLBeacon = data.scanValue(start: 0, length: data.count)//正确读为十进制1"信标.majorVersion//正确读作十进制2"信标.次要版本//没有正确读取信标.applicationHostId//没有正确读取信标版本号

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

解决方案

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

<前>A B x x C C C C D D D D

哪里

<前>抵消成员0 A - 主要版本 (UInt8)1 B - 次要版本 (UInt8)2 x x - 填充4 C C C C - applicationHostId (UInt32)8 D D D D - 版本号 (UInt32)

并插入填充,以便 UInt32 成员是对齐到其大小的倍数的内存地址.这是也证实了

print(MemoryLayout.size)//12

(有关 Swift 中对齐的更多信息,请参阅类型布局).

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

<前>01 02 0A 00 00 00 0B 00 00 00A B x x C C C C D D D D

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

I'm trying to read bare Data into a Swift 4 struct using the withUnsafeBytes method. The problem

The network UDP packet has this format:

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)

Then I have an extension on Data that takes a start and the length of bytes to read

extension Data {
    func scanValue<T>(start: Int, length: Int) -> T {
        return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
    }
}

This works correctly when reading the values one by one:

// 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)

Then I created a struct that represents the entire packet as follows

struct XPLBeacon {
    var majorVersion: UInt8        // 1 Byte
    var minorVersion: UInt8        // 1 Byte
    var applicationHostId: UInt32  // 4 Bytes
    var versionNumber: UInt32      // 4 Bytes
}

But when I read the data directly into the structure I have some issues:

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

I it supposed to work to parse an entire struct like this?

解决方案

Reading the entire structure from the data does not work because the struct members are padded to their natural boundary. The memory layout of struct XPLBeacon is

 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 are aligned to memory addresses which are a multiple of their size. This is also confirmed by

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

(For more information about alignment in Swift, see Type Layout).

If you read the entire data into the struct then the bytes are assigned as follows

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

which explains why major/minorVersion are correct, but applicationHostId and versionNumber are wrong. Reading all members separately from the data is the correct solution.

这篇关于在 Swift 中将数据读入结构体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆