Доступ к битовым полям при чтении/записи двоичных структур данных

Я пишу парсер для двоичного формата. Этот двоичный формат включает в себя разные таблицы, которые снова находятся в двоичном формате, в котором обычно используются разные размеры полей (от 50 до 100 из них).

Большинство этих структур будут иметь битовые поля и будут выглядеть примерно так, как показано на C:

struct myHeader
{
  unsigned char fieldA : 3
  unsigned char fieldB : 2;
  unsigned char fieldC : 3;
  unsigned short fieldD : 14;
  unsigned char fieldE : 4
}

Я наткнулся на структурный модуль, но понял, что его самое низкое разрешение было байтом, а не бит, иначе модуль в значительной степени был подходящим для этой работы.

Я знаю, что битподы поддерживаются с помощью ctypes, но я не уверен, как здесь взаимодействовать ctypes structs, содержащие битовые поля.

Другим вариантом является манипулирование битами и подача его в байты и использование его с помощью модуля struct, но поскольку у меня есть около 50-100 различных типов таких структур, запись кода для этого становится более подверженной ошибкам, Меня также беспокоит эффективность, поскольку этот инструмент может использоваться для синтаксического анализа больших гигабайт двоичных данных.

Спасибо.

Ответ 1

Используя bitstring (о чем вы упоминаете, вы смотрите), его должно быть достаточно просто реализовать. Сначала создайте некоторые данные для декодирования:

>>> myheader = "3, 2, 3, 14, 4"
>>> a = bitstring.pack(myheader, 1, 0, 5, 1000, 2)
>>> a.bin
'00100101000011111010000010'
>>> a.tobytes()
'%\x0f\xa0\x80'

И затем снова декодировать это просто

>>> a.readlist(myheader)
[1, 0, 5, 1000, 2]

Ваша главная забота - это скорость. Библиотека хорошо оптимизирована на Python, но это не так быстро, как библиотека C.

Ответ 2

Я не проверил это строго, но, похоже, работает с неподписанными типами (редактирование: оно также работает с подписанными байтами/короткими типами).

Изменить 2: Это действительно поразило или пропустило. Это зависит от того, как компилятор библиотеки упаковывал биты в структуру, что не стандартизировано. Например, с gcc 4.5.3 он работает до тех пор, пока я не использую атрибут для упаковки struct, то есть __attribute__ ((__packed__)) (поэтому вместо 6 байтов он упаковывается в 4 байта, который вы можете проверить с помощью __alignof__ и sizeof). Я могу заставить его почти работать, добавив _pack_ = True в определение структуры ctypes, но он не работает для fieldE. Заметки gcc: поле "Смещение заполненного битового поля" E изменилось в GCC 4.4 ".

import ctypes

class MyHeader(ctypes.Structure):
    _fields_ = [
        ('fieldA', ctypes.c_ubyte, 3),
        ('fieldB', ctypes.c_ubyte, 2),
        ('fieldC', ctypes.c_ubyte, 3),
        ('fieldD', ctypes.c_ushort, 14),
        ('fieldE', ctypes.c_ubyte, 4),
    ]

lib = ctypes.cdll.LoadLibrary('C/bitfield.dll')

hdr = MyHeader()
lib.set_header(ctypes.byref(hdr))

for x in hdr._fields_:
    print("%s: %d" % (x[0], getattr(hdr, x[0])))

Вывод:

fieldA: 3
fieldB: 1
fieldC: 5
fieldD: 12345
fieldE: 9

С

typedef struct _MyHeader {
    unsigned char  fieldA  :  3;
    unsigned char  fieldB  :  2;
    unsigned char  fieldC  :  3;
    unsigned short fieldD  : 14;
    unsigned char  fieldE  :  4;
} MyHeader, *pMyHeader; 

int set_header(pMyHeader hdr) {

    hdr->fieldA = 3;
    hdr->fieldB = 1;
    hdr->fieldC = 5;
    hdr->fieldD = 12345;
    hdr->fieldE = 9;

    return(0);
}