From 416bfc76faadd0257d5930389eb9b5796f15159f Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Mon, 7 Apr 2025 20:50:51 +0200 Subject: [PATCH] WIP add 'items', fix some layout issues --- src/gmser_dyn.erl | 153 +++++++++++++++++++++++++++++++--------------- 1 file changed, 103 insertions(+), 50 deletions(-) diff --git a/src/gmser_dyn.erl b/src/gmser_dyn.erl index 52590f5..4f0d563 100644 --- a/src/gmser_dyn.erl +++ b/src/gmser_dyn.erl @@ -87,7 +87,8 @@ decode_tag_and_vsn([TagBin, VsnBin | Fields]) -> dynamic_types() -> #{ vsn => ?VSN , codes => - #{ 248 => int + #{ 247 => negint + , 248 => int , 249 => binary , 250 => bool , 251 => list @@ -96,7 +97,8 @@ dynamic_types() -> , 254 => id , 255 => label} , rev => - #{ int => 248 + #{ negint => 247 + , int => 248 , binary => 249 , bool => 250 , list => 251 @@ -107,7 +109,8 @@ dynamic_types() -> , labels => #{} , rev_labels => #{} , templates => - #{ int => int + #{ negint => negint + , int => int , binary => binary , bool => bool , list => list @@ -175,27 +178,37 @@ encode_(Term, Vsn, Types) -> encode_(Term, Emit, Vsn, Types) -> {Tag, Template} = auto_template(Term), - Enc = encode_from_template(Template, Term, Vsn, Types), if Emit -> - [emit_code(Tag, Types), Enc]; + [emit_code(Tag, Types), + encode_from_template(Template, Term, false, true, Vsn, Types)]; true -> - Enc + encode_from_template(Template, Term, false, true, Vsn, Types) + end. + %% if Emit -> + %% [emit_code(Tag, Types), Enc]; + %% true -> + %% Enc + %% end. + +encode_typed_(Type, Term, Vsn, #{codes := Codes, rev := Rev} = Types) -> + case (is_map_key(Type, Codes) orelse is_map_key(Type, Rev)) of + true -> + encode_typed_(Type, Term, true, true, Vsn, Types); + false -> + encode_maybe_template(Type, Term, Vsn, Types) end. -encode_typed_(Type, Term, Vsn, Types) -> - encode_typed_(Type, Term, true, Vsn, Types). - -encode_typed_(any, Term, _, Vsn, Types) -> +encode_typed_(any, Term, _, _, Vsn, Types) -> encode_(Term, true, Vsn, Types); -encode_typed_(Code, Term, Emit, Vsn, #{codes := Codes} = Types) when is_map_key(Code, Codes) -> +encode_typed_(Code, Term, Emit, IsDyn, Vsn, #{codes := Codes} = Types) when is_map_key(Code, Codes) -> {_Tag, Template} = template(Code, Vsn, Types), - maybe_emit(Emit, Code, encode_from_template(Template, Term, false, Vsn, Types)); -encode_typed_(Tag, Term, Emit, Vsn, #{templates := Ts, rev := Rev} = Types) + maybe_emit(Emit, Code, encode_from_template(Template, Term, false, IsDyn, Vsn, Types)); +encode_typed_(Tag, Term, Emit, IsDyn, Vsn, #{templates := Ts, rev := Rev} = Types) when is_map_key(Tag, Ts) -> Template = dyn_template_(maps:get(Tag, Ts), Vsn), Code = maps:get(Tag, Rev), - maybe_emit(Emit, Code, encode_from_template(Template, Term, false, Vsn, Types)); -encode_typed_(MaybeTemplate, Term, _, Vsn, Types) -> + maybe_emit(Emit, Code, encode_from_template(Template, Term, false, IsDyn, Vsn, Types)); +encode_typed_(MaybeTemplate, Term, _, _, Vsn, Types) -> encode_maybe_template(MaybeTemplate, Term, Vsn, Types). maybe_emit(true, Code, Enc) -> @@ -203,12 +216,18 @@ maybe_emit(true, Code, Enc) -> maybe_emit(false, _, Enc) -> Enc. +encode_maybe_template(#{items := _} = Type, Term, Vsn, Types) -> + case is_map(Term) of + true -> + [emit_code(map, Types), encode_from_template(Type, Term, true, true, Vsn, Types)]; + false -> + error({invalid, Type, Term}) + end; encode_maybe_template(Pat, Term, Vsn, Types) when is_list(Pat); - is_tuple(Pat); - is_map(Pat) -> + is_tuple(Pat) -> {Tag, _} = auto_template(Pat), [emit_code(Tag, Types), - encode_from_template(Pat, Term, true, Vsn, Types)]; + encode_from_template(Pat, Term, true, true, Vsn, Types)]; encode_maybe_template(Other, Term, _Vsn, _Types) -> error({illegal_template, Other, Term}). @@ -233,10 +252,19 @@ auto_template(T) -> is_atom(T) -> {label, label}; % binary_to_existing_atom() is_integer(T), T >= 0 -> {int, int}; + is_integer(T), + T < 0 -> {negint, negint}; true -> - error(invalid_type) + error({invalid_type, T}) end. +decode_from_template(#{items := Items}, Fld, Vsn, Types) when is_list(Fld) -> + Zipped = lists:zip(Items, Fld), + lists:foldl( + fun({{K, Type}, V}, Map) -> + maps:is_key(K, Map) andalso error(badarg, duplicate_field), + Map#{K => decode_from_template(Type, V, Vsn, Types)} + end, #{}, Zipped); decode_from_template(list, Fld, Vsn, Types) -> decode_(Fld, Vsn, Types, []); decode_from_template(map, Fld, Vsn, Types) -> @@ -245,7 +273,8 @@ decode_from_template(map, Fld, Vsn, Types) -> || T <- TupleFields], maps:from_list(Items); decode_from_template(tuple, Fld, Vsn, Types) -> - Items = decode_(Fld, Vsn, Types, []), + Items = [decode_field_(F, Vsn, Types) || F <- Fld], + %% Items = decode_(Fld, Vsn, Types, []), list_to_tuple(Items); decode_from_template([Type], Fields, Vsn, Types) -> [decode_from_template(Type, F, Vsn, Types) @@ -260,6 +289,7 @@ 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 @@ -267,31 +297,38 @@ decode_from_template(Type, Fld, _, Types) when Type == int decode_basic(Type, Fld, Types). encode_from_template(Type, V, Vsn, Types) -> - encode_from_template(Type, V, true, Vsn, Types). + encode_from_template(Type, V, true, true, Vsn, Types). -encode_from_template(any, V, _, Vsn, Types) -> +encode_from_template(any, V, _Emit, _IsDyn, Vsn, Types) -> encode_(V, true, Vsn, Types); -encode_from_template(list, L, _, Vsn, Types) when is_list(L) -> +encode_from_template(list, L, _, _IsDyn, Vsn, Types) when is_list(L) -> assert_type(is_list(L), list, L), [encode_(V, Vsn, Types) || V <- L]; -encode_from_template(map, M, _, Vsn, Types) -> +encode_from_template(#{items := Items}, M, _, IsDyn, Vsn, Types) -> assert_type(is_map(M), map, M), - [encode_({K,V}, false, Vsn, Types) + lists:map( + fun({K, Type}) -> + V = maps:get(K, M), + encode_from_template({any,Type}, {K,V}, true, IsDyn, Vsn, Types) + end, Items); +encode_from_template(map, M, _, IsDyn, Vsn, Types) -> + assert_type(is_map(M), map, M), + [encode_from_template({any,any}, {K,V}, false, IsDyn, Vsn, Types) || {K, V} <- lists:sort(maps:to_list(M))]; -encode_from_template(tuple, T, Emit, Vsn, Types) -> +encode_from_template(tuple, T, Emit, _IsDyn, Vsn, Types) -> assert_type(is_tuple(T), tuple, T), - [encode_(V, Emit, Vsn, Types) || V <- tuple_to_list(T)]; -encode_from_template(T, V, Emit, Vsn, Types) when is_tuple(T) -> + [encode_(V, true, Vsn, Types) || V <- tuple_to_list(T)]; +encode_from_template(T, V, Emit, IsDyn, Vsn, Types) when is_tuple(T) -> assert_type(is_tuple(V), T, V), assert_type(tuple_size(T) =:= tuple_size(V), T, V), Zipped = lists:zip(tuple_to_list(T), tuple_to_list(V)), - [encode_from_template(T1, V1, Emit, Vsn, Types) || {T1, V1} <- Zipped]; -encode_from_template([Type] = T, List, Emit, Vsn, Types) -> + [encode_from_template(T1, V1, Emit, IsDyn, Vsn, Types) || {T1, V1} <- Zipped]; +encode_from_template([Type] = T, List, Emit, IsDyn, Vsn, Types) -> assert_type(is_list(List), T, List), - [encode_from_template(Type, V, Emit, Vsn, Types) || V <- List]; -encode_from_template(Type, List, Emit, Vsn, Types) when is_list(Type), is_list(List) -> - encode_fields(Type, List, Emit, Vsn, Types); -encode_from_template(label, V, Emit, _, Types) -> + [encode_from_template(Type, V, Emit, IsDyn, Vsn, Types) || V <- List]; +encode_from_template(Type, List, Emit, IsDyn, Vsn, Types) when is_list(Type), is_list(List) -> + encode_fields(Type, List, Emit, IsDyn, Vsn, Types); +encode_from_template(label, V, Emit, _, _, Types) -> assert_type(is_atom(V), label, V), case find_cached_label(V, Types) of error -> @@ -299,14 +336,15 @@ encode_from_template(label, V, Emit, _, Types) -> {ok, Code} when is_integer(Code) -> [encode_basic(int, Code)] end; -encode_from_template(Type, V, Emit, _, Types) when Type == id - ; Type == binary - ; Type == bool - ; Type == int - ; Type == label -> +encode_from_template(Type, V, Emit, _, _, Types) when Type == id + ; Type == binary + ; Type == bool + ; Type == int + ; Type == negint + ; Type == label -> encode_basic(Type, V, Emit, Types); -encode_from_template(Type, V, Emit, Vsn, Types) -> - encode_typed_(Type, V, Emit, Vsn, Types). +encode_from_template(Type, V, Emit, IsDyn, Vsn, Types) -> + encode_typed_(Type, V, Emit, IsDyn, Vsn, Types). %% error({illegal, Type, V}). assert_type(true, _, _) -> ok; @@ -315,18 +353,18 @@ assert_type(_, Type, V) -> error({illegal, Type, V}). %% Basically, dynamically encoding a statically defined object encode_fields([{Field, Type}|TypesLeft], - [{Field, Val}|FieldsLeft], Emit, Vsn, Types) -> - [ encode_from_template(Type, Val, Emit, Vsn, Types) - | encode_fields(TypesLeft, FieldsLeft, Emit, Vsn, Types)]; + [{Field, Val}|FieldsLeft], Emit, IsDyn, Vsn, Types) -> + [ encode_from_template(Type, Val, Emit, IsDyn, Vsn, Types) + | encode_fields(TypesLeft, FieldsLeft, Emit, IsDyn, Vsn, Types)]; encode_fields([{_Field, _Type} = FT|_TypesLeft], - [Val |_FieldsLeft], _Emit, _Vsn, _Types) -> + [Val |_FieldsLeft], _Emit, _IsDyn, _Vsn, _Types) -> error({illegal_field, FT, Val}); encode_fields([Type|TypesLeft], - [Val |FieldsLeft], Emit, Vsn, Types) when is_atom(Type) -> + [Val |FieldsLeft], Emit, IsDyn, Vsn, Types) when is_atom(Type) -> %% Not sure about this ... - [ encode_from_template(Type, Val, Emit, Vsn, Types) - | encode_fields(TypesLeft, FieldsLeft, Emit, Vsn, Types)]; -encode_fields([], [], _, _, _) -> + [ encode_from_template(Type, Val, Emit, IsDyn, Vsn, Types) + | encode_fields(TypesLeft, FieldsLeft, Emit, IsDyn, Vsn, Types)]; +encode_fields([], [], _, _, _, _) -> []. decode_fields([{Tag, Type}|TypesLeft], @@ -352,9 +390,14 @@ decode_basic(Type, V, _) -> decode_basic(label, Fld) -> binary_to_existing_atom(decode_basic(binary, Fld), utf8); +decode_basic(negint, Fld) -> + I = gmserialization:decode_field(int, Fld), + -I; decode_basic(Type, Fld) -> gmserialization:decode_field(Type, Fld). +encode_basic(negint, I, _, Types) when is_integer(I), I < 0 -> + [emit_code(negint, Types), gmserialization:encode_field(int, -I)]; encode_basic(Tag, V, true, Types) -> [emit_code(Tag, Types), encode_basic(Tag, V)]; encode_basic(Tag, V, false, _) -> @@ -536,6 +579,7 @@ round_trip_test_() -> t_sample_types() -> [ 5 + , -5 , <<"a">> , [1,2,3] , {<<"a">>,1} @@ -560,6 +604,11 @@ user_types_test_() -> , ?_test(t_reg_label_cache2()) ]}. +dynamic_types_test_() -> + [ ?_test(revert_to_default_types()) + , ?_test(typed_map()) + ]. + t_round_trip(T) -> ?debugVal(T), ?assertMatch({T, T}, {T, decode(encode(T))}). @@ -645,7 +694,11 @@ t_reg_label_cache2() -> Tup = {'1', '1'}, Enc = gmser_dyn:encode_typed(lbl_tup2, Tup), [<<0>>,<<1>>,[<<3,235>>,[[<<49>>],[<<49>>]]]] = Enc, - Tup = gmser_dyn:decode(Enc). + _Tup = gmser_dyn:decode(Enc). +typed_map() -> + Term = #{a => 13, {key,1} => [a]}, + Enc = encode_typed(#{items => [{a,int},{{key,1},[label]}]}, Term), + ?assertEqual(Term, decode(Enc)). -endif.