mpack简明教程

文章目录

    • 摘要
    • MessagePack简介
    • MPACK的简单使用
    • 在定长的buffer存储不定长的数据
    • 读取截断的数据

摘要

本文先简单介绍MessagePack的基本概念。

然后,介绍一个MessagePack C API - MPack的通常使用。

接着尝试对MPack截断数据的读取。

注:本文完整代码见仓库。


MessagePack简介

如果你使用过C/C++的json库,那么上手MessagePack是比较容易的。关于C/C++ Json库的使用可见:C++ JSON库的一般使用方法-CSDN博客。

这里,我先说下结论,对于用户层面而言,MessagePack相对于json节省空间,但牺牲了可读性(对于人类而言)。(更多区别见:Why Not Just Use JSON?)

下面我们来看两个示例,了解下MessagPack是如何压缩json内容。这个压缩过程,遵循MessagePack specification(MessagePack规范)

第一个示例,来自MessagePack首页。它将27个字节的JSON内容,压缩到18个字节。

在这里插入图片描述

我们根据MessagePack规范来看下上面是如何压缩的。

# 82的含义
## 对于元素个数不超过15个的map存储,遵循如下格式
## 82 == 1000 0010 表示该结构为map,有两个元素
+--------+~~~~~~~~~~~~~~~~~+
|1000XXXX|   N*2 objects   |
+--------+~~~~~~~~~~~~~~~~~+

# A7的含义
## 对于长度不超过31的固定长度字符串存储,遵循如下格式
## A7 == 1010 0111 表示该结构为字符串,有7个字符
+--------+========+
|101XXXXX|  data  |
+--------+========+

# C3含义
## false:
+--------+
|  0xc2  |
+--------+
## true:
+--------+
|  0xc3  |
+--------+

# A6的含义--略,查询过程同上面A7

# 00的含义
## 可以使用7-bit存储的正整数,遵循如下格式
## 00 = 0000 0000
+--------+
|0XXXXXXX|
+--------+

我们再来看一个示例,练习下。这个示例来自: mpack/docs/expect.md at develop · ludocode/mpack

[
  "hello",
  "world!",
  [
    1,
    2,
    3,
    4
  ]
]

上面这个json,使用MessagPack编码如下。

93                     # an array containing three elements
  a5 68 65 6c 6c 6f    # "hello"
  a6 77 6f 72 6c 64 21 # "world!"
  94                   # an array containing four elements
    01                 # 1
    02                 # 2
    03                 # 3
    04                 # 4

MPACK的简单使用

了解了MessagePack的概念之后,我们看下它的C api的使用。MPACK是MessagePack C语言实现的API之一。mpack的stars没有msgpack-c多。但是mpack对libc的版本没有要求。下面是对mpack的使用教程。

JSON的使用包含两个部分:将json数据写入内存/文件;从已有的json数据中读取内容。

mapck在使用结构上,和json类似,分为写和读。MPack api是mpack的api文档,接口使用的详细介绍见文档。写数据的部分调用Write API。如果没有内存限制,读取的时候使用Node API。如果有内存限制,则使用Reader API+Expect API。(这里说的内存限制是指,程序运行的内存限制,而不是数据存储的内存限制)

这个参考示例不错,在开始之前,可以一读:一个C语言MessagePack库:mpack

下面我们开始写demo。写一个最简单,也是最常用的demo:存储数据的内存无限制时,使用mpack进行数据的写入和读取。示例参考自官方的README:ludocode/mpack: MPack - A C encoder/decoder for the MessagePack serialization format / msgpack.org[C]. 这里对这些API进行简单的介绍,详细介绍见官方文档。

  • mpack_writer_init_growable(mpack_writer_t* writer, char** target_data, size_t* target_size)使用一个会增长的buffer。
  • 在调用mpack_writer_destroy后,将上面target_data指向写入数据的地址。
  • 最后必须调用MPACK_FREE()释放申请的数据。
  • 使用mpack_build_map开始构建一个map,我们此时不知道map中元素的个数。如果知道元素个数,使用mpack_start_map()替代。
  • 使用mpack_build_map后,后面必须跟偶数个数的元素,在结束的时候调用 mpack_complete_map()
  • 使用Node API读取数据。
#include "mpack.h"
#include <stdio.h>

/*
{
    "name": "3-1",
    "number": 56,
    "students": [
        {
            "name": "zhangsan",
            "score": 76.8
        },
        {
            "name": "lisi",
            "score": 77
        }
    ]
}
*/

mpack_error_t class_information_serialize(char **data, size_t *size) {
  mpack_writer_t writer;
  mpack_writer_init_growable(&writer, data, size);

  mpack_build_map(&writer);

  mpack_write_cstr(&writer, "name");
  mpack_write_cstr(&writer, "3-1");

  mpack_write_cstr(&writer, "number");
  mpack_write_u8(&writer, 56);

  mpack_write_cstr(&writer, "students");
  mpack_build_array(&writer);

  mpack_start_map(&writer, 2);
  mpack_write_cstr(&writer, "name");
  mpack_write_cstr(&writer, "zhangsan");
  mpack_write_cstr(&writer, "score");
  mpack_write_float(&writer, 76.8);
  mpack_finish_map(&writer);

  mpack_start_map(&writer, 2);
  mpack_write_cstr(&writer, "name");
  mpack_write_cstr(&writer, "lisi");
  mpack_write_cstr(&writer, "score");
  mpack_write_float(&writer, 77);
  mpack_finish_map(&writer);

  mpack_complete_array(&writer);

  mpack_complete_map(&writer);

  mpack_error_t ret = mpack_writer_destroy(&writer);

  return ret;
}

mpack_error_t class_information_deserialize(const char *data, size_t length) {
  mpack_tree_t tree;
  mpack_tree_init_data(&tree, data, length);
  mpack_tree_parse(&tree);

  mpack_node_t root = mpack_tree_root(&tree);
  const char *name = mpack_node_str(mpack_node_map_cstr(root, "name"));
  size_t name_len = mpack_node_strlen(mpack_node_map_cstr(root, "name"));
  uint8_t number = mpack_node_u8(mpack_node_map_cstr(root, "number"));
  printf("name:%.*s\n", name_len, name);
  printf("number:%u\n", number);

  printf("students:\n");
  mpack_node_t students = mpack_node_map_cstr(root, "students");
  size_t student_num = mpack_node_array_length(students);
  for (unsigned int i = 0; i < student_num; i++) {
    mpack_node_t student = mpack_node_array_at(students, i);
    const char *name = mpack_node_str(mpack_node_map_cstr(student, "name"));
    size_t name_len = mpack_node_strlen(mpack_node_map_cstr(student, "name"));
    float score = mpack_node_float(mpack_node_map_cstr(student, "score"));
    printf("  name:%.*s\n", name_len, name);
    printf("  score:%.2f\n", score);
  }

  mpack_error_t ret = mpack_tree_destroy(&tree);
  return ret;
}

int main(int argc, char *argv[]) {
  char *data = NULL;
  size_t size = 0;
  class_information_serialize(&data, &size);
  class_information_deserialize(data, size);
  MPACK_FREE(data);
}

程序输出如下。

name:3-1
number:56
students:
  name:zhangsan
  score:76.80
  name:lisi
  score:77.00

在定长的buffer存储不定长的数据

工作的时候,会想使用比较奇怪的调用。首先,所有的数据都要存储在一个定长的buffer里面。因为这个buffer是从一个内存池中取出的,所以它的长度是定长的。但是,往里面写入数据的时候,会写多次,长度不一定。

修改上面的示例:现在要写入不定个数的student到数组中;允许截断;

截断的时候发生了什么?截断后还能否读取?

遇到截断,writer会设置错误的标记位。正常编码的时候,这个writer数据不应该在后续进行读取。因为它已经被标记为错误。

但是,总有些头铁的需求,想用发生截断时,已经写入内存的数据。这个从API文档里面是看不出来的,要看mpack的源码。

我先说结论:

  • 从前往后,不断将build中的内容,复制写入buffer。写之前检查空间是否足够,不够则停止写入,并标记错误。
  • 使用 node api 无法读取。因为数据被截断,不合法。

大体结构图如下。

在这里插入图片描述

示例代码如下。

#include "mpack.h"
#include <stdio.h>

/*
{
    "name": "3-1",
    "number": 56,
    "students": [
        {
            "name": "zhangsan",
            "score": 76.8
        },
        {
            "name": "lisi",
            "score": 77
        }
    ]
}
*/

mpack_error_t class_information_serialize(char *data, size_t *size) {
  mpack_writer_t writer;
  mpack_writer_init(&writer, data, *size);

  mpack_build_map(&writer);

  mpack_write_cstr(&writer, "name");
  mpack_write_cstr(&writer, "3-1");

  mpack_write_cstr(&writer, "number");
  mpack_write_u8(&writer, 56);

  mpack_write_cstr(&writer, "students");

  mpack_build_array(&writer);

  for (unsigned int i = 0; i < 56; i++) {
    mpack_start_map(&writer, 2);
    mpack_write_cstr(&writer, "name");
    mpack_write_cstr(&writer, "zhangsan");
    mpack_write_cstr(&writer, "score");
    mpack_write_float(&writer, 76.8);

    if (mpack_writer_error(&writer) != mpack_ok) {
      printf("error_%u: %d\n", i, mpack_writer_error(&writer));
    }
    mpack_finish_map(&writer);
  }

  mpack_complete_array(&writer);

  mpack_complete_map(&writer);

  if (mpack_writer_error(&writer) != mpack_ok) {
    printf("after write all students error: %d\n", mpack_writer_error(&writer));
  }

  *size = mpack_writer_buffer_used(&writer);
  mpack_error_t ret = mpack_writer_destroy(&writer);

  return ret;
}

mpack_error_t class_information_deserialize(const char *data, size_t length) {
  mpack_tree_t tree;
  mpack_tree_init_data(&tree, data, length);
  mpack_tree_parse(&tree);

  if (mpack_tree_error(&tree) != mpack_ok) {
    printf("parse tree error: %d\n", mpack_tree_error(&tree));
  }

  mpack_node_t root = mpack_tree_root(&tree);
  const char *name = mpack_node_str(mpack_node_map_cstr(root, "name"));
  size_t name_len = mpack_node_strlen(mpack_node_map_cstr(root, "name"));
  uint8_t number = mpack_node_u8(mpack_node_map_cstr(root, "number"));
  printf("name:%.*s\n", name_len, name);
  printf("number:%u\n", number);

  printf("students:\n");
  mpack_node_t students = mpack_node_map_cstr(root, "students");
  size_t student_num = mpack_node_array_length(students);
  for (unsigned int i = 0; i < student_num; i++) {
    mpack_node_t student = mpack_node_array_at(students, i);
    const char *name = mpack_node_str(mpack_node_map_cstr(student, "name"));
    size_t name_len = mpack_node_strlen(mpack_node_map_cstr(student, "name"));
    float score = mpack_node_float(mpack_node_map_cstr(student, "score"));
    printf("  name:%.*s\n", name_len, name);
    printf("  score:%.2f\n", score);
  }

  mpack_error_t ret = mpack_tree_destroy(&tree);
  return ret;
}

int main(int argc, char *argv[]) {
#define DATA_BUFFER_SIZE 35
  char data[DATA_BUFFER_SIZE] = {0};
  size_t size = DATA_BUFFER_SIZE;
  class_information_serialize(data, &size);
  class_information_deserialize(data, size);
}

输出如下。

after write all students error: 6
parse tree error: 3
name:
number:0
students:

读取截断的数据

既然使用node api解析数据会失败。那不要解析,顺序读取,一直读取到异常。

参考自:Using the Expect API。代码如下。

#include "mpack.h"
#include <stdio.h>

/*
[
  {
    "name": "zhangsan",
    "score": 76.8
  },
  {
    "name": "lisi",
    "score": 77
  }
  ...
]

*/

mpack_error_t class_information_serialize(char *data, size_t *size) {
  mpack_writer_t writer;
  mpack_writer_init(&writer, data, *size);

  mpack_build_array(&writer);

  for (unsigned int i = 0; i < 56; i++) {
    mpack_build_map(&writer);
    mpack_write_cstr(&writer, "name");
    mpack_write_cstr(&writer, "zhangsan");
    mpack_write_cstr(&writer, "score");
    mpack_write_float(&writer, 76.8);
    mpack_complete_map(&writer);
  }

  mpack_complete_array(&writer);

  if (mpack_writer_error(&writer) != mpack_ok) {
    printf("after write all students error: %d\n", mpack_writer_error(&writer));
  }

  *size = mpack_writer_buffer_used(&writer);
  mpack_error_t ret = mpack_writer_destroy(&writer);

  return ret;
}

mpack_error_t class_information_deserialize(const char *data, size_t length) {
  mpack_reader_t reader;
  mpack_reader_init_data(&reader, data, length);

  uint32_t students_num = mpack_expect_array(&reader);
  printf("students num: %u\n", students_num);
  for (unsigned int i = 0; i < students_num; i++) {
    uint32_t elem_cnt = mpack_expect_map(&reader);
    mpack_expect_cstr_match(&reader, "name");
    char name[20];
    size_t name_len = mpack_expect_str_buf(&reader, name, 20);
    mpack_expect_cstr_match(&reader, "score");
    float score = mpack_expect_float(&reader);

    if (mpack_reader_error(&reader) != mpack_ok) {
      break;
    }

    printf("name:%.*s\n", name_len, name);
    printf("score:%.2f\n", score);
    mpack_done_map(&reader);
  }

  return mpack_ok;
}

int main(int argc, char *argv[]) {
#define DATA_BUFFER_SIZE 100
  char data[DATA_BUFFER_SIZE] = {0};
  size_t size = DATA_BUFFER_SIZE;
  class_information_serialize(data, &size);
  class_information_deserialize(data, size);
}

输出如下。

after write all students error: 6
students num: 56
name:zhangsan
score:76.80
name:zhangsan
score:76.80
name:zhangsan
score:76.80

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/392351.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【自然语言处理】实验3,文本情感分析

清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 代码和报告均为本人自己实现&#xff08;实验满分&#xff09;&#xff0c;只展示主要任务实验结果&#xff0c;如果需要详细的实验报告或者代码可以私聊博主 有任何疑问或者问题&#xff0c;也欢…

会声会影2024新功能及剪辑视频步骤教程

会声会影2024的新功能主要包括&#xff1a; 全新的标题动态与特效&#xff1a;用户可以为文字标题指定进入、中场和退出的不同动态效果&#xff0c;比如闪现进入、中场弹跳和淡出退出等&#xff0c;让文字标题更具动感。此外&#xff0c;还新增了多个标题特效&#xff0c;包括…

Unity中关于ScrollRect组件完整解决方案(ScrollRect中元素自动排版+ScrollRect中元素自动定位到Viewport可见范围内)

一、元素自动排版功能 1、首先要往我们的unity项目中导入两个脚本文件&#xff0c;脚本文件名称分别是UIScrollEventListener和CZScrollRect&#xff0c;这两个脚本文件代码如下所示。 1-1、介绍UIScrollEventListener脚本写法。 using System.Collections; using System.Co…

代码随想录day24--回溯的应用3

LeetCode93.修复IP地址 题目描述&#xff1a; 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是…

安装luajit及使用python运行lua脚本

使用Python运行lua脚本前&#xff0c;需要先安装LuaJIT&#xff0c;LuaJIT的官网是下载 (luajit.org) 目前已不再使用.exe文件的下载方式&#xff0c;需要使用Git从公共仓库下载源码&#xff0c;git命令为&#xff1a; $ git clone https://luajit.org/git/luajit.git 下载后…

Open CASCADE学习|布尔运算

目录 1、加法&#xff1a;BRepAlgoAPI_Fuse 2、减法&#xff1a;BRepAlgoAPI_Cut 3、交集&#xff1a;BRepAlgoAPI_Common 4、交线&#xff1a;BRepAlgoAPI_Section 1、加法&#xff1a;BRepAlgoAPI_Fuse #include <gp_Pnt.hxx>#include <BRepPrimAPI_MakeBox.hxx…

云计算基础 -NUMA

UMA UMA中文翻译叫&#xff1a;一致性内存访问 多个CPU通过同一根前端总线&#xff08;FSB&#xff09;来访问内存&#xff08;所有的内存访问都需要通过北桥芯片来完成&#xff09;&#xff0c;若多个CPU访问内存的不同内存单元还是相同内存单元&#xff0c;同一时刻&#x…

Vuex核心知识整理

目录 1 搭建vuex环境 2 求和案例 3 getters 配置项 4 mapState 和 mapGetters 5 mapMutations 和 mapActions 6 Vuex 模块化 1 搭建vuex环境 vuex工作原理图&#xff08;摘自官网&#xff09; 什么时候使用Vuex&#xff1a; 1.当多个组件依赖于统一状态 2.来自不同组件…

2.15日学习打卡----初学Zookeeper(二)

2.15日学习打卡 目录: 2.15日学习打卡一. Zookeeper部署运行伪集群安装集群安装服务管理 二. Zookeeper系统模型数据模型节点特性客户端命令行节点数据信息Watcher监听机制权限控制 ACL 三. 原生api操作Zookeeper四. zkclient库操作Zookeeper五. Apache Curator操作Zookeeper六…

不同的AI修改同一篇文章标题

提问AI 我写了一篇文章&#xff0c;请帮我把标题重新改一下&#xff1a;“比较不同AI分析同一个错误代码及给出解决方案的能力&#xff08;结果出我意料&#xff09;” 这篇文章的原地址为&#xff1a;https://blog.csdn.net/snans/article/details/136132211 答案对比结果&am…

RegExp正则表达式左限定右限定左右限定,预查询,预查寻,断言 : (?<= , (?= , (?<! , (?!

RegExp正则表达式左限定右限定左右限定,预查询,预查寻,断言 : (?< , (? , (?<! , (?! 有好多种称呼 (?< , (? , (?<! , (?! 有好多种称呼 , 我称为: 左限定, 右限定, 左否定, 右否定 (?<左限定)    (?右限定)(?<!左否定)    (?!右限定) 再…

Linux|centos7下的编译|ffmpeg的二进制安装

Windows版本的ffmpeg&#xff1a; ###注意&#xff0c;高版本可能必须要windows10以及以上才支持&#xff0c;win7估计是用不了的 下载地址&#xff1a;Builds - CODEX FFMPEG gyan.dev 或者这个下载地址&#xff1a;https://github.com/BtbN/FFmpeg-Builds/releases 这两个…

C++面试宝典第28题:寻找丢失的数字

题目 给定一个包含n个整数的数组nums,其中nums[i]在区间[1, n]内。请找出所有在[1, n]范围内,但没有出现在nums中的数字,并以数组的形式返回结果。 示例1: 输入:nums = [4, 3, 2, 7, 8, 2, 3, 1] 输出:[5, 6] 示例2: 输入:nums = [1, 1] 输出:[2] 解析 初看这道题,…

基于飞腾ARM+FPGA国产化计算模块联合解决方案

联合解决方案概述 随着特殊领域电子信息系统对自主创新需求的日益提升&#xff0c;需不断开展国产抗恶劣环境计算整机及模块产 品的研制和升级。特殊领域电子信息系统的自主创新&#xff0c;是指依靠自身技术手段和安全机制&#xff0c;实现信息系统从硬 件到软件的自主研发…

阿里云香港服务器详解_CN2线路测试_BGP多线精品测试

阿里云香港服务器中国香港数据中心网络线路类型BGP多线精品&#xff0c;中国电信CN2高速网络高质量、大规格BGP带宽&#xff0c;运营商精品公网直连中国内地&#xff0c;时延更低&#xff0c;优化海外回中国内地流量的公网线路&#xff0c;可以提高国际业务访问质量。阿里云服务…

【python】网络爬虫与信息提取--正则表达式

一、正则表达式 正则表达式是用来简洁表达一组字符串的表达式。是通用的字符串表达框架&#xff0c;简洁表达一组字符串的表达式&#xff0c;针对字符串表达“简洁”和“特征”思想的工具&#xff0c;判断某字符串的特征归属。 用处&#xff1a;表达文本类型的特征&#xff1b;…

【JavaEE】_HTTP请求报头header

目录 1. Host 2. Content-Length与Content-Type 2.1 Content-Length 2.2 Content-Type 3. User-Agent&#xff08;UA&#xff09; 4. Referer 5. Cookie header的整体格式是“键值对”结构&#xff0c;一行是一个键值对&#xff0c;这些键值对都是HTTP定义好的、有特殊含…

【Leetcode刷题笔记】27. 移除元素

原题链接 Leetcode 27. 移除元素 题目 给你一个数组 nums 和一个值 val&#xff0c;你需要原地移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并原地修改输入数组。元素的顺序可以改变。…

算法练习-赎金信(思路+流程图+代码)

难度参考 难度&#xff1a;中等 分类&#xff1a;哈希表 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。且所在课程未提供测试平台&#xff0c;故实现代码主要为自行测试的那种&#xff0c;以下内容均为个人笔记&#xff0c;旨…

C#,整数转为短字符串(Short string)的加解密算法与源代码

1 整数转为短字符串的应用 网站生成的动态 URL 往往以内容序列号id为标识与参数&#xff0c;比如&#xff1a; http://www.jerry.com/tom.aspx?id1 使用 Web Rewrite&#xff0c;可以实现网页静态化&#xff0c;称为&#xff1a; http://www.jerry.com/content/1.html 对…