解析PB序列化后的整型数

一直都想了解一下PB(Protocol Buffers)是怎么进行序列化的,今天终于找到了对应的文档,就在PB官网的Encoding一节中,传送门。由于只粗略看了一下,还没办法编写出完整的解析程序(好吧就算认真看一遍应该也是写不出来的吧),所以打算先写一个解析序列化的二进制数据中的整数值的程序。先讲一下文档中所描述的序列化原理。

以官网文档中的例子来说,就是int32类型的150序列化之后的结果是下面的三个字节(第一个字节排在最左边,而在每一个字节内部则是从右往左为升序,即低位在最右边)

08 96 01

08是由field_number左移三位后与wire_type进行按位或运算得到的结果,表明这个值对应于.proto中位置为1的字段,并且类型是int32。由于这里只处理int32类型的字节序列,所以第一个字节就压根不要管了,只需要考虑怎么处理序列化后的字节序列即可。那么这两个字节怎么解析成150呢?很简单,首先考虑字节的msb,也就是字节中的最高位。第二个字节的最高位是1,第三个字节的最高位是0,因此目标数值应当从这两个字节中解析出来。接下来,将第二个字节的msb移除,得到0010110;第三个字节去掉了最高位后则得到了0000001。将这两部分按高低位连接为一串bit后就得到了10010110,转换为十进制后也就是150了。

理解了原理之后,就可以写出下面这样的CL代码了

(defun parse-int32 (&rest bytes)
  (let ((parts '()))
    (dolist (byte bytes)
      (push (subseq (format nil "~8,'0B" byte) 1)
            parts))
    (parse-integer (apply #'concatenate 'string parts) :radix 2)))

代码很简单,就是将输入的字节先转换为二进制形式的字符串,去掉最高位后拼接在一起,再按照二进制解析为整数即可。以官方文档中的例子进行测试,示例代码为

(parse-int32 #X96 #X01)

结果即为150,如果以官方文档中的另一个例子进行调用,也可以获得300的结果

(parse-int32 #XAC #X02)

这里偷工减料的地方,在于根本没有判断所读取的字节是否已经足够解析出一个整型数了,而是将所有输入的字节都纳入了计算中。并且,基于字符串的计算方法看起来似乎不太正统。算了,只是为了写写博客顺便看看自己有没有正确理解PB的序列化方法而已。

全文完