我想知道如何最好地处理磁盘上的数据结构,因为存储布局需要与逻辑设计完全匹配。我发现结构对齐&当你需要为你的存储设置一个特定的布局时,包装并没有多大帮助。
我解决这个问题的方法是使用处理器指令定义结构的(宽度),并在追加逻辑结构模型后面的数据后使用分配字符(字节)数组时写入磁盘的宽度。
例如:
typedef struct __attribute__((packed, aligned(1))) foo {
uint64_t some_stuff;
uint8_t flag;
} foo;
如果我坚持foo on-disk" flag"价值将出现在数据的最后。鉴于我在使用& foo类型的fread读取数据时可以轻松使用foo,然后通常使用struct而不需要任何进一步的字节摆弄。
相反,我更喜欢这样做
#define foo_width sizeof(uint64_t)+sizeof(uint8_t)
uint8_t *foo = calloc(1, foo_width);
foo[0] = flag_value;
memcpy(foo+1, encode_int64(some_value), sizeof(uint64_t));
然后我只使用fwrite和fread来提交和读取字节,但稍后解压缩它们以便使用存储在各种逻辑字段中的数据。
我想知道哪种方法最好用,因为我希望磁盘存储的布局与逻辑布局相匹配......这只是一个例子......
如果有人知道每个方法在解码/解包字节方面的效率与直接从它的磁盘表示中复制结构请分享,我个人更喜欢使用第二种方法,因为它让我完全控制存储布局,但我还没有准备好牺牲性能,因为这种方法需要大量的循环逻辑来解压/遍历字节到数据中的各个边界。
感谢。
答案 0 :(得分:1)
根据您的要求(考虑外观和性能),第一种方法更好,因为编译器将为您完成艰苦的工作。换句话说,如果工具(在这种情况下为编译器)为您提供某些功能,那么您不希望自己实现它,因为在大多数情况下,工具的实现将比您的更有效。
答案 1 :(得分:0)
我更喜欢接近你的第二种方法,但没有memcpy:
void store_i64le(void *dest, uint64_t value)
{ // Generic version which will work with any platform
uint8_t *d = dest;
d[0] = (uint8_t)(value);
d[1] = (uint8_t)(value >> 8);
d[2] = (uint8_t)(value >> 16);
d[3] = (uint8_t)(value >> 24);
d[4] = (uint8_t)(value >> 32);
d[5] = (uint8_t)(value >> 40);
d[6] = (uint8_t)(value >> 48);
d[7] = (uint8_t)(value >> 56);
}
store_i64le(foo+1, some_value);
在典型的ARM上,上面的store_i64le方法将转换为大约30个字节 - 合理的时间,空间和复杂性权衡。从速度的角度来看并不是最理想的,但是从空间的角度来看,对于像Cortex-M0这样不支持未对齐写入的东西来说,这并不是最优的。请注意,写入的代码对机器字节顺序没有依赖性。如果有人知道有人正在使用一个小端平台,其硬件会将未对齐的32位访问转换为8位和16位访问序列,那么可以将该方法重写为
void store_i64le(void *dest, uint64_t value)
{ // For an x86 or little-endian ARM which can handle unaligned 32-bit loads and stores
uint32_t *d = dest;
d[0] = (uint32_t)(value);
d[1] = (uint32_t)(value >> 32);
}
在它可以工作的平台上会更快。请注意,该方法的调用方式与一次一个字节的版本相同;调用者不必担心使用哪种方法。
答案 2 :(得分:0)
如果您使用的是Linux或Windows,则只需对内存映射文件并将指针强制转换为C结构的类型。无论您在此映射区域中编写什么,都将以操作系统可用的最有效方式自动刷新到磁盘。它会比调用"写"以及最小的麻烦更有效率。
正如其他人所说,它不是非常便携。要在little-endian和big-endian之间移植,通常的策略是用big-endian或little-endian编写整个文件,并在访问它时进行转换。但是,这会抛弃你的速度。保持速度的一种方法是编写一个外部实用程序,将整个文件转换一次,然后在将结构从一个平台移动到另一个平台时运行该实用程序。
如果您有两个不同的平台通过共享网络路径访问单个文件,如果您尝试仅仅因为同步问题而自己编写文件,则会遇到很多麻烦,所以我建议采用完全不同的方法比如使用sqlite。