msgpack 原理

存储/压缩

对于无符号整型:

-----------     <128,则直接1个字节;

----------     [128,255],  则{0xcc, N}  2个字节表示

--------------[256,65535], 则{0xcd, N} 3个字节表示。(因为N介于[256,65535], 所以需要2个字节; 总共3个字节)

--------------[65536, 2^31-1]    {0xce, N}--------------5个字节

--------------> uint32_max时,{0xcf,  N}----------9个字节。

从这里看, 对于较小值,有压缩效果, 否则pack之后可能长度比原值更长。 这里的0xcc, 0xcd, 0xce, 0xcf高位都为1,同时表示后面还有多少个字节时用来表示同一个整数的。


对于有符号整型, 稍微更复杂一些,前置字节以0xdX开头。



msgpack的序列化最终都归结到基本类型, 如bool, char, int, double等; 在msg/type/下提供了list、string、set、map、queue的支持。

因此, 如果使用了第三方容器, 且自己的类又封装了该第三方容器, 则该类不能被msgpack序列化。

对自定义类的序列化支持, 需要使用宏 MSGPACK_DEFINE(member1, member2, ...) 的形式, 且: member必须为上述的基本类型,或者由基本类型组合得到的类且该类有MSGPACK_DEFINE的类型。

type定死了, 比如之前提到的0xcc, 0xcd等。


对于用户自定义类型,应该就是Raw bytes,length表示raw bytes的长度, 后续时该对象序列化的具体内容。


msgpack无法反射。 无法从一段buffer中自动反序列化得到正确的对象。 调用者必须明确指定类型才能反序列化成功。

msgpack的通常顺序是反序列化得到一个msgpack::obj, 然后再主动convert为用户指定的类型。


protobuf压缩比msgpack好。 且更方便灵活。 protobuf序列化性能应该稍差,因为protobuf的varint比msgpack的复杂。

protobuf自动生成代码有利:节省时间, skema足够灵活; 有弊:某些接口功能不够灵活或者缺接口。

msgpack只需要在类中加一个宏即可实现序列化支持。(也是有限制的,见第一个标红段)



上述两图片来自:

http://www.myexception.cn/open-source/1618388.html

<think>我们需要解释MSGAPCK_DEFINE_MAP和MSGPACK_DEFINE_ARRAY在msgpack-cpp中的区别,并给出代码示例说明两者的不同。在msgpack-cpp中,这两个宏用于定义结构体如何被序列化。它们的主要区别在于序列化后的数据格式:一个是map(键值对),另一个是array(数组)。1.MSGPACK_DEFINE_MAP:序列化后,结构体字段将转换为一个映射(map),其中每个字段都有一个名称(即结构体成员变量名)作为键。2.MSGPACK_DEFINE_ARRAY:序列化后,结构体字段将转换为一个数组(array),按在宏中出现的顺序排列,没有键名。下面通过代码示例来展示两者的区别。</think> # MSGPACK_DEFINE_MAP vs MSGPACK_DEFINE_ARRAY的区别详解 在 msgpack-cpp 库中,`MSGPACK_DEFINE_MAP` 和 `MSGPACK_DEFINE_ARRAY` 都用于简化自定义类型的序列化,但它们在生成的数据格式上有显著区别。 ## 核心区别对比 | 特性 | `MSGPACK_DEFINE_MAP` | `MSGPACK_DEFINE_ARRAY` | |------|----------------------|-----------------------| | **序列化格式** | 键值对(map格式) | 有序数组 | | **字段标识** | 使用成员变量名作为键 | 无字段名,仅有序位置 | | **序列化顺序** | 任意顺序(依赖Map特性) | 按宏定义顺序 | | **向后兼容性** | ★★★ 新字段不影响现有客户端 | ★☆ 新增字段会破坏兼容性 | | **数据大小** | 稍大(包含字段名) | 较小(仅数值) | | **人类可读性** | 较好(有键名) | 差(纯数组) | ## 代码示例说明 ```cpp #include <msgpack.hpp> #include <iostream> struct MapData { int id; std::string name; double price; // 使用MAP序列化 MSGPACK_DEFINE_MAP(id, name, price); }; struct ArrayData { int id; std::string name; double price; // 使用ARRAY序列化 MSGPACK_DEFINE_ARRAY(id, name, price); }; int main() { // 创建测试数据 MapData map_data{123, "ProductX", 19.99}; ArrayData array_data{456, "ProductY", 29.99}; // 序列化 msgpack::sbuffer map_buf, array_buf; msgpack::pack(map_buf, map_data); msgpack::pack(array_buf, array_data); // 输出数据格式差异 std::cout << "Map格式大小: " << map_buf.size() << " bytes\n"; std::cout << "Array格式大小: " << array_buf.size() << " bytes\n"; // 反序列化查看结构 msgpack::object_handle oh_map = msgpack::unpack(map_buf.data(), map_buf.size()); msgpack::object_handle oh_array = msgpack::unpack(array_buf.data(), array_buf.size()); std::cout << "Map格式对象: " << oh_map.get() << std::endl; std::cout << "Array格式对象: " << oh_array.get() << std::endl; return 0; } ``` ### 输出结果示例: ``` Map格式大小: 38 bytes Array格式大小: 27 bytes Map格式对象: {"id":123,"name":"ProductX","price":19.99} Array格式对象: [456,"ProductY",29.99] ``` ## 详细解释 ### 1. MSGPACK_DEFINE_MAP - **原理**:将结构体序列化为 map 类型,每个字段都有一个键名(字段名) - **优点**: - 字段可以按任意顺序排列而不影响反序列化结果 - 新增或删减字段后,旧格式数据仍能部分兼容 - 数据具有自描述性,易于调试 - **缺点**: - 序列化后的数据体积较大(包含字段名字符串) - 字段名会暴露实现细节(在网络安全敏感的场合需要注意) ### 2. MSGPACK_DEFINE_ARRAY - **原理**:将结构体序列化为有序数组,顺序由宏中的字段顺序决定 - **优点**: - 数据体积小(比 map 格式平均减少 30%-40%) - 序列化/反序列化速度快(不需要处理键名) - 适用于高性能场景和对带宽敏感的场合 - **缺点**: - 对结构变更极其敏感(字段顺序改变即不兼容) - 调试困难(纯数值数组,没有字段标识) - 无法处理可选字段 ## 何时使用哪种格式 | 场景 | 推荐格式 | |------|----------| | 内部服务间通信(带宽敏感) | MSGPACK_DEFINE_ARRAY | | 跨版本API接口 | MSGPACK_DEFINE_MAP | | 调试日志/开发环境 | MSGPACK_DEFINE_MAP | | 配置文件存储 | MSGPACK_DEFINE_MAP | | 高性能实时处理 | MSGPACK_DEFINE_ARRAY | | 跨语言/跨平台通信 | MSGPACK_DEFINE_MAP | | 大量重复结构的数据传输 | MSGPACK_DEFINE_ARRAY | ## 兼容性处理技巧 ```cpp // 版本控制技巧 struct SysEventInfo { int version = 1; // 添加版本号字段 int mainData; std::optional<float> newField; // 用可选类型兼容新字段 MSGPACK_DEFINE_MAP(version, mainData, newField); }; // 反序列化时检查版本 void unpack_event(const msgpack::object& obj) { if (obj.via.map.ptr[0].key.as<std::string>() == "version") { int ver = obj.via.map.ptr[0].val.as<int>(); // 根据版本号调整处理逻辑 } } ``` ## 高级技巧:混合使用两种格式 ```cpp struct HybridData { // 稳定字段用数组提高性能 int id; double value; // 易变字段用map增强兼容性 std::map<std::string, std::string> attributes; // 组合序列化方法 template <typename Packer> void msgpack_pack(Packer& pk) const { // 固定部分用数组 pk.pack_array(2); pk.pack(id); pk.pack(value); // 扩展字段用map pk.pack(attributes); } }; ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值