您的位置:首页 > 编程语言 > Java开发

代码自动生成工具(二)-miniproto的java库实现

2017-06-25 16:32 731 查看
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

本项目暂命名miniproto1.0。

有限的兼容protobuf的语法规则 和 编码规则。包括protobuf2.0和3.0的编码规则。

实现proto结构的序列化、反序列化功能。

代码生成工具,需要boost库支持(主要用了spirit库做文本解析),本人用的是boost.1.64.0。

生成后的代码,仅需要miniproto自身提供的lib(对应c++)、dll(对应c#)、jar(对应java),不需要其他第三方库。

完整项目下载地址(vs工程编译请选择release/win32)

Github项目地址

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

前文介绍了miniproto,这里介绍其java库的实现

1、容器

先吐槽一下java的容器。

java本着一切皆对象的思想,容器中不能放基本数据类型,只能放基本数据类型的封装类对象。

也就是说List<int>是非法的,List<Integer>才合法。

因此miniproto中对于array/set/map这种类型的字段来说,不可避免的会出现拆装箱操作,比较蛋疼。

2、泛型

再吐槽一下java中的泛型。

对于java中的泛型,个人理解,就是发明出来,用来在编译时做参数类型校验的一个玩意。

上面说了,java中一切皆对象,容器中只能放对象。

所以我向一个List中,同时放Integer,和String,在有泛型之前是没问题的。编译不会报错。

导致当你处理容器中的元素的时候,如果不小心把一个Integer强转成String处理了,就会ClassCastException。

于是有了泛型,你向List<Integer>中放一个String对象,编译器就会报错了。

然而也仅此而已了!!!

编译完之后,就没有泛型什么事了!!!

class文件中,已经没有泛型参数的信息了!!!

全部变成Object了,wtf!!!

它既没有像c++的模板机制那样,编译时,不同的泛型参数,直接生成不同的代码再编译。

也没有像c#那样,编译时先生成中间代码,泛型形参是中间代码中的一个占位符。创建实例时,再根据泛型参数的实参类型,生成不同的本地代码。

因此在miniproto中,希望通过泛型,直接进行不同数据类型的编解码功能的重载的意图就没法实现了。

当然我也不可能穷举所有参数类型,分别给出重载。

主要是map,它有key和value两个泛型参数,穷举的话,key的种类×value的种类实在太多,懒的写。

因此只能另外多传入一个类型参数(miniproto中是一个自定义枚举)。

根据这个参数,对T(运行时T已经变成Object)做强转,再去调用不同数据类型的编解码功能的重载。

3、枚举

java的槽点实在有点多。

jdk1.5之前我们只能用 public static final 来定义一系列的常量。1.5增加了枚举。

但是枚举是个类。

你写的 public enum XXX,最终都是被换成了public class XXX extends java.lang.Enum。

你写的枚举中的常量YYY,最终都是被换成了这个类中的 public static final XXX YYY 这样一个XXX的静态对象。

于是当我有一个数字int,我要转成对应的枚举项的时候,只能通过一个这个class XXX中的一个static方法,switch...case返回对应的YYY。

而static方法,意味着不能多态。也就是说我不能通过调用基类的接口,得到子类的实现,来完成数字转枚举。必须拿到具体的枚举类型,才能完成转化。

因此,在编解码功能的底层实现中,对于枚举类型,只能当成一个Integer处理。

proto的message中的枚举成员,或者枚举类型的容器,只能实现成Integer,或者是List<Integer>。

在message的getter/setter中,再调用具体的枚举类型做相应转化。

4、ProtoTool

基本雷同C++的ProtoTool(除掉上面吐槽的几点)。针对不同数据类型,给出一系列的编解码功能实现。

下面给出ProtoTool代码,对照代码做出注释说明。具体实现就不贴了,可以去下载完整项目。

package common.miniproto;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Iterator;

public class ProtoTool {
// Zigzag编/解码
public static int Zigzag(int value);
public static long Zigzag(long value);
public static int DeZigzag(int value);
public static long DeZigzag(long value);

// Number编/解码
public static int NumberByteSize(int value);
public static int NumberByteSize(long value);
public static int NumberByteSize(float value);
public static int NumberByteSize(double value);
public static void NumberCode(int value, OutputStream buf) throws IOException;
public static void NumberCode(long value, OutputStream buf) throws IOException;
public static void NumberCode(float value, OutputStream buf) throws IOException;
public static void NumberCode(double value, OutputStream buf) throws IOException;
public static int NumberDecode(int value, InputStream buf) throws IOException;
public static long NumberDecode(long value, InputStream buf) throws IOException;
public static float NumberDecode(float value, InputStream buf) throws IOException;
public static double NumberDecode(double value, InputStream buf) throws IOException;

// proto key编/解码
public static int KeyByteSize(int num, ProtoDefine.ProtoWireType type);
public static void KeyCode(int num, ProtoDefine.ProtoWireType type, OutputStream buf) throws IOException;
public static int KeyDecode(InputStream buf) throws IOException;

// 未知字段解码
public static void UnknownDecode(ProtoDefine.ProtoWireType type, InputStream buf) throws IOException;

// 单个字段编/解码
public static int BoolByteSize(Boolean value);
public static void BoolCode(Boolean value, OutputStream buf) throws IOException;
public static Boolean BoolDecode(InputStream buf) throws IOException;

public static int Int32ByteSize(Integer value);
public static void Int32Code(Integer value, OutputStream buf) throws IOException;
public static Integer Int32Decode(InputStream buf) throws IOException;

public static int SInt32ByteSize(Integer value);
public static void SInt32Code(Integer value, OutputStream buf) throws IOException;
public static Integer SInt32Decode(InputStream buf) throws IOException;

public static int UInt32ByteSize(Integer value);
public static void UInt32Code(Integer value, OutputStream buf) throws IOException;
public static Integer UInt32Decode(InputStream buf) throws IOException;

public static int Int64ByteSize(Long value);
public static void Int64Code(Long value, OutputStream buf) throws IOException;
public static Long Int64Decode(InputStream buf) throws IOException;

public static int SInt64ByteSize(Long value);
public static void SInt64Code(Long value, OutputStream buf) throws IOException;
public static Long SInt64Decode(InputStream buf) throws IOException;

public static int UInt64ByteSize(Long value);
public static void UInt64Code(Long value, OutputStream buf) throws IOException;
public static Long UInt64Decode(InputStream buf) throws IOException;

public static int EnumByteSize(Integer value);
public static void EnumCode(Integer value, OutputStream buf) throws IOException;
public static Integer EnumDecode(InputStream buf) throws IOException;

public static int FloatByteSize(Float value);
public static void FloatCode(Float value, OutputStream buf) throws IOException;
public static Float FloatDecode(InputStream buf) throws IOException;

public static int DoubleByteSize(Double value);
public static void DoubleCode(Double value, OutputStream buf) throws IOException;
public static Double DoubleDecode(InputStream buf) throws IOException;

public static int StringByteSize(String value) throws IOException;
public static void StringCode(String value, OutputStream buf) throws IOException;
public static String StringDecode(InputStream buf) throws IOException;

public static <M extends ProtoBase> int MessageByteSize(M value) throws IOException;
public static <M extends ProtoBase> void MessageCode(M value, OutputStream buf) throws IOException;
public static <M extends ProtoBase> M MessageDecode(InputStream buf, Class<M> cls) throws IOException;

// Array,Set容器中,每一个元素的编/解码
private static <T> int EntryByteSize(T value, ProtoDefine.ProtoFieldType type) throws IOException;
private static <T> void EntryCode(T value, OutputStream buf, ProtoDefine.ProtoFieldType type) throws IOException;
private static <T> void EntryDecode(Collection<T> values, InputStream buf, ProtoDefine.ProtoFieldType type) throws IOException;
private static <T extends ProtoBase> void EntryDecode(Collection<T> values, InputStream buf, ProtoDefine.ProtoFieldType type, Class<T> cls) throws IOException;

// Array容器的编/解码
// 内部调用EntryByteSize/Code/Decode
private static <T> int ArrayByteSizeWithoutLength(List<T> values, ProtoDefine.ProtoFieldType type) throws IOException;
public static <T> int ArrayByteSize(List<T> values, ProtoDefine.ProtoFieldType type) throws IOException;
public static <T> void ArrayCode(List<T> values, OutputStream buf, ProtoDefine.ProtoFieldType type) throws IOException;
public static <T> void ArrayDecode(List<T> values, InputStream buf, ProtoDefine.ProtoFieldType type) throws IOException;
public static <T extends ProtoBase> void ArrayDecode(List<T> values, InputStream buf, ProtoDefine.ProtoFieldType type, Class<T> cls) throws IOException;

// Set容器的编/解码
// 内部调用EntryByteSize/Code/Decode
private static <T> int SetByteSizeWithoutLength(Set<T> values, ProtoDefine.ProtoFieldType type) throws IOException;
public static <T> int SetByteSize(Set<T> values, ProtoDefine.ProtoFieldType type) throws IOException;
public static <T> void SetCode(Set<T> values, OutputStream buf, ProtoDefine.ProtoFieldType type) throws IOException;
public static <T> void SetDecode(Set<T> values, InputStream buf, ProtoDefine.ProtoFieldType type) throws IOException;

// Map容器中,每一个元素的编/解码
private static <K, V> int EntryByteSizeWithoutLength(K key, V value, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType) throws IOException;
public static <K, V> int EntryByteSize(K key, V value, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType) throws IOException;
public static <K, V> void EntryCode(K key, V value, OutputStream buf, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType) throws IOException;
public static <K, V> void EntryDecode(Map<K, V> values, InputStream buf, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType) throws IOException;
public static <K, V extends ProtoBase> void EntryDecode(Map<K, V> values, InputStream buf, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType, Class<V> cls) throws IOException;

// Map容器的编/解码
// 内部调用EntryByteSize/Code/Decode
private static <K, V> int MapByteSizeWithoutLength(Map<K, V> values, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType) throws IOException;
public static <K, V> int MapByteSize(Map<K, V> values, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType) throws IOException;
public static <K, V> void MapCode(Map<K, V> values, OutputStream buf, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType) throws IOException;
public static <K, V> void MapDecode(Map<K, V> values, InputStream buf, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType) throws IOException;
public static <K, V extends ProtoBase> void MapDecode(Map<K, V> values, InputStream buf, ProtoDefine.ProtoFieldType keyType, ProtoDefine.ProtoFieldType valueType, Class<V> cls) throws IOException;
}


5、ProtoBase

基本雷同C++的ProtoBase,多一个toString接口,方便调式时显示message的内容信息

package common.miniproto;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;

public abstract class ProtoBase {

public ProtoBase();

public abstract int ByteSize() throws IOException;
public abstract void Code(OutputStream buf, int size) throws IOException;
public abstract void Decode(InputStream buf, int size) throws IOException;

public abstract void Clear();
public abstract void Release();

public boolean SerializeToStream(OutputStream buf, int size);
public boolean ParseFromStream(InputStream buf, int size);

public String toString();
}

6、ProtoBitMap

基本雷同c++的ProtoBitMap,位图长度作为构造函数的参数,多一个toString接口。

package common.miniproto;

public class ProtoBitMap {

public ProtoBitMap(int count);

public void SetBit(int index);
public void ClearBit(int index);
public boolean HasBit(int index);

public void Clear();

private byte[] m_Bits = null;

public String toString();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息