WIP refactor gmser_dyn
This commit is contained in:
+196
-64
@@ -24,10 +24,12 @@
|
||||
, deserialize/3 ]). %% (Bin, Vsn, Types) -> Term
|
||||
|
||||
%% register a type schema, inspect existing schema
|
||||
-export([ register_types/1
|
||||
, registered_types/0
|
||||
, registered_types/1
|
||||
, latest_vsn/0
|
||||
-export([ register_types/1 %% updates stored types
|
||||
, registered_types/0 %% -"-
|
||||
, registered_types/1 %% -"-
|
||||
, add_types/1 %% Returns updated types
|
||||
, add_types/2 %% -"-
|
||||
, latest_vsn/0 %% -"-
|
||||
, get_opts/1
|
||||
, set_opts/1
|
||||
, set_opts/2
|
||||
@@ -162,11 +164,36 @@ decode_typed(Type, Fields0, Vsn, Types0) ->
|
||||
Types = proper_types(Types0, Vsn),
|
||||
case decode_tag_and_vsn(Fields0) of
|
||||
{0, Vsn, Fields} ->
|
||||
decode_from_template(Type, Fields, Vsn, Types);
|
||||
decode_typed_(Type, Fields, Vsn, Types);
|
||||
Other ->
|
||||
error({illegal_encoding, Other})
|
||||
end.
|
||||
|
||||
decode_typed_(Type, [CodeBin, Fields], Vsn, Types) when is_map(Type) ->
|
||||
Code = decode_basic(int, CodeBin),
|
||||
{Tag, Template} = template(Code, Vsn, Types),
|
||||
Dyn = is_dyn_template(Tag),
|
||||
case {Type, Template} of
|
||||
{#{items := _}, map} -> decode_from_template(Type, Code, Fields, Dyn, Vsn, Types);
|
||||
{#{items := _}, items} -> decode_from_template(Type, Code, Fields, Dyn, Vsn, Types);
|
||||
{#{alt := _}, _} -> decode_from_template(Type, Code, Fields, Dyn, Vsn, Types);
|
||||
{#{switch:= _}, map} -> decode_from_template(Type, Code, Fields, Dyn, Vsn, Types);
|
||||
_ ->
|
||||
error(badarg)
|
||||
end;
|
||||
decode_typed_(Type, [CodeBin, Fields], Vsn, Types) ->
|
||||
Code = decode_basic(int, CodeBin),
|
||||
{_, Template} = template(Type, Vsn, Types),
|
||||
case template(Code, Vsn, Types) of
|
||||
{Tag, Template} ->
|
||||
Dyn = is_dyn_template(Tag),
|
||||
decode_from_template(Template, Code, Fields, Dyn, Vsn, Types);
|
||||
_ ->
|
||||
error(badarg)
|
||||
end;
|
||||
decode_typed_(_, _, _, _) ->
|
||||
error(illegal_encoding).
|
||||
|
||||
decode_tag_and_vsn([TagBin, VsnBin, Fields]) ->
|
||||
{decode_basic(int, TagBin),
|
||||
decode_basic(int, VsnBin),
|
||||
@@ -189,6 +216,17 @@ assert_vsn(V, #{vsn := V} = Types) -> Types;
|
||||
assert_vsn(V, #{vsn := Other} ) -> error({version_mismatch, V, Other});
|
||||
assert_vsn(V, #{} = Types ) -> Types#{vsn => V}.
|
||||
|
||||
-define(ANYINT, 246).
|
||||
-define(NEGINT, 247).
|
||||
-define(INT, 248).
|
||||
-define(BINARY, 249).
|
||||
-define(BOOL, 250).
|
||||
-define(LIST, 251).
|
||||
-define(MAP, 252).
|
||||
-define(TUPLE, 253).
|
||||
-define(ID, 254).
|
||||
-define(LABEL, 255).
|
||||
|
||||
dynamic_types() ->
|
||||
#{ vsn => ?VSN
|
||||
, codes =>
|
||||
@@ -203,16 +241,16 @@ dynamic_types() ->
|
||||
, 254 => id
|
||||
, 255 => label}
|
||||
, rev =>
|
||||
#{ anyint => 246
|
||||
, negint => 247
|
||||
, int => 248
|
||||
, binary => 249
|
||||
, bool => 250
|
||||
, list => 251
|
||||
, map => 252
|
||||
, tuple => 253
|
||||
, id => 254
|
||||
, label => 255}
|
||||
#{ anyint => ?ANYINT
|
||||
, negint => ?NEGINT
|
||||
, int => ?INT
|
||||
, binary => ?BINARY
|
||||
, bool => ?BOOL
|
||||
, list => ?LIST
|
||||
, map => ?MAP
|
||||
, tuple => ?TUPLE
|
||||
, id => ?ID
|
||||
, label => ?LABEL }
|
||||
, labels => #{}
|
||||
, rev_labels => #{}
|
||||
, templates =>
|
||||
@@ -230,6 +268,12 @@ dynamic_types() ->
|
||||
, options => #{}
|
||||
}.
|
||||
|
||||
is_dyn_template(T) ->
|
||||
is_dyn_template_(T, dynamic_types()).
|
||||
|
||||
is_dyn_template_(T, #{templates := Ts}) ->
|
||||
is_map_key(T, Ts).
|
||||
|
||||
registered_types() ->
|
||||
registered_types(latest_vsn()).
|
||||
|
||||
@@ -264,8 +308,9 @@ find_cached_label(Lbl, #{labels := Lbls}) ->
|
||||
|
||||
decode_([CodeBin, Flds], Vsn, Types) ->
|
||||
Code = decode_basic(int, CodeBin),
|
||||
{_Tag, Template} = template(Code, Vsn, Types),
|
||||
decode_from_template(Template, Flds, Vsn, Types).
|
||||
{Tag, Template} = template(Code, Vsn, Types),
|
||||
Dyn = is_dyn_template(Tag),
|
||||
decode_from_template(Template, Code, Flds, Dyn, Vsn, Types).
|
||||
|
||||
encode_(Term, Vsn, Types) ->
|
||||
encode_(Term, dyn(emit()), Vsn, Types).
|
||||
@@ -351,53 +396,80 @@ auto_template(T) ->
|
||||
T < 0 -> {negint, negint};
|
||||
true ->
|
||||
error({invalid_type, T})
|
||||
end.
|
||||
end.
|
||||
|
||||
decode_from_template(any, Fld, Vsn, Types) ->
|
||||
decode_from_template(any, _, Fld, _, Vsn, Types) ->
|
||||
decode_(Fld, Vsn, Types);
|
||||
decode_from_template(#{items := Items}, Fld, Vsn, Types) when is_list(Fld) ->
|
||||
Zipped = lists:zipwith(
|
||||
fun({{K, T}, V}) -> {K, T, V};
|
||||
({{opt,K,T}, V}) -> {K, T, V}
|
||||
end, Items, Fld),
|
||||
decode_from_template(#{items := Items}, _, Fld, _Dyn, Vsn, Types) when is_list(Fld) ->
|
||||
Zipped = dec_zip_items(Items, Fld, Vsn, Types),
|
||||
lists:foldl(
|
||||
fun({K, Type, V}, Map) ->
|
||||
maps:is_key(K, Map) andalso error(badarg, duplicate_field),
|
||||
Map#{K => decode_from_template({any,Type}, V, Vsn, Types)}
|
||||
fun({K, Type, Code, V}, Map) ->
|
||||
maps:is_key(K, Map) andalso error(badarg, duplicate_key),
|
||||
Map#{K => decode_from_template(Type, Code, V, true, Vsn, Types)}
|
||||
end, #{}, Zipped);
|
||||
decode_from_template(#{alt := Alts} = T, Fld, Vsn, Types) when is_list(Alts) ->
|
||||
decode_alt(Alts, Fld, T, Vsn, Types);
|
||||
decode_from_template(#{switch := Alts} = T, Fld, Vsn, Types) when is_map(Alts) ->
|
||||
decode_switch(Alts, Fld, T, Vsn, Types);
|
||||
decode_from_template(list, Flds, Vsn, Types) ->
|
||||
decode_from_template(#{alt := Alts} = T, Code, Fld, _, Vsn, Types) when is_list(Alts) ->
|
||||
decode_alt(Alts, Code, Fld, T, Vsn, Types);
|
||||
decode_from_template(#{switch := Alts} = T, Code, Fld, Dyn, Vsn, Types) when is_map(Alts) ->
|
||||
decode_switch(Alts, Code, Fld, T, Dyn, Vsn, Types);
|
||||
decode_from_template(list, _, Flds, _, Vsn, Types) ->
|
||||
[decode_(F, Vsn, Types) || F <- Flds];
|
||||
decode_from_template(map, Fld, Vsn, Types) ->
|
||||
decode_from_template(map, _, Fld, _, Vsn, Types) ->
|
||||
TupleFields = [F || F <- Fld],
|
||||
Items = [decode_from_template({any,any}, T, Vsn, Types)
|
||||
Items = [decode_from_template({any,any}, ?TUPLE, T, true, Vsn, Types)
|
||||
|| T <- TupleFields],
|
||||
maps:from_list(Items);
|
||||
decode_from_template(tuple, Fld, Vsn, Types) ->
|
||||
decode_from_template(tuple, _, Fld, _, Vsn, Types) ->
|
||||
Items = [decode_(F, Vsn, Types) || F <- Fld],
|
||||
list_to_tuple(Items);
|
||||
decode_from_template([Type], Fields, Vsn, Types) ->
|
||||
[decode_from_template(Type, F, Vsn, Types)
|
||||
decode_from_template([Type], _, Fields, _Dyn, Vsn, Types) ->
|
||||
[decode_typed_(Type, F, Vsn, Types)
|
||||
|| F <- Fields];
|
||||
decode_from_template(Type, V, Vsn, Types) when is_list(Type), is_list(V) ->
|
||||
decode_fields(Type, V, Vsn, Types);
|
||||
decode_from_template(Type, V, Vsn, Types) when is_tuple(Type), is_list(V) ->
|
||||
decode_from_template(Type, _, V, Dyn, Vsn, Types) when is_list(Type), is_list(V) ->
|
||||
decode_fields(Type, V, Dyn, Vsn, Types);
|
||||
decode_from_template(Type, _, V, _Dyn, Vsn, Types) when is_tuple(Type), is_list(V) ->
|
||||
Zipped = lists:zip(tuple_to_list(Type), V),
|
||||
Items = [decode_from_template(T1, V1, Vsn, Types) || {T1, V1} <- Zipped],
|
||||
Items = [decode_typed_(T1, V1, Vsn, Types) || {T1, V1} <- Zipped],
|
||||
list_to_tuple(Items);
|
||||
decode_from_template(label, [C], _, #{rev_labels := RLbls}) ->
|
||||
decode_from_template(label, _, [C], _, _, #{rev_labels := RLbls}) ->
|
||||
Code = decode_basic(int, C),
|
||||
maps:get(Code, RLbls);
|
||||
decode_from_template(Type, Fld, _, Types) when Type == int
|
||||
; Type == negint
|
||||
; Type == binary
|
||||
; Type == bool
|
||||
; Type == id
|
||||
; Type == label ->
|
||||
decode_basic(Type, Fld, Types).
|
||||
decode_from_template(Type, Code, Fld, _, Vsn, Types) when Type == int
|
||||
; Type == negint
|
||||
; Type == binary
|
||||
; Type == bool
|
||||
; Type == id
|
||||
; Type == label ->
|
||||
case template(Code, Vsn, Types) of
|
||||
Type ->
|
||||
decode_basic(Type, Fld, Types);
|
||||
_ ->
|
||||
error(badarg)
|
||||
end.
|
||||
|
||||
dec_zip_items([{K, T}|Is], [{K1, KEnc, VEnc}|Fs], Vsn, Types) ->
|
||||
if K =:= K1 ->
|
||||
[{K, T, KEnc, VEnc} | dec_zip_items(Is, Fs, Vsn, Types)];
|
||||
true ->
|
||||
error(illegal_map)
|
||||
end;
|
||||
dec_zip_items([{K, T}|Is], [[KEnc, VEnc]|Fs], Vsn, Types) ->
|
||||
case decode_(KEnc, Vsn, Types) of
|
||||
K ->
|
||||
[{K, T, KEnc, VEnc} | dec_zip_items(Is, Fs, Vsn, Types)];
|
||||
_ ->
|
||||
error(illegal_map)
|
||||
end;
|
||||
dec_zip_items([{opt, K, T}|Is], [{K, KEnc, VEnc}|Fs], Vsn, Types) ->
|
||||
[{K, T, KEnc, VEnc} | dec_zip_items(Is, Fs, Vsn, Types)];
|
||||
dec_zip_items([{opt, K, T}|Is], [[KEnc,VEnc]|Fs], Vsn, Types) ->
|
||||
case decode_(KEnc, Vsn, Types) of
|
||||
K ->
|
||||
[{K, T, KEnc, VEnc} | dec_zip_items(Is, Fs, Vsn, Types)];
|
||||
OtherK ->
|
||||
dec_zip_items(Is, [{OtherK, KEnc, VEnc}|Fs], Vsn, Types)
|
||||
end;
|
||||
dec_zip_items([], [], _, _) ->
|
||||
[].
|
||||
|
||||
encode_from_template(any, V, _E, Vsn, Types) ->
|
||||
encode_(V, dyn(emit()), Vsn, Types);
|
||||
@@ -475,12 +547,12 @@ encode_from_template(Type, V, E, Vsn, Types) ->
|
||||
assert_type(true, _, _) -> ok;
|
||||
assert_type(_, Type, V) -> error({illegal, Type, V}).
|
||||
|
||||
decode_alt([A|Alts], Fld, T, Vsn, Types) ->
|
||||
try decode_from_template(A, Fld, Vsn, Types)
|
||||
decode_alt([A|Alts], Code, Fld, T, Vsn, Types) ->
|
||||
try decode_from_template(A, Code, Fld, true, Vsn, Types)
|
||||
catch error:_ ->
|
||||
decode_alt(Alts, Fld, T, Vsn, Types)
|
||||
decode_alt(Alts, Code, Fld, T, Vsn, Types)
|
||||
end;
|
||||
decode_alt([], Fld, T, _, _) ->
|
||||
decode_alt([], _Code, Fld, T, _, _) ->
|
||||
error({illegal, T, Fld}).
|
||||
|
||||
encode_alt(Alts, Term, T, E, Vsn, Types) ->
|
||||
@@ -495,12 +567,12 @@ encode_alt_([A|Alts], Term, T, E, Vsn, Types) ->
|
||||
encode_alt_([], Term, T, _, _, _) ->
|
||||
error({illegal, T, Term}).
|
||||
|
||||
decode_switch(Alts, Fld, T, Vsn, Types) ->
|
||||
[KFld, VFld] = Fld,
|
||||
decode_switch(Alts, Code, Fld, T, Dyn, Vsn, Types) ->
|
||||
[[KFld, VFld]] = Fld,
|
||||
Key = decode_(KFld, Vsn, Types),
|
||||
case maps:find(Key, Alts) of
|
||||
{ok, SubType} ->
|
||||
SubTerm = decode_from_template(SubType, VFld, Vsn, Types),
|
||||
SubTerm = decode_from_template(SubType, Code, VFld, Dyn, Vsn, Types),
|
||||
#{Key => SubTerm};
|
||||
error ->
|
||||
error({illegal, T, Fld})
|
||||
@@ -536,11 +608,11 @@ encode_fields([], [], _, _, _) ->
|
||||
[].
|
||||
|
||||
decode_fields([{Tag, Type}|TypesLeft],
|
||||
[Field |FieldsLeft], Vsn, Types) ->
|
||||
[Field |FieldsLeft], Dyn, Vsn, Types) ->
|
||||
|
||||
[ {Tag, decode_from_template(Type, Field, Vsn, Types)}
|
||||
| decode_fields(TypesLeft, FieldsLeft, Vsn, Types)];
|
||||
decode_fields([], [], _, _) ->
|
||||
[ {Tag, decode_from_template(Type, 0, Field, Dyn, Vsn, Types)}
|
||||
| decode_fields(TypesLeft, FieldsLeft, Dyn, Vsn, Types)];
|
||||
decode_fields([], [], _, _, _) ->
|
||||
[].
|
||||
|
||||
|
||||
@@ -609,14 +681,20 @@ register_types(Types) when is_map(Types) ->
|
||||
register_types(latest_vsn(), Types).
|
||||
|
||||
register_types(Vsn, Types) ->
|
||||
Result = add_types(Types),
|
||||
put_types(Vsn, Result).
|
||||
|
||||
add_types(Types) ->
|
||||
add_types(Types, dynamic_types()).
|
||||
|
||||
add_types(Types, PrevTypes) ->
|
||||
Codes = maps:get(codes, Types, #{}),
|
||||
Rev = rev_codes(Codes),
|
||||
Templates = maps:get(templates, Types, #{}),
|
||||
Labels = maps:get(labels, Types, #{}),
|
||||
Options = maps:get(options, Types, #{}),
|
||||
#{codes := Codes0, rev := Rev0, labels := Labels0,
|
||||
templates := Templates0, options := Options0} =
|
||||
dynamic_types(),
|
||||
templates := Templates0, options := Options0} = PrevTypes,
|
||||
Merged = #{ codes => maps:merge(Codes0, Codes)
|
||||
, rev => maps:merge(Rev0, Rev)
|
||||
, templates => maps:merge(Templates0, Templates)
|
||||
@@ -624,8 +702,7 @@ register_types(Vsn, Types) ->
|
||||
, labels => maps:merge(Labels0, Labels) },
|
||||
assert_sizes(Merged),
|
||||
assert_mappings(Merged),
|
||||
Merged1 = assert_label_cache(Merged),
|
||||
put_types(Vsn, Merged1).
|
||||
assert_label_cache(Merged).
|
||||
|
||||
latest_vsn() ->
|
||||
case persistent_term:get(pt_key(), undefined) of
|
||||
@@ -817,6 +894,7 @@ user_types_test_() ->
|
||||
, ?_test(t_reg_template_vsnd_fun())
|
||||
, ?_test(t_reg_label_cache())
|
||||
, ?_test(t_reg_label_cache2())
|
||||
, ?_test(t_reg_map())
|
||||
, ?_test(t_reg_options())
|
||||
]}.
|
||||
|
||||
@@ -833,6 +911,45 @@ versioned_types_test_() ->
|
||||
[ ?_test(t_new_version())
|
||||
].
|
||||
|
||||
consistency_test_() ->
|
||||
[?_test(t_full_round_trip(Type, Term))
|
||||
|| {Type, Term} <-
|
||||
lists:flatmap(
|
||||
fun({Type, Terms}) ->
|
||||
[{Type,T} || T <- Terms]
|
||||
end, full_round_trip_terms())].
|
||||
|
||||
full_round_trip_terms() ->
|
||||
[
|
||||
{ #{items => [{a, binary}]}, [#{a => <<"foo">>}] }
|
||||
, { #{items => [ {a, int}
|
||||
, {opt, b, int}
|
||||
, {c, int} ]}, [ #{a => 1, b => 2, c => 3}
|
||||
, #{a => 1, c => 3} ] }
|
||||
, { #{alt => [int, label]}, [ 1, a ] }
|
||||
, { #{switch => #{ a => int, b => binary }}, [ #{a => 17}
|
||||
, #{b => <<"foo">>} ]}
|
||||
].
|
||||
|
||||
t_full_round_trip(Type, Term) ->
|
||||
?debugFmt("Type = ~p, Term = ~p", [Type, Term]),
|
||||
Types = dynamic_types(),
|
||||
ETyped = encode_typed(Type, Term, Types),
|
||||
Types1 = gmser_dyn_types:add_type(mytype, 1030, Type, Types),
|
||||
EReg = encode_typed(mytype, Term, Types1),
|
||||
DTyped = decode_typed(Type, ETyped, Types),
|
||||
?debugFmt("DTyped = ~p", [DTyped]),
|
||||
DReg = decode_typed(mytype, EReg, Types1),
|
||||
?assertEqual(Term, DTyped),
|
||||
?assertEqual(Term, DReg),
|
||||
DDynT = decode(ETyped),
|
||||
?assertEqual(Term, DDynT),
|
||||
try decode(EReg)
|
||||
catch
|
||||
error:_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
t_round_trip(T) ->
|
||||
?debugVal(T),
|
||||
?assertMatch({T, T}, {T, decode(encode(T))}).
|
||||
@@ -928,6 +1045,21 @@ t_reg_label_cache2() ->
|
||||
[<<0>>,<<1>>,[<<3,235>>,[[<<49>>],[<<49>>]]]] = Enc,
|
||||
_Tup = gmser_dyn:decode(Enc).
|
||||
|
||||
t_reg_map() ->
|
||||
Types =
|
||||
#{codes => #{1013 => my_map},
|
||||
templates => #{my_map => #{items => [{a, label},
|
||||
{b, int}]}}
|
||||
},
|
||||
register_types(Types),
|
||||
Enc0 = gmser_dyn:encode_typed(my_map, #{a => foo, b => 17}),
|
||||
dbg:tracer(),
|
||||
dbg:tpl(?MODULE,x),
|
||||
dbg:p(all,[c]),
|
||||
#{a := foo, b := 17} = try gmser_dyn:decode(Enc0)
|
||||
after dbg:stop() end,
|
||||
ok.
|
||||
|
||||
t_reg_options() ->
|
||||
register_types(set_opts(#{missing_labels => convert})),
|
||||
[Dyn,Vsn,[Am,<<"random">>]] = gmser_dyn:encode(random),
|
||||
|
||||
+17
-4
@@ -17,6 +17,9 @@
|
||||
, is_id/1
|
||||
]).
|
||||
|
||||
-export([ t_id/1
|
||||
]).
|
||||
|
||||
%% For aec_serialization
|
||||
-export([ encode/1
|
||||
, decode/1
|
||||
@@ -26,11 +29,18 @@
|
||||
, val
|
||||
}).
|
||||
|
||||
-type tag() :: 'account' | 'name'
|
||||
| 'commitment' | 'contract' | 'channel'
|
||||
| 'associate_chain' | 'entry' .
|
||||
-type tag() :: 'account'
|
||||
| 'associate_chain'
|
||||
| 'channel'
|
||||
| 'commitment'
|
||||
| 'contract'
|
||||
| 'contract_source'
|
||||
| 'name'
|
||||
| 'native_token'
|
||||
| 'entry'.
|
||||
|
||||
-type val() :: <<_:256>>.
|
||||
-opaque(id() :: #id{}).
|
||||
-type id() :: #id{}.
|
||||
|
||||
-export_type([ id/0
|
||||
, tag/0
|
||||
@@ -94,6 +104,9 @@ decode(<<Tag:?TAG_SIZE/unit:8, Val:?PUB_SIZE/binary>>) ->
|
||||
#id{ tag = decode_tag(Tag)
|
||||
, val = Val}.
|
||||
|
||||
-spec t_id(any()) -> id().
|
||||
t_id(#id{} = Id) -> Id.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
|
||||
Reference in New Issue
Block a user