Dson提供了两个版本的二进制格式,从整体上看他们是一样的,区别在于一个使用number映射字段,一个使用string映射字段。 使用number映射字段可以使编码后的包体更小,编解码性能也更好。
我们以object的编码为例介绍流的构成。
[dsonType + wireType] + length1 + [lnumber + idep] + [length2] + [subType] + [hasValue] + [data] ... 5 bits 3 bits 4Bytes n bits 3 bits 4 Bytes 1~5 Byte 1Byte 0~n Bytes 1 Byte(unit8) 总长度 1~n Byte int32 unit32
- length1区域记录动态容器类结构(Object、Array、Header)的数据总长度。
- 为以方便截取数据,Length1区域固定Fixed32编码。
- 每一个Value都会写入其类型信息
DsonType
,数字还会记录其编码信息WireType
。 - DsonType 和 WireType 合并编码,共1个字节。
- WireType分为:VARINT(0)、UINT(1)、SINT(2)、FIXED(3) -可参考谷歌protobuf。
- int32和int64数字的编码类型会随着数字序列化,以确保对方正确的解码。
- WireType的比特位用于非数字类型时可以表达其它信息 -- 比如标记null字段。
- Dson最初是为序列化而创建的,因此考虑过继承问题,Dson是支持继承的。
- 字段的fullNumber由两部分构成,localNumber(本地编号) + idep(继承深度)。
- idep的取值范围[0,7],localNumber不建议超过8192,不可以为负
- fullNumber为uint32类型。
- Length2区域用于记录变长内置结构的长度;固定长度的属性类型没有length字段。
- 数字没有length字段,数字通过wireType确定如何解码。
- string的length是uint32变长编码。
- Binary的length也是uint32变长编码。
- subType用于记录Binary、ExtInt32、ExtInt64、ExtDouble、ExtString的Type。
- subType统一使用
uint32
编码。
- Ext扩展类型允许Value部分不存在;为保持编码简单,我们不记录在WireType的比特上。
- hasValue用于记录Binary、ExtInt32、ExtInt64、ExtDouble、ExtString是否包含数据。
- bool使用wireType记录了其值;wireType为1表示true,0表示false
- Float和Double使用wireType记录了后导全0的字节数
- 浮点数的前16位总是写入
- 对于Float,前16位包含了7个有效数据位,就涵盖了大量的常用数;WireType的取值范围为[0, 2]
- 对于Double,前16位包含了4个数据位,也包含了少许常用数据;WireType的取值范围为[0, 6]
- 浮点数压缩算法的实际收益不理想,不过聊胜于无。
- ptr/lptr使用wireType标记了namespace、type、policy是否存在,只有存在时才写入;localId总是写入。
- 001 用于标记namespace
- 010 用于标记type
- 100 用于标记policy
- 编码顺序为 localId、namespace、type、policy (可选字段后序列化)
- 其中 type 和 policy 使用unit32编码
- datetime使用wireType存储了enables
output.writeUint32(binary.length);
output.writeRawBytes(binary.data);
- 由于指针的使用量可能较大,我们在wireType上记录了namespace、type、policy是否有值的信息。
output.writeString(dsonValue.localId);
if (dsonValue.hasNamespcae) {
output.writeString(dsonValue.namespace);
}
if (dsonValue.type != 0) {
output.writeRawByte(dsonValue.type);
}
if (dsonValue.policy != 0) {
output.writeRawByte(dsonValue.policy);
}
- 由于指针的使用量可能较大,我们在wireType上记录了namespace、type、policy是否有值的信息
- localId 使用
Uint
编码,localId通常应该为正值。
output.writeUInt64(dsonValue.localId);
if (dsonValue.hasNamespcae) {
output.writeString(dsonValue.namespace);
}
if (dsonValue.type != 0) {
output.writeRawByte(dsonValue.type);
}
if (dsonValue.policy != 0) {
output.writeRawByte(dsonValue.policy);
}
- 我未对DateTime的编码做优化,简单按序写入。
- offset使用
sint
编码,时区容易出现负数。 - enables使用wireType比特位存储
output.writeUint64(dateTime.getSeconds());
output.writeUint32(dateTime.getNanos());
output.writeSint32(dateTime.getOffset());
- 我未对Timestamp的编码做优化,简单按序写入。
output.writeUint64(dateTime.getSeconds());
output.writeUint32(dateTime.getNanos());
- string采用utf8编码
- header是object/array的一个匿名属性,在object中是没有字段id但有类型的值。
文档型编码格式:
length [dsonType + wireType] + [length + name] + [length] + [subType] + [data] ... 4Bytes 5 bits 3 bits nBytes 4 Bytes 1~5 Byte 0~n Bytes 数据长度 1 Byte(unit8) string int32 unit32
String映射字段其实和number映射字段的差别只是 fullNumber 变为了字符串类型的名字,而name按照普通的String值编码, 即:length采用uint32编码,data采用utf8编码。