Чтение 32-битных упакованных двоичных данных в 64-битной системе

StackOverflow https://stackoverflow.com/questions/135246

  •  02-07-2019
  •  | 
  •  

Вопрос

Я пытаюсь написать расширение 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-битное целое число.

Когда вы имеете дело с кроссплатформенными проблемами, следует обратить внимание на две основные вещи:

  1. Битность.Если ваши упакованные данные записываются с использованием 32-битных целых чисел, то весь ваш код должен явно указывать 32-битные целые числа при чтении. и письмо.
  2. Порядок байтов.Если вы переместите свой код с чипов 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.

Почему вы не используете структура упаковка?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top