具有灵活数组成员到bytearray的Python ctypes结构

时间:2020-09-11 20:04:42

标签: python c ctypes python-2.x

我正在编写一个将编写二进制文件的Python应用程序。该文件将通过在嵌入式目标上运行的一些C代码进行解析。

我有信心可以从Struct class派生出来,但是打包格式很糟糕,而且我的所有结构都是小尾数,所以我想到了使用ctypes包。

假设我具有以下C结构:

struct my_c_struct
{
    uint32_t    a;
    uint16_t    b;
    uint16_t    table[];
};

在C端,我使用投射到内存缓冲区的指针对该结构进行操作,所以我可以这样做:

uint8_t buf[128];
struct my_c_struct *p = (struct my_c_struct*) buf;
p->table[0] = 0xBEEF;

如何最好地用Python表示这一点?我的第一个尝试是:

class MyCStruct(ctypes.LittleEndianStructure):

    c_uint32 = ctypes.c_uint32
    c_uint16 = ctypes.c_uint16
    
    _pack_ = 1

    _fields_ = [
        ("a", c_uint32),
        ("b", c_uint16),
    ]

    def __init__(self, a, b):
        """
        Constructor
        """
        super(ctypes.LittleEndianStructure, self).__init__(a, b)
        self.table = []

    def pack(self):
        data = bytearray(self.table)
        return bytearray(self)+data

pack()方法背后的想法是,它将在结构的末尾组装可变长度表。请记住,我不知道在创建对象时table有多少个条目。

我实现它的方式显然不起作用。因此,我正在考虑将ctypes-devived类嵌套在纯Python类中:

class MyCStruct:

    class my_c_struct(ctypes.LittleEndianStructure):
        _pack_ = 1
        _fields_ = [ ("a", ctypes.c_uint32),
                     ("b", ctypes.c_uint16) ]


    def __init__(self, a, b):
        """
        Constructor
        """
        self.c_struct = self.my_c_struct(a,b)
        self.table = []
    
    def pack(self):
        self.c_struct.b = len(self.table)
        x = bytearray(self.c_struct)
        y = bytearray()
        for v in self._crc_table:
            y += struct.pack("<H", v)
        return x + y

这是这样做的好方法吗?我不想深入兔子洞只是为了发现有更好的方法。

注意事项:我正在使用Python 2(请不要问...),因此仅Python 3的解决方案对我来说无济于事,但对整个宇宙都有用。 / p>

干杯!

1 个答案:

答案 0 :(得分:0)

struct模块非常容易使用来解决此问题(Python 2代码):

>>> import struct
>>> a = 1
>>> b = 2
>>> table = [3,4]
>>> struct.pack('<LH{}H'.format(len(table)),a,b,*table)
'\x01\x00\x00\x00\x02\x00\x03\x00\x04\x00'

使用.formattable中插入16位值的长度,并使用*tabletable扩展为正确数量的参数。

使用ctypes执行此操作比较复杂。此函数声明一个具有正确的变量数组大小的自定义结构,并填充该结构,然后生成原始数据字节的字节字符串:

#!python2
from ctypes import *

def make_var_struct(a,b,table):
    class Struct(Structure):
        _pack_ = 1
        _fields_ = (('a',c_uint32),
                    ('b',c_uint16),
                    ('table',c_uint16 * len(table)))
    return Struct(a,b,(c_uint16*len(table))(*table))

s = make_var_struct(1,2,[3,4])
print(repr(''.join(buffer(s))))

输出:

'\x01\x00\x00\x00\x02\x00\x03\x00\x04\x00'
相关问题