tunm, 一種對標JSON的二進位制資料協議
阿新 • • 發佈:2022-05-17
Tunm
simple binary proto
一種對標JSON的二進位制資料協議
支援的資料型別
基本支援的型別 "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "varint", "float", "string", "raw", "array", "map"
各種數值型別格式說明
- u8/i8 用一個位元組進行寫入
- u16/i16/u32/i32/u64/i64 分別對應大小的資料寫入, 小端模式
- float 精度只有3位小數, 當成*1000的u32處理
- double 精度只有6位小數, 當成*1000000的u64資料
- 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為例
extern crate tunm_proto as tunm; use tunm::{Value, Buffer}; mod test_data; use std::collections::{HashMap}; fn main() { println!("welcome to tickdream rust protocol"); let mut hash_value = HashMap::<Value, Value>::new(); hash_value.insert(Value::Str("name".to_string()), Value::Str("tunm_proto".to_string())); hash_value.insert(Value::Str("tunm_proto".to_string()), Value::U16(1 as u16)); { let mut buffer = Buffer::new(); tunm::encode_proto(&mut buffer, &"cmd_test_op".to_string(), vec![Value::Map(hash_value.clone())]).unwrap(); let just_str = " [\"cmd_test_op\", [\"tunm_proto\", {\"name\": \"tunm_proto\", \"tunm_proto\": 1}]] "; println!("just json len = {}", just_str.len()); println!("buffer len == {}", buffer.data_len()); // just read field let read = tunm::decode_proto(&mut buffer).unwrap(); match read { (name, val) => { assert_eq!(name, "cmd_test_op".to_string()); assert_eq!(val[0], Value::Map(hash_value)); assert_eq!(val.len(), 1); } } } }
格式說明
資料協議分為三部分(協議名稱, 字串索引區, 資料區(預設為陣列))
如資料協議名為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長度 = 2.2M
解析JSON用時 = Ok(1.520187s)
用tunm_proto壓縮test_level4_json的長度 = 370k
壓縮JSON耗時 = Ok(31.842ms)
name = cmd_level4_full
解析buffer耗時 = Ok(22.642ms)
解析速度約為JSON的68倍, 符合預期, 大小為明文的0.16倍, 符合壓縮比