为什么非犯罪序列化添加了5个字节零前缀
Why does an non-intrusive serialization add a 5 byte zero prefix?
我正在调查使用Boost :: Archive的应用程序中从非标准到标准字符串的端口。非标准字符串具有以下示例所示的非侵入性样式定义的(de-)序列化。序列化和次要化可以按预期工作,但是当移植应用程序收到旧消息时,它会出现不良的分配。这是由5个字节(全零)的插入在字符串大小之前引起的。
是什么原因导致插入这5个字节?这是某种魔术标记吗?
示例:
#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
一样。
来自boost(1.65.1)doc:
默认情况下,对于每个类别的类别,类信息都写入档案。此信息包括版本号,实现级别和跟踪行为。这是必要的,以便即使程序的后续版本更改了类的某些当前特征值,也可以正确地进行档案化。这些数据的空间最小。有一点运行时开销,因为必须检查每个类以查看其是否已经包含在存档中的类信息。在某些情况下,即使这也可能被认为太多了。可以通过将实现级别类特征设置为:boost :: serialization :: object_serializable来消除此额外开销。
现在,可能(*)这是标准类的默认值。实际上,添加
BOOST_CLASS_IMPLEMENTATION(own_string, boost::serialization::object_serializable)
在全局范围内使test_x_string结果以相同的字节结果。这应该解释观察到的额外字节差异。
也就是说,我没有找到有关标准类序列化特征的任何特定保证(其他人可能比我更了解)。
(*)实际上有关特质设置可移植性的部分提到:
避免此问题的另一种方法是为所有原始类型的模板my_wrapper的所有专业化分配序列化特征,以使类信息永远不会保存。这是我们为STL Collections实施序列化
已完成的工作。
因此,这可能会给您足够的信心,即在这种情况下标准收集(包括std :: string)将给出相同的字节。
我认为您要求无证件实现详细信息。不必有一个原因。这是存档格式的实现细节。
这是一个有趣的问题。
您必须告诉库您不需要类型的所有功能(对象跟踪,类型信息,版本使用)。具体而言,这说明了如何实现相同的足迹。
注意,您显然会失去禁用的功能
活在coliru
#include <iostream>
#include <string>
#include <sstream>
struct own_string { // simplified custom string class
std::string content;
};
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/tracking.hpp>
BOOST_CLASS_IMPLEMENTATION(own_string, boost::serialization::level_type::object_serializable)
BOOST_CLASS_TRACKING(own_string, boost::serialization::track_never)
//#include <boost/serialization/wrapper.hpp>
//BOOST_CLASS_IS_WRAPPER(own_string)
#include <boost/serialization/array_wrapper.hpp>
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;
if (size)
ar & boost::serialization::make_array(&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);
if (size)
ar & boost::serialization::make_array(&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;
}
#include <boost/archive/binary_oarchive.hpp>
void test_normal_string()
{
std::stringstream ss;
{
boost::archive::binary_oarchive ar{ss, boost::archive::no_header|boost::archive::no_codecvt};
std::string test = "";
//std::cout << string_to_hex(ss.str()) << std::endl;
ar << test;
}
//adds 00 00 00 00 00 00 00 00
std::string bytes = ss.str();
std::cout << string_to_hex(bytes) << " (" << bytes.size() << " bytes)n";
}
void test_own_string()
{
std::stringstream ss;
{
boost::archive::binary_oarchive ar{ss, boost::archive::no_header|boost::archive::no_codecvt};
own_string otest{""};
//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::string bytes = ss.str();
std::cout << string_to_hex(bytes) << " (" << bytes.size() << " bytes)n";
}
int main()
{
test_normal_string();
test_own_string();
}
打印
0000000000000000 (8 bytes)
0000000000000000 (8 bytes)
请注意,样品删除了许多其他噪声/高架来源。
- 从文件中读取多个字节,并将它们存储在C++中进行比较
- 为什么 bool 和 _Bool 如果它们在内存中占用 1 个字节,它们只能存储 0 或 1
- 在 Linux 中,uint32_t从 4 个字节更改为 6 个字节
- 运行时错误:引用绑定到类型"int"的未对齐地址0xbebebebebebebec6,这需要 4 个字节对齐 (stl_vector.h)
- 向指针地址添加 20 个字节偏移量
- 将(N 个字节)无符号字符指针转换为浮点数和双 C++
- C++/地址空间:每个地址 2 个字节?
- 为什么这个结构需要 24 个字节
- 为什么带有 vptr 的对象长 12 个字节?
- 使用 valgrind 检查我的链表暗示中的内存泄漏,让我"肯定丢失:1 个块中有 40 个字节"
- 复制后删除原始数组指针将前 3 个字节设置为 0
- 在编译时而不是运行时创建一个由两个字节组成的值
- 为什么在我的实现中,所有数组都对齐到 16 个字节?
- 使用 Python 导出 4 个字节浮点数
- C++向指针地址添加 4 个字节
- 是否强制转换void**并将第一个字节设置为nullptr
- AES-128 CFB-8解密的前16个字节已损坏
- Qt TCP 套接字 - 写入超过 15 个字节
- 在"C++代码"部分中,可能会写入两个字节
- 视觉C++转换和写入值为 10 的无符号 int 给出 5 个字节