├── include └── .gitignore ├── priv └── .gitignore ├── .tool-versions ├── benchmarks ├── google_message1.dat ├── google_message2.dat ├── d-msgs │ ├── d-msg-n01.dat │ ├── d-msg-n02.dat │ ├── d-msg-n03.dat │ ├── d-msg-n04.dat │ ├── d-msg-n05.dat │ ├── d-msg-n06.dat │ ├── d-msg-n07.dat │ ├── d-msg-n08.dat │ ├── d-msg-n09.dat │ ├── d-msg-n10.dat │ ├── d-msg-n11.dat │ ├── d-msg-n12.dat │ ├── d-msg-n13.dat │ ├── d-msg-n14.dat │ ├── d-msg-n15.dat │ ├── d-msg-n16.dat │ ├── d-msg-n17.dat │ ├── d-msg-n18.dat │ ├── d-msg-n19.dat │ ├── d-msg-n20.dat │ ├── d-msg-n21.dat │ ├── d-msg-n22.dat │ ├── d-msg-n23.dat │ ├── d-msg-n24.dat │ ├── d-msg-n25.dat │ ├── d-msg-n26.dat │ ├── d-msg-n27.dat │ ├── d-msg-n28.dat │ ├── d-msg-n29.dat │ ├── d-msg-n30.dat │ ├── d-msg-n31.dat │ ├── d-msg-n32.dat │ ├── d-msg-n33.dat │ ├── d-msg-n34.dat │ ├── d-msg-n35.dat │ ├── d-msg-n36.dat │ ├── d-msg-n37.dat │ ├── d-msg-n38.dat │ ├── d-msg-n39.dat │ ├── d-msg-n40.dat │ ├── d-msg-n41.dat │ ├── d-msg-n42.dat │ ├── d-msg-n43.dat │ ├── d-msg-n44.dat │ ├── d-msg-n45.dat │ ├── d-msg-n46.dat │ ├── d-msg-n47.dat │ ├── d-msg-n48.dat │ ├── d-msg-n49.dat │ ├── d-msg-n50.dat │ ├── d-msg-n51.dat │ ├── d-msg-n52.dat │ ├── d-msg-n53.dat │ ├── d-msg-n54.dat │ ├── d-msg-n55.dat │ ├── d-msg-n56.dat │ ├── d-msg-n57.dat │ ├── d-msg-n58.dat │ ├── d-msg-n59.dat │ ├── d-msg-n60.dat │ ├── d-msg-n61.dat │ ├── d-msg-n62.dat │ ├── d-msg-n63.dat │ ├── d-msg-n64.dat │ ├── d-msg-n65.dat │ ├── d-msg-n66.dat │ ├── d-msg-n67.dat │ ├── d-msg-n68.dat │ ├── d-msg-n69.dat │ ├── d-msg-n70.dat │ ├── d-msg-n71.dat │ ├── d-msg-n72.dat │ ├── d-msg-n73.dat │ ├── d-msg-n74.dat │ ├── d-msg-n75.dat │ ├── d-msg-n76.dat │ ├── d-msg-n77.dat │ └── d-all-concatenated.dat ├── rebar.config ├── README.txt ├── msg.proto ├── Makefile ├── proto-bench └── d.proto ├── .editorconfig ├── src ├── enif_protobuf.app.src └── enif_protobuf.erl ├── .gitignore ├── Makefile ├── test ├── ep_issue_31_tests.erl ├── ep_issue_19_tests.erl ├── ep_issue_enum_decoding_tests.erl ├── ep_issue_29_tests.erl ├── ep_issue_11_tests.erl ├── ep_issue_27_tests.erl ├── ep_tests.erl ├── ep_proper_encode_tests.erl └── ep_proper_decode_tests.erl ├── c_src ├── ep_cache.h ├── ep_node.h ├── ep_codec.h ├── Makefile ├── ep_cache.c ├── enif_protobuf.h ├── enif_protobuf.c ├── ep_node.c └── ep_encoder.c ├── rebar.config ├── scripts └── generate_emakefile.escript ├── .github └── workflows │ └── ci.yml └── README.md /include/.gitignore: -------------------------------------------------------------------------------- 1 | .* -------------------------------------------------------------------------------- /priv/.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | erlang 26.2.1 -------------------------------------------------------------------------------- /benchmarks/google_message1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/google_message1.dat -------------------------------------------------------------------------------- /benchmarks/google_message2.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/google_message2.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n01.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n01.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n02.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n02.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n03.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n03.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n04.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n04.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n05.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n05.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n06.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n06.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n07.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n07.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n08.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n08.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n09.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n09.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n10.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n10.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n11.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n11.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n12.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n12.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n13.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n13.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n14.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n14.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n15.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n15.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n16.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n16.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n17.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n17.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n18.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n18.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n19.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n19.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n20.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n20.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n21.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n21.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n22.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n22.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n23.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n23.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n24.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n24.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n25.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n25.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n26.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n26.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n27.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n27.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n28.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n28.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n29.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n29.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n30.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n30.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n31.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n31.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n32.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n32.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n33.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n33.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n34.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n34.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n35.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n35.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n36.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n36.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n37.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n37.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n38.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n38.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n39.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n39.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n40.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n40.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n41.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n41.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n42.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n42.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n43.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n43.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n44.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n44.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n45.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n45.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n46.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n46.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n47.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n47.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n48.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n48.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n49.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n49.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n50.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n50.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n51.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n51.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n52.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n52.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n53.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n53.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n54.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n54.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n55.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n55.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n56.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n56.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n57.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n57.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n58.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n58.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n59.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n59.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n60.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n60.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n61.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n61.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n62.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n62.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n63.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n63.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n64.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n64.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n65.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n65.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n66.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n66.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n67.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n67.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n68.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n68.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n69.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n69.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n70.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n70.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n71.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n71.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n72.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n72.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n73.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n73.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n74.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n74.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n75.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n75.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n76.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n76.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-msg-n77.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-msg-n77.dat -------------------------------------------------------------------------------- /benchmarks/d-msgs/d-all-concatenated.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinganix/enif_protobuf/HEAD/benchmarks/d-msgs/d-all-concatenated.dat -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 2 6 | indent_style = space 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /src/enif_protobuf.app.src: -------------------------------------------------------------------------------- 1 | {application, enif_protobuf, [ 2 | {description, "A Google Protobuf implementation with enif (Erlang nif)."}, 3 | {vsn, "git"}, 4 | {registered, []}, 5 | {applications, [kernel, stdlib]} 6 | ]}. 7 | -------------------------------------------------------------------------------- /benchmarks/rebar.config: -------------------------------------------------------------------------------- 1 | {deps, [ 2 | {enif_protobuf, ".*", {git, "https://github.com/jinganix/enif_protobuf.git", {branch, "master"}}}, 3 | {gpb, ".*", {git, "https://github.com/tomas-abrahamsson/gpb.git", {branch, "master"}}} 4 | ]}. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | *.dump 3 | *.iml 4 | *.o 5 | *.so 6 | .cproject 7 | .eunit 8 | .idea 9 | .project 10 | .rebar 11 | .settings 12 | _build 13 | benchmarks/tmp 14 | cmake-build-debug 15 | deps 16 | ebin 17 | log 18 | rebar.lock 19 | rebar3 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REBAR := $(shell which rebar3 2>/dev/null || echo ./rebar3) 2 | REBAR_URL := https://s3.amazonaws.com/rebar3/rebar3 3 | 4 | .PHONY: clean compile tests 5 | 6 | all: compile 7 | 8 | compile: $(REBAR) 9 | $(REBAR) compile 10 | 11 | tests: $(REBAR) 12 | $(REBAR) eunit 13 | 14 | clean: $(REBAR) 15 | $(REBAR) clean 16 | 17 | ./rebar3: 18 | erl -noshell -s inets start -s ssl start \ 19 | -eval '{ok, saved_to_file} = httpc:request(get, {"$(REBAR_URL)", []}, [], [{stream, "./rebar3"}])' \ 20 | -s inets stop -s init stop 21 | chmod +x ./rebar3 22 | -------------------------------------------------------------------------------- /test/ep_issue_31_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ep_issue_31_tests). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -include_lib("gpb/include/gpb.hrl"). 7 | 8 | % https://github.com/jinganix/enif_protobuf/issues/31 9 | issue_31_test() -> 10 | Defs = [ 11 | {{enum, very_long}, defs_enum_gen(100000, [])} 12 | ], 13 | 14 | enif_protobuf:purge_cache(), 15 | enif_protobuf:load_cache(Defs). 16 | 17 | defs_enum_gen(0, OptList) -> OptList; 18 | defs_enum_gen(N, CurOptList) when N > 0 -> 19 | defs_enum_gen(N-1, [{list_to_atom("opt" ++ integer_to_list(N)), N} | CurOptList]). 20 | 21 | -------------------------------------------------------------------------------- /test/ep_issue_19_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ep_issue_19_tests). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -include_lib("gpb/include/gpb.hrl"). 7 | 8 | -record(m1, {a}). 9 | 10 | % https://github.com/jinganix/enif_protobuf/issues/19 11 | issue_19_test() -> 12 | Defs = [ 13 | {{msg, m1}, [ 14 | #field{name = a, fnum = 1, rnum = #m1.a, type = uint64, occurrence = required, opts = []} 15 | ]} 16 | ], 17 | Bin = <<8, 181, 207, 209, 168, 154, 47>>, 18 | enif_protobuf:load_cache(Defs), 19 | {m1, 1621972248501} = enif_protobuf:decode(Bin, m1), 20 | {m1, 1621972248501} = gpb:decode_msg(Bin, m1, Defs). 21 | -------------------------------------------------------------------------------- /test/ep_issue_enum_decoding_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ep_issue_enum_decoding_tests). 2 | -compile(export_all). 3 | 4 | -include_lib("eunit/include/eunit.hrl"). 5 | -include_lib("gpb/include/gpb.hrl"). 6 | 7 | issue_enum_decoding_test() -> 8 | PAtom = pre_existing_atom, 9 | Defs = [{syntax, "proto3"}, 10 | {proto3_msgs, [enum_with_default]}, 11 | {{enum, en}, [{zzz, 0}, {PAtom, 1}, {nval, -1}]}, 12 | {{msg, enum_with_default}, [#?gpb_field{name = a, fnum = 1, rnum = 2, type = {enum, en}, 13 | occurrence = defaulty, opts = []}]}], 14 | ok = enif_protobuf:load_cache(Defs), 15 | {enum_with_default, zzz} = enif_protobuf:decode(<<>>, enum_with_default). 16 | 17 | -------------------------------------------------------------------------------- /c_src/ep_cache.h: -------------------------------------------------------------------------------- 1 | #ifndef __EP_CACHE_H__ 2 | #define __EP_CACHE_H__ 3 | 4 | #include "enif_protobuf.h" 5 | 6 | struct ep_cache_s { 7 | size_t size; 8 | size_t used; 9 | ep_node_id_t *ids; 10 | ep_node_name_t *names; 11 | 12 | uint32_t proto_v; 13 | }; 14 | 15 | int 16 | ep_cache_create(size_t size, ep_cache_t **cache); 17 | 18 | void 19 | ep_cache_destroy(ep_cache_t **cache); 20 | 21 | int 22 | ep_cache_insert(ep_node_t *node, ep_cache_t *cache); 23 | 24 | void 25 | ep_cache_sort(ep_cache_t *cache); 26 | 27 | ep_node_t * 28 | get_node_by_id(uint32_t id, ep_cache_t *cache); 29 | 30 | ep_node_t * 31 | get_node_by_name(ERL_NIF_TERM name, ep_cache_t *cache); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {deps, []}. 2 | 3 | {erl_opts, [debug_info]}. 4 | 5 | {pre_hooks, [ 6 | {"(linux|darwin|solaris)", compile, "make -C c_src"}, 7 | {"(freebsd|openbsd)", compile, "gmake -C c_src"} 8 | ]}. 9 | 10 | {post_hooks, [ 11 | {"(linux|darwin|solaris)", clean, "make -C c_src clean"}, 12 | {"(freebsd|openbsd)", clean, "gmake -C c_src clean"} 13 | ]}. 14 | 15 | {eunit_opts, [ 16 | verbose, 17 | {report, 18 | { 19 | eunit_surefire, 20 | [{dir, "./_build/test"}] 21 | }} 22 | ]}. 23 | 24 | {profiles, [ 25 | {test, [ 26 | {deps, [ 27 | {gpb, ".*", {git, "https://github.com/tomas-abrahamsson/gpb.git", {branch, "master"}}} 28 | ]} 29 | ]} 30 | ]}. 31 | -------------------------------------------------------------------------------- /test/ep_issue_29_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ep_issue_29_tests). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -include_lib("gpb/include/gpb.hrl"). 7 | 8 | -record(m1, {a}). 9 | 10 | % https://github.com/jinganix/enif_protobuf/issues/29 11 | issue_29_test() -> 12 | Defs = [ 13 | {{msg, a_message}, [ 14 | {field, non_trivial_map, 1, 2, {map, string, {msg, non_trivial_item}}, repeated, []} 15 | ]}, 16 | {{msg, non_trivial_item}, [ 17 | {field, item, 1, 2, int64, optional, []} 18 | ]} 19 | ], 20 | Bin = <<10,7,10,1,97,18,2,8,1>>, 21 | Bin = gpb:encode_msg({a_message, [{"a", {non_trivial_item, 1}}]}, Defs), 22 | Bin = enif_protobuf:encode_msg({a_message, [{"a", {non_trivial_item, 1}}]}, Defs). 23 | -------------------------------------------------------------------------------- /scripts/generate_emakefile.escript: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env escript 2 | 3 | main(_Args) -> 4 | CompileOpts = [debug_info], 5 | CheckFor = [{proper, 'PROPER', "proper.hrl"}, {eqc, 'EQC', "eqc.hrl"}], 6 | Opts = lists:foldl(fun({Mod, Define, Hrl}, Acc) -> 7 | case is_avail(Mod, Hrl) of 8 | false -> 9 | Acc; 10 | true -> 11 | [{d, Define} | Acc] 12 | end 13 | end, CompileOpts, CheckFor), 14 | MakeLine = {'*', Opts}, 15 | 16 | FileName = filename:join(["test", "Emakefile"]), 17 | case file:open(FileName, [write]) of 18 | {ok, File} -> 19 | io:format(File, "~p.~n", [MakeLine]); 20 | Error -> 21 | io:format("Could not open file ~s: ~p~n", [FileName, Error]) 22 | end. 23 | 24 | is_avail(AppMod, Hrl) -> 25 | case code:lib_dir(AppMod) of 26 | {error, bad_name} -> 27 | false; 28 | Dir -> 29 | filelib:is_regular(filename:join([Dir, "include", Hrl])) 30 | end. 31 | -------------------------------------------------------------------------------- /test/ep_issue_11_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ep_issue_11_tests). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -include_lib("gpb/include/gpb.hrl"). 7 | 8 | % https://github.com/jinganix/enif_protobuf/issues/11 9 | issue_11_test() -> 10 | Defs = [ 11 | {{msg, pro_180_items_list}, [ 12 | #field{name = id, fnum = 1, rnum = 2, type = uint32, occurrence = defaulty, opts = []}, 13 | #field{name = num, fnum = 2, rnum = 3, type = uint32, occurrence = defaulty, opts = []}, 14 | #field{name = text, fnum = 3, rnum = 4, type = string, occurrence = defaulty, opts = []} 15 | ]}, 16 | {{msg, pro_180_all_prop}, [ 17 | #field{name = key, fnum = 1, rnum = 2, type = uint32, occurrence = defaulty, opts = []}, 18 | #field{name = value, fnum = 2, rnum = 3, type = uint32, occurrence = defaulty, opts = []}, 19 | #field{name = incval, fnum = 3, rnum = 4, type = uint32, occurrence = defaulty, opts = []} 20 | ]}, 21 | {proto3_msgs, [pro_180_all_prop, pro_180_items_list]} 22 | ], 23 | ok = enif_protobuf:load_cache(Defs). 24 | -------------------------------------------------------------------------------- /src/enif_protobuf.erl: -------------------------------------------------------------------------------- 1 | -module(enif_protobuf). 2 | 3 | -export([ 4 | set_opts/1, 5 | load_cache/1, 6 | purge_cache/0, 7 | encode/1, 8 | encode_msg/2, 9 | decode/2, 10 | decode_msg/3, 11 | debug_term/1 12 | ]). 13 | 14 | -define(NOT_LOADED, not_loaded(?LINE)). 15 | 16 | -compile([no_native]). 17 | 18 | -on_load(init/0). 19 | 20 | init() -> 21 | PrivDir = case code:priv_dir(?MODULE) of 22 | {error, _} -> 23 | BeamDir = filename:dirname(code:which(?MODULE)), 24 | AppPath = filename:dirname(BeamDir), 25 | filename:join(AppPath, "priv"); 26 | Path -> 27 | Path 28 | end, 29 | Threads = erlang:system_info(schedulers), 30 | ok = erlang:load_nif(filename:join(PrivDir, "enif_protobuf"), Threads). 31 | 32 | not_loaded(Line) -> 33 | erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}). 34 | 35 | set_opts(_Opts) -> 36 | ?NOT_LOADED. 37 | 38 | load_cache(_List) -> 39 | ?NOT_LOADED. 40 | 41 | purge_cache() -> 42 | ?NOT_LOADED. 43 | 44 | encode(_Tuple) -> 45 | ?NOT_LOADED. 46 | 47 | decode(_Binary, _Name) -> 48 | ?NOT_LOADED. 49 | 50 | debug_term(_Term) -> 51 | ?NOT_LOADED. 52 | 53 | encode_msg(Msg, Defs) -> 54 | ok = load_cache(Defs), 55 | encode(Msg). 56 | 57 | decode_msg(Bin, Name, Defs) -> 58 | ok = load_cache(Defs), 59 | decode(Bin, Name). 60 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | schedule: 12 | - cron: '0 10 * * *' # Once per day at 10am UTC 13 | 14 | jobs: 15 | ubuntu: 16 | name: Test on Ubuntu 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | matrix: 21 | otp: ["24", "25", "26"] 22 | fail-fast: false 23 | 24 | container: 25 | image: erlang:${{ matrix.otp }} 26 | 27 | env: 28 | ERL_LIBS: './' 29 | 30 | steps: 31 | - uses: actions/checkout@v4 32 | - name: Checkout Proper 33 | uses: actions/checkout@v4 34 | with: 35 | repository: proper-testing/proper 36 | path: "./proper" 37 | - name: Run test 38 | run: | 39 | make 40 | make tests 41 | 42 | macos: 43 | name: Test on MacOS 44 | runs-on: macos-latest 45 | 46 | strategy: 47 | matrix: 48 | otp: ["24", "25", "26"] 49 | fail-fast: false 50 | 51 | env: 52 | ERL_LIBS: './' 53 | 54 | steps: 55 | - uses: actions/checkout@v4 56 | - name: Brew Version Check 57 | run: brew --version 58 | - name: Keep Brew Fresh 59 | run: brew update 60 | - name: Install Erlang 61 | run: brew install erlang@${{ matrix.otp }} 62 | - name: Brew Link 63 | run: brew link erlang@${{ matrix.otp }} --force 64 | - name: Checkout Proper 65 | uses: actions/checkout@v4 66 | with: 67 | repository: proper-testing/proper 68 | path: "./proper" 69 | - name: Run test 70 | run: | 71 | make 72 | make tests 73 | -------------------------------------------------------------------------------- /c_src/ep_node.h: -------------------------------------------------------------------------------- 1 | #ifndef __EP_NODE_H__ 2 | #define __EP_NODE_H__ 3 | 4 | #include "enif_protobuf.h" 5 | 6 | struct ep_enum_field_s { 7 | ERL_NIF_TERM name; 8 | int32_t value; 9 | uint32_t proto_v; 10 | }; 11 | 12 | struct ep_field_s { 13 | ERL_NIF_TERM name; 14 | occurrence_type_e o_type; 15 | field_type_e type; 16 | ep_node_t *sub_node; 17 | ERL_NIF_TERM sub_name; 18 | ERL_NIF_TERM defaut_value; 19 | uint32_t id; 20 | uint32_t fnum; 21 | uint32_t rnum; 22 | uint32_t proto_v; 23 | uint32_t is_oneof; 24 | uint32_t packed; 25 | }; 26 | 27 | struct ep_fnum_field_s { 28 | uint32_t fnum; 29 | ep_field_t *field; 30 | }; 31 | 32 | struct ep_node_s { 33 | node_type_e n_type; 34 | ERL_NIF_TERM name; 35 | uint32_t id; 36 | uint32_t proto_v; 37 | uint32_t size; 38 | uint32_t v_size; 39 | void *fields; 40 | void *v_fields; 41 | }; 42 | 43 | struct ep_node_id_s { 44 | uint32_t id; 45 | ep_node_t *node; 46 | }; 47 | 48 | struct ep_node_name_s { 49 | ERL_NIF_TERM name; 50 | ep_node_t *node; 51 | }; 52 | 53 | void 54 | free_node(ep_node_t *node); 55 | 56 | ERL_NIF_TERM 57 | parse_node(ErlNifEnv *env, ERL_NIF_TERM term, ep_node_t **node, uint32_t proto_v, ERL_NIF_TERM proto3_list); 58 | 59 | ERL_NIF_TERM 60 | prelink_nodes(ErlNifEnv *env, ep_cache_t *cache); 61 | 62 | void 63 | stack_ensure_all(ErlNifEnv *env, ep_cache_t *cache); 64 | 65 | ERL_NIF_TERM 66 | stack_ensure(ErlNifEnv *env, ep_stack_t *stack, ep_spot_t **spot); 67 | 68 | int 69 | get_field_compare_name(const void *a, const void *b); 70 | 71 | int 72 | get_map_field_compare_fnum(const void *a, const void *b); 73 | 74 | int 75 | get_field_compare_fnum(const void *a, const void *b); 76 | 77 | int 78 | get_enum_compare_name(const void *a, const void *b); 79 | 80 | int 81 | get_enum_compare_value(const void *a, const void *b); 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /c_src/ep_codec.h: -------------------------------------------------------------------------------- 1 | #ifndef __EP_CODEC_H__ 2 | #define __EP_CODEC_H__ 3 | 4 | #include "enif_protobuf.h" 5 | 6 | #define MAX_UINT64_ENCODED_SIZE 10 7 | 8 | enum { 9 | spot_default, 10 | spot_tuple, 11 | spot_map, 12 | spot_list 13 | }; 14 | 15 | typedef enum { 16 | WIRE_TYPE_VARINT = 0, 17 | WIRE_TYPE_64BIT = 1, 18 | WIRE_TYPE_LENGTH_PREFIXED = 2, 19 | /* "Start group" and "end group" wire types are unsupported. */ 20 | WIRE_TYPE_32BIT = 5, 21 | } wire_type_e; 22 | 23 | static inline int16_t 24 | swap_int16(int16_t value) 25 | { 26 | return (value << 8) | ((value >> 8) & 0x00ff); 27 | } 28 | 29 | static inline uint16_t 30 | swap_uint16(uint16_t value) 31 | { 32 | return (value << 8) | (value >> 8); 33 | } 34 | 35 | static inline int32_t 36 | swap_int32(int32_t value) 37 | { 38 | return ((value & 0x000000ff) << 24) | 39 | ((value & 0x0000ff00) << 8) | 40 | ((value & 0x00ff0000) >> 8) | 41 | ((value >> 24) & 0x000000ff); 42 | } 43 | 44 | static inline uint32_t 45 | swap_uint32(uint32_t value) 46 | { 47 | return ((value & 0x000000ff) << 24) | 48 | ((value & 0x0000ff00) << 8) | 49 | ((value & 0x00ff0000) >> 8) | 50 | (value >> 24); 51 | } 52 | 53 | static inline int64_t 54 | swap_int64(int64_t value) 55 | { 56 | return ((value & 0x00000000000000ffUL) << 56) | 57 | ((value & 0x000000000000ff00UL) << 40) | 58 | ((value & 0x0000000000ff0000UL) << 24) | 59 | ((value & 0x00000000ff000000UL) << 8) | 60 | ((value & 0x000000ff00000000UL) >> 8) | 61 | ((value & 0x0000ff0000000000UL) >> 24) | 62 | ((value & 0x00ff000000000000UL) >> 40) | 63 | ((value >> 56) & 0x00000000000000ffUL); 64 | } 65 | 66 | static inline uint64_t 67 | swap_uint64(uint64_t value) 68 | { 69 | return ((value & 0x00000000000000ffUL) << 56) | 70 | ((value & 0x000000000000ff00UL) << 40) | 71 | ((value & 0x0000000000ff0000UL) << 24) | 72 | ((value & 0x00000000ff000000UL) << 8) | 73 | ((value & 0x000000ff00000000UL) >> 8) | 74 | ((value & 0x0000ff0000000000UL) >> 24) | 75 | ((value & 0x00ff000000000000UL) >> 40) | 76 | (value >> 56); 77 | } 78 | 79 | /* 80 | * encode 81 | */ 82 | ERL_NIF_TERM 83 | encode(ErlNifEnv *env, ERL_NIF_TERM term, ep_tdata_t *tdata); 84 | 85 | /* 86 | * decode 87 | */ 88 | ERL_NIF_TERM 89 | decode(ErlNifEnv *env, ep_tdata_t *tdata, ep_node_t *node); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /benchmarks/README.txt: -------------------------------------------------------------------------------- 1 | About the benchmarks 2 | -------------------- 3 | 4 | The benchmark setup is very similar to the benchmarks found in the 5 | protobuf's subversion repository. 6 | 7 | Deviations from the benchmarks in protobuf's subversion repository: 8 | 9 | * The google_size.proto and the google_speed.proto have been 10 | fused into msg.proto since gpb doesn't know how to optimize 11 | neither for speed nor for code size. 12 | * The "repeated group" construction has been changed into a 13 | repeated (sub) message, since groups were not supported by gpb originally. 14 | The google_message2.dat file has been updated accordingly. 15 | * Addition of the d.proto and messages in d-msgs/ This contains 16 | randomly generated messages according to d.proto. 17 | The d-msgs/d-all-concatenated.dat will cause a lot of message 18 | merging to happen upon deserialization. 19 | 20 | Running a benchmark 21 | ------------------- 22 | 23 | 1. Build the benchmarking code (in this benchmarks directory) 24 | $ make 25 | 26 | 2. Run the benchmarks: 27 | $ make benchmarks 28 | 29 | This will call the proto-bench escript. Arguments are given in 30 | triples - the first is the module name; the second is the message 31 | name, the third is the filename. For example: 32 | $ ./proto-bench \ msg Message1 google_message1.dat \ 33 | msg Message2 google_message2.dat 34 | 35 | There are currently some alternative benchmarks one can run: 36 | 37 | - make maps-benchmarks 38 | 39 | This runs the same benchmarks (the google messages) with code 40 | compiled for maps with maps_unset_optional = omitted 41 | 42 | - make erl-benchamrks 43 | 44 | Benchmark the google messages using records (suffix = _r), and 45 | twice with maps; once with maps_unset_optional = omitted 46 | (suffix = _mo) once with maps_unset_optional = present_undefined 47 | (suffix = _mp) thus, the target "benchmarks" and "maps-benchmarks" 48 | run subsets of this target. 49 | 50 | - make nif-benchmarks 51 | 52 | Benchmark the google messages using the nif bindings to Google's 53 | libprotobuf 54 | 55 | To run the benchmarks with HiPE-compiled code, set the HIPE 56 | variable, for example like this: 57 | 58 | make HIPE=1 erl-benchmarks 59 | 60 | Please note that things here might change as development shifts, 61 | for instance target names and variables, these names are super stable. 62 | 63 | 3. Wait! Each test runs for around 30--35 seconds, and there are 2 tests 64 | per msg/data combination. The above command will take about 65 | 4.5 -- 5 minutes to run. 66 | -------------------------------------------------------------------------------- /test/ep_issue_27_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ep_issue_27_tests). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -include_lib("gpb/include/gpb.hrl"). 7 | 8 | -record(default_uint32_msg, { 9 | count_num = 0 :: non_neg_integer() | undefined % = 1, optional, 32 bits 10 | }). 11 | 12 | -record(default_string_mix_msg, { 13 | count_num = 0 :: non_neg_integer() | undefined, % = 1, optional, 32 bits 14 | rname = [] :: unicode:chardata() | undefined, % = 2, optional 15 | py_id = 0 :: non_neg_integer() | undefined, % = 3, optional, 32 bits 16 | status_arr = [] :: [non_neg_integer()] | undefined, % = 4, repeated, 32 bits 17 | py_num = 0 :: non_neg_integer() | undefined, % = 5, optional, 32 bits 18 | gname = [] :: unicode:chardata() | undefined, % = 6, optional 19 | gnum = 0 :: non_neg_integer() | undefined % = 7, optional, 32 bits 20 | }). 21 | 22 | % https://github.com/jinganix/enif_protobuf/issues/27 23 | get_proto_defs() -> 24 | [ 25 | {syntax, "proto3"}, 26 | {{msg, default_uint32_msg}, [ 27 | #field{name = count_num, fnum = 1, rnum = 2, type = uint32, occurrence = optional, opts = []} 28 | ]}, 29 | {proto3_msgs, [default_uint32_msg]} 30 | ]. 31 | 32 | get_proto_defs_02() -> 33 | [ 34 | {syntax, "proto3"}, 35 | {{msg, default_string_mix_msg}, [ 36 | #field{name = count_num, fnum = 1, rnum = 2, type = uint32, occurrence = optional, opts = []}, 37 | #field{name = rname, fnum = 2, rnum = 3, type = string, occurrence = optional, opts = []}, 38 | #field{name = py_id, fnum = 3, rnum = 4, type = uint32, occurrence = optional, opts = []}, 39 | #field{name = status_arr, fnum = 4, rnum = 5, type = uint32, occurrence = repeated, opts = [packed]}, 40 | #field{name = py_num, fnum = 5, rnum = 6, type = uint32, occurrence = optional, opts = []}, 41 | #field{name = gname, fnum = 11, rnum = 7, type = string, occurrence = optional, opts = []}, 42 | #field{name = gnum, fnum = 12, rnum = 8, type = uint32, occurrence = optional, opts = []} 43 | ]}, 44 | {proto3_msgs, [default_string_mix_msg]} 45 | ]. 46 | 47 | issue_27_test() -> 48 | Defs = get_proto_defs_02() ++ get_proto_defs(), 49 | enif_protobuf:load_cache(Defs), 50 | Msg1 = #default_uint32_msg{}, 51 | Bin1 = <<8, 0>>, 52 | Bin1 = gpb:encode_msg(Msg1, Defs), 53 | Bin1 = enif_protobuf:encode_msg(Msg1, Defs), 54 | 55 | Bin2 = <<8,99,18,4,116,101,115,116,24,0,40,0,90,0,96,0>>, 56 | Msg2 = #default_string_mix_msg{count_num = 99, rname = <<"test">>, py_id = 0, status_arr = [], gname = <<>>}, 57 | Bin2 = gpb:encode_msg(Msg2, Defs), 58 | Bin2 = enif_protobuf:encode_msg(Msg2, Defs). 59 | -------------------------------------------------------------------------------- /c_src/Makefile: -------------------------------------------------------------------------------- 1 | # Based on c_src.mk from erlang.mk by Loic Hoguin 2 | 3 | CURDIR := $(shell pwd) 4 | BASEDIR := $(abspath $(CURDIR)/..) 5 | 6 | PROJECT ?= $(notdir $(BASEDIR)) 7 | PROJECT := $(strip $(PROJECT)) 8 | 9 | ERTS_INCLUDE_DIR ?= $(shell erl -noshell -eval "io:format(\"~ts/erts-~ts/include/\", [code:root_dir(), erlang:system_info(version)])." -s erlang halt) 10 | ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, include)])." -s erlang halt) 11 | ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, lib)])." -s erlang halt) 12 | 13 | C_SRC_DIR = $(CURDIR) 14 | C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so 15 | 16 | # System type and C compiler/flags. 17 | 18 | UNAME_SYS := $(shell uname -s) 19 | ifeq ($(UNAME_SYS), Darwin) 20 | ARCH_SYS := $(shell uname -p) 21 | ifeq ($(ARCH_SYS), arm) 22 | ARCH_NAME := arm64 23 | else 24 | ARCH_NAME := x86_64 25 | endif 26 | CC ?= cc 27 | CFLAGS ?= -O3 -std=c99 -arch $(ARCH_NAME) -finline-functions -Wall -Wmissing-prototypes 28 | CXXFLAGS ?= -O3 -arch $(ARCH_NAME) -finline-functions -Wall 29 | LDFLAGS ?= -arch $(ARCH_NAME) -flat_namespace -undefined dynamic_lookup 30 | else ifeq ($(UNAME_SYS), FreeBSD) 31 | CC ?= cc 32 | CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes 33 | CXXFLAGS ?= -O3 -finline-functions -Wall 34 | else ifeq ($(UNAME_SYS), Linux) 35 | CC ?= gcc 36 | CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes 37 | CXXFLAGS ?= -O3 -finline-functions -Wall 38 | endif 39 | 40 | CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) 41 | CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) 42 | 43 | LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lei -lpthread 44 | LDFLAGS += -shared 45 | 46 | # Verbosity. 47 | 48 | c_verbose_0 = @echo " C " $(?F); 49 | c_verbose = $(c_verbose_$(V)) 50 | 51 | cpp_verbose_0 = @echo " CPP " $(?F); 52 | cpp_verbose = $(cpp_verbose_$(V)) 53 | 54 | link_verbose_0 = @echo " LD " $(@F); 55 | link_verbose = $(link_verbose_$(V)) 56 | 57 | SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \)) 58 | OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) 59 | 60 | COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c 61 | COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c 62 | 63 | $(C_SRC_OUTPUT): $(OBJECTS) 64 | @mkdir -p $(BASEDIR)/priv/ 65 | $(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT) 66 | 67 | %.o: %.c 68 | $(COMPILE_C) $(OUTPUT_OPTION) $< 69 | 70 | %.o: %.cc 71 | $(COMPILE_CPP) $(OUTPUT_OPTION) $< 72 | 73 | %.o: %.C 74 | $(COMPILE_CPP) $(OUTPUT_OPTION) $< 75 | 76 | %.o: %.cpp 77 | $(COMPILE_CPP) $(OUTPUT_OPTION) $< 78 | 79 | clean: 80 | @rm -f $(C_SRC_OUTPUT) $(OBJECTS) 81 | -------------------------------------------------------------------------------- /c_src/ep_cache.c: -------------------------------------------------------------------------------- 1 | #include "enif_protobuf.h" 2 | 3 | int 4 | ep_cache_create(size_t size, ep_cache_t **cache) 5 | { 6 | ep_cache_t *ca; 7 | 8 | ca = _calloc(sizeof(ep_cache_t), 1); 9 | if (ca == NULL) { 10 | return RET_ERROR; 11 | } 12 | 13 | ca->size = size; 14 | ca->used = 0; 15 | ca->ids = _calloc(sizeof(ep_node_id_t) * size, 1); 16 | if (ca->ids == NULL) { 17 | _free(ca); 18 | return RET_ERROR; 19 | } 20 | 21 | ca->names = _calloc(sizeof(ep_node_name_t) * size, 1); 22 | if (ca->names == NULL) { 23 | _free(ca->ids); 24 | _free(ca); 25 | return RET_ERROR; 26 | } 27 | 28 | *cache = ca; 29 | return RET_OK; 30 | } 31 | 32 | void 33 | ep_cache_destroy(ep_cache_t **cache) 34 | { 35 | uint32_t i; 36 | ep_cache_t *ca = *cache; 37 | 38 | if (*cache == NULL) { 39 | return; 40 | } 41 | 42 | for (i = 0; i < ca->used; i++) { 43 | 44 | /* 45 | * Ids.node and Types.node point to the same node. Just free once. 46 | */ 47 | if (ca->ids[i].node != NULL) { 48 | free_node(ca->ids[i].node); 49 | } 50 | } 51 | 52 | _free(ca->ids); 53 | _free(ca->names); 54 | _free(ca); 55 | 56 | *cache = NULL; 57 | } 58 | 59 | int 60 | ep_cache_insert(ep_node_t *node, ep_cache_t *cache) 61 | { 62 | if (cache->used >= cache->size) { 63 | return RET_ERROR; 64 | } 65 | 66 | cache->ids[cache->used].id = node->id; 67 | cache->ids[cache->used].node = node; 68 | 69 | cache->names[cache->used].name = node->name; 70 | cache->names[cache->used].node = node; 71 | 72 | cache->used++; 73 | 74 | return RET_OK; 75 | } 76 | 77 | static int 78 | sort_compare_id(const void *a, const void *b) 79 | { 80 | return (int) (((ep_node_id_t *) a)->id - ((ep_node_id_t *) b)->id); 81 | } 82 | 83 | static int 84 | sort_compare_type(const void *a, const void *b) 85 | { 86 | return (int) (((ep_node_name_t *) a)->name - ((ep_node_name_t *) b)->name); 87 | } 88 | 89 | void 90 | ep_cache_sort(ep_cache_t *cache) 91 | { 92 | qsort(cache->ids, cache->used, sizeof(ep_node_id_t), sort_compare_id); 93 | qsort(cache->names, cache->used, sizeof(ep_node_name_t), sort_compare_type); 94 | } 95 | 96 | static int 97 | search_compare_id(const void *a, const void *b) 98 | { 99 | return (int) (*((uint32_t *) a) - ((ep_node_id_t *) b)->id); 100 | } 101 | 102 | ep_node_t * 103 | get_node_by_id(uint32_t id, ep_cache_t *cache) 104 | { 105 | ep_node_id_t *i_node; 106 | 107 | i_node = bsearch(&id, cache->ids, cache->used, sizeof(ep_node_id_t), search_compare_id); 108 | if (i_node == NULL) { 109 | return NULL; 110 | } 111 | 112 | return i_node->node; 113 | } 114 | 115 | static int 116 | search_compare_name(const void *a, const void *b) 117 | { 118 | return (int) (*((ERL_NIF_TERM *) a) - ((ep_node_name_t *) b)->name); 119 | } 120 | 121 | ep_node_t * 122 | get_node_by_name(ERL_NIF_TERM name, ep_cache_t *cache) 123 | { 124 | ep_node_name_t *n_node; 125 | 126 | n_node = bsearch(&name, cache->names, cache->used, sizeof(ep_node_name_t), search_compare_name); 127 | if (n_node == NULL) { 128 | return NULL; 129 | } 130 | 131 | return n_node->node; 132 | } 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI](https://github.com/jinganix/enif_protobuf/actions/workflows/ci.yml/badge.svg)](https://github.com/jinganix/enif_protobuf/actions/workflows/ci.yml) 2 | 3 | # enif_protobuf 4 | A Google Protobuf implementation with enif (Erlang nif). 5 | 6 | Base on gpb, see more info at 7 | https://github.com/tomas-abrahamsson/gpb 8 | 9 | Basic example of using epb 10 | -------------------------- 11 | 12 | Let's say we have a protobuf file, `x.proto` 13 | ```protobuf 14 | message Person { 15 | required string name = 1; 16 | required int32 id = 2; 17 | optional string email = 3; 18 | } 19 | ``` 20 | We can generate code for this definition in a number of different 21 | ways. Here we use the command line tool. 22 | ```shell 23 | # export GPB_PATH=/path/to/gpb 24 | # ${GPB_PATH}/bin/protoc-erl -I. x.proto 25 | ``` 26 | Now we've got `x.erl` and `x.hrl`. First we compile it. 27 | ```shell 28 | # erlc -I${GPB_PATH}/include x.erl 29 | ``` 30 | 31 | Then we can try it out in the Erlang shell. When use rebar3, the `EPB_EBIN_PATH` 32 | is in `_build` directory. 33 | ```erlang 34 | # export EPB_EBIN_PATH=/path/to/enif_protobuf/ebin 35 | # erl -pa ${EPB_EBIN_PATH} 36 | Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false] 37 | 38 | Eshell V7.3 (abort with ^G) 39 | 1> rr("x.hrl"). 40 | ['Person'] 41 | 2> Bin=x:encode_msg(#'Person'{name="abc def", id=345, email="a@example.com"}). 42 | <<10,7,97,98,99,32,100,101,102,16,217,2,26,13,97,64,101, 43 | 120,97,109,112,108,101,46,99,111,109>> 44 | 3> enif_protobuf:load_cache(x:get_msg_defs()). 45 | ok 46 | 4> Bin=enif_protobuf:encode(#'Person'{name="abc def", id=345, email="a@example.com"}). 47 | <<10,7,97,98,99,32,100,101,102,16,217,2,26,13,97,64,101, 48 | 120,97,109,112,108,101,46,99,111,109>> 49 | 5> enif_protobuf:decode(Bin,'Person'). 50 | #'Person'{name = <<"abc def">>,id = 345, 51 | email = <<"a@example.com">>} 52 | 6> enif_protobuf:set_opts([{string_as_list, true}]). 53 | ok 54 | 7> enif_protobuf:decode(Bin,'Person'). 55 | #'Person'{name = "abc def",id = 345,email = "a@example.com"} 56 | 8> enif_protobuf:encode(#'Person'{name="你好", id=345, email="a@example.com"}). 57 | {error,[20320,22909]} 58 | 9> enif_protobuf:set_opts([{with_utf8, true}]). 59 | ok 60 | 10> enif_protobuf:encode(#'Person'{name="你好", id=345, email="a@example.com"}). 61 | <<10,6,228,189,160,229,165,189,16,217,2,26,13,97,64,101, 62 | 120,97,109,112,108,101,46,99,111,109>> 63 | ``` 64 | 65 | Performance 66 | ----------- 67 | 68 | Here is a comparison between enif_protobuf(epb) and gpb 69 | 70 | [MB/s] | epb | gpb | gpb nif | 71 | --------------+--------+-------+---------+ 72 | small msgs | | | | 73 | serialize | 116.09 | 32.17 | 38.02 | 74 | deserialize | 111.88 | 35.57 | 61.87 | 75 | --------------+--------+-------+---------+ 76 | large msgs | | | | 77 | serialize | 122.86 | 20.90 | 38.64 | 78 | deserialize | 114.72 | 32.53 | 59.29 | 79 | --------------+--------+-------+---------+ 80 | 81 | HW info 82 | 83 | CPU info 84 | model name : Intel(R) Core(TM) i3-3220 CPU @ 3.30GHz 85 | cache size : 3072 KB 86 | cores/threads : 4 87 | bogomips : 6585.04 88 | 89 | Erlang (SMP,ASYNC_THREADS) (BEAM) emulator version 7.3 90 | 91 | The performances are measured as number of processed MB/s, serialized form. Higher values means better performance. 92 | 93 | The benchmarks are run with small and large messages (228 and 84584 bytes, respectively, in serialized form) 94 | -------------------------------------------------------------------------------- /benchmarks/msg.proto: -------------------------------------------------------------------------------- 1 | message Message1 { 2 | required string field1 = 1; 3 | optional string field9 = 9; 4 | optional string field18 = 18; 5 | optional bool field80 = 80 [default=false]; 6 | optional bool field81 = 81 [default=true]; 7 | required int32 field2 = 2; 8 | required int32 field3 = 3; 9 | optional int32 field280 = 280; 10 | optional int32 field6 = 6 [default=0]; 11 | optional int64 field22 = 22; 12 | optional string field4 = 4; 13 | repeated fixed64 field5 = 5; 14 | optional bool field59 = 59 [default=false]; 15 | optional string field7 = 7; 16 | optional int32 field16 = 16; 17 | optional int32 field130 = 130 [default=0]; 18 | optional bool field12 = 12 [default=true]; 19 | optional bool field17 = 17 [default=true]; 20 | optional bool field13 = 13 [default=true]; 21 | optional bool field14 = 14 [default=true]; 22 | optional int32 field104 = 104 [default=0]; 23 | optional int32 field100 = 100 [default=0]; 24 | optional int32 field101 = 101 [default=0]; 25 | optional string field102 = 102; 26 | optional string field103 = 103; 27 | optional int32 field29 = 29 [default=0]; 28 | optional bool field30 = 30 [default=false]; 29 | optional int32 field60 = 60 [default=-1]; 30 | optional int32 field271 = 271 [default=-1]; 31 | optional int32 field272 = 272 [default=-1]; 32 | optional int32 field150 = 150; 33 | optional int32 field23 = 23 [default=0]; 34 | optional bool field24 = 24 [default=false]; 35 | optional int32 field25 = 25 [default=0]; 36 | optional Message1SubMessage field15 = 15; 37 | optional bool field78 = 78; 38 | optional int32 field67 = 67 [default=0]; 39 | optional int32 field68 = 68; 40 | optional int32 field128 = 128 [default=0]; 41 | optional string field129 = 129 [default="xxxxxxxxxxxxxxxxxxxxx"]; 42 | optional int32 field131 = 131 [default=0]; 43 | } 44 | 45 | message Message1SubMessage { 46 | optional int32 field1 = 1 [default=0]; 47 | optional int32 field2 = 2 [default=0]; 48 | optional int32 field3 = 3 [default=0]; 49 | optional string field15 = 15; 50 | optional bool field12 = 12 [default=true]; 51 | optional int64 field13 = 13; 52 | optional int64 field14 = 14; 53 | optional int32 field16 = 16; 54 | optional int32 field19 = 19 [default=2]; 55 | optional bool field20 = 20 [default=true]; 56 | optional bool field28 = 28 [default=true]; 57 | optional fixed64 field21 = 21; 58 | optional int32 field22 = 22; 59 | optional bool field23 = 23 [ default=false ]; 60 | optional bool field206 = 206 [default=false]; 61 | optional fixed32 field203 = 203; 62 | optional int32 field204 = 204; 63 | optional string field205 = 205; 64 | optional uint64 field207 = 207; 65 | optional uint64 field300 = 300; 66 | } 67 | 68 | message Message2 { 69 | optional string field1 = 1; 70 | optional int64 field3 = 3; 71 | optional int64 field4 = 4; 72 | optional int64 field30 = 30; 73 | optional bool field75 = 75 [default=false]; 74 | optional string field6 = 6; 75 | optional bytes field2 = 2; 76 | optional int32 field21 = 21 [default=0]; 77 | optional int32 field71 = 71; 78 | optional float field25 = 25; 79 | optional int32 field109 = 109 [default=0]; 80 | optional int32 field210 = 210 [default=0]; 81 | optional int32 field211 = 211 [default=0]; 82 | optional int32 field212 = 212 [default=0]; 83 | optional int32 field213 = 213 [default=0]; 84 | optional int32 field216 = 216 [default=0]; 85 | optional int32 field217 = 217 [default=0]; 86 | optional int32 field218 = 218 [default=0]; 87 | optional int32 field220 = 220 [default=0]; 88 | optional int32 field221 = 221 [default=0]; 89 | optional float field222 = 222 [default=0.0]; 90 | optional int32 field63 = 63; 91 | repeated Message2Group1 Group1 = 10; 92 | repeated string field128 = 128; 93 | optional int64 field131 = 131; 94 | repeated string field127 = 127; 95 | optional int32 field129 = 129; 96 | repeated int64 field130 = 130; 97 | optional bool field205 = 205 [default=false]; 98 | optional bool field206 = 206 [default=false]; 99 | } 100 | 101 | message Message2Group1 { 102 | required float field11 = 11; 103 | optional float field26 = 26; 104 | optional string field12 = 12; 105 | optional string field13 = 13; 106 | repeated string field14 = 14; 107 | required uint64 field15 = 15; 108 | optional int32 field5 = 5; 109 | optional string field27 = 27; 110 | optional int32 field28 = 28; 111 | optional string field29 = 29; 112 | optional string field16 = 16; 113 | repeated string field22 = 22; 114 | repeated int32 field73 = 73; 115 | optional int32 field20 = 20 [default=0]; 116 | optional string field24 = 24; 117 | optional Message2GroupedMessage field31 = 31; 118 | } 119 | 120 | message Message2GroupedMessage { 121 | optional float field1 = 1; 122 | optional float field2 = 2; 123 | optional float field3 = 3 [default=0.0]; 124 | optional bool field4 = 4; 125 | optional bool field5 = 5; 126 | optional bool field6 = 6 [default=true]; 127 | optional bool field7 = 7 [default=false]; 128 | optional float field8 = 8; 129 | optional bool field9 = 9; 130 | optional float field10 = 10; 131 | optional int64 field11 = 11; 132 | } 133 | -------------------------------------------------------------------------------- /benchmarks/Makefile: -------------------------------------------------------------------------------- 1 | REBAR := $(shell which rebar3 2>/dev/null || echo ./rebar3) 2 | REBAR_URL := https://s3.amazonaws.com/rebar3/rebar3 3 | 4 | erl ?= erl 5 | erlc ?= erlc 6 | escript ?= escript 7 | 8 | ifdef HIPE 9 | HIPE_ERLC_OPTS := +'native' +'{hipe,[o3]}' 10 | else 11 | HIPE_ERLC_OPTS := 12 | endif 13 | 14 | PROTOBUF_CFLAGS ?= $(shell pkg-config --cflags protobuf) 15 | PROTOBUF_LDFLAGS ?= $(shell pkg-config --libs protobuf) 16 | PROTOC ?= $(shell which protoc) 17 | ifdef PROTOHOME 18 | PROTOBUF_CFLAGS := "-I$(PROTOHOME)/src" 19 | PROTOBUF_LIBS := $(PROTOHOME)/src/.libs 20 | PROTOBUF_LDFLAGS := "-Wl,-rpath=$(PROTOBUF_LIBS)" "-L$(PROTOBUF_LIBS)" -lprotobuf -pthread -lpthread 21 | PROTOC := "$(PROTOHOME)/src/protoc" 22 | else 23 | PROTOBUF_CFLAGS ?= "-I/usr/local/src/protobuf/src" 24 | PROTOBUF_LIBS ?= /usr/local/src/protobuf/src/.libs 25 | PROTOBUF_LDFLAGS ?= "-Wl,-rpath=$(PROTOBUF_LIBS)" "-L$(PROTOBUF_LIBS)" -lprotobuf -pthread -lpthread 26 | endif 27 | 28 | 29 | all: compile benchmarks-erl-code 30 | 31 | ./rebar3: 32 | erl -noshell -s inets start -s ssl start \ 33 | -eval '{ok, saved_to_file} = httpc:request(get, {"$(REBAR_URL)", []}, [], [{stream, "./rebar3"}])' \ 34 | -s inets stop -s init stop 35 | chmod +x ./rebar3 36 | 37 | compile: $(REBAR) 38 | $(REBAR) compile 39 | 40 | benchmarks-erl-code: tmp/msg_r.beam tmp/d_r.beam \ 41 | tmp/msg_mo.beam tmp/d_mo.beam \ 42 | tmp/msg_mp.beam tmp/d_mp.beam 43 | 44 | benchmarks-gr-code: tmp/msg_r.beam 45 | 46 | benchmarks-maps-mo-code: tmp/msg_mo.beam 47 | 48 | benchmarks-nif-code: tmp/msg.beam tmp/msg.nif.so \ 49 | tmp/d.beam tmp/d.nif.so 50 | 51 | show-hw-sw-info = \ 52 | echo CPU info; \ 53 | egrep '^model name' /proc/cpuinfo | head -1; \ 54 | egrep '^cache' /proc/cpuinfo | head -1; \ 55 | printf 'cores/threads : %s\n' `egrep -c '^processor' /proc/cpuinfo`; \ 56 | egrep '^bogomips' /proc/cpuinfo | head -1; \ 57 | echo; \ 58 | $(erl) +V 59 | 60 | # Args: 61 | # $1 - module suffix 62 | # $2 - any options 63 | # $3 - proto file name 64 | gpb-compile = \ 65 | $(erl) -boot start_clean -pa _build/default/lib/*/ebin -noshell -noinput +B \ 66 | -v never -c false -I`pwd` -o tmp \ 67 | -modsuffix $1 \ 68 | $(2) \ 69 | -s gpb_compile c $(3) 70 | 71 | tmp/%_r.erl: %.proto 72 | [ -d tmp ] || mkdir tmp 73 | $(call gpb-compile,_r,,$<) 74 | 75 | tmp/%_mo.erl: %.proto 76 | [ -d tmp ] || mkdir tmp 77 | $(call gpb-compile,_mo,-maps -maps_unset_optional omitted,$<) 78 | 79 | tmp/%_mp.erl: %.proto 80 | [ -d tmp ] || mkdir tmp 81 | $(call gpb-compile,_mp,-maps -maps_unset_optional present_undefined,$<) 82 | 83 | %.beam: %.erl 84 | cd tmp; $(erlc) -Wall $(HIPE_ERLC_OPTS) -I../_build/default/lib/gpb/include +debug_info \ 85 | $(patsubst tmp/%,%,$<) 86 | 87 | tmp/%.nif.so: %.proto 88 | $(PROTOC) --cpp_out=tmp/ $*.proto 89 | $(CXX) -g -fPIC -O3 $(PROTOBUF_CFLAGS) $(CXXFLAGS) \ 90 | -o tmp/$*.pb.o -c tmp/$*.pb.cc 91 | $(CXX) -g -fPIC -Wall -O3 $(PROTOBUF_CFLAGS) $(CXXFLAGS) \ 92 | -o tmp/$*.nif.o -c tmp/$*.nif.cc 93 | $(CXX) -g -fPIC -shared -O3 -Wall $(PROTOBUF_CFLAGS) $(CXXFLAGS) \ 94 | -o $@ tmp/$*.nif.o tmp/$*.pb.o $(PROTOBUF_LDFLAGS) 95 | 96 | tmp/%.erl: %.proto 97 | [ -d tmp ] || mkdir tmp 98 | $(call gpb-compile,"",-nif,$<) 99 | 100 | clean: 101 | $(RM) -r tmp 102 | 103 | # Run only the "standard" Google benchmark messages with records 104 | benchmarks: benchmarks-gr-code 105 | @$(call show-hw-sw-info) 106 | @echo 107 | ./proto-bench \ 108 | msg_r Message1 google_message1.dat \ 109 | msg_r Message2 google_message2.dat 110 | 111 | # Run only (one of) the maps benchmarks 112 | maps-benchmarks: benchmarks-maps-mo-code 113 | @$(call show-hw-sw-info) 114 | @echo 115 | ./proto-bench \ 116 | msg_mo Message1 google_message1.dat \ 117 | msg_mo Message2 google_message2.dat 118 | 119 | # "Standard" Google benchmark messages 120 | erl-benchmarks: benchmarks-erl-code 121 | @$(call show-hw-sw-info) 122 | @echo 123 | ./proto-bench \ 124 | --echo "-- Records --" \ 125 | msg_r Message1 google_message1.dat \ 126 | msg_r Message2 google_message2.dat \ 127 | --echo "--" \ 128 | --echo "-- Maps (unset optionals: omitted) --" \ 129 | msg_mo Message1 google_message1.dat \ 130 | msg_mo Message2 google_message2.dat \ 131 | --echo "--" \ 132 | --echo "-- Maps (unset optionals: present, undefined) --" \ 133 | msg_mp Message1 google_message1.dat \ 134 | msg_mp Message2 google_message2.dat 135 | 136 | # Some "d" messages 137 | d-benchmarks: benchmarks-code 138 | @$(call show-hw-sw-info) 139 | @echo 140 | ./proto-bench \ 141 | --echo "-- Records --" \ 142 | d_r dm1 --multi d-msgs/d-msg-n*.dat --end-multi \ 143 | d_r dm1 d-msgs/d-all-concatenated.dat \ 144 | --echo "--" \ 145 | --echo "-- Maps (unset optionals: omitted) --" \ 146 | d_mo dm1 --multi d-msgs/d-msg-n*.dat --end-multi \ 147 | d_mo dm1 d-msgs/d-all-concatenated.dat \ 148 | --echo "--" \ 149 | --echo "-- Maps (unset optionals: present, undefined) --" \ 150 | d_mp dm1 --multi d-msgs/d-msg-n*.dat --end-multi \ 151 | d_mp dm1 d-msgs/d-all-concatenated.dat 152 | 153 | # Some "d" messages 154 | nif-benchmarks: benchmarks-nif-code 155 | @$(call show-hw-sw-info) 156 | @echo 157 | ./proto-bench \ 158 | msg Message1 google_message1.dat \ 159 | msg Message2 google_message2.dat \ 160 | d dm1 --multi d-msgs/d-msg-n*.dat --end-multi \ 161 | d dm1 d-msgs/d-all-concatenated.dat \ 162 | -------------------------------------------------------------------------------- /benchmarks/proto-bench: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env escript 2 | %% This line tells emacs to use -*- erlang -*- mode -*- coding: iso-8859-1 -*- 3 | %%! -pa _build/default/lib/enif_protobuf/ebin/ -pa tmp/ -sname protoexerciser 4 | 5 | %%% Copyright (C) 2011 Tomas Abrahamsson 6 | %%% 7 | %%% Author: Tomas Abrahamsson 8 | %%% 9 | %%% This library is free software; you can redistribute it and/or 10 | %%% modify it under the terms of the GNU Lesser General Public 11 | %%% License as published by the Free Software Foundation; either 12 | %%% version 2.1 of the License, or (at your option) any later version. 13 | %%% 14 | %%% This library is distributed in the hope that it will be useful, 15 | %%% but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | %%% Lesser General Public License for more details. 18 | %%% 19 | %%% You should have received a copy of the GNU Lesser General Public 20 | %%% License along with this library; if not, write to the Free Software 21 | %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | %%% MA 02110-1301 USA 23 | 24 | -mode(compile). 25 | %-compile(export_all). 26 | -export([main/1]). 27 | 28 | main(Args) -> 29 | run_tests(Args). 30 | 31 | run_tests(["--echo", Str | Rest]) -> 32 | io:format("~s~n", [Str]), 33 | run_tests(Rest); 34 | run_tests([MsgModuleStr, MsgNameStr | MsgFileAndRest]) -> 35 | MsgModule = list_to_atom(MsgModuleStr), 36 | MsgName = list_to_atom(MsgNameStr), 37 | {MsgFiles, Rest} = pickup_msg_file_or_files(MsgFileAndRest), 38 | MsgBins = [begin {{ok, B},_} = {file:read_file(MsgFile),MsgFile}, 39 | B 40 | end || MsgFile <- MsgFiles], 41 | DataSize = iolist_size(MsgBins), 42 | EMsgName = case encode_needs_msgname(MsgModule) of 43 | true -> MsgName; 44 | false -> undefined 45 | end, 46 | ok = enif_protobuf:load_cache(MsgModule:get_msg_defs()), 47 | Decoder = fun() -> decode_bins(MsgBins, MsgModule, MsgName) end, 48 | Msgs = [MsgModule:decode_msg(Bin, MsgName) || Bin <- MsgBins], 49 | Encoder = fun() -> encode_msgs(Msgs, MsgModule, EMsgName) end, 50 | maybe_warn_skewed_results(), 51 | io:format("Benchmarking ~s ~s with file ~s~n", 52 | [MsgModule, MsgName, string:join(MsgFiles, ",")]), 53 | run_test("Serialize to binary", DataSize, Encoder), 54 | run_test("Deserialize from binary", DataSize, Decoder), 55 | io:format("~n"), 56 | run_tests(Rest); 57 | run_tests([]) -> 58 | ok. 59 | 60 | encode_needs_msgname(MsgModule) -> 61 | Exports = MsgModule:module_info(exports), 62 | case lists:keyfind(encode_msg, 1, Exports) of 63 | {encode_msg, 1} -> false; %% records 64 | {encode_msg, 2} -> true %% maps 65 | end. 66 | 67 | pickup_msg_file_or_files(["--multi" | MsgFilesAndRest]) -> 68 | EndMarker = "--end-multi", 69 | {MsgFiles, [EndMarker | Rest]} = 70 | lists:splitwith(fun(S) -> S /= EndMarker end, MsgFilesAndRest), 71 | {MsgFiles, Rest}; 72 | pickup_msg_file_or_files([MsgFile | Rest]) -> 73 | {[MsgFile], Rest}. 74 | 75 | 76 | decode_bins([MsgBin | Rest], MsgModule, MsgName) -> 77 | enif_protobuf:decode(MsgBin, MsgName), 78 | decode_bins(Rest, MsgModule, MsgName); 79 | decode_bins([], _, _) -> 80 | ok. 81 | 82 | encode_msgs([Msg | Rest], MsgModule, undefined) -> 83 | enif_protobuf:encode(Msg), %% MsgName not needed for records 84 | encode_msgs(Rest, MsgModule, undefined); 85 | encode_msgs([Msg | Rest], MsgModule, MsgName) -> 86 | enif_protobuf:encode(Msg, MsgName), %% MsgName needed for maps 87 | encode_msgs(Rest, MsgModule, MsgName); 88 | encode_msgs([], _, _) -> 89 | ok. 90 | 91 | 92 | maybe_warn_skewed_results() -> 93 | try list_to_integer(erlang:system_info(otp_release)) of 94 | N when N >= 22 -> 95 | io:format( 96 | "\n" 97 | "NB: The results are currently skewed on Erlang 22 and later.\n" 98 | " Optimizations in Erlang 22 and the way the benchmarks\n" 99 | " are implemented cause more GC than normal.\n" 100 | " The benchmarking code is not yet adapted. It will show\n" 101 | " pessimistic values.\n" 102 | " See https://github.com/erlang/otp/commit/7d941c529d#commitcomment-31091771\n" 103 | " for more info.\n" 104 | "\n"); 105 | _ -> 106 | ok 107 | catch _:_ -> 108 | ok 109 | end. 110 | 111 | 112 | 113 | run_test(Description, DataSize, Action) -> 114 | {P,M} = spawn_monitor( 115 | fun() -> 116 | run_test_aux(Description, DataSize, Action) 117 | end), 118 | receive 119 | {'DOWN', M, process, P, normal} -> 120 | ok; 121 | {'DOWN', M, process, P, Other} -> 122 | error({aux_died, Other}) 123 | end. 124 | 125 | run_test_aux(Description, DataSize, Action) -> 126 | MinSampleTime = 2, %% seconds 127 | TargetTime = 30, %% seconds 128 | {Elapsed, NumIterations} = iterate_until_elapsed(MinSampleTime, Action), 129 | TargetNumIterations = round((TargetTime / Elapsed) * NumIterations), 130 | Elapsed2 = time_action(TargetNumIterations, Action), 131 | io:format("~s: ~w iterations in ~.3fs; ~.2fMB/s~n", 132 | [Description, TargetNumIterations, Elapsed2, 133 | (TargetNumIterations * DataSize) / (Elapsed2 * 1024 * 1024)]), 134 | ok. 135 | 136 | iterate_until_elapsed(MaxDuration, Action) -> 137 | iterate_until_elapsed_2(1, MaxDuration, Action). 138 | 139 | iterate_until_elapsed_2(NumIterations, MaxDuration, Action) -> 140 | case time_action(NumIterations, Action) of 141 | Elapsed when Elapsed < MaxDuration -> 142 | iterate_until_elapsed_2(NumIterations * 2, MaxDuration, Action); 143 | Elapsed when Elapsed >= MaxDuration -> 144 | {Elapsed, NumIterations} 145 | end. 146 | 147 | time_action(NumIterations, Action) -> 148 | garbage_collect(), 149 | T0 = os:timestamp(), 150 | iterate_action(NumIterations, Action), 151 | T1 = os:timestamp(), 152 | timer:now_diff(T1, T0) / 1000000. 153 | 154 | iterate_action(N, Action) when N > 0 -> 155 | Action(), 156 | iterate_action(N - 1, Action); 157 | iterate_action(0, _Action) -> 158 | ok. 159 | -------------------------------------------------------------------------------- /test/ep_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ep_tests). 2 | 3 | -compile(export_all). 4 | 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -include_lib("gpb/include/gpb.hrl"). 7 | 8 | -record('Person', { 9 | name :: iolist(), % = 1 10 | id :: integer(), % = 2, 32 bits 11 | email :: iolist() | undefined % = 3 12 | }). 13 | 14 | -record(m1, {a}). 15 | 16 | load_cache_test() -> 17 | ok = enif_protobuf:load_cache([ 18 | {{msg, m1}, [ 19 | {field, int32, 1, 1, int32, optional, [packed, {default, ok}]}, 20 | {field, int64, 2, 2, int64, optional, [packed, {default, ok}]}, 21 | {field, uint32, 3, 3, uint32, optional, [packed, {default, ok}]}, 22 | {field, uint64, 4, 4, uint64, optional, [packed, {default, ok}]}, 23 | {field, sint32, 5, 5, sint32, optional, [packed, {default, ok}]}, 24 | {field, sint64, 6, 6, sint64, optional, [packed, {default, ok}]}, 25 | {field, fixed32, 7, 7, fixed32, optional, [packed, {default, ok}]}, 26 | {field, fixed64, 8, 8, fixed64, optional, [packed, {default, ok}]}, 27 | {field, sfixed32, 9, 9, sfixed32, optional, [packed, {default, ok}]}, 28 | {field, sfixed64, 10, 10, sfixed64, optional, [packed, {default, ok}]}, 29 | {field, bool, 11, 11, bool, optional, [packed, {default, ok}]}, 30 | {field, float, 12, 12, float, optional, [packed, {default, ok}]}, 31 | {field, double, 13, 13, double, optional, [packed, {default, ok}]}, 32 | {field, string, 14, 14, string, optional, [packed, {default, ok}]}, 33 | {field, bytes, 15, 15, bytes, optional, [packed, {default, ok}]}, 34 | {field, enum, 16, 16, {enum, e}, optional, [packed, {default, ok}]}, 35 | {field, msg, 17, 17, {msg, m2}, optional, [packed, {default, ok}]}, 36 | {field, map, 18, 18, {map, string, fixed32}, optional, [packed, {default, ok}]}, 37 | {field, required, 19, 19, fixed32, required, [packed, {default, ok}]}, 38 | {field, optional, 20, 20, fixed32, optional, [packed, {default, ok}]}, 39 | {field, repeated, 21, 21, fixed32, repeated, [packed, {default, ok}]}, 40 | {gpb_oneof, oneof, 22, [ 41 | {field, int32, 22, 22, int32, optional, [packed, {default, ok}]}, 42 | {field, int64, 23, 22, int64, optional, [packed, {default, ok}]} 43 | ]} 44 | ]}, 45 | {{msg, m2}, [ 46 | {field, int32, 0, 1, int32, optional, [packed, {default, ok}]} 47 | ]}, 48 | {{msg, m3}, [ 49 | {field, int32, 0, 1, int32, optional, [packed, {default, ok}]}, 50 | {field, int64, 1, 2, int64, optional, [packed, {default, ok}]} 51 | ]}, 52 | {{enum, e}, [ 53 | {v1, 100}, 54 | {v2, -2}, 55 | {v3, -2}, 56 | {option, allow_alias, true} 57 | ]} 58 | ]). 59 | 60 | load_oneof_test() -> 61 | ok = enif_protobuf:load_cache([{{msg, 'Person'}, [ 62 | #gpb_oneof{name = message_body, rnum = 2, fields = [ 63 | #field{name = file_children, fnum = 3, rnum = 2, type = int32, occurrence = optional, opts = []}, 64 | #field{name = xattr, fnum = 4, rnum = 2, type = bytes, occurrence = optional, opts = []}]}, 65 | #field{name = proxy_session_id, fnum = 21, rnum = 3, type = bytes, occurrence = optional, opts = []} 66 | ]}]). 67 | 68 | nested_oneof_test() -> 69 | ok = enif_protobuf:load_cache([ 70 | {{msg, 'ChildLink'}, [ 71 | #field{name = name, fnum = 2, rnum = 2, type = bytes, occurrence = required, opts = []} 72 | ]}, 73 | {{msg, 'FileChildren'}, [ 74 | #field{name = child_links, fnum = 1, rnum = 2, type = {msg, 'ChildLink'}, occurrence = repeated, opts = []} 75 | ]}, 76 | {{msg, 'FuseResponse'}, [ 77 | #gpb_oneof{name = fuse_response, rnum = 2, fields = [ 78 | #field{name = file_children, fnum = 3, rnum = 2, type = {msg, 'FileChildren'}, occurrence = optional, opts = []}, 79 | #field{name = xattr, fnum = 13, rnum = 2, type = bytes, occurrence = optional, opts = []} 80 | ]} 81 | ]}, 82 | {{msg, 'ServerMessage'}, [ 83 | #gpb_oneof{name = message_body, rnum = 2, fields = [ 84 | #field{name = fuse_response, fnum = 15, rnum = 2, type = {msg, 'FuseResponse'}, occurrence = optional, opts = []} 85 | ]} 86 | ]} 87 | ]), 88 | Msg = {'ServerMessage', 89 | {fuse_response, 90 | {'FuseResponse', 91 | {file_children, 92 | {'FileChildren', [{'ChildLink', <<"1">>}]} 93 | } 94 | } 95 | } 96 | }, 97 | Bin = enif_protobuf:encode(Msg), 98 | Msg = enif_protobuf:decode(Bin, 'ServerMessage'). 99 | 100 | loading_cache() -> 101 | ok = enif_protobuf:load_cache([{{msg, 'Person'}, [ 102 | #field{name = name, fnum = 1, rnum = 2, type = string, occurrence = required, opts = []}, 103 | #field{name = id, fnum = 2, rnum = 3, type = int32, occurrence = required, opts = []}, 104 | #field{name = email, fnum = 3, rnum = 4, type = string, occurrence = optional, opts = []}]} 105 | ]). 106 | 107 | encoding() -> 108 | enif_protobuf:encode(#'Person'{name = "abc def", id = 345, email = "a@example.com"}). 109 | 110 | loop_encoding(0) -> 111 | encoding(), 112 | ok; 113 | loop_encoding(N) -> 114 | case rand:uniform() > 0.99 of 115 | true -> 116 | loading_cache(); 117 | _ -> 118 | ignore 119 | end, 120 | encoding(), 121 | loop_encoding(N - 1). 122 | 123 | smp_cache_encoding_test_() -> 124 | rand:uniform(), 125 | Processors = erlang:system_info(logical_processors), 126 | N = 500000, 127 | {spawn, {timeout, 60, ?_test(begin 128 | [spawn(fun() -> 129 | loading_cache(), 130 | loop_encoding(N) 131 | end) || _N <- lists:seq(1, Processors * 2)], 132 | loading_cache(), 133 | loop_encoding(N + 1000000) 134 | end)}}. 135 | 136 | decoding() -> 137 | Bin = <<10, 7, 97, 98, 99, 32, 100, 101, 102, 16, 217, 2, 26, 13, 97, 64, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109>>, 138 | enif_protobuf:decode(Bin, 'Person'). 139 | 140 | loop_decoding(0) -> 141 | decoding(), 142 | ok; 143 | loop_decoding(N) -> 144 | case rand:uniform() > 0.99 of 145 | true -> 146 | loading_cache(); 147 | _ -> 148 | ignore 149 | end, 150 | decoding(), 151 | loop_decoding(N - 1). 152 | 153 | smp_cache_decoding_test_() -> 154 | rand:uniform(), 155 | Processors = erlang:system_info(logical_processors), 156 | N = 500000, 157 | {spawn, {timeout, 60, ?_test(begin 158 | [spawn(fun() -> 159 | loading_cache(), 160 | loop_decoding(N) 161 | end) || _N <- lists:seq(1, Processors * 2)], 162 | loading_cache(), 163 | loop_decoding(N + 1000000) 164 | end)}}. 165 | -------------------------------------------------------------------------------- /c_src/enif_protobuf.h: -------------------------------------------------------------------------------- 1 | #ifndef __EPB_H__ 2 | #define __EPB_H__ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "erl_nif.h" 11 | 12 | #define IS_DEBUG 0 13 | #define USE_OS_MEM 0 14 | #define DEBUG_MEM 0 15 | 16 | #define RET_OK 0 17 | #define RET_ERROR 1 18 | 19 | #define TRUE 1 20 | #define FALSE 0 21 | 22 | #define ENC_INIT_SIZE (1024 * 1024) 23 | #define STACK_INIT_SIZE 2 24 | #define ARRAY_INIT_SIZE 32 25 | 26 | #ifdef _MSC_VER 27 | #define inline __inline 28 | #define __func__ __FUNCTION__ 29 | #define _NAN (-NAN) 30 | #else 31 | #define _NAN NAN 32 | #endif 33 | 34 | #ifndef _MSC_VER 35 | #define sprintf_s(p, s, fmt, ...) sprintf(p, fmt, __VA_ARGS__) 36 | #endif 37 | 38 | #if IS_DEBUG 39 | #define debug_out(fmt) do { \ 40 | printf("%s:%d:%s(): " fmt "\r\n", __FILE__, __LINE__, __func__); \ 41 | } while (0) 42 | 43 | #define debug_step() printf("%s:%d:%s()\r\n", __FILE__, __LINE__, __func__) 44 | 45 | #define debug(fmt, ...) do { \ 46 | printf("%s:%d:%s(): " fmt "\r\n", __FILE__, __LINE__, __func__, __VA_ARGS__); \ 47 | } while (0) 48 | #else 49 | #define debug_step() do {} while(0) 50 | #define debug(fmt, ...) do {} while(0) 51 | #endif 52 | 53 | #if IS_DEBUG 54 | #define raise_exception(env, term) do { \ 55 | printf("%s:%d:%s()\r\n", __FILE__, __LINE__, __func__); \ 56 | enif_raise_exception(env, term); \ 57 | return enif_make_tuple2(env, ((ep_state_t *) enif_priv_data(env))->atom_error, term); \ 58 | } while(0) 59 | 60 | #define return_error(env, term) do { \ 61 | printf("%s:%d:%s()\r\n", __FILE__, __LINE__, __func__); \ 62 | return enif_make_tuple2(env, ((ep_state_t *) enif_priv_data(env))->atom_error, term); \ 63 | } while(0) 64 | #else 65 | #define raise_exception(env, term) return enif_raise_exception(env, term) 66 | 67 | #define return_error(env, term) return enif_make_tuple2(env, ((ep_state_t *) enif_priv_data(env))->atom_error, term) 68 | #endif 69 | 70 | #define check_ret(ret, func) do { \ 71 | if (((ret) = (func)) != RET_OK) { \ 72 | debug("ret %lu", (unsigned long) ret); \ 73 | return ret; \ 74 | } \ 75 | } while(0) 76 | 77 | typedef struct ep_enum_field_s ep_enum_field_t; 78 | typedef struct ep_field_s ep_field_t; 79 | typedef struct ep_fnum_field_s ep_fnum_field_t; 80 | typedef struct ep_node_id_s ep_node_id_t; 81 | typedef struct ep_node_name_s ep_node_name_t; 82 | typedef struct ep_msg_s ep_msg_t; 83 | typedef struct ep_enum_s ep_enum_t; 84 | typedef struct ep_node_s ep_node_t; 85 | 86 | typedef struct ep_state_s ep_state_t; 87 | typedef struct ep_cache_s ep_cache_t; 88 | typedef struct ep_enc_s ep_enc_t; 89 | typedef struct ep_dec_s ep_dec_t; 90 | typedef struct ep_spot_s ep_spot_t; 91 | typedef struct ep_stack_s ep_stack_t; 92 | 93 | typedef enum { 94 | //field_unknown = INT_MIN, 95 | field_unknown = 0, 96 | field_int32 = 1, 97 | field_int64 = 2, 98 | field_uint32 = 3, 99 | field_uint64 = 4, 100 | field_sint32 = 5, 101 | field_sint64 = 6, 102 | field_fixed32 = 7, 103 | field_fixed64 = 8, 104 | field_sfixed32 = 9, 105 | field_sfixed64 = 10, 106 | field_bool = 11, 107 | field_float = 12, 108 | field_double = 13, 109 | field_string = 14, 110 | field_bytes = 15, 111 | 112 | field_enum = 16, 113 | field_msg = 17, 114 | field_map = 18, 115 | field_oneof = 19 116 | } field_type_e; 117 | 118 | typedef enum { 119 | occurrence_required = 0, 120 | occurrence_optional = 1, 121 | occurrence_repeated = 2, 122 | occurrence_defaulty = 3 123 | } occurrence_type_e; 124 | 125 | typedef enum { 126 | node_unknown = 0, 127 | node_msg = 1, 128 | node_enum = 2, 129 | node_oneof = 3, 130 | node_map = 4 131 | } node_type_e; 132 | 133 | struct ep_stack_s { 134 | ep_spot_t *end; 135 | ep_spot_t *spots; 136 | size_t size; 137 | }; 138 | 139 | struct ep_enc_s { 140 | char *tmp; 141 | char *p; 142 | char *sentinel; 143 | char *end; 144 | char *mem; 145 | size_t size; 146 | ERL_NIF_TERM result; 147 | }; 148 | 149 | struct ep_dec_s { 150 | char *p; 151 | char *sentinel; 152 | char *end; 153 | ErlNifBinary bin; 154 | ERL_NIF_TERM term; 155 | ERL_NIF_TERM result; 156 | }; 157 | 158 | struct ep_spot_s { 159 | int32_t type; 160 | 161 | int8_t need_length; 162 | size_t sentinel_size; 163 | 164 | size_t pos; 165 | ep_node_t *node; 166 | ep_field_t *field; 167 | 168 | ERL_NIF_TERM *array; // is_tuple 169 | ERL_NIF_TERM list; // is_list 170 | 171 | ERL_NIF_TERM *t_arr; 172 | size_t t_size; 173 | size_t t_used; 174 | ERL_NIF_TERM result; 175 | char *end_sentinel; 176 | }; 177 | 178 | typedef struct ep_tdata_s { 179 | ep_stack_t stack; 180 | ep_enc_t enc; 181 | ep_dec_t dec; 182 | } ep_tdata_t; 183 | 184 | typedef struct ep_lock_s { 185 | ErlNifTid tid; 186 | ep_tdata_t *tdata; 187 | } ep_lock_t; 188 | 189 | struct ep_state_s { 190 | ep_cache_t *cache; 191 | 192 | uint32_t lock_n; 193 | uint32_t lock_used; 194 | ep_lock_t *locks; 195 | ep_tdata_t *tdata; 196 | ErlNifRWLock *cache_lock; 197 | ErlNifRWLock *local_lock; 198 | 199 | struct opts_s { 200 | uint32_t with_utf8; 201 | uint32_t string_as_list; 202 | } opts; 203 | 204 | ERL_NIF_TERM atom_ok; 205 | ERL_NIF_TERM atom_error; 206 | ERL_NIF_TERM atom_true; 207 | ERL_NIF_TERM atom_false; 208 | ERL_NIF_TERM atom_undefined; 209 | ERL_NIF_TERM atom_field; 210 | ERL_NIF_TERM atom_option; 211 | ERL_NIF_TERM atom_infinity; 212 | ERL_NIF_TERM atom_min_infinity; 213 | ERL_NIF_TERM atom_nan; 214 | 215 | ERL_NIF_TERM atom_int32; 216 | ERL_NIF_TERM atom_int64; 217 | ERL_NIF_TERM atom_uint32; 218 | ERL_NIF_TERM atom_uint64; 219 | ERL_NIF_TERM atom_sint32; 220 | ERL_NIF_TERM atom_sint64; 221 | ERL_NIF_TERM atom_fixed32; 222 | ERL_NIF_TERM atom_fixed64; 223 | ERL_NIF_TERM atom_sfixed32; 224 | ERL_NIF_TERM atom_sfixed64; 225 | ERL_NIF_TERM atom_bool; 226 | ERL_NIF_TERM atom_float; 227 | ERL_NIF_TERM atom_double; 228 | ERL_NIF_TERM atom_string; 229 | ERL_NIF_TERM atom_bytes; 230 | 231 | ERL_NIF_TERM atom_enum; 232 | ERL_NIF_TERM atom_msg; 233 | ERL_NIF_TERM atom_map; 234 | 235 | ERL_NIF_TERM atom_required; 236 | ERL_NIF_TERM atom_optional; 237 | ERL_NIF_TERM atom_defaulty; 238 | ERL_NIF_TERM atom_repeated; 239 | }; 240 | 241 | #define to_const(p) (const ERL_NIF_TERM **) (&(p)) 242 | 243 | #if USE_OS_MEM 244 | 245 | #define _alloc(size) malloc(size) 246 | #define _free(ptr) free(ptr) 247 | #define _realloc(ptr, size) realloc(ptr, size) 248 | #define _calloc(nmemb, size) calloc(nmemb, size) 249 | 250 | #elif DEBUG_MEM 251 | 252 | extern size_t mem_total; 253 | 254 | #define _alloc(size) __alloc(size, __FILE__, __LINE__, __func__) 255 | 256 | static inline void * 257 | __alloc(size_t size, const char *file, int line, const char *func) 258 | { 259 | size_t *p; 260 | 261 | p = enif_alloc(size + sizeof(size_t)); 262 | *p++ = size; 263 | mem_total += size; 264 | printf("%s:%d:%s() alloc: %ld, total: %ld\r\n", file, line, func, (long) size, (long)mem_total); 265 | 266 | return p; 267 | } 268 | 269 | #define _free(ptr) __free(ptr, __FILE__, __LINE__, __func__) 270 | 271 | static inline void 272 | __free(void *ptr, const char *file, int line, const char *func) 273 | { 274 | size_t *p; 275 | 276 | p = (size_t *) ptr; 277 | p--; 278 | mem_total = mem_total - (*p); 279 | printf("%s:%d:%s() free: %ld, total: %ld\r\n", file, line, func, (long) *p, (long) mem_total); 280 | 281 | enif_free(p); 282 | } 283 | 284 | #define _realloc(ptr, size) __realloc(ptr, size, __FILE__, __LINE__, __func__) 285 | 286 | static inline void * 287 | __realloc(void *ptr, size_t size, const char *file, int line, const char *func) 288 | { 289 | void *p; 290 | 291 | p = __alloc(size, file, line, func); 292 | if (p != NULL) { 293 | __free(ptr, file, line, func); 294 | return p; 295 | } 296 | 297 | return NULL; 298 | } 299 | 300 | #define _calloc(nmemb, size) __calloc(nmemb, size, __FILE__, __LINE__, __func__) 301 | 302 | static inline void * 303 | __calloc(size_t nmemb, size_t size, const char *file, int line, const char *func) 304 | { 305 | void *ptr; 306 | 307 | size *= nmemb; 308 | ptr = __alloc(size, file, line, func); 309 | return ptr == NULL ? ptr : memset(ptr, 0x00, size); 310 | } 311 | #else 312 | 313 | #define _alloc(size) enif_alloc(size) 314 | #define _free(ptr) enif_free(ptr) 315 | #define _realloc(ptr, size) enif_realloc(ptr, size) 316 | 317 | static inline void * 318 | _calloc(size_t nmemb, size_t size) 319 | { 320 | void *ptr; 321 | 322 | size *= nmemb; 323 | ptr = _alloc(size); 324 | return ptr == NULL ? ptr : memset(ptr, 0x00, size); 325 | } 326 | 327 | #endif 328 | 329 | #include "ep_codec.h" 330 | #include "ep_cache.h" 331 | #include "ep_node.h" 332 | 333 | ERL_NIF_TERM 334 | make_atom(ErlNifEnv *env, const char *name); 335 | 336 | #endif 337 | -------------------------------------------------------------------------------- /benchmarks/d.proto: -------------------------------------------------------------------------------- 1 | // many deeply nested messages 2 | 3 | enum de01 { 4 | DE01_E01 = 200; 5 | DE01_E02 = 201; 6 | DE01_E03 = 202; 7 | DE01_E04 = 203; 8 | DE01_E05 = 204; 9 | DE01_E06 = 205; 10 | DE01_E07 = 206; 11 | DE01_E08 = 207; 12 | DE01_E10 = 208; 13 | DE01_E11 = 209; 14 | DE01_E12 = 210; 15 | DE01_E13 = 211; 16 | DE01_E14 = 212; 17 | DE01_E15 = 213; 18 | DE01_E16 = 214; 19 | DE01_E17 = 215; 20 | DE01_E18 = 216; 21 | DE01_E19 = 217; 22 | DE01_E20 = 218; 23 | DE01_E21 = 219; 24 | } 25 | message dm02 { 26 | required fixed32 dm02f01 = 1; 27 | required uint32 dm02f02 = 2; 28 | required string dm02f03 = 3; 29 | } 30 | message dm03 { 31 | required fixed32 dm03f01 = 1; 32 | required uint32 dm03f02 = 2; 33 | optional uint32 dm03f03 = 3 [default = 0]; 34 | optional uint32 dm03f04 = 4 [default = 0]; 35 | required string dm03f05 = 5; 36 | } 37 | message dm04 { 38 | required string dm04f01 = 1; 39 | required uint32 dm04f02 = 10; 40 | required fixed32 dm04f03 = 2; 41 | required uint32 dm04f04 = 3; 42 | optional uint32 dm04f05 = 4 [default = 0]; 43 | optional uint32 dm04f06 = 5 [default = 0]; 44 | required string dm04f07 = 6; 45 | required string dm04f08 = 7; 46 | required string dm04f09 = 8; 47 | required string dm04f10 = 9; 48 | } 49 | message dm05 { 50 | } 51 | message dm06 { 52 | } 53 | message dm07 { 54 | optional fixed64 dm07f01 = 1; 55 | repeated uint32 dm07f02 = 2; 56 | } 57 | message dm08 { 58 | optional fixed64 dm08f01 = 1; 59 | repeated uint32 dm08f02 = 2; 60 | } 61 | message dm09 { 62 | optional string dm09f01 = 1; 63 | optional bool dm09f02 = 2; 64 | } 65 | message dm10 { 66 | optional string dm10f01 = 1; 67 | required uint32 dm10f02 = 2; 68 | optional uint32 dm10f03 = 3; 69 | } 70 | enum de02 { 71 | DE02_E01 = 1; 72 | DE02_E02 = 2; 73 | } 74 | message dm11 { 75 | optional string dm11f01 = 1; 76 | optional de02 dm11f02 = 2; 77 | } 78 | message dm12 { 79 | required string dm12f01 = 1; 80 | required uint32 dm12f02 = 2; 81 | optional bool dm12f03 = 3; 82 | } 83 | message dm13 { 84 | required uint32 dm13f01 = 1; 85 | optional uint32 dm13f02 = 2; 86 | } 87 | message dm14 { 88 | required uint32 dm14f01 = 1; 89 | required de02 dm14f02 = 2; 90 | } 91 | message dm15 { 92 | required uint32 dm15f01 = 1; 93 | } 94 | message dm16 { 95 | required uint32 dm16f01 = 1; 96 | } 97 | enum de03 { 98 | DE03_E01 = 1; 99 | DE03_E02 = 2; 100 | } 101 | message dm17 { 102 | required uint32 dm17f01 = 1; 103 | required de03 dm17f02 = 2; 104 | } 105 | message dm18 { 106 | required uint32 dm18f01 = 1; 107 | required uint32 dm18f02 = 2; 108 | required int32 dm18f03 = 3; 109 | optional bool dm18f04 = 4 [default = false]; 110 | optional uint32 dm18f05 = 5 [default = 100]; 111 | optional uint32 dm18f06 = 6 [default = 100]; 112 | } 113 | message dm19 { 114 | required uint32 dm19f01 = 1; 115 | } 116 | message dm20 { 117 | required uint32 dm20f01 = 1; 118 | } 119 | message dm21 { 120 | required uint32 dm21f01 = 1; 121 | } 122 | message dm22 { 123 | required uint32 dm22f01 = 1; 124 | } 125 | enum de04 { 126 | DE04_E01 = 100; 127 | DE04_E02 = 101; 128 | DE04_E03 = 102; 129 | DE04_E04 = 103; 130 | DE04_E05 = 104; 131 | } 132 | message dm23 { 133 | optional uint32 dm23f01 = 1; 134 | optional fixed32 dm23f02 = 2; 135 | optional bytes dm23f03 = 3; 136 | } 137 | message dm24 { 138 | optional string dm24f01 = 1; 139 | optional string dm24f02 = 2; 140 | optional string dm24f03 = 3; 141 | optional string dm24f04 = 4; 142 | optional string dm24f05 = 6; 143 | } 144 | message dm25 { 145 | optional string dm25f01 = 1; 146 | optional string dm25f02 = 2; 147 | optional string dm25f03 = 3; 148 | } 149 | message dm26 { 150 | optional string dm26f01 = 1; 151 | optional string dm26f02 = 2; 152 | optional string dm26f03 = 3; 153 | optional uint32 dm26f04 = 4; 154 | } 155 | message dm27 { 156 | optional string dm27f01 = 1; 157 | optional uint32 dm27f02 = 2; 158 | } 159 | message dm28 { 160 | optional dm23 dm28f01 = 1; 161 | optional dm24 dm28f02 = 2; 162 | optional dm25 dm28f03 = 3; 163 | } 164 | message dm29 { 165 | optional dm23 dm29f01 = 1; 166 | optional dm26 dm29f02 = 2; 167 | } 168 | message dm30 { 169 | } 170 | message dm31 { 171 | repeated dm26 dm31f01 = 1; 172 | } 173 | message dm32 { 174 | optional string dm32f01 = 1; 175 | optional uint32 dm32f02 = 2; 176 | repeated dm27 dm32f03 = 3; 177 | } 178 | enum de05 { 179 | DE05_E01 = 300; 180 | DE05_E02 = 301; 181 | DE05_E03 = 302; 182 | DE05_E04 = 303; 183 | DE05_E05 = 304; 184 | DE05_E06 = 305; 185 | DE05_E07 = 306; 186 | DE05_E08 = 307; 187 | DE05_E09 = 308; 188 | DE05_E10 = 309; 189 | DE05_E11 = 310; 190 | } 191 | enum de06 { 192 | DE06_E01 = 1; 193 | DE06_E02 = 2; 194 | DE06_E03 = 3; 195 | } 196 | enum de07 { 197 | DE07_E01 = 1; 198 | DE07_E02 = 2; 199 | } 200 | message dm33 { 201 | } 202 | message dm34 { 203 | } 204 | message dm35 { 205 | optional uint32 dm35f01 = 1; 206 | optional uint32 dm35f02 = 2; 207 | optional uint32 dm35f03 = 3; 208 | optional uint32 dm35f04 = 4; 209 | optional uint32 dm35f05 = 5; 210 | optional uint32 dm35f06 = 6; 211 | } 212 | message dm36 { 213 | required de06 dm36f01 = 1; 214 | optional de07 dm36f02 = 6 [default = DE07_E02]; 215 | required int32 dm36f03 = 2; 216 | required int32 dm36f04 = 7; 217 | optional dm33 dm36f05 = 3; 218 | optional dm34 dm36f06 = 4; 219 | optional dm35 dm36f07 = 5; 220 | } 221 | message dm37 { 222 | repeated dm36 dm37f01 = 1; 223 | } 224 | message dm38 { 225 | repeated int32 dm38f01 = 1; 226 | } 227 | message dm39 { 228 | repeated int32 dm39f01 = 1; 229 | } 230 | message dm40 { 231 | repeated int32 dm40f01 = 1; 232 | } 233 | message dm41 { 234 | optional uint32 dm41f01 = 1; 235 | } 236 | message dm42 { 237 | optional uint32 dm42f01 = 1; 238 | } 239 | message dm43 { 240 | } 241 | message dm44 { 242 | required uint32 dm44f01 = 1; 243 | } 244 | message dm45 { 245 | } 246 | message dm46 { 247 | required uint32 dm46f01 = 1; 248 | required uint32 dm46f02 = 2; 249 | } 250 | message dm47 { 251 | } 252 | enum de08 { 253 | DE08_E01 = 400; 254 | DE08_E02 = 401; 255 | DE08_E03 = 402; 256 | DE08_E04 = 403; 257 | DE08_E05 = 404; 258 | DE08_E06 = 405; 259 | DE08_E07 = 406; 260 | DE08_E08 = 407; 261 | DE08_E09 = 408; 262 | DE08_E10 = 409; 263 | DE08_E11 = 410; 264 | DE08_E12 = 411; 265 | DE08_E13 = 412; 266 | } 267 | message dm48 { 268 | } 269 | message dm49 { 270 | required uint32 dm49f01 = 2; 271 | required uint32 dm49f02 = 3; 272 | } 273 | message dm50 { 274 | required uint32 dm50f01 = 1; 275 | optional uint32 dm50f02 = 2; 276 | required bool dm50f03 = 3; 277 | } 278 | message dm51 { 279 | optional bool dm51f01 = 1 [default = false]; 280 | optional dm50 dm51f02 = 2; 281 | } 282 | message dm52 { 283 | optional uint32 dm52f01 = 1; 284 | optional sint32 dm52f02 = 2; 285 | optional int32 dm52f03 = 3; 286 | optional uint32 dm52f04 = 4; 287 | optional int32 dm52f05 = 5; 288 | optional int32 dm52f06 = 6; 289 | optional int32 dm52f07 = 7; 290 | optional int32 dm52f08 = 8; 291 | optional int32 dm52f09 = 9; 292 | optional int32 dm52f10 = 10; 293 | optional sint32 dm52f11 = 11; 294 | optional int32 dm52f12 = 12; 295 | optional uint32 dm52f13 = 13; 296 | optional int32 dm52f14 = 14; 297 | optional uint32 dm52f15 = 15; 298 | optional int32 dm52f16 = 16; 299 | optional int32 dm52f17 = 17; 300 | optional int32 dm52f18 = 18; 301 | optional int32 dm52f19 = 19; 302 | optional bytes dm52f20 = 20; 303 | optional uint32 dm52f21 = 21; 304 | optional uint32 dm52f22 = 22; 305 | optional dm51 dm52f23 = 23; 306 | } 307 | message dm53 { 308 | } 309 | message dm54 { 310 | } 311 | message dm55 { 312 | } 313 | enum de09 { 314 | DE09_E01 = 1; 315 | DE09_E02 = 2; 316 | DE09_E03 = 3; 317 | } 318 | message dm56 { 319 | required int32 dm56f01 = 1; 320 | optional int32 dm56f02 = 2; 321 | optional sint32 dm56f03 = 3; 322 | optional int32 dm56f04 = 4; 323 | optional int32 dm56f05 = 5; 324 | } 325 | message dm57 { 326 | required de09 dm57f01 = 1; 327 | optional dm56 dm57f02 = 2; 328 | } 329 | message dm58 { 330 | repeated dm57 dm58f01 = 1; 331 | } 332 | message dm59 { 333 | repeated int32 dm59f01 = 1; 334 | } 335 | enum de10 { 336 | DE10_E01 = 1; 337 | DE10_E02 = 2; 338 | } 339 | message dm60 { 340 | required de10 dm60f01 = 1; 341 | } 342 | message dm61 { 343 | } 344 | message dm62 { 345 | } 346 | message dm63 { 347 | required uint32 dm63f01 = 1; 348 | optional uint32 dm63f02 = 2 [default = 100]; 349 | optional uint32 dm63f03 = 3; 350 | optional uint32 dm63f04 = 4 [default = 211]; 351 | optional int32 dm63f05 = 5; 352 | optional int32 dm63f06 = 6; 353 | optional bool dm63f07 = 7 [default = true]; 354 | } 355 | message dm64 { 356 | } 357 | enum de11 { 358 | DE11_E01 = 500; 359 | DE11_E02 = 501; 360 | DE11_E03 = 502; 361 | } 362 | enum de12 { 363 | DE12_E01 = -2; 364 | DE12_E02 = 0; 365 | DE12_E03 = 2; 366 | } 367 | enum de13 { 368 | DE13_E01 = 1; 369 | DE13_E02 = 3; 370 | DE13_E03 = 5; 371 | } 372 | enum de14 { 373 | DE14_E01 = -2; 374 | DE14_E02 = 0; 375 | DE14_E03 = 1; 376 | DE14_E04 = 2; 377 | } 378 | enum de15 { 379 | DE15_E01 = -2; 380 | DE15_E02 = 0; 381 | DE15_E03 = 2; 382 | } 383 | enum de16 { 384 | DE16_E01 = -2; 385 | DE16_E02 = 0; 386 | DE16_E03 = 2; 387 | } 388 | enum de17 { 389 | DE17_E01 = 0; 390 | DE17_E02 = 1; 391 | } 392 | message dm65 { 393 | } 394 | message dm66 { 395 | optional uint32 dm66f01 = 1; 396 | optional uint32 dm66f02 = 2; 397 | optional uint32 dm66f03 = 3; 398 | optional uint32 dm66f04 = 4; 399 | optional int32 dm66f05 = 5; 400 | optional int32 dm66f06 = 6; 401 | optional int32 dm66f07 = 7; 402 | optional de12 dm66f08 = 8; 403 | optional de13 dm66f09 = 9; 404 | optional de14 dm66f10 = 10; 405 | optional de15 dm66f11 = 11; 406 | optional de16 dm66f12 = 12; 407 | optional int32 dm66f13 = 16; 408 | optional int32 dm66f14 = 13; 409 | optional int32 dm66f15 = 14; 410 | optional bool dm66f16 = 18; 411 | optional de17 dm66f17 = 15; 412 | optional int32 dm66f18 = 17; 413 | } 414 | message dm67 { 415 | } 416 | enum de18 { 417 | DE18_E01 = 600; 418 | DE18_E02 = 601; 419 | DE18_E03 = 602; 420 | DE18_E04 = 603; 421 | DE18_E05 = 604; 422 | DE18_E06 = 605; 423 | DE18_E07 = 606; 424 | DE18_E08 = 607; 425 | } 426 | message dm68 { 427 | optional string dm68f01 = 1; 428 | optional uint32 dm68f02 = 2; 429 | optional fixed32 dm68f03 = 3; 430 | optional uint32 dm68f04 = 4; 431 | optional uint32 dm68f05 = 5; 432 | optional uint32 dm68f06 = 6; 433 | } 434 | message dm69 { 435 | } 436 | message dm70 { 437 | optional fixed32 dm70f01 = 1; 438 | } 439 | message dm71 { 440 | } 441 | message dm72 { 442 | } 443 | message dm73 { 444 | required fixed32 dm73f01 = 1; 445 | } 446 | message dm74 { 447 | required fixed32 dm74f01 = 1; 448 | } 449 | message dm75 { 450 | required fixed32 dm75f01 = 1; 451 | } 452 | enum de19 { 453 | DE19_E01 = 700; 454 | DE19_E02 = 701; 455 | } 456 | message dm76 { 457 | } 458 | message dm77 { 459 | } 460 | enum de20 { 461 | DE20_E01 = 800; 462 | DE20_E02 = 801; 463 | } 464 | message dm78 { 465 | } 466 | message dm79 { 467 | required bool dm79f01 = 1; 468 | repeated uint32 dm79f02 = 2; 469 | repeated uint32 dm79f03 = 3; 470 | repeated uint32 dm79f04 = 4; 471 | repeated int32 dm79f05 = 5; 472 | } 473 | message dm1 { 474 | required uint32 dm01f01 = 1; 475 | optional dm28 dm01f02 = 100; 476 | optional dm29 dm01f03 = 101; 477 | optional dm30 dm01f04 = 102; 478 | optional dm31 dm01f05 = 103; 479 | optional dm32 dm01f06 = 104; 480 | optional dm02 dm01f07 = 200; 481 | optional dm03 dm01f08 = 201; 482 | optional dm04 dm01f09 = 202; 483 | optional dm05 dm01f10 = 203; 484 | optional dm06 dm01f11 = 204; 485 | optional dm07 dm01f12 = 205; 486 | optional dm08 dm01f13 = 206; 487 | optional dm09 dm01f14 = 207; 488 | optional dm10 dm01f15 = 208; 489 | optional dm11 dm01f16 = 209; 490 | optional dm12 dm01f17 = 210; 491 | optional dm13 dm01f18 = 211; 492 | optional dm14 dm01f19 = 212; 493 | optional dm15 dm01f20 = 213; 494 | optional dm16 dm01f21 = 214; 495 | optional dm17 dm01f22 = 215; 496 | optional dm18 dm01f23 = 216; 497 | optional dm20 dm01f24 = 217; 498 | optional dm21 dm01f25 = 218; 499 | optional dm19 dm01f26 = 219; 500 | optional dm37 dm01f27 = 300; 501 | optional dm38 dm01f28 = 301; 502 | optional dm39 dm01f29 = 302; 503 | optional dm40 dm01f30 = 303; 504 | optional dm41 dm01f31 = 304; 505 | optional dm42 dm01f32 = 305; 506 | optional dm43 dm01f33 = 306; 507 | optional dm44 dm01f34 = 307; 508 | optional dm45 dm01f35 = 308; 509 | optional dm46 dm01f36 = 309; 510 | optional dm47 dm01f37 = 310; 511 | optional dm48 dm01f38 = 400; 512 | optional dm49 dm01f39 = 401; 513 | optional dm52 dm01f40 = 402; 514 | optional dm53 dm01f41 = 403; 515 | optional dm54 dm01f42 = 404; 516 | optional dm55 dm01f43 = 405; 517 | optional dm58 dm01f44 = 406; 518 | optional dm59 dm01f45 = 407; 519 | optional dm60 dm01f46 = 408; 520 | optional dm61 dm01f47 = 409; 521 | optional dm63 dm01f48 = 410; 522 | optional dm64 dm01f49 = 411; 523 | optional dm62 dm01f50 = 412; 524 | optional dm65 dm01f51 = 500; 525 | optional dm66 dm01f52 = 501; 526 | optional dm67 dm01f53 = 502; 527 | optional dm68 dm01f54 = 600; 528 | optional dm69 dm01f55 = 601; 529 | optional dm70 dm01f56 = 602; 530 | optional dm71 dm01f57 = 603; 531 | optional dm72 dm01f58 = 604; 532 | optional dm73 dm01f59 = 605; 533 | optional dm74 dm01f60 = 606; 534 | optional dm75 dm01f61 = 607; 535 | optional dm76 dm01f62 = 700; 536 | optional dm77 dm01f63 = 701; 537 | optional dm78 dm01f64 = 800; 538 | optional dm79 dm01f65 = 801; 539 | } 540 | 541 | 542 | -------------------------------------------------------------------------------- /c_src/enif_protobuf.c: -------------------------------------------------------------------------------- 1 | #include "enif_protobuf.h" 2 | 3 | #if DEBUG_MEM 4 | size_t mem_total = 0; 5 | #endif // DEBUG_MEM 6 | 7 | ERL_NIF_TERM 8 | make_atom(ErlNifEnv *env, const char *name) 9 | { 10 | ERL_NIF_TERM atom; 11 | 12 | if (enif_make_existing_atom(env, name, &atom, ERL_NIF_LATIN1)) { 13 | return atom; 14 | } 15 | 16 | return enif_make_atom(env, name); 17 | } 18 | 19 | /* 20 | * nif library callbacks 21 | */ 22 | static int 23 | load(ErlNifEnv *env, void **priv, ERL_NIF_TERM info) 24 | { 25 | ep_enc_t *enc; 26 | uint32_t lock_n, i; 27 | ep_stack_t *stack; 28 | ep_state_t *state; 29 | 30 | if (*priv == NULL) { 31 | if (!enif_get_uint(env, info, &lock_n)) { 32 | return RET_ERROR; 33 | } 34 | 35 | state = _calloc(sizeof(ep_state_t), 1); 36 | if (state == NULL) { 37 | return RET_ERROR; 38 | } 39 | state->lock_n = lock_n; 40 | state->cache_lock = enif_rwlock_create("CACHE_LOCK"); 41 | state->local_lock = enif_rwlock_create("LOCAL_LOCK"); 42 | 43 | /* 44 | * init state->tdata 45 | */ 46 | state->tdata = _calloc(sizeof(ep_tdata_t), state->lock_n); 47 | if (state->tdata == NULL) { 48 | return RET_ERROR; 49 | } 50 | 51 | for (i = 0; i < state->lock_n; i++) { 52 | 53 | stack = &(state->tdata[i].stack); 54 | stack->size = STACK_INIT_SIZE; 55 | stack->spots = _calloc(sizeof(ep_spot_t), stack->size); 56 | if (stack->spots == NULL) { 57 | return RET_ERROR; 58 | } 59 | stack->end = stack->spots + stack->size; 60 | 61 | enc = &(state->tdata[i].enc); 62 | enc->mem = _calloc(ENC_INIT_SIZE, 1); 63 | enc->size = ENC_INIT_SIZE; 64 | } 65 | 66 | /* 67 | * init state->locks 68 | */ 69 | state->locks = _calloc(sizeof(ep_lock_t), state->lock_n); 70 | if (state->locks == NULL) { 71 | return RET_ERROR; 72 | } 73 | 74 | for (i = 0; i < state->lock_n; i++) { 75 | state->locks[i].tdata = &(state->tdata[i]); 76 | } 77 | 78 | #define EP_MAKE_ATOM(env, state, name) (state)->atom_##name = make_atom(env, #name) 79 | 80 | EP_MAKE_ATOM(env, state, ok); 81 | EP_MAKE_ATOM(env, state, error); 82 | EP_MAKE_ATOM(env, state, true); 83 | EP_MAKE_ATOM(env, state, false); 84 | EP_MAKE_ATOM(env, state, undefined); 85 | EP_MAKE_ATOM(env, state, field); 86 | EP_MAKE_ATOM(env, state, option); 87 | EP_MAKE_ATOM(env, state, infinity); 88 | (state)->atom_min_infinity = make_atom(env, "-infinity"); 89 | EP_MAKE_ATOM(env, state, nan); 90 | 91 | EP_MAKE_ATOM(env, state, int32); 92 | EP_MAKE_ATOM(env, state, int64); 93 | EP_MAKE_ATOM(env, state, uint32); 94 | EP_MAKE_ATOM(env, state, uint64); 95 | EP_MAKE_ATOM(env, state, sint32); 96 | EP_MAKE_ATOM(env, state, sint64); 97 | EP_MAKE_ATOM(env, state, fixed32); 98 | EP_MAKE_ATOM(env, state, fixed64); 99 | EP_MAKE_ATOM(env, state, sfixed32); 100 | EP_MAKE_ATOM(env, state, sfixed64); 101 | EP_MAKE_ATOM(env, state, bool); 102 | EP_MAKE_ATOM(env, state, float); 103 | EP_MAKE_ATOM(env, state, double); 104 | EP_MAKE_ATOM(env, state, string); 105 | EP_MAKE_ATOM(env, state, bytes); 106 | 107 | EP_MAKE_ATOM(env, state, enum); 108 | EP_MAKE_ATOM(env, state, msg); 109 | EP_MAKE_ATOM(env, state, map); 110 | 111 | EP_MAKE_ATOM(env, state, required); 112 | EP_MAKE_ATOM(env, state, optional); 113 | EP_MAKE_ATOM(env, state, defaulty); 114 | EP_MAKE_ATOM(env, state, repeated); 115 | 116 | *priv = (void *) state; 117 | } 118 | return RET_OK; 119 | } 120 | 121 | static int 122 | reload(ErlNifEnv *env, void **priv, ERL_NIF_TERM info) 123 | { 124 | return RET_OK; 125 | } 126 | 127 | static int 128 | upgrade(ErlNifEnv *env, void **priv, void **old_priv, ERL_NIF_TERM info) 129 | { 130 | *priv = *old_priv; 131 | return load(env, priv, info); 132 | } 133 | 134 | static void 135 | unload(ErlNifEnv *env, void *priv) 136 | { 137 | #if 0 138 | if (priv != NULL) { 139 | state = (ep_state_t *) enif_priv_data(env); 140 | _free(state->stack.mem); 141 | ep_cache_destroy(&(state->cache)); 142 | _free(state); 143 | } 144 | return; 145 | #endif 146 | } 147 | 148 | static ERL_NIF_TERM 149 | load_cache_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 150 | { 151 | char buf[16]; 152 | ep_spot_t *spot; 153 | ep_node_t *node = NULL; 154 | ep_cache_t *cache, *old_cache; 155 | ep_stack_t *stack; 156 | int32_t arity; 157 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 158 | uint32_t i, len = 0, proto_v = 2, max_fields = 2; 159 | ERL_NIF_TERM term, head, tail, ret, proto3_list = 0; 160 | ERL_NIF_TERM *array; 161 | 162 | if (argc != 1) { 163 | return enif_make_badarg(env); 164 | } 165 | 166 | term = argv[0]; 167 | while (enif_get_list_cell(env, term, &head, &tail)) { 168 | if (!enif_get_tuple(env, head, &arity, to_const(array)) || arity != 2) { 169 | raise_exception(env, head); 170 | } 171 | 172 | if (array[0] == make_atom(env, "syntax")) { 173 | if (!enif_get_string(env, array[1], buf, sizeof(buf), ERL_NIF_LATIN1)) { 174 | raise_exception(env, head); 175 | } 176 | 177 | if (!strncmp(buf, "proto2", sizeof("proto2"))) { 178 | proto_v = 2; 179 | } else if (!strncmp(buf, "proto3", sizeof("proto3"))) { 180 | proto_v = 3; 181 | } else { 182 | raise_exception(env, head); 183 | } 184 | 185 | term = tail; 186 | continue; 187 | } 188 | 189 | if (array[0] == make_atom(env, "proto3_msgs")) { 190 | if (enif_is_list(env, array[1])) { 191 | proto3_list = array[1]; 192 | } else { 193 | raise_exception(env, head); 194 | } 195 | term = tail; 196 | continue; 197 | } 198 | 199 | len++; 200 | term = tail; 201 | } 202 | 203 | if (len == 0) { 204 | raise_exception(env, argv[0]); 205 | } 206 | 207 | if (ep_cache_create(len, &cache) != RET_OK) { 208 | raise_exception(env, argv[0]); 209 | } 210 | 211 | term = argv[0]; 212 | while (enif_get_list_cell(env, term, &head, &tail)) { 213 | if (!enif_get_tuple(env, head, &arity, to_const(array)) || arity != 2) { 214 | ep_cache_destroy(&cache); 215 | raise_exception(env, head); 216 | } 217 | 218 | if ((ret = parse_node(env, head, &node, proto_v, proto3_list)) != RET_OK) { 219 | if (node != NULL) { 220 | free_node(node); 221 | } 222 | ep_cache_destroy(&cache); 223 | raise_exception(env, ret); 224 | } 225 | 226 | if (node != NULL) { 227 | if (node->n_type == node_msg || node->n_type == node_map) { 228 | max_fields = max_fields >= node->size ? max_fields : node->size; 229 | } 230 | ep_cache_insert(node, cache); 231 | } 232 | term = tail; 233 | } 234 | 235 | if (!cache->used) { 236 | ep_cache_destroy(&cache); 237 | raise_exception(env, argv[0]); 238 | } 239 | ep_cache_sort(cache); 240 | 241 | if ((ret = prelink_nodes(env, cache)) != RET_OK) { 242 | ep_cache_destroy(&cache); 243 | return ret; 244 | } 245 | 246 | enif_rwlock_rwlock(state->cache_lock); 247 | 248 | stack_ensure_all(env, cache); 249 | for (i = 0; i < state->lock_n; i++) { 250 | stack = &(state->tdata[i].stack); 251 | spot = stack->spots; 252 | while (spot < stack->end) { 253 | spot->t_size = max_fields + 1; 254 | if (spot->t_arr == NULL) { 255 | spot->t_arr = _calloc(sizeof(ERL_NIF_TERM), spot->t_size); 256 | } else { 257 | spot->t_arr = _realloc(spot->t_arr, sizeof(ERL_NIF_TERM) * spot->t_size); 258 | } 259 | spot++; 260 | } 261 | } 262 | old_cache = state->cache; 263 | state->cache = cache; 264 | 265 | enif_rwlock_rwunlock(state->cache_lock); 266 | 267 | if (old_cache != NULL) { 268 | ep_cache_destroy(&old_cache); 269 | } 270 | 271 | return state->atom_ok; 272 | } 273 | 274 | static ERL_NIF_TERM 275 | purge_cache_0(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 276 | { 277 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 278 | 279 | if(argc != 0) { 280 | return enif_make_badarg(env); 281 | } 282 | 283 | if (state->cache != NULL) { 284 | ep_cache_destroy(&(state->cache)); 285 | } 286 | 287 | return state->atom_ok; 288 | } 289 | 290 | static int 291 | search_compare_lock(const void *a, const void *b) 292 | { 293 | return (int) ((size_t) *((ErlNifTid *) a) - (size_t) ((ep_lock_t *) b)->tid); 294 | } 295 | 296 | static int 297 | sort_compare_lock(const void *a, const void *b) 298 | { 299 | return (int) ((size_t) ((ep_lock_t *) a)->tid - (size_t) ((ep_lock_t *) b)->tid); 300 | } 301 | 302 | static inline void 303 | clear_locks(ep_state_t *state, ErlNifTid *tid) 304 | { 305 | uint32_t i; 306 | 307 | enif_rwlock_rwlock(state->cache_lock); 308 | if (state->lock_used == state->lock_n) { 309 | for (i = 0; i < state->lock_n; i++) { 310 | state->locks[i].tid = *tid; 311 | } 312 | state->lock_used = 0; 313 | } 314 | enif_rwlock_rwunlock(state->cache_lock); 315 | 316 | return; 317 | } 318 | 319 | static ep_lock_t * 320 | get_lock(ep_state_t *state, ErlNifTid *tid) 321 | { 322 | ep_lock_t *lock; 323 | 324 | if (state->lock_used < state->lock_n) { 325 | //debug("used: %d, lock_n: %d", state->lock_used, state->lock_n); 326 | enif_rwlock_rlock(state->local_lock); 327 | lock = bsearch(tid, state->locks, state->lock_used, sizeof(ep_lock_t), search_compare_lock); 328 | enif_rwlock_runlock(state->local_lock); 329 | if (lock == NULL) { 330 | enif_rwlock_rwlock(state->local_lock); 331 | if (state->lock_used == state->lock_n) { 332 | clear_locks(state, tid); 333 | } else { 334 | lock = &state->locks[state->lock_used]; 335 | lock->tid = *tid; 336 | qsort(state->locks, state->lock_used + 1, sizeof(ep_lock_t), sort_compare_lock); 337 | (state->lock_used)++; 338 | } 339 | enif_rwlock_rwunlock(state->local_lock); 340 | } 341 | } else { 342 | lock = bsearch(tid, state->locks, state->lock_used, sizeof(ep_lock_t), search_compare_lock); 343 | if (lock == NULL) { 344 | clear_locks(state, tid); 345 | } 346 | } 347 | 348 | return lock; 349 | } 350 | 351 | static ERL_NIF_TERM 352 | encode_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 353 | { 354 | ep_lock_t *lock; 355 | ep_tdata_t *tdata; 356 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 357 | ErlNifTid tid; 358 | ERL_NIF_TERM ret; 359 | 360 | if (argc != 1 || !enif_is_tuple(env, argv[0])) { 361 | return enif_make_badarg(env); 362 | } 363 | 364 | if (state->cache == NULL) { 365 | raise_exception(env, make_atom(env, "cache_not_exists")); 366 | } 367 | 368 | tid = enif_thread_self(); 369 | lock = get_lock(state, &tid); 370 | if (lock == NULL) { 371 | return encode_1(env, argc, argv); 372 | } 373 | 374 | enif_rwlock_rlock(state->cache_lock); 375 | if (lock->tid != tid) { 376 | enif_rwlock_runlock(state->cache_lock); 377 | return encode_1(env, argc, argv); 378 | } 379 | 380 | //debug("used: %d, lock_n: %d, lock: 0x%016lx", state->lock_used, state->lock_n, (size_t) lock); 381 | tdata = lock->tdata; 382 | tdata->enc.p = tdata->enc.mem; 383 | tdata->enc.end = tdata->enc.mem + tdata->enc.size; 384 | 385 | if ((ret = (encode(env, argv[0], tdata))) == RET_OK) { 386 | ret = tdata->enc.result; 387 | } 388 | enif_rwlock_runlock(state->cache_lock); 389 | 390 | return ret; 391 | } 392 | 393 | static ERL_NIF_TERM 394 | decode_2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 395 | { 396 | ep_node_t *node; 397 | ep_lock_t *lock; 398 | ep_tdata_t *tdata; 399 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 400 | ErlNifTid tid; 401 | ERL_NIF_TERM ret; 402 | 403 | if (argc != 2 || !enif_is_binary(env, argv[0]) || !enif_is_atom(env, argv[1])) { 404 | return enif_make_badarg(env); 405 | } 406 | 407 | if (state->cache == NULL) { 408 | raise_exception(env, make_atom(env, "cache_not_exists")); 409 | } 410 | 411 | tid = enif_thread_self(); 412 | lock = get_lock(state, &tid); 413 | if (lock == NULL) { 414 | return decode_2(env, argc, argv); 415 | } 416 | 417 | enif_rwlock_rlock(state->cache_lock); 418 | if (lock->tid != tid) { 419 | enif_rwlock_runlock(state->cache_lock); 420 | return decode_2(env, argc, argv); 421 | } 422 | tdata = lock->tdata; 423 | 424 | //debug("used: %d, lock_n: %d, lock: 0x%016lx", state->lock_used, state->lock_n, (size_t) lock); 425 | if (!enif_inspect_binary(env, argv[0], &(tdata->dec.bin))) { 426 | enif_rwlock_runlock(state->cache_lock); 427 | raise_exception(env, argv[0]); 428 | } 429 | 430 | tdata->dec.p = (char *) (tdata->dec.bin.data); 431 | tdata->dec.end = tdata->dec.p + tdata->dec.bin.size; 432 | tdata->dec.term = argv[0]; 433 | 434 | node = get_node_by_name(argv[1], state->cache); 435 | if (node == NULL) { 436 | enif_rwlock_runlock(state->cache_lock); 437 | raise_exception(env, argv[1]); 438 | } 439 | if ((ret = (decode(env, tdata, node))) == RET_OK) { 440 | ret = tdata->dec.result; 441 | } 442 | enif_rwlock_runlock(state->cache_lock); 443 | 444 | return ret; 445 | } 446 | 447 | static ERL_NIF_TERM 448 | set_opts_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 449 | { 450 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 451 | int32_t arity; 452 | ERL_NIF_TERM head, tail, *array; 453 | 454 | if (argc != 1 && !enif_is_list(env, argv[0])) { 455 | return enif_make_badarg(env); 456 | } 457 | 458 | head = argv[0]; 459 | while (enif_get_list_cell(env, head, &head, &tail)) { 460 | if (enif_get_tuple(env, head, &arity, to_const(array)) && arity == 2) { 461 | if (array[0] == make_atom(env, "with_utf8")) { 462 | if (array[1] == state->atom_true) { 463 | state->opts.with_utf8 = 1; 464 | } else if (array[1] == state->atom_false) { 465 | state->opts.with_utf8 = 0; 466 | } else { 467 | return enif_make_badarg(env); 468 | } 469 | } else if (array[0] == make_atom(env, "string_as_list")) { 470 | if (array[1] == state->atom_true) { 471 | state->opts.string_as_list = 1; 472 | } else if (array[1] == state->atom_false) { 473 | state->opts.string_as_list = 0; 474 | } else { 475 | return enif_make_badarg(env); 476 | } 477 | } else { 478 | return enif_make_badarg(env); 479 | } 480 | } else { 481 | return enif_make_badarg(env); 482 | } 483 | 484 | head = tail; 485 | } 486 | 487 | return state->atom_ok; 488 | } 489 | 490 | #if DEBUG 491 | static ERL_NIF_TERM 492 | debug_term_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) 493 | { 494 | ERL_NIF_TERM term; 495 | 496 | if (argc != 1) { 497 | return enif_make_badarg(env); 498 | } 499 | 500 | term = argv[0]; 501 | if (enif_is_atom(env, term)) { 502 | printf("atom v:%lu\r\n", (unsigned long) term); 503 | } 504 | 505 | return term; 506 | } 507 | #endif 508 | 509 | static ErlNifFunc funcs[] = 510 | { 511 | #if DEBUG 512 | {"debug_term", 1, debug_term_1}, 513 | #endif 514 | {"set_opts", 1, set_opts_1}, 515 | {"load_cache", 1, load_cache_1}, 516 | {"purge_cache", 0, purge_cache_0}, 517 | {"encode", 1, encode_1}, 518 | {"decode", 2, decode_2} 519 | }; 520 | 521 | ERL_NIF_INIT(enif_protobuf, funcs, &load, &reload, &upgrade, &unload); 522 | -------------------------------------------------------------------------------- /test/ep_proper_encode_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ep_proper_encode_tests). 2 | 3 | -ifdef(PROPER). 4 | -include_lib("proper/include/proper.hrl"). 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -include_lib("gpb/include/gpb.hrl"). 7 | 8 | -compile(export_all). 9 | 10 | -import(enif_protobuf, [set_opts/1, load_cache/1, encode_msg/2, encode/1]). 11 | 12 | -record(m1, {a}). 13 | -record(m2, {a, b}). 14 | -record(m3, {a, b, c}). 15 | -record(m4, {a, b, c, d}). 16 | 17 | ep_proper_test() -> 18 | Functions = [F || {F, 0} <- ?MODULE:module_info(exports), F > 'prop_', F < 'prop`'], 19 | lists:foreach(fun(F) -> 20 | ?debugFmt("-> ~p", [F]), 21 | ?assert(proper:quickcheck(?MODULE:F(), [long_result, {to_file, user}])) 22 | end, Functions). 23 | 24 | utf8char() -> 25 | union([ 26 | integer(0, 36095), 27 | integer(57344, 65533), 28 | integer(65536, 1114111) 29 | ]). 30 | 31 | utf8string() -> list(utf8char()). 32 | 33 | ascii_string() -> list(choose(0, 127)). 34 | 35 | uint32() -> choose(0, 4294967295). 36 | 37 | sint32() -> choose(-2147483648, 2147483647). 38 | 39 | uint64() -> choose(0, 18446744073709551615). 40 | 41 | sint64() -> 42 | choose(-9223372036854775808, 9223372036854775807). 43 | 44 | value() -> 45 | oneof([{real(), double}, {real(), float}, {nan, float}, 46 | {infinity, float}, {'-infinity', float}, {nan, double}, 47 | {infinity, double}, {'-infinity', double}, 48 | {uint32(), uint32}, {uint64(), uint64}, 49 | {sint32(), sint32}, {sint64(), sint64}, 50 | {uint32(), fixed32}, {uint64(), fixed64}, 51 | {sint32(), sfixed32}, {sint64(), sfixed64}, 52 | {sint32(), int32}, {sint64(), int64}, {bool(), bool}, 53 | {sint32(), enum}, {utf8string(), string}, 54 | {binary(), bytes}]). 55 | 56 | prop_encode_int32() -> 57 | Defs = [ 58 | {{msg, m4}, [ 59 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = int32, occurrence = required, opts = []}, 60 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = int32, occurrence = optional, opts = []}, 61 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = int32, occurrence = repeated, opts = []}, 62 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = int32, occurrence = repeated, opts = [packed]} 63 | ]} 64 | ], 65 | ok = load_cache(Defs), 66 | ?FORALL(Message, 67 | #m4{ 68 | a = sint32(), 69 | b = oneof([sint32(), undefined]), 70 | c = list(sint32()), 71 | d = list(sint32()) 72 | }, 73 | begin 74 | encode(Message) =:= gpb:encode_msg(Message, Defs) 75 | end). 76 | 77 | prop_encode_int64() -> 78 | Defs = [ 79 | {{msg, m4}, [ 80 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = int64, occurrence = required, opts = []}, 81 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = int64, occurrence = optional, opts = []}, 82 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = int64, occurrence = repeated, opts = []}, 83 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = int64, occurrence = repeated, opts = [packed]} 84 | ]} 85 | ], 86 | ok = load_cache(Defs), 87 | ?FORALL(Message, 88 | #m4{ 89 | a = sint64(), 90 | b = oneof([sint64(), undefined]), 91 | c = list(sint64()), 92 | d = list(sint64()) 93 | }, 94 | begin 95 | encode(Message) =:= gpb:encode_msg(Message, Defs) 96 | end). 97 | 98 | prop_encode_uint32() -> 99 | Defs = [ 100 | {{msg, m4}, [ 101 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = uint32, occurrence = required, opts = []}, 102 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = uint32, occurrence = optional, opts = []}, 103 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = uint32, occurrence = repeated, opts = []}, 104 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = uint32, occurrence = repeated, opts = [packed]} 105 | ]} 106 | ], 107 | ok = load_cache(Defs), 108 | ?FORALL(Message, 109 | #m4{ 110 | a = uint32(), 111 | b = oneof([uint32(), undefined]), 112 | c = list(uint32()), 113 | d = list(uint32()) 114 | }, 115 | begin 116 | encode(Message) =:= gpb:encode_msg(Message, Defs) 117 | end). 118 | 119 | prop_encode_uint64() -> 120 | Defs = [ 121 | {{msg, m4}, [ 122 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = uint64, occurrence = required, opts = []}, 123 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = uint64, occurrence = optional, opts = []}, 124 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = uint64, occurrence = repeated, opts = []}, 125 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = uint64, occurrence = repeated, opts = [packed]} 126 | ]} 127 | ], 128 | ok = load_cache(Defs), 129 | ?FORALL(Message, 130 | #m4{ 131 | a = uint64(), 132 | b = oneof([uint64(), undefined]), 133 | c = list(uint64()), 134 | d = list(uint64()) 135 | }, 136 | begin 137 | encode(Message) =:= gpb:encode_msg(Message, Defs) 138 | end). 139 | 140 | prop_encode_sint32() -> 141 | Defs = [ 142 | {{msg, m4}, [ 143 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sint32, occurrence = required, opts = []}, 144 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sint32, occurrence = optional, opts = []}, 145 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sint32, occurrence = repeated, opts = []}, 146 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sint32, occurrence = repeated, opts = [packed]} 147 | ]} 148 | ], 149 | ok = load_cache(Defs), 150 | ?FORALL(Message, 151 | #m4{ 152 | a = sint32(), 153 | b = oneof([sint32(), undefined]), 154 | c = list(sint32()), 155 | d = list(sint32()) 156 | }, 157 | begin 158 | encode(Message) =:= gpb:encode_msg(Message, Defs) 159 | end). 160 | 161 | prop_encode_sint64() -> 162 | Defs = [ 163 | {{msg, m4}, [ 164 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sint64, occurrence = required, opts = []}, 165 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sint64, occurrence = optional, opts = []}, 166 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sint64, occurrence = repeated, opts = []}, 167 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sint64, occurrence = repeated, opts = [packed]} 168 | ]} 169 | ], 170 | ok = load_cache(Defs), 171 | ?FORALL(Message, 172 | #m4{ 173 | a = sint64(), 174 | b = oneof([sint64(), undefined]), 175 | c = list(sint64()), 176 | d = list(sint64()) 177 | }, 178 | begin 179 | encode(Message) =:= gpb:encode_msg(Message, Defs) 180 | end). 181 | 182 | prop_encode_fixed32() -> 183 | Defs = [ 184 | {{msg, m4}, [ 185 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = fixed32, occurrence = required, opts = []}, 186 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = fixed32, occurrence = optional, opts = []}, 187 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = fixed32, occurrence = repeated, opts = []}, 188 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = fixed32, occurrence = repeated, opts = [packed]} 189 | ]} 190 | ], 191 | ok = load_cache(Defs), 192 | ?FORALL(Message, 193 | #m4{ 194 | a = uint32(), 195 | b = oneof([uint32(), undefined]), 196 | c = list(uint32()), 197 | d = list(uint32()) 198 | }, 199 | begin 200 | encode(Message) =:= gpb:encode_msg(Message, Defs) 201 | end). 202 | 203 | prop_encode_fixed64() -> 204 | Defs = [ 205 | {{msg, m4}, [ 206 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = fixed64, occurrence = required, opts = []}, 207 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = fixed64, occurrence = optional, opts = []}, 208 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = fixed64, occurrence = repeated, opts = []}, 209 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = fixed64, occurrence = repeated, opts = [packed]} 210 | ]} 211 | ], 212 | ok = load_cache(Defs), 213 | ?FORALL(Message, 214 | #m4{ 215 | a = uint64(), 216 | b = oneof([uint64(), undefined]), 217 | c = list(uint64()), 218 | d = list(uint64()) 219 | }, 220 | begin 221 | encode(Message) =:= gpb:encode_msg(Message, Defs) 222 | end). 223 | 224 | prop_encode_sfixed32() -> 225 | Defs = [ 226 | {{msg, m4}, [ 227 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sfixed32, occurrence = required, opts = []}, 228 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sfixed32, occurrence = optional, opts = []}, 229 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sfixed32, occurrence = repeated, opts = []}, 230 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sfixed32, occurrence = repeated, opts = [packed]} 231 | ]} 232 | ], 233 | ok = load_cache(Defs), 234 | ?FORALL(Message, 235 | #m4{ 236 | a = sint32(), 237 | b = oneof([sint32(), undefined]), 238 | c = list(sint32()), 239 | d = list(sint32()) 240 | }, 241 | begin 242 | encode(Message) =:= gpb:encode_msg(Message, Defs) 243 | end). 244 | 245 | prop_encode_sfixed64() -> 246 | Defs = [ 247 | {{msg, m4}, [ 248 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sfixed64, occurrence = required, opts = []}, 249 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sfixed64, occurrence = optional, opts = []}, 250 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sfixed64, occurrence = repeated, opts = []}, 251 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sfixed64, occurrence = repeated, opts = [packed]} 252 | ]} 253 | ], 254 | ok = load_cache(Defs), 255 | ?FORALL(Message, 256 | #m4{ 257 | a = sint64(), 258 | b = oneof([sint64(), undefined]), 259 | c = list(sint64()), 260 | d = list(sint64()) 261 | }, 262 | begin 263 | encode(Message) =:= gpb:encode_msg(Message, Defs) 264 | end). 265 | 266 | prop_encode_float() -> 267 | Defs = [ 268 | {{msg, m4}, [ 269 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = float, occurrence = required, opts = []}, 270 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = float, occurrence = optional, opts = []}, 271 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = float, occurrence = repeated, opts = []}, 272 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = float, occurrence = repeated, opts = [packed]} 273 | ]} 274 | ], 275 | ok = load_cache(Defs), 276 | ?FORALL(Message, 277 | #m4{ 278 | a = oneof([infinity, '-infinity', nan, float(), sint32()]), 279 | b = oneof([infinity, '-infinity', nan, float(), sint32(), undefined]), 280 | c = list(oneof([infinity, '-infinity', nan, float(), sint32()])), 281 | d = list(oneof([infinity, '-infinity', nan, float(), sint32()])) 282 | }, 283 | begin 284 | encode(Message) =:= gpb:encode_msg(Message, Defs) 285 | end). 286 | 287 | prop_encode_double() -> 288 | Defs = [ 289 | {{msg, m4}, [ 290 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = double, occurrence = required, opts = []}, 291 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = double, occurrence = optional, opts = []}, 292 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = double, occurrence = repeated, opts = []}, 293 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = double, occurrence = repeated, opts = [packed]} 294 | ]} 295 | ], 296 | ok = load_cache(Defs), 297 | ?FORALL(Message, 298 | #m4{ 299 | a = oneof([infinity, '-infinity', nan, float(), sint32()]), 300 | b = oneof([infinity, '-infinity', nan, float(), sint32(), undefined]), 301 | c = list(oneof([infinity, '-infinity', nan, float(), sint32()])), 302 | d = list(oneof([infinity, '-infinity', nan, float(), sint32()])) 303 | }, 304 | begin 305 | encode(Message) =:= gpb:encode_msg(Message, Defs) 306 | end). 307 | 308 | prop_encode_bool() -> 309 | Defs = [ 310 | {{msg, m4}, [ 311 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = bool, occurrence = required, opts = []}, 312 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = bool, occurrence = optional, opts = []}, 313 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = bool, occurrence = repeated, opts = []}, 314 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = bool, occurrence = repeated, opts = [packed]} 315 | ]} 316 | ], 317 | ok = load_cache(Defs), 318 | ?FORALL(Message, 319 | #m4{ 320 | a = oneof([true, false, 1, 0]), 321 | b = oneof([true, false, 1, 0, undefined]), 322 | c = list(oneof([true, false, 1, 0])), 323 | d = list(oneof([true, false, 1, 0])) 324 | }, 325 | begin 326 | encode(Message) =:= gpb:encode_msg(Message, Defs) 327 | end). 328 | 329 | prop_encode_ascii_string() -> 330 | Defs = [ 331 | {{msg, m3}, [ 332 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = string, occurrence = required, opts = []}, 333 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = string, occurrence = optional, opts = []}, 334 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = string, occurrence = repeated, opts = []} 335 | ]} 336 | ], 337 | ok = load_cache(Defs), 338 | ?FORALL(Message, 339 | #m3{ 340 | a = ascii_string(), 341 | b = oneof([ascii_string(), undefined]), 342 | c = list(ascii_string()) 343 | }, 344 | begin 345 | encode(Message) =:= gpb:encode_msg(Message, Defs) 346 | end). 347 | 348 | prop_encode_utf8_string() -> 349 | Defs = [ 350 | {{msg, m3}, [ 351 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = string, occurrence = required, opts = []}, 352 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = string, occurrence = optional, opts = []}, 353 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = string, occurrence = repeated, opts = []} 354 | ]} 355 | ], 356 | ok = load_cache(Defs), 357 | ok = enif_protobuf:set_opts([{with_utf8, true}]), 358 | ?FORALL(Message, 359 | #m3{ 360 | a = utf8string(), 361 | b = oneof([utf8string(), undefined]), 362 | c = list(utf8string()) 363 | }, 364 | begin 365 | encode(Message) =:= gpb:encode_msg(Message, Defs) 366 | end). 367 | 368 | prop_encode_bytes() -> 369 | Defs = [ 370 | {{msg, m3}, [ 371 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = bytes, occurrence = required, opts = []}, 372 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = bytes, occurrence = optional, opts = []}, 373 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = bytes, occurrence = repeated, opts = []} 374 | ]} 375 | ], 376 | ok = load_cache(Defs), 377 | ok = enif_protobuf:set_opts([{with_utf8, false}]), 378 | ?FORALL(Message, 379 | #m3{ 380 | a = binary(), 381 | b = oneof([binary(), undefined]), 382 | c = list(binary()) 383 | }, 384 | begin 385 | encode(Message) =:= gpb:encode_msg(Message, Defs) 386 | end). 387 | 388 | prop_encode_enum() -> 389 | Defs = [ 390 | {{msg, m4}, [ 391 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = {enum, e}, occurrence = required, opts = []}, 392 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = {enum, e}, occurrence = optional, opts = []}, 393 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = {enum, e}, occurrence = repeated, opts = []}, 394 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = {enum, e}, occurrence = repeated, opts = [packed]} 395 | ]}, 396 | {{enum, e}, [{v1, 100}, {v2, 150}]} 397 | ], 398 | ok = load_cache(Defs), 399 | ?FORALL(Message, 400 | #m4{ 401 | a = oneof([v1, v2, sint32()]), 402 | b = oneof([v1, v2, sint32(), undefined]), 403 | c = list(oneof([v1, v2, sint32()])), 404 | d = list(oneof([v1, v2, sint32()])) 405 | }, 406 | begin 407 | encode(Message) =:= gpb:encode_msg(Message, Defs) 408 | end). 409 | 410 | prop_encode_enum_aliases() -> 411 | Defs = [ 412 | {{msg, m4}, [ 413 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = {enum, e}, occurrence = required, opts = []}, 414 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = {enum, e}, occurrence = optional, opts = []}, 415 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = {enum, e}, occurrence = repeated, opts = []}, 416 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = {enum, e}, occurrence = repeated, opts = [packed]} 417 | ]}, 418 | {{enum, e}, [{option, allow_alias, true}, {v1, 100}, {v2, 150}, {v3, 100}]} 419 | ], 420 | ok = load_cache(Defs), 421 | ?FORALL(Message, 422 | #m4{ 423 | a = oneof([v1, v2, v3, sint32()]), 424 | b = oneof([v1, v2, v3, sint32(), undefined]), 425 | c = list(oneof([v1, v2, v3, sint32()])), 426 | d = list(oneof([v1, v2, v3, sint32()])) 427 | }, 428 | begin 429 | encode(Message) =:= gpb:encode_msg(Message, Defs) 430 | end). 431 | 432 | prop_encode_map() -> 433 | Type = {map, string, fixed32}, 434 | Defs = [ 435 | {{msg, m3}, [ 436 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = Type, occurrence = required, opts = []}, 437 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = Type, occurrence = optional, opts = []}, 438 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = Type, occurrence = repeated, opts = []} 439 | ]} 440 | ], 441 | ok = load_cache(Defs), 442 | ?FORALL(Message, 443 | #m3{ 444 | a = {ascii_string(), sint32()}, 445 | b = oneof([{ascii_string(), sint32()}, undefined]), 446 | c = list({ascii_string(), sint32()}) 447 | }, 448 | begin 449 | encode(Message) =:= gpb:encode_msg(Message, Defs) 450 | end). 451 | 452 | prop_encode_oneof() -> 453 | Defs = [ 454 | {{msg, m1}, [ 455 | #gpb_oneof{name = a, rnum = #m1.a, fields = [ 456 | #?gpb_field{name = a1, fnum = 1, rnum = #m1.a, type = int32, occurrence = optional, opts = []}, 457 | #?gpb_field{name = a2, fnum = 2, rnum = #m1.a, type = int32, occurrence = optional, opts = []} 458 | ]}]} 459 | ], 460 | ok = load_cache(Defs), 461 | ?FORALL(Message, 462 | #m1{a = oneof([undefined, {a1, sint32()}, {a2, sint32()}])}, 463 | begin 464 | encode(Message) =:= gpb:encode_msg(Message, Defs) 465 | end). 466 | 467 | prop_encode_sub_msg() -> 468 | Defs = [ 469 | {{msg, m3}, [ 470 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = {msg, m2}, occurrence = required, opts = []}, 471 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = {msg, m2}, occurrence = optional, opts = []}, 472 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = {msg, m2}, occurrence = repeated, opts = []} 473 | ]}, 474 | {{msg, m2}, [ 475 | #?gpb_field{name = a, fnum = 1, rnum = #m2.a, type = uint32, occurrence = required, opts = []}, 476 | #?gpb_field{name = b, fnum = 2, rnum = #m2.b, type = uint64, occurrence = required, opts = []} 477 | ]} 478 | ], 479 | ok = load_cache(Defs), 480 | ?FORALL(Message, 481 | #m3{ 482 | a = #m2{a = uint32(), b = uint64()}, 483 | b = oneof([#m2{a = uint32(), b = uint64()}, undefined]), 484 | c = list(#m2{a = uint32(), b = uint64()}) 485 | }, 486 | begin 487 | encode(Message) =:= gpb:encode_msg(Message, Defs) 488 | end). 489 | -endif. 490 | -------------------------------------------------------------------------------- /c_src/ep_node.c: -------------------------------------------------------------------------------- 1 | #include "enif_protobuf.h" 2 | 3 | #define A_ALLOW_ALIAS "allow_alias" 4 | #define A_DEFAULT "default" 5 | #define A_ONEOF "gpb_oneof" 6 | #define A_PACKED "packed" 7 | #define A_DEPRECATED "deprecated" 8 | 9 | ERL_NIF_TERM 10 | fill_msg_field(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field); 11 | 12 | ep_node_t * 13 | make_node(int fields_n, node_type_e n_type); 14 | 15 | ERL_NIF_TERM 16 | link_sub_node(ErlNifEnv *env, ep_cache_t *cache, ep_field_t *field); 17 | 18 | ERL_NIF_TERM 19 | do_prelink_nodes(ErlNifEnv *env, ep_cache_t *cache, ep_node_t *node); 20 | 21 | ERL_NIF_TERM 22 | parse_enum_fields(ErlNifEnv *env, ERL_NIF_TERM term, ep_node_t *node); 23 | 24 | ERL_NIF_TERM 25 | parse_field_basic(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field); 26 | 27 | ERL_NIF_TERM 28 | parse_map_type(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field); 29 | 30 | ERL_NIF_TERM 31 | parse_field_type(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field); 32 | 33 | ERL_NIF_TERM 34 | parse_occurrence_type(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field); 35 | 36 | ERL_NIF_TERM 37 | parse_opts(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field); 38 | 39 | ERL_NIF_TERM 40 | parse_oneof_fields(ErlNifEnv *env, ERL_NIF_TERM term, ep_node_t *node); 41 | 42 | ERL_NIF_TERM 43 | fill_oneof_field(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field); 44 | 45 | ERL_NIF_TERM 46 | parse_msg_fields(ErlNifEnv *env, ERL_NIF_TERM term, ep_node_t *node); 47 | 48 | ERL_NIF_TERM 49 | stack_ensure_size(ErlNifEnv *env, ep_stack_t *stack, size_t size); 50 | 51 | static int 52 | sort_compare_name_field(const void *a, const void *b) 53 | { 54 | return (int) (((ep_field_t *) a)->name - ((ep_field_t *) b)->name); 55 | } 56 | 57 | static int 58 | sort_compare_msg_field(const void *a, const void *b) 59 | { 60 | return (int) (((ep_field_t *) a)->rnum - ((ep_field_t *) b)->rnum); 61 | } 62 | 63 | static int 64 | sort_compare_fnum_field(const void *a, const void *b) 65 | { 66 | return (int) (((ep_fnum_field_t *) a)->fnum - ((ep_fnum_field_t *) b)->fnum); 67 | } 68 | 69 | ep_node_t * 70 | make_node(int fields_n, node_type_e n_type) 71 | { 72 | ep_node_t *node = _calloc(sizeof(ep_node_t), 1); 73 | 74 | if (node == NULL) { 75 | return NULL; 76 | } 77 | 78 | if (fields_n > 0) { 79 | if (n_type == node_msg || n_type == node_oneof) { 80 | node->fields = _calloc(sizeof(ep_field_t), fields_n); 81 | if (node->fields == NULL) { 82 | free_node(node); 83 | return NULL; 84 | } 85 | } else if (n_type == node_map) { 86 | node->fields = _calloc(sizeof(ep_field_t), fields_n); 87 | if (node->fields == NULL) { 88 | free_node(node); 89 | return NULL; 90 | } 91 | } else if (n_type == node_enum) { 92 | node->fields = _calloc(sizeof(ep_enum_field_t), fields_n); 93 | node->v_fields = _calloc(sizeof(ep_enum_field_t), fields_n); 94 | if (node->fields == NULL || node->v_fields == NULL) { 95 | free_node(node); 96 | return NULL; 97 | } 98 | } 99 | } 100 | 101 | return node; 102 | } 103 | 104 | void 105 | free_node(ep_node_t *node) 106 | { 107 | size_t i; 108 | ep_field_t *field; 109 | 110 | if (node->fields != NULL) { 111 | for (i = 0; i < node->size; i++) { 112 | if (node->n_type == node_msg || node->n_type == node_map) { 113 | field = &(((ep_field_t *) node->fields)[i]); 114 | if (field->type == field_map || field->type == field_oneof) { 115 | if (field->sub_node != NULL) { 116 | free_node(field->sub_node); 117 | field->sub_node = NULL; 118 | } 119 | } 120 | } 121 | } 122 | _free(node->fields); 123 | node->fields = NULL; 124 | } 125 | 126 | if (node->v_fields != NULL) { 127 | _free(node->v_fields); 128 | node->v_fields = NULL; 129 | } 130 | 131 | _free(node); 132 | } 133 | 134 | static int 135 | sort_compare_enum_field(const void *a, const void *b) 136 | { 137 | return (int) (((ep_enum_field_t *) a)->name - ((ep_enum_field_t *) b)->name); 138 | } 139 | 140 | static int 141 | sort_compare_enum_v_field(const void *a, const void *b) 142 | { 143 | return (int) (((ep_enum_field_t *) a)->value - ((ep_enum_field_t *) b)->value); 144 | } 145 | 146 | ERL_NIF_TERM 147 | parse_enum_fields(ErlNifEnv *env, ERL_NIF_TERM term, ep_node_t *node) 148 | { 149 | int32_t arity, allow_alias = 0,value; 150 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 151 | ERL_NIF_TERM *array; 152 | ERL_NIF_TERM head, tail, tmp; 153 | ep_enum_field_t *field, *v_field, *vf; 154 | 155 | tmp = term; 156 | while (enif_get_list_cell(env, tmp, &head, &tail)) { 157 | if (!enif_get_tuple(env, head, &arity, to_const(array))) { 158 | raise_exception(env, term); 159 | } 160 | 161 | if (arity == 3) { 162 | if (array[0] == state->atom_option && array[1] == make_atom(env, A_ALLOW_ALIAS)) { 163 | if (array[2] == state->atom_true) { 164 | allow_alias = 1; 165 | break; 166 | } else if (array[2] == state->atom_false) { 167 | allow_alias = 0; 168 | break; 169 | } else { 170 | raise_exception(env, head); 171 | } 172 | } else { 173 | raise_exception(env, head); 174 | } 175 | } else if (arity != 2) { 176 | raise_exception(env, head); 177 | } 178 | tmp = tail; 179 | } 180 | 181 | field = node->fields; 182 | v_field = node->v_fields; 183 | while (enif_get_list_cell(env, term, &head, &tail)) { 184 | if (!enif_get_tuple(env, head, &arity, to_const(array))) { 185 | raise_exception(env, term); 186 | } 187 | 188 | if (arity == 2) { 189 | if (enif_is_atom(env, array[0]) && enif_get_int(env, array[1], &value)) { 190 | field->name = array[0]; 191 | field->value = value; 192 | } else { 193 | raise_exception(env, term); 194 | } 195 | } else { 196 | term = tail; 197 | continue; 198 | } 199 | 200 | v_field->name = field->name; 201 | v_field->value = field->value; 202 | for (vf = node->v_fields; vf < v_field; vf++) { 203 | if (vf->value == v_field->value) { 204 | if (allow_alias) { 205 | v_field--; 206 | break; 207 | } else { 208 | raise_exception(env, head); 209 | } 210 | } 211 | } 212 | field->proto_v = node->proto_v; 213 | field++; 214 | v_field++; 215 | 216 | term = tail; 217 | } 218 | 219 | qsort(node->fields, node->size, sizeof(ep_enum_field_t), sort_compare_enum_field); 220 | node->v_size = (uint32_t) (v_field - (ep_enum_field_t *) node->v_fields); 221 | qsort(node->v_fields, node->v_size, sizeof(ep_enum_field_t), sort_compare_enum_v_field); 222 | 223 | return RET_OK; 224 | } 225 | 226 | /* 227 | * msg 228 | */ 229 | ERL_NIF_TERM 230 | parse_field_basic(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field) 231 | { 232 | int32_t arity; 233 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 234 | ERL_NIF_TERM *array; 235 | 236 | if (term == state->atom_int32) { field->type = field_int32; return RET_OK; } 237 | else if (term == state->atom_int64) { field->type = field_int64; return RET_OK; } 238 | else if (term == state->atom_uint32) { field->type = field_uint32; return RET_OK; } 239 | else if (term == state->atom_uint64) { field->type = field_uint64; return RET_OK; } 240 | else if (term == state->atom_sint32) { field->type = field_sint32; return RET_OK; } 241 | else if (term == state->atom_sint64) { field->type = field_sint64; return RET_OK; } 242 | else if (term == state->atom_fixed32) { field->type = field_fixed32; return RET_OK; } 243 | else if (term == state->atom_fixed64) { field->type = field_fixed64; return RET_OK; } 244 | else if (term == state->atom_sfixed32) { field->type = field_sfixed32; return RET_OK; } 245 | else if (term == state->atom_sfixed64) { field->type = field_sfixed64; return RET_OK; } 246 | else if (term == state->atom_bool) { field->type = field_bool; return RET_OK; } 247 | else if (term == state->atom_float) { field->type = field_float; return RET_OK; } 248 | else if (term == state->atom_double) { field->type = field_double; return RET_OK; } 249 | else if (term == state->atom_string) { field->type = field_string; return RET_OK; } 250 | else if (term == state->atom_bytes) { field->type = field_bytes; return RET_OK; } 251 | 252 | else if (enif_get_tuple(env, term, &arity, to_const(array))) { 253 | if (arity == 2 && array[0] == state->atom_enum && enif_is_atom(env, array[1])) { 254 | field->type = field_enum; 255 | field->sub_name = array[1]; 256 | field->sub_node = NULL; 257 | return RET_OK; 258 | } else if (arity == 2 && array[0] == state->atom_msg && enif_is_atom(env, array[1])) { 259 | field->type = field_msg; 260 | field->sub_name = array[1]; 261 | field->sub_node = NULL; 262 | return RET_OK; 263 | } 264 | } 265 | 266 | return RET_ERROR; 267 | } 268 | 269 | ERL_NIF_TERM 270 | parse_map_type(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field) 271 | { 272 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 273 | int32_t arity; 274 | ep_node_t *node; 275 | ERL_NIF_TERM *array; 276 | 277 | if (!enif_get_tuple(env, term, &arity, to_const(array))) { 278 | raise_exception(env, term); 279 | } 280 | 281 | if (arity != 3 || array[0] != state->atom_map) { 282 | raise_exception(env, term); 283 | } 284 | 285 | field->type = field_map; 286 | field->sub_node = make_node(2, node_msg); 287 | if (field->sub_node == NULL) { 288 | raise_exception(env, state->atom_error); 289 | } 290 | 291 | node = field->sub_node; 292 | node->name = field->name; 293 | node->proto_v = field->proto_v; 294 | node->id = 0; 295 | node->size = 2; 296 | node->n_type = node_map; 297 | 298 | if (array[1] != state->atom_int32 299 | && array[1] != state->atom_int64 300 | && array[1] != state->atom_uint32 301 | && array[1] != state->atom_uint64 302 | && array[1] != state->atom_sint32 303 | && array[1] != state->atom_sint64 304 | && array[1] != state->atom_fixed32 305 | && array[1] != state->atom_fixed64 306 | && array[1] != state->atom_sfixed32 307 | && array[1] != state->atom_sfixed64 308 | && array[1] != state->atom_bool 309 | && array[1] != state->atom_double 310 | && array[1] != state->atom_string 311 | ) { 312 | raise_exception(env, array[1]); 313 | } 314 | 315 | parse_msg_fields(env, enif_make_list2(env, 316 | enif_make_tuple7(env, 317 | state->atom_field, 318 | make_atom(env, "k"), 319 | enif_make_uint(env, 1), 320 | enif_make_uint(env, 2), 321 | array[1], 322 | state->atom_optional, 323 | enif_make_list(env, 0) 324 | ), 325 | enif_make_tuple7(env, 326 | state->atom_field, 327 | make_atom(env, "v"), 328 | enif_make_uint(env, 2), 329 | enif_make_uint(env, 3), 330 | array[2], 331 | state->atom_optional, 332 | enif_make_list(env, 0) 333 | ) 334 | ), node); 335 | return RET_OK; 336 | } 337 | 338 | ERL_NIF_TERM 339 | parse_field_type(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field) 340 | { 341 | if (parse_field_basic(env, term, field) == RET_OK) { 342 | return RET_OK; 343 | } 344 | 345 | if (parse_map_type(env, term, field) == RET_OK) { 346 | return RET_OK; 347 | } 348 | 349 | return RET_ERROR; 350 | } 351 | 352 | ERL_NIF_TERM 353 | parse_occurrence_type(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field) 354 | { 355 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 356 | 357 | if (term == state->atom_required) { field->o_type = occurrence_required; return RET_OK; } 358 | else if (term == state->atom_optional) { field->o_type = occurrence_optional; return RET_OK; } 359 | else if (term == state->atom_defaulty) { field->o_type = occurrence_defaulty; return RET_OK; } 360 | else if (term == state->atom_repeated) { field->o_type = occurrence_repeated; return RET_OK; } 361 | 362 | return RET_ERROR; 363 | } 364 | 365 | ERL_NIF_TERM 366 | parse_opts(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field) 367 | { 368 | int32_t arity; 369 | ERL_NIF_TERM *array; 370 | ERL_NIF_TERM head, tail; 371 | 372 | while (enif_get_list_cell(env, term, &head, &tail)) { 373 | if (head == make_atom(env, A_PACKED)) { 374 | field->packed = TRUE; 375 | } else if (enif_get_tuple(env, head, &arity, to_const(array)) 376 | && arity == 2 && array[0] == make_atom(env, A_DEFAULT)) { 377 | field->defaut_value = array[1]; 378 | } else if (head == make_atom(env, A_DEPRECATED)) { 379 | /* skip */ 380 | } else { 381 | return RET_ERROR; 382 | } 383 | 384 | term = tail; 385 | } 386 | 387 | return RET_OK; 388 | } 389 | 390 | ERL_NIF_TERM 391 | parse_oneof_fields(ErlNifEnv *env, ERL_NIF_TERM term, ep_node_t *node) 392 | { 393 | ep_field_t *field; 394 | int32_t arity; 395 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 396 | ERL_NIF_TERM head, tail, ret; 397 | ERL_NIF_TERM *array; 398 | 399 | field = node->fields; 400 | 401 | if (!enif_is_list(env, term)) { 402 | raise_exception(env, term); 403 | } 404 | while (enif_get_list_cell(env, term, &head, &tail)) { 405 | if (!enif_get_tuple(env, head, &arity, to_const(array))) { 406 | raise_exception(env, term); 407 | } 408 | if (arity == 7 && array[0] == state->atom_field) { 409 | check_ret(ret, fill_msg_field(env, head, field)); 410 | } else { 411 | raise_exception(env, term); 412 | } 413 | field++; 414 | term = tail; 415 | } 416 | 417 | qsort(node->fields, node->size, sizeof(ep_field_t), sort_compare_name_field); 418 | return RET_OK; 419 | } 420 | 421 | ERL_NIF_TERM 422 | fill_oneof_field(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field) 423 | { 424 | int32_t arity; 425 | uint32_t len; 426 | ERL_NIF_TERM *array, ret; 427 | 428 | if (!enif_get_tuple(env, term, &arity, to_const(array))) { 429 | raise_exception(env, term); 430 | } 431 | 432 | if (array[0] != make_atom(env, A_ONEOF)) { 433 | raise_exception(env, term); 434 | } 435 | field->name = array[1]; 436 | 437 | if (!enif_get_uint(env, array[2], &(field->rnum))) { 438 | raise_exception(env, term); 439 | } 440 | (field->rnum)--; 441 | 442 | field->type = field_oneof; 443 | 444 | if (!enif_get_list_length(env, array[3], &len) || len == 0) { 445 | raise_exception(env, term); 446 | } 447 | field->sub_node = make_node(len, node_oneof); 448 | 449 | field->sub_node->n_type = node_oneof; 450 | field->sub_node->size = len; 451 | check_ret(ret, parse_oneof_fields(env, array[3], field->sub_node)); 452 | field->fnum = ((ep_field_t *) (field->sub_node->fields))->fnum; 453 | 454 | return RET_OK; 455 | } 456 | 457 | ERL_NIF_TERM 458 | fill_msg_field(ErlNifEnv *env, ERL_NIF_TERM term, ep_field_t *field) 459 | { 460 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 461 | int32_t arity; 462 | ERL_NIF_TERM *array; 463 | 464 | if (!enif_get_tuple(env, term, &arity, to_const(array))) { 465 | raise_exception(env, term); 466 | } 467 | 468 | if (arity != 7 || array[0] != state->atom_field) { 469 | raise_exception(env, term); 470 | } 471 | 472 | if (!enif_is_atom(env, array[1])) { 473 | raise_exception(env, term); 474 | } 475 | field->name = array[1]; 476 | 477 | if (!enif_get_uint(env, array[2], &(field->fnum)) 478 | || !enif_get_uint(env, array[3], &(field->rnum))) { 479 | raise_exception(env, term); 480 | } 481 | (field->rnum)--; 482 | 483 | if (parse_field_type(env, array[4], field) != RET_OK) { 484 | raise_exception(env, term); 485 | } 486 | 487 | if (parse_occurrence_type(env, array[5], field) != RET_OK) { 488 | raise_exception(env, term); 489 | } 490 | 491 | if (parse_opts(env, array[6], field) != RET_OK) { 492 | raise_exception(env, term); 493 | } 494 | 495 | return RET_OK; 496 | } 497 | 498 | ERL_NIF_TERM 499 | parse_msg_fields(ErlNifEnv *env, ERL_NIF_TERM term, ep_node_t *node) 500 | { 501 | ep_field_t *field, *f; 502 | int32_t arity; 503 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 504 | uint32_t i, j; 505 | ep_fnum_field_t *ff; 506 | ERL_NIF_TERM head, tail, ret; 507 | ERL_NIF_TERM *array; 508 | 509 | field = node->fields; 510 | node->v_size = 0; 511 | while (enif_get_list_cell(env, term, &head, &tail)) { 512 | if (!enif_get_tuple(env, head, &arity, to_const(array))) { 513 | raise_exception(env, head); 514 | } 515 | 516 | if (arity == 7 && array[0] == state->atom_field) { 517 | check_ret(ret, fill_msg_field(env, head, field)); 518 | node->v_size++; 519 | } else if (array[0] == make_atom(env, A_ONEOF)) { 520 | check_ret(ret, fill_oneof_field(env, head, field)); 521 | node->v_size += field->sub_node->size; 522 | } else { 523 | raise_exception(env, head); 524 | } 525 | 526 | field->proto_v = node->proto_v; 527 | field++; 528 | term = tail; 529 | } 530 | 531 | qsort(node->fields, node->size, sizeof(ep_field_t), sort_compare_msg_field); 532 | 533 | if (node->v_size > 0) { 534 | node->v_fields = _calloc(sizeof(ep_fnum_field_t), node->v_size); 535 | ff = node->v_fields; 536 | field = node->fields; 537 | for (i = 0; i < node->size; i++) { 538 | if (field->type == field_oneof) { 539 | f = field->sub_node->fields; 540 | for (j = 0; j < field->sub_node->size; j++) { 541 | ff->fnum = f->fnum; 542 | ff->field = f; 543 | f->is_oneof = TRUE; 544 | ff++; 545 | f++; 546 | } 547 | } else { 548 | ff->fnum = field->fnum; 549 | ff->field = field; 550 | ff++; 551 | } 552 | field++; 553 | } 554 | 555 | ff = node->v_fields; 556 | for (i = 0; i < node->v_size; i++) { 557 | ff++; 558 | } 559 | 560 | qsort(node->v_fields, node->v_size, sizeof(ep_fnum_field_t), sort_compare_fnum_field); 561 | } 562 | 563 | return RET_OK; 564 | } 565 | 566 | ERL_NIF_TERM 567 | parse_node(ErlNifEnv *env, ERL_NIF_TERM term, ep_node_t **node, uint32_t proto_v, ERL_NIF_TERM proto3_list) 568 | { 569 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 570 | int32_t arity, msg_arity; 571 | uint32_t len; 572 | node_type_e n_type; 573 | ERL_NIF_TERM head, tail, *array, *msg_array, ret; 574 | 575 | *node = NULL; 576 | if (!enif_get_tuple(env, term, &arity, to_const(array)) || arity != 2) { 577 | return RET_OK; 578 | } 579 | 580 | if (!enif_get_tuple(env, array[0], &msg_arity, to_const(msg_array)) || msg_arity != 2) { 581 | return RET_OK; 582 | } 583 | 584 | if (!enif_get_list_length(env, array[1], &len)) { 585 | return RET_OK; 586 | } 587 | 588 | if (msg_array[0] == state->atom_msg) { 589 | n_type = node_msg; 590 | } else if (msg_array[0] == state->atom_enum) { 591 | n_type = node_enum; 592 | } else { 593 | return RET_OK; 594 | } 595 | 596 | *node = make_node(len, n_type); 597 | if (*node == NULL) { 598 | raise_exception(env, state->atom_error); 599 | } 600 | 601 | if (!enif_is_atom(env, msg_array[1])) { 602 | raise_exception(env, term); 603 | } 604 | (*node)->name = msg_array[1]; 605 | 606 | (*node)->proto_v = proto_v; 607 | if (proto3_list) { 608 | while (enif_get_list_cell(env, proto3_list, &head, &tail)) { 609 | if ((*node)->name == head) { 610 | (*node)->proto_v = 3; 611 | } 612 | proto3_list = tail; 613 | } 614 | } 615 | 616 | (*node)->id = 0; 617 | (*node)->size = len; 618 | (*node)->n_type = n_type; 619 | 620 | if (n_type == node_msg) { 621 | check_ret(ret, parse_msg_fields(env, array[1], (*node))); 622 | } else { 623 | check_ret(ret, parse_enum_fields(env, array[1], (*node))); 624 | } 625 | return RET_OK; 626 | } 627 | 628 | ERL_NIF_TERM 629 | link_sub_node(ErlNifEnv *env, ep_cache_t *cache, ep_field_t *field) 630 | { 631 | ERL_NIF_TERM ret; 632 | 633 | if (field->type == field_msg || field->type == field_map || field->type == field_enum) { 634 | if (field->sub_node == NULL) { 635 | field->sub_node = get_node_by_name(field->sub_name, cache); 636 | if (field->sub_node == NULL) { 637 | raise_exception(env, field->sub_name); 638 | } 639 | } else { 640 | check_ret(ret, do_prelink_nodes(env, cache, field->sub_node)); 641 | } 642 | } 643 | return RET_OK; 644 | } 645 | 646 | ERL_NIF_TERM 647 | do_prelink_nodes(ErlNifEnv *env, ep_cache_t *cache, ep_node_t *node) 648 | { 649 | uint32_t i; 650 | ep_field_t *field; 651 | ERL_NIF_TERM ret; 652 | ep_fnum_field_t *fnum_field; 653 | 654 | if (node->n_type == node_msg || node->n_type == node_map) { 655 | for (i = 0; i < node->size; i++) { 656 | field = (ep_field_t *) (node->fields) + i; 657 | check_ret(ret, link_sub_node(env, cache, field)); 658 | } 659 | 660 | for (i = 0; i < node->v_size; i++) { 661 | fnum_field = (ep_fnum_field_t *) (node->v_fields) + i; 662 | check_ret(ret, link_sub_node(env, cache, fnum_field->field)); 663 | } 664 | } 665 | return RET_OK; 666 | } 667 | 668 | ERL_NIF_TERM 669 | prelink_nodes(ErlNifEnv *env, ep_cache_t *cache) 670 | { 671 | size_t i; 672 | ep_node_t *node; 673 | ERL_NIF_TERM ret; 674 | 675 | for (i = 0; i < cache->used; i++) { 676 | node = cache->names[i].node; 677 | check_ret(ret, do_prelink_nodes(env, cache, node)); 678 | } 679 | 680 | return RET_OK; 681 | } 682 | 683 | ERL_NIF_TERM 684 | stack_ensure(ErlNifEnv *env, ep_stack_t *stack, ep_spot_t **spot) 685 | { 686 | ep_spot_t *spots; 687 | size_t size; 688 | 689 | if ((*spot) >= stack->end) { 690 | size = stack->size * 2; 691 | spots = _realloc(stack->spots, sizeof(ep_spot_t) * size); 692 | if (spots == NULL) { 693 | raise_exception(env, enif_make_string(env, "realloc failed", ERL_NIF_LATIN1)); 694 | } 695 | 696 | *spot = spots + (*spot - stack->spots); 697 | 698 | memset(spots + stack->size, 0x00, sizeof(ep_spot_t) * (size - stack->size)); 699 | stack->spots = spots; 700 | stack->size = size; 701 | stack->end = stack->spots + stack->size; 702 | } 703 | 704 | return RET_OK; 705 | } 706 | 707 | ERL_NIF_TERM 708 | stack_ensure_size(ErlNifEnv *env, ep_stack_t *stack, size_t size) 709 | { 710 | ep_spot_t *spots; 711 | 712 | if (stack->size >= size) { 713 | return RET_OK; 714 | } 715 | 716 | spots = _realloc(stack->spots, sizeof(ep_spot_t) * size); 717 | if (spots == NULL) { 718 | raise_exception(env, enif_make_string(env, "realloc failed", ERL_NIF_LATIN1)); 719 | } 720 | 721 | memset(spots + stack->size, 0x00, sizeof(ep_spot_t) * (size - stack->size)); 722 | stack->spots = spots; 723 | stack->size = size; 724 | stack->end = stack->spots + stack->size; 725 | 726 | return RET_OK; 727 | } 728 | 729 | void 730 | stack_ensure_all(ErlNifEnv *env, ep_cache_t *cache) 731 | { 732 | size_t i, j, stack_size; 733 | ep_spot_t *spot; 734 | ep_field_t *field; 735 | ep_stack_t *stack; 736 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 737 | 738 | stack = &(state->tdata[0].stack); 739 | 740 | for (i = 0; i < cache->used; i++) { 741 | spot = stack->spots; 742 | spot->type = spot_tuple; 743 | spot->node = cache->names[i].node; 744 | spot->pos = 0; 745 | 746 | while (spot >= stack->spots) { 747 | if (spot->type == spot_tuple) { 748 | if (spot->pos == spot->node->size) { 749 | spot->type = spot_default; 750 | spot->pos = 0; 751 | spot--; 752 | continue; 753 | } 754 | 755 | // skip over enums - they can't be nested 756 | if (spot->node->n_type == node_enum) { 757 | spot->pos = spot->node->size; 758 | continue; 759 | } 760 | 761 | for (j = spot->pos; j < (size_t) (spot->node->size); j++) { 762 | spot->pos = j + 1; 763 | field = ((ep_field_t *) (spot->node->fields)) + j; 764 | if (field->o_type == occurrence_repeated) { 765 | if (field->type == field_msg || field->type == field_map) { 766 | spot++; 767 | stack_ensure(env, stack, &spot); 768 | 769 | spot->type = spot_list; 770 | spot->pos = 0; 771 | spot->node = field->sub_node; 772 | break; 773 | } 774 | } else if (field->type == field_oneof 775 | || field->type == field_msg 776 | || field->type == field_map) { 777 | spot++; 778 | stack_ensure(env, stack, &spot); 779 | 780 | spot->type = spot_tuple; 781 | spot->pos = 0; 782 | spot->node = field->sub_node; 783 | break; 784 | } 785 | } 786 | } else if (spot->type == spot_list) { 787 | if (spot->pos > 0) { 788 | spot--; 789 | continue; 790 | } 791 | spot->pos = 1; 792 | spot++; 793 | stack_ensure(env, stack, &spot); 794 | 795 | spot->node = (spot - 1)->node; 796 | spot->type = spot_tuple; 797 | spot->pos = 0; 798 | } 799 | } 800 | } 801 | 802 | stack_size = stack->size; 803 | for (i = 1; i < state->lock_n; i++) { 804 | stack = &(state->tdata[i].stack); 805 | stack_ensure_size(env, stack, stack_size); 806 | } 807 | } 808 | 809 | int 810 | get_field_compare_name(const void *a, const void *b) 811 | { 812 | return (int) (*((ERL_NIF_TERM *) a) - ((ep_field_t *) b)->name); 813 | } 814 | 815 | int 816 | get_map_field_compare_fnum(const void *a, const void *b) 817 | { 818 | return (int) (*((int32_t *) a) - ((ep_field_t *) b)->fnum); 819 | } 820 | 821 | int 822 | get_field_compare_fnum(const void *a, const void *b) 823 | { 824 | return (int) (*((int32_t *) a) - ((ep_fnum_field_t *) b)->fnum); 825 | } 826 | 827 | int 828 | get_enum_compare_name(const void *a, const void *b) 829 | { 830 | return (int) (*((ERL_NIF_TERM *) a) - ((ep_enum_field_t *) b)->name); 831 | } 832 | 833 | int 834 | get_enum_compare_value(const void *a, const void *b) 835 | { 836 | return (int) (*((int32_t *) a) - ((ep_enum_field_t *) b)->value); 837 | } 838 | -------------------------------------------------------------------------------- /c_src/ep_encoder.c: -------------------------------------------------------------------------------- 1 | #include "enif_protobuf.h" 2 | 3 | #if 0 4 | #define enc_ensure(env, enc, size) do { \ 5 | printf("%s:%d:%s()\r\n", __FILE__, __LINE__, __func__); \ 6 | _enc_ensure(env, enc, size); \ 7 | } while(0) 8 | #endif 9 | 10 | #define enc_ensure_default(env, enc) enc_ensure(env, enc, MAX_UINT64_ENCODED_SIZE) 11 | 12 | static inline ep_enc_t * 13 | enc_ensure(ErlNifEnv *env, ep_enc_t *enc, size_t size) 14 | { 15 | if (enc->p + size > enc->end) { 16 | 17 | enc->size *= 2; 18 | while (enc->mem + enc->size < enc->p + size) { 19 | enc->size *= 2; 20 | } 21 | 22 | enc->tmp = _realloc(enc->mem, enc->size); 23 | if (enc->tmp == NULL) { 24 | return NULL; 25 | } 26 | 27 | enc->sentinel = (enc->sentinel - enc->mem) + enc->tmp; 28 | enc->p = (enc->p - enc->mem) + enc->tmp; 29 | enc->mem = enc->tmp; 30 | enc->end = enc->tmp + enc->size; 31 | } 32 | 33 | return enc; 34 | } 35 | 36 | static inline void 37 | do_pack_uint32(ErlNifEnv *env, uint32_t val, ep_enc_t *enc) 38 | { 39 | if (val >= 0x80) { 40 | *(enc->p)++ = val | 0x80; 41 | val >>= 7; 42 | 43 | if (val >= 0x80) { 44 | *(enc->p)++ = val | 0x80; 45 | val >>= 7; 46 | 47 | if (val >= 0x80) { 48 | *(enc->p)++ = val | 0x80; 49 | val >>= 7; 50 | 51 | if (val >= 0x80) { 52 | *(enc->p)++ = val | 0x80; 53 | val >>= 7; 54 | } 55 | } 56 | } 57 | } 58 | 59 | *(enc->p)++ = val; 60 | } 61 | 62 | static inline ERL_NIF_TERM 63 | pack_uint32(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 64 | { 65 | uint32_t val; 66 | 67 | if (!enif_get_uint(env, term, &val)) { 68 | raise_exception(env, term); 69 | } 70 | 71 | if (val == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 72 | return RET_OK; 73 | } 74 | 75 | enc_ensure_default(env, enc); 76 | 77 | do_pack_uint32(env, val, enc); 78 | 79 | return RET_OK; 80 | } 81 | 82 | static inline ERL_NIF_TERM 83 | pack_sint32(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 84 | { 85 | int32_t val; 86 | 87 | if (!enif_get_int(env, term, &val)) { 88 | raise_exception(env, term); 89 | } 90 | 91 | if (val == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 92 | return RET_OK; 93 | } 94 | 95 | enc_ensure_default(env, enc); 96 | 97 | if (val < 0) { 98 | val = (-val) * 2 - 1; 99 | 100 | } else { 101 | val = val * 2; 102 | } 103 | 104 | do_pack_uint32(env, val, enc); 105 | 106 | return RET_OK; 107 | } 108 | 109 | static inline ERL_NIF_TERM 110 | pack_int32(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 111 | { 112 | int32_t val; 113 | 114 | if (!enif_get_int(env, term, &val)) { 115 | raise_exception(env, term); 116 | } 117 | 118 | if (val == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 119 | return RET_OK; 120 | } 121 | 122 | enc_ensure_default(env, enc); 123 | 124 | if (val < 0) { 125 | *(enc->p)++ = val | 0x80; 126 | *(enc->p)++ = (val >> 7) | 0x80; 127 | *(enc->p)++ = (val >> 14) | 0x80; 128 | *(enc->p)++ = (val >> 21) | 0x80; 129 | *(enc->p)++ = (val >> 28) | 0x80; 130 | *((int32_t *) (enc->p)) = 0xffffffff; 131 | enc->p += sizeof(int32_t); 132 | *(enc->p)++ = 0x01; 133 | 134 | } else { 135 | do_pack_uint32(env, val, enc); 136 | } 137 | 138 | return RET_OK; 139 | } 140 | 141 | static inline ERL_NIF_TERM 142 | pack_fixed32(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 143 | { 144 | int32_t val; 145 | 146 | if (!enif_get_int(env, term, &val)) { 147 | raise_exception(env, term); 148 | } 149 | 150 | if (val == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 151 | return RET_OK; 152 | } 153 | 154 | enc_ensure(env, enc, sizeof(int32_t)); 155 | 156 | //*((int32_t *) (enc->p)) = swap_int32(val); 157 | *((int32_t *) (enc->p)) = val; 158 | enc->p += sizeof(int32_t); 159 | 160 | return RET_OK; 161 | } 162 | 163 | static inline void 164 | do_pack_uint64(ErlNifEnv *env, uint64_t val, ep_enc_t *enc) 165 | { 166 | uint32_t hi = (uint32_t) (val >> 32); 167 | uint32_t lo = (uint32_t) val; 168 | 169 | if (hi == 0) { 170 | do_pack_uint32(env, (uint32_t) lo, enc); 171 | return; 172 | } 173 | 174 | *(enc->p)++ = (lo) | 0x80; 175 | *(enc->p)++ = (lo >> 7) | 0x80; 176 | *(enc->p)++ = (lo >> 14) | 0x80; 177 | *(enc->p)++ = (lo >> 21) | 0x80; 178 | 179 | if (hi < 8) { 180 | *(enc->p)++ = (hi << 4) | (lo >> 28); 181 | return; 182 | 183 | } else { 184 | *(enc->p)++ = ((hi & 7) << 4) | (lo >> 28) | 0x80; 185 | hi >>= 3; 186 | } 187 | 188 | while (hi >= 128) { 189 | *(enc->p)++ = hi | 0x80; 190 | hi >>= 7; 191 | } 192 | *(enc->p)++ = hi; 193 | } 194 | 195 | static inline ERL_NIF_TERM 196 | pack_uint64(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 197 | { 198 | ErlNifUInt64 val; 199 | 200 | if (!enif_get_uint64(env, term, &val)) { 201 | raise_exception(env, term); 202 | } 203 | 204 | if (val == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 205 | return RET_OK; 206 | } 207 | 208 | enc_ensure_default(env, enc); 209 | 210 | do_pack_uint64(env, val, enc); 211 | 212 | return RET_OK; 213 | } 214 | 215 | static inline ERL_NIF_TERM 216 | pack_sint64(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 217 | { 218 | ErlNifSInt64 val; 219 | 220 | if (!enif_get_int64(env, term, &val)) { 221 | raise_exception(env, term); 222 | } 223 | 224 | if (val == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 225 | return RET_OK; 226 | } 227 | 228 | enc_ensure_default(env, enc); 229 | 230 | if (val < 0) { 231 | val = (-val) * 2 - 1; 232 | 233 | } else { 234 | val = val * 2; 235 | } 236 | 237 | do_pack_uint64(env, val, enc); 238 | 239 | return RET_OK; 240 | } 241 | 242 | static inline ERL_NIF_TERM 243 | pack_int64(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 244 | { 245 | ErlNifSInt64 val; 246 | 247 | if (!enif_get_int64(env, term, &val)) { 248 | raise_exception(env, term); 249 | } 250 | 251 | if (val == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 252 | return RET_OK; 253 | } 254 | 255 | enc_ensure_default(env, enc); 256 | 257 | do_pack_uint64(env, (uint64_t) val, enc); 258 | 259 | return RET_OK; 260 | } 261 | 262 | static inline ERL_NIF_TERM 263 | pack_fixed64(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 264 | { 265 | ErlNifSInt64 val; 266 | 267 | if (!enif_get_int64(env, term, &val)) { 268 | raise_exception(env, term); 269 | } 270 | 271 | if (val == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 272 | return RET_OK; 273 | } 274 | 275 | enc_ensure(env, enc, sizeof(int64_t)); 276 | 277 | //*((int64_t *) (enc->p)) = swap_int64(val); 278 | *((int64_t *) (enc->p)) = val; 279 | enc->p += sizeof(int64_t); 280 | 281 | return RET_OK; 282 | } 283 | 284 | static inline ERL_NIF_TERM 285 | pack_boolean(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 286 | { 287 | int32_t val; 288 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 289 | 290 | if (term == state->atom_true) { 291 | val = 1; 292 | 293 | } else if (term == state->atom_false) { 294 | val = 0; 295 | 296 | } else if (!enif_get_int(env, term, &val) || (val != 1 && val != 0)) { 297 | raise_exception(env, term); 298 | } 299 | 300 | if (val == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 301 | return RET_OK; 302 | } 303 | 304 | enc_ensure(env, enc, sizeof(int8_t)); 305 | 306 | *(enc->p)++ = (int8_t) (val & 0x000000ff); 307 | 308 | return RET_OK; 309 | } 310 | 311 | static inline ERL_NIF_TERM 312 | pack_float(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 313 | { 314 | float val; 315 | double d_val; 316 | int32_t i_val; 317 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 318 | 319 | if (enif_get_double(env, term, &d_val)) { 320 | val = (float) d_val; 321 | 322 | } else if (enif_get_int(env, term, &i_val)) { 323 | val = (float) i_val; 324 | 325 | } else if (enif_is_atom(env, term)) { 326 | 327 | if (term == state->atom_infinity) { 328 | val = INFINITY; 329 | 330 | } else if (term == state->atom_min_infinity) { 331 | val = -INFINITY; 332 | 333 | } else if (term == state->atom_nan) { 334 | val = _NAN; 335 | 336 | } else { 337 | raise_exception(env, term); 338 | } 339 | 340 | } else { 341 | raise_exception(env, term); 342 | } 343 | 344 | if (val == 0.0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 345 | return RET_OK; 346 | } 347 | 348 | enc_ensure(env, enc, sizeof(int32_t)); 349 | 350 | *((int32_t *) (enc->p)) = *((int32_t *) &val); 351 | enc->p += sizeof(int32_t); 352 | 353 | return RET_OK; 354 | } 355 | 356 | static inline ERL_NIF_TERM 357 | pack_double(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 358 | { 359 | double val; 360 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 361 | ErlNifSInt64 l_val; 362 | 363 | if (!enif_get_double(env, term, &val)) { 364 | 365 | if (enif_get_int64(env, term, &l_val)) { 366 | val = (double) l_val; 367 | 368 | } else if (enif_is_atom(env, term)) { 369 | 370 | if (term == state->atom_infinity) { 371 | val = INFINITY; 372 | 373 | } else if (term == state->atom_min_infinity) { 374 | val = -INFINITY; 375 | 376 | } else if (term == state->atom_nan) { 377 | val = _NAN; 378 | 379 | } else { 380 | raise_exception(env, term); 381 | } 382 | 383 | } else { 384 | raise_exception(env, term); 385 | } 386 | } 387 | 388 | if (val == 0.0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 389 | return RET_OK; 390 | } 391 | 392 | enc_ensure(env, enc, sizeof(int64_t)); 393 | 394 | *((int64_t *) (enc->p)) = *((int64_t *) &val); 395 | 396 | enc->p += sizeof(int64_t); 397 | 398 | return RET_OK; 399 | } 400 | 401 | static inline ERL_NIF_TERM 402 | pack_utf8(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 403 | { 404 | uint32_t val; 405 | 406 | if (!enif_get_uint(env, term, &val)) { 407 | raise_exception(env, term); 408 | } 409 | 410 | if (val < 0x80) { 411 | *(enc->p)++ = (uint8_t) val; 412 | 413 | } else if (val < 0x800) { 414 | *(enc->p)++ = (uint8_t) (0xC0 + (val >> 6)); 415 | *(enc->p)++ = (uint8_t) (0x80 + (val & 0x3F)); 416 | 417 | } else if (val == 0xFFFF) { 418 | *(enc->p)++ = (uint8_t) 0xFF; 419 | 420 | } else if (val == 0xFFFE) { 421 | *(enc->p)++ = (uint8_t) 0xFE; 422 | 423 | } else if (val < 0x10000) { 424 | *(enc->p)++ = (uint8_t) (0xE0 + (val >> 12)); 425 | *(enc->p)++ = (uint8_t) (0x80 + ((val >> 6) & 0x3F)); 426 | *(enc->p)++ = (uint8_t) (0x80 + (val & 0x3F)); 427 | 428 | } else if (val < 0x110000) { 429 | *(enc->p)++ = (uint8_t) (0xF0 + (val >> 18)); 430 | *(enc->p)++ = (uint8_t) (0x80 + ((val >> 12) & 0x3F)); 431 | *(enc->p)++ = (uint8_t) (0x80 + ((val >> 6) & 0x3F)); 432 | *(enc->p)++ = (uint8_t) (0x80 + (val & 0x3F)); 433 | 434 | } else { 435 | raise_exception(env, term); 436 | } 437 | 438 | return RET_OK; 439 | } 440 | 441 | static inline ERL_NIF_TERM 442 | pack_bytes(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 443 | { 444 | ErlNifBinary bin; 445 | 446 | if (!enif_inspect_iolist_as_binary(env, term, &bin)) { 447 | raise_exception(env, term); 448 | } 449 | 450 | if (bin.size == 0) { 451 | if (field->proto_v == 3 && field->o_type == occurrence_defaulty) { 452 | return RET_OK; 453 | } 454 | 455 | enc_ensure(env, enc, 1); 456 | *(enc->p++) = 0; 457 | return RET_OK; 458 | } 459 | 460 | enc_ensure(env, enc, MAX_UINT64_ENCODED_SIZE + bin.size); 461 | 462 | do_pack_uint32(env, (uint32_t) bin.size, enc); 463 | 464 | memcpy(enc->p, bin.data, bin.size); 465 | enc->p += bin.size; 466 | 467 | return RET_OK; 468 | } 469 | 470 | static inline ERL_NIF_TERM 471 | pack_string_list(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 472 | { 473 | ErlNifBinary bin; 474 | ERL_NIF_TERM head, tail, ret; 475 | 476 | while (enif_get_list_cell(env, term, &head, &tail)) { 477 | if (enif_is_list(env, head)) { 478 | check_ret(ret, pack_string_list(env, head, enc, field)); 479 | } else if (enif_is_binary(env, head) && enif_inspect_binary(env, head, &bin)) { 480 | if (bin.size > 0) { 481 | enc_ensure(env, enc, bin.size); 482 | memcpy(enc->p, bin.data, bin.size); 483 | enc->p += bin.size; 484 | } 485 | } else { 486 | enc_ensure(env, enc, 4); 487 | check_ret(ret, pack_utf8(env, head, enc, field)); 488 | } 489 | term = tail; 490 | } 491 | return RET_OK; 492 | } 493 | 494 | static inline ERL_NIF_TERM 495 | pack_string(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 496 | { 497 | size_t payload_len; 498 | uint8_t *p, *end; 499 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 500 | ErlNifBinary bin; 501 | ERL_NIF_TERM ret; 502 | 503 | if (!state->opts.with_utf8) { 504 | return pack_bytes(env, term, enc, field); 505 | } 506 | 507 | if (enif_is_list(env, term)) { 508 | if (enif_is_empty_list(env, term)) { 509 | if (field->proto_v == 3 && field->o_type == occurrence_defaulty) { 510 | return RET_OK; 511 | } 512 | 513 | enc_ensure(env, enc, 1); 514 | *(enc->p++) = 0; 515 | return RET_OK; 516 | } 517 | 518 | enc_ensure(env, enc, MAX_UINT64_ENCODED_SIZE); 519 | enc->p += MAX_UINT64_ENCODED_SIZE; 520 | enc->tmp = enc->p; 521 | 522 | if (enif_inspect_iolist_as_binary(env, term, &bin)) { 523 | p = bin.data; 524 | end = bin.data + bin.size; 525 | while (p < end) { 526 | if (*p < 0x80) { 527 | *(enc->p)++ = *p; 528 | } else { 529 | *(enc->p)++ = (uint8_t) (0xC0 + (*p >> 6)); 530 | *(enc->p)++ = (uint8_t) (0x80 + (*p & 0x3F)); 531 | } 532 | p++; 533 | } 534 | } else { 535 | check_ret(ret, pack_string_list(env, term, enc, field)); 536 | } 537 | 538 | payload_len = enc->p - enc->tmp; 539 | enc->p = enc->tmp - MAX_UINT64_ENCODED_SIZE; 540 | if (payload_len == 0 && field->proto_v == 3 && field->o_type == occurrence_defaulty) { 541 | return RET_OK; 542 | } 543 | do_pack_uint32(env, (uint32_t) payload_len, enc); 544 | memmove(enc->p, enc->tmp, payload_len); 545 | enc->p += payload_len; 546 | } else if (enif_is_binary(env, term) && enif_inspect_binary(env, term, &bin)) { 547 | 548 | if (bin.size == 0) { 549 | if (field->proto_v == 3 && field->o_type == occurrence_defaulty) { 550 | return RET_OK; 551 | } 552 | 553 | enc_ensure(env, enc, 1); 554 | *(enc->p++) = 0; 555 | return RET_OK; 556 | } 557 | 558 | enc_ensure(env, enc, MAX_UINT64_ENCODED_SIZE + bin.size); 559 | do_pack_uint32(env, (uint32_t) bin.size, enc); 560 | memcpy(enc->p, bin.data, bin.size); 561 | enc->p += bin.size; 562 | } else { 563 | raise_exception(env, term); 564 | } 565 | 566 | return RET_OK; 567 | } 568 | 569 | static inline ERL_NIF_TERM 570 | pack_enum(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 571 | { 572 | int32_t val; 573 | ep_node_t *node = field->sub_node; 574 | ep_enum_field_t *sub_field; 575 | 576 | if (enif_is_atom(env, term)) { 577 | sub_field = bsearch(&term, node->fields, node->size, sizeof(ep_enum_field_t), get_enum_compare_name); 578 | if (sub_field == NULL) { 579 | raise_exception(env, term); 580 | } 581 | val = sub_field->value; 582 | } else if (!enif_get_int(env, term, &val)) { 583 | raise_exception(env, term); 584 | } 585 | 586 | sub_field = node->fields; 587 | if (sub_field->value == val && sub_field->proto_v == 3 && field->o_type == occurrence_defaulty) { 588 | return RET_OK; 589 | } 590 | 591 | enc_ensure_default(env, enc); 592 | 593 | if (val < 0) { 594 | *(enc->p)++ = val | 0x80; 595 | *(enc->p)++ = (val >> 7) | 0x80; 596 | *(enc->p)++ = (val >> 14) | 0x80; 597 | *(enc->p)++ = (val >> 21) | 0x80; 598 | *(enc->p)++ = (val >> 28) | 0x80; 599 | *((int32_t *) (enc->p)) = 0xffffffff; 600 | enc->p += sizeof(int32_t); 601 | *(enc->p)++ = 0x01; 602 | } else { 603 | do_pack_uint32(env, val, enc); 604 | }; 605 | 606 | return RET_OK; 607 | } 608 | 609 | static inline ERL_NIF_TERM 610 | pack_tag(ErlNifEnv *env, uint32_t id, ep_enc_t *enc) 611 | { 612 | enc_ensure_default(env, enc); 613 | 614 | if (id < (1UL << (32 - 3))) { 615 | do_pack_uint32(env, id << 3, enc); 616 | 617 | } else { 618 | 619 | do_pack_uint64(env, ((uint64_t) id) << 3, enc); 620 | } 621 | 622 | return RET_OK; 623 | } 624 | 625 | static inline ep_field_t * 626 | get_oneof_field(ErlNifEnv *env, ERL_NIF_TERM term, ep_node_t *node, ERL_NIF_TERM *out) 627 | { 628 | int32_t arity; 629 | ERL_NIF_TERM *array; 630 | 631 | if (!enif_get_tuple(env, term, &arity, to_const(array))) { 632 | return NULL; 633 | } 634 | 635 | if (arity != 2 || !enif_is_atom(env, array[0])) { 636 | return NULL; 637 | } 638 | 639 | *out = array[1]; 640 | 641 | return bsearch(&(array[0]), node->fields, node->size, sizeof(ep_field_t), get_field_compare_name); 642 | } 643 | 644 | static ERL_NIF_TERM 645 | pack_element_packed(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 646 | { 647 | ERL_NIF_TERM ret; 648 | 649 | switch (field->type) { 650 | case field_sint32: 651 | check_ret(ret, pack_sint32(env, term, enc, field)); 652 | break; 653 | 654 | case field_enum: 655 | check_ret(ret, pack_enum(env, term, enc, field)); 656 | break; 657 | 658 | case field_int32: 659 | check_ret(ret, pack_int32(env, term, enc, field)); 660 | break; 661 | 662 | case field_uint32: 663 | check_ret(ret, pack_uint32(env, term, enc, field)); 664 | break; 665 | 666 | case field_sint64: 667 | check_ret(ret, pack_sint64(env, term, enc, field)); 668 | break; 669 | 670 | case field_int64: 671 | check_ret(ret, pack_int64(env, term, enc, field)); 672 | break; 673 | 674 | case field_uint64: 675 | check_ret(ret, pack_uint64(env, term, enc, field)); 676 | break; 677 | 678 | case field_sfixed32: 679 | case field_fixed32: 680 | check_ret(ret, pack_fixed32(env, term, enc, field)); 681 | break; 682 | 683 | case field_float: 684 | check_ret(ret, pack_float(env, term, enc, field)); 685 | break; 686 | 687 | case field_sfixed64: 688 | case field_fixed64: 689 | check_ret(ret, pack_fixed64(env, term, enc, field)); 690 | break; 691 | 692 | case field_double: 693 | check_ret(ret, pack_double(env, term, enc, field)); 694 | break; 695 | 696 | case field_bool: 697 | check_ret(ret, pack_boolean(env, term, enc, field)); 698 | break; 699 | 700 | default: 701 | raise_exception(env, term); 702 | } 703 | 704 | return RET_OK; 705 | } 706 | 707 | static ERL_NIF_TERM 708 | pack_field(ErlNifEnv *env, ERL_NIF_TERM term, ep_enc_t *enc, ep_field_t *field) 709 | { 710 | char *sentinel; 711 | ERL_NIF_TERM ret; 712 | 713 | enc->sentinel = enc->p; 714 | pack_tag(env, field->fnum, enc); 715 | sentinel = enc->p; 716 | 717 | switch (field->type) { 718 | case field_sint32: 719 | *(enc->sentinel) |= WIRE_TYPE_VARINT; 720 | check_ret(ret, pack_sint32(env, term, enc, field)); 721 | break; 722 | 723 | case field_enum: 724 | *(enc->sentinel) |= WIRE_TYPE_VARINT; 725 | check_ret(ret, pack_enum(env, term, enc, field)); 726 | break; 727 | 728 | case field_int32: 729 | *(enc->sentinel) |= WIRE_TYPE_VARINT; 730 | check_ret(ret, pack_int32(env, term, enc, field)); 731 | break; 732 | 733 | case field_uint32: 734 | *(enc->sentinel) |= WIRE_TYPE_VARINT; 735 | check_ret(ret, pack_uint32(env, term, enc, field)); 736 | break; 737 | 738 | case field_sint64: 739 | *(enc->sentinel) |= WIRE_TYPE_VARINT; 740 | check_ret(ret, pack_sint64(env, term, enc, field)); 741 | break; 742 | 743 | case field_int64: 744 | *(enc->sentinel) |= WIRE_TYPE_VARINT; 745 | check_ret(ret, pack_int64(env, term, enc, field)); 746 | break; 747 | 748 | case field_uint64: 749 | *(enc->sentinel) |= WIRE_TYPE_VARINT; 750 | check_ret(ret, pack_uint64(env, term, enc, field)); 751 | break; 752 | 753 | case field_sfixed32: 754 | case field_fixed32: 755 | *(enc->sentinel) |= WIRE_TYPE_32BIT; 756 | check_ret(ret, pack_fixed32(env, term, enc, field)); 757 | break; 758 | 759 | case field_float: 760 | *(enc->sentinel) |= WIRE_TYPE_32BIT; 761 | check_ret(ret, pack_float(env, term, enc, field)); 762 | break; 763 | 764 | case field_sfixed64: 765 | case field_fixed64: 766 | *(enc->sentinel) |= WIRE_TYPE_64BIT; 767 | check_ret(ret, pack_fixed64(env, term, enc, field)); 768 | break; 769 | 770 | case field_double: 771 | *(enc->sentinel) |= WIRE_TYPE_64BIT; 772 | check_ret(ret, pack_double(env, term, enc, field)); 773 | break; 774 | 775 | case field_bool: 776 | *(enc->sentinel) |= WIRE_TYPE_VARINT; 777 | check_ret(ret, pack_boolean(env, term, enc, field)); 778 | break; 779 | 780 | case field_string: 781 | *(enc->sentinel) |= WIRE_TYPE_LENGTH_PREFIXED; 782 | check_ret(ret, pack_string(env, term, enc, field)); 783 | break; 784 | 785 | case field_bytes: 786 | *(enc->sentinel) |= WIRE_TYPE_LENGTH_PREFIXED; 787 | check_ret(ret, pack_bytes(env, term, enc, field)); 788 | break; 789 | 790 | default: 791 | raise_exception(env, term); 792 | } 793 | 794 | if (sentinel == enc->p) { 795 | enc->p = enc->sentinel; 796 | } 797 | 798 | return RET_OK; 799 | } 800 | 801 | ERL_NIF_TERM 802 | encode(ErlNifEnv *env, ERL_NIF_TERM term, ep_tdata_t *tdata) 803 | { 804 | size_t i, payload_len; 805 | int32_t arity; 806 | ep_enc_t *enc; 807 | ep_spot_t *spot; 808 | ep_stack_t *stack; 809 | ep_state_t *state = (ep_state_t *) enif_priv_data(env); 810 | ep_field_t *field; 811 | ErlNifBinary bin; 812 | ERL_NIF_TERM head, tail, ret; 813 | 814 | stack = &(tdata->stack); 815 | enc = &(tdata->enc); 816 | 817 | spot = stack->spots; 818 | if (!enif_get_tuple(env, term, &arity, to_const(spot->array))) { 819 | raise_exception(env, term); 820 | } 821 | 822 | spot->node = get_node_by_name(spot->array[0], state->cache); 823 | if (spot->node == NULL || (arity - 1) != spot->node->size) { 824 | raise_exception(env, spot->array[0]); 825 | } 826 | (spot->array)++; 827 | 828 | spot->type = spot_tuple; 829 | spot->pos = 0; 830 | 831 | while (spot >= stack->spots) { 832 | 833 | if (spot->type == spot_tuple) { 834 | if (spot->pos == spot->node->size) { 835 | if (spot->need_length) { 836 | spot->need_length = FALSE; 837 | 838 | payload_len = enc->p - (enc->mem + spot->sentinel_size + MAX_UINT64_ENCODED_SIZE); 839 | enc->p = enc->mem + spot->sentinel_size; 840 | do_pack_uint32(env, (uint32_t) payload_len, enc); 841 | memmove(enc->p, enc->mem + spot->sentinel_size + MAX_UINT64_ENCODED_SIZE, payload_len); 842 | enc->p += payload_len; 843 | } 844 | spot->pos = 0; 845 | spot--; 846 | continue; 847 | } 848 | 849 | for (i = spot->pos; i < (size_t) (spot->node->size); i++) { 850 | spot->pos = i + 1; 851 | term = spot->array[i]; 852 | field = ((ep_field_t *) (spot->node->fields)) + i; 853 | 854 | if (field->o_type == occurrence_optional 855 | || field->o_type == occurrence_defaulty 856 | || field->type == field_oneof 857 | ) { 858 | if (term == state->atom_undefined) { 859 | continue; 860 | } 861 | } 862 | 863 | if (field->o_type == occurrence_repeated && field->packed == TRUE) { 864 | if (!enif_is_list(env, term)) { 865 | raise_exception(env, spot->array[i]); 866 | } 867 | 868 | if (enif_is_empty_list(env, term)) { 869 | continue; 870 | } 871 | 872 | enc->sentinel = enc->p; 873 | pack_tag(env, field->fnum, enc); 874 | *(enc->sentinel) |= WIRE_TYPE_LENGTH_PREFIXED; 875 | 876 | enc->sentinel = enc->p; 877 | enc_ensure_default(env, enc); 878 | enc->p += MAX_UINT64_ENCODED_SIZE; 879 | 880 | while (enif_get_list_cell(env, term, &head, &tail)) { 881 | check_ret(ret, pack_element_packed(env, head, enc, field)); 882 | term = tail; 883 | } 884 | 885 | payload_len = enc->p - enc->sentinel - MAX_UINT64_ENCODED_SIZE; 886 | enc->p = enc->sentinel; 887 | do_pack_uint32(env, (uint32_t) payload_len, enc); 888 | memmove(enc->p, enc->sentinel + MAX_UINT64_ENCODED_SIZE, payload_len); 889 | enc->p += payload_len; 890 | 891 | } else if (field->o_type == occurrence_repeated) { 892 | 893 | if (!enif_is_list(env, term)) { 894 | raise_exception(env, term); 895 | } 896 | 897 | if (enif_is_empty_list(env, term)) { 898 | continue; 899 | } 900 | 901 | if (field->type == field_msg || field->type == field_map) { 902 | 903 | spot++; 904 | spot->node = field->sub_node; 905 | if (spot->node == NULL) { 906 | raise_exception(env, term); 907 | } 908 | 909 | spot->field = field; 910 | spot->type = spot_list; 911 | spot->list = term; 912 | break; 913 | 914 | } else { 915 | while (enif_get_list_cell(env, term, &head, &tail)) { 916 | check_ret(ret, pack_field(env, head, enc, field)); 917 | term = tail; 918 | } 919 | } 920 | 921 | } else { 922 | if (field->type == field_oneof) { 923 | field = get_oneof_field(env, spot->array[i], field->sub_node, &term); 924 | if (field == NULL) { 925 | raise_exception(env, spot->array[i]); 926 | } 927 | } 928 | 929 | if (field->type == field_msg || field->type == field_map) { 930 | enc->sentinel = enc->p; 931 | pack_tag(env, field->fnum, enc); 932 | *(enc->sentinel) |= WIRE_TYPE_LENGTH_PREFIXED; 933 | 934 | spot++; 935 | 936 | if (!enif_get_tuple(env, term, &arity, to_const(spot->array))) { 937 | raise_exception(env, term); 938 | } 939 | 940 | (spot->array)++; 941 | arity--; 942 | 943 | spot->node = field->sub_node; 944 | if (spot->node == NULL || arity != spot->node->size) { 945 | raise_exception(env, term); 946 | } 947 | 948 | spot->type = spot_tuple; 949 | spot->pos = 0; 950 | 951 | spot->need_length = TRUE; 952 | spot->sentinel_size = enc->p - enc->mem; 953 | enc_ensure_default(env, enc); 954 | enc->p += MAX_UINT64_ENCODED_SIZE; 955 | 956 | break; 957 | } else { 958 | check_ret(ret, pack_field(env, term, enc, field)); 959 | } 960 | } 961 | } 962 | 963 | } else if (spot->type == spot_list) { 964 | 965 | if (!enif_get_list_cell(env, spot->list, &head, &tail)) { 966 | spot--; 967 | continue; 968 | } 969 | 970 | spot->list = tail; 971 | 972 | enc->sentinel = enc->p; 973 | pack_tag(env, spot->field->fnum, enc); 974 | *(enc->sentinel) |= WIRE_TYPE_LENGTH_PREFIXED; 975 | 976 | spot++; 977 | spot->node = (spot - 1)->node; 978 | spot->type = spot_tuple; 979 | spot->pos = 0; 980 | 981 | spot->need_length = TRUE; 982 | spot->sentinel_size = enc->p - enc->mem; 983 | enc_ensure_default(env, enc); 984 | enc->p += MAX_UINT64_ENCODED_SIZE; 985 | 986 | if (!enif_get_tuple(env, head, &arity, to_const(spot->array))) { 987 | raise_exception(env, head); 988 | } 989 | 990 | if (spot->node->n_type != node_map) { 991 | (spot->array)++; 992 | } 993 | } 994 | } 995 | 996 | if (enc->p > enc->mem) { 997 | 998 | if (!enif_alloc_binary(enc->p - enc->mem, &bin)) { 999 | raise_exception(env, make_atom(env, "alloc_binary")); 1000 | } 1001 | 1002 | memcpy(bin.data, enc->mem, bin.size); 1003 | 1004 | } else { 1005 | if (!enif_alloc_binary(0, &bin)) { 1006 | raise_exception(env, make_atom(env, "alloc_binary")); 1007 | } 1008 | } 1009 | enc->result = enif_make_binary(env, &bin); 1010 | 1011 | return RET_OK; 1012 | } 1013 | -------------------------------------------------------------------------------- /test/ep_proper_decode_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ep_proper_decode_tests). 2 | 3 | -ifdef(PROPER). 4 | -include_lib("proper/include/proper.hrl"). 5 | -include_lib("eunit/include/eunit.hrl"). 6 | -include_lib("gpb/include/gpb.hrl"). 7 | 8 | -compile(export_all). 9 | 10 | -record(m1, {a}). 11 | -record(m2, {a, b}). 12 | -record(m3, {a, b, c}). 13 | -record(m4, {a, b, c, d}). 14 | 15 | ep_proper_test() -> 16 | Functions = [F || {F, 0} <- ?MODULE:module_info(exports), F > 'prop_', F < 'prop`'], 17 | lists:foreach(fun(F) -> 18 | ?debugFmt("-> ~p", [F]), 19 | ?assert(proper:quickcheck(?MODULE:F(), [long_result, {to_file, user}])) 20 | end, Functions). 21 | 22 | utf8char() -> 23 | union([ 24 | integer(0, 36095), 25 | integer(57344, 65533), 26 | integer(65536, 1114111) 27 | ]). 28 | 29 | utf8string() -> list(utf8char()). 30 | 31 | ascii_string() -> list(choose(0, 127)). 32 | 33 | uint32() -> choose(0, 4294967295). 34 | 35 | sint32() -> choose(-2147483648, 2147483647). 36 | 37 | uint64() -> choose(0, 18446744073709551615). 38 | 39 | sint64() -> 40 | choose(-9223372036854775808, 9223372036854775807). 41 | 42 | value() -> 43 | oneof([{real(), double}, {real(), float}, {nan, float}, 44 | {infinity, float}, {'-infinity', float}, {nan, double}, 45 | {infinity, double}, {'-infinity', double}, 46 | {uint32(), uint32}, {uint64(), uint64}, 47 | {sint32(), sint32}, {sint64(), sint64}, 48 | {uint32(), fixed32}, {uint64(), fixed64}, 49 | {sint32(), sfixed32}, {sint64(), sfixed64}, 50 | {sint32(), int32}, {sint64(), int64}, {bool(), bool}, 51 | {sint32(), enum}, {utf8string(), string}, 52 | {binary(), bytes}]). 53 | 54 | prop_decode_int32() -> 55 | Defs = [ 56 | {{msg, m4}, [ 57 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = int32, occurrence = required, opts = []}, 58 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = int32, occurrence = optional, opts = []}, 59 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = int32, occurrence = repeated, opts = []}, 60 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = int32, occurrence = repeated, opts = [packed]} 61 | ]} 62 | ], 63 | Defs2 = [ 64 | {{msg, m4}, [ 65 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = int32, occurrence = required, opts = []}, 66 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = int32, occurrence = optional, opts = []}, 67 | #?gpb_field{name = d, fnum = 3, rnum = #m4.d, type = int32, occurrence = repeated, opts = [packed]}, 68 | #?gpb_field{name = c, fnum = 4, rnum = #m4.c, type = int32, occurrence = repeated, opts = []} 69 | ]} 70 | ], 71 | ?FORALL(Message, 72 | #m4{ 73 | a = sint32(), 74 | b = oneof([sint32(), undefined]), 75 | c = list(sint32()), 76 | d = list(sint32()) 77 | }, 78 | begin 79 | ok = enif_protobuf:load_cache(Defs), 80 | Bin = gpb:encode_msg(Message, Defs), 81 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 82 | ok = enif_protobuf:load_cache(Defs2), 83 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 84 | end). 85 | 86 | prop_decode_int64() -> 87 | Defs = [ 88 | {{msg, m4}, [ 89 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = int64, occurrence = required, opts = []}, 90 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = int64, occurrence = optional, opts = []}, 91 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = int64, occurrence = repeated, opts = []}, 92 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = int64, occurrence = repeated, opts = [packed]} 93 | ]} 94 | ], 95 | Defs2 = [ 96 | {{msg, m4}, [ 97 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = int64, occurrence = required, opts = []}, 98 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = int64, occurrence = optional, opts = []}, 99 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = int64, occurrence = repeated, opts = [packed]}, 100 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = int64, occurrence = repeated, opts = []} 101 | ]} 102 | ], 103 | ?FORALL(Message, 104 | #m4{ 105 | a = sint64(), 106 | b = oneof([sint64(), undefined]), 107 | c = list(sint64()), 108 | d = list(sint64()) 109 | }, 110 | begin 111 | ok = enif_protobuf:load_cache(Defs), 112 | Bin = gpb:encode_msg(Message, Defs), 113 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 114 | ok = enif_protobuf:load_cache(Defs2), 115 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 116 | end). 117 | 118 | prop_decode_uint32() -> 119 | Defs = [ 120 | {{msg, m4}, [ 121 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = uint32, occurrence = required, opts = []}, 122 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = uint32, occurrence = optional, opts = []}, 123 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = uint32, occurrence = repeated, opts = []}, 124 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = uint32, occurrence = repeated, opts = [packed]} 125 | ]} 126 | ], 127 | Defs2 = [ 128 | {{msg, m4}, [ 129 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = uint32, occurrence = required, opts = []}, 130 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = uint32, occurrence = optional, opts = []}, 131 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = uint32, occurrence = repeated, opts = [packed]}, 132 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = uint32, occurrence = repeated, opts = []} 133 | ]} 134 | ], 135 | ?FORALL(Message, 136 | #m4{ 137 | a = uint32(), 138 | b = oneof([uint32(), undefined]), 139 | c = list(uint32()), 140 | d = list(uint32()) 141 | }, 142 | begin 143 | ok = enif_protobuf:load_cache(Defs), 144 | Bin = gpb:encode_msg(Message, Defs), 145 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 146 | ok = enif_protobuf:load_cache(Defs2), 147 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 148 | end). 149 | 150 | prop_decode_uint64() -> 151 | Defs = [ 152 | {{msg, m4}, [ 153 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = uint64, occurrence = required, opts = []}, 154 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = uint64, occurrence = optional, opts = []}, 155 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = uint64, occurrence = repeated, opts = []}, 156 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = uint64, occurrence = repeated, opts = [packed]} 157 | ]} 158 | ], 159 | Defs2 = [ 160 | {{msg, m4}, [ 161 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = uint64, occurrence = required, opts = []}, 162 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = uint64, occurrence = optional, opts = []}, 163 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = uint64, occurrence = repeated, opts = [packed]}, 164 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = uint64, occurrence = repeated, opts = []} 165 | ]} 166 | ], 167 | ?FORALL(Message, 168 | #m4{ 169 | a = uint64(), 170 | b = oneof([uint64(), undefined]), 171 | c = list(uint64()), 172 | d = list(uint64()) 173 | }, 174 | begin 175 | ok = enif_protobuf:load_cache(Defs), 176 | Bin = gpb:encode_msg(Message, Defs), 177 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 178 | ok = enif_protobuf:load_cache(Defs2), 179 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 180 | end). 181 | 182 | prop_decode_sint32() -> 183 | Defs = [ 184 | {{msg, m4}, [ 185 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sint32, occurrence = required, opts = []}, 186 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sint32, occurrence = optional, opts = []}, 187 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sint32, occurrence = repeated, opts = []}, 188 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sint32, occurrence = repeated, opts = [packed]} 189 | ]} 190 | ], 191 | Defs2 = [ 192 | {{msg, m4}, [ 193 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sint32, occurrence = required, opts = []}, 194 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sint32, occurrence = optional, opts = []}, 195 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sint32, occurrence = repeated, opts = [packed]}, 196 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sint32, occurrence = repeated, opts = []} 197 | ]} 198 | ], 199 | ?FORALL(Message, 200 | #m4{ 201 | a = sint32(), 202 | b = oneof([sint32(), undefined]), 203 | c = list(sint32()), 204 | d = list(sint32()) 205 | }, 206 | begin 207 | ok = enif_protobuf:load_cache(Defs), 208 | Bin = gpb:encode_msg(Message, Defs), 209 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 210 | ok = enif_protobuf:load_cache(Defs2), 211 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 212 | end). 213 | 214 | prop_decode_sint64() -> 215 | Defs = [ 216 | {{msg, m4}, [ 217 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sint64, occurrence = required, opts = []}, 218 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sint64, occurrence = optional, opts = []}, 219 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sint64, occurrence = repeated, opts = []}, 220 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sint64, occurrence = repeated, opts = [packed]} 221 | ]} 222 | ], 223 | Defs2 = [ 224 | {{msg, m4}, [ 225 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sint64, occurrence = required, opts = []}, 226 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sint64, occurrence = optional, opts = []}, 227 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sint64, occurrence = repeated, opts = [packed]}, 228 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sint64, occurrence = repeated, opts = []} 229 | ]} 230 | ], 231 | ?FORALL(Message, 232 | #m4{ 233 | a = sint64(), 234 | b = oneof([sint64(), undefined]), 235 | c = list(sint64()), 236 | d = list(sint64()) 237 | }, 238 | begin 239 | ok = enif_protobuf:load_cache(Defs), 240 | Bin = gpb:encode_msg(Message, Defs), 241 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 242 | ok = enif_protobuf:load_cache(Defs2), 243 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 244 | end). 245 | 246 | prop_decode_fixed32() -> 247 | Defs = [ 248 | {{msg, m4}, [ 249 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = fixed32, occurrence = required, opts = []}, 250 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = fixed32, occurrence = optional, opts = []}, 251 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = fixed32, occurrence = repeated, opts = []}, 252 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = fixed32, occurrence = repeated, opts = [packed]} 253 | ]} 254 | ], 255 | Defs2 = [ 256 | {{msg, m4}, [ 257 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = fixed32, occurrence = required, opts = []}, 258 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = fixed32, occurrence = optional, opts = []}, 259 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = fixed32, occurrence = repeated, opts = [packed]}, 260 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = fixed32, occurrence = repeated, opts = []} 261 | ]} 262 | ], 263 | ?FORALL(Message, 264 | #m4{ 265 | a = uint32(), 266 | b = oneof([uint32(), undefined]), 267 | c = list(uint32()), 268 | d = list(uint32()) 269 | }, 270 | begin 271 | ok = enif_protobuf:load_cache(Defs), 272 | Bin = gpb:encode_msg(Message, Defs), 273 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 274 | ok = enif_protobuf:load_cache(Defs2), 275 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 276 | end). 277 | 278 | prop_decode_fixed64() -> 279 | Defs = [ 280 | {{msg, m4}, [ 281 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = fixed64, occurrence = required, opts = []}, 282 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = fixed64, occurrence = optional, opts = []}, 283 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = fixed64, occurrence = repeated, opts = []}, 284 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = fixed64, occurrence = repeated, opts = [packed]} 285 | ]} 286 | ], 287 | Defs2 = [ 288 | {{msg, m4}, [ 289 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = fixed64, occurrence = required, opts = []}, 290 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = fixed64, occurrence = optional, opts = []}, 291 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = fixed64, occurrence = repeated, opts = [packed]}, 292 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = fixed64, occurrence = repeated, opts = []} 293 | ]} 294 | ], 295 | ?FORALL(Message, 296 | #m4{ 297 | a = uint64(), 298 | b = oneof([uint64(), undefined]), 299 | c = list(uint64()), 300 | d = list(uint64()) 301 | }, 302 | begin 303 | ok = enif_protobuf:load_cache(Defs), 304 | Bin = gpb:encode_msg(Message, Defs), 305 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 306 | ok = enif_protobuf:load_cache(Defs2), 307 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 308 | end). 309 | 310 | prop_decode_sfixed32() -> 311 | Defs = [ 312 | {{msg, m4}, [ 313 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sfixed32, occurrence = required, opts = []}, 314 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sfixed32, occurrence = optional, opts = []}, 315 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sfixed32, occurrence = repeated, opts = []}, 316 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sfixed32, occurrence = repeated, opts = [packed]} 317 | ]} 318 | ], 319 | Defs2 = [ 320 | {{msg, m4}, [ 321 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sfixed32, occurrence = required, opts = []}, 322 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sfixed32, occurrence = optional, opts = []}, 323 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sfixed32, occurrence = repeated, opts = [packed]}, 324 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sfixed32, occurrence = repeated, opts = []} 325 | ]} 326 | ], 327 | ?FORALL(Message, 328 | #m4{ 329 | a = sint32(), 330 | b = oneof([sint32(), undefined]), 331 | c = list(sint32()), 332 | d = list(sint32()) 333 | }, 334 | begin 335 | ok = enif_protobuf:load_cache(Defs), 336 | Bin = gpb:encode_msg(Message, Defs), 337 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 338 | ok = enif_protobuf:load_cache(Defs2), 339 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 340 | end). 341 | 342 | prop_decode_sfixed64() -> 343 | Defs = [ 344 | {{msg, m4}, [ 345 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sfixed64, occurrence = required, opts = []}, 346 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sfixed64, occurrence = optional, opts = []}, 347 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sfixed64, occurrence = repeated, opts = []}, 348 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sfixed64, occurrence = repeated, opts = [packed]} 349 | ]} 350 | ], 351 | Defs2 = [ 352 | {{msg, m4}, [ 353 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = sfixed64, occurrence = required, opts = []}, 354 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = sfixed64, occurrence = optional, opts = []}, 355 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = sfixed64, occurrence = repeated, opts = [packed]}, 356 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = sfixed64, occurrence = repeated, opts = []} 357 | ]} 358 | ], 359 | ?FORALL(Message, 360 | #m4{ 361 | a = sint64(), 362 | b = oneof([sint64(), undefined]), 363 | c = list(sint64()), 364 | d = list(sint64()) 365 | }, 366 | begin 367 | ok = enif_protobuf:load_cache(Defs), 368 | Bin = gpb:encode_msg(Message, Defs), 369 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 370 | ok = enif_protobuf:load_cache(Defs2), 371 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 372 | end). 373 | 374 | prop_decode_float() -> 375 | Defs = [ 376 | {{msg, m4}, [ 377 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = float, occurrence = required, opts = []}, 378 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = float, occurrence = optional, opts = []}, 379 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = float, occurrence = repeated, opts = []}, 380 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = float, occurrence = repeated, opts = [packed]} 381 | ]} 382 | ], 383 | Defs2 = [ 384 | {{msg, m4}, [ 385 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = float, occurrence = required, opts = []}, 386 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = float, occurrence = optional, opts = []}, 387 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = float, occurrence = repeated, opts = [packed]}, 388 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = float, occurrence = repeated, opts = []} 389 | ]} 390 | ], 391 | ?FORALL(Message, 392 | #m4{ 393 | a = oneof([infinity, '-infinity', nan, float(), integer()]), 394 | b = oneof([infinity, '-infinity', nan, float(), integer(), undefined]), 395 | c = list(oneof([infinity, '-infinity', nan, float(), integer()])), 396 | d = list(oneof([infinity, '-infinity', nan, float(), integer()])) 397 | }, 398 | begin 399 | ok = enif_protobuf:load_cache(Defs), 400 | Bin = gpb:encode_msg(Message, Defs), 401 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 402 | ok = enif_protobuf:load_cache(Defs2), 403 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 404 | end). 405 | 406 | prop_decode_double() -> 407 | Defs = [ 408 | {{msg, m4}, [ 409 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = double, occurrence = required, opts = []}, 410 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = double, occurrence = optional, opts = []}, 411 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = double, occurrence = repeated, opts = []}, 412 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = double, occurrence = repeated, opts = [packed]} 413 | ]} 414 | ], 415 | Defs2 = [ 416 | {{msg, m4}, [ 417 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = double, occurrence = required, opts = []}, 418 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = double, occurrence = optional, opts = []}, 419 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = double, occurrence = repeated, opts = [packed]}, 420 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = double, occurrence = repeated, opts = []} 421 | ]} 422 | ], 423 | ?FORALL(Message, 424 | #m4{ 425 | a = oneof([infinity, '-infinity', nan, float(), integer()]), 426 | b = oneof([infinity, '-infinity', nan, float(), integer(), undefined]), 427 | c = list(oneof([infinity, '-infinity', nan, float(), integer()])), 428 | d = list(oneof([infinity, '-infinity', nan, float(), integer()])) 429 | }, 430 | begin 431 | ok = enif_protobuf:load_cache(Defs), 432 | Bin = gpb:encode_msg(Message, Defs), 433 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 434 | ok = enif_protobuf:load_cache(Defs2), 435 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 436 | end). 437 | 438 | prop_decode_bool() -> 439 | Defs = [ 440 | {{msg, m4}, [ 441 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = bool, occurrence = required, opts = []}, 442 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = bool, occurrence = optional, opts = []}, 443 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = bool, occurrence = repeated, opts = []}, 444 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = bool, occurrence = repeated, opts = [packed]} 445 | ]} 446 | ], 447 | Defs2 = [ 448 | {{msg, m4}, [ 449 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = bool, occurrence = required, opts = []}, 450 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = bool, occurrence = optional, opts = []}, 451 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = bool, occurrence = repeated, opts = [packed]}, 452 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = bool, occurrence = repeated, opts = []} 453 | ]} 454 | ], 455 | ok = enif_protobuf:load_cache(Defs), 456 | ?FORALL(Message, 457 | #m4{ 458 | a = oneof([true, false, 1, 0]), 459 | b = oneof([true, false, 1, 0, undefined]), 460 | c = list(oneof([true, false, 1, 0])), 461 | d = list(oneof([true, false, 1, 0])) 462 | }, 463 | begin 464 | Bin = gpb:encode_msg(Message, Defs), 465 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 466 | ok = enif_protobuf:load_cache(Defs2), 467 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 468 | end). 469 | 470 | prop_decode_ascii_string() -> 471 | Defs = [ 472 | {{msg, m3}, [ 473 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = string, occurrence = required, opts = []}, 474 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = string, occurrence = optional, opts = []}, 475 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = string, occurrence = repeated, opts = []} 476 | ]} 477 | ], 478 | ok = enif_protobuf:load_cache(Defs), 479 | ok = enif_protobuf:set_opts([{string_as_list, true}]), 480 | ?FORALL(Message, 481 | #m3{ 482 | a = ascii_string(), 483 | b = oneof([ascii_string(), undefined]), 484 | c = list(ascii_string()) 485 | }, 486 | begin 487 | Bin = gpb:encode_msg(Message, Defs), 488 | enif_protobuf:decode(Bin, m3) =:= gpb:decode_msg(Bin, m3, Defs) 489 | end). 490 | 491 | prop_decode_utf8_string() -> 492 | Defs = [ 493 | {{msg, m3}, [ 494 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = string, occurrence = required, opts = []}, 495 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = string, occurrence = optional, opts = []}, 496 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = string, occurrence = repeated, opts = []} 497 | ]} 498 | ], 499 | ok = enif_protobuf:load_cache(Defs), 500 | ok = enif_protobuf:set_opts([{with_utf8, true}, {string_as_list, true}]), 501 | ?FORALL(Message, 502 | #m3{ 503 | a = utf8string(), 504 | b = oneof([utf8string(), undefined]), 505 | c = list(utf8string()) 506 | }, 507 | begin 508 | Bin = gpb:encode_msg(Message, Defs), 509 | enif_protobuf:decode(Bin, m3) =:= gpb:decode_msg(Bin, m3, Defs) 510 | end). 511 | 512 | prop_encode_bytes() -> 513 | Defs = [ 514 | {{msg, m3}, [ 515 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = bytes, occurrence = required, opts = []}, 516 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = bytes, occurrence = optional, opts = []}, 517 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = bytes, occurrence = repeated, opts = []} 518 | ]} 519 | ], 520 | ok = enif_protobuf:load_cache(Defs), 521 | ?FORALL(Message, 522 | #m3{ 523 | a = binary(), 524 | b = oneof([binary(), undefined]), 525 | c = list(binary()) 526 | }, 527 | begin 528 | Bin = gpb:encode_msg(Message, Defs), 529 | enif_protobuf:decode(Bin, m3) =:= gpb:decode_msg(Bin, m3, Defs) 530 | end). 531 | 532 | prop_decode_map() -> 533 | Type = {map, string, fixed32}, 534 | Defs = [ 535 | {{msg, m3}, [ 536 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = Type, occurrence = required, opts = []}, 537 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = Type, occurrence = optional, opts = []}, 538 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = Type, occurrence = repeated, opts = []} 539 | ]} 540 | ], 541 | ok = enif_protobuf:load_cache(Defs), 542 | ok = enif_protobuf:set_opts([{string_as_list, true}, {with_utf8, false}]), 543 | ?FORALL(Message, 544 | #m3{ 545 | a = {ascii_string(), integer()}, 546 | b = oneof([{ascii_string(), integer()}, undefined]), 547 | c = list({ascii_string(), integer()}) 548 | }, 549 | begin 550 | Bin = gpb:encode_msg(Message, Defs), 551 | Decoded = enif_protobuf:decode(Bin, m3), 552 | Decoded#m3{c = lists:reverse(Decoded#m3.c)} =:= gpb:decode_msg(Bin, m3, Defs) 553 | end). 554 | 555 | prop_decode_enum() -> 556 | Defs = [ 557 | {{msg, m4}, [ 558 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = {enum, e}, occurrence = required, opts = []}, 559 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = {enum, e}, occurrence = optional, opts = []}, 560 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = {enum, e}, occurrence = repeated, opts = []}, 561 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = {enum, e}, occurrence = repeated, opts = [packed]} 562 | ]}, 563 | {{enum, e}, [{v1, 100}, {v2, 150}]} 564 | ], 565 | Defs2 = [ 566 | {{msg, m4}, [ 567 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = {enum, e}, occurrence = required, opts = []}, 568 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = {enum, e}, occurrence = optional, opts = []}, 569 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = {enum, e}, occurrence = repeated, opts = [packed]}, 570 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = {enum, e}, occurrence = repeated, opts = []} 571 | ]}, 572 | {{enum, e}, [{v1, 100}, {v2, 150}]} 573 | ], 574 | ?FORALL(Message, 575 | #m4{ 576 | a = oneof([v1, v2, sint32()]), 577 | b = oneof([v1, v2, sint32(), undefined]), 578 | c = list(oneof([v1, v2, sint32()])), 579 | d = list(oneof([v1, v2, sint32()])) 580 | }, 581 | begin 582 | ok = enif_protobuf:load_cache(Defs), 583 | Bin = gpb:encode_msg(Message, Defs), 584 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 585 | ok = enif_protobuf:load_cache(Defs2), 586 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 587 | end). 588 | 589 | prop_decode_enum_aliases() -> 590 | Defs = [ 591 | {{msg, m4}, [ 592 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = {enum, e}, occurrence = required, opts = []}, 593 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = {enum, e}, occurrence = optional, opts = []}, 594 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = {enum, e}, occurrence = repeated, opts = []}, 595 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = {enum, e}, occurrence = repeated, opts = [packed]} 596 | ]}, 597 | {{enum, e}, [{option, allow_alias, true}, {v1, 100}, {v2, 150}, {v3, 100}]} 598 | ], 599 | Defs2 = [ 600 | {{msg, m4}, [ 601 | #?gpb_field{name = a, fnum = 1, rnum = #m4.a, type = {enum, e}, occurrence = required, opts = []}, 602 | #?gpb_field{name = b, fnum = 2, rnum = #m4.b, type = {enum, e}, occurrence = optional, opts = []}, 603 | #?gpb_field{name = c, fnum = 3, rnum = #m4.c, type = {enum, e}, occurrence = repeated, opts = [packed]}, 604 | #?gpb_field{name = d, fnum = 4, rnum = #m4.d, type = {enum, e}, occurrence = repeated, opts = []} 605 | ]}, 606 | {{enum, e}, [{option, allow_alias, true}, {v1, 100}, {v2, 150}, {v3, 100}]} 607 | ], 608 | ?FORALL(Message, 609 | #m4{ 610 | a = oneof([v1, v2, v3, sint32()]), 611 | b = oneof([v1, v2, v3, sint32(), undefined]), 612 | c = list(oneof([v1, v2, v3, sint32()])), 613 | d = list(oneof([v1, v2, v3, sint32()])) 614 | }, 615 | begin 616 | ok = enif_protobuf:load_cache(Defs), 617 | Bin = gpb:encode_msg(Message, Defs), 618 | Ret = enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs), 619 | ok = enif_protobuf:load_cache(Defs2), 620 | Ret and (enif_protobuf:decode(Bin, m4) =:= gpb:decode_msg(Bin, m4, Defs2)) 621 | end). 622 | 623 | prop_decode_oneof() -> 624 | Defs = [ 625 | {{msg, m1}, [ 626 | #gpb_oneof{name = a, rnum = #m1.a, fields = [ 627 | #?gpb_field{name = a1, fnum = 1, rnum = #m1.a, type = int32, occurrence = optional, opts = []}, 628 | #?gpb_field{name = a2, fnum = 2, rnum = #m1.a, type = int32, occurrence = optional, opts = []} 629 | ]}]} 630 | ], 631 | ok = enif_protobuf:load_cache(Defs), 632 | ?FORALL(Message, 633 | #m1{a = oneof([undefined, {a1, integer()}, {a2, integer()}])}, 634 | begin 635 | Bin = gpb:encode_msg(Message, Defs), 636 | enif_protobuf:decode(Bin, m1) =:= gpb:decode_msg(Bin, m1, Defs) 637 | end). 638 | 639 | prop_decode_sub_msg() -> 640 | Defs = [ 641 | {{msg, m3}, [ 642 | #?gpb_field{name = a, fnum = 1, rnum = #m3.a, type = {msg, m2}, occurrence = required, opts = []}, 643 | #?gpb_field{name = b, fnum = 2, rnum = #m3.b, type = {msg, m2}, occurrence = optional, opts = []}, 644 | #?gpb_field{name = c, fnum = 3, rnum = #m3.c, type = {msg, m2}, occurrence = repeated, opts = []} 645 | ]}, 646 | {{msg, m2}, [ 647 | #?gpb_field{name = a, fnum = 1, rnum = #m2.a, type = uint32, occurrence = required, opts = []}, 648 | #?gpb_field{name = b, fnum = 2, rnum = #m2.b, type = uint64, occurrence = required, opts = []} 649 | ]} 650 | ], 651 | ok = enif_protobuf:load_cache(Defs), 652 | ?FORALL(Message, 653 | #m3{ 654 | a = #m2{a = uint32(), b = uint64()}, 655 | b = oneof([#m2{a = uint32(), b = uint64()}, undefined]), 656 | c = list(#m2{a = uint32(), b = uint64()}) 657 | }, 658 | begin 659 | Bin = gpb:encode_msg(Message, Defs), 660 | enif_protobuf:decode(Bin, m3) =:= gpb:decode_msg(Bin, m3, Defs) 661 | end). 662 | -endif. 663 | --------------------------------------------------------------------------------