LiteByte 二进制数据交换格式
转载请标明原文地址:https://segmentfault.com/a/1190000021329368
一、简介
LiteByte是一种轻量级的二进制数据交换格式。
体积小巧、简单易用是设计目标,主要用于解决前后台数据传输的问题。
版本:0.13.2
作者:冰封百度(ZhangYu)
设计的灵感来源于C# struct内存对齐后的紧凑格式。
Gitee:https://gitee.com/zhangyu800/litebyte
1.特点
1.紧凑的二进制数据格式,支持变长整型,数据量小。
2.自定义类型文件,使用方便。
2.实现思路
把一个对象分为两个部分:结构和值。
结构用文本定义,越方便越好。
值用于网络传输,越小越好。
// 比如像这样的对象 拆分成结构和值两部分
public class User {
public int id = 1001;
public string name = "冰封百度";
}
// ↙ ↘
// 结构 // 值
public class User {
public int id; // 1001 = [0x02, 0xE9, 0x03]
public string name; // "冰封百度" = [0x00, 0x05, 0xB0, 0x51, 0x01, 0x5C, 0x7E, 0x76, 0xA6, 0x5E]
}
// 对象的值就是:[0x02, 0xE9, 0x03, 0x05, 0xB0, 0x51, 0x01, 0x5C, 0x7E, 0x76, 0xA6, 0x5E]
前后台依赖相同的结构体文本,转换时把对象的值拆出来传输,解析时把值还原成对象。
3.支持的数据类型
一、基本数据类型(27种)
1.整数类型:
(01)bit1(bool); 1位定长无符号整数 取值范围:[0 = false、1 = true]
(02)char: 16位定长无符号整数 取值范围:[0 ~ 65535]
(03)int8(sbyte): 8位定长有符号整数 取值范围:[-128 ~ 127]
(04)int16: 16位定长有符号整数 取值范围:[-32768 ~ 3276]
(05)int32: 32位定长有符号整数 取值范围:[-2147483648 ~ 2147483647]
(06)int64: 64位定长有符号整数 取值范围:[-9223372036854775808 ~ 9223372036854775807]
(07)uint8(byte): 8位定长无符号整数 取值范围:[0 ~ 255]
(08)uint16: 16位定长无符号整数 取值范围:[0 ~ 65535]
(09)uint32: 32位定长无符号整数 取值范围:[0 ~ 4294967295]
(10)uint64: 64位定长无符号整数 取值范围:[0 ~ 18446744073709551615]
(11)vint16(short): 1+(8~16)位变长有符号整数 取值范围:同int16
(12)vint32(int): 2+(8~32)位变长有符号整数 取值范围:同int32
(13)vint64(long): 3+(8~64)位变长有符号整数 取值范围:同int64
(14)vuint16(ushort):1+(8~16)位变长无符号整数 取值范围:同uint16
(15)vuint32(uint): 2+(8~32)位变长无符号整数 取值范围:同uint32
(16)vuint64(ulong): 3+(8~64)位变长无符号整数 取值范围:同uint64
2.浮点数:
(17)float8: 8位定长浮点数 取值范围:[0/255 ~ 255/255]
(18)float16(half): 16位定长浮点数 取值范围:[-65504 ~ 65504]
(19)float32(float): 32位定长浮点数 取值范围:[-3.40282347E+38F ~ 3.40282347E+38F]
(20)float64(double):64位定长浮点数 取值范围:[-1.7976931348623157E+308 ~ 1.7976931348623157E+308]
(21)decimal: vuint32 + (4 x n)位变长浮点数 取值范围:[-a.b ~ a.b](decimal转为字符串)
3.字符串:
(22)ascii: vuint32 x 8位定长字符串
(23)utf8: vuint32 x (8~32位) 变长字符串
(24)utf16: vuint32 x (16~32位) 变长字符串
(25)utf32: vuint32 x 32位 定长字符串(with BOM)
(26)vunicode(string):vuint32 x vuint32 变长字符串
4.日期时间
(27)DateTime: vuint64(8~64)位变长整数(转为Java timestamp毫秒)
——————————————————————————————————————————————————————————————————
二、特殊数据类型(3种)
1.数组 Array(int[]、long[]、string[] 等)
2.列表 List<T>(List<int>、List<long>、List<string> 等)
3.哈希表 Dictionary<TKey, TValue>(Dictionary<int, int>、Dictionary<string, string> 等)
——————————————————————————————————————————————————————————————————
三、自定义数据类型(有默认构造函数的 非泛型)
1.class类(默认值为null):
public class UserVO {
public int id;
public string name;
}
2.struct结构体(有默认值 不为null):
public struct Vector3VO {
public float x;
public float y;
public float z;
}
4.不支持的数据类型
1.无默认构造函数的自定义类型(需要传参数初始化的自定义类型)
2.带泛型的自定义类型(初始化时需要传泛型的)
3.循环引用的类型(比如LinkedListNode 有Previous和Next引用会导致对象被循环引用 无法初始化)
二、使用方法
1.创建自定义类型文本文件(ClassTestVO.txt)。
2.创建自定义类(ClassTestVO.cs)
3.使用LBUtil.LoadClasses(["class1", "class2"])加载自定义类型文件内容。
4.使用LBUtil.Serialize(obj, "ClassTestVO") 把对象序列化成二进制数据。
5.使用LBUtil.Deserilize
1.创建自定义类型文本
创建一个自定义类型文件 ClassTestVO.txt
内容如下:
/// <summary> 自定义类型测试 </summary>
public class ClassTestVO {
public bool boolValue;
public char charValue;
public byte byteValue;
public sbyte sbyteValue;
public short shortValue;
public ushort ushortValue;
public int intValue;
public uint uintValue;
public long longValue;
public ulong ulongValue;
public float floatValue;
public double doubleValue;
public decimal decimalValue;
public DateTime dateTime;
public string stringValue;
public int[] array;
public List<int> list;
public Dictionary<string, string> map;
public IDCard classObject;
public Position structObject;
public EmptyVO emptyObject;
}
/// <summary> 身份证信息 </summary>
public class IDCard {
public string id;
public string name;
public int age;
public int gender;
public string address;
}
/// <summary> 位置信息 </summary>
public struct Position {
public float x;
public float y;
public float z;
}
/// <summary> 没任何成员的对象 </summary>
public class EmptyVO {
}
2.创建自定义类
创建 ClassTestVO.txt
对应的 ClassTestVO.cs
内容如下:
namespace LiteByte.Test {
/// <summary>
/// <para>Class测试</para>
/// 字段和属性(get; set;)都支持. null对象也支持
/// </summary>
public class ClassTestVO {
// Field和Property写法都支持 class类型可以为null struct类型不支持为null
// public string name;
// public string name { get; set; }
public bool boolValue;
public char charValue;
public byte byteValue;
public sbyte sbyteValue;
public short shortValue;
public ushort ushortValue;
public int intValue;
public uint uintValue;
public long longValue;
public ulong ulongValue;
public float floatValue;
public double doubleValue;
public decimal decimalValue;
public DateTime dateTime;
public string stringValue;
public int[] array;
public List<int> list;
public Dictionary<string, string> map;
public IDCard classObject;
public Position structObject;
public EmptyVO emptyObject;
}
/// <summary> 身份证信息 </summary>
public class IDCard {
public string id;
public string name;
public int age;
public int gender;
public string address;
public IDCard() { }
public IDCard(string id, string name, int age, int gender, string address) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
this.address = address;
}
public override string ToString() {
string _id = id == null ? "null" : id;
string _name = name == null ? "null" : name;
string _address = address == null ? "null" : address;
return $"{{{_id}, {_name}, {age}, {gender}, {_address}}}";
}
}
/// <summary> 位置信息 </summary>
public struct Position {
public float x;
public float y;
public float z;
public Position() { }
public Position(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
override public string ToString() {
return $"({x}, {y}, {z})";
}
}
/// <summary> 没任何成员的对象 </summary>
public class EmptyVO {
}
}
3.加载自定义类型文件
使用 LBUtil.LoadClasses(["classText1", "classText2"])
加载自定义类型文件内容
// 加载自定义类型
private static void LoadClasses() {
string root = "Resources/LBTypes/";
string[] files = { "ClassTestVO.txt" };
string[] codes = new string[files.Length];
for (int i = 0; i < files.Length; i++) {
string url = root + files[i];
string text = File.ReadAllText(url, Encoding.UTF8);
codes[i] = text;
}
LBUtil.LoadClasses(codes);
}
4.序列化对象
使用 LBUtil.Serialize(obj, "ClassTestVO")
把对象序列化成二进制数据。
private static void ClassTest() {
// Serialize
ClassTestVO vo = new ClassTestVO() {
boolValue = true,
charValue = 'a',
byteValue = byte.MaxValue,
sbyteValue = sbyte.MinValue,
shortValue = short.MinValue,
ushortValue = ushort.MaxValue,
intValue = 10086,
uintValue = 68001,
longValue = long.MinValue,
ulongValue = ulong.MaxValue,
floatValue = -123.456f,
doubleValue = 12345.1234567890,
decimalValue = -1234567890.0123456789m,
dateTime = DateTime.Now,
stringValue = "asdf1234",
array = new int[] { 1, 2, 3, 4, 5 },
list = new List<int>() { 10, 11, 12, 13, 14 },
map = new Dictionary<string, string>() { { "a", "ABC" }, { "b", "BCD" }, { "c", "CDE" } },
classObject = new IDCard("123456YYYYMMDD123X", "张三", 18, 1, "地球村"),
structObject = new Position(1.1f, 1.2f, 1.3f),
emptyObject = new EmptyVO()
};
byte[] bytes = LBUtil.Serialize(vo, "ClassTestVO");
// Deserialize
ClassTestVO _vo = LBUtil.Deserialize<ClassTestVO>(bytes, "ClassTestVO");
string json = JsonSerializer.Serialize(_vo, jsonOptions);
Trace.WriteLine($"ClassTest() LBSize:{bytes.Length} JsonSize:{json.Length} json:\n{json}");
/*
输出:
ClassTest() LBSize:165 JsonSize:617 json:
{"boolValue":true,"charValue":"a","byteValue":255,"sbyteValue":-128,"shortValue":-32768,"ushortValue":65535,"intValue":10086,"uintValue":68001,"longValue":-9223372036854775808,"ulongValue":18446744073709551615,"floatValue":-123.456,"doubleValue":12345.123456789,"decimalValue":-1234567890.0123456789,"dateTime":"2025-02-27T14:38:22.217","stringValue":"asdf1234","array":[1,2,3,4,5],"list":[10,11,12,13,14],"map":{"a":"ABC","b":"BCD","c":"CDE"},"classObject":{"id":"123456YYYYMMDD123X","name":"\u5F20\u4E09","age":18,"gender":1,"address":"\u5730\u7403\u6751"},"structObject":{"x":1.1,"y":1.2,"z":1.3},"emptyObject":{}}
*/
}
5.反序列化对象
使用 LBUtil.Deserilize<ClassTestVO>(bytes, "ClassTestVO")
把二进制数据反序列化成对象
// Deserialize
ClassTestVO _vo = LBUtil.Deserialize<ClassTestVO>(bytes, "ClassTestVO");
三、更新日志:
0.7.0: 用IL.Emit代替反射,大幅度提升get和set性能。
0.8.0: 添加编译多个类的功能,一个配置文件可以写多个类了,可以一次性识别。
0.8.1: 删除LBObject类,因为IL优化后get和set的性能很好,不再需要LBObject了。
0.8.2: 优化LBParser中 从tokens中删除访问修饰符的代码,不再生成新的list。
0.8.3: 优化反射工具类 ReflectionUtil 创建实例方法中的参数。
0.9.0: 优化反射工具(增加IL.Emit工具、代理工具、指针工具 用于优化反射) 增加反射工具对IL2CPP模式的兼容性。
0.10.0: 重构LBReader读取类 增加对List的支持 优化LBConverter类。
0.10.1: 修复BUG:1.LBWriter WriteVarLength时没有正确扩容的BUG 2.LBConverter没有正确抛出错误信息的BUG。
0.11.0: 重构LBWriter写入类 增加对List的支持 优化LBConverter类。
0.11.1: 优化LBConverter.ToObject() 和 ToBytes()。
0.12.0: 精简代码 增加LBWriter和LBReader的代码重用性。
0.13.0: 重写LBLexer、LBParser、LBWriter、LBReader、LBConverter 全面改进用法 提升便捷性
0.13.1: 重写LBReflectionUtil、LBMemberWrapper 用指针替代Field的反射、用Delegate替代Property的反射 提升性能
0.13.2: 增加对decimal、DateTime类型的支持。重写WriteBits()、ReadBits()方法, 添加LBTest测试类。
四、其他说明:
由于能力有限,暂时只实现了C#(.Net)版本
其他语言后续有时间再写,虽然造了个轮子,不过感觉造轮子的过程中收获远大于付出,挺开心的。
建了个群,有需求的可加。
QQ群:715800513
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端