Search code examples
pythonpython-3.xctypesendianness

Python Ctypes: convert list of integers to array of shorts


I'm trying to convert a list of integers to a ctypes array of shorts. I want to then assign that array to a field in a BigEndianStructure. I tried doing this:

from ctypes import BigEndianStructure, c_uint16

class Test(BigEndianStructure):
    _pack_ = 1
    _fields_ = [('arr', c_uint16 * 10)]

num_list = [45, 56, 23]
tester = Test()
short_array = c_uint16 * 10
tester.arr = short_array.from_buffer_copy(bytes(num_list))

But it didn't like that the list was smaller than what it expected:

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    tester.arr = short_array.from_buffer_copy(bytes(num_list))
ValueError: Buffer size too small (3 instead of at least 20 bytes)

So then I tried to extend the list and convert the ints to big endian bytes:

new_list = num_list[:10] + [0]*(10-len(num_list))
buffer = b''
for item in new_list:
    buffer += item.to_bytes(2, byteorder='big')
tester.arr = short_array.from_buffer_copy(buffer)

But it complains about the buffer not being a "be_array" which I'm assuming has to do with the endianness:

Traceback (most recent call last):
  File "test.py", line 14, in <module>
    tester.arr = short_array.from_buffer_copy(buffer)
TypeError: incompatible types, c_ushort_Array_10 instance instead of c_ushort_be_Array_10 instance

Am I overthinking this? Does anyone have any suggestions on how to get around this?

Edit: clarification from the comments, the corresponding structure in C has a uint16_t arr[MAX_LEN], where MAX_LEN=10. So I want to send a 0 filled array if the passed array is not the full MAX_LEN.


Solution

  • There is minimal support for a BigEndianStructure. You can't create a c_ushort_be or c_ushort_be_Array_10 but you can assign to a string slice if your list is shorter than your array and it will do the right thing:

    from ctypes import *
    from binascii import hexlify
    
    class Test(BigEndianStructure):
        _fields_ = [('arr', c_uint16 * 10)]
    
    num_list = [45, 56, 23]
    tester = Test()
    tester.arr[:len(num_list)] = num_list
    print(hexlify(bytes(tester)))
    

    Output (hexadecimal representation of raw structure):

    b'002d003800170000000000000000000000000000'
    

    See also the struct module. It might suit your needs.