1

I have a program that reads and writes a binary file. A file is interchangeable between executions of the program on the same platform, but a file produced on one machine may not be valid on another platform due to the sizes of types, endian-ness etc.

I want a quick way to be able to assert that a given file is valid for reading on a given architecture. I am not interested in making a file cross-architecture (in fact the file is memory-mapped structs). I only want a way of checking that the file was created on an architecture with the same size types, etc before reading it.

One idea is to write a struct with constant magic numbers in to the start of the file. This can be read and verified. Another would be to store the sizeof various types in single-byte integers.

This is for C but I suppose the question is language-agnostic for languages with the same kinds of issues.

What's the best way to do this?

I welcome amendments for the title of this question!

4

4 回答 4

2

我喜欢文件创意开头的幻数。您可以使用神奇的值进行这些检查:

  • 如果至少有两个魔术字节,并且您将它们视为单个多字节整数,则可以检测字节序变化。例如,如果您选择 0xABCD,并且您的代码读取 0xCDAB,那么您在一个与写入文件的平台具有不同字节序的平台上。

  • 如果您使用 4 字节或 8 字节整数,则可以检测 32 位和 64 位平台,前提是您选择了数据类型,因此这两个平台上的大小不同。

  • 如果不只是一个整数或者你仔细选择它,你可以排除不小心读取另一个程序写出的文件的可能性,概率很高。请参阅任何 Unixy 类型系统上的 /etc/magic 以获得要避免的值的良好列表。

于 2010-08-23T17:03:09.610 回答
1
#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 } ;

这应该足以验证类型大小和字节顺序,除了浮点数对此非常困难。

如果您想验证您的浮点数在写入器和读取器上是否以相同的格式存储,那么您可能希望在不同的此标头之后的大小,并验证它们是否是您认为应该的大小。

很有可能存储带有版本号的实际魔术字符串也可以作为另一种检查这是正确的文件格式。

如果您不关心浮动或类似的东西,请随时删除它们。我没有包括 char 因为它应该总是 1 个字节。

如果您还存储一些结构的大小,例如:

struct misalligned {
    char c;
    uint64_t u;
};

这应该允许您轻松确定生成生成文件的代码的编译器的对齐和填充。如果这是在大多数关心对齐的 32 位计算机上完成的,那么大小将为 96,因为 c 和 u 之间会有 3 个字节的填充,但如果它是在 64 位机器上完成的,那么它的大小可能是 128,有c 和 u 之间的 7 个字节的填充。如果这是在 AVR 上完成的,那么它的大小很可能是 9,因为没有填充。

笔记

  • 这个答案依赖于说明文件正在被内存映射并且除了识别文件格式错误之外不需要可移植性的问题。如果问题是关于一般文件存储和检索,我会做出不同的回答。最大的区别是打包数据结构。
于 2010-08-23T22:08:48.900 回答
1

调用该uname(2)函数(或非 POSIX 平台上的等效函数)并将sysnamemachine字段写入struct utsname文件开头的标题中。

(不仅仅是大小和字节序,还有浮点格式和结构填充标准也各不相同。因此,您想要断言的机器 ABI 确实是相同的)。

于 2010-08-24T01:25:06.960 回答
0

首先,我完全同意Warren Young之前提供的答案。

这是我们正在谈论的元数据案例。

在文件系统和同质内容上,我更喜欢在二进制文件的开头有一个填充(到结构的大小)元数据。这允许保留数据结构对齐并简化附加写入。

如果是异构的,我更喜欢在每个数据或数据范围之前使用 Structure-Value 或 Structure-Length-Value(也称为类型长度值)。

在随机加入的流上,您可能希望在二进制数据的持续流动期间与HDLC(在维基百科上)和元数据重复之类的结构同步。如果您熟悉音频/视频格式,您可能会想到本质上由帧组成的数据流中的 TAG。

不错的主题!

于 2010-08-23T22:22:13.527 回答