From d4b70d942c639fc340c82a3ffd29f261ae65df36 Mon Sep 17 00:00:00 2001 From: AICells <1713699517@qq.com> Date: Fri, 19 Jul 2019 00:54:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 初始化提交 --- README.md | 9 + ebin/genProto.app | 9 + proto/0_test.msg | 24 +++ rebar.config | 2 + src/genProto.app.src | 13 ++ src/protoCode.erl | 247 ++++++++++++++++++++++ src/protoField.erl | 91 +++++++++ src/protoGen.erl | 365 +++++++++++++++++++++++++++++++++ src/protoParse.erl | 475 +++++++++++++++++++++++++++++++++++++++++++ test/genProto.bat | 1 + test/protoMsg.erl | 285 ++++++++++++++++++++++++++ test/protoMsg.hrl | 28 +++ test/remake.bat | 3 + test/test.erl | 319 +++++++++++++++++++++++++++++ 14 files changed, 1871 insertions(+) create mode 100644 README.md create mode 100644 ebin/genProto.app create mode 100644 proto/0_test.msg create mode 100644 rebar.config create mode 100644 src/genProto.app.src create mode 100644 src/protoCode.erl create mode 100644 src/protoField.erl create mode 100644 src/protoGen.erl create mode 100644 src/protoParse.erl create mode 100644 test/genProto.bat create mode 100644 test/protoMsg.erl create mode 100644 test/protoMsg.hrl create mode 100644 test/remake.bat create mode 100644 test/test.erl diff --git a/README.md b/README.md new file mode 100644 index 0000000..9dc08b2 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +genProto +===== + +An OTP library + +Build +----- + + $ rebar3 compile diff --git a/ebin/genProto.app b/ebin/genProto.app new file mode 100644 index 0000000..d38ca64 --- /dev/null +++ b/ebin/genProto.app @@ -0,0 +1,9 @@ +{application,genProto, + [{description,"An OTP library"}, + {vsn,"0.1.0"}, + {registered,[]}, + {applications,[kernel,stdlib]}, + {env,[]}, + {modules,[protoCode,protoField,protoGen,protoParse]}, + {licenses,["Apache 2.0"]}, + {links,[]}]}. diff --git a/proto/0_test.msg b/proto/0_test.msg new file mode 100644 index 0000000..de90830 --- /dev/null +++ b/proto/0_test.msg @@ -0,0 +1,24 @@ +%% 测试协议 注释可以用 % 也可以用 // +test { + string aa; +} + +// phoneNumber info +phoneNumber{ + test number; //电话号码 + int32 type; //类型 +} + +%% person info +person{ + string name; %% 名字 + int32 id; %% id + string email; //邮箱 + list[phoneNumber] phone; // list of phoneNumber +} + +%% addressBook info +addressBook { + list[person] person; + list[person] other; +} diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..f618f3e --- /dev/null +++ b/rebar.config @@ -0,0 +1,2 @@ +{erl_opts, [debug_info]}. +{deps, []}. \ No newline at end of file diff --git a/src/genProto.app.src b/src/genProto.app.src new file mode 100644 index 0000000..d56af6f --- /dev/null +++ b/src/genProto.app.src @@ -0,0 +1,13 @@ +{application, genProto, + [{description, "An OTP library"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, + [kernel, + stdlib + ]}, + {env, []}, + {modules, []}, + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/src/protoCode.erl b/src/protoCode.erl new file mode 100644 index 0000000..2ca57d1 --- /dev/null +++ b/src/protoCode.erl @@ -0,0 +1,247 @@ +-module(protoCode). +-compile(nowarn_unused_function). + + +-export([encode/1, decode/1, encodeRec/1, decodeRec/2]). + +-define(bool(V), (case V of true -> <<1:8>>; _ -> <<0:8>> end)). +-define(int8(V), <>). +-define(uint8(V), <>). +-define(int16(V), <>). +-define(uint16(V), <>). +-define(int32(V), <>). +-define(uint32(V), <>). +-define(int64(V), <>). +-define(uint64(V), <>). +-define(float(V), <>). +-define(double(V), <>). +-define(string(V), (string(V))). +-define(record(V), (case V of undefined -> [<<0:8>>]; V -> [<<1:8>>, encodeRec(V)] end)). +-define(list_bool(List), [<<(length(List)):16/big>>, [?bool(V) || V <- List]]). +-define(list_int8(List), [<<(length(List)):16/big>>, [?int8(V) || V <- List]]). +-define(list_uint8(List), [<<(length(List)):16/big>>, [?uint8(V) || V <- List]]). +-define(list_int16(List), [<<(length(List)):16/big>>, [?int16(V) || V <- List]]). +-define(list_uint16(List), [<<(length(List)):16/big>>, [?uint16(V) || V <- List]]). +-define(list_int32(List), [<<(length(List)):16/big>>, [?int32(V) || V <- List]]). +-define(list_uint32(List), [<<(length(List)):16/big>>, [?uint32(V) || V <- List]]). +-define(list_int64(List), [<<(length(List)):16/big>>, [?int64(V) || V <- List]]). +-define(list_uint64(List), [<<(length(List)):16/big>>, [?uint64(V) || V <- List]]). +-define(list_float(List), [<<(length(List)):16/big>>, [?float(V) || V <- List]]). +-define(list_double(List), [<<(length(List)):16/big>>, [?double(V) || V <- List]]). +-define(list_string(List), [<<(length(List)):16/big>>, [string(V) || V <- List]]). +-define(list_record(List), [<<(length(List)):16/big>>, [encodeRec(V) || V <- List]]). + +string(Str) when is_binary(Str) -> + [<<(byte_size(Str)):16/big>>, Str]; +string(Str) -> + Str2 = unicode:characters_to_binary(Str, utf8), + [<<(byte_size(Str2)):16/big>>, Str2]. + +encode(Record) -> + MsgBin = encodeRec(Record), + MsgId = getMsgId(element(1, Record)), + [<>, MsgBin]. + +decode(Bin) -> + <> = Bin, + SchList = getMsgSchema(MsgId), + {<<>>, ResultList} = decodeField(SchList, MsgBin, [getMsgType(MsgId)]), + list_to_tuple(ResultList). + +decodeRec(RecordName, Bin) -> + SchList = getMsgSchema(RecordName), + {LeftBin, Result} = decodeField(SchList, Bin, [RecordName]), + {LeftBin, list_to_tuple(Result)}. + +decodeField([], LeftBin, Result) -> + {LeftBin, lists:reverse(Result)}; +decodeField([Type | SchList], MsgBin, Result) -> + case Type of + int32 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint32 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + string -> + <> = MsgBin, + decodeField(SchList, LeftBin, [StrBin | Result]); + int16 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint16 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + int8 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint8 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + int64 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint64 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + bool -> + <> = MsgBin, + case Bool =:= 1 of + true -> + decodeField(SchList, LeftBin, [true | Result]); + _ -> + decodeField(SchList, LeftBin, [false | Result]) + end; + falat -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Float | Result]); + double -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Float | Result]); + {list, int32} -> + <> = MsgBin, + {LeftBin, RetList} = deInt32List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint32} -> + <> = MsgBin, + {LeftBin, RetList} = deUint32List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, int16} -> + <> = MsgBin, + {LeftBin, RetList} = deInt16List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint16} -> + <> = MsgBin, + {LeftBin, RetList} = deUint16List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, int8} -> + <> = MsgBin, + {LeftBin, RetList} = deInt8List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint8} -> + <> = MsgBin, + {LeftBin, RetList} = deUint8List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, string} -> + <> = MsgBin, + {LeftBin, RetList} = deStringList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, int64} -> + <> = MsgBin, + {LeftBin, RetList} = deInt64List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint64} -> + <> = MsgBin, + {LeftBin, RetList} = deUint64List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, bool} -> + <> = MsgBin, + {LeftBin, RetList} = deBoolList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, flat} -> + <> = MsgBin, + {LeftBin, RetList} = deFloatList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, double} -> + <> = MsgBin, + {LeftBin, RetList} = deDoubleList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, RecordName} -> + <> = MsgBin, + {LeftBin, RetList} = deRecordList(Len, RecordName, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + RecordName -> + <> = MsgBin, + case IsUndef of + 0 -> + decodeField(SchList, LeftBin, [undefined | Result]); + _ -> + SubSchList = getMsgSchema(RecordName), + {SubLeftBin, SubResultList} = decodeField(SubSchList, LeftBin, [RecordName]), + decodeField(SchList, SubLeftBin, [list_to_tuple(SubResultList) | Result]) + end + end. + +deBoolList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deBoolList(N, <>, RetList) -> + case Bool =:= 1 of + true -> + deBoolList(N - 1, LeftBin, [true | RetList]); + _ -> + deBoolList(N - 1, LeftBin, [false | RetList]) + end. +deInt8List(0, MsgBin, RetList) -> + {MsgBin, RetList}; +deInt8List(N, <>, RetList) -> + deInt8List(N - 1, LeftBin, [Int | RetList]). + +deUint8List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint8List(N, <>, RetList) -> + deUint8List(N - 1, LeftBin, [Int | RetList]). + +deInt16List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deInt16List(N, <>, RetList) -> + deInt16List(N - 1, LeftBin, [Int | RetList]). + +deUint16List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint16List(N, <>, RetList) -> + deUint16List(N - 1, LeftBin, [Int | RetList]). + +deInt32List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deInt32List(N, <>, RetList) -> + deInt32List(N - 1, LeftBin, [Int | RetList]). + +deUint32List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint32List(N, <>, RetList) -> + deUint32List(N - 1, LeftBin, [Int | RetList]). + +deInt64List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deInt64List(N, <>, RetList) -> + deInt64List(N - 1, LeftBin, [Int | RetList]). + +deUint64List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint64List(N, <>, RetList) -> + deUint64List(N - 1, LeftBin, [Int | RetList]). + +deFloatList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deFloatList(N, <>, RetList) -> + deFloatList(N - 1, LeftBin, [Float | RetList]). + +deDoubleList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deDoubleList(N, <>, RetList) -> + deDoubleList(N - 1, LeftBin, [Float | RetList]). + +deStringList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deStringList(N, <>, RetList) -> + deStringList(N - 1, LeftBin, [StrBin | RetList]). + +deRecordList(0, _RecordName, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deRecordList(N, RecordName, MsgBin, RetList) -> + {LeftBin, Tuple} = decodeRec(RecordName, MsgBin), + deRecordList(N - 1, RecordName, LeftBin, [Tuple | RetList]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%% 防止编译报错占位 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +encodeRec(_) -> + ok. +getMsgId(_) -> + ok. +getMsgType(_) -> + ok. +getMsgSchema(_) -> + ok. + + + diff --git a/src/protoField.erl b/src/protoField.erl new file mode 100644 index 0000000..c5c8a8c --- /dev/null +++ b/src/protoField.erl @@ -0,0 +1,91 @@ +-module(protoField). + +-compile([export_all, nowarn_export_all]). + +-define(TypeList, [ + "bool", + "int8", + "uint8", + "int16", + "uint16", + "int32", + "uint32", + "int64", + "uint64", + "float", + "double", + "string" +]). +-define(TypeValue, [ + {"bool", "false", "boolean"}, + {"int8", "0", "int8()"}, + {"uint8", "0", "uint8()"}, + {"int16", "0", "int16()"}, + {"uint16", "0", "uint16()"}, + {"int32", "0", "int32()"}, + {"uint32", "0", "uint32()"}, + {"int64", "0", "int64()"}, + {"uint64", "0", "uint64()"}, + {"float", "0.0", "float()"}, + {"double", "0.0", "flaot"}, + {"string", "\"\"", "string()"} +]). + +getSchemaInfo(TypeStr) -> + case lists:member(TypeStr, ?TypeList) of + true -> + erlang:list_to_atom(TypeStr); + _ -> + case TypeStr of + "list[" ++ LeftStr -> + [SubTypeStr | _] = re:split(LeftStr, "\\]", [{return, list}]), + {list, erlang:list_to_atom(SubTypeStr)}; + _ -> + erlang:list_to_atom(TypeStr) + end + end. + +builtRecStr({TypeStr, NameStr}) -> + case lists:keyfind(TypeStr, 1, ?TypeValue) of + {TypeStr, DefValueStr, DefTypeStr} -> + NameStr ++ " = " ++ DefValueStr ++ " :: " ++ DefTypeStr ++ "\n\t"; + _ -> + case TypeStr of + "list[" ++ LeftStr -> + [SubTypeStr | _] = re:split(LeftStr, "\\]", [{return, list}]), + case lists:keyfind(SubTypeStr, 1, ?TypeValue) of + {SubTypeStr, _DefSubValueStr, DefSubTypeStr} -> + NameStr ++ " = [] " ++ " :: [" ++ DefSubTypeStr ++ "]\n\t"; + _ -> + NameStr ++ " = [] " ++ " :: [#" ++ SubTypeStr ++ "{}]\n\t" + end; + _ -> + NameStr ++ " = undefined " ++ " :: #" ++ TypeStr ++ "{}\n\t" + end + end. + +builtPackStr(TypeStr) -> + case lists:member(TypeStr, ?TypeList) of + true -> + "?" ++ TypeStr ++ "("; + _ -> + case TypeStr of + "list[" ++ LeftStr -> + [SubTypeStr | _] = re:split(LeftStr, "\\]", [{return, list}]), + SubStr = + case lists:member(SubTypeStr, ?TypeList) of + true -> + SubTypeStr; + _ -> + "record" + end, + "?list_" ++ SubStr ++ "("; + _ -> + "?record(" + end + end. + + + + + diff --git a/src/protoGen.erl b/src/protoGen.erl new file mode 100644 index 0000000..1ca9207 --- /dev/null +++ b/src/protoGen.erl @@ -0,0 +1,365 @@ +-module(protoGen). + +-export([ + convertFile/1 + , convert/1 + , convertDir/0 + , convertDir/1 + , convertDir/3 +]). + +protoHrlHeader() -> +"-opaque int8() :: -128..127. +-opaque int16() :: -32768..32767. +-opaque int32() :: -2147483648..2147483647. +-opaque int64() :: -9223372036854775808..9223372036854775807. +-opaque uint8() :: 0..255. +-opaque uint16() :: 0..65536. +-opaque uint32() :: 0..4294967295. +-opaque uint64() :: 0..18446744073709551615. +-opaque single() :: float(). +-opaque double() :: float().\n\n". + +protoErlHeader() -> +"-module(protoMsg).\n\n +-export([encode/1, decode/1, encodeRec/1, decodeRec/2]). + +-define(bool(V), (case V of true -> <<1:8>>; _ -> <<0:8>> end)). +-define(int8(V), <>). +-define(uint8(V), <>). +-define(int16(V), <>). +-define(uint16(V), <>). +-define(int32(V), <>). +-define(uint32(V), <>). +-define(int64(V), <>). +-define(uint64(V), <>). +-define(float(V), <>). +-define(double(V), <>). +-define(string(V), (string(V))). +-define(record(V), (case V of undefined -> [<<0:8>>]; V -> [<<1:8>>, encodeRec(V)] end)). +-define(list_bool(List), [<<(length(List)):16/big>>, [?bool(V) || V <- List]]). +-define(list_int8(List), [<<(length(List)):16/big>>, [?int8(V) || V <- List]]). +-define(list_uint8(List), [<<(length(List)):16/big>>, [?uint8(V) || V <- List]]). +-define(list_int16(List), [<<(length(List)):16/big>>, [?int16(V) || V <- List]]). +-define(list_uint16(List), [<<(length(List)):16/big>>, [?uint16(V) || V <- List]]). +-define(list_int32(List), [<<(length(List)):16/big>>, [?int32(V) || V <- List]]). +-define(list_uint32(List), [<<(length(List)):16/big>>, [?uint32(V) || V <- List]]). +-define(list_int64(List), [<<(length(List)):16/big>>, [?int64(V) || V <- List]]). +-define(list_uint64(List), [<<(length(List)):16/big>>, [?uint64(V) || V <- List]]). +-define(list_float(List), [<<(length(List)):16/big>>, [?float(V) || V <- List]]). +-define(list_double(List), [<<(length(List)):16/big>>, [?double(V) || V <- List]]). +-define(list_string(List), [<<(length(List)):16/big>>, [string(V) || V <- List]]). +-define(list_record(List), [<<(length(List)):16/big>>, [encodeRec(V) || V <- List]]). + +string(Str) when is_binary(Str) -> + [<<(byte_size(Str)):16/big>>, Str]; +string(Str) -> + Str2 = unicode:characters_to_binary(Str, utf8), + [<<(byte_size(Str2)):16/big>>, Str2]. + +encode(Record) -> + MsgBin = encodeRec(Record), + MsgId = getMsgId(element(1, Record)), + [<>, MsgBin]. + +decode(Bin) -> + <> = Bin, + SchList = getMsgSchema(MsgId), + {<<>>, ResultList} = decodeField(SchList, MsgBin, [getMsgType(MsgId)]), + list_to_tuple(ResultList). + +decodeRec(RecordName, Bin) -> + SchList = getMsgSchema(RecordName), + {LeftBin, Result} = decodeField(SchList, Bin, [RecordName]), + {LeftBin, list_to_tuple(Result)}. + +decodeField([], LeftBin, Result) -> + {LeftBin, lists:reverse(Result)}; +decodeField([Type | SchList], MsgBin, Result) -> + case Type of + int32 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint32 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + string -> + <> = MsgBin, + decodeField(SchList, LeftBin, [StrBin | Result]); + int16 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint16 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + int8 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint8 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + int64 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint64 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + bool -> + <> = MsgBin, + case Bool =:= 1 of + true -> + decodeField(SchList, LeftBin, [true | Result]); + _ -> + decodeField(SchList, LeftBin, [false | Result]) + end; + falat -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Float | Result]); + double -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Float | Result]); + {list, int32} -> + <> = MsgBin, + {LeftBin, RetList} = deInt32List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint32} -> + <> = MsgBin, + {LeftBin, RetList} = deUint32List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, int16} -> + <> = MsgBin, + {LeftBin, RetList} = deInt16List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint16} -> + <> = MsgBin, + {LeftBin, RetList} = deUint16List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, int8} -> + <> = MsgBin, + {LeftBin, RetList} = deInt8List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint8} -> + <> = MsgBin, + {LeftBin, RetList} = deUint8List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, string} -> + <> = MsgBin, + {LeftBin, RetList} = deStringList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, int64} -> + <> = MsgBin, + {LeftBin, RetList} = deInt64List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint64} -> + <> = MsgBin, + {LeftBin, RetList} = deUint64List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, bool} -> + <> = MsgBin, + {LeftBin, RetList} = deBoolList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, flat} -> + <> = MsgBin, + {LeftBin, RetList} = deFloatList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, double} -> + <> = MsgBin, + {LeftBin, RetList} = deDoubleList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, RecordName} -> + <> = MsgBin, + {LeftBin, RetList} = deRecordList(Len, RecordName, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + RecordName -> + <> = MsgBin, + case IsUndef of + 0 -> + decodeField(SchList, LeftBin, [undefined | Result]); + _ -> + SubSchList = getMsgSchema(RecordName), + {SubLeftBin, SubResultList} = decodeField(SubSchList, LeftBin, [RecordName]), + decodeField(SchList, SubLeftBin, [list_to_tuple(SubResultList) | Result]) + end + end. + +deBoolList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deBoolList(N, <>, RetList) -> + case Bool =:= 1 of + true -> + deBoolList(N - 1, LeftBin, [true | RetList]); + _ -> + deBoolList(N - 1, LeftBin, [false | RetList]) + end. +deInt8List(0, MsgBin, RetList) -> + {MsgBin, RetList}; +deInt8List(N, <>, RetList) -> + deInt8List(N - 1, LeftBin, [Int | RetList]). + +deUint8List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint8List(N, <>, RetList) -> + deUint8List(N - 1, LeftBin, [Int | RetList]). + +deInt16List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deInt16List(N, <>, RetList) -> + deInt16List(N - 1, LeftBin, [Int | RetList]). + +deUint16List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint16List(N, <>, RetList) -> + deUint16List(N - 1, LeftBin, [Int | RetList]). + +deInt32List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deInt32List(N, <>, RetList) -> + deInt32List(N - 1, LeftBin, [Int | RetList]). + +deUint32List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint32List(N, <>, RetList) -> + deUint32List(N - 1, LeftBin, [Int | RetList]). + +deInt64List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deInt64List(N, <>, RetList) -> + deInt64List(N - 1, LeftBin, [Int | RetList]). + +deUint64List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint64List(N, <>, RetList) -> + deUint64List(N - 1, LeftBin, [Int | RetList]). + +deFloatList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deFloatList(N, <>, RetList) -> + deFloatList(N - 1, LeftBin, [Float | RetList]). + +deDoubleList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deDoubleList(N, <>, RetList) -> + deDoubleList(N - 1, LeftBin, [Float | RetList]). + +deStringList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deStringList(N, <>, RetList) -> + deStringList(N - 1, LeftBin, [StrBin | RetList]). + +deRecordList(0, _RecordName, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deRecordList(N, RecordName, MsgBin, RetList) -> + {LeftBin, Tuple} = decodeRec(RecordName, MsgBin), + deRecordList(N - 1, RecordName, LeftBin, [Tuple | RetList]).\n\n". + +genSchema({FieldType, _FieldName}, AccList) -> + SchemaStr = protoField:getSchemaInfo(FieldType), + [SchemaStr | AccList]. + +genMsgHrl(FieldInfo, {Index, AccList}) -> + TemStr = + case Index of + 1 -> + ""; + _ -> + "," + end, + RecStr = TemStr ++ protoField:builtRecStr(FieldInfo), + {Index - 1, [RecStr | AccList]}. + +genEncodeRec({MsgName, _MsgId, FieldList}) -> + FieldLen = length(FieldList), + FunHead = + fun(_, {Index, StrAcc}) -> + {Index - 1, ", V" ++ integer_to_list(Index) ++ StrAcc} + end, + {_, TemStr} = lists:foldr(FunHead, {FieldLen, "}) ->\n\t"}, FieldList), + HeadStr = "encodeRec({" ++ MsgName ++ TemStr, + + FunBody = + fun({FieldType, _FieldName}, {Index, PStrAcc}) -> + TemV = "V" ++ integer_to_list(Index), + PackStr = protoField:builtPackStr(FieldType) ++ TemV ++ ")", + case Index == 1 of + true -> + {Index - 1, PackStr ++ PStrAcc}; + _ -> + {Index - 1, ", " ++ PackStr ++ PStrAcc} + + end + end, + {_, LastStr} = lists:foldr(FunBody, {FieldLen, ""}, FieldList), + case FieldLen > 0 of + true -> + HeadStr ++ "[" ++ LastStr ++ "];\n"; + _ -> + HeadStr ++ "[];\n" + end. + +convertFile(File) -> + protoParse:parseFile(File). + +convert([ProtoDir, HrlDir, ErlDir]) -> + convertDir(atom_to_list(ProtoDir), atom_to_list(HrlDir), atom_to_list(ErlDir)). + +convertDir() -> + convertDir("./", "./", "./"). +convertDir(ProtoDir) -> + convertDir(ProtoDir, "./", "./"). +convertDir(ProtoDir, HrlDir, ErlDir) -> + FunRead = + fun(File, Acc) -> + case filename:extension(File) == ".msg" of + true -> + io:format("Convert proto msg file: ~s ~n", [File]), + BaseName = filename:basename(File, ".msg"), + [ModIndex | _ModName] = re:split(BaseName, "_"), + Index = binary_to_integer(ModIndex), + put(messageid, Index * 1000 + 1), + SProto = protoParse:parseFile(File), + [SProto | Acc]; + _ -> + Acc + + end + end, + %% 下面文件帅选并不能准确的帅选出文件名为.msg结尾的文件 在FunRead函数中纠正处理一下 + SProtoListOfList = filelib:fold_files(ProtoDir, "\\.msg", true, FunRead, []), + SProtoList = lists:append(SProtoListOfList), + SortedSProtoList = lists:sort(fun({_Name1, MessageId1, _FieldList1}, {_Name2, MessageId2, _FieldList2}) -> + MessageId1 > MessageId2 end, SProtoList), + FunSpell = + fun({MsgName, MsgId, FieldList} = MsgInfo, {MsgHrlAcc, MsgTypeAcc, MsgIdAcc, MsgEndStr, MsgSchemaAcc}) -> + TypeStr = "getMsgType(" ++ integer_to_list(MsgId) ++ ")->\n\t" ++ MsgName ++ ";\n", + IdStr = "getMsgId(" ++ MsgName ++ ")->\n\t" ++ integer_to_list(MsgId) ++ ";\n", + EncodeStr = genEncodeRec(MsgInfo), + Len = erlang:length(FieldList), + SchemaList = lists:foldr(fun genSchema/2, [], FieldList), + SchemaStr = "getMsgSchema(" ++ MsgName ++ ")->\n\t" ++ "getMsgSchema(" ++ integer_to_list(MsgId) ++ ");\n" ++ + "getMsgSchema(" ++ integer_to_list(MsgId) ++ ")->\n\t" ++ io_lib:format("~p", [SchemaList]) ++ ";\n", + {_, LastFieldStr} = lists:foldr(fun genMsgHrl/2, {Len, ""}, FieldList), + HrlStr = "-record(" ++ MsgName ++ " ,{\n\t" ++ LastFieldStr ++ "}).\n", + {[HrlStr | MsgHrlAcc], [TypeStr | MsgTypeAcc], [IdStr | MsgIdAcc], [EncodeStr | MsgEndStr], [SchemaStr | MsgSchemaAcc]} + end, + {MsgHrlStr, MsgTypeStr, MsgIdStr, MsgEndStr, MsgSchemaStr} = lists:foldl(FunSpell, {[], ["getMsgType(_) ->\n\tundefined.\n\n"], ["getMsgId(_) ->\n\t0.\n\n"], ["encodeRec(_) ->\n\t[].\n\n"], ["getMsgSchema(_) ->\n\t[].\n\n"]}, SortedSProtoList), + + ModStr = protoErlHeader(), + OutputStr = ModStr ++ MsgTypeStr ++ MsgIdStr ++ MsgEndStr ++ MsgSchemaStr, + HrlFilename = do_write_hrl(HrlDir, protoMsg, protoHrlHeader() ++ MsgHrlStr), + ErlFilename = do_write_erl(ErlDir, protoMsg, OutputStr), + + io:format("protoConvert hrl dir : ~s ~n", [HrlDir]), + io:format("protoConvert erl dir : ~s ~n", [ErlDir]), + io:format("protoConvert to hrl file ~s succ.~n", [HrlFilename]), + io:format("protoConvert to erl file ~s succ.~n", [ErlFilename]), + ok. + +do_write_hrl(OutDir, Mod, Str) when is_list(OutDir) -> + Filename = filename:join([OutDir, atom_to_list(Mod) ++ ".hrl"]), + ok = file:write_file(Filename, Str), + Filename. + +do_write_erl(OutDir, Mod, Str) when is_list(OutDir) -> + Filename = filename:join([OutDir, atom_to_list(Mod) ++ ".erl"]), + ok = file:write_file(Filename, Str), + Filename. \ No newline at end of file diff --git a/src/protoParse.erl b/src/protoParse.erl new file mode 100644 index 0000000..b9e409b --- /dev/null +++ b/src/protoParse.erl @@ -0,0 +1,475 @@ +-module(protoParse). +-compile(nowarn_unused_function). + +-export([ + parseParse/1 + , parseFile/1 +]). + +-define(p_anything, true). +-define(p_charclass, true). +-define(p_choose, true). +-define(p_label, true). +-define(p_not, true). +-define(p_one_or_more, true). +-define(p_optional, true). +-define(p_scan, true). +-define(p_seq, true). +-define(p_string, true). +-define(p_zero_or_more, true). + +-spec parseFile(file:name()) -> any(). +parseFile(Filename) -> + case file:read_file(Filename) of + {ok, Bin} -> + parseParse(Bin); + Err -> Err + end. + +-spec parseParse(binary() | list()) -> any(). +parseParse(List) when is_list(List) -> + parseParse(unicode:characters_to_binary(List)); +parseParse(Input) when is_binary(Input) -> + Result = + case 'all'(Input, {{line, 1}, {column, 1}}) of + {AST, <<>>, _Index} -> + AST; + Any -> + Any + end, + release_memo(), + Result. + +-spec 'all'(input(), index()) -> parse_result(). +'all'(Input, Index) -> + p(Input, Index, 'all', + fun(I, D) -> + (p_seq([fun 'blank0'/2, p_zero_or_more(p_seq([fun 'protocol'/2, fun 'blank0'/2]))]))(I, D) + end, + fun(Node, _Idx) -> + [_ | [T]] = Node, DataList = [H || [H | _] <- T], DataList + end). + +-spec 'protocol'(input(), index()) -> parse_result(). +'protocol'(Input, Index) -> + p(Input, Index, 'protocol', + fun(I, D) -> + (p_seq([p_label('name', fun 'name'/2), fun 'blank0'/2, p_label('structural', fun 'structural'/2)]))(I, D) + end, + fun(Node, _Idx) -> + Name = binary_to_list(iolist_to_binary(proplists:get_value(name, Node))), + Structural = proplists:get_value(structural, Node), + MsgId = get(messageid), + put(messageid, MsgId + 1), + {Name, MsgId, Structural} + end). + +-spec 'structural'(input(), index()) -> parse_result(). +'structural'(Input, Index) -> + p(Input, Index, 'structural', + fun(I, D) -> + (p_seq([p_string(<<"{">>), fun 'blank0'/2, p_zero_or_more(p_seq([fun 'field'/2, fun 'blank0'/2])), p_string(<<"}">>)]))(I, D) + end, + fun(Node, _Idx) -> + [_, _, List, _] = Node, [H || [H | _] <- List] + end). + +-spec 'field'(input(), index()) -> parse_result(). +'field'(Input, Index) -> + p(Input, Index, 'field', + fun(I, D) -> + (p_seq([p_label('datatype', fun 'typename'/2), fun 'blanks'/2, p_label('name', fun 'name'/2), fun 'blank0'/2, p_string(<<";">>)]))(I, D) + end, + fun(Node, _Idx) -> + DataType = binary_to_list(iolist_to_binary(proplists:get_value(datatype, Node))), + Name = binary_to_list(iolist_to_binary(proplists:get_value(name, Node))), + {DataType, Name} + end). + +-spec 'eof'(input(), index()) -> parse_result(). +'eof'(Input, Index) -> + p(Input, Index, 'eof', + fun(I, D) -> + (p_not(p_string(<<".">>)))(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'newline'(input(), index()) -> parse_result(). +'newline'(Input, Index) -> + p(Input, Index, 'newline', + fun(I, D) -> + (p_seq([p_optional(p_charclass(<<"[\r]">>)), p_charclass(<<"[\n]">>)]))(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'line_comment'(input(), index()) -> parse_result(). +'line_comment'(Input, Index) -> + p(Input, Index, 'line_comment', + fun(I, D) -> + (p_seq([p_choose([p_string(<<"//">>), p_string(<<"%">>)]), p_zero_or_more(p_seq([p_not(fun 'newline'/2), p_anything()])), p_choose([fun 'newline'/2, fun 'eof'/2])]))(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'blank'(input(), index()) -> parse_result(). +'blank'(Input, Index) -> + p(Input, Index, 'blank', + fun(I, D) -> + (p_choose([p_charclass(<<"[\s\t]">>), fun 'newline'/2, fun 'line_comment'/2]))(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'blank0'(input(), index()) -> parse_result(). +'blank0'(Input, Index) -> + p(Input, Index, 'blank0', + fun(I, D) -> + (p_zero_or_more(fun 'blank'/2))(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'blanks'(input(), index()) -> parse_result(). +'blanks'(Input, Index) -> + p(Input, Index, 'blanks', + fun(I, D) -> (p_one_or_more(fun 'blank'/2))(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'alpha'(input(), index()) -> parse_result(). +'alpha'(Input, Index) -> + p(Input, Index, 'alpha', + fun(I, D) -> + (p_choose([p_charclass(<<"[a-z]">>), p_charclass(<<"[A-Z]">>), p_string(<<"_">>), p_string(<<"[">>), p_string(<<"]">>)]))(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'alnum'(input(), index()) -> parse_result(). +'alnum'(Input, Index) -> + p(Input, Index, 'alnum', + fun(I, D) -> + (p_choose([fun 'alpha'/2, p_charclass(<<"[0-9]">>)]))(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'word'(input(), index()) -> parse_result(). +'word'(Input, Index) -> + p(Input, Index, 'word', + fun(I, D) -> + (p_seq([fun 'alpha'/2, p_zero_or_more(fun 'alnum'/2)]))(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'name'(input(), index()) -> parse_result(). +'name'(Input, Index) -> + p(Input, Index, 'name', + fun(I, D) -> + (fun 'word'/2)(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-spec 'typename'(input(), index()) -> parse_result(). +'typename'(Input, Index) -> + p(Input, Index, 'typename', + fun(I, D) -> + (fun 'word'/2)(I, D) + end, + fun(Node, _Idx) -> + Node + end). + +-file("peg_includes.hrl", 1). +-type index() :: {{line, pos_integer()}, {column, pos_integer()}}. +-type input() :: binary(). +-type parse_failure() :: {fail, term()}. +-type parse_success() :: {term(), input(), index()}. +-type parse_result() :: parse_failure() | parse_success(). +-type parse_fun() :: fun((input(), index()) -> parse_result()). +-type xform_fun() :: fun((input(), index()) -> term()). + +-spec p(input(), index(), atom(), parse_fun(), xform_fun()) -> parse_result(). +p(Inp, StartIndex, Name, ParseFun, TransformFun) -> + case get_memo(StartIndex, Name) of % See if the current reduction is memoized + {ok, Memo} -> % If it is, return the stored result + Memo; + _ -> % If not, attempt to parse + Result = + case ParseFun(Inp, StartIndex) of + {fail, _} = Failure -> % If it fails, memoize the failure + Failure; + {Match, InpRem, NewIndex} -> % If it passes, transform and memoize the result. + Transformed = TransformFun(Match, StartIndex), + {Transformed, InpRem, NewIndex} + end, + memoize(StartIndex, Name, Result), + Result + end. + +-spec setup_memo() -> list(). +setup_memo() -> + erase(). + +-spec release_memo() -> list(). +release_memo() -> + erase(). + +-spec memoize(index(), atom(), parse_result()) -> any(). +memoize(Index, Name, Result) -> + case get(Index) of + undefined -> + put(Index, [{Name, Result}]); + [] -> + put(Index, [{Name, Result}]); + Plist -> + put(Index, [{Name, Result} | Plist]) + end. + +-spec get_memo(index(), atom()) -> {ok, term()} | {error, not_found}. +get_memo(Index, Name) -> + case get(Index) of + undefined -> + {error, not_found}; + [] -> + {error, not_found}; + Plist -> + case proplists:lookup(Name, Plist) of + {Name, Result} -> + {ok, Result}; + _ -> + {error, not_found} + end + end. + +-ifdef(p_eof). +-spec p_eof() -> parse_fun(). +p_eof() -> + fun(<<>>, Index) -> + {eof, [], Index}; + (_, Index) -> + {fail, {expected, eof, Index}} end. +-endif. + +-ifdef(p_optional). +-spec p_optional(parse_fun()) -> parse_fun(). +p_optional(P) -> + fun(Input, Index) -> + case P(Input, Index) of + {fail, _} -> + {[], Input, Index}; + {_, _, _} = Success -> + Success + end + end. +-endif. + +-ifdef(p_not). +-spec p_not(parse_fun()) -> parse_fun(). +p_not(P) -> + fun(Input, Index) -> + case P(Input, Index) of + {fail, _} -> + {[], Input, Index}; + {Result, _, _} -> + {fail, {expected, {no_match, Result}, Index}} + end + end. +-endif. + +-ifdef(p_assert). +-spec p_assert(parse_fun()) -> parse_fun(). +p_assert(P) -> + fun(Input, Index) -> + case P(Input, Index) of + {fail, _} = Failure -> + Failure; + _ -> + {[], Input, Index} + end + end. +-endif. + +-ifdef(p_seq). +-spec p_seq([parse_fun()]) -> parse_fun(). +p_seq(P) -> + fun(Input, Index) -> + p_all(P, Input, Index, []) + end. + +-spec p_all([parse_fun()], input(), index(), [term()]) -> parse_result(). +p_all([], Inp, Index, Accum) -> + {lists:reverse(Accum), Inp, Index}; +p_all([P | Parsers], Inp, Index, Accum) -> + case P(Inp, Index) of + {fail, _} = Failure -> + Failure; + {Result, InpRem, NewIndex} -> + p_all(Parsers, InpRem, NewIndex, [Result | Accum]) + end. +-endif. + +-ifdef(p_choose). +-spec p_choose([parse_fun()]) -> parse_fun(). +p_choose(Parsers) -> + fun(Input, Index) -> + p_attempt(Parsers, Input, Index, none) + end. + +-spec p_attempt([parse_fun()], input(), index(), none | parse_failure()) -> parse_result(). +p_attempt([], _Input, _Index, Failure) -> Failure; +p_attempt([P | Parsers], Input, Index, FirstFailure) -> + case P(Input, Index) of + {fail, _} = Failure -> + case FirstFailure of + none -> + p_attempt(Parsers, Input, Index, Failure); + _ -> + p_attempt(Parsers, Input, Index, FirstFailure) + end; + Result -> + Result + end. +-endif. + +-ifdef(p_zero_or_more). +-spec p_zero_or_more(parse_fun()) -> parse_fun(). +p_zero_or_more(P) -> + fun(Input, Index) -> + p_scan(P, Input, Index, []) + end. +-endif. + +-ifdef(p_one_or_more). +-spec p_one_or_more(parse_fun()) -> parse_fun(). +p_one_or_more(P) -> + fun(Input, Index) -> + Result = p_scan(P, Input, Index, []), + case Result of + {[_ | _], _, _} -> + Result; + _ -> + {fail, {expected, Failure, _}} = P(Input, Index), + {fail, {expected, {at_least_one, Failure}, Index}} + end + end. +-endif. + +-ifdef(p_label). +-spec p_label(atom(), parse_fun()) -> parse_fun(). +p_label(Tag, P) -> + fun(Input, Index) -> + case P(Input, Index) of + {fail, _} = Failure -> + Failure; + {Result, InpRem, NewIndex} -> + {{Tag, Result}, InpRem, NewIndex} + end + end. +-endif. + +-ifdef(p_scan). +-spec p_scan(parse_fun(), input(), index(), [term()]) -> {[term()], input(), index()}. +p_scan(_, <<>>, Index, Accum) -> + {lists:reverse(Accum), <<>>, Index}; +p_scan(P, Inp, Index, Accum) -> + case P(Inp, Index) of + {fail, _} -> + {lists:reverse(Accum), Inp, Index}; + {Result, InpRem, NewIndex} -> + p_scan(P, InpRem, NewIndex, [Result | Accum]) + end. +-endif. + +-ifdef(p_string). +-spec p_string(binary()) -> parse_fun(). +p_string(S) -> + Length = erlang:byte_size(S), + fun(Input, Index) -> + try + <> = Input, + {S, Rest, p_advance_index(S, Index)} + catch + error:{badmatch, _} -> + {fail, {expected, {string, S}, Index}} + end + end. +-endif. + +-ifdef(p_anything). +-spec p_anything() -> parse_fun(). +p_anything() -> + fun(<<>>, Index) -> + {fail, {expected, any_character, Index}}; + (Input, Index) when is_binary(Input) -> + <> = Input, + {<>, Rest, p_advance_index(<>, Index)} + end. +-endif. + +-ifdef(p_charclass). +-spec p_charclass(string() | binary()) -> parse_fun(). +p_charclass(Class) -> + {ok, RE} = re:compile(Class, [unicode, dotall]), + fun(Inp, Index) -> + case re:run(Inp, RE, [anchored]) of + {match, [{0, Length} | _]} -> + {Head, Tail} = erlang:split_binary(Inp, Length), + {Head, Tail, p_advance_index(Head, Index)}; + _ -> + {fail, {expected, {character_class, binary_to_list(Class)}, Index}} + end + end. +-endif. + +-ifdef(p_regexp). +-spec p_regexp(binary()) -> parse_fun(). +p_regexp(Regexp) -> + {ok, RE} = re:compile(Regexp, [unicode, dotall, anchored]), + fun(Inp, Index) -> + case re:run(Inp, RE) of + {match, [{0, Length} | _]} -> + {Head, Tail} = erlang:split_binary(Inp, Length), + {Head, Tail, p_advance_index(Head, Index)}; + _ -> + {fail, {expected, {regexp, binary_to_list(Regexp)}, Index}} + end + end. +-endif. + +-ifdef(line). +-spec line(index() | term()) -> pos_integer() | undefined. +line({{line, L}, _}) -> L; +line(_) -> undefined. +-endif. + +-ifdef(column). +-spec column(index() | term()) -> pos_integer() | undefined. +column({_, {column, C}}) -> C; +column(_) -> undefined. +-endif. + +-spec p_advance_index(input() | unicode:charlist() | pos_integer(), index()) -> index(). +p_advance_index(MatchedInput, Index) when is_list(MatchedInput) orelse is_binary(MatchedInput) -> % strings + lists:foldl(fun p_advance_index/2, Index, unicode:characters_to_list(MatchedInput)); +p_advance_index(MatchedInput, Index) when is_integer(MatchedInput) -> % single characters + {{line, Line}, {column, Col}} = Index, + case MatchedInput of + $\n -> {{line, Line + 1}, {column, 1}}; + _ -> {{line, Line}, {column, Col + 1}} + end. diff --git a/test/genProto.bat b/test/genProto.bat new file mode 100644 index 0000000..90774bc --- /dev/null +++ b/test/genProto.bat @@ -0,0 +1 @@ +erl -pa ../ebin -s protoGen convertDir ../proto ./ ./ diff --git a/test/protoMsg.erl b/test/protoMsg.erl new file mode 100644 index 0000000..cf8612f --- /dev/null +++ b/test/protoMsg.erl @@ -0,0 +1,285 @@ +-module(protoMsg). + + +-export([encode/1, decode/1, encodeRec/1, decodeRec/2]). + +-define(bool(V), (case V of true -> <<1:8>>; _ -> <<0:8>> end)). +-define(int8(V), <>). +-define(uint8(V), <>). +-define(int16(V), <>). +-define(uint16(V), <>). +-define(int32(V), <>). +-define(uint32(V), <>). +-define(int64(V), <>). +-define(uint64(V), <>). +-define(float(V), <>). +-define(double(V), <>). +-define(string(V), (string(V))). +-define(record(V), (case V of undefined -> [<<0:8>>]; V -> [<<1:8>>, encodeRec(V)] end)). +-define(list_bool(List), [<<(length(List)):16/big>>, [?bool(V) || V <- List]]). +-define(list_int8(List), [<<(length(List)):16/big>>, [?int8(V) || V <- List]]). +-define(list_uint8(List), [<<(length(List)):16/big>>, [?uint8(V) || V <- List]]). +-define(list_int16(List), [<<(length(List)):16/big>>, [?int16(V) || V <- List]]). +-define(list_uint16(List), [<<(length(List)):16/big>>, [?uint16(V) || V <- List]]). +-define(list_int32(List), [<<(length(List)):16/big>>, [?int32(V) || V <- List]]). +-define(list_uint32(List), [<<(length(List)):16/big>>, [?uint32(V) || V <- List]]). +-define(list_int64(List), [<<(length(List)):16/big>>, [?int64(V) || V <- List]]). +-define(list_uint64(List), [<<(length(List)):16/big>>, [?uint64(V) || V <- List]]). +-define(list_float(List), [<<(length(List)):16/big>>, [?float(V) || V <- List]]). +-define(list_double(List), [<<(length(List)):16/big>>, [?double(V) || V <- List]]). +-define(list_string(List), [<<(length(List)):16/big>>, [string(V) || V <- List]]). +-define(list_record(List), [<<(length(List)):16/big>>, [encodeRec(V) || V <- List]]). + +string(Str) when is_binary(Str) -> + [<<(byte_size(Str)):16/big>>, Str]; +string(Str) -> + Str2 = unicode:characters_to_binary(Str, utf8), + [<<(byte_size(Str2)):16/big>>, Str2]. + +encode(Record) -> + MsgBin = encodeRec(Record), + MsgId = getMsgId(element(1, Record)), + [<>, MsgBin]. + +decode(Bin) -> + <> = Bin, + SchList = getMsgSchema(MsgId), + {<<>>, ResultList} = decodeField(SchList, MsgBin, [getMsgType(MsgId)]), + list_to_tuple(ResultList). + +decodeRec(RecordName, Bin) -> + SchList = getMsgSchema(RecordName), + {LeftBin, Result} = decodeField(SchList, Bin, [RecordName]), + {LeftBin, list_to_tuple(Result)}. + +decodeField([], LeftBin, Result) -> + {LeftBin, lists:reverse(Result)}; +decodeField([Type | SchList], MsgBin, Result) -> + case Type of + int32 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint32 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + string -> + <> = MsgBin, + decodeField(SchList, LeftBin, [StrBin | Result]); + int16 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint16 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + int8 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint8 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + int64 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + uint64 -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Int | Result]); + bool -> + <> = MsgBin, + case Bool =:= 1 of + true -> + decodeField(SchList, LeftBin, [true | Result]); + _ -> + decodeField(SchList, LeftBin, [false | Result]) + end; + falat -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Float | Result]); + double -> + <> = MsgBin, + decodeField(SchList, LeftBin, [Float | Result]); + {list, int32} -> + <> = MsgBin, + {LeftBin, RetList} = deInt32List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint32} -> + <> = MsgBin, + {LeftBin, RetList} = deUint32List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, int16} -> + <> = MsgBin, + {LeftBin, RetList} = deInt16List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint16} -> + <> = MsgBin, + {LeftBin, RetList} = deUint16List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, int8} -> + <> = MsgBin, + {LeftBin, RetList} = deInt8List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint8} -> + <> = MsgBin, + {LeftBin, RetList} = deUint8List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, string} -> + <> = MsgBin, + {LeftBin, RetList} = deStringList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, int64} -> + <> = MsgBin, + {LeftBin, RetList} = deInt64List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, uint64} -> + <> = MsgBin, + {LeftBin, RetList} = deUint64List(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, bool} -> + <> = MsgBin, + {LeftBin, RetList} = deBoolList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, flat} -> + <> = MsgBin, + {LeftBin, RetList} = deFloatList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, double} -> + <> = MsgBin, + {LeftBin, RetList} = deDoubleList(Len, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + {list, RecordName} -> + <> = MsgBin, + {LeftBin, RetList} = deRecordList(Len, RecordName, LeftListBin, []), + decodeField(SchList, LeftBin, [RetList | Result]); + RecordName -> + <> = MsgBin, + case IsUndef of + 0 -> + decodeField(SchList, LeftBin, [undefined | Result]); + _ -> + SubSchList = getMsgSchema(RecordName), + {SubLeftBin, SubResultList} = decodeField(SubSchList, LeftBin, [RecordName]), + decodeField(SchList, SubLeftBin, [list_to_tuple(SubResultList) | Result]) + end + end. + +deBoolList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deBoolList(N, <>, RetList) -> + case Bool =:= 1 of + true -> + deBoolList(N - 1, LeftBin, [true | RetList]); + _ -> + deBoolList(N - 1, LeftBin, [false | RetList]) + end. +deInt8List(0, MsgBin, RetList) -> + {MsgBin, RetList}; +deInt8List(N, <>, RetList) -> + deInt8List(N - 1, LeftBin, [Int | RetList]). + +deUint8List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint8List(N, <>, RetList) -> + deUint8List(N - 1, LeftBin, [Int | RetList]). + +deInt16List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deInt16List(N, <>, RetList) -> + deInt16List(N - 1, LeftBin, [Int | RetList]). + +deUint16List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint16List(N, <>, RetList) -> + deUint16List(N - 1, LeftBin, [Int | RetList]). + +deInt32List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deInt32List(N, <>, RetList) -> + deInt32List(N - 1, LeftBin, [Int | RetList]). + +deUint32List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint32List(N, <>, RetList) -> + deUint32List(N - 1, LeftBin, [Int | RetList]). + +deInt64List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deInt64List(N, <>, RetList) -> + deInt64List(N - 1, LeftBin, [Int | RetList]). + +deUint64List(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deUint64List(N, <>, RetList) -> + deUint64List(N - 1, LeftBin, [Int | RetList]). + +deFloatList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deFloatList(N, <>, RetList) -> + deFloatList(N - 1, LeftBin, [Float | RetList]). + +deDoubleList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deDoubleList(N, <>, RetList) -> + deDoubleList(N - 1, LeftBin, [Float | RetList]). + +deStringList(0, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deStringList(N, <>, RetList) -> + deStringList(N - 1, LeftBin, [StrBin | RetList]). + +deRecordList(0, _RecordName, MsgBin, RetList) -> + {MsgBin, lists:reverse(RetList)}; +deRecordList(N, RecordName, MsgBin, RetList) -> + {LeftBin, Tuple} = decodeRec(RecordName, MsgBin), + deRecordList(N - 1, RecordName, LeftBin, [Tuple | RetList]). + +getMsgType(1)-> + test; +getMsgType(2)-> + phoneNumber; +getMsgType(3)-> + person; +getMsgType(4)-> + addressBook; +getMsgType(_) -> + undefined. + +getMsgId(test)-> + 1; +getMsgId(phoneNumber)-> + 2; +getMsgId(person)-> + 3; +getMsgId(addressBook)-> + 4; +getMsgId(_) -> + 0. + +encodeRec({test, V1}) -> + [?string(V1)]; +encodeRec({phoneNumber, V1, V2}) -> + [?record(V1), ?int32(V2)]; +encodeRec({person, V1, V2, V3, V4}) -> + [?string(V1), ?int32(V2), ?string(V3), ?list_record(V4)]; +encodeRec({addressBook, V1, V2}) -> + [?list_record(V1), ?list_record(V2)]; +encodeRec(_) -> + []. + +getMsgSchema(test)-> + getMsgSchema(1); +getMsgSchema(1)-> + [string]; +getMsgSchema(phoneNumber)-> + getMsgSchema(2); +getMsgSchema(2)-> + [test,int32]; +getMsgSchema(person)-> + getMsgSchema(3); +getMsgSchema(3)-> + [string,int32,string,{list,phoneNumber}]; +getMsgSchema(addressBook)-> + getMsgSchema(4); +getMsgSchema(4)-> + [{list,person},{list,person}]; +getMsgSchema(_) -> + []. + diff --git a/test/protoMsg.hrl b/test/protoMsg.hrl new file mode 100644 index 0000000..46f3bb3 --- /dev/null +++ b/test/protoMsg.hrl @@ -0,0 +1,28 @@ +-opaque int8() :: -128..127. +-opaque int16() :: -32768..32767. +-opaque int32() :: -2147483648..2147483647. +-opaque int64() :: -9223372036854775808..9223372036854775807. +-opaque uint8() :: 0..255. +-opaque uint16() :: 0..65536. +-opaque uint32() :: 0..4294967295. +-opaque uint64() :: 0..18446744073709551615. +-opaque single() :: float(). +-opaque double() :: float(). + +-record(test ,{ + aa = "" :: string() + }). +-record(phoneNumber ,{ + number = undefined :: #test{} + ,type = 0 :: int32() + }). +-record(person ,{ + name = "" :: string() + ,id = 0 :: int32() + ,email = "" :: string() + ,phone = [] :: [#phoneNumber{}] + }). +-record(addressBook ,{ + person = [] :: [#person{}] + ,other = [] :: [#person{}] + }). diff --git a/test/remake.bat b/test/remake.bat new file mode 100644 index 0000000..e1944d0 --- /dev/null +++ b/test/remake.bat @@ -0,0 +1,3 @@ +#if not exist ebin md ebin +FOR %%f in (*.erl) DO erlc -W0 -o . %%f +pause \ No newline at end of file diff --git a/test/test.erl b/test/test.erl new file mode 100644 index 0000000..83b9116 --- /dev/null +++ b/test/test.erl @@ -0,0 +1,319 @@ +-module(test). + +-include("protoMsg.hrl"). +-compile(export_all). + +hh1(0) -> + ok; +hh1(N) -> + A = [tt1,tt2,tt3,tt4,{tet,tt1},{tet, tt2},{tet,tt3}, yyy], + test(A), + hh1(N -1). + +hh2(0) -> + ok; +hh2(N) -> + A = [tt1,tt2,tt3,tt4,{tet,tt1},{tet, tt2},{tet,tt3}, yyy], + tet(A), + hh2(N -1). + +test([]) -> + ok; +test([A | T]) -> + case A of + tt1 -> + ok; + tt2 -> + ok; + tt3 -> + ok; + tt4 -> + ok; + {tet, tt1} -> + ok; + {tet, tt2} -> + ok; + {tet, tt3} -> + ok; + _ -> + ok + end, + test(T). + + +tet([]) -> + ok; +tet([tt1 | T]) -> + ok, + tet(T); +tet([tt2 | T]) -> + ok, + tet(T); +tet([tt3 | T]) -> + ok, + tet(T); +tet([tt4 | T]) -> + ok, + tet(T); +tet([{tet, tt1} | T]) -> + ok, + tet(T); +tet([{tet, tt2} | T]) -> + ok, + tet(T); +tet([{tet, tt3} | T]) -> + ok, + tet(T); +tet([YY | T]) -> + ok, + tet(T). + +tt1(0)-> + AddressBook = #addressBook{ + person = [ + #person{ + name = "Alice", + id = 10000, + phone = [ + #phoneNumber{ number = #test{aa = "123456789"} , type = 1 }, + #phoneNumber{ number = #test{aa = "87654321"} , type = 2 } + ] + }, + #person{ + name = "Bob", + id = 20000, + phone = [ + #phoneNumber{number = #test{aa = "01234567890"} , type = 3 } + ] + } + ] + }, + Bin = protoMsg:encode(AddressBook); +tt1(N) -> + tt1(), + tt1(N- 1). + +tt1() -> + AddressBook = #addressBook{ + person = [ + #person{ + name = "Alice", + id = 10000, + phone = [ + #phoneNumber{ number = #test{aa = "123456789"} , type = 1 }, + #phoneNumber{ number = #test{aa = "87654321"} , type = 2 } + ] + }, + #person{ + name = "Bob", + id = 20000, + phone = [ + #phoneNumber{number = #test{aa = "01234567890"} , type = 3 } + ] + } + ] + }, + %"others" => [ + % #{ + % "name" => "Carol", + % "id" => 30000, + % "phone" => [ + % #{ "number" => #{"aa" => "9876543210"} } + % ] + % } + %] + %AddressBook = #person{name = "1232134", id = 11111,email = "aaa" ,phone = [#phoneNumber{}] }, + %AddressBook = #phoneNumber{number =#test{aa = "dffsaf"},type = 12 }, + Bin = protoMsg:encode(AddressBook). + %ok = file:write_file("fff.bin", Bin), + %print_bin(Bin), + %Bin = protoCode:encode(AddressBook), + %ok = file:write_file("fff.bin", Bin), + %print_bin(Bin), + %MsgId = protoMsg:getMsgId(element(1, AddressBook)), + %<>. + + +tt(N) -> + AddressBook = #addressBook{ + person = [ + #person{ + name = "Alice", + id = 10000, + phone = [ + #phoneNumber{ number = #test{aa = "123456789"} , type = 1 }, + #phoneNumber{ number = #test{aa = "87654321"} , type = 2 } + ] + }, + #person{ + name = "Bob", + id = 20000, + phone = [ + #phoneNumber{number = #test{aa = "01234567890"} , type = 3 } + ] + } + ] + }, + %"others" => [ + % #{ + % "name" => "Carol", + % "id" => 30000, + % "phone" => [ + % #{ "number" => #{"aa" => "9876543210"} } + % ] + % } + %] + %AddressBook = #person{name = "1232134", id = 11111,email = "aaa" ,phone = [#phoneNumber{}] }, + %AddressBook = #phoneNumber{number =#test{aa = "dffsaf"},type = 12 }, + Bin = protoMsg:encode(AddressBook), + %ok = file:write_file("fff.bin", Bin), + %print_bin(Bin), + tt(N, iolist_to_binary(Bin)). +tt(0, Bin) -> + protoMsg:decode(Bin); +tt(N, Bin) -> + protoMsg:decode(Bin), + tt(N-1, Bin). + + + %{Len, List, RestBin} = protoMsg("AddressBook", Bin), + %io:format("Len:~p, RestBin:~p~n", [Len, RestBin]), + %io:format("List:~p~n", [List]), + %{Map, _, _} = sproto:decode2("AddressBook", Bin), + %Map. + + +ttt1(0)-> + AddressBook = #addressBook{ + person = [ + #person{ + name = "Alice", + id = 10000, + phone = [ + #phoneNumber{ number = #test{aa = "你好啊 嘿嘿"} , type = 1 }, + #phoneNumber{ number = #test{aa = "87654321"} , type = 2 } + ] + }, + #person{ + name = "Bob", + id = 20000, + phone = [ + #phoneNumber{number = #test{aa = "范德萨地方范德萨发"} , type = 3 } + ] + } + ] + }, + term_to_binary(AddressBook); +ttt1(N) -> + ttt1(), + ttt1(N- 1). + +ttt1() -> + AddressBook = #addressBook{ + person = [ + #person{ + name = "Alice", + id = 10000, + phone = [ + #phoneNumber{ number = #test{aa = "你好啊 嘿嘿"} , type = 1 }, + #phoneNumber{ number = #test{aa = "87654321"} , type = 2 } + ] + }, + #person{ + name = "Bob", + id = 20000, + phone = [ + #phoneNumber{number = #test{aa = "范德萨地方范德萨发"} , type = 3 } + ] + } + ] + }, + %"others" => [ + % #{ + % "name" => "Carol", + % "id" => 30000, + % "phone" => [ + % #{ "number" => #{"aa" => "9876543210"} } + % ] + % } + %] + %AddressBook = #person{name = "1232134", id = 11111,email = "aaa" ,phone = [#phoneNumber{}] }, + %AddressBook = #phoneNumber{number =#test{aa = "dffsaf"},type = 12 }, + Bin = term_to_binary(AddressBook). +%ok = file:write_file("fff.bin", Bin), +%print_bin(Bin), +%Bin = protoCode:encode(AddressBook), +%ok = file:write_file("fff.bin", Bin), +%print_bin(Bin), +%MsgId = protoMsg:getMsgId(element(1, AddressBook)), +%<>. + + +ttt(N) -> + AddressBook = #addressBook{ + person = [ + #person{ + name = "Alice", + id = 10000, + phone = [ + #phoneNumber{ number = #test{aa = "123456789"} , type = 1 }, + #phoneNumber{ number = #test{aa = "87654321"} , type = 2 } + ] + }, + #person{ + name = "Bob", + id = 20000, + phone = [ + #phoneNumber{number = #test{aa = "01234567890"} , type = 3 } + ] + } + ] + }, + %"others" => [ + % #{ + % "name" => "Carol", + % "id" => 30000, + % "phone" => [ + % #{ "number" => #{"aa" => "9876543210"} } + % ] + % } + %] + %AddressBook = #person{name = "1232134", id = 11111,email = "aaa" ,phone = [#phoneNumber{}] }, + %AddressBook = #phoneNumber{number =#test{aa = "dffsaf"},type = 12 }, + %ok = file:write_file("fff.bin", Bin), + %print_bin(Bin), + ttt(N, term_to_binary(AddressBook)). +ttt(0, Bin) -> + binary_to_term(Bin); +ttt(N, Bin) -> + binary_to_term(Bin), + ttt(N-1, Bin). + + + + + + + + + + + +print_bin(Bin) -> + ByteList = lists:reverse(bin_to_hex(Bin, [])), + Fun = fun(Byte, Acc) -> + io:format("~2.16.0b ", [Byte]), + case Acc rem 8 =:= 0 of + true -> io:format("~n", []); + false -> ok + end, + Acc + 1 + end, + lists:foldl(Fun, 1, ByteList), + io:format("bytes:~p~n", [byte_size(Bin)]). + +bin_to_hex(<<>>, Acc) -> + Acc; +bin_to_hex(Bin, Acc) -> + <> = Bin, + bin_to_hex(Bin2, [A|Acc]). \ No newline at end of file