elti
elti是什么?
一个基于c++11规范的序列化/反序列化协定与实现,与json,xml等文本协定相比,elti能够传递二进制数据;与protobuf协定相比,elti配置简略,即拿即用。
elti是相似于bson和cbor的序列化协定,相较于bson协定空间利用率不现实( https://blog.csdn.net/m0_38110132/article/details/77716792 ) ,cbor的c/c++实现又太少(没有找到宽泛应用的稳固的轮子), elti旨在提供一个在效率,接口易用性,扩展性,空间利用率等方面有一个平衡稳固的体现,elti次要用来做数据传输而不是展现,即elti不思考可读性。
github : https://github.com/chloro-pn/…
格局
Element := Key : Value
Value :=
| Map
| Array
| Data
Map := { Element }
ARRAY := { Value }
Data := [ byte0, byte1, ... byten ]
反对平台
linux, macOS
第三方库
elti应用 https://github.com/sorribas/varint.c 作为对整形数据进行变长编码的库。 应用 https://github.com/catchorg/Catch2 作为单元测试库。
Building
just run make. 配置库文件+头文件门路即可应用。
Test
采纳Catch2作为单元测试库,于test文件夹下。
BenchMark
耗时(s) | elti | protobuf | rapidjson | nlohmann/json |
---|---|---|---|---|
测试数据1 | 0.060 | 0.0056 | 0.037 | 0.070 |
测试数据2 | 0.079 | 0.026 | 0.352 | 0.770 |
测试数据3 | 0.365 | 0.235 | 3.68 | 7.225 |
具体信息见benchmark/BENCH_MARK.md
TODO
- 减少定位器定位后果到Value(Data, Map, Array)的转化操作,目前定位器对于数据的拜访能力无限,例如不反对拜访数组 长度类型
- 反对复合类型的序列化/反序列化机制,例如:
class test {
private:
classA a;
classB b;
};
seri(const test& t, std::vector<uint8\_t\>& container) {
seri(a);
seri(b);
}
目前这种操作不反对,没有对container做进一步形象。
应用
#include "elti.h"
大多数状况下导入elti.h头文件即可
elti::Map* map = elti::makeMap();
map->set("name", elti::makeData("nanpang"));
map->set("age", elti::makeData(27));
map->set("sex", elti::makeData(1));
map->set("eof", elti::makeData(false));
elti::Data* data = elti::makeData(elti::varintNum(14553));
map->set("flow_id", data);
std::string content(4096, 'a');
map->set("content", elti::makeData(content));
elti::Array* array = elti::makeArray();
for(int i= 0; i < 10; ++i) {
array->push_back(elti::makeData(i));
}
map->set("ids", array);
构建须要序列化的数据,通过elti::makeMap, elti::makeArray和elti::makeData三个API去结构元对象,并通过元对象间的组合模式构建数据的构造。 elti的构造在逻辑上和json类似,同样采取key-value的模式(这里的value能够是Map, Array或者Data),一个Map对象能够存储key-value的汇合, 通过set接口一个Array对象能够存储value的汇合,通过push_back接口。一个Data对象存储具体的数据,包含 字符串,int8_t,uint8_t,…int64_t,uint64_t,varintNum,二进制数据(std::vector<uint8_t>),bool等类型。
注:
- varintNum类型是变长编码的整形数据,用以压缩空间。
- elti目前不反对浮点数的序列化/反序列化
- 二进制数据通过std::vector<uint8_t>或者std::string传递。
- 所有通过set,push_back接口放入的对象由elti负责开释,不须要使用者开释。
elti::Root root(map);
std::string result;
root.seri(result);
通过应用Map类型对象,Array类型对象或者Data类型对象结构elti::Root对象(同样由elti负责开释此根对象),通过seri接口实现序列化。
elti::Root new_root;
size_t offset = new_root.parse(result.data());
assert(offset == result.size());
通过默认构造函数结构elti::Root对象,通过parse接口实现反序列化,该接口返回的数据为序列化数据的总长度,在没有产生谬误的状况下应该与传入的序列化数据长度相等。
auto arr = new_root["ids"];
for(int i = 0; i < arr.size(); ++i) {
std::cout << "ids index : " << i << " id : " << arr[elti::num(i)].get<int>() << std::endl;
}
std::cout << "flow id : " << new_root["flow_id"].get<elti::varintNum>().getNum() << std::endl;
std::cout << "name : " << new_root["name"].get<std::string>() << std::endl;
std::cout << "content size : " << new_root["content"].get<std::string>().size() << std::endl;
数据的拜访,Root类型重载了operator[],拜访Map对象通过operator[](const char*)接口,拜访Array对象通过operator[](num)接口和size()接口, 拜访Data对象通过.get<>()接口。 注:
- Root假如用户依照正确的类型拜访对应的对象,通过assert进行判断,如果通过接口拜访了不对应的类型,debug模式下会报错,release模式下后果未定义。
变长编码
elti在存储元信息时应用变长编码以节俭空间,用户也能够通过传递给Data类型varintNum类型对象以应用变长编码传递整形数据(目前变长编码仅反对无符号类型):
elti::Data* data = elti::makeData(elti::varintNum(14553)); //编码
std::cout << "flow id : " << new_root["flow_id"].get<elti::varintNum>().getNum() << std::endl; //解码
自定义序列化/反序列化接口
elti仅提供根本类型的序列化机制,你也能够通过实例化以下两个模板参数来定制特定类型的序列化,反序列化接口:
template<typename T>
void seri(const T& obj, std::vector<uint8_t>& container);
template<typename T>
T parse(const std::vector<uint8_t>& container);
例子:
class test {
public:
test(int a, std::string n): age(a), name(n) {
}
//just for test.
int age;
std::string name;
};
namespace elti {
template<>
void seri(const test& obj, std::vector<uint8_t>& container) {
container.resize(sizeof(obj.age) + obj.name.size());
memcpy(&container.front(), &obj.age, sizeof(obj.age));
memcpy(&container.front() + sizeof(obj.age), obj.name.data(), obj.name.size());
}
template<>
test parse(const std::vector<uint8_t>& container) {
int age;
std::string name;
memcpy(&age, &container.front(), sizeof(age));
name.append((char*)(&container.front() + sizeof(age)), container.size() - sizeof(age));
return test(age, name);
}
}
int main() {
elti::Map* obj = elti::makeMap();
//调用自定义的seri接口
obj->set("obj", elti::makeData(test(25, "nanpang")));
elti::Root root(obj);
std::string result;
root.seri(result);
elti::Root new_root;
size_t offset = new_root.parse(result.data());
assert(offset == result.length());
//调用自定义的parse接口
test t = new_root["obj"].get<test>();
std::cout << "age : " << t.age << " name : " << t.name << std::endl;
return 0;
}
定位器
//应用序列化数据指针结构一个定位器对象。
elti::PositionerRoot pst(result.data());
//应用定位器对象如同应用Root对象,然而定位器只会解析必要门路并定位数据,跳过不相干的数据。
std::string book = pst["books"][elti::num(1)].get<std::string>();
发表回复