今天介绍一个新类,这个类的使用将会简化Python的struct的打包以及解包操作code逻辑(unpack以及pack);是zdataclass,目前出到V0.3;
pip install zdataclass
库
from zdataclass.zdataclass import *
from dataclasses import dataclass
import dataclasses
1. 带长度域结构(简单结构)
@dataclass
class s_with_length_field(basedataclass):
'''
typedef struct s_with_length_field {
uint8 length;
uint8 * data;
}
'''
length: uint8 = dataclasses.field(default=None, metadata={DATA_FIELD: 'data'})
data: bytearray = dataclasses.field(default_factory=bytearray, metadata={LENGTH_FIELD: 'length', UNION_FIELD: True})
上述length取值是依赖于data域的length。
测试示例:
def test_length_field():
d = s_with_length_field(data=b'\x01\x02')
print('{}, len={}'.format(d, len(d)))
print(hexlify(d.pack()))
d2 = s_with_length_field().unpack(d.pack())
print('{}, len={}'.format(d2, len(d2)))
if d2 == d:
print('test_length_field pass\r\n')
else:
print('test_length_field fail\r\n')
if __name__ == '__main__':
test_length_field()
数据结果:
s_with_length_field(length=0x02(2), data=bytearray(b'\x01\x02')), len=3
b'020102'
s_with_length_field(length=0x02(2), data=bytearray(b'\x01\x02')), len=3
test_length_field pass
2. 带UNION域结构
@dataclass
class s_with_union_field(basedataclass):
'''
typedef struct s_with_union_field {
uint16 hci_length;
union
{
uint8* hci_data;
struct {
uint16 l2c_length;
uint16 cid;
uint8* l2c_data;
}l2c;
};
};
'''
hci_length: uint16 = dataclasses.field(default=None, metadata={DATA_FIELD: 'hci_data'})
hci_data: bytearray = dataclasses.field(default_factory=bytearray,
metadata={UNION_FIELD: True, LENGTH_FIELD: 'hci_length'})
l2c_length: uint16 = dataclasses.field(default=None, metadata={DATA_FIELD: 'l2c_data'})
cid: uint16 = None
l2c_data: bytearray = dataclasses.field(default_factory=bytearray,
metadata={UNION_FIELD: True, LENGTH_FIELD: 'l2c_length'})
上述如果单独看Python源码,有些囫囵的感觉,如hci_data中的field中定义的元数据,存在一个UNION_FIELD:True则表示,后续所有element或成员都共享此域存储空间;
此处l2c_length、cid、l2c_data都是共享hci_data存储域的;另外注意的是其中hci_length取值是依赖于hci_data域的length;而l2c_length取值依赖于l2c_data数据length;
下面以一个示例说明这一切:
def test_union_field():
d = s_with_union_field(cid=0x0040, l2c_data=b'\x01')
print('{}, len={}'.format(d, len(d)))
data = d.pack()
print(hexlify(data))
d2 = s_with_union_field().unpack(data)
print('{}, len={}'.format(d2, len(d2)))
if d2 == d:
print('test_union_field pass\r\n')
else:
print('test_union_field fail\r\n')
if __name__ == '__main__':
test_union_field()
输出结果:
s_with_union_field(hci_length=0x0005(5), hci_data=bytearray(b'\x01\x00@\x00\x01'), l2c_length=0x0001(1), cid=0x0040(64), l2c_data=bytearray(b'\x01')), len=7
b'05000100400001'
s_with_union_field(hci_length=0x0005(5), hci_data=bytearray(b'\x01\x00@\x00\x01'), l2c_length=0x0001(1), cid=0x0040(64), l2c_data=bytearray(b'\x01')), len=7
test_union_field pass
3. 通用数据结构
@dataclass
class s_with_common_field(basedataclass):
'''
# C++中结构示例
typedef struct s_with_common_field {
uint8 code;
uint8 size;
uint8 *data;
uint16 crc;
};
'''
code: uint8 = 0x01
size: uint8 = dataclasses.field(default=None, metadata={DATA_FIELD: 'data'})
data: bytearray = dataclasses.field(default_factory=bytearray, metadata={LENGTH_FIELD: 'size'})
crc: uint16 = None
注意上述@dataclass这个在Python3.7中心加入的,而basedataClass就是引用的zdataclass类接口;在类中使用的uint8、uint16、uint32都是重新封装的;
具体可以看源码定义。在上述值得关注的就是size与data域定义,注意metadata里面的内容,在data中定义有LENGTH_FIELD域,输入指定bytearray,其size自动赋值的;
测试案例:
def test_with_common_field():
# 此处的两个值只是简单的示例数据
# 实际应用中可自行输入数据
payload = b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00'
crc = crc16(payload)
# 注意data的输入
# crc的赋值
d = s_with_common_field(data=payload, crc=crc)
print('1: {}, len={}'.format(d, len(d)))
# 数据打包
data = d.pack()
# 数据解包
d2 = s_with_common_field().unpack(data)
print('2: {}, len={}'.format(d2, len(d2)))
print(hexlify(data))
if __name__ == '__main__':
test_with_common_field()
输出结果:
1: s_with_common_field(code=0x01(1), size=0x0a(10), data=bytearray(b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x00'), crc=0x6c01(27649)), len=14
2: s_with_common_field(code=0x01(1), size=0x0a(10), data=bytearray(b'\x01\x02\x03\x04\x05\x06\x07\x08\t\x00'), crc=0x6c01(27649)), len=14
b'010a01020304050607080900016c'
上述非常快捷的打包、解包一个较简单的数据结构。