本项目暂命名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#库的实现。
Zigzag编码和Varint编码就不再赘述,可以参考miniproto的c++库实现。
1、委托
容器元素的编解码,用到了c#的委托。类似于c的函数指针的一个东西。
委托这个东西本文就不具体介绍了,网上很多深入浅出的文章说明。
这里说下为什么会用到委托:主要是因为c#的泛型,并不存在c++中的模板参数自动推导机制。比如:
static void FunA(int t)
{}
static void FunA(long t)
{}
static void FunB<T>(T t)
{
FunA(t); // 此处c#编译是通不过的
}
这样的代码在c++中是没有问题的,编译器会根据 实际调用FunB的地方传递的实参类型来判断形参T是什么类型,进而去调用更匹配的FunA的重载。
但是c#没有这样的机制,这样写编译是通不过的。用委托能相对来说比较优雅的解决这个需求(参考这篇文章)。如下:
delegate void Call<T>(T value);
private static class Function<T>
{
public static Call<T> call;
}
static void FunA(int t)
{}
static void FunA(long t)
{}
static void FunB<T>(T t)
{
Function<T>.call(t);
}
static void Main(string[] args)
{
Function<int>.call = (int value) => FunA(value);
Function<long>.call = (long value) => FunA(value);
FunB<int>(1);
FunB<long>(1L);
}
2、ProtoTool
基本雷同C++的ProtoTool,前半部分基本相同,后半部分容器的编解码用到委托。针对不同数据类型,给出一系列的编解码功能实现。
下面给出ProtoTool代码,对照代码做出注释说明。具体实现就不贴了,可以去下载完整项目。
// proto字段编码工具类
public static class ProtoTool
{
// Zigzag编/解码
public static uint Zigzag(int value);
public static ulong Zigzag(long value);
public static int DeZigzag(uint value);
public static long DeZigzag(ulong value);
// Varint/fixed32/fixed64 编/解码
public static ulong NumberByteSize(uint value);
public static ulong NumberByteSize(ulong value);
public static ulong NumberByteSize(float value);
public static ulong NumberByteSize(double value);
public static ulong NumberCode(uint value, Stream buf);
public static ulong NumberCode(ulong value, Stream buf);
public static ulong NumberCode(float value, Stream buf);
public static ulong NumberCode(double value, Stream buf);
public static ulong NumberDecode(ref uint value, Stream buf);
public static ulong NumberDecode(ref ulong value, Stream buf);
public static ulong NumberDecode(ref float value, Stream buf);
public static ulong NumberDecode(ref double value, Stream buf);
// proto key编/解码,即对 (字段Tag << 3 + 字段WiteType) 做varint编/解码
public static ulong KeyByteSize(uint num, uint type);
public static ulong KeyCode(uint num, uint type, Stream buf);
public static ulong KeyDecode(ref uint num, ref uint type, Stream buf);
// 未知字段解码
// 如果出现未定义的tag字段时,对该字段的编解码
// 如果出现不支持的WiteType,抛UnknownWireTypeException
public static ulong UnknownDecode(uint type, Stream buf);
// 单个字段编/解码
public static ulong BoolByteSize(bool value);
public static ulong BoolCode(bool value, Stream buf);
public static ulong BoolDecode(ref bool value, Stream buf);
public static ulong Int32ByteSize(int value);
public static ulong Int32Code(int value, Stream buf);
public static ulong Int32Decode(ref int value, Stream buf);
public static ulong Int64ByteSize(long value);
public static ulong Int64Code(long value, Stream buf);
public static ulong Int64Decode(ref long value, Stream buf);
public static ulong SInt32ByteSize(int value);
public static ulong SInt32Code(int value, Stream buf);
public static ulong SInt32Decode(ref int value, Stream buf);
public static ulong SInt64ByteSize(long value);
public static ulong SInt64Code(long value, Stream buf);
public static ulong SInt64Decode(ref long value, Stream buf);
public static ulong UInt32ByteSize(uint value);
public static ulong UInt32Code(uint value, Stream buf);
public static ulong UInt32Decode(ref uint value, Stream buf);
public static ulong UInt64ByteSize(ulong value);
public static ulong UInt64Code(ulong value, Stream buf);
public static ulong UInt64Decode(ref ulong value, Stream buf);
public static ulong EnumByteSize<E>(E value);
public static ulong EnumCode<E>(E value, Stream buf);
public static ulong EnumDecode<E>(ref E value, Stream buf);
public static ulong FloatByteSize(float value);
public static ulong FloatCode(float value, Stream buf);
public static ulong FloatDecode(ref float value, Stream buf);
public static ulong DoubleByteSize(double value);
public static ulong DoubleCode(double value, Stream buf);
public static ulong DoubleDecode(ref double value, Stream buf);
public static ulong StringByteSize(string value);
public static ulong StringCode(string value, Stream buf);
public static ulong StringDecode(ref string value, Stream buf);
public static ulong MessageByteSize<M>(M value) where M : ProtoBase;
public static ulong MessageCode<M>(M value, Stream buf) where M : ProtoBase;
public static ulong MessageDecode<M>(ref M value, Stream buf) where M : ProtoBase;
// 针对不同数据类型的编/解码函数的委托
// 利用模板对应 具体数据类型 以及其对应的ByteSize/Code/Decode处理函数
delegate TResult FunByteSize<T, TResult>(T value);
delegate TResult FunCode<T, TResult>(T value, Stream buf);
delegate TResult FunDecode<T, TResult>(ref T value, Stream buf);
private static class Cache<T, P>
{
public static FunByteSize<T, ulong> ByteSize;
public static FunCode<T, ulong> Code;
public static FunDecode<T, ulong> Decode;
}
// 初始化,注册具体类型T对应的ByteSize/Code/Decode委托函数
// 对于自定义枚举类型,实际逻辑处理时,现用现注册委托函数
static ProtoTool();
// Array,Set容器中,每一个元素的编/解码
private static ulong EntryByteSize<T, P>(T value, P type);
private static ulong EntryCode<T, P>(T value, Stream buf, P type);
private static ulong EntryDecode<T, P>(ref T value, Stream buf, P type);
// Array容器的编/解码
// 内部调用EntryByteSize/Code/Decode
private static ulong ArrayByteSizeWithoutLength<T, P>(IList<T> values, P type);
public static ulong ArrayByteSize<T, P>(IList<T> values, P type);
public static ulong ArrayCode<T, P>(IList<T> values, Stream buf, P type);
public static ulong ArrayDecode<T, P>(IList<T> values, Stream buf, P type);
// Array容器中元素为的ProtoBase时的编/解码
// 由于需要调用ProtoBase的接口,所以T必须做约束,单独做重载
// 内部调用EntryByteSize/Code/Decode
private static ulong ArrayByteSizeWithoutLength<T>(IList<T> values) where T : ProtoBase;
public static ulong ArrayByteSize<T>(IList<T> values) where T : ProtoBase;
public static ulong ArrayCode<T>(IList<T> values, Stream buf) where T : ProtoBase;
public static ulong ArrayDecode<T>(IList<T> values, Stream buf) where T : ProtoBase, new();
// Set容器的编/解码
// 内部调用EntryByteSize/Code/Decode
private static ulong SetByteSizeWithoutLength<T, P>(ISet<T> values, P type);
public static ulong SetByteSize<T, P>(ISet<T> values, P type);
public static ulong SetCode<T, P>(ISet<T> values, Stream buf, P type);
public static ulong SetDecode<T, P>(ISet<T> values, Stream buf, P type);
// Map容器中,每一个元素的编/解码
private static ulong EntryByteSizeWithoutLength<K, V, KP, VP>(K key, V value, KP keyType, VP valueType) where KP : ProtoType where VP : ProtoType;
public static ulong EntryByteSize<K, V, KP, VP>(K key, V value, KP keyType, VP valueType) where KP : ProtoType where VP : ProtoType;
public static ulong EntryCode<K, V, KP, VP>(K key, V value, Stream buf, KP keyType, VP valueType) where KP : ProtoType where VP : ProtoType;
public static ulong EntryDecode<K, V, KP, VP>(ref K key, ref V value, Stream buf, KP keyType, VP valueType) where KP : ProtoType where VP : ProtoType;
// Map容器中元素为的ProtoBase时,每一个元素的编/解码
// 由于需要调用ProtoBase的接口,所以V必须做约束,单独做重载
private static ulong EntryByteSizeWithoutLength<K, V, KP>(K key, V value, KP keyType) where V : ProtoBase where KP : ProtoType;
public static ulong EntryByteSize<K, V, KP>(K key, V value, KP keyType) where V : ProtoBase where KP : ProtoType;
public static ulong EntryCode<K, V, KP>(K key, V value, Stream buf, KP keyType) where V : ProtoBase where KP : ProtoType;
public static ulong EntryDecode<K, V, KP>(ref K key, ref V value, Stream buf, KP keyType) where V : ProtoBase where KP : ProtoType;
// Map容器的编/解码
// 内部调用EntryByteSize/Code/Decode
private static ulong DictionaryByteSizeWithoutLength<K, V, KP, VP>(IDictionary<K, V> values, KP keyType, VP valueType) where KP : ProtoType where VP : ProtoType;
public static ulong DictionaryByteSize<K, V, KP, VP>(IDictionary<K, V> values, KP keyType, VP valueType) where KP : ProtoType where VP : ProtoType;
public static ulong DictionaryCode<K, V, KP, VP>(IDictionary<K, V> values, Stream buf, KP keyType, VP valueType) where KP : ProtoType where VP : ProtoType;
public static ulong DictionaryDecode<K, V, KP, VP>(IDictionary<K, V> values, Stream buf, KP keyType, VP valueType) where KP : ProtoType where VP : ProtoType;
// Map容器中元素为的ProtoBase时的编/解码
// 由于需要调用ProtoBase的接口,所以V必须做约束,单独做重载
// 内部调用EntryByteSize/Code/Decode
private static ulong DictionaryByteSizeWithoutLength<K, V, KP>(IDictionary<K, V> values, KP keyType) where V : ProtoBase where KP : ProtoType;
public static ulong DictionaryByteSize<K, V, KP>(IDictionary<K, V> values, KP keyType) where V : ProtoBase where KP : ProtoType;
public static ulong DictionaryCode<K, V, KP>(IDictionary<K, V> values, Stream buf, KP keyType) where V : ProtoBase where KP : ProtoType;
public static ulong DictionaryDecode<K, V, KP>(IDictionary<K, V> values, Stream buf, KP keyType) where V : ProtoBase where KP : ProtoType;
}
3、ProtoBase
基本雷同C++的ProtoBase,只是没有了 序列化/反序列化 到 char *buf 内存,因为都是Stream流。
// 所有自定义message的基类
public abstract class ProtoBase
{
public ProtoBase();
public abstract ulong ByteSize();
public abstract ulong Code(Stream buf, ulong size);
public abstract ulong Decode(Stream buf, ulong size);
public abstract void Clear();
public abstract void Release();
public bool SerializeToStream(Stream buf, ulong size);
public bool ParseFromStream(Stream buf, ulong size);
};
4、ProtoBitMap
基本雷同c++的ProtoBitMap,除掉这里没法把位图长度作为模板参数,因此只能作为构造函数的参数
public class ProtoBitMap
{
public ProtoBitMap(uint count);
public void SetBit(uint index);
public void ClearBit(uint index);
public bool HasBit(uint index);
public void Clear();
private byte[] m_Bits = null;
};