2 releases
new 0.1.3 | Dec 12, 2024 |
---|---|
0.1.2 | Dec 11, 2024 |
#310 in Encoding
291 downloads per month
140KB
2K
SLoC
hcproto
simple binary proto 一种对标JSON的二进制数据协议, 支持JSON的所有类型的动态组合
支持的数据类型
基本支持的类型 "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "varint", "f32", "f64", "string", "raw", "array", "map"
各种数值类型格式说明
- u8/i8 用一个字节进行写入
- u16/i16/u32/i32/u64/i64 分别对应大小的数据写入, 小端模式
- f32 按IEEE754进行写入
- f64 按IEEE754进行写入
- varint 可变长的整型数据
如果是正数则*2, 如果是负数则-(x + 1) * 2, 相当于0->0, -1->1, 1->2,-2->3,2->4来做处理, 因为是小子节的数比较多, 每bit里的第一位则表示是否是最后一位, 如果10000001, 则表示还要继续往下读如果是00000001则表示这是最后一位
- str 字符串类型, 则先用varint表示str的长度, 然后再写入str的值
- str_idx 字符串索引值, 在str的arr表中的第几位, 重复的str则在同一个位置, 用varint表示
- array 数组类型, 先用varint表示array的长度, 然后再写入各个value的数值
- map map类型, 先用varint表示map的长度, 然后先写入key, 再写入value, 依次循环到结束
与protobuf差异
相对protobuf, 无需预先定义任何的数据格式, 更好的适应多变的场景, 或者客户端不好更新的情况, 拥有更好的自适应性, 简单开封即用, 和JSON一样, 在可支持的数据类型里, 可以自由的进行转换
与JSON的差异
可以把这个看做是二进制的JSON格式, 有更好的压缩率和更快的解析速度
数据使用, 以Rust为例
use algorithm::buf::Bt;
use hcproto::{Buffer, Value};
use std::time::SystemTime;
mod test_data;
use std::collections::HashMap;
fn test_level4_json() {
let mut now = SystemTime::now();
let parsed = test_data::get_json();
println!("解析JSON用时 = {:?}", now.elapsed());
now = SystemTime::now();
// println!("ok!!! parsed= {:?}", parsed);
let mut buffer = Buffer::new();
hcproto::encode_proto(&mut buffer, &"cmd_level4_full".to_string(), vec![parsed]).unwrap();
println!(
"用tunm_proto压缩test_level4_json的长度 = {}k",
buffer.remaining() / 1024
);
println!("压缩JSON耗时 = {:?}", now.elapsed());
now = SystemTime::now();
let read = hcproto::decode_proto(&mut buffer).unwrap();
println!("解析buffer耗时 = {:?}", now.elapsed());
match read {
(name, _val) => {
assert_eq!(name, "cmd_level4_full".to_string());
// println!("value === {:?}", val);
}
}
}
格式说明
数据协议分为三部分(协议名称, 字符串索引区, 数据区(默认为数组)) 如数据协议名为cmd_test_op, 数据为["tunm_proto", {"name": "tunm_proto", "tunm_proto": 1}]
- 那么数据将先压缩协议名cmd_test_op, 将先写下可变长度(varint)值为11占用1字节, 然后再写入cmd_test_op的utf8的字节数
- 接下来准备写入字符串索引区, 索引数据用到的字符串为["tunm_proto", "name"]两个字符串, 即将写入可变长度(varint)值为2占用一字节, 然后分别写入字符串tunm_proto和name两个字符串, 这样子字符串相接近有利于压缩, 且如果有相同的字符串可以更好的进行复用
- 接下来准备写入数据区, 首先判断为一个数组, 写入类型u8(TYPE_ARR=16), 写入数组长度varint(2), 准备开始写第一个数据, 字符串tunm_proto, 已转成id, 则写入类型u8(TYPE_STR_IDX=14), 查索引号0, 则写入varint(0), 第一个字段写入完毕, 接下来第二个字段是一个map数据, 写入map长度varint(2), 然后进行遍历得到key值为name, 则写入写入类型u8(TYPE_STR_IDX=14),查索引号1, 则写入varint(1), 然后开始写name对应的值tunm_proto, 写入TYPE_STR_IDX类型的0值, 则这组key写入完毕, 依此类推写入第二组数据
测试打印的结果 用完整的level-full4.json
解析JSON用时 = Ok(3.104869s)
用tunm_proto压缩test_level4_json的长度 = 370k
压缩JSON耗时 = Ok(22.2712ms)
解析buffer耗时 = Ok(16.9738ms)
普通文本的长度 = 90
解析速度约为JSON的68倍, 符合预期, 大小为明文的0.16倍, 符合压缩比
相关连接
Dependencies
~2.5–3.5MB
~58K SLoC