摘要
本文先簡(jiǎn)單介紹MessagePack的基本概念。
然后,介紹一個(gè)MessagePack C API - MPack的通常使用。
接著嘗試對(duì)MPack截?cái)鄶?shù)據(jù)的讀取。
注:本文完整代碼見倉(cāng)庫(kù)。
MessagePack簡(jiǎn)介
如果你使用過C/C++的json庫(kù),那么上手MessagePack是比較容易的。關(guān)于C/C++ Json庫(kù)的使用可見:C++ JSON庫(kù)的一般使用方法-CSDN博客。
這里,我先說下結(jié)論,對(duì)于用戶層面而言,MessagePack相對(duì)于json節(jié)省空間,但犧牲了可讀性(對(duì)于人類而言)。(更多區(qū)別見:Why Not Just Use JSON?)
下面我們來看兩個(gè)示例,了解下MessagPack是如何壓縮json內(nèi)容。這個(gè)壓縮過程,遵循MessagePack specification(MessagePack規(guī)范)
第一個(gè)示例,來自MessagePack首頁(yè)。它將27個(gè)字節(jié)的JSON內(nèi)容,壓縮到18個(gè)字節(jié)。
我們根據(jù)MessagePack規(guī)范來看下上面是如何壓縮的。
# 82的含義
## 對(duì)于元素個(gè)數(shù)不超過15個(gè)的map存儲(chǔ),遵循如下格式
## 82 == 1000 0010 表示該結(jié)構(gòu)為map,有兩個(gè)元素
+--------+~~~~~~~~~~~~~~~~~+
|1000XXXX| N*2 objects |
+--------+~~~~~~~~~~~~~~~~~+
# A7的含義
## 對(duì)于長(zhǎng)度不超過31的固定長(zhǎng)度字符串存儲(chǔ),遵循如下格式
## A7 == 1010 0111 表示該結(jié)構(gòu)為字符串,有7個(gè)字符
+--------+========+
|101XXXXX| data |
+--------+========+
# C3含義
## false:
+--------+
| 0xc2 |
+--------+
## true:
+--------+
| 0xc3 |
+--------+
# A6的含義--略,查詢過程同上面A7
# 00的含義
## 可以使用7-bit存儲(chǔ)的正整數(shù),遵循如下格式
## 00 = 0000 0000
+--------+
|0XXXXXXX|
+--------+
我們?cè)賮砜匆粋€(gè)示例,練習(xí)下。這個(gè)示例來自: mpack/docs/expect.md at develop · ludocode/mpack
[
"hello",
"world!",
[
1,
2,
3,
4
]
]
上面這個(gè)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的簡(jiǎn)單使用
了解了MessagePack的概念之后,我們看下它的C api的使用。MPACK是MessagePack C語(yǔ)言實(shí)現(xiàn)的API之一。mpack的stars沒有msgpack-c多。但是mpack對(duì)libc的版本沒有要求。下面是對(duì)mpack的使用教程。
JSON的使用包含兩個(gè)部分:將json數(shù)據(jù)寫入內(nèi)存/文件;從已有的json數(shù)據(jù)中讀取內(nèi)容。
mapck在使用結(jié)構(gòu)上,和json類似,分為寫和讀。MPack api是mpack的api文檔,接口使用的詳細(xì)介紹見文檔。寫數(shù)據(jù)的部分調(diào)用Write API
。如果沒有內(nèi)存限制,讀取的時(shí)候使用Node API
。如果有內(nèi)存限制,則使用Reader API
+Expect API
。(這里說的內(nèi)存限制是指,程序運(yùn)行的內(nèi)存限制,而不是數(shù)據(jù)存儲(chǔ)的內(nèi)存限制)
這個(gè)參考示例不錯(cuò),在開始之前,可以一讀:一個(gè)C語(yǔ)言MessagePack庫(kù):mpack
下面我們開始寫demo。寫一個(gè)最簡(jiǎn)單,也是最常用的demo:存儲(chǔ)數(shù)據(jù)的內(nèi)存無(wú)限制時(shí),使用mpack進(jìn)行數(shù)據(jù)的寫入和讀取。示例參考自官方的README:ludocode/mpack: MPack - A C encoder/decoder for the MessagePack serialization format / msgpack.org[C]. 這里對(duì)這些API進(jìn)行簡(jiǎn)單的介紹,詳細(xì)介紹見官方文檔。
-
mpack_writer_init_growable(mpack_writer_t* writer, char** target_data, size_t* target_size)
使用一個(gè)會(huì)增長(zhǎng)的buffer。 - 在調(diào)用
mpack_writer_destroy
后,將上面target_data
指向?qū)懭霐?shù)據(jù)的地址。 - 最后必須調(diào)用
MPACK_FREE()
釋放申請(qǐng)的數(shù)據(jù)。 - 使用
mpack_build_map
開始構(gòu)建一個(gè)map,我們此時(shí)不知道m(xù)ap中元素的個(gè)數(shù)。如果知道元素個(gè)數(shù),使用mpack_start_map()
替代。 - 使用
mpack_build_map
后,后面必須跟偶數(shù)個(gè)數(shù)的元素,在結(jié)束的時(shí)候調(diào)用mpack_complete_map()
。 - 使用
Node API
讀取數(shù)據(jù)。
#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
在定長(zhǎng)的buffer存儲(chǔ)不定長(zhǎng)的數(shù)據(jù)
工作的時(shí)候,會(huì)想使用比較奇怪的調(diào)用。首先,所有的數(shù)據(jù)都要存儲(chǔ)在一個(gè)定長(zhǎng)的buffer里面。因?yàn)檫@個(gè)buffer是從一個(gè)內(nèi)存池中取出的,所以它的長(zhǎng)度是定長(zhǎng)的。但是,往里面寫入數(shù)據(jù)的時(shí)候,會(huì)寫多次,長(zhǎng)度不一定。
修改上面的示例:現(xiàn)在要寫入不定個(gè)數(shù)的student到數(shù)組中;允許截?cái)啵?/p>
截?cái)嗟臅r(shí)候發(fā)生了什么?截?cái)嗪筮€能否讀???
遇到截?cái)?,writer會(huì)設(shè)置錯(cuò)誤的標(biāo)記位。正常編碼的時(shí)候,這個(gè)writer數(shù)據(jù)不應(yīng)該在后續(xù)進(jìn)行讀取。因?yàn)樗呀?jīng)被標(biāo)記為錯(cuò)誤。
但是,總有些頭鐵的需求,想用發(fā)生截?cái)鄷r(shí),已經(jīng)寫入內(nèi)存的數(shù)據(jù)。這個(gè)從API文檔里面是看不出來的,要看mpack的源碼。
我先說結(jié)論:
- 從前往后,不斷將build中的內(nèi)容,復(fù)制寫入buffer。寫之前檢查空間是否足夠,不夠則停止寫入,并標(biāo)記錯(cuò)誤。
- 使用 node api 無(wú)法讀取。因?yàn)閿?shù)據(jù)被截?cái)啵缓戏ā?/li>
大體結(jié)構(gòu)圖如下。
示例代碼如下。
#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:
讀取截?cái)嗟臄?shù)據(jù)
既然使用node api
解析數(shù)據(jù)會(huì)失敗。那不要解析,順序讀取,一直讀取到異常。
參考自:Using the Expect API。代碼如下。文章來源:http://www.zghlxwxcb.cn/news/detail-830380.html
#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);
}
輸出如下。文章來源地址http://www.zghlxwxcb.cn/news/detail-830380.html
after write all students error: 6
students num: 56
name:zhangsan
score:76.80
name:zhangsan
score:76.80
name:zhangsan
score:76.80
到了這里,關(guān)于mpack簡(jiǎn)明教程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!