为什么如此高效?解密kryo各个数据类型的序列化编码机制,强( 二 )


表示演示一下:kryo两字节能存储的数据的特点是高字节中前两位为0 , 例如:0011 1011 0 010 1001其?存储方式为buffer [0] =先存最后字节的低7位 , 010 1001 , 然后第一位之前 , 加1 , 表示还需要申请第二个字节来存储 。 此时buffer [0] = 1010 1001 buffer [1] =存储011 1011 0(这个0是原第一个字节未存储的部分) , 此时缓冲区[1]的8位中的高位为0 , 表示存储结束 。
下图展示了kryo用2个字节存储一个int类型的数据的示意图 。
为什么如此高效?解密kryo各个数据类型的序列化编码机制,强文章插图
同理 , 用3个字节可以表示2 ^ 21 -1 。 kryo使用变长字节(1-5)个字节来存储int类型(java中固定占4字节) 。
1.2 int反序列化(byte [] ---> int)buffer [0] =低位 , buffer [1]高位 , 具体解码实现为:Input#readVarInt , 反序列化就是根据上述编码规则 , 将byte []序列化为int数字 。
/** Reads a 1-5 byte int. It is guaranteed that a varible length encoding will be used. */ public int readVarInt (boolean optimizePositive) throws KryoException {if (require(1) < 5) return readInt_slow(optimizePositive);int b = buffer[position++];int result = bif ((bb = buffer[position++];result |= (bif ((bresult |= (bif ((bresult |= (bif ((bresult |= (b}}}}return optimizePositive ? result : ((result >>> 1) ^ -(result}Input#require(count)返回的是缓存区剩余字节数(变量) 。 其实现思路是 , 一个一个字节的读取 , 读到第一个字节后 , 首先提取有效存储位的数据 ,buffer [0]&0x7F , 然后判断高位是否为1 , 如果不为1 , 直接返回 , 如果为1 , 则继续读取第二位buffer [1] , 同样首先提取有效数据位(低7位) , 然后对这数据向左移7位 , 在与buffer [0]进行或运算 。 也就是 , varint的存放是小端序列 , 越先读到的位 , 在整个int序列中越靠近低位 。
2 , 字符串序列化其实现类DefaultSerializers $ StringSerializer 。
static public class StringSerializer extends Serializer {{setImmutable(true);setAcceptsNull(true);// @1}public void write (Kryo kryo, Output output, String object) {output.writeString(object);}public String read (Kryo kryo, Input input, Class type) {return input.readString();} }代码@ 1:String位不可变 , 允许为空 , 也就是序列化时需要考虑String s = null的情况 。
2.1序列化(字符串----> byte [])输出#writeString
public void writeString (String value) throws KryoException {if (value =http://kandian.youth.cn/index/= null) {// @1writeByte(0x80); // 0 means null, bit 8 means UTF8.return;}int charCount = value.length();if (charCount == 0) {// @2writeByte(1 | 0x80); // 1 means empty string, bit 8 means UTF8.return;}// Detect ASCII.boolean ascii = false;if (charCount> 1for (int i = 0; i < charCount; i++) {int c = value.charAt(i);if (c > 127) {ascii = false;break;}}}if (ascii) {// @4if (capacity - position < charCount)writeAscii_slow(value, charCount);else {value.getBytes(0, charCount, buffer, position);position += charCount;}buffer[position - 1] |= 0x80;} else {writeUtf8Length(charCount + 1);// @5int charIndex = 0;if (capacity - position >= charCount) {// @6// Try to write 8 bit chars.byte[] buffer = this.buffer;int position = this.position;for (; charIndex < charCount; charIndex++) {int c = value.charAt(charIndex);if (c > 127) break;buffer[position++] = (byte)c;}this.position = position;}if (charIndex < charCount) writeString_slow(value, charCount, charIndex);// @7} }首先对字符串编码成字节序列 , 通常采用的编码方式为长度:具体内容 , 通常的做法 , 表示串行序列长度为固定字节 , 例如4位 , 那kryo是如何来表示的呢?请看初步分析 。
代码@ 1:如果字符串为null , 采用一个字节来表示长度 , 长度为0 , 并且该字节的高位填充1 , 表示字符串使用UTF-8编码 , 空字符串的最终表示为:1000 0000。
代码@ 2:空字符串表示 , 长度用1来表示 , 同样高位使用1填充表示字符串使用UTF-8编码 , 空字符串最终表示为:10000001 。 注:长度为1表示空字符串 。
代码@ 3:如果字符长度大于1并且小于64 , 依次检查字符 , 如果其ascii小于127 , 则认为可以使用ascii来表示空格 , 不能超过127的原因是 , 其中字节的高一个需要表示编码 , 0表示ascii , 当用ascii编码来表示字符串是 , 第高2位需要使用表示还是结束标记 。
代码@ 4:如果使用ascii编码 , 则单独字符 , 使用一个字节表示 , 高1位表示编码标记为 , 高2位表示是否结束标记 。
代码@ 5:按照UTF-8编码 , 写入其长度 , 用变长int(varint)写入字符串长度 , 具体实现如下:
输出#writeUtf8Length
private void writeUtf8Length (int value) {if (value >>> 6 == 0) {require(1);buffer[position++] = (byte)(value | 0x80); // Set bit 8.} else if (value >>> 13 == 0) {require(2);byte[] buffer = this.buffer;buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8.buffer[position++] = (byte)(value >>> 6);} else if (value >>> 20 == 0) {require(3);byte[] buffer = this.buffer;buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8.buffer[position++] = (byte)((value >>> 6) | 0x80); // Set bit 8.buffer[position++] = (byte)(value >>> 13);} else if (value >>> 27 == 0) {require(4);byte[] buffer = this.buffer;buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8.buffer[position++] = (byte)((value >>> 6) | 0x80); // Set bit 8.buffer[position++] = (byte)((value >>> 13) | 0x80); // Set bit 8.buffer[position++] = (byte)(value >>> 20);} else {require(5);byte[] buffer = this.buffer;buffer[position++] = (byte)(value | 0x40 | 0x80); // Set bit 7 and 8.buffer[position++] = (byte)((value >>> 6) | 0x80); // Set bit 8.buffer[position++] = (byte)((value >>> 13) | 0x80); // Set bit 8.buffer[position++] = (byte)((value >>> 20) | 0x80); // Set bit 8.buffer[position++] = (byte)(value >>> 27);} }