LiteByte 二进制数据交换格式
转载请标明原文地址:https://segmentfault.com/a/1190000021329368
一、简介

LiteByte是一种轻量级的二进制数据交换格式。
体积小巧、简单易用是设计目标,主要用于解决前后台数据传输的问题。
版本:0.14.0
作者:冰封百度(ZhangYu)
设计的灵感来源于C# struct内存对齐后的紧凑格式。
Gitee:https://gitee.com/zhangyu800/litebyte
LiteByte.dll:https://pan.baidu.com/s/1GPipodHqs1eE4ZTyqOjBmA?pwd=jix1
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.支持的数据类型
一、基本数据类型(35种)
1.比特类型:
(01)bit1(bool): 1位定长无符号整数 取值范围 [0:false、1:true]
(02)bit2: 2位定长无符号整数 取值范围 [0 ~ 3]
(03)bit3: 3位定长无符号整数 取值范围 [0 ~ 7]
(04)bit4: 4位定长无符号整数 取值范围 [0 ~ 15]
(05)bit5: 5位定长无符号整数 取值范围 [0 ~ 31]
(06)bit6: 6位定长无符号整数 取值范围 [0 ~ 63]
(07)bit7: 7位定长无符号整数 取值范围 [0 ~ 127]
(08)bit8: 8位定长无符号整数 取值范围 [0 ~ 255]
2.整数类型:
(09)bool: 8位定长无符号整数 取值范围 [0:false、1:true]
(10)char: 16位定长无符号整数 取值范围 [0 ~ 65535]
(11)int8(sbyte): 8位定长有符号整数 取值范围 [-128 ~ 127]
(12)int16(short): 16位定长有符号整数 取值范围 [-32768 ~ 3276]
(13)int32(int): 32位定长有符号整数 取值范围 [-2147483648 ~ 2147483647]
(14)int64(long): 64位定长有符号整数 取值范围 [-9223372036854775808 ~ 9223372036854775807]
(15)uint8(byte): 8位定长无符号整数 取值范围 [0 ~ 255]
(16)uint16(ushort): 16位定长无符号整数 取值范围 [0 ~ 65535]
(17)uint32(uint): 32位定长无符号整数 取值范围 [0 ~ 4294967295]
(18)uint64(ulong): 64位定长无符号整数 取值范围 [0 ~ 18446744073709551615]
3.浮点数:
(19)float16(half): 16位定长浮点数 取值范围 [-65504 ~ 65504]
(20)float32(float): 32位定长浮点数 取值范围 [-3.40282347E+38F ~ 3.40282347E+38F]
(21)float64(double): 64位定长浮点数 取值范围 [-1.7976931348623157E+308 ~ 1.7976931348623157E+308]
4.变长整数
(22)vint16(vshort): 1+(8~16)位变长有符号整数 取值范围 同int16
(23)vint32(vint): 2+(8~32)位变长有符号整数 取值范围 同int32
(24)vint64(vlong): 3+(8~64)位变长有符号整数 取值范围 同int64
(25)vuint16(vushort): 1+(8~16)位变长无符号整数 取值范围 同uint16
(26)vuint32(vuint): 2+(8~32)位变长无符号整数 取值范围 同uint32
(27)vuint64(vulong): 3+(8~64)位变长无符号整数 取值范围 同uint64
(28)decimal: vuint32 + (n x 4)位变长浮点数 取值范围 [-m.n ~ m.n](转为字符串)
(29)DateTime: vuint64 8~64位变长整数(Ticks转为Java timestamp毫秒)
5.字符串:
(30)vunicode(string): 1+vuint32 + (n x 8~31位) 变长字符串
(31)ascii(ASCII): 1+vuint32 + (n x 8位) 定长字符串
(32)utf8(UTF8): 1+vuint32 + (n x 8~32位) 变长字符串
(33)utf16(UTF16): 1+vuint32 + (n x 16~32位) 变长字符串
(34)utf32(UTF32): 1+vuint32 + (n x 32位) 定长字符串(with BOM)
(35)gbk(GBK): 1+vuint32 + (n x 8~32位) 变长字符串
——————————————————————————————————————————————————————————————————
二、特殊数据类型(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 UserEntity {
public int id;
public string name;
}
2.struct结构体(有默认值 不为null):
public struct Vector3Entity
public float x;
public float y;
public float z;
}
4.不支持的数据类型
1.无默认构造函数的自定义类型(需要传参数初始化的自定义类型)
2.带泛型的自定义类型(初始化时需要传泛型的)
3.循环引用的类型(比如LinkedListNode 有Previous和Next引用会导致对象被循环引用 无法初始化)
二、使用方法
LiteByte序列化有两种使用方式,分别为手动序列化和自动序列化。
1.手动序列化
// 以UserEntity的序列化为例
public class UserEntity {
public int id;
public string name;
}
private void ReadWriteTest1() {
// 数据对象
var entity = new UserEntity();
entity.id = 123;
entity.name = "张三";
// 序列化
var writer = new LBWriter();
writer.WriteInt32(entity.id);
writer.WriteVUnicode(entity.name);
var bytes = writer.ToBytes();
// 反序列化
var reader = new LBReader(bytes);
var obj = new UserEntity();
obj.id = reader.ReadInt32();
obj.name = reader.ReadVUnicode();
// 输出信息
Trace.WriteLine("========================== 手动序列化测试 ==========================");
Trace.WriteLine($"[序列化] 字节大小:{bytes.Length} 字节数组:{BitConverter.ToString(bytes)}");
Trace.WriteLine($"[反序列化]\n{JsonSerializer.Serialize(obj, jsonOptions)}");
}
// JsonSerializer配置
private JsonSerializerOptions jsonOptions = new JsonSerializerOptions() {
WriteIndented = true,
IncludeFields = true,
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
};
/*
// 输出结果:
========================== 手动序列化测试 ==========================
[序列化] 字节大小:10 字节数组:7B-00-00-00-28-02-20-5F-09-4E
[反序列化]
{
"id": 123,
"name": "张三"
}
*/
把序列化、反序列化代码封装到一起:
UserEntitySerializer.cs:
using LiteByte;
using LiteByte.Converters;
using Test.Entity;
public class UserEntitySerializer : ILBSerializer<UserEntity> {
public static byte[] Serialize(UserEntity entity) {
var writer = new LBWriter();
Write(writer, entity);
return writer.ToBytes();
}
public static UserEntity Deserialize(byte[] bytes) {
var reader = new LBReader(bytes);
return Read(reader);
}
public static void Write(LBWriter writer, UserEntity entity) {
if (entity == null) {
// 空对象 写入1 表示空
writer.WriteBit1(true);
return;
}
// 非空对象 写入0 表示非空
writer.WriteBit1(false);
// 写入对象字段
writer.WriteInt32(entity.id);
writer.WriteVUnicode(entity.name);
}
public static UserEntity Read(LBReader reader) {
// 判断对象是否为空
if (reader.ReadBit1()) return null;
// 读取对象字段
var entity = new UserEntity();
entity.id = reader.ReadInt32();
entity.name = reader.ReadVUnicode();
return entity;
}
}
public class UserEntity {
public int id;
public string name;
}
UserEntitySerializer测试:
private void ReadWriteTest2() {
// 数据
var entity = new UserEntity();
entity.id = 123;
entity.name = "张三";
// 序列化
var bytes = UserEntitySerializer.Serialize(entity);
// 反序列化
var obj = UserEntitySerializer.Deserialize(bytes);
// 输出信息
Trace.WriteLine("========================== 手动序列化测试2 ==========================");
Trace.WriteLine($"[序列化] 字节大小:{bytes.Length} 字节数组:{BitConverter.ToString(bytes)}");
Trace.WriteLine($"[反序列化]\n{JsonSerializer.Serialize(obj, jsonOptions)}");
}
2.自动序列化
1.创建数据实体类 UserEntity.cs
2.创建自定义类型文本 LBClasses.txt 写入与UserEntity类对应的配置文件
3.使用 LBUtil.LoadClasses(["LBClass1", "LBClass2", "LBClass3"]);加载自定义类型文本内容。
4.使用 var bytes = LBUtil.Serialize(entity); 把对象序列化成字节数组。
5.使用 LBUtil.Deserilize<UserEntity>(bytes); 把字节数组反序列化成对象。
1.创建自定义类
创建一个数据实体类文件 UserEntity.cs 内容如下:
// 实体类
public class UserEntity {
public int id;
public string name;
}
2.创建自定义类型文本
创建一个自定义类型文件 UserEntity.txt 内容如下:
// 自定义类型 类名(对应UserEntity.cs中的类名)
public class UserEntity {
// 自定义类型文本支持单行注释
/* 自定义类型文本支持多行注释 */
// 定义实体类中需要序列化的字段类型和名称(Property属性也这样写)
public int id;
// 定义实体类中需要序列化的字段类型和名称(string默认使用vunicode编码方式(变长unicode))
public string name;
// 不支持(set get)写法
//public int id { get; set; }
//public int name { get; set; }
}
3.加载自定义类型文件
使用 LBUtil.LoadClasses(["LBClass1", "LBClass2", "LBClass3"]) 加载自定义类型文件内容
/// <summary> 加载自定义类 </summary>
private void LoadClasses() {
// 加载自定义类型配置文本
string root = $"{Directory.GetCurrentDirectory()}\\Resources\\";
string lbClasses = File.ReadAllText(root + "LBClasses.txt", Encoding.UTF8);
LBUtil.LoadClasses(lbClasses);
}
4.序列化、反序列化
使用 LBUtil.Serialize(entity) 把对象序列化成二进制数据
使用 LBUtil.Deserilize<UserEntity>(bytes) 把二进制数据反序列化成对象
// JsonSerializer配置
private JsonSerializerOptions jsonOptions = new JsonSerializerOptions() {
WriteIndented = true,
IncludeFields = true,
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
};
private void AutoSerializeTest() {
// 数据对象
var entity = new UserEntity();
entity.id = 123;
entity.name = "张三";
// 序列化
var bytes = LBUtil.Serialize(entity);
// 反序列化
var obj = LBUtil.Deserialize<UserEntity>(bytes);
// 输出信息
Trace.WriteLine("========================== 自动序列化测试 ==========================");
Trace.WriteLine($"[序列化] 字节大小:{bytes.Length} 字节数组:{BitConverter.ToString(bytes)}");
Trace.WriteLine($"[反序列化]\n{JsonSerializer.Serialize(obj, jsonOptions)}");
}
三、更新日志:
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测试类。
0.13.3: 增加LBWriter写入数据时 缓冲区的最大容量限制。增加写入数组时 预先计算容量。
0.13.4: 修改VInt算法 所有负数转为正数 符号位放在最高位。
0.13.5: 添加LBType预估大小 用于LBWriter初始化缓冲区大小 降低GC。
0.13.6: LBNumberTypeReader 优化代码 增加Skip(byteCount) 方法 可以跳过字节。
0.13.7: 添加bit1、bit2、bit3、bit4、bit5、bit6、bit7、bit8类型、bool类型、GBK类型
0.13.8:删除Float8类型
0.13.9:指针操作(Reader、Writer、反射)替换为Unsafe.As<TFrom, TTo>(ref source)方法
0.14.0:重构代码 重构结构 优化性能 提升易用性
四、其他说明:
由于能力有限,暂时只实现了C#(.Net)版本
其他语言后续有时间再写,虽然造了个轮子,不过感觉造轮子的过程中收获远大于付出,挺开心的。
建了个群,有需求的可加。
QQ群:715800513

浙公网安备 33010602011771号