protobuf序列化一个int型整数1

本文详细分析了protobuf如何序列化一个int型整数,重点介绍了序列化过程,从`AbstractMessageLite.toByteArray()`到`writeUInt32NoTag()`,解释了在value小于128时的优化策略,以及protobuf如何在1-5字节动态保存数据,节省存储空间。

// 简单过了下计算过程

定义并生成消息

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字节描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值