为什么非侵入式序列化要添加 5 字节零前缀?

2023-12-04

我正在研究使用 boost::archive 的应用程序中从非标准字符串到标准字符串的端口。非标准字符串的(反)序列化以非侵入式方式定义,如下例所示。序列化和反序列化按预期工作,但是当移植的应用程序收到旧消息时,它会因分配错误而崩溃。这是由于在字符串大小之前插入了 5 个字节(全为零)造成的。

是什么原因导致插入这5个额外字节呢?这是某种魔法标记吗?

Example:

#include <iostream>
#include <string>
#include <sstream>
#include <boost/serialization/split_free.hpp>
#include <boost/archive/binary_oarchive.hpp>

struct own_string { // simplified custom string class
    std::string content;
};

namespace boost
{
    namespace serialization
    {
        template<class Archive>
        inline void save(
            Archive & ar,
            const own_string & t,
            const unsigned int /* file_version */)
        {
            size_t size = t.content.size();
            ar << size;
            ar.save_binary(&t.content[0], size);
        }

        template<class Archive>
        inline void load(
            Archive & ar,
            own_string & t,
            const unsigned int /* file_version */)
        {
            size_t size;
            ar >> size;
            t.content.resize(size);
            ar.load_binary(&t.content[0], size);
        }

// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
        template<class Archive>
        inline void serialize(
            Archive & ar,
            own_string & t,
            const unsigned int file_version)
        {
            boost::serialization::split_free(ar, t, file_version);
        }

    } // namespace serialization
} // namespace boost

std::string string_to_hex(const std::string& input)
{
    static const char* const lut = "0123456789ABCDEF";
    size_t len = input.length();

    std::string output;
    output.reserve(2 * len);
    for (size_t i = 0; i < len; ++i)
    {
        const unsigned char c = input[i];
        output.push_back(lut[c >> 4]);
        output.push_back(lut[c & 15]);
    }
    return output;
}

void test_normal_string()
{
    std::stringstream ss;
    boost::archive::binary_oarchive ar{ss};

    std::string test = "";

    std::cout << string_to_hex(ss.str()) << std::endl;
    ar << test;

    //adds 00 00 00 00 00 00 00 00
    std::cout << string_to_hex(ss.str()) << std::endl;
}

void test_own_string()
{
    std::stringstream ss;
    boost::archive::binary_oarchive ar{ss};

    std::string test = "";

    own_string otest{test};
    std::cout << string_to_hex(ss.str()) << std::endl;
    ar << otest;

    //adds 00 00 00 00 00 00 00 00 00 00 00 00 00
    std::cout << string_to_hex(ss.str()) << std::endl;
}

int main()
{
    test_normal_string();
    test_own_string();
}

因此,您想要反序列化先前序列化的own_string就好像它是一个std::string.

From 提升(1.65.1)文档:

默认情况下,对于每个序列化的类,类信息都会写入存档中。此信息包括版本号、实现级别和跟踪行为。这是必要的,以便即使程序的后续版本更改了类的某些当前特征值,也可以正确反序列化存档。该数据的空间开销是最小的。由于必须检查每个类以查看其类信息是否已包含在存档中,因此存在一点运行时开销。在某些情况下,即使这样也可能被认为太过分了。通过将实现级别类特征设置为:boost::serialization::object_serialized,可以消除这种额外的开销。

现在,可能(*)这是标准类的默认设置。事实上,添加

BOOST_CLASS_IMPLEMENTATION(own_string, boost::serialization::object_serializable)

在全局范围内使 test_X_string 结果相同的字节。这应该可以解释观察到的额外字节差异。

也就是说,我未能找到有关标准类序列化特征的任何具体保证(其他人可能比我更了解)。

(*) 实际上是关于特征设置的可移植性部分提到:

避免此问题的另一种方法是将序列化特征分配给所有原始类型的模板 my_wrapper 的所有特化,以便永远不会保存类信息。这就是我们为 STL 集合实现序列化所做的事情

因此,这可能会给您足够的信心,标准集合(因此包括 std::string)在这种情况下会给出相同的字节。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么非侵入式序列化要添加 5 字节零前缀? 的相关文章

随机推荐