本项目暂命名miniproto1.0。
有限的兼容protobuf的语法规则 和 编码规则。包括protobuf2.0和3.0的编码规则。
实现proto结构的序列化、反序列化功能。
代码生成工具,需要boost库支持(主要用了spirit库做文本解析),本人用的是boost.1.64.0。
生成后的代码,仅需要miniproto自身提供的lib(对应c++)、dll(对应c#)、jar(对应java),不需要其他第三方库。
完整项目下载地址(项目为vs2017建立的,boost库路径请自行配置)
前文介绍了miniproto,这里介绍其c++库的实现
1、Zigzag编码
一个正负数转换的算法,可以将负数映射成正数,并且使得转换后的数字利于压缩
0 --> 0
-1 --> 1
1 --> 2
-2 --> 3
2 --> 4
对应代码实现:
uint32 ProtoTool::Zigzag(int32 value)
{
uint32 res = (value << 1) ^ (value >> 31);
return res;
}
uint64 ProtoTool::Zigzag(int64 value)
{
uint64 res = (value << 1) ^ (value >> 63);
return res;
}
int32 ProtoTool::DeZigzag(uint32 value)
{
int32 res = (value >> 1) ^ -((*(int32 *)(&value)) & 1);
return res;
}
int64 ProtoTool::DeZigzag(uint64 value)
{
int64 res = (value >> 1) ^ -((*(int64 *)(&value)) & 1);
return res;
}
2、Varint编码
对数字进行压缩的算法
一个int 或者 longlong,占用4/8个字节,但是里面实际存的值,往往并不是真的用满4/8字节,大部分情况下高位都是0。
那么对于这种高位上的无用的0,就可以想办法给他省略掉。
varint对数字,从低位向高位,每7位为1截,第8位存后续高位还有没有值,有则标1,没有则标0。
这样对于:
0 ~ 2^7 的数字,需要1字节存储
0 ~ 2^14 的数字,需要2字节存储
0 ~ 2^21 的数字,需要3字节存储
0 ~ 2^28 的数字,需要4字节存储
0 ~ 2^35 的数字,需要5字节存储
0 ~ 2^42 的数字,需要6字节存储
0 ~ 2^49 的数字,需要7字节存储
0 ~ 2^56 的数字,需要8字节存储
0 ~ 2^63 的数字,需要9字节存储
0 ~ 2^64 的数字,需要10字节存储
可以看出,当数字很大的时候,才会多浪费1-2个字节。而当数字比较小的时候,节省的字节还是比较可观的。
对应代码实现:
byte_size ProtoTool::NumberCode(uint32 value, byte *buf)
{
byte_size bytes = 0;
byte temp = 0;
while (true)
{
temp = (byte)(value & 0x7f);
if ((value >>= 7) != 0)
{
buf[bytes++] = temp | 0x80;
}
else
{
buf[bytes++] = temp;
break;
}
};
return bytes;
}
byte_size ProtoTool::NumberCode(uint64 value, byte *buf)
{
byte_size bytes = 0;
byte temp = 0;
while (true)
{
temp = (byte)(value & 0x7f);
if ((value >>= 7) != 0)
{
buf[bytes++] = temp | 0x80;
}
else
{
buf[bytes++] = temp;
break;
}
};
return bytes;
}
byte_size ProtoTool::NumberDecode(uint32& value, const byte *buf)
{
byte_size bytes = 0;
value = 0;
while (true)
{
byte temp = buf[bytes];
value = value | ((((uint32)temp) & 0x7f) << (7 * bytes));
if ((temp & 0x80) != 0)
{
bytes++;
}
else
{
bytes++;
break;
}
}
return bytes;
}
byte_size ProtoTool::NumberDecode(uint64& value, const byte *buf)
{
byte_size bytes = 0;
value = 0;
while (true)
{
byte temp = buf[bytes];
value = value | ((((uint64)temp) & 0x7f) << (7 * bytes));
if ((temp & 0x80) != 0)
{
bytes++;
}
else
{
bytes++;
break;
}
}
return bytes;
}
3、ProtoTool
编解码工具类,该工具类针对不同数据类型,给出一系列的编解码功能实现。
下面给出代码,对照代码做出注释说明。具体实现就不贴了,可以去下载完整项目。
// proto字段编/解码工具类,所有编/解码接口均提供 byte* 和 stream& 两种重载
class ProtoTool
{
public:
// Zigzag 编/解码
static uint32 Zigzag(int32 value);
static uint64 Zigzag(int64 value);
static int32 DeZigzag(uint32 value);
static int64 DeZigzag(uint64 value);
// Varint/fixed32/fixed64 编/解码
static byte_size NumberByteSize(uint32 value);
static byte_size NumberByteSize(uint64 value);
static byte_size NumberByteSize(float value);
static byte_size NumberByteSize(double value);
static byte_size NumberCode(uint32 value, byte *buf);
static byte_size NumberCode(uint64 value, byte *buf);
static byte_size NumberCode(float value, byte *buf);
static byte_size NumberCode(double value, byte *buf);
static byte_size NumberDecode(uint32& value, const byte *buf);
static byte_size NumberDecode(uint64& value, const byte *buf);
static byte_size NumberDecode(float& value, const byte *buf);
static byte_size NumberDecode(double& value, const byte *buf);
static byte_size NumberCode(uint32 value, std::ostream& buf);
static byte_size NumberCode(uint64 value, std::ostream& buf);
static byte_size NumberCode(float value, std::ostream& buf);
static byte_size NumberCode(double value, std::ostream& buf);
static byte_size NumberDecode(uint32& value, std::istream& buf);
static byte_size NumberDecode(uint64& value, std::istream& buf);
static byte_size NumberDecode(float& value, std::istream& buf);
static byte_size NumberDecode(double& value, std::istream& buf);
// proto key编/解码,即对 (字段Tag << 3 + 字段WiteType) 做varint编/解码
static byte_size KeyByteSize(unsigned int num, unsigned int type);
static byte_size KeyCode(unsigned int num, unsigned int type, byte *buf);
static byte_size KeyDecode(unsigned int& num, unsigned int& type, const byte *buf);
static byte_size KeyCode(unsigned int num, unsigned int type, std::ostream& buf);
static byte_size KeyDecode(unsigned int& num, unsigned int& type, std::istream& buf);
// 未知字段解码
// 如果出现未定义的tag字段时,对该字段的编解码
// 如果出现不支持的WiteType,抛UnknownWireTypeException
static byte_size UnknownDecode(unsigned int type, const byte *buf);
static byte_size UnknownDecode(unsigned int type, std::istream& buf);
// 基本数据类型的成员字段编/解码
// bool成员
static byte_size BoolByteSize(bool value);
static byte_size BoolCode(bool value, byte *buf);
static byte_size BoolDecode(bool& value, const byte *buf);
static byte_size BoolCode(bool value, std::ostream& buf);
static byte_size BoolDecode(bool& value, std::istream& buf);
// int32成员
static byte_size Int32ByteSize(int32 value);
static byte_size Int32Code(int32 value, byte *buf);
static byte_size Int32Decode(int32& value, const byte *buf);
static byte_size Int32Code(int32 value, std::ostream& buf);
static byte_size Int32Decode(int32& value, std::istream& buf);
// sint32成员
static byte_size SInt32ByteSize(int32 value);
static byte_size SInt32Code(int32 value, byte *buf);
static byte_size SInt32Decode(int32& value, const byte *buf);
static byte_size SInt32Code(int32 value, std::ostream& buf);
static byte_size SInt32Decode(int32& value, std::istream& buf);
// uint32成员
static byte_size UInt32ByteSize(uint32 value);
static byte_size UInt32Code(uint32 value, byte *buf);
static byte_size UInt32Decode(uint32& value, const byte *buf);
static byte_size UInt32Code(uint32 value, std::ostream& buf);
static byte_size UInt32Decode(uint32& value, std::istream& buf);
// int64成员
static byte_size Int64ByteSize(int64 value);
static byte_size Int64Code(int64 value, byte *buf);
static byte_size Int64Decode(int64& value, const byte *buf);
static byte_size Int64Code(int64 value, std::ostream& buf);
static byte_size Int64Decode(int64& value, std::istream& buf);
// sint64成员
static byte_size SInt64ByteSize(int64 value);
static byte_size SInt64Code(int64 value, byte *buf);
static byte_size SInt64Decode(int64& value, const byte *buf);
static byte_size SInt64Code(int64 value, std::ostream& buf);
static byte_size SInt64Decode(int64& value, std::istream& buf);
// uint64成员
static byte_size UInt64ByteSize(uint64 value);
static byte_size UInt64Code(uint64 value, byte *buf);
static byte_size UInt64Decode(uint64& value, const byte *buf);
static byte_size UInt64Code(uint64 value, std::ostream& buf);
static byte_size UInt64Decode(uint64& value, std::istream& buf);
// 自定义enum成员
template <typename E>
static byte_size EnumByteSize(E value);
template <typename E>
static byte_size EnumCode(E value, byte *buf);
template <typename E>
static byte_size EnumDecode(E& value, const byte *buf);
template <typename E>
static byte_size EnumCode(E value, std::ostream& buf);
template <typename E>
static byte_size EnumDecode(E& value, std::istream& buf);
// float成员
static byte_size FloatByteSize(float value);
static byte_size FloatCode(float value, byte *buf);
static byte_size FloatDecode(float& value, const byte *buf);
static byte_size FloatCode(float value, std::ostream& buf);
static byte_size FloatDecode(float& value, std::istream& buf);
// double成员
static byte_size DoubleByteSize(double value);
static byte_size DoubleCode(double value, byte *buf);
static byte_size DoubleDecode(double& value, const byte *buf);
static byte_size DoubleCode(double value, std::ostream& buf);
static byte_size DoubleDecode(double& value, std::istream& buf);
// std::base_string成员
template <template<typename> class A = std::allocator>
static byte_size StringByteSize(const String<A>& value);
template <template<typename> class A = std::allocator>
static byte_size StringCode(const String<A>& value, byte *buf);
template <template<typename> class A = std::allocator>
static byte_size StringDecode(String<A>& value, const byte *buf);
template <template<typename> class A = std::allocator>
static byte_size StringCode(const String<A>& value, std::ostream& buf);
template <template<typename> class A = std::allocator>
static byte_size StringDecode(String<A>& value, std::istream& buf);
// 自定义message成员
template <typename M>
static byte_size MessageByteSize(const M& value);
template <typename M>
static byte_size MessageCode(const M& value, byte *buf);
template <typename M>
static byte_size MessageDecode(M& value, const byte *buf);
template <typename M>
static byte_size MessageCode(const M& value, std::ostream& buf);
template <typename M>
static byte_size MessageDecode(M& value, std::istream& buf);
// 容器类字段,对每个元素的编/解码
// 由于int32/sint32(均对应int),int64/sint64(均对应long long)的数据类型是一致的
// 但编/解码逻辑是不一致的,sint32/sint64需要Zigzag后再Varint
// 因此,如果仅仅是这样的接口:byte_size EntryCode(const T& value, byte *buf)
// 模板参数T,对于int32/sint32,int64/sint64,模板展开后是同样的代码
// 因此需要额外参数类型对其进行区分,重载
// 因此对每种数据类型,定义了其对应的重载参数类型type
// 这个type没有什么实际逻辑功能,仅用于让编译器可以区分不同的函数重载
// 使得不同的EntryCode/Decode去调用上面实现的具体的某种数据类型的XXXCode/Decode
template <class T>
static byte_size EntryByteSize(const T& value, ProtoBool type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoInt32 type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoSInt32 type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoUInt32 type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoInt64 type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoSInt64 type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoUInt64 type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoEnum type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoFloat type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoDouble type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoString type);
template <class T>
static byte_size EntryByteSize(const T& value, ProtoMessage type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoBool type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoInt32 type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoSInt32 type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoUInt32 type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoInt64 type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoSInt64 type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoUInt64 type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoEnum type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoFloat type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoDouble type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoString type);
template <class T>
static byte_size EntryCode(const T& value, byte *buf, ProtoMessage type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoBool type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoInt32 type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoSInt32 type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoUInt32 type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoInt64 type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoSInt64 type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoUInt64 type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoEnum type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoFloat type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoDouble type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoString type);
template <class T>
static byte_size EntryDecode(T& value, const byte *buf, ProtoMessage type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoBool type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoInt32 type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoSInt32 type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoUInt32 type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoInt64 type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoSInt64 type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoUInt64 type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoEnum type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoFloat type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoDouble type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoString type);
template <class T>
static byte_size EntryCode(const T& value, std::ostream& buf, ProtoMessage type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoBool type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoInt32 type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoSInt32 type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoUInt32 type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoInt64 type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoSInt64 type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoUInt64 type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoEnum type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoFloat type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoDouble type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoString type);
template <class T>
static byte_size EntryDecode(T& value, std::istream& buf, ProtoMessage type);
// std::vector元素的编解码
// 内部调用EntryCode/Decode
template <class T, class P>
static byte_size ArrayEntryByteSize(const T& value, P type);
template <class T, class P>
static byte_size ArrayEntryCode(const T& value, byte *buf, P type);
template <class T, class P>
static byte_size ArrayEntryDecode(T& value, const byte *buf, P type);
template <class T, class P>
static byte_size ArrayEntryCode(const T& value, std::ostream& buf, P type);
template <class T, class P>
static byte_size ArrayEntryDecode(T& value, std::istream& buf, P type);
// std::vector编解码
// 调用ArrayEntryCode/Decode
template <class T, template<typename> class A, class P>
static byte_size ArrayByteSizeWithoutLength(const Array<T, A>& values, P type);
template <class T, template<typename> class A, class P>
static byte_size ArrayByteSize(const Array<T, A>& values, P type);
template <class T, template<typename> class A, class P>
static byte_size ArrayCode(const Array<T, A>& values, byte *buf, P type);
template <class T, template<typename> class A, class P>
static byte_size ArrayDecode(Array<T, A>& values, const byte *buf, P type);
template <class T, template<typename> class A, class P>
static byte_size ArrayCode(const Array<T, A>& values, std::ostream& buf, P type);
template <class T, template<typename> class A, class P>
static byte_size ArrayDecode(Array<T, A>& values, std::istream& buf, P type);
// std::set元素的编解码
// 调用EntryCode/Decode
template <class T, class P>
static byte_size SetEntryByteSize(const T& value, P type);
template <class T, class P>
static byte_size SetEntryCode(const T& value, byte *buf, P type);
template <class T, class P>
static byte_size SetEntryDecode(T& value, const byte *buf, P type);
template <class T, class P>
static byte_size SetEntryCode(const T& value, std::ostream& buf, P type);
template <class T, class P>
static byte_size SetEntryDecode(T& value, std::istream& buf, P type);
// std::set编解码
// 调用SetEntryCode/Decode
template <class T, template<typename> class A, class P>
static byte_size SetByteSizeWithoutLength(const Set<T, A>& values, P type);
template <class T, template<typename> class A, class P>
static byte_size SetByteSize(const Set<T, A>& values, P type);
template <class T, template<typename> class A, class P>
static byte_size SetCode(const Set<T, A>& values, byte *buf, P type);
template <class T, template<typename> class A, class P>
static byte_size SetDecode(Set<T, A>& values, const byte *buf, P type);
template <class T, template<typename> class A, class P>
static byte_size SetCode(const Set<T, A>& values, std::ostream& buf, P type);
template <class T, template<typename> class A, class P>
static byte_size SetDecode(Set<T, A>& values, std::istream& buf, P type);
// std::map键值对的编解码
// 调用EntryCode/Decode
template <class K, class V, class KP, class VP>
static byte_size MapEntryByteSizeWithoutLength(const K& key, const V& value, KP keyType, VP valueType);
template <class K, class V, class KP, class VP>
static byte_size MapEntryByteSize(const K& key, const V& value, KP keyType, VP valueType);
template <class K, class V, class KP, class VP>
static byte_size MapEntryCode(const K& key, const V& value, byte *buf, KP keyType, VP valueType);
template <class K, class V, class KP, class VP>
static byte_size MapEntryDecode(K& key, V& value, const byte *buf, KP keyType, VP valueType);
template <class K, class V, class KP, class VP>
static byte_size MapEntryCode(const K& key, const V& value, std::ostream& buf, KP keyType, VP valueType);
template <class K, class V, class KP, class VP>
static byte_size MapEntryDecode(K& key, V& value, std::istream& buf, KP keyType, VP valueType);
// std::map编解码
// 调用MapEntryCode/Decode
template <class K, class V, template<typename> class A, class KP, class VP>
static byte_size MapByteSizeWithoutLength(const Map<K, V, A>& values, KP keyType, VP valueType);
template <class K, class V, template<typename> class A, class KP, class VP>
static byte_size MapByteSize(const Map<K, V, A>& values, KP keyType, VP valueType);
template <class K, class V, template<typename> class A, class KP, class VP>
static byte_size MapCode(const Map<K, V, A>& values, byte *buf, KP keyType, VP valueType);
template <class K, class V, template<typename> class A, class KP, class VP>
static byte_size MapDecode(Map<K, V, A>& values, const byte *buf, KP keyType, VP valueType);
template <class K, class V, template<typename> class A, class KP, class VP>
static byte_size MapCode(const Map<K, V, A>& values, std::ostream& buf, KP keyType, VP valueType);
template <class K, class V, template<typename> class A, class KP, class VP>
static byte_size MapDecode(Map<K, V, A>& values, std::istream& buf, KP keyType, VP valueType);
};
4、ProtoBase
所有自定义message的基类,规范message接口,实现多态
提供序列化/反序列化 到 内存/流 的接口, 提供clear/release接口
对应代码:
// 所有自定义message的基类
class ProtoBase
{
public:
ProtoBase();
virtual ~ProtoBase();
public:
virtual byte_size ByteSize() const = 0;
virtual byte_size Code(byte *buf, byte_size size) const = 0;
virtual byte_size Decode(const byte *buf, byte_size size) = 0;
virtual byte_size Code(std::ostream& buf, byte_size size) const = 0;
virtual byte_size Decode(std::istream& buf, byte_size size) = 0;
public:
virtual void Clear() = 0;
virtual void Release() = 0;
public:
bool SerializeToArray(byte *buf, byte_size size) const;
bool ParseFromArray(const byte *buf, byte_size size);
bool SerializeToStream(std::ostream& buf, byte_size size) const;
bool ParseFromStream(std::istream& buf, byte_size size);
};
5、ProtoBitMap
采用模板实现的一个位图类,用于message中标记字段是否设置了值。
位图长度作为模板参数,没有使用stl的bitset,因为我只需要很简单的标记功能就可以。
对应代码:
template <uint32 N>
class ProtoBitMap
{
public:
ProtoBitMap();
ProtoBitMap(const ProtoBitMap<N>& other);
~ProtoBitMap();
ProtoBitMap<N>& operator=(const ProtoBitMap<N>& other);
public:
void SetBit(uint32 index);
void ClearBit(uint32 index);
bool HasBit(uint32 index) const;
public:
void Clear();
private:
byte m_Bits[(N != 0) ? ((N - 1) / 8 + 1) : 1];
};