// 简单过了下计算过程
定义并生成消息
syntax="proto3";
package com.test.pb;
option java_package = "com.test.protobuf";
option java_outer_classname = "PersonProto";
message Person {
int32 id=1;
}
测试类
Java在内存中占32位,protobuf占1个字节8位
package com.test.protobuf;
import java.util.Arrays;
/** protobuf的二进制序列化操作:
* 序列化时减少了额外信息
* 动态伸缩性保存字节
* */
public class TestProto {
public static void main(String[] args) throws Exception {
serialize();
System.out.println("----------");
deserialize();
}
public static void serialize() throws Exception {
PersonProto.Person.Builder builder = PersonProto.Person.newBuilder();
builder.setId(1);
com.test.protobuf.PersonProto.Person p = builder.build();
//
System.out.println(Arrays.toString(p.toByteArray()));
}
public static void deserialize() throws Exception {
PersonProto.Person.Builder builder = PersonProto.Person.newBuilder();
builder.setId(1);
com.test.protobuf.PersonProto.Person p = builder.build();
//
PersonProto.Person newPerson = PersonProto.Person.parseFrom(p.toByteArray());
System.out.println(newPerson);
}
}
执行结果:
[8, 1]
----------
id: 1
序列化过程分析
AbstractMessageLite.class中的toByteArray()方法
/* builder.build().toByteArray()*/
@Override
public byte[] toByteArray() {
try {
final byte[] result = new byte[getSerializedSize()];
final CodedOutputStream output = CodedOutputStream.newInstance(result);
writeTo(output);
output.checkNoSpaceLeft();
return result;
} catch (IOException e) {
throw new RuntimeException(getSerializingExceptionMessage("byte array"), e);
}
}
com.test.protobuf.PersonProto.java中的writeTo()方法 //生成消息的java类,按编号顺序进行序列化
@java.lang.Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
if (id_ != 0) {
output.writeInt32(1, id_);
}
unknownFields.writeTo(output);
}
com.google.protobuf.CodedOutputStream.class的writeInt32()方法
@Override
public final void writeInt32(final int fieldNumber, final int value) throws IOException {
writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
writeInt32NoTag(value);
}
writeInt32NoTag()方法.
@Override
public final void writeInt32NoTag(int value) throws IOException {
if (value >= 0) {
writeUInt32NoTag(value);
} else {
// Must sign-extend.
writeUInt64NoTag(value);
}
}
最后进入 writeUInt32NoTag()方法
@Override
public final void writeUInt32NoTag(int value) throws IOException {
if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) {
while (true) {
if ((value & ~0x7F) == 0) {
UnsafeUtil.putByte(buffer, position++, (byte) value);
return;
} else {
UnsafeUtil.putByte(buffer, position++, (byte) ((value & 0x7F) | 0x80));
value >>>= 7;
}
}
} else {
try {
while (true) {
if ((value & ~0x7F) == 0) {
buffer[position++] = (byte) value;
return;
} else {
buffer[position++] = (byte) ((value & 0x7F) | 0x80);
value >>>= 7;
}
}
} catch (IndexOutOfBoundsException e) {
throw new OutOfSpaceException(
String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
}
}
}
结果分析
if ((value & ~0x7F) == 0) 意思是当value<128
0x7F=127 即二进制 0111 1111
0x7F转换为1个字节的二进制取反~为1000 0000
value & 1000 0000
为?000 0000(0000 0000=0 或 1000 0000=128)
所以value<128
(value & 0x7F) | 0x80)
0x80=128 即二进制 1000 0000
value & 0x7F(0111 1111) 保存1至7位
value | 0x80 = 1??? ??? //获取第8位为1, 则1-7位是上面的(value & 0x7F)
value>>>=7 数据位右移7位并赋值
在int型4字节,protobuf把一个8位的字节用7位表示数据,最高位1位用来表示后面是否还有数据, protobuf传输时(1-5字节)动态保存,保留最小数据空间
//
Java在内存中占32位,protobuf占1个字节8位
pb定义消息中int32,如果数值比较小,在0~127时,使用一个字节打包。
java中int 4字节 32位,protobuf是按1-5字节动态描述,还有long用1-9字节描述