pythonpython-3.xctypes

How to convert ctypes to bytes


I want to access ctype.Structure with I have defined, as a bytes in Python (b'').

I don't know it is a correct way? I need to send a header (it should be architecture free) to other device.

So I defined it for example as:

    class Header(ctypes.Structure):
    _pack_ = 2
    _fields_ = [
        ('version', c_uint8),
        ('additional_options', c_uint8),
        ('soft_version', c_uint16),
        ('compilation_time', c_uint8 * 6),
        ('crc', c_uint16)
    ]

Now I need to calculate CRC. Start from filed at version to compilation_time I have a function that work with bytes.

So for me it would be OK just convert ctypes.Structure to bytes (b'') or access directly memory and change last two bytes.

I have tried use struct but I didn't found pragma option.


Solution

  • struct is what you want:

    import struct
    from binascii import crc32
    
    fmt = struct.Struct('<BBH6s')
    data = struct.pack('<BBH6s', 1, 2, 3, b'170207')
    print(data)
    print(data.hex(' '))
    crc = 0xABCD # <do your calculation here>
    data += struct.pack('<H',crc)
    print(data)
    print(data.hex(' '))
    

    < means pack little-endian and will not add padding bytes like C would, so no _pack_ is necessary. If you need big-endian, use > instead.

    I used 6s and passed a byte string of the correct length for your time. If you want to pass raw bytes instead use 6B and list the 6 bytes instead of the one string.

    Output:

    b'\x01\x02\x03\x00170207'
    01 02 03 00 31 37 30 32 30 37
    b'\x01\x02\x03\x00170207\xcd\xab'
    01 02 03 00 31 37 30 32 30 37 cd ab
    

    Here is a ctypes solution. I used crc32 because one was available in the library for demo purposes.

    import ctypes as ct
    from binascii import crc32
    
    class Header(ct.LittleEndianStructure):
        _pack_ = 1  # prevent alignment padding
        _fields_ = (('version', ct.c_uint8),
                    ('additional_options', ct.c_uint8),
                    ('soft_version', ct.c_uint16),
                    ('compilation_time', ct.c_uint8 * 6),
                    ('crc', ct.c_uint32))
    
    h = Header(1, 2, 3)
    h.compilation_time[:] = b'ABCDEF'  # use slicing to copy into an array
    b = bytes(h)[:Header.crc.offset]   # convert struct to bytes and truncate CRC
    print(b)
    h.crc = crc32(b)  # Set the correct CRC
    b = bytes(h)      # convert whole struct to bytes
    print(b)
    print(b.hex(' '))
    

    Output:

    b'\x01\x02\x03\x00ABCDEF'
    b'\x01\x02\x03\x00ABCDEF\xe7s\x85\xa6'
    01 02 03 00 41 42 43 44 45 46 e7 73 85 a6