LiteByte 二进制数据交换格式

转载请标明原文地址:https://segmentfault.com/a/1190000021329368

一、简介

icon_1_alpha_256x72.png
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

posted @ 2019-12-20 14:47  冰封百度  阅读(579)  评论(0)    收藏  举报