1. 程式人生 > 其它 >tunm, 一種對標JSON的二進位制資料協議

tunm, 一種對標JSON的二進位制資料協議

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}]

  1. 那麼資料將先壓縮協議名cmd_test_op, 將先寫下可變長度(varint)值為11佔用1位元組, 然後再寫入cmd_test_op的utf8的位元組數
  2. 接下來準備寫入字串索引區, 索引資料用到的字串為["tunm_proto", "name"]兩個字串, 即將寫入可變長度(varint)值為2佔用一位元組, 然後分別寫入字串tunm_proto和name兩個字串, 這樣子字串相接近有利於壓縮, 且如果有相同的字串可以更好的進行復用
  3. 接下來準備寫入資料區,
    首先判斷為一個數組, 寫入型別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倍, 符合壓縮比

相關連線

協議地址https://github.com/tickbh/TunmProto