Лучший способ утверждать совместимую архитектуру при чтении и записи файла? - PullRequest
1 голос
/ 23 августа 2010

У меня есть программа, которая читает и записывает двоичный файл. Файл является взаимозаменяемым между выполнениями программы на одной и той же платформе, но файл, созданный на одной машине, может быть недопустимым на другой платформе из-за размеров типов, порядкового номера и т. Д.

Я хочу, чтобы быстрый способ утверждать, что данный файл допустим для чтения на данной архитектуре. Я не заинтересован в создании файловой кросс-архитектуры (фактически файл представляет собой структуры с отображением в памяти). Мне нужен только способ проверить, что файл был создан на архитектуре с одинаковыми типами размеров и т. Д., Прежде чем читать его.

Одна идея состоит в том, чтобы написать структуру с постоянными магическими числами в начале файла. Это можно прочитать и проверить. Другой вариант - хранить sizeof различных типов в однобайтовых целых числах.

Это для C, но я полагаю, что вопрос не зависит от языка для языков с такими же проблемами.

Какой лучший способ сделать это?

Я приветствую поправки к названию этого вопроса!

Ответы [ 4 ]

2 голосов
/ 23 августа 2010

Мне нравится магическое число в начале идеи файла.Вы можете выполнить эти проверки с магическим значением:

  • Если есть хотя бы два магических байта и вы рассматриваете их как одно многобайтовое целое число, вы можете обнаружить изменения порядка байтов.Например, если вы выбираете 0xABCD, а ваш код читает 0xCDAB, вы находитесь на платформе с порядком байтов, отличным от того, на котором был записан файл.

  • Если вы используете 4-или 8-байтовое целое число, вы можете обнаружить 32- или 64-битные платформы, если вы выберете тип данных, так что на двух платформах он будет разным.

  • Если есть ещечем просто целое число или вы выбираете его тщательно, вы можете исключить возможность случайного чтения файла, записанного другой программой, с высокой степенью вероятности.См. / Etc / magic в любой системе типов Unixy для хорошего списка значений, которых следует избегать.

1 голос
/ 24 августа 2010

Вызовите функцию uname(2) (или эквивалент на не-POSIX-платформах) и запишите поля sysname и machine из struct utsname в заголовок в начале файла.

(Это нечто большее, чем просто размеры и порядковый номер - есть также форматы с плавающей запятой и стандарты дополнения структуры, которые также различаются. Так что это действительно ABI машины, который вы хотите утверждать, тот же).

1 голос
/ 24 августа 2010
#include <stdint.h>

union header {
     uint8_t a[8];
     uint64_t u;
};

const struct header h = { .u = (sizeof(      short  ) <<  0 )
                             | (sizeof(        int  ) <<  8 ) 
                             | (sizeof(       long  ) << 16 ) 
                             | (sizeof(   long long ) << 24 )
                             | (sizeof(       float ) << 32 )
                             | (sizeof(      double ) << 40 )
                             | (sizeof( long double ) << 48 )
                             | 0 } ;

Этого должно быть достаточно, чтобы проверить размеры типов и порядковый номер, за исключением того, что числа с плавающей запятой для этого невероятно сложны.

Если вы хотите убедиться, что ваши числа с плавающей запятой хранятся в одном и том жеотформатируйте на устройстве записи и чтения, тогда вы можете захотеть сохранить пару постоянных чисел с плавающей запятой (более интересных, чем 0, 1 и -1) в различных размерах после этого заголовка и убедиться, что они соответствуют вашим ожиданиям.be.

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

Если вас не волнует floatили что-то в этом роде, тогда не стесняйтесь удалять их.Я не включил символ, потому что он должен быть всегда 1 байтом.

Это может быть хорошей идеей, если вы также сохраните размер некоторой структуры, такой как:

struct misalligned {
    char c;
    uint64_t u;
};

Это должно позволитьВы можете легко определить выравнивание и заполнение компилятора, который сгенерировал код, сгенерировавший файл.Если бы это было сделано на большинстве 32-битных компьютеров, которые заботятся о выравнивании, размер был бы 96, потому что между c и u было бы 3 байта заполнения, но если бы это было сделано на 64-битной машине, то его размер мог бы быть 128, имея7 байтов заполнения между c и u.Если бы это было сделано на AVR, размер этого, скорее всего, был бы 9, потому что там не было бы заполнения.

NOTE

  • этот ответ основывался на вопросе о том, что файлы былипамять сопоставлена ​​и нет необходимости в переносимости, за исключением того, что файл был неверного формата.Если бы вопрос касался общего хранения файлов и повторной проверки, я бы ответил по-другому.Самым большим отличием будет упаковка структур данных.
0 голосов
/ 24 августа 2010

Во-первых, я полностью согласен с предыдущим ответом Уоррена Янга .

Это случай метаданных , о котором мы говорим.

В файловой системе и однородном содержимом я бы предпочел иметь один дополненный (до размера структуры) метаданные в начале двоичного файла. Это позволяет сохранить структуру данных выравнивание и упростить написание приложений.

Если это неоднородно, я бы предпочел использовать Structure-Value или Structure-Length-Value (также известную как Type Length Value) перед каждыми данными или диапазоном данных.

В потоке со случайным объединением может потребоваться синхронизация структуры с чем-то вроде HDLC (в Википедии) и повторение метаданных во время постоянного потока двоичных данных. Если вы знакомы с аудио / видео форматом, вы можете подумать о тегах внутри потока данных, который по сути состоит из кадров.

Хороший предмет!

...