diff --git a/erlang_grpcbox_bench/.dockerignore b/erlang_grpcbox_bench/.dockerignore new file mode 120000 index 00000000..3e4e48b0 --- /dev/null +++ b/erlang_grpcbox_bench/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/erlang_grpcbox_bench/.gitignore b/erlang_grpcbox_bench/.gitignore new file mode 100644 index 00000000..f1c45545 --- /dev/null +++ b/erlang_grpcbox_bench/.gitignore @@ -0,0 +1,19 @@ +.rebar3 +_* +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +_build +.idea +*.iml +rebar3.crashdump +*~ diff --git a/erlang_grpcbox_bench/Dockerfile b/erlang_grpcbox_bench/Dockerfile new file mode 100644 index 00000000..6380ee34 --- /dev/null +++ b/erlang_grpcbox_bench/Dockerfile @@ -0,0 +1,51 @@ +FROM erlang:23-alpine as builder + +WORKDIR /src/app +COPY erlang_grpcbox_bench/rebar.* . + +RUN \ + --mount=type=cache,target=/root/.cache/rebar3 \ + --mount=type=cache,target=/var/cache/apk ln -vs /var/cache/apk /etc/apk/cache && \ + set -x \ + && apk update && apk upgrade \ + && apk add git emacs-nox \ + && git version \ + && rebar3 --version \ + && rebar3 do get-deps, compile + +COPY proto proto +COPY erlang_grpcbox_bench/config config +COPY erlang_grpcbox_bench/src src + +RUN \ + --mount=type=cache,target=/root/.cache/rebar3 \ + set -x \ + && git init \ + && git add -A . \ + && rebar3 do grpc gen, fmt \ + && git --no-pager diff && [[ 0 -eq $(git --no-pager diff --name-only | wc -l) ]] + +RUN \ + --mount=type=cache,target=/root/.cache/rebar3 \ + set -x \ + && rebar3 as prod tar \ + && mkdir -p /opt/rel \ + && tar -zxf $PWD/_build/prod/rel/*/*.tar.gz -C /opt/rel + +# --- + +FROM alpine:3.13 + +WORKDIR /app +COPY --from=builder /opt/rel . + +RUN set -x \ + && apk update && apk upgrade \ + && apk add openssl-dev ncurses \ + && rm -r /var/cache/apk/* \ + && ln -s $PWD/bin/erlang_grpcbox_bench $PWD/bin/rel + +EXPOSE 50051 + +ENTRYPOINT ["/app/bin/rel"] +CMD ["foreground"] diff --git a/erlang_grpcbox_bench/config/sys.config b/erlang_grpcbox_bench/config/sys.config new file mode 100644 index 00000000..78a48ef0 --- /dev/null +++ b/erlang_grpcbox_bench/config/sys.config @@ -0,0 +1,25 @@ +[{sasl, [{utc_log, true} + ]} + +,{grpcbox, [{servers, [#{grpc_opts => #{service_protos => [helloworld_pb + ] + ,services => #{'helloworld.Greeter' => egb_handler + } + } + ,transport_opts => #{ssl => false} + ,listen_opts => #{port => 50051 + ,ip => {0,0,0,0} + } + }]} + ]} + +,{kernel, [{logger_level, debug} + ,{logger, [{handler, default, logger_std_h, #{formatter => {flatlog, #{map_depth => 3 + ,term_depth => 50 + ,colored => true + }} + }} + ]} + ]} + +]. diff --git a/erlang_grpcbox_bench/rebar.config b/erlang_grpcbox_bench/rebar.config new file mode 100644 index 00000000..6b3dfef8 --- /dev/null +++ b/erlang_grpcbox_bench/rebar.config @@ -0,0 +1,52 @@ +{profiles, [{prod, [{relx, [{dev_mode, false} + ,{include_erts, true} + ,{include_src, false} + ]} + ,{grpc, [{protos, "./proto"} + ]} + ]} + ]}. + +{erl_opts, [debug_info + ,warnings_as_errors + ,warn_unused_vars + ,{i, "src"} + ]}. + +{deps, [grpcbox + ,flatlog + ]}. + +{grpc, [{protos, "../proto/helloworld"} + ,{gpb_opts, [{module_name_suffix, "_pb"} + ,strings_as_binaries + ,type_specs + ,{verify, always} + ,report + ]} + ]}. + +{plugins, [grpcbox_plugin + ,rebar3_fmt + ]}. + +{shell, [{apps, [erlang_grpcbox_bench]} + ,{config, "config/sys.config"} + ]}. + +{relx, [{release, {erlang_grpcbox_bench, "1.0.0"}, [erlang_grpcbox_bench]} + ,{dev_mode, true} + ,{include_erts, false} + %% ,{vm_args, "config/vm.args"} + ,{sys_config, "config/sys.config"} + ]}. + +{xref_checks, [undefined_function_calls + ,undefined_functions + %% ,exports_not_used + ,locals_not_used + ,deprecated_function_calls + ,deprecated_functions + ]}. +{xref_ignores, [helloworld_pb + ]}. diff --git a/erlang_grpcbox_bench/rebar.lock b/erlang_grpcbox_bench/rebar.lock new file mode 100644 index 00000000..889abf7f --- /dev/null +++ b/erlang_grpcbox_bench/rebar.lock @@ -0,0 +1,26 @@ +{"1.2.0", +[{<<"acceptor_pool">>,{pkg,<<"acceptor_pool">>,<<"1.0.0">>},1}, + {<<"chatterbox">>,{pkg,<<"ts_chatterbox">>,<<"0.11.0">>},1}, + {<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},1}, + {<<"flatlog">>,{pkg,<<"flatlog">>,<<"0.1.1">>},0}, + {<<"gproc">>,{pkg,<<"gproc">>,<<"0.8.0">>},1}, + {<<"grpcbox">>,{pkg,<<"grpcbox">>,<<"0.14.0">>},0}, + {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}]}. +[ +{pkg_hash,[ + {<<"acceptor_pool">>, <<"43C20D2ACAE35F0C2BCD64F9D2BDE267E459F0F3FD23DAB26485BF518C281B21">>}, + {<<"chatterbox">>, <<"B8F372C706023EB0DE5BF2976764EDB27C70FE67052C88C1F6A66B3A5626847F">>}, + {<<"ctx">>, <<"8FF88B70E6400C4DF90142E7F130625B82086077A45364A78D208ED3ED53C7FE">>}, + {<<"flatlog">>, <<"4A181C1B7596A0181C727C06858CBF4BA8C273643D0A2A2FB434EAD961584575">>}, + {<<"gproc">>, <<"CEA02C578589C61E5341FCE149EA36CCEF236CC2ECAC8691FBA408E7EA77EC2F">>}, + {<<"grpcbox">>, <<"3EB321BCD2275BAF8B54CF381FEB7B0559A50C02544DE28FDA039C7F2F9D1A7A">>}, + {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}]}, +{pkg_hash_ext,[ + {<<"acceptor_pool">>, <<"0CBCD83FDC8B9AD2EEE2067EF8B91A14858A5883CB7CD800E6FCD5803E158788">>}, + {<<"chatterbox">>, <<"722FE2BAD52913AB7E87D849FC6370375F0C961FFB2F0B5E6D647C9170C382A6">>}, + {<<"ctx">>, <<"A14ED2D1B67723DBEBBE423B28D7615EB0BDCBA6FF28F2D1F1B0A7E1D4AA5FC2">>}, + {<<"flatlog">>, <<"7F1DB0E971A42D5929D5C297D3D8D17340BF2DACE2AFFF292B2AFBD4AD6B8452">>}, + {<<"gproc">>, <<"580ADAFA56463B75263EF5A5DF4C86AF321F68694E7786CB057FD805D1E2A7DE">>}, + {<<"grpcbox">>, <<"E24159B7B6D3F9869BBE528845C0125FED2259366BA908FD04A1F45FE81D0660">>}, + {<<"hpack">>, <<"06F580167C4B8B8A6429040DF36CC93BBA6D571FAEAEC1B28816523379CBB23A">>}]} +]. diff --git a/erlang_grpcbox_bench/src/egb_handler.erl b/erlang_grpcbox_bench/src/egb_handler.erl new file mode 100644 index 00000000..e901ffc5 --- /dev/null +++ b/erlang_grpcbox_bench/src/egb_handler.erl @@ -0,0 +1,28 @@ +%% Copyright © 2021 Pierre Fenoll ‹pierrefenoll@gmail.com› +%% -*- coding: utf-8 -*- +-module(egb_handler). +-behavior(helloworld_greeter_bhvr). + +-include_lib("kernel/include/logger.hrl"). + +-export([say_hello/2]). + +-define(NOW(), erlang:monotonic_time()). +-define(SINCE(T), erlang:convert_time_unit(?NOW()-T, native, microsecond)). + +%% @doc Unary RPC +-spec say_hello(ctx:ctx(), helloworld_pb:hello_request()) -> + {ok, helloworld_pb:hello_reply(), ctx:ctx()} | grpcbox_stream:grpc_error_response(). + +say_hello(Ctx, Req) -> + StartTime = erlang:monotonic_time(), + ?LOG_DEBUG(#{status=>handling, req=>Req}), + + #{name := Name + } = Req, + + Rep = #{message => Name + }, + + ?LOG_INFO(#{status=>handled, rep=>Rep, in_us=>?SINCE(StartTime)}), + {ok, Rep, Ctx}. diff --git a/erlang_grpcbox_bench/src/erlang_grpcbox_bench.app.src b/erlang_grpcbox_bench/src/erlang_grpcbox_bench.app.src new file mode 100644 index 00000000..7f270714 --- /dev/null +++ b/erlang_grpcbox_bench/src/erlang_grpcbox_bench.app.src @@ -0,0 +1,18 @@ +{application, erlang_grpcbox_bench, + [{description, "An OTP app for https://github.com/LesnyRumcajs/grpc_bench"} + ,{vsn, "0.1.0"} + ,{modules, []} + ,{registered, []} + ,{env, []} + ,{mod, {erlang_grpcbox_bench_app,[]}} + ,{applications, [kernel + ,stdlib + ,sasl + ,flatlog + ,ctx + ,grpcbox + ]} + + ,{licenses, ["MIT"]} + ,{links, []} + ]}. diff --git a/erlang_grpcbox_bench/src/erlang_grpcbox_bench_app.erl b/erlang_grpcbox_bench/src/erlang_grpcbox_bench_app.erl new file mode 100644 index 00000000..43d34d67 --- /dev/null +++ b/erlang_grpcbox_bench/src/erlang_grpcbox_bench_app.erl @@ -0,0 +1,16 @@ +%%%------------------------------------------------------------------- +%% @doc erlang_grpcbox_bench public API +%% @end +%%%------------------------------------------------------------------- +-module(erlang_grpcbox_bench_app). +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + erlang_grpcbox_bench_sup:start_link(). + +stop(_State) -> + ok. + +%% internal functions diff --git a/erlang_grpcbox_bench/src/erlang_grpcbox_bench_sup.erl b/erlang_grpcbox_bench/src/erlang_grpcbox_bench_sup.erl new file mode 100644 index 00000000..4e81a1b8 --- /dev/null +++ b/erlang_grpcbox_bench/src/erlang_grpcbox_bench_sup.erl @@ -0,0 +1,32 @@ +%%%------------------------------------------------------------------- +%% @doc erlang_grpcbox_bench top level supervisor. +%% @end +%%%------------------------------------------------------------------- +-module(erlang_grpcbox_bench_sup). +-behaviour(supervisor). + +-export([start_link/0]). +-export([init/1]). + +-define(SERVER, ?MODULE). + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +init([]) -> + SupFlags = #{strategy => one_for_all, + intensity => 0, + period => 1}, + ChildSpecs = [], + {ok, {SupFlags, ChildSpecs}}. + +%% internal functions diff --git a/erlang_grpcbox_bench/src/helloworld_greeter_bhvr.erl b/erlang_grpcbox_bench/src/helloworld_greeter_bhvr.erl new file mode 100644 index 00000000..b3e9c589 --- /dev/null +++ b/erlang_grpcbox_bench/src/helloworld_greeter_bhvr.erl @@ -0,0 +1,13 @@ +%%%------------------------------------------------------------------- +%% @doc Behaviour to implement for grpc service helloworld.Greeter. +%% @end +%%%------------------------------------------------------------------- + +%% this module was generated on 2021-05-02T13:23:03+00:00 and should not be modified manually + +-module(helloworld_greeter_bhvr). + +%% @doc Unary RPC +-callback say_hello(ctx:ctx(), helloworld_pb:hello_request()) -> + {ok, helloworld_pb:hello_reply(), ctx:ctx()} | grpcbox_stream:grpc_error_response(). + diff --git a/erlang_grpcbox_bench/src/helloworld_greeter_client.erl b/erlang_grpcbox_bench/src/helloworld_greeter_client.erl new file mode 100644 index 00000000..de0fe12a --- /dev/null +++ b/erlang_grpcbox_bench/src/helloworld_greeter_client.erl @@ -0,0 +1,43 @@ +%%%------------------------------------------------------------------- +%% @doc Client module for grpc service helloworld.Greeter. +%% @end +%%%------------------------------------------------------------------- + +%% this module was generated on 2021-05-02T13:23:03+00:00 and should not be modified manually + +-module(helloworld_greeter_client). + +-compile(export_all). +-compile(nowarn_export_all). + +-include_lib("grpcbox/include/grpcbox.hrl"). + +-define(is_ctx(Ctx), is_tuple(Ctx) andalso element(1, Ctx) =:= ctx). + +-define(SERVICE, 'helloworld.Greeter'). +-define(PROTO_MODULE, 'helloworld_pb'). +-define(MARSHAL_FUN(T), fun(I) -> ?PROTO_MODULE:encode_msg(I, T) end). +-define(UNMARSHAL_FUN(T), fun(I) -> ?PROTO_MODULE:decode_msg(I, T) end). +-define(DEF(Input, Output, MessageType), #grpcbox_def{service=?SERVICE, + message_type=MessageType, + marshal_fun=?MARSHAL_FUN(Input), + unmarshal_fun=?UNMARSHAL_FUN(Output)}). + +%% @doc Unary RPC +-spec say_hello(helloworld_pb:hello_request()) -> + {ok, helloworld_pb:hello_reply(), grpcbox:metadata()} | grpcbox_stream:grpc_error_response() | {error, any()}. +say_hello(Input) -> + say_hello(ctx:new(), Input, #{}). + +-spec say_hello(ctx:t() | helloworld_pb:hello_request(), helloworld_pb:hello_request() | grpcbox_client:options()) -> + {ok, helloworld_pb:hello_reply(), grpcbox:metadata()} | grpcbox_stream:grpc_error_response() | {error, any()}. +say_hello(Ctx, Input) when ?is_ctx(Ctx) -> + say_hello(Ctx, Input, #{}); +say_hello(Input, Options) -> + say_hello(ctx:new(), Input, Options). + +-spec say_hello(ctx:t(), helloworld_pb:hello_request(), grpcbox_client:options()) -> + {ok, helloworld_pb:hello_reply(), grpcbox:metadata()} | grpcbox_stream:grpc_error_response() | {error, any()}. +say_hello(Ctx, Input, Options) -> + grpcbox_client:unary(Ctx, <<"/helloworld.Greeter/SayHello">>, Input, ?DEF(hello_request, hello_reply, <<"helloworld.HelloRequest">>), Options). + diff --git a/erlang_grpcbox_bench/src/helloworld_pb.erl b/erlang_grpcbox_bench/src/helloworld_pb.erl new file mode 100644 index 00000000..34ff05d2 --- /dev/null +++ b/erlang_grpcbox_bench/src/helloworld_pb.erl @@ -0,0 +1,696 @@ +%% -*- coding: utf-8 -*- +%% @private +%% Automatically generated, do not edit +%% Generated by gpb_compile version 4.17.3 +-module(helloworld_pb). + +-export([encode_msg/2, encode_msg/3]). +-export([decode_msg/2, decode_msg/3]). +-export([merge_msgs/3, merge_msgs/4]). +-export([verify_msg/2, verify_msg/3]). +-export([get_msg_defs/0]). +-export([get_msg_names/0]). +-export([get_group_names/0]). +-export([get_msg_or_group_names/0]). +-export([get_enum_names/0]). +-export([find_msg_def/1, fetch_msg_def/1]). +-export([find_enum_def/1, fetch_enum_def/1]). +-export([enum_symbol_by_value/2, enum_value_by_symbol/2]). +-export([get_service_names/0]). +-export([get_service_def/1]). +-export([get_rpc_names/1]). +-export([find_rpc_def/2, fetch_rpc_def/2]). +-export([fqbin_to_service_name/1]). +-export([service_name_to_fqbin/1]). +-export([fqbins_to_service_and_rpc_name/2]). +-export([service_and_rpc_name_to_fqbins/2]). +-export([fqbin_to_msg_name/1]). +-export([msg_name_to_fqbin/1]). +-export([fqbin_to_enum_name/1]). +-export([enum_name_to_fqbin/1]). +-export([get_package_name/0]). +-export([uses_packages/0]). +-export([source_basename/0]). +-export([get_all_source_basenames/0]). +-export([get_all_proto_names/0]). +-export([get_msg_containment/1]). +-export([get_pkg_containment/1]). +-export([get_service_containment/1]). +-export([get_rpc_containment/1]). +-export([get_enum_containment/1]). +-export([get_proto_by_msg_name_as_fqbin/1]). +-export([get_proto_by_service_name_as_fqbin/1]). +-export([get_proto_by_enum_name_as_fqbin/1]). +-export([get_protos_by_pkg_name_as_fqbin/1]). +-export([gpb_version_as_string/0, gpb_version_as_list/0]). + + +%% enumerated types + +-export_type([]). + +%% message types +-type hello_request() :: + #{name => unicode:chardata() % = 1, optional + }. + +-type hello_reply() :: + #{message => unicode:chardata() % = 1, optional + }. + +-export_type(['hello_request'/0, 'hello_reply'/0]). +-type '$msg_name'() :: hello_request | hello_reply. +-type '$msg'() :: hello_request() | hello_reply(). +-export_type(['$msg_name'/0, '$msg'/0]). + +-spec encode_msg('$msg'(), '$msg_name'()) -> binary(). +encode_msg(Msg, MsgName) when is_atom(MsgName) -> encode_msg(Msg, MsgName, []). + +-spec encode_msg('$msg'(), '$msg_name'(), list()) -> binary(). +encode_msg(Msg, MsgName, Opts) -> + case proplists:get_bool(verify, Opts) of + true -> verify_msg(Msg, MsgName, Opts); + false -> ok + end, + TrUserData = proplists:get_value(user_data, Opts), + case MsgName of + hello_request -> encode_msg_hello_request(id(Msg, TrUserData), TrUserData); + hello_reply -> encode_msg_hello_reply(id(Msg, TrUserData), TrUserData) + end. + + +encode_msg_hello_request(Msg, TrUserData) -> encode_msg_hello_request(Msg, <<>>, TrUserData). + + +encode_msg_hello_request(#{} = M, Bin, TrUserData) -> + case M of + #{name := F1} -> + begin + TrF1 = id(F1, TrUserData), + case is_empty_string(TrF1) of + true -> Bin; + false -> e_type_string(TrF1, <>, TrUserData) + end + end; + _ -> Bin + end. + +encode_msg_hello_reply(Msg, TrUserData) -> encode_msg_hello_reply(Msg, <<>>, TrUserData). + + +encode_msg_hello_reply(#{} = M, Bin, TrUserData) -> + case M of + #{message := F1} -> + begin + TrF1 = id(F1, TrUserData), + case is_empty_string(TrF1) of + true -> Bin; + false -> e_type_string(TrF1, <>, TrUserData) + end + end; + _ -> Bin + end. + +-compile({nowarn_unused_function,e_type_sint/3}). +e_type_sint(Value, Bin, _TrUserData) when Value >= 0 -> e_varint(Value * 2, Bin); +e_type_sint(Value, Bin, _TrUserData) -> e_varint(Value * -2 - 1, Bin). + +-compile({nowarn_unused_function,e_type_int32/3}). +e_type_int32(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; +e_type_int32(Value, Bin, _TrUserData) -> + <> = <>, + e_varint(N, Bin). + +-compile({nowarn_unused_function,e_type_int64/3}). +e_type_int64(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; +e_type_int64(Value, Bin, _TrUserData) -> + <> = <>, + e_varint(N, Bin). + +-compile({nowarn_unused_function,e_type_bool/3}). +e_type_bool(true, Bin, _TrUserData) -> <>; +e_type_bool(false, Bin, _TrUserData) -> <>; +e_type_bool(1, Bin, _TrUserData) -> <>; +e_type_bool(0, Bin, _TrUserData) -> <>. + +-compile({nowarn_unused_function,e_type_string/3}). +e_type_string(S, Bin, _TrUserData) -> + Utf8 = unicode:characters_to_binary(S), + Bin2 = e_varint(byte_size(Utf8), Bin), + <>. + +-compile({nowarn_unused_function,e_type_bytes/3}). +e_type_bytes(Bytes, Bin, _TrUserData) when is_binary(Bytes) -> + Bin2 = e_varint(byte_size(Bytes), Bin), + <>; +e_type_bytes(Bytes, Bin, _TrUserData) when is_list(Bytes) -> + BytesBin = iolist_to_binary(Bytes), + Bin2 = e_varint(byte_size(BytesBin), Bin), + <>. + +-compile({nowarn_unused_function,e_type_fixed32/3}). +e_type_fixed32(Value, Bin, _TrUserData) -> <>. + +-compile({nowarn_unused_function,e_type_sfixed32/3}). +e_type_sfixed32(Value, Bin, _TrUserData) -> <>. + +-compile({nowarn_unused_function,e_type_fixed64/3}). +e_type_fixed64(Value, Bin, _TrUserData) -> <>. + +-compile({nowarn_unused_function,e_type_sfixed64/3}). +e_type_sfixed64(Value, Bin, _TrUserData) -> <>. + +-compile({nowarn_unused_function,e_type_float/3}). +e_type_float(V, Bin, _) when is_number(V) -> <>; +e_type_float(infinity, Bin, _) -> <>; +e_type_float('-infinity', Bin, _) -> <>; +e_type_float(nan, Bin, _) -> <>. + +-compile({nowarn_unused_function,e_type_double/3}). +e_type_double(V, Bin, _) when is_number(V) -> <>; +e_type_double(infinity, Bin, _) -> <>; +e_type_double('-infinity', Bin, _) -> <>; +e_type_double(nan, Bin, _) -> <>. + +-compile({nowarn_unused_function,e_unknown_elems/2}). +e_unknown_elems([Elem | Rest], Bin) -> + BinR = case Elem of + {varint, FNum, N} -> + BinF = e_varint(FNum bsl 3, Bin), + e_varint(N, BinF); + {length_delimited, FNum, Data} -> + BinF = e_varint(FNum bsl 3 bor 2, Bin), + BinL = e_varint(byte_size(Data), BinF), + <>; + {group, FNum, GroupFields} -> + Bin1 = e_varint(FNum bsl 3 bor 3, Bin), + Bin2 = e_unknown_elems(GroupFields, Bin1), + e_varint(FNum bsl 3 bor 4, Bin2); + {fixed32, FNum, V} -> + BinF = e_varint(FNum bsl 3 bor 5, Bin), + <>; + {fixed64, FNum, V} -> + BinF = e_varint(FNum bsl 3 bor 1, Bin), + <> + end, + e_unknown_elems(Rest, BinR); +e_unknown_elems([], Bin) -> Bin. + +-compile({nowarn_unused_function,e_varint/3}). +e_varint(N, Bin, _TrUserData) -> e_varint(N, Bin). + +-compile({nowarn_unused_function,e_varint/2}). +e_varint(N, Bin) when N =< 127 -> <>; +e_varint(N, Bin) -> + Bin2 = <>, + e_varint(N bsr 7, Bin2). + +is_empty_string("") -> true; +is_empty_string(<<>>) -> true; +is_empty_string(L) when is_list(L) -> not string_has_chars(L); +is_empty_string(B) when is_binary(B) -> false. + +string_has_chars([C | _]) when is_integer(C) -> true; +string_has_chars([H | T]) -> + case string_has_chars(H) of + true -> true; + false -> string_has_chars(T) + end; +string_has_chars(B) when is_binary(B), byte_size(B) =/= 0 -> true; +string_has_chars(C) when is_integer(C) -> true; +string_has_chars(<<>>) -> false; +string_has_chars([]) -> false. + + +decode_msg(Bin, MsgName) when is_binary(Bin) -> decode_msg(Bin, MsgName, []). + +decode_msg(Bin, MsgName, Opts) when is_binary(Bin) -> + TrUserData = proplists:get_value(user_data, Opts), + decode_msg_1_catch(Bin, MsgName, TrUserData). + +-ifdef('OTP_RELEASE'). +decode_msg_1_catch(Bin, MsgName, TrUserData) -> + try decode_msg_2_doit(MsgName, Bin, TrUserData) + catch Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) + end. +-else. +decode_msg_1_catch(Bin, MsgName, TrUserData) -> + try decode_msg_2_doit(MsgName, Bin, TrUserData) + catch Class:Reason -> + StackTrace = erlang:get_stacktrace(), + error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) + end. +-endif. + +decode_msg_2_doit(hello_request, Bin, TrUserData) -> id(decode_msg_hello_request(Bin, TrUserData), TrUserData); +decode_msg_2_doit(hello_reply, Bin, TrUserData) -> id(decode_msg_hello_reply(Bin, TrUserData), TrUserData). + + + +decode_msg_hello_request(Bin, TrUserData) -> dfp_read_field_def_hello_request(Bin, 0, 0, 0, id(<<>>, TrUserData), TrUserData). + +dfp_read_field_def_hello_request(<<10, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> d_field_hello_request_name(Rest, Z1, Z2, F, F@_1, TrUserData); +dfp_read_field_def_hello_request(<<>>, 0, 0, _, F@_1, _) -> #{name => F@_1}; +dfp_read_field_def_hello_request(Other, Z1, Z2, F, F@_1, TrUserData) -> dg_read_field_def_hello_request(Other, Z1, Z2, F, F@_1, TrUserData). + +dg_read_field_def_hello_request(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, TrUserData) when N < 32 - 7 -> dg_read_field_def_hello_request(Rest, N + 7, X bsl N + Acc, F, F@_1, TrUserData); +dg_read_field_def_hello_request(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, TrUserData) -> + Key = X bsl N + Acc, + case Key of + 10 -> d_field_hello_request_name(Rest, 0, 0, 0, F@_1, TrUserData); + _ -> + case Key band 7 of + 0 -> skip_varint_hello_request(Rest, 0, 0, Key bsr 3, F@_1, TrUserData); + 1 -> skip_64_hello_request(Rest, 0, 0, Key bsr 3, F@_1, TrUserData); + 2 -> skip_length_delimited_hello_request(Rest, 0, 0, Key bsr 3, F@_1, TrUserData); + 3 -> skip_group_hello_request(Rest, 0, 0, Key bsr 3, F@_1, TrUserData); + 5 -> skip_32_hello_request(Rest, 0, 0, Key bsr 3, F@_1, TrUserData) + end + end; +dg_read_field_def_hello_request(<<>>, 0, 0, _, F@_1, _) -> #{name => F@_1}. + +d_field_hello_request_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, TrUserData) when N < 57 -> d_field_hello_request_name(Rest, N + 7, X bsl N + Acc, F, F@_1, TrUserData); +d_field_hello_request_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, TrUserData) -> + {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, + dfp_read_field_def_hello_request(RestF, 0, 0, F, NewFValue, TrUserData). + +skip_varint_hello_request(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> skip_varint_hello_request(Rest, Z1, Z2, F, F@_1, TrUserData); +skip_varint_hello_request(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> dfp_read_field_def_hello_request(Rest, Z1, Z2, F, F@_1, TrUserData). + +skip_length_delimited_hello_request(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, TrUserData) when N < 57 -> skip_length_delimited_hello_request(Rest, N + 7, X bsl N + Acc, F, F@_1, TrUserData); +skip_length_delimited_hello_request(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, TrUserData) -> + Length = X bsl N + Acc, + <<_:Length/binary, Rest2/binary>> = Rest, + dfp_read_field_def_hello_request(Rest2, 0, 0, F, F@_1, TrUserData). + +skip_group_hello_request(Bin, _, Z2, FNum, F@_1, TrUserData) -> + {_, Rest} = read_group(Bin, FNum), + dfp_read_field_def_hello_request(Rest, 0, Z2, FNum, F@_1, TrUserData). + +skip_32_hello_request(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> dfp_read_field_def_hello_request(Rest, Z1, Z2, F, F@_1, TrUserData). + +skip_64_hello_request(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> dfp_read_field_def_hello_request(Rest, Z1, Z2, F, F@_1, TrUserData). + +decode_msg_hello_reply(Bin, TrUserData) -> dfp_read_field_def_hello_reply(Bin, 0, 0, 0, id(<<>>, TrUserData), TrUserData). + +dfp_read_field_def_hello_reply(<<10, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> d_field_hello_reply_message(Rest, Z1, Z2, F, F@_1, TrUserData); +dfp_read_field_def_hello_reply(<<>>, 0, 0, _, F@_1, _) -> #{message => F@_1}; +dfp_read_field_def_hello_reply(Other, Z1, Z2, F, F@_1, TrUserData) -> dg_read_field_def_hello_reply(Other, Z1, Z2, F, F@_1, TrUserData). + +dg_read_field_def_hello_reply(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, TrUserData) when N < 32 - 7 -> dg_read_field_def_hello_reply(Rest, N + 7, X bsl N + Acc, F, F@_1, TrUserData); +dg_read_field_def_hello_reply(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, TrUserData) -> + Key = X bsl N + Acc, + case Key of + 10 -> d_field_hello_reply_message(Rest, 0, 0, 0, F@_1, TrUserData); + _ -> + case Key band 7 of + 0 -> skip_varint_hello_reply(Rest, 0, 0, Key bsr 3, F@_1, TrUserData); + 1 -> skip_64_hello_reply(Rest, 0, 0, Key bsr 3, F@_1, TrUserData); + 2 -> skip_length_delimited_hello_reply(Rest, 0, 0, Key bsr 3, F@_1, TrUserData); + 3 -> skip_group_hello_reply(Rest, 0, 0, Key bsr 3, F@_1, TrUserData); + 5 -> skip_32_hello_reply(Rest, 0, 0, Key bsr 3, F@_1, TrUserData) + end + end; +dg_read_field_def_hello_reply(<<>>, 0, 0, _, F@_1, _) -> #{message => F@_1}. + +d_field_hello_reply_message(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, TrUserData) when N < 57 -> d_field_hello_reply_message(Rest, N + 7, X bsl N + Acc, F, F@_1, TrUserData); +d_field_hello_reply_message(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, TrUserData) -> + {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, + dfp_read_field_def_hello_reply(RestF, 0, 0, F, NewFValue, TrUserData). + +skip_varint_hello_reply(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> skip_varint_hello_reply(Rest, Z1, Z2, F, F@_1, TrUserData); +skip_varint_hello_reply(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> dfp_read_field_def_hello_reply(Rest, Z1, Z2, F, F@_1, TrUserData). + +skip_length_delimited_hello_reply(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, TrUserData) when N < 57 -> skip_length_delimited_hello_reply(Rest, N + 7, X bsl N + Acc, F, F@_1, TrUserData); +skip_length_delimited_hello_reply(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, TrUserData) -> + Length = X bsl N + Acc, + <<_:Length/binary, Rest2/binary>> = Rest, + dfp_read_field_def_hello_reply(Rest2, 0, 0, F, F@_1, TrUserData). + +skip_group_hello_reply(Bin, _, Z2, FNum, F@_1, TrUserData) -> + {_, Rest} = read_group(Bin, FNum), + dfp_read_field_def_hello_reply(Rest, 0, Z2, FNum, F@_1, TrUserData). + +skip_32_hello_reply(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> dfp_read_field_def_hello_reply(Rest, Z1, Z2, F, F@_1, TrUserData). + +skip_64_hello_reply(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, TrUserData) -> dfp_read_field_def_hello_reply(Rest, Z1, Z2, F, F@_1, TrUserData). + +read_group(Bin, FieldNum) -> + {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum), + <> = Bin, + {Group, Rest}. + +%% Like skipping over fields, but record the total length, +%% Each field is <(FieldNum bsl 3) bor FieldType> ++ +%% Record the length because varints may be non-optimally encoded. +%% +%% Groups can be nested, but assume the same FieldNum cannot be nested +%% because group field numbers are shared with the rest of the fields +%% numbers. Thus we can search just for an group-end with the same +%% field number. +%% +%% (The only time the same group field number could occur would +%% be in a nested sub message, but then it would be inside a +%% length-delimited entry, which we skip-read by length.) +read_gr_b(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum) + when N < (32-7) -> + read_gr_b(Tl, N+7, X bsl N + Acc, NumBytes, TagLen+1, FieldNum); +read_gr_b(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, + FieldNum) -> + Key = X bsl N + Acc, + TagLen1 = TagLen + 1, + case {Key bsr 3, Key band 7} of + {FieldNum, 4} -> % 4 = group_end + {NumBytes, TagLen1}; + {_, 0} -> % 0 = varint + read_gr_vi(Tl, 0, NumBytes + TagLen1, FieldNum); + {_, 1} -> % 1 = bits64 + <<_:64, Tl2/binary>> = Tl, + read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 8, 0, FieldNum); + {_, 2} -> % 2 = length_delimited + read_gr_ld(Tl, 0, 0, NumBytes + TagLen1, FieldNum); + {_, 3} -> % 3 = group_start + read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); + {_, 4} -> % 4 = group_end + read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); + {_, 5} -> % 5 = bits32 + <<_:32, Tl2/binary>> = Tl, + read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 4, 0, FieldNum) + end. + +read_gr_vi(<<1:1, _:7, Tl/binary>>, N, NumBytes, FieldNum) + when N < (64-7) -> + read_gr_vi(Tl, N+7, NumBytes+1, FieldNum); +read_gr_vi(<<0:1, _:7, Tl/binary>>, _, NumBytes, FieldNum) -> + read_gr_b(Tl, 0, 0, NumBytes+1, 0, FieldNum). + +read_gr_ld(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) + when N < (64-7) -> + read_gr_ld(Tl, N+7, X bsl N + Acc, NumBytes+1, FieldNum); +read_gr_ld(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) -> + Len = X bsl N + Acc, + NumBytes1 = NumBytes + 1, + <<_:Len/binary, Tl2/binary>> = Tl, + read_gr_b(Tl2, 0, 0, NumBytes1 + Len, 0, FieldNum). + +merge_msgs(Prev, New, MsgName) when is_atom(MsgName) -> merge_msgs(Prev, New, MsgName, []). + +merge_msgs(Prev, New, MsgName, Opts) -> + TrUserData = proplists:get_value(user_data, Opts), + case MsgName of + hello_request -> merge_msg_hello_request(Prev, New, TrUserData); + hello_reply -> merge_msg_hello_reply(Prev, New, TrUserData) + end. + +-compile({nowarn_unused_function,merge_msg_hello_request/3}). +merge_msg_hello_request(PMsg, NMsg, _) -> + S1 = #{}, + case {PMsg, NMsg} of + {_, #{name := NFname}} -> S1#{name => NFname}; + {#{name := PFname}, _} -> S1#{name => PFname}; + _ -> S1 + end. + +-compile({nowarn_unused_function,merge_msg_hello_reply/3}). +merge_msg_hello_reply(PMsg, NMsg, _) -> + S1 = #{}, + case {PMsg, NMsg} of + {_, #{message := NFmessage}} -> S1#{message => NFmessage}; + {#{message := PFmessage}, _} -> S1#{message => PFmessage}; + _ -> S1 + end. + + +verify_msg(Msg, MsgName) when is_atom(MsgName) -> verify_msg(Msg, MsgName, []). + +verify_msg(Msg, MsgName, Opts) -> + TrUserData = proplists:get_value(user_data, Opts), + case MsgName of + hello_request -> v_msg_hello_request(Msg, [MsgName], TrUserData); + hello_reply -> v_msg_hello_reply(Msg, [MsgName], TrUserData); + _ -> mk_type_error(not_a_known_message, Msg, []) + end. + + +-compile({nowarn_unused_function,v_msg_hello_request/3}). +-dialyzer({nowarn_function,v_msg_hello_request/3}). +v_msg_hello_request(#{} = M, Path, TrUserData) -> + case M of + #{name := F1} -> v_type_string(F1, [name | Path], TrUserData); + _ -> ok + end, + lists:foreach(fun (name) -> ok; + (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) + end, + maps:keys(M)), + ok; +v_msg_hello_request(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [] -- maps:keys(M), hello_request}, M, Path); +v_msg_hello_request(X, Path, _TrUserData) -> mk_type_error({expected_msg, hello_request}, X, Path). + +-compile({nowarn_unused_function,v_msg_hello_reply/3}). +-dialyzer({nowarn_function,v_msg_hello_reply/3}). +v_msg_hello_reply(#{} = M, Path, TrUserData) -> + case M of + #{message := F1} -> v_type_string(F1, [message | Path], TrUserData); + _ -> ok + end, + lists:foreach(fun (message) -> ok; + (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) + end, + maps:keys(M)), + ok; +v_msg_hello_reply(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [] -- maps:keys(M), hello_reply}, M, Path); +v_msg_hello_reply(X, Path, _TrUserData) -> mk_type_error({expected_msg, hello_reply}, X, Path). + +-compile({nowarn_unused_function,v_type_string/3}). +-dialyzer({nowarn_function,v_type_string/3}). +v_type_string(S, Path, _TrUserData) when is_list(S); is_binary(S) -> + try unicode:characters_to_binary(S) of + B when is_binary(B) -> ok; + {error, _, _} -> mk_type_error(bad_unicode_string, S, Path) + catch + error:badarg -> mk_type_error(bad_unicode_string, S, Path) + end; +v_type_string(X, Path, _TrUserData) -> mk_type_error(bad_unicode_string, X, Path). + +-compile({nowarn_unused_function,mk_type_error/3}). +-spec mk_type_error(_, _, list()) -> no_return(). +mk_type_error(Error, ValueSeen, Path) -> + Path2 = prettify_path(Path), + erlang:error({gpb_type_error, {Error, [{value, ValueSeen}, {path, Path2}]}}). + + +-compile({nowarn_unused_function,prettify_path/1}). +-dialyzer({nowarn_function,prettify_path/1}). +prettify_path([]) -> top_level; +prettify_path(PathR) -> lists:append(lists:join(".", lists:map(fun atom_to_list/1, lists:reverse(PathR)))). + + +-compile({nowarn_unused_function,id/2}). +-compile({inline,id/2}). +id(X, _TrUserData) -> X. + +-compile({nowarn_unused_function,v_ok/3}). +-compile({inline,v_ok/3}). +v_ok(_Value, _Path, _TrUserData) -> ok. + +-compile({nowarn_unused_function,m_overwrite/3}). +-compile({inline,m_overwrite/3}). +m_overwrite(_Prev, New, _TrUserData) -> New. + +-compile({nowarn_unused_function,cons/3}). +-compile({inline,cons/3}). +cons(Elem, Acc, _TrUserData) -> [Elem | Acc]. + +-compile({nowarn_unused_function,lists_reverse/2}). +-compile({inline,lists_reverse/2}). +'lists_reverse'(L, _TrUserData) -> lists:reverse(L). +-compile({nowarn_unused_function,'erlang_++'/3}). +-compile({inline,'erlang_++'/3}). +'erlang_++'(A, B, _TrUserData) -> A ++ B. + + +get_msg_defs() -> + [{{msg, hello_request}, [#{name => name, fnum => 1, rnum => 2, type => string, occurrence => optional, opts => []}]}, {{msg, hello_reply}, [#{name => message, fnum => 1, rnum => 2, type => string, occurrence => optional, opts => []}]}]. + + +get_msg_names() -> [hello_request, hello_reply]. + + +get_group_names() -> []. + + +get_msg_or_group_names() -> [hello_request, hello_reply]. + + +get_enum_names() -> []. + + +fetch_msg_def(MsgName) -> + case find_msg_def(MsgName) of + Fs when is_list(Fs) -> Fs; + error -> erlang:error({no_such_msg, MsgName}) + end. + + +-spec fetch_enum_def(_) -> no_return(). +fetch_enum_def(EnumName) -> erlang:error({no_such_enum, EnumName}). + + +find_msg_def(hello_request) -> [#{name => name, fnum => 1, rnum => 2, type => string, occurrence => optional, opts => []}]; +find_msg_def(hello_reply) -> [#{name => message, fnum => 1, rnum => 2, type => string, occurrence => optional, opts => []}]; +find_msg_def(_) -> error. + + +find_enum_def(_) -> error. + + +-spec enum_symbol_by_value(_, _) -> no_return(). +enum_symbol_by_value(E, V) -> erlang:error({no_enum_defs, E, V}). + + +-spec enum_value_by_symbol(_, _) -> no_return(). +enum_value_by_symbol(E, V) -> erlang:error({no_enum_defs, E, V}). + + + +get_service_names() -> ['helloworld.Greeter']. + + +get_service_def('helloworld.Greeter') -> {{service, 'helloworld.Greeter'}, [#{name => 'SayHello', input => hello_request, output => hello_reply, input_stream => false, output_stream => false, opts => []}]}; +get_service_def(_) -> error. + + +get_rpc_names('helloworld.Greeter') -> ['SayHello']; +get_rpc_names(_) -> error. + + +find_rpc_def('helloworld.Greeter', RpcName) -> 'find_rpc_def_helloworld.Greeter'(RpcName); +find_rpc_def(_, _) -> error. + + +'find_rpc_def_helloworld.Greeter'('SayHello') -> #{name => 'SayHello', input => hello_request, output => hello_reply, input_stream => false, output_stream => false, opts => []}; +'find_rpc_def_helloworld.Greeter'(_) -> error. + + +fetch_rpc_def(ServiceName, RpcName) -> + case find_rpc_def(ServiceName, RpcName) of + Def when is_map(Def) -> Def; + error -> erlang:error({no_such_rpc, ServiceName, RpcName}) + end. + + +%% Convert a a fully qualified (ie with package name) service name +%% as a binary to a service name as an atom. +fqbin_to_service_name(<<"helloworld.Greeter">>) -> 'helloworld.Greeter'; +fqbin_to_service_name(X) -> error({gpb_error, {badservice, X}}). + + +%% Convert a service name as an atom to a fully qualified +%% (ie with package name) name as a binary. +service_name_to_fqbin('helloworld.Greeter') -> <<"helloworld.Greeter">>; +service_name_to_fqbin(X) -> error({gpb_error, {badservice, X}}). + + +%% Convert a a fully qualified (ie with package name) service name +%% and an rpc name, both as binaries to a service name and an rpc +%% name, as atoms. +fqbins_to_service_and_rpc_name(<<"helloworld.Greeter">>, <<"SayHello">>) -> {'helloworld.Greeter', 'SayHello'}; +fqbins_to_service_and_rpc_name(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). + + +%% Convert a service name and an rpc name, both as atoms, +%% to a fully qualified (ie with package name) service name and +%% an rpc name as binaries. +service_and_rpc_name_to_fqbins('helloworld.Greeter', 'SayHello') -> {<<"helloworld.Greeter">>, <<"SayHello">>}; +service_and_rpc_name_to_fqbins(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). + + +fqbin_to_msg_name(<<"helloworld.HelloRequest">>) -> hello_request; +fqbin_to_msg_name(<<"helloworld.HelloReply">>) -> hello_reply; +fqbin_to_msg_name(E) -> error({gpb_error, {badmsg, E}}). + + +msg_name_to_fqbin(hello_request) -> <<"helloworld.HelloRequest">>; +msg_name_to_fqbin(hello_reply) -> <<"helloworld.HelloReply">>; +msg_name_to_fqbin(E) -> error({gpb_error, {badmsg, E}}). + + +-spec fqbin_to_enum_name(_) -> no_return(). +fqbin_to_enum_name(E) -> error({gpb_error, {badenum, E}}). + + +-spec enum_name_to_fqbin(_) -> no_return(). +enum_name_to_fqbin(E) -> error({gpb_error, {badenum, E}}). + + +get_package_name() -> helloworld. + + +%% Whether or not the message names +%% are prepended with package name or not. +uses_packages() -> true. + + +source_basename() -> "helloworld.proto". + + +%% Retrieve all proto file names, also imported ones. +%% The order is top-down. The first element is always the main +%% source file. The files are returned with extension, +%% see get_all_proto_names/0 for a version that returns +%% the basenames sans extension +get_all_source_basenames() -> ["helloworld.proto"]. + + +%% Retrieve all proto file names, also imported ones. +%% The order is top-down. The first element is always the main +%% source file. The files are returned sans .proto extension, +%% to make it easier to use them with the various get_xyz_containment +%% functions. +get_all_proto_names() -> ["helloworld"]. + + +get_msg_containment("helloworld") -> [hello_reply, hello_request]; +get_msg_containment(P) -> error({gpb_error, {badproto, P}}). + + +get_pkg_containment("helloworld") -> helloworld; +get_pkg_containment(P) -> error({gpb_error, {badproto, P}}). + + +get_service_containment("helloworld") -> ['helloworld.Greeter']; +get_service_containment(P) -> error({gpb_error, {badproto, P}}). + + +get_rpc_containment("helloworld") -> [{'helloworld.Greeter', 'SayHello'}]; +get_rpc_containment(P) -> error({gpb_error, {badproto, P}}). + + +get_enum_containment("helloworld") -> []; +get_enum_containment(P) -> error({gpb_error, {badproto, P}}). + + +get_proto_by_msg_name_as_fqbin(<<"helloworld.HelloRequest">>) -> "helloworld"; +get_proto_by_msg_name_as_fqbin(<<"helloworld.HelloReply">>) -> "helloworld"; +get_proto_by_msg_name_as_fqbin(E) -> error({gpb_error, {badmsg, E}}). + + +get_proto_by_service_name_as_fqbin(<<"helloworld.Greeter">>) -> "helloworld"; +get_proto_by_service_name_as_fqbin(E) -> error({gpb_error, {badservice, E}}). + + +-spec get_proto_by_enum_name_as_fqbin(_) -> no_return(). +get_proto_by_enum_name_as_fqbin(E) -> error({gpb_error, {badenum, E}}). + + +get_protos_by_pkg_name_as_fqbin(<<"helloworld">>) -> ["helloworld"]; +get_protos_by_pkg_name_as_fqbin(E) -> error({gpb_error, {badpkg, E}}). + + + +gpb_version_as_string() -> + "4.17.3". + +gpb_version_as_list() -> + [4,17,3].