Доступ к битовым полям при чтении / записи двоичных структур данных - PullRequest
11 голосов
/ 26 августа 2011

Я пишу парсер для двоичного формата. Этот двоичный формат включает в себя различные таблицы, которые также в двоичном формате обычно содержат переменные размеры полей (где-то от 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
}

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

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

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

Спасибо.

Ответы [ 2 ]

6 голосов
/ 26 августа 2011

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

Редактировать 2: Это действительно удар или промах. Это зависит от способа компиляции библиотеки битами в структуру, которая не стандартизирована. Например, в gcc 4.5.3 он работает до тех пор, пока я не использую атрибут для упаковки структуры, то есть __attribute__ ((__packed__)) (поэтому вместо 6 байтов он упаковывается в 4 байта, которые можно проверить с помощью __alignof__ и sizeof). Я могу заставить его почти работать, добавив _pack_ = True к определению структуры ctypes, но для fieldE это не получится. gcc отмечает: «Смещение упакованного битового поля« fieldE »изменилось в 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

C

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);
}
4 голосов
/ 26 августа 2011

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

>>> 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.

...