C struct => ctypes struct ... это отображение корректно? - PullRequest
3 голосов
/ 22 марта 2012

Я пытаюсь получить доступ к двум устаревшим функциям де / сжатия из Python, которые написаны на C и в настоящее время доступны через DLL (у меня есть источник C).

Функции передаются (частично) заполненной структуре C и используют эту информацию для сжатия или распаковки данных в предоставленном буфере.

Так называются функции. Я добавил __cdecl для совместимости с Python.

// Both functions return 0 on success and nonzero value on failure
int __cdecl pkimplode(struct pkstream *pStr);
int __cdecl pkexplode(struct pkstream *pStr);

Вот структура pkstream, как определено в C:

struct pkstream {
   unsigned char *pInBuffer;           // Pointer to input buffer
   unsigned int nInSize;               // Size of input buffer
   unsigned char *pOutBuffer;          // Pointer to output buffer
   unsigned int nOutSize;              // Size of output buffer upon return
   unsigned char nLitSize;             // Specifies fixed or var size literal bytes
   unsigned char nDictSizeByte;        // Dictionary size; either 1024, 2048, or 4096
   // The rest of the members of this struct are used internally,
   // so setting these values outside pkimplode or pkexplode has no effect
   unsigned char *pInPos;              // Current position in input buffer
   unsigned char *pOutPos;             // Current position in output buffer
   unsigned char nBits;                // Number of bits in bit buffer
   unsigned long nBitBuffer;           // Stores bits until enough to output a byte
   unsigned char *pDictPos;            // Position in dictionary
   unsigned int nDictSize;             // Maximum size of dictionary
   unsigned int nCurDictSize;          // Current size of dictionary
   unsigned char Dict[0x1000];         // Sliding dictionary used for compdecomp
};

Это моя попытка отразить эту структуру в Python.

# Define the pkstream struct
class PKSTREAM(Structure):
   _fields_ = [('pInBuffer', c_ubyte),
               ('nInSize', c_uint),
               ('pOutBuffer', c_ubyte),
               ('nOutSize', c_uint),
               ('nLitSize', c_ubyte),
               ('nDictSizeByte', c_ubyte),
               ('pInPos', c_ubyte),
               ('pOutPos', c_ubyte),
               ('nBits', c_ubyte),
               ('nBitBuffer', c_ulong),
               ('pDictPos', c_ubyte),
               ('nDictSize', c_uint),
               ('nCurDictSize', c_uint),
               ('Dict', c_ubyte)]

Я был бы очень признателен за помощь в решении следующих вопросов (которые я предпочитаю задавать на внешнем интерфейсе, а не просто «подгонять» его, надеюсь, по понятным причинам):

  1. Я не уверен, использовать ли c_ubyte, c_char или c_char_p для членов типа unsigned char . c_ubyte наиболее точно отображается в ctypes для unsigned char (по крайней мере, в соответствии с документами), но на самом деле является? int / long? в Python.

  2. Иногда элемент является указателем на неподписанный символ ... будет ли это сопоставляться с c_char_p? Документы ctypes говорят, что ВСЕ строки байтов и юникод в любом случае передаются как указатели, так что для этого мне нужно сделать?

  3. Мне нужно предоставить функцию pOutBuffer, которая должна быть указателем на расположение выделенной памяти, в которую функция может копировать де / сжатые данные. Я считаю, что я должен использовать create_string_buffer (), чтобы создать буфер соответствующего размера для этого?

  4. Мне также нужно знать, как определить член Dict [0x1000] , который ищет (для меня) создание 4096-байтового буфера для внутреннего использования в функциях. Я знаю, что мое определение явно неверно, но не знаю, как оно должно быть определено?

  5. Должны ли функции C быть оформлены как __stdcall или __cdecl? (Я использовал последний в некоторых тестовых DLL, так как до этого момента работал).

Любая обратная связь будет очень признательна!

Заранее спасибо,

Джеймс

1 Ответ

2 голосов
/ 22 марта 2012

Если данные в структуре являются указателями, вы должны также объявить их как указатели на стороне Python.

Один из способов сделать это - использовать утилиту POINTER в ctypes - это объект на несколько более высоком уровне, чем ctypes.c_char_p (и не полностью совместимый с этим) - но ваш код станет болееудобочитаемый.Кроме того, для моделирования массивов C базовые типы ctypes могут быть умножены на скаляр, а возвращаемый объект - это объект, который можно использовать в качестве вектора C базового типа того же размера - (поэтому поле Dict можно определить какниже c_ubyte * 4096)

Обратите внимание, что хотя char эквивалентно c_ubyte, int эквивалентно c_int вместо c_uint и аналогично для long.

В вашем определении структуры не указано, что буферы, на которые указывают ссылки, const.Если вы передадите строку python (неизменную) и ваша библиотека попытается изменить ее, вы получите ошибки.Вместо этого вы должны передать изменяемую память, которая возвращается из create_string_buffer, инициализированной вашей строкой.

POINTER = ctypes.POINTER
# Define the pkstream struct
class PKSTREAM(Structure):
   _fields_ = [('pInBuffer', POINTER(c_char)),
               ('nInSize', c_int),
               ('pOutBuffer', POINTER(c_char)),
               ('nOutSize', c_int),
               ('nLitSize', c_char),
               ('nDictSizeByte', c_char),
               ('pInPos', POINTER(c_char)),
               ('pOutPos', POINTER(c_char)),
               ('nBits', c_char),
               ('nBitBuffer', c_long),
               ('pDictPos', POINTER(c_char)),
               ('nDictSize', c_int),
               ('nCurDictSize', c_int),
               ('Dict', c_char * 0x1000)]

Что касается (5), я не знаю, как вы должны украсить свои функции C - используйте все, что работает.

...