【问题标题】:Writing aligned data in binary file在二进制文件中写入对齐数据
【发布时间】:2017-07-21 20:55:05
【问题描述】:

我正在创建一个包含一些数据对象的文件。数据对象有不同的大小,是这样的(非常简化):

struct Data{
   uint64_t size;
   char     blob[MAX_SIZE];
// ... methods here:
}

在稍后的某个步骤,文件将在内存中 mmap(), 所以我希望每个数据对象的开头都以 8 个字节对齐的内存地址开始,uint64_t size 将被存储(让我们忽略字节序)。

代码看起来或多或少这样(目前硬编码 8 个字节):

size_t calcAlign(size_t const value, size_t const align_size){
    return align_size - value % align_size;
}

template<class ITERATOR>
void process(std::ofstream &file_data, ITERATOR begin, ITERATOR end){
    for(auto it = begin; it != end; ++it){
        const auto &data = *it;

        size_t bytesWriten = data.writeToFile(file_data);

        size_t const alignToBeAdded = calcAlign(bytesWriten, 8);

        if (alignToBeAdded != 8){
            uint64_t const placeholder = 0;

            file_data.write( (const char *) & placeholder, (std::streamsize) alignToBeAdded);
        }
    }
}

这是实现文件内对齐的最佳方式吗?

【问题讨论】:

  • “对齐”是什么意思?在文件中的特定位置写入?您是否尝试过寻找在文件中到想要的位置?
  • 我会编辑问题
  • 因此,您希望所有数据都采用 8 字节格式(64 位)。对吗?
  • 只是一个关于你可能通过在文件中向前搜索并用间隙写入可能得到的“洞”的注释,当你对文件进行内存映射时,这些“洞”将被包括在内,结构的位置将在内存映射中对齐 8 字节。此外,文件中的“漏洞”可能会减少它在磁盘上使用的空间。如果您用将占用磁盘空间的数据(甚至是零)填充这些孔。
  • 你必须四舍五入。所以它是 8 * ((offset + 7) / 8)。

标签: c++ file alignment


【解决方案1】:

你不需要依赖writeToFile来返回大小,你可以使用ofstream::tellp

const auto beginPos = file_data.tellp();
// write stuff to file
const auto alignSize = (file_data.tellp()-beginPos)%8;
if(alignSize)
    file_data.write("\0\0\0\0\0\0\0\0",8-alignSize);

编辑后 OP 评论: 在一个最小的例子上进行了测试,它可以工作。

#include <iostream>
#include <fstream>
int main(){
    using namespace std;
    ofstream file_data;
    file_data.open("tempfile.dat", ios::out | ios::binary);
    const auto beginPos = file_data.tellp();
    file_data.write("something", 9);
    const auto alignSize = (file_data.tellp() - beginPos) % 8;
    if (alignSize)
        file_data.write("\0\0\0\0\0\0\0\0", 8 - alignSize);
    file_data.close();
    return 0;
}

【讨论】:

  • ofstream::tellp - 很好。我将 alignToBeAdded 用于其他用途,但将使用 ofstream::tellp 来实现。大约在一段时间内 - 肯定比 calc 更有效,并且使用单个操作写入? (使用 ostream::put(char) 的好地方
  • 实际上,您可以完全摆脱 while 并只编写一次,因为它更高效、更安全,因为 alignSize 永远不会 >=8。代码修改
【解决方案2】:

您可以通过操作输入缓冲区而不是文件处理来优化流程。修改您的 Data 结构,以便填充缓冲区的代码负责对齐。

struct Data{
    uint64_t size;
    char blob[MAX_SIZE];
    // ... other methods here

    // Ensure buffer alignment
    static_assert(MAX_SIZE % 8 != 0, "blob size must be aligned to 8 bytes to avoid Buffer Overflow.");

    uint64_t Fill(const char* data, uint64_t dataLength) {
        // Validations...

        memcpy(this->blob, data, dataLength);
        this->size = dataLength;

        const auto paddingLen = calcAlign(dataLength, 8) % 8;
        if (padding > 0) {
            memset(this->blob + dataLength, 0, paddingLen);
        }

        // Return the aligned size
        return dataLength + paddingLen;
    }
};

现在,当您将数据传递给“处理”函数时,只需使用从 Fill 返回的大小,这可以确保 8 字节对齐。 这样您仍然可以手动进行对齐,但您不必向文件写入两次。

注意:此代码假定您也使用 Data 作为输入缓冲区。如果您的代码在将缓冲区写入文件之前使用其他对象来保存缓冲区,则您应该使用相同的主体。

如果您可以使用 POSIX,另请参阅 pwrite

【讨论】:

  • 我做了类似的事情。我将考虑因素“推送”到 Data 类中。
猜你喜欢
  • 1970-01-01
  • 2014-09-29
  • 1970-01-01
  • 1970-01-01
  • 2016-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多