How can I get the length of a python ctypes structure member for formatting of hex output?

Viewed 190

Using this code:

from ctypes import *
class Tracerec(BigEndianStructure):
    def __repr__(self):
        textlist = list()
        for name, *dtype in self._fields_:
            value = getattr(self, name)
            if type(value) == int: textlist.append(f'{name}:0x{value:x}')
            else: textlist.append(f'{name}:{value}')
        fulltext = ' '.join(textlist)
        return f'<{self.__class__.__name__}={fulltext}>'
    def __getitem__(self, i):
        if type(i)==str: return getattr(self, i)
        return getattr(self, self._fields_[i][0])
    def __len__(self):
        return len(self._fields_)

class Mystruct(Tracerec):
    _fields_ = [
        ('a', c_uint16),
        ('b', c_uint16),
        ('c', c_uint32),
    ]

buffer = b'\x01\x02\x03\x04\x00\x00\x05\x06'
x = Mystruct.from_buffer_copy(buffer)
x

I get this output:

<Mystruct=a:0x102 b:0x304 c:0x506>

But I would like it to format to the ctypes byte length but sizeof(dtype) only returns this type has no size. Example of desired output:

<Mystruct=a:0x0102 b:0x0304 c:0x00000506>
2 Answers

Try sizeof(*dtype) and 0 padding left:

...
for name, *dtype in self._fields_:
    value = getattr(self, name)
    size = sizeof(*dtype) * 2
    if type(value) == int: textlist.append(f'{name}:0x{value:0{size}x}')
    ...

Here is a my final solution (hopefully), which also supports the use of bitsize and Array types will be exploded too:

class Tracerec(BigEndianStructure):
    # do not define _fields_ in this parent class
    
    def _format_value(self, value, dtype):
        if isinstance(value, Array):
            # Use Array undocumented _type_:
            text = ','.join([ self._format_value(x, value._type_) for x in value ])
            return f'[{text}]'
        elif type(value) == int:
            size = sizeof(dtype) * 2   # size mutliply by byte width
            return f'0x{value:0{size}x}'
        else:
            return f'{value}'
    
    def _format_field(self, field):
        name, dtype, *bitsize = field
        value = getattr(self, name)
        return f'{name}:{self._format_value(value, dtype)}'

    def __repr__(self):
        text = ' '.join( [ self._format_field(x) for x in self._fields_ ] )
        return f'<{self.__class__.__name__}={text}>'
    def __getitem__(self, i):
        if type(i)==str: return getattr(self, i)
        return getattr(self, self._fields_[i][0])
    def __len__(self):
        return len(self._fields_)

class Mystruct(Tracerec):
    _fields_ = [
        ('a', c_uint16),
        ('b', c_uint16,14),
        ('c', c_uint32),
        ('d', c_uint16 * 3)
    ]

buffer = b'\x01\x02\x03\x04\x00\x00\x05\x06\x07\x07\x08\x08\x09\x09\0x0\0x0'
x = Mystruct.from_buffer_copy(buffer)
x

And the output:

<Mystruct=a:0x0102 b:0x00c1 c:0x00000506 d:[0x0707,0x0808,0x0909]>
Related