Чтение 32-битных упакованных двоичных данных в 64-битной системе
Вопрос
Я пытаюсь написать расширение Python C, которое считывает упакованные двоичные данные (они хранятся как структуры структур), а затем анализирует их в объекты Python.Все работает как положено на 32-битной машине (бинарные файлы всегда пишутся на 32-битной архитектуре), но не на 64-битной машине.Есть ли «предпочтительный» способ сделать это?
Было бы много кода для публикации, но в качестве примера:
struct
{
WORD version;
BOOL upgrade;
time_t time1;
time_t time2;
} apparms;
File *fp;
fp = fopen(filePath, "r+b");
fread(&apparms, sizeof(apparms), 1, fp);
return Py_BuildValue("{s:i,s:l,s:l}",
"sysVersion",apparms.version,
"powerFailTime", apparms.time1,
"normKitExpDate", apparms.time2
);
Теперь в 32-битной системе это работает отлично, но в 64-битной мои размеры time_t разные (длина 32-битной и 64-битной).
Блин, вы такие быстрые.
Патрик, изначально я начал использовать пакет struct, но обнаружил, что он слишком медленный для моих нужд.Плюс я искал повод написать расширение Python.
Я знаю, что это глупый вопрос, но каких типов мне следует остерегаться?
Спасибо.
Решение
Явно укажите, что ваши типы данных (например.целые числа) являются 32-битными.В противном случае, если при чтении у вас есть два целых числа рядом друг с другом, они будут прочитаны как одно 64-битное целое число.
Когда вы имеете дело с кроссплатформенными проблемами, следует обратить внимание на две основные вещи:
- Битность.Если ваши упакованные данные записываются с использованием 32-битных целых чисел, то весь ваш код должен явно указывать 32-битные целые числа при чтении. и письмо.
- Порядок байтов.Если вы переместите свой код с чипов Intel на PPC или SPARC, порядок байтов будет неправильным.Вам придется импортировать данные, а затем перевернуть их, чтобы они соответствовали текущей архитектуре.В противном случае 12 (
0x0000000C
) будет читаться как 201326592 (0x0C000000
).
Надеюсь, это поможет.
Другие советы
Модуль struct должен уметь это делать, хотя выравнивание структур в середине данных всегда является проблемой.Однако сделать это правильно не так уж и сложно:выясните (один раз), к какой границе выравниваются структуры в структурах, затем дополните (вручную, с помощью спецификатора «x») до этой границы.Вы можете дважды проверить заполнение, сравнив struct.calcsize() с фактическими данными.Это, конечно, проще, чем писать для него расширение C.
Чтобы продолжать использовать Py_BuildValue() таким образом, у вас есть два варианта.Вы можете определить размер time_t во время компиляции (с точки зрения фундаментальных типов, например, «int», «long» или «ssize_t»), а затем использовать правильный символ формата для Py_BuildValue — «i» для int, «l» — длинное, «n» — ssize_t.Или вы можете использовать PyInt_FromSsize_t() вручную, и в этом случае компилятор выполнит за вас повышающее преобразование, а затем использует символы формата «O» для передачи результата в Py_BuildValue.
Вам необходимо убедиться, что вы используете в своей структуре независимые от архитектуры элементы.Например, int может иметь длину 32 бита в одной архитектуре и 64 бита в другой.Как предлагали другие, используйте int32_t
вместо этого типы стилей.Если ваша структура содержит невыровненные элементы, вам, возможно, придется иметь дело с заполнением, добавленным компилятором.
Другая распространенная проблема с данными кросс-архитектуры — порядок байтов.Архитектура Intel i386 имеет прямой порядок байтов, но если вы читаете на совершенно другой машине (например,Alpha или Sparc), об этом вам тоже придется побеспокоиться.
Модуль struct Python справляется с обеими этими ситуациями, используя префикс, передаваемый как часть строки формата.
- @ — использовать собственный размер, порядок байтов и выравнивание.я = sizeof (целое), l = sizeof (длинный)
- = - Использовать собственный порядок байтов, но стандартные размеры и выравнивание (i=32 бита, l=64 бита)
- < — стандартные размеры/выравнивание с прямым порядком байтов
- Стандартные размеры/выравнивание с прямым порядком байтов
В общем, если данные передаются с вашей машины, вам следует привязать порядок байтов и формат размера/заполнения к чему-то конкретному, т.е.используйте «<» или «>» в качестве формата.Если вы хотите обработать это в своем расширении C, вам может потребоваться добавить некоторый код для этого.
Какой у вас код для чтения двоичных данных?Убедитесь, что вы копируете данные в типы правильного размера, например int32_t
вместо того, чтобы просто int
.
Почему вы не используете структура упаковка?