欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

一个轻量的 wire format 解释器(google protobuf 的二进制格式)

程序员文章站 2022-07-14 14:13:19
...
google 的 protobuf 项目,底层的二进制格式设计很精简,格式的详细描述参考下面的链接:
http://code.google.com/apis/protocolbuffers/docs/encoding.html

下载链接
http://spjson.googlecode.com/files/spjson-0.3.src.tar.gz
暂时是作为 spjson 的一部分。

这个格式被 google 才称为 wire format ,实质是一个用于保存 key/value 对的二进制格式。
key 被称为 fieldNumber 。
value 分为几种类型:varint,64bit,32bit,Length-delimited 。

varint 是指变长整数,实质上可能对应 int8,int16,int32,int64 和他们的有符号类型。varint 对于保存小整数有非常好的压缩效果,对于 <= 128 的整数只需要一个字节。具体的算法可以参考上面的链接。

64bit 和 32bit 按我的理解主要可能是用于映射 double 和 float 。因为整数本身已经全部可以通过 varint 来解决了。 把 double 和 float 按 little-endian byte order 进行保存。

Length-delimited 用于保存字符串和二进制数据。二进制数据可能是另外一个 protobuf ,这样就可以实现支持内嵌复杂类型的功能。

实现用典型的 codec 的方式来实现,一个 encoder 和一个 decoder 。
class SP_ProtoBufEncoder
{
public:
    SP_ProtoBufEncoder( int initLen = 0 );
    ~SP_ProtoBufEncoder();

    int addVarint( int fieldNumber, uint64_t value );
    int addDouble( int fieldNumber, double value );
    int addFloat( int fieldNumber, float value );

    int addBinary( int fieldNumber, const char * buffer, int len );

    const char * getBuffer();
    int getSize();
};

class SP_ProtoBufDecoder
{
public:
    SP_ProtoBufDecoder( const char * buffer, int len );
    ~SP_ProtoBufDecoder();

    bool getNext( KeyValPair_t * pair );

    bool find( int fieldNumber, KeyValPair_t * pair, int index = 0 );
};



encoder 对于不同的类型,提供了对应的 add 方法。在任何时候,都可以调用 encoder 的 getBuffer 和 getSize 获取当前已经生成的 buffer 。

decoder 提供 getNext 和 find 两个方法。getNext 用于遍历 protobuf 的所有字段。find 方法用于根据 fieldNumber 来获取对应的字段。定义了一个辅助数据结构 KeyValPair_t 来简化这个两个方法的参数。find 方法内部使用二分查找法来加速。