%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(asn1ct_constructed_per).
-export([gen_encode_sequence/3]).
-export([gen_decode_sequence/3]).
-export([gen_encode_set/3]).
-export([gen_decode_set/3]).
-export([gen_encode_sof/4]).
-export([gen_decode_sof/4]).
-export([gen_encode_choice/3]).
-export([gen_decode_choice/3]).
-include("asn1_records.hrl").
%-compile(export_all).
-import(asn1ct_gen, [emit/1,demit/1,get_record_name_prefix/1]).
-type type_name() :: any().
%% ENCODE GENERATOR FOR SEQUENCE TYPE ** **********
-spec gen_encode_set(Gen, TypeName, #type{}) -> 'ok' when
Gen :: #gen{},
TypeName :: type_name().
gen_encode_set(Gen, TypeName, D) ->
gen_encode_constructed(Gen, TypeName, D).
-spec gen_encode_sequence(Gen, TypeName, #type{}) -> 'ok' when
Gen :: #gen{},
TypeName :: type_name().
gen_encode_sequence(Gen, TypeName, D) ->
gen_encode_constructed(Gen, TypeName, D).
gen_encode_constructed(Erule, Typename, #type{}=D) ->
asn1ct_name:start(),
Imm = gen_encode_constructed_imm(Erule, Typename, D),
asn1ct_imm:enc_cg(Imm, is_aligned(Erule)),
emit([".",nl]).
gen_encode_constructed_imm(Gen, Typename, #type{}=D) ->
{CompList,TableConsInfo} = enc_complist(D),
ExternalImm = external_imm(Gen, Typename),
Optionals = optionals(to_textual_order(CompList)),
ImmOptionals = enc_optionals(Gen, Optionals),
Ext = extensible_enc(CompList),
Aligned = is_aligned(Gen),
ExtImm = case Ext of
{ext,ExtPos,NumExt} when NumExt > 0 ->
gen_encode_extaddgroup(Gen, CompList),
Value = make_var(val),
enc_extensions(Gen, Value, ExtPos, NumExt, Aligned);
_ ->
[]
end,
MatchImm = enc_map_match(Gen, CompList),
{EncObj,ObjSetImm} = enc_table(Gen, TableConsInfo, D),
ImmSetExt =
case Ext of
{ext,_Pos,NumExt2} when NumExt2 > 0 ->
asn1ct_imm:per_enc_extension_bit({var,"Extensions"}, Aligned);
{ext,_Pos,_} ->
asn1ct_imm:per_enc_extension_bit([], Aligned);
_ ->
[]
end,
ImmBody = gen_enc_components_call(Gen, Typename, CompList, EncObj, Ext),
ExternalImm ++ MatchImm ++ ExtImm ++ ObjSetImm ++
asn1ct_imm:enc_append([ImmSetExt] ++ ImmOptionals ++ ImmBody).
external_imm(Gen, ['EXTERNAL']) ->
Next = asn1ct_gen:mk_var(asn1ct_name:next(val)),
Curr = asn1ct_gen:mk_var(asn1ct_name:curr(val)),
asn1ct_name:new(val),
F = case Gen of
#gen{pack=record} -> transform_to_EXTERNAL1990;
#gen{pack=map} -> transform_to_EXTERNAL1990_maps
end,
[{call,ext,F,[{var,Curr}],{var,Next}}];
external_imm(_, _) ->
[].
enc_extensions(#gen{pack=record}, Value, ExtPos, NumExt, Aligned) ->
asn1ct_imm:per_enc_extensions(Value, ExtPos, NumExt, Aligned);
enc_extensions(#gen{pack=map}, Value, ExtPos, NumExt, Aligned) ->
Vars = [{var,lists:concat(["Input@",Pos])} ||
Pos <- lists:seq(ExtPos, ExtPos+NumExt-1)],
Undefined = atom_to_list(?MISSING_IN_MAP),
asn1ct_imm:per_enc_extensions_map(Value, Vars, Undefined, Aligned).
enc_complist(#type{def=Def}) ->
case Def of
#'SEQUENCE'{tablecinf=TCI,components=CL0,extaddgroup=ExtAddGroup} ->
case ExtAddGroup of
undefined ->
{CL0,TCI};
_ when is_integer(ExtAddGroup) ->
%% This is a fake SEQUENCE representing an
%% ExtensionAdditionGroup. Renumber the textual
%% order so we get the right index of the
%% components.
CL = add_textual_order(CL0),
{CL,TCI}
end;
#'SET'{tablecinf=TCI,components=CL} ->
{CL,TCI}
end.
enc_table(Gen, #simpletableattributes{objectsetname=ObjectSet,
c_name=AttrN,
c_index=N,
usedclassfield=UniqueFieldName,
uniqueclassfield=UniqueFieldName,
valueindex=ValueIndex0}, _) ->
{Module,ObjSetName} = ObjectSet,
#typedef{typespec=#'ObjectSet'{gen=MustGen}} =
asn1_db:dbget(Module, ObjSetName),
case MustGen of
true ->
ValueIndex = ValueIndex0 ++ [{N+1,'ASN1_top'}],
Val = make_var(val),
{ObjSetImm,Dst} = enc_dig_out_value(Gen, ValueIndex, Val),
{{AttrN,Dst},ObjSetImm};
false ->
{false,[]}
end;
enc_table(_Gen, #simpletableattributes{}, _) ->
{false,[]};
enc_table(_Gen, _, #type{tablecinf=TCInf}) ->
case TCInf of
[{objfun,_}|_] ->
%% The simpletableattributes was at an outer
%% level and the objfun has been passed through the
%% function call.
{{"got objfun through args",{var,"ObjFun"}},[]};
_ ->
{false,[]}
end.
enc_optionals(Gen, Optionals) ->
Var = make_var(val),
enc_optionals_1(Gen, Optionals, Var).
enc_optionals_1(#gen{pack=record}=Gen, [{Pos,DefVals}|T], Var) ->
{Imm0,Element} = asn1ct_imm:enc_element(Pos+1, Var),
Imm = asn1ct_imm:per_enc_optional(Element, DefVals),
[Imm0++Imm|enc_optionals_1(Gen, T, Var)];
enc_optionals_1(#gen{pack=map}=Gen, [{Pos,DefVals0}|T], V) ->
Var = {var,lists:concat(["Input@",Pos])},
DefVals = translate_missing_value(Gen, DefVals0),
Imm = asn1ct_imm:per_enc_optional(Var, DefVals),
[Imm|enc_optionals_1(Gen, T, V)];
enc_optionals_1(_, [], _) ->
[].
enc_map_match(#gen{pack=record}, _Cs) ->
[];
enc_map_match(#gen{pack=map}, Cs0) ->
Var0 = "Input",
Cs = enc_flatten_components(Cs0),
M = [[quote_atom(Name),":=",lists:concat([Var0,"@",Order])] ||
#'ComponentType'{prop=mandatory,name=Name,
textual_order=Order} <- Cs],
Mand = case M of
[] ->
[];
[_|_] ->
Patt = {expr,lists:flatten(["#{",lists:join(",", M),"}"])},
[{assign,Patt,{var,asn1ct_name:curr(val)}}]
end,
Os0 = [{Name,Order} ||
#'ComponentType'{prop=Prop,name=Name,
textual_order=Order} <- Cs,
Prop =/= mandatory],
{var,Val} = make_var(val),
F = fun({Name,Order}) ->
Var = lists:concat([Var0,"@",Order]),
P0 = ["case ",Val," of\n"
" #{",quote_atom(Name),":=",Var,"_0} -> ",
Var,"_0;\n"
" _ -> ",atom_to_list(?MISSING_IN_MAP),"\n"
"end"],
P = lists:flatten(P0),
{assign,{var,Var},P}
end,
Os = [F(O) || O <- Os0],
Mand ++ Os.
enc_flatten_components({Root1,Ext0,Root2}=CL) ->
{_,Gs} = extgroup_pos_and_length(CL),
Ext = wrap_extensionAdditionGroups(Ext0, Gs),
Root1 ++ Root2 ++ [mark_optional(C) || C <- Ext];
enc_flatten_components({Root,Ext}) ->
enc_flatten_components({Root,Ext,[]});
enc_flatten_components(Cs) ->
Cs.
gen_encode_extaddgroup(#gen{pack=record}, CompList) ->
case extgroup_pos_and_length(CompList) of
{extgrouppos,[]} ->
ok;
{extgrouppos,ExtGroupPosLenList} ->
_ = [gen_encode_eag_record(G) ||
G <- ExtGroupPosLenList],
ok
end;
gen_encode_extaddgroup(#gen{pack=map}, Cs0) ->
Cs = enc_flatten_components(Cs0),
gen_encode_eag_map(Cs).
gen_encode_eag_map([#'ComponentType'{name=Group,typespec=Type}|Cs]) ->
case Type of
#type{def=#'SEQUENCE'{extaddgroup=G,components=GCs0}}
when is_integer(G) ->
Ns = [N || #'ComponentType'{name=N,prop=mandatory} <- GCs0],
test_for_mandatory(Ns, Group),
gen_encode_eag_map(Cs);
_ ->
gen_encode_eag_map(Cs)
end;
gen_encode_eag_map([]) ->
ok.
test_for_mandatory([Mand|_], Group) ->
emit([{next,val}," = case ",{curr,val}," of",nl,
"#{",quote_atom(Mand),":=_} -> ",
{curr,val},"#{",{asis,Group},"=>",{curr,val},"};",nl,
"#{} -> ",{curr,val},nl,
"end,",nl]),
asn1ct_name:new(val);
test_for_mandatory([], _) ->
ok.
gen_encode_eag_record({ActualPos,VirtualPos,Len}) ->
Val = asn1ct_gen:mk_var(asn1ct_name:curr(val)),
Elements = get_input_vars(Val, VirtualPos, Len),
Expr = any_non_value(Val, VirtualPos, Len),
emit([{next,val}," = case ",Expr," of",nl,
"false -> setelement(",{asis,ActualPos+1},", ",
{curr,val},", asn1_NOVALUE);",nl,
"true -> setelement(",{asis,ActualPos+1},", ",
{curr,val},", {extaddgroup,", Elements,"})",nl,
"end,",nl]),
asn1ct_name:new(val).
any_non_value(Val, Pos, N) ->
L = any_non_value_1(Val, Pos, N),
lists:join(" orelse ", L).
any_non_value_1(_, _, 0) ->
[];
any_non_value_1(Val, Pos, N) ->
Var = get_input_var(Val, Pos),
[Var ++ " =/= asn1_NOVALUE"|any_non_value_1(Val, Pos+1, N-1)].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% generate decode function for SEQUENCE and SET
%%
gen_decode_set(Erules,Typename,D) ->
gen_decode_constructed(Erules,Typename,D).
gen_decode_sequence(Erules,Typename,D) ->
gen_decode_constructed(Erules,Typename,D).
gen_decode_constructed(Erule, Typename, #type{}=D) ->
Imm0 = gen_dec_constructed_imm(Erule, Typename, #type{}=D),
Imm = opt_imm(Imm0),
asn1ct_name:start(),
emit_gen_dec_imm(Imm),
emit([".",nl,nl]).
opt_imm(Imm0) ->
{Imm,_} = opt_imm_1(Imm0, unknown, []),
Imm.
opt_imm_1([{imm,Imm0,F}|T], Al0, Acc) ->
{Imm,Al} = asn1ct_imm:optimize_alignment(Imm0, Al0),
opt_imm_1(T, Al, [{imm,Imm,F}|Acc]);
opt_imm_1([ignore|T], Al, Acc) ->
opt_imm_1(T, Al, Acc);
opt_imm_1([{ignore,_}=H|T], Al, Acc) ->
opt_imm_1(T, Al, [H|Acc]);
opt_imm_1([{safe,ignore}|T], Al, Acc) ->
opt_imm_1(T, Al, Acc);
opt_imm_1([{safe,_}=H|T], Al, Acc) ->
opt_imm_1(T, Al, [H|Acc]);
opt_imm_1([{group,G0}|T], Al0, Acc) ->
{G,Al} = opt_imm_1(G0, Al0, []),
opt_imm_1(T, Al, [{group,G}|Acc]);
opt_imm_1([Emit|T], _, Acc) when is_function(Emit, 1) ->
opt_imm_1(T, unknown, [Emit|Acc]);
opt_imm_1([], Al, Acc) ->
{lists:reverse(Acc),Al}.
emit_gen_dec_imm(L) ->
emit_gen_dec_imm(L, "", []).
emit_gen_dec_imm([{ignore,Fun}|T], Sep, St0) ->
St = Fun(St0),
emit_gen_dec_imm(T, Sep, St);
emit_gen_dec_imm([{group,L}|T], Sep, St0) ->
emit(Sep),
St = emit_gen_dec_imm_group(L, St0),
emit_gen_dec_imm(T, [com,nl], St);
emit_gen_dec_imm([{imm,Imm,Emit}|T], Sep, St0) ->
emit(Sep),
St = Emit(Imm, St0),
emit_gen_dec_imm(T, [com,nl], St);
emit_gen_dec_imm([{safe,Item}|T], Sep, St) ->
emit_gen_dec_imm([Item|T], Sep, St);
emit_gen_dec_imm([Emit|T], Sep, St0) ->
emit(Sep),
St = Emit(St0),
emit_gen_dec_imm(T, [com,nl], St);
emit_gen_dec_imm([], _, _) -> ok.
emit_gen_dec_imm_group([H|T], St0) ->
St = emit_gen_dec_group_item(H, St0),
emit_gen_dec_imm_group(T, St);
emit_gen_dec_imm_group([], St) -> St.
emit_gen_dec_group_item({ignore,Fun}, St) ->
Fun(St);
emit_gen_dec_group_item({imm,Imm,Fun}, St) ->
Fun(Imm, St);
emit_gen_dec_group_item({safe,Item}, St) ->
emit_gen_dec_group_item(Item, St);
emit_gen_dec_group_item(Emit, St) ->
Emit(St).
gen_dec_constructed_imm(Erule, Typename, #type{}=D) ->
{CompList,TableConsInfo} =
case D#type.def of
#'SEQUENCE'{tablecinf=TCI,components=CL} ->
{add_textual_order(CL),TCI};
#'SET'{tablecinf=TCI,components=CL} ->
%% {add_textual_order(CL),TCI}
{CL,TCI} % the textual order is already taken care of
end,
Ext = extensible_dec(CompList),
EmitExt = case Ext of
{ext,_Pos,_NumExt} ->
gen_dec_extension_value();
_ -> ignore
end,
Optionals = optionals(CompList),
EmitOpt = case Optionals of
[] ->
ignore;
[_|_] ->
gen_dec_optionals(Optionals)
end,
ObjSetInfo =
case TableConsInfo of
%% {ObjectSet,AttrN,N,UniqueFieldName} ->%% N is index of attribute that determines constraint
#simpletableattributes{objectsetname=ObjectSet,
c_name=AttrN,
usedclassfield=UniqueFieldName,
uniqueclassfield=UniqueFieldName,
valueindex=ValIndex} ->
%% {AttrN,ObjectSet};
F = fun(#'ComponentType'{typespec=CT})->
case {asn1ct_gen:get_constraint(CT#type.constraint,componentrelation),CT#type.tablecinf} of
{no,[{objfun,_}|_R]} -> true;
_ -> false
end
end,
case lists:any(F,flat_complist(CompList)) of
true -> % when component relation constraint establish
%% relation from a component to another components
%% subtype component
{{AttrN,{deep,ObjectSet,UniqueFieldName,ValIndex}},
UniqueFieldName,ValIndex};
false ->
{{AttrN,ObjectSet},UniqueFieldName,ValIndex}
end;
_ ->
case D#type.tablecinf of
[{objfun,_}|_] ->
{{"got objfun through args","ObjFun"},false,false};
_ ->
{false,false,false}
end
end,
{DecObjInf,_,_} = ObjSetInfo,
EmitComp = gen_dec_components_call(Erule, Typename, CompList,
DecObjInf, Ext, length(Optionals)),
EmitObjSets = gen_dec_objsets_fun(Erule, ObjSetInfo),
EmitPack = fun(_) ->
gen_dec_pack(Erule, Typename, CompList)
end,
RestGroup = {group,[{safe,EmitObjSets},{safe,EmitPack}]},
[EmitExt,EmitOpt|EmitComp++[RestGroup]].
gen_dec_objsets_fun(Gen, ObjSetInfo) ->
fun({AccTerm,AccBytes}) ->
{_,_UniqueFName,ValueIndex} = ObjSetInfo,
case {AccTerm,AccBytes} of
{[],[]} ->
ok;
{_,[]} ->
ok;
{[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} ->
ValueMatch = value_match(Gen, ValueIndex, Term),
_ = [begin
gen_dec_open_type(Gen, ValueMatch, ObjSet,
LeadingAttr, T),
emit([com,nl])
end || T <- ListOfOpenTypes],
ok
end
end.
gen_dec_pack(Gen, Typename, CompList) ->
case Typename of
['EXTERNAL'] ->
dec_external(Gen, Typename);
_ ->
asn1ct_name:new(res),
gen_dec_do_pack(Gen, Typename, CompList),
emit([com,nl,
"{",{curr,res},",",{curr,bytes},"}"])
end.
dec_external(#gen{pack=record}=Gen, Typename) ->
RecordName = list_to_atom(record_name(Gen, Typename)),
All = [{var,Term} || Term <- asn1ct_name:all(term)],
Record = [{asis,RecordName}|All],
emit(["OldFormat={",lists:join(",", Record),"},",nl,
"ASN11994Format =",nl,
{call,ext,transform_to_EXTERNAL1994,
["OldFormat"]},com,nl,
"{ASN11994Format,",{curr,bytes},"}"]);
dec_external(#gen{pack=map}, _Typename) ->
Vars = asn1ct_name:all(term),
Names = ['direct-reference','indirect-reference',
'data-value-descriptor',encoding],
Zipped = lists:zip(Names, Vars),
MapInit = lists:join(",", [["'",N,"'=>",{var,V}] || {N,V} <- Zipped]),
emit(["OldFormat = #{",MapInit,"}",com,nl,
"ASN11994Format =",nl,
{call,ext,transform_to_EXTERNAL1994_maps,
["OldFormat"]},com,nl,
"{ASN11994Format,",{curr,bytes},"}"]).
gen_dec_do_pack(#gen{pack=record}=Gen, TypeName, CompList) ->
Zipped0 = zip_components(CompList, asn1ct_name:all(term)),
Zipped = textual_order(Zipped0),
RecordName = ["'",record_name(Gen, TypeName),"'"],
L = [RecordName|[{var,Var} || {_,Var} <- Zipped]],
emit([{curr,res}," = {",lists:join(",", L),"}"]);
gen_dec_do_pack(#gen{pack=map}, _, CompList0) ->
CompList = enc_flatten_components(CompList0),
Zipped0 = zip_components(CompList, asn1ct_name:all(term)),
Zipped = textual_order(Zipped0),
PF = fun({#'ComponentType'{prop='OPTIONAL'},_}) -> false;
({_,_}) -> true
end,
{Mandatory,Optional} = lists:partition(PF, Zipped),
L = [[{asis,Name},"=>",{var,Var}] ||
{#'ComponentType'{name=Name},Var} <- Mandatory],
emit([{curr,res}," = #{",lists:join(",", L),"}"]),
gen_dec_map_optional(Optional),
gen_dec_merge_maps(asn1ct_name:all(map)).
gen_dec_map_optional([{#'ComponentType'{name=Name},Var}|T]) ->
asn1ct_name:new(res),
emit([com,nl,
{curr,res}," = case ",{var,Var}," of",nl,
" asn1_NOVALUE -> ",{prev,res},";",nl,
" _ -> ",{prev,res},"#{",{asis,Name},"=>",{var,Var},"}",nl,
"end"]),
gen_dec_map_optional(T);
gen_dec_map_optional([]) ->
ok.
gen_dec_merge_maps([M|Ms]) ->
asn1ct_name:new(res),
emit([com,nl,
{curr,res}," = maps:merge(",{prev,res},", ",{var,M},")"]),
gen_dec_merge_maps(Ms);
gen_dec_merge_maps([]) ->
ok.
quote_atom(A) when is_atom(A) ->
io_lib:format("~p", [A]).
%% record_name([TypeName]) -> RecordNameString
%% Construct a record name for the constructed type, ignoring any
%% fake sequences that are used to represent an extension addition
%% group. Such fake sequences never appear as a top type, and their
%% name always start with "ExtAddGroup".
record_name(Gen, Typename0) ->
[TopType|Typename1] = lists:reverse(Typename0),
Typename = filter_ext_add_groups(Typename1, [TopType]),
lists:concat([get_record_name_prefix(Gen),
asn1ct_gen:list2rname(Typename)]).
filter_ext_add_groups([H|T], Acc) when is_atom(H) ->
case atom_to_list(H) of
"ExtAddGroup"++_ ->
filter_ext_add_groups(T, Acc);
_ ->
filter_ext_add_groups(T, [H|Acc])
end;
filter_ext_add_groups([H|T], Acc) ->
filter_ext_add_groups(T, [H|Acc]);
filter_ext_add_groups([], Acc) -> Acc.
zip_components({Root,Ext}, Vars) ->
zip_components({Root,Ext,[]}, Vars);
zip_components({R1,Ext0,R2}, Vars) ->
Ext = [mark_optional(C) || C <- Ext0],
zip_components(R1++R2++Ext, Vars);
zip_components(Cs, Vars) when is_list(Cs) ->
zip_components_1(Cs, Vars).
zip_components_1([#'ComponentType'{}=C|Cs], [V|Vs]) ->
[{C,V}|zip_components_1(Cs, Vs)];
zip_components_1([_|Cs], Vs) ->
zip_components_1(Cs, Vs);
zip_components_1([], []) ->
[].
textual_order([{#'ComponentType'{textual_order=undefined},_}|_]=L) ->
L;
textual_order(L0) ->
L = [{Ix,P} || {#'ComponentType'{textual_order=Ix},_}=P <- L0],
[C || {_,C} <- lists:sort(L)].
to_textual_order({Root,Ext}) ->
{to_textual_order(Root),Ext};
to_textual_order(Cs) when is_list(Cs) ->
case Cs of
[#'ComponentType'{textual_order=undefined}|_] ->
Cs;
_ ->
lists:keysort(#'ComponentType'.textual_order,Cs)
end;
to_textual_order(Cs) ->
Cs.
gen_dec_open_type(Erule, Val, {Xmod,Xtype}, LeadingAttr,
{_,{Name,RestFieldNames},Term,TmpTerm,Prop}) ->
#typedef{typespec=ObjSet0} = asn1_db:dbget(Xmod, Xtype),
#'ObjectSet'{class=Class,set=ObjSet1} = ObjSet0,
#'Externaltypereference'{module=ClMod,type=ClType} = Class,
#classdef{typespec=ClassDef} = asn1_db:dbget(ClMod, ClType),
#objectclass{fields=ClassFields} = ClassDef,
Extensible = lists:member('EXTENSIONMARK', ObjSet1),
Typename = [Name,ClType],
ObjSet = index_object_set(Erule, ClType, Name,
ObjSet1, ClassFields),
Key = erlang:md5(term_to_binary({decode,ObjSet,RestFieldNames,
Prop,Extensible})),
Gen = fun(_Fd, N) ->
dec_objset_optional(N, Prop),
dec_objset(Erule, N, ObjSet, RestFieldNames, Typename),
dec_objset_default(N, Name, LeadingAttr, Extensible)
end,
Prefix = lists:concat(["dec_os_",Name]),
F = asn1ct_func:call_gen(Prefix, Key, Gen),
emit([Term," = ",{asis,F},"(",TmpTerm,", ",Val,")"]).
dec_objset_optional(N, {'DEFAULT',Val}) ->
dec_objset_optional_1(N, Val);
dec_objset_optional(N, 'OPTIONAL') ->
dec_objset_optional_1(N, asn1_NOVALUE);
dec_objset_optional(_N, mandatory) -> ok.
dec_objset_optional_1(N, Val) ->
emit([{asis,N},"(",{asis,Val},", _Id) ->",nl,
{asis,Val},";",nl]).
dec_objset(_Erule, _N, [], _, _) ->
ok;
dec_objset(Erule, N, [Obj|Objs], RestFields, Cl) ->
dec_objset_1(Erule, N, Obj, RestFields, Cl),
emit([";",nl]),
dec_objset(Erule, N, Objs, RestFields, Cl).
dec_objset_default(N, C, LeadingAttr, false) ->
emit([{asis,N},"(Bytes, Id) ->",nl,
"exit({'Type not compatible with table constraint',"
"{{component,",{asis,C},"},"
"{value,Bytes},"
"{unique_name_and_value,",{asis,LeadingAttr},",Id}}}).",nl,nl]);
dec_objset_default(N, _, _, true) ->
emit([{asis,N},"(Bytes, Id) ->",nl|
case asn1ct:use_legacy_types() of
false ->
["{asn1_OPENTYPE,Bytes}.",nl,nl];
true ->
["Bytes.",nl,nl]
end]).
dec_objset_1(Erule, N, {Id,Obj}, RestFields, Typename) ->
emit([{asis,N},"(Bytes, Id) when Id =:= ",{asis,Id}," ->",nl]),
dec_objset_2(Erule, Obj, RestFields, Typename).
dec_objset_2(Erule, Obj, RestFields0, Typename) ->
case Obj of
#typedef{name={primitive,bif},typespec=Type} ->
Imm = asn1ct_gen_per:gen_dec_imm(Erule, Type),
{Term,_} = asn1ct_imm:dec_slim_cg(Imm, 'Bytes'),
emit([com,nl,Term]);
#typedef{name={constructed,bif},typespec=Type}=Def ->
Prefix = "dec_outlined_",
Key = {dec_outlined,Def},
Gen = fun(_Fd, Name) ->
gen_dec_obj(Erule, Name, Typename, Type)
end,
Func = asn1ct_func:call_gen(Prefix, Key, Gen),
emit(["{Term,_} = ",{asis,Func},"(Bytes)",com,nl,
"Term"]);
#typedef{name=Type} ->
emit(["{Result,_} = ",{asis,enc_func("dec_", Type)},"(Bytes),",nl,
"Result"]);
#'Externaltypereference'{module=Mod,type=Type} ->
emit("{Term,_} = "),
Func = enc_func("dec_", Type),
case get(currmod) of
Mod ->
emit([{asis,Func},"(Bytes)"]);
_ ->
emit([{asis,Mod},":",{asis,Func},"(Bytes)"])
end,
emit([com,nl,
"Term"]);
#'Externalvaluereference'{module=Mod,value=Value} ->
case asn1_db:dbget(Mod, Value) of
#typedef{typespec=#'Object'{def=Def}} ->
{object,_,Fields} = Def,
[NextField|RestFields] = RestFields0,
{NextField,Typedef} = lists:keyfind(NextField, 1, Fields),
dec_objset_2(Erule, Typedef, RestFields, Typename)
end
end.
gen_dec_obj(Erules, Name, Typename, Type) ->
emit([{asis,Name},"(Bytes) ->",nl]),
InnerType = asn1ct_gen:get_inner(Type#type.def),
asn1ct_gen:gen_decode_constructed(Erules, Typename,
InnerType, Type).
gen_encode_choice(Erule, TopType, D) ->
asn1ct_name:start(),
Imm = gen_encode_choice_imm(Erule, TopType, D),
asn1ct_imm:enc_cg(Imm, is_aligned(Erule)),
emit([".",nl]).
gen_encode_choice_imm(Erule, TopType, #type{def={'CHOICE',CompList}}) ->
Ext = extensible_enc(CompList),
Aligned = is_aligned(Erule),
Cs = gen_enc_choice(Erule, TopType, CompList, Ext),
[{assign,{expr,"{ChoiceTag,ChoiceVal}"},"Val"}|
asn1ct_imm:per_enc_choice({var,"ChoiceTag"}, Cs, Aligned)].
gen_decode_choice(Erules,Typename,D) when is_record(D,type) ->
asn1ct_name:start(),
asn1ct_name:new(bytes),
{'CHOICE',CompList} = D#type.def,
Ext = extensible_enc(CompList),
gen_dec_choice(Erules,Typename,CompList,Ext),
emit({".",nl}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Encode generator for SEQUENCE OF type
gen_encode_sof(Erule, Typename, SeqOrSetOf, D) ->
asn1ct_name:start(),
Imm = gen_encode_sof_imm(Erule, Typename, SeqOrSetOf, D),
asn1ct_imm:enc_cg(Imm, is_aligned(Erule)),
emit([".",nl,nl]).
gen_encode_sof_imm(Erule, Typename, SeqOrSetOf, #type{}=D) ->
{_SeqOrSetOf,ComponentType} = D#type.def,
Aligned = is_aligned(Erule),
CompType = ComponentType#type.def,
Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf, CompType),
Conttype = asn1ct_gen:get_inner(CompType),
Currmod = get(currmod),
Imm0 = case asn1ct_gen:type(Conttype) of
{primitive,bif} ->
asn1ct_gen_per:gen_encode_prim_imm({var,"Comp"},
ComponentType, Aligned);
{constructed,bif} ->
TypeName = [Constructed_Suffix|Typename],
Enc = enc_func(asn1ct_gen:list2name(TypeName)),
ObjArg = case D#type.tablecinf of
[{objfun,_}|_] -> [{var,"ObjFun"}];
_ -> []
end,
[{apply,{local,Enc,CompType},
[{var,"Comp"}|ObjArg]}];
#'Externaltypereference'{module=Currmod,type=Ename} ->
[{apply,{local,enc_func(Ename),CompType},[{var,"Comp"}]}];
#'Externaltypereference'{module=EMod,type=Ename} ->
[{apply,{EMod,enc_func(Ename),CompType},[{var,"Comp"}]}];
'ASN1_OPEN_TYPE' ->
asn1ct_gen_per:gen_encode_prim_imm({var,"Comp"},
#type{def='ASN1_OPEN_TYPE'},
Aligned)
end,
asn1ct_imm:per_enc_sof({var,"Val"}, D#type.constraint, 'Comp',
Imm0, Aligned).
gen_decode_sof(Erules, Typename, SeqOrSetOf, #type{}=D) ->
asn1ct_name:start(),
do_gen_decode_sof(Erules, Typename, SeqOrSetOf, D),
emit([".",nl,nl]).
do_gen_decode_sof(Erules, Typename, SeqOrSetOf, D) ->
{_SeqOrSetOf,ComponentType} = D#type.def,
SizeConstraint = asn1ct_imm:effective_constraint(bitstring,
D#type.constraint),
ObjFun =
case D#type.tablecinf of
[{objfun,_}|_R] ->
", ObjFun";
_ ->
""
end,
{Num,Buf} = gen_decode_length(SizeConstraint, Erules),
Key = erlang:md5(term_to_binary({Typename,SeqOrSetOf,ComponentType})),
Gen = fun(_Fd, Name) ->
gen_decode_sof_components(Erules, Name,
Typename, SeqOrSetOf,
ComponentType)
end,
F = asn1ct_func:call_gen("dec_components", Key, Gen),
emit([",",nl,
{asis,F},"(",Num,", ",Buf,ObjFun,", [])"]).
is_aligned(#gen{erule=per,aligned=Aligned}) -> Aligned.
gen_decode_length(Constraint, Erule) ->
emit(["%% Length with constraint ",{asis,Constraint},nl]),
Imm = asn1ct_imm:per_dec_length(Constraint, true, is_aligned(Erule)),
asn1ct_imm:dec_slim_cg(Imm, "Bytes").
gen_decode_sof_components(Erule, Name, Typename, SeqOrSetOf, Cont) ->
{ObjFun,ObjFun_Var} =
case Cont#type.tablecinf of
[{objfun,_}|_R] ->
{", ObjFun",", _"};
_ ->
{"",""}
end,
emit([{asis,Name},"(0, Bytes",ObjFun_Var,", Acc) ->",nl,
"{lists:reverse(Acc),Bytes};",nl]),
emit([{asis,Name},"(Num, Bytes",ObjFun,", Acc) ->",nl,
"{Term,Remain} = "]),
Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,
Cont#type.def),
Conttype = asn1ct_gen:get_inner(Cont#type.def),
case asn1ct_gen:type(Conttype) of
{primitive,bif} ->
asn1ct_gen_per:gen_dec_prim(Erule, Cont, "Bytes"),
emit({com,nl});
{constructed,bif} ->
NewTypename = [Constructed_Suffix|Typename],
emit({"'dec_",asn1ct_gen:list2name(NewTypename),
"'(Bytes",ObjFun,"),",nl});
#'Externaltypereference'{}=Etype ->
asn1ct_gen_per:gen_dec_external(Etype, "Bytes"),
emit([com,nl]);
'ASN1_OPEN_TYPE' ->
asn1ct_gen_per:gen_dec_prim(Erule, #type{def='ASN1_OPEN_TYPE'},
"Bytes"),
emit({com,nl});
_ ->
emit({"'dec_",Conttype,"'(Bytes),",nl})
end,
emit([{asis,Name},"(Num-1, Remain",ObjFun,", [Term|Acc]).",nl]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% General and special help functions (not exported)
extensible_dec(CompList) when is_list(CompList) ->
noext;
extensible_dec({RootList,ExtList}) ->
{ext,length(RootList)+1,ext_length(ExtList)};
extensible_dec({Rl1,Ext,Rl2}) ->
{ext,length(Rl1)+length(Rl2)+1,ext_length(Ext)}.
extensible_enc(CompList) when is_list(CompList) ->
noext;
extensible_enc({RootList,ExtList}) ->
{ext,length(RootList)+1,ext_length(ExtList)};
extensible_enc({Rl1,Ext,_Rl2}) ->
{ext,length(Rl1)+1,ext_length(Ext)}.
ext_length(ExtList) -> ext_length(ExtList,normal,0).
ext_length([{'ExtensionAdditionGroup',_Num}|T],_,Acc)->
ext_length(T,group,Acc);
ext_length(['ExtensionAdditionGroupEnd'|T],group,Acc) ->
ext_length(T,normal,Acc+1);
ext_length([#'ComponentType'{}|T],State=group,Acc) ->
ext_length(T,State,Acc);
ext_length([#'ComponentType'{}|T],State=normal,Acc) ->
ext_length(T,State,Acc+1);
ext_length([],_,Acc) ->
Acc.
extgroup_pos_and_length(CompList) when is_list(CompList) ->
{extgrouppos,[]};
extgroup_pos_and_length({RootList,ExtList}) ->
ActualPos = length(RootList) +1,
%% position to get and deliver data in the record to the user
VirtualPos = ActualPos,
%% position to encode/decode the extaddgroup as an opentype sequence
extgrouppos(ExtList,ActualPos,VirtualPos,[]);
extgroup_pos_and_length({RootList,ExtList,_Rl2}) ->
extgroup_pos_and_length({RootList,ExtList}).
extgrouppos([{'ExtensionAdditionGroup',_Num}|T],ActualPos,VirtualPos,Acc) ->
extgrouppos(T,ActualPos,VirtualPos,0,Acc);
extgrouppos([_|T],ActualPos,VirtualPos,Acc) ->
extgrouppos(T,ActualPos+1,VirtualPos+1,Acc);
extgrouppos([],_,_,Acc) ->
{extgrouppos,lists:reverse(Acc)}.
extgrouppos(['ExtensionAdditionGroupEnd'|T],ActualPos,VirtualPos,Len,Acc) ->
extgrouppos(T,ActualPos+1,VirtualPos+Len,[{ActualPos,VirtualPos,Len}|Acc]);
extgrouppos([_|T],ActualPos,VirtualPos,Len,Acc) ->
extgrouppos(T,ActualPos,VirtualPos,Len+1,Acc).
gen_dec_extension_value() ->
Imm0 = {get_bits,1,[1]},
E = fun(Imm, _) ->
emit(["{Ext,",{next,bytes},"} = "]),
BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
asn1ct_imm:dec_code_gen(Imm, BytesVar),
asn1ct_name:new(bytes)
end,
{imm,Imm0,E}.
gen_dec_optionals(Optionals) ->
Imm0 = {get_bits,length(Optionals),[1]},
E = fun(Imm, _) ->
BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
emit(["{Opt,",{next,bytes},"} = "]),
asn1ct_imm:dec_code_gen(Imm, BytesVar),
asn1ct_name:new(bytes)
end,
{imm,Imm0,E}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
optionals({Root1,Ext,Root2}) ->
Opt1 = optionals(Root1, 1),
ExtComps = length([C || C = #'ComponentType'{} <- Ext]),
Opt2 = optionals(Root2, 1 + length(Root1) + ExtComps),
Opt1 ++ Opt2;
optionals({L,_Ext}) ->
optionals(L, 1);
optionals(L) ->
optionals(L, 1).
optionals([#'ComponentType'{prop='OPTIONAL'}|Rest], Pos) ->
[{Pos,[asn1_NOVALUE]}|optionals(Rest, Pos+1)];
optionals([#'ComponentType'{typespec=T,prop={'DEFAULT',Val}}|Cs], Pos) ->
Vals = def_values(T, Val),
[{Pos,Vals}|optionals(Cs, Pos+1)];
optionals([#'ComponentType'{}|Rest], Pos) ->
optionals(Rest, Pos+1);
optionals([], _) ->
[].
%%%%%%%%%%%%%%%%%%%%%%
%% create_optionality_table(Cs=[#'ComponentType'{textual_order=undefined}|_]) ->
%% {NewCs,_} = lists:mapfoldl(fun(C,Num) ->
%% {C#'ComponentType'{textual_order=Num},
%% Num+1}
%% end,
%% 1,Cs),
%% create_optionality_table(NewCs);
create_optionality_table(Cs) ->
IsOptional = fun('OPTIONAL') -> true;
({'DEFAULT',_}) -> true;
(_) -> false
end,
OptionalsElNum = [TO || #'ComponentType'{prop = O,textual_order=TO} <- Cs,
IsOptional(O)],
{Table,_} = lists:mapfoldl(fun(X,Num) ->
{{Num,X},Num+1}
end,
1,lists:sort(OptionalsElNum)),
Table.
get_optionality_pos(TextPos,OptTable) ->
case lists:keysearch(TextPos,2,OptTable) of
{value,{OptNum,_}} ->
OptNum;
_ ->
no_num
end.
add_textual_order(Cs) when is_list(Cs) ->
{NewCs,_} = add_textual_order1(Cs,1),
NewCs;
add_textual_order({Root,Ext}) ->
{NewRoot,Num} = add_textual_order1(Root,1),
{NewExt,_} = add_textual_order1(Ext,Num),
{NewRoot,NewExt};
add_textual_order({R1,Ext,R2}) ->
{NewR1,Num1} = add_textual_order1(R1,1),
{NewExt,Num2} = add_textual_order1(Ext,Num1),
{NewR2,_} = add_textual_order1(R2,Num2),
{NewR1,NewExt,NewR2}.
%%add_textual_order1(Cs=[#'ComponentType'{textual_order=Int}|_],I)
%% when is_integer(Int) ->
%% {Cs,I};
add_textual_order1(Cs,NumIn) ->
lists:mapfoldl(fun(C=#'ComponentType'{},Num) ->
{C#'ComponentType'{textual_order=Num},
Num+1};
(OtherMarker,Num) ->
{OtherMarker,Num}
end,
NumIn,Cs).
gen_enc_components_call(Erule, TopType, {Root,ExtList}, DynamicEnc, Ext) ->
gen_enc_components_call(Erule, TopType, {Root,ExtList,[]}, DynamicEnc, Ext);
gen_enc_components_call(Erule, TopType, {R1,ExtList0,R2}=CL, DynamicEnc, Ext) ->
Root = R1 ++ R2,
Imm0 = gen_enc_components_call1(Erule, TopType, Root, DynamicEnc, noext),
ExtImm = case Ext of
{ext,_,ExtNum} when ExtNum > 0 ->
[{var,"Extensions"}];
_ ->
[]
end,
{extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL),
ExtList1 = wrap_extensionAdditionGroups(ExtList0, ExtGroupPosLen),
ExtList = [mark_optional(C) || C <- ExtList1],
Imm1 = gen_enc_components_call1(Erule, TopType, ExtList, DynamicEnc, Ext),
Imm0 ++ [ExtImm|Imm1];
gen_enc_components_call(Erule, TopType, CompList, DynamicEnc, Ext) ->
%% No extension marker.
gen_enc_components_call1(Erule, TopType, CompList, DynamicEnc, Ext).
mark_optional(#'ComponentType'{prop=Prop0}=C) ->
Prop = case Prop0 of
mandatory -> 'OPTIONAL';
'OPTIONAL'=Keep -> Keep;
{'DEFAULT',_}=Keep -> Keep
end,
C#'ComponentType'{prop=Prop};
mark_optional(Other) ->
Other.
gen_enc_components_call1(Gen, TopType, [C|Rest], DynamicEnc, Ext) ->
#'ComponentType'{name=Cname,typespec=Type,
prop=Prop,textual_order=Num} = C,
InnerType = asn1ct_gen:get_inner(Type#type.def),
CommentString = attribute_comment(InnerType, Num, Cname),
ImmComment = asn1ct_imm:enc_comment(CommentString),
{Imm0,Element} = enc_fetch_field(Gen, Num, Prop),
Imm1 = gen_enc_line_imm(Gen, TopType, Cname, Type,
Element, DynamicEnc, Ext),
Imm2 = case Prop of
mandatory ->
Imm1;
'OPTIONAL' ->
enc_absent(Gen, Element, [asn1_NOVALUE], Imm1);
{'DEFAULT',Def} ->
DefValues = def_values(Type, Def),
enc_absent(Gen, Element, DefValues, Imm1)
end,
Imm = case Imm2 of
[] -> [];
_ -> [ImmComment|Imm0 ++ Imm2]
end,
[Imm|gen_enc_components_call1(Gen, TopType, Rest, DynamicEnc, Ext)];
gen_enc_components_call1(_Gen, _TopType, [], _, _) ->
[].
enc_absent(Gen, Var, Absent0, Imm) ->
Absent = translate_missing_value(Gen, Absent0),
asn1ct_imm:enc_absent(Var, Absent, Imm).
translate_missing_value(#gen{pack=record}, Optionals) ->
Optionals;
translate_missing_value(#gen{pack=map}, Optionals) ->
case Optionals of
[asn1_NOVALUE|T] -> [?MISSING_IN_MAP|T];
[asn1_DEFAULT|T] -> [?MISSING_IN_MAP|T];
{call,_,_,_} -> Optionals
end.
enc_fetch_field(#gen{pack=record}, Num, _Prop) ->
Val = make_var(val),
asn1ct_imm:enc_element(Num+1, Val);
enc_fetch_field(#gen{pack=map}, Num, _) ->
{[],{var,lists:concat(["Input@",Num])}}.
def_values(#type{def=#'Externaltypereference'{module=Mod,type=Type}}, Def) ->
#typedef{typespec=T} = asn1_db:dbget(Mod, Type),
def_values(T, Def);
def_values(#type{def={'BIT STRING',[]}}, Bs) when is_bitstring(Bs) ->
case asn1ct:use_legacy_types() of
false ->
[asn1_DEFAULT,Bs];
true ->
ListBs = [B || <<B:1>> <= Bs],
IntBs = lists:foldl(fun(B, A) ->
(A bsl 1) bor B
end, 0, lists:reverse(ListBs)),
Sz = bit_size(Bs),
Compact = case 8 - Sz rem 8 of
8 ->
{0,Bs};
Unused ->
{Unused,<<Bs:Sz/bits,0:Unused>>}
end,
[asn1_DEFAULT,Bs,Compact,ListBs,IntBs]
end;
def_values(#type{def={'BIT STRING',[_|_]=Ns}}, List) when is_list(List) ->
Bs = asn1ct_gen:named_bitstring_value(List, Ns),
As = case asn1ct:use_legacy_types() of
false ->
[List,Bs];
true ->
ListBs = [B || <<B:1>> <= Bs],
IntBs = lists:foldl(fun(B, A) ->
(A bsl 1) bor B
end, 0, lists:reverse(ListBs)),
[List,Bs,ListBs,IntBs]
end,
{call,per_common,is_default_bitstring,As};
def_values(#type{def={'INTEGER',Ns}}, Def) ->
[asn1_DEFAULT,Def|case lists:keyfind(Def, 2, Ns) of
false -> [];
{Val,Def} -> [Val]
end];
def_values(_, Def) ->
[asn1_DEFAULT,Def].
gen_enc_line_imm(Erule, TopType, Cname, Type, Element, DynamicEnc, Ext) ->
Imm0 = gen_enc_line_imm_1(Erule, TopType, Cname, Type,
Element, DynamicEnc),
Aligned = is_aligned(Erule),
case Ext of
{ext,_Ep2,_} ->
asn1ct_imm:per_enc_open_type(Imm0, Aligned);
_ ->
Imm0
end.
gen_enc_line_imm_1(Erule, TopType, Cname, Type, Element, DynamicEnc) ->
Atype =
case Type of
#type{def=#'ObjectClassFieldType'{type=InnerType}} ->
InnerType;
_ ->
asn1ct_gen:get_inner(Type#type.def)
end,
Aligned = is_aligned(Erule),
case Atype of
{typefield,_} ->
{_LeadingAttrName,Fun} = DynamicEnc,
case (Type#type.def)#'ObjectClassFieldType'.fieldname of
{Name,RestFieldNames} when is_atom(Name) ->
Imm = enc_var_type_call(Erule, Name, RestFieldNames,
Type, Fun, Element),
asn1ct_imm:per_enc_open_type(Imm, Aligned)
end;
_ ->
CurrMod = get(currmod),
case asn1ct_gen:type(Atype) of
#'Externaltypereference'{module=CurrMod,type=EType} ->
[{apply,{local,enc_func(EType),Atype},[Element]}];
#'Externaltypereference'{module=Mod,type=EType} ->
[{apply,{Mod,enc_func(EType),Atype},[Element]}];
{primitive,bif} ->
asn1ct_gen_per:gen_encode_prim_imm(Element, Type, Aligned);
'ASN1_OPEN_TYPE' ->
case Type#type.def of
#'ObjectClassFieldType'{type=OpenType} ->
asn1ct_gen_per:gen_encode_prim_imm(Element,
#type{def=OpenType},
Aligned);
_ ->
asn1ct_gen_per:gen_encode_prim_imm(Element,
Type,
Aligned)
end;
{constructed,bif} ->
NewTypename = [Cname|TopType],
Enc = enc_func(asn1ct_gen:list2name(NewTypename)),
case {Type#type.tablecinf,DynamicEnc} of
{[{objfun,_}|_R],{_,EncFun}} ->
[{apply,{local,Enc,Type},[Element,EncFun]}];
_ ->
[{apply,{local,Enc,Type},[Element]}]
end
end
end.
enc_func(Type) ->
enc_func("enc_", Type).
enc_func(Prefix, Name) ->
list_to_atom(lists:concat([Prefix,Name])).
enc_var_type_call(Erule, Name, RestFieldNames,
#type{tablecinf=TCI}, Fun, Val) ->
[{objfun,#'Externaltypereference'{module=Xmod,type=Xtype}}] = TCI,
#typedef{typespec=ObjSet0} = asn1_db:dbget(Xmod, Xtype),
#'ObjectSet'{class=Class,set=ObjSet1} = ObjSet0,
#'Externaltypereference'{module=ClMod,type=ClType} = Class,
#classdef{typespec=ClassDef} = asn1_db:dbget(ClMod, ClType),
#objectclass{fields=ClassFields} = ClassDef,
Extensible = lists:member('EXTENSIONMARK', ObjSet1),
ObjSet = index_object_set(Erule, ClType, Name,
ObjSet1, ClassFields),
Key = erlang:md5(term_to_binary({encode,ObjSet,RestFieldNames,Extensible})),
TypeName = [ClType,Name],
Imm = enc_objset_imm(Erule, TypeName, Name, ObjSet,
RestFieldNames, Extensible),
Lambda = {lambda,[{var,"Val"},{var,"Id"}],Imm},
Gen = fun(_Fd, N) ->
Aligned = is_aligned(Erule),
emit([{asis,N},"(Val, Id) ->",nl]),
asn1ct_imm:enc_cg(Imm, Aligned),
emit([".",nl])
end,
Prefix = lists:concat(["enc_os_",Name]),
[{call_gen,Prefix,Key,Gen,Lambda,[Val,Fun]}].
index_object_set(_Erules, _ClType, Name, Set0, ClassFields) ->
Set = index_object_set_1(Name, Set0, ClassFields),
lists:sort(Set).
index_object_set_1(Name, [{_,Key,Code}|T], ClassFields) ->
case index_object_set_2(Name, Code, ClassFields) of
none ->
index_object_set_1(Name, T, ClassFields);
Type ->
[{Key,Type}|index_object_set_1(Name, T, ClassFields)]
end;
index_object_set_1(Name, [_|T], ClassFields) ->
index_object_set_1(Name, T, ClassFields);
index_object_set_1(_, [], _) ->
[].
index_object_set_2(Name, [{Name,Type}|_], _ClassFields) ->
Type;
index_object_set_2(Name, [_|T], ClassFields) ->
index_object_set_2(Name, T, ClassFields);
index_object_set_2(Name, [], ClassFields) ->
case lists:keyfind(Name, 2, ClassFields) of
{typefield,Name,'OPTIONAL'} ->
none;
{objectfield,Name,_,_,'OPTIONAL'} ->
none;
{typefield,Name,{'DEFAULT',#type{}=Type}} ->
InnerType = asn1ct_gen:get_inner(Type#type.def),
case asn1ct_gen:type(InnerType) of
{primitive,bif} ->
#typedef{name={primitive,bif},typespec=Type};
{constructed,bif} ->
#typedef{name={constructed,bif},typespec=Type}
end
end.
enc_objset_imm(Erule, TypeName, Component, ObjSet,
RestFieldNames, Extensible) ->
Aligned = is_aligned(Erule),
E = {error,
fun() ->
emit(["exit({'Type not compatible with table constraint',"
"{component,",{asis,Component},"},"
"{value,Val},"
"{unique_name_and_value,'_'}})",nl])
end},
[{'cond',
[[{eq,{var,"Id"},Key}|
enc_obj(Erule, Obj, TypeName, RestFieldNames, Aligned)] ||
{Key,Obj} <- ObjSet] ++
[['_',case Extensible of
false ->
E;
true ->
case asn1ct:use_legacy_types() of
false ->
{call,per_common,open_type_to_binary,
[{var,"Val"}]};
true ->
{call,per_common,legacy_open_type_to_binary,
[{var,"Val"}]}
end
end]]}].
enc_obj(Erule, Obj, TypeName, RestFieldNames0, Aligned) ->
Val = {var,"Val"},
case Obj of
#typedef{name={constructed,bif},typespec=Type}=Def ->
Prefix = "enc_outlined_",
Key = {enc_outlined,Def},
Gen = fun(_Fd, Name) ->
gen_enc_obj(Erule, Name, TypeName, Type)
end,
[{call_gen,Prefix,Key,Gen,undefined,[Val]}];
#typedef{name={primitive,bif},typespec=Def} ->
asn1ct_gen_per:gen_encode_prim_imm({var,"Val"}, Def, Aligned);
#typedef{name=Type} ->
[{apply,{local,enc_func(Type),Type},[{var,"Val"}]}];
#'Externalvaluereference'{module=Mod,value=Value} ->
case asn1_db:dbget(Mod, Value) of
#typedef{typespec=#'Object'{def=Def}} ->
{object,_,Fields} = Def,
[NextField|RestFieldNames] = RestFieldNames0,
{NextField,Typedef} = lists:keyfind(NextField, 1, Fields),
enc_obj(Erule, Typedef, TypeName,
RestFieldNames, Aligned)
end;
#'Externaltypereference'{module=Mod,type=Type} ->
Func = enc_func(Type),
case get(currmod) of
Mod ->
[{apply,{local,Func,Obj},[{var,"Val"}]}];
_ ->
[{apply,{Mod,Func,Obj},[{var,"Val"}]}]
end
end.
gen_enc_obj(Erules, Name, Typename, Type) ->
emit([{asis,Name},"(Val) ->",nl]),
InnerType = asn1ct_gen:get_inner(Type#type.def),
asn1ct_gen:gen_encode_constructed(Erules, Typename,
InnerType, Type).
gen_dec_components_call(Erule, TopType, {Root,ExtList},
DecInfObj, Ext, NumberOfOptionals) ->
gen_dec_components_call(Erule,TopType,{Root,ExtList,[]},
DecInfObj,Ext,NumberOfOptionals);
gen_dec_components_call(Gen, TopType, {Root1,ExtList,Root2}=CL,
DecInfObj, Ext, NumberOfOptionals) ->
%% The type has extensionmarker
OptTable = create_optionality_table(Root1++Root2),
Init = {ignore,fun(_) -> {[],[]} end},
{EmitRoot,Tpos} =
gen_dec_comp_calls(Root1++Root2, Gen, TopType, OptTable,
DecInfObj, noext, NumberOfOptionals,
1, []),
EmitGetExt = gen_dec_get_extension(Gen),
{extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL),
NewExtList = wrap_extensionAdditionGroups(ExtList, ExtGroupPosLen),
{EmitExts,_} = gen_dec_comp_calls(NewExtList, Gen, TopType, OptTable,
DecInfObj, Ext, NumberOfOptionals,
Tpos, []),
NumExtsToSkip = ext_length(ExtList),
Finish =
fun(St) ->
emit([{next,bytes},"= "]),
Mod = case Gen of
#gen{erule=per,aligned=false} -> uper;
#gen{erule=per,aligned=true} -> per
end,
asn1ct_func:call(Mod, skipextensions,
[{curr,bytes},NumExtsToSkip+1,"Extensions"]),
asn1ct_name:new(bytes),
St
end,
[Init] ++ EmitRoot ++ [EmitGetExt|EmitExts] ++ [Finish];
gen_dec_components_call(Erule, TopType, CompList, DecInfObj,
Ext, NumberOfOptionals) ->
%% The type has no extensionmarker
OptTable = create_optionality_table(CompList),
Init = {ignore,fun(_) -> {[],[]} end},
{Cs,_} = gen_dec_comp_calls(CompList, Erule, TopType, OptTable,
DecInfObj, Ext, NumberOfOptionals,
1, []),
[Init|Cs].
gen_dec_get_extension(Erule) ->
Imm0 = asn1ct_imm:per_dec_extension_map(is_aligned(Erule)),
E = fun(Imm, St) ->
emit([nl,"%% Extensions",
nl,
"{Extensions,",{next,bytes},"} = ",
"case Ext of",nl,
"0 -> {<<>>,",{curr,bytes},"};",nl,
"1 ->",nl]),
BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
{Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
emit([com,nl,
"{",Dst,",",DstBuf,"}",nl,
"end"]),
asn1ct_name:new(bytes),
St
end,
{imm,Imm0,E}.
gen_dec_comp_calls([C|Cs], Erule, TopType, OptTable, DecInfObj,
Ext, NumberOfOptionals, Tpos, Acc) ->
L = gen_dec_comp_call(C, Erule, TopType, Tpos, OptTable, DecInfObj,
Ext, NumberOfOptionals),
gen_dec_comp_calls(Cs, Erule, TopType, OptTable, DecInfObj,
Ext, NumberOfOptionals, Tpos+1, [L|Acc]);
gen_dec_comp_calls([], _, _, _, _, _, _, Tpos, Acc) ->
{lists:append(lists:reverse(Acc)),Tpos}.
gen_dec_comp_call(Comp, Gen, TopType, Tpos, OptTable, DecInfObj,
Ext, NumberOfOptionals) ->
#'ComponentType'{name=Cname,typespec=Type,
prop=Prop,textual_order=TextPos} = Comp,
Pos = case Ext of
noext -> Tpos;
{ext,Epos,_Enum} -> Tpos - Epos + 1
end,
InnerType = asn1ct_gen:get_inner(Type#type.def),
CommentString = attribute_comment(InnerType, TextPos, Cname),
Comment = fun(St) ->
emit([nl,"%% ",CommentString,nl]),
St
end,
Preamble =
case {InnerType,is_mandatory_predef_tab_c(Ext, Prop, DecInfObj)} of
{{typefield,_},true} ->
%% DecInfObj /= {"got objfun through args","ObjFun"} |
%% (DecInfObj == {"got objfun through args","ObjFun"} &
%% Ext == noext & Prop == mandatory)
fun(St) ->
asn1ct_name:new(term),
asn1ct_name:new(tmpterm),
emit(["{",{curr,tmpterm},", ",{next,bytes},"} = "]),
St
end;
_ ->
case Type of
#type{def=#'SEQUENCE'{
extaddgroup=GroupNum,
components=CompList}} when is_integer(GroupNum)->
dec_match_extadd_fun(Gen, CompList);
_ ->
fun(St) ->
asn1ct_name:new(term),
emit(["{",{curr,term}]),
emit([",",{next,bytes},"} = "]),
St
end
end
end,
{Pre,Post} = comp_call_pre_post(Gen, Ext, Prop, Pos, Type, TextPos,
OptTable, NumberOfOptionals, Ext),
Lines = gen_dec_seq_line_imm(Gen, TopType, Comp, Tpos, DecInfObj, Ext),
AdvBuffer = {ignore,fun(St) ->
asn1ct_name:new(bytes),
St
end},
[{group,[{safe,Comment},{safe,Preamble}] ++ Pre ++
Lines ++ Post ++ [{safe,AdvBuffer}]}].
dec_match_extadd_fun(#gen{pack=record}, CompList) ->
fun(St) ->
emit(["{{_,"]),
emit_extaddgroupTerms(term, CompList),
emit(["}"]),
emit([",",{next,bytes},"} = "]),
St
end;
dec_match_extadd_fun(#gen{pack=map}, _CompList) ->
fun(St) ->
asn1ct_name:new(map),
emit(["{",{curr,map},",",{next,bytes},"} = "]),
St
end.
comp_call_pre_post(_Gen, noext, mandatory, _, _, _, _, _, _) ->
{[],[]};
comp_call_pre_post(_Gen, noext, Prop, _, Type, TextPos,
OptTable, NumOptionals, Ext) ->
%% OPTIONAL or DEFAULT
OptPos = get_optionality_pos(TextPos, OptTable),
Element = case NumOptionals - OptPos of
0 ->
"Opt band 1";
Shift ->
lists:concat(["(Opt bsr ",Shift,") band 1"])
end,
{[fun(St) ->
emit(["case ",Element," of",nl,
"1 ->",nl]),
St
end],
[fun(St) ->
emit([";",nl,
"0 ->",nl,
"{"]),
gen_dec_component_no_val(Ext, Type, Prop),
emit([",",{curr,bytes},"}",nl,
"end"]),
St
end]};
comp_call_pre_post(Gen, {ext,_,_}, Prop, Pos, Type, _, _, _, Ext) ->
%% Extension
{[fun(St) ->
emit(["case Extensions of",nl,
" <<_:",Pos-1,",1:1,_/bitstring>> ->",nl]),
St
end],
[extadd_group_fun(Gen, Prop, Type, Ext)]}.
extadd_group_fun(#gen{pack=record}, Prop, Type, Ext) ->
fun(St) ->
emit([";",nl,
"_ ->",nl,
"{"]),
case Type of
#type{def=#'SEQUENCE'{
extaddgroup=Number2,
components=ExtGroupCompList2}}
when is_integer(Number2)->
emit("{extAddGroup,"),
gen_dec_extaddGroup_no_val(Ext, Type, ExtGroupCompList2),
emit("}");
_ ->
gen_dec_component_no_val(Ext, Type, Prop)
end,
emit([",",{curr,bytes},"}",nl,
"end"]),
St
end;
extadd_group_fun(#gen{pack=map}, Prop, Type, Ext) ->
fun(St) ->
emit([";",nl,
"_ ->",nl,
"{"]),
case Type of
#type{def=#'SEQUENCE'{
extaddgroup=Number2,
components=Comp}}
when is_integer(Number2)->
dec_map_extaddgroup_no_val(Ext, Type, Comp);
_ ->
gen_dec_component_no_val(Ext, Type, Prop)
end,
emit([",",{curr,bytes},"}",nl,
"end"]),
St
end.
is_mandatory_predef_tab_c(noext, mandatory,
{"got objfun through args","ObjFun"}) ->
true;
is_mandatory_predef_tab_c(_, _, {"got objfun through args","ObjFun"}) ->
false;
is_mandatory_predef_tab_c(_,_,_) ->
true.
gen_dec_extaddGroup_no_val(Ext, Type, [#'ComponentType'{prop=Prop}])->
gen_dec_component_no_val(Ext, Type, Prop),
ok;
gen_dec_extaddGroup_no_val(Ext, Type, [#'ComponentType'{prop=Prop}|Rest])->
gen_dec_component_no_val(Ext, Type, Prop),
emit(","),
gen_dec_extaddGroup_no_val(Ext, Type, Rest);
gen_dec_extaddGroup_no_val(_, _, []) ->
ok.
gen_dec_component_no_val(_, Type, {'DEFAULT',DefVal0}) ->
DefVal = asn1ct_gen:conform_value(Type, DefVal0),
emit([{asis,DefVal}]);
gen_dec_component_no_val(_, _, 'OPTIONAL') ->
emit({"asn1_NOVALUE"});
gen_dec_component_no_val({ext,_,_}, _, mandatory) ->
emit({"asn1_NOVALUE"}).
dec_map_extaddgroup_no_val(Ext, Type, Comp) ->
L0 = [dec_map_extaddgroup_no_val_1(N, P, Ext, Type) ||
#'ComponentType'{name=N,prop=P} <- Comp],
L = [E || E <- L0, E =/= []],
emit(["#{",lists:join(",", L),"}"]).
dec_map_extaddgroup_no_val_1(Name, {'DEFAULT',DefVal0}, _Ext, Type) ->
DefVal = asn1ct_gen:conform_value(Type, DefVal0),
[Name,"=>",{asis,DefVal}];
dec_map_extaddgroup_no_val_1(_Name, 'OPTIONAL', _, _) ->
[];
dec_map_extaddgroup_no_val_1(_Name, mandatory, {ext,_,_}, _) ->
[].
gen_dec_choice_line(Erule, TopType, Comp, Pre) ->
Imm0 = gen_dec_line_imm(Erule, TopType, Comp, false, Pre),
Init = {ignore,fun(_) -> {[],[]} end},
Imm = [{group,[Init|Imm0]}],
emit_gen_dec_imm(Imm).
gen_dec_seq_line_imm(Erule, TopType, Comp, Pos, DecInfObj, Ext) ->
Pre = gen_dec_line_open_type(Erule, Ext, Pos),
gen_dec_line_imm(Erule, TopType, Comp, DecInfObj, Pre).
gen_dec_line_imm(Erule, TopType, Comp, DecInfObj, Pre) ->
#'ComponentType'{name=Cname,typespec=Type} = Comp,
Atype =
case Type of
#type{def=#'ObjectClassFieldType'{type=InnerType}} ->
InnerType;
_ ->
asn1ct_gen:get_inner(Type#type.def)
end,
Decode = gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj),
Post =
fun({SaveBytes,Finish}) ->
{AccTerm,AccBytes} = Finish(),
#'ComponentType'{name=Cname} = Comp,
case DecInfObj of
{Cname,ObjSet} ->
ObjSetRef =
case ObjSet of
{deep,OSName,_,_} ->
OSName;
_ -> ObjSet
end,
{AccTerm++[{ObjSetRef,Cname,
asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
AccBytes++SaveBytes};
_ ->
{AccTerm,AccBytes++SaveBytes}
end
end,
[Pre,Decode,{safe,Post}].
gen_dec_line_open_type(Erule, {ext,Ep,_}, Pos) when Pos >= Ep ->
Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
{safe,fun(St) ->
emit(["begin",nl]),
BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
{Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
emit([",",nl,"{TmpValx",Pos,",_} = "]),
{Dst,
fun() ->
emit([",",nl,
"{TmpValx",Pos,",",DstBuf,"}",nl,
"end"]),
St
end}
end};
gen_dec_line_open_type(_, _, _) ->
{safe,fun(St) ->
{asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
fun() -> St end}
end}.
gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp,
DecInfObj) ->
#'ComponentType'{name=Cname,typespec=Type,prop=Prop} = Comp,
fun({_BytesVar,PrevSt}) ->
case DecInfObj of
false -> % This is in a choice with typefield components
{Name,RestFieldNames} =
(Type#type.def)#'ObjectClassFieldType'.fieldname,
Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
{TmpTerm,TempBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
emit([com,nl]),
#type{tablecinf=[{objfun,
#'Externaltypereference'{module=Xmod,
type=Xtype}}]} =
Type,
gen_dec_open_type(Erule, "ObjFun", {Xmod,Xtype},
'_', {'_',{Name,RestFieldNames},
'Result',TmpTerm,mandatory}),
emit([com,nl,
"{",{asis,Cname},",{Result,",TempBuf,"}}"]),
{[],PrevSt};
{"got objfun through args","ObjFun"} ->
%% this is when the generated code gots the
%% objfun though arguments on function
%% invocation.
if
Prop =:= mandatory ->
ok;
true ->
asn1ct_name:new(tmpterm),
asn1ct_name:new(tmpbytes),
emit([nl," {",{curr,tmpterm},", ",{curr,tmpbytes},"} ="])
end,
{Name,RestFieldNames} =
(Type#type.def)#'ObjectClassFieldType'.fieldname,
Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
asn1ct_imm:dec_code_gen(Imm, BytesVar),
emit([com,nl]),
#type{tablecinf=[{objfun,
#'Externaltypereference'{module=Xmod,
type=Xtype}}]} =
Type,
Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
TmpTerm = asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
if
Prop =:= mandatory ->
gen_dec_open_type(Erule, "ObjFun", {Xmod,Xtype},
'_', {'_',{Name,RestFieldNames},
Term,TmpTerm,Prop});
true ->
emit([" {"]),
gen_dec_open_type(Erule, "ObjFun", {Xmod,Xtype},
'_', {'_',{Name,RestFieldNames},
'_',TmpTerm,Prop}),
emit([",",nl,{curr,tmpbytes},"}"])
end,
{[],PrevSt};
_ ->
Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
asn1ct_imm:dec_code_gen(Imm, BytesVar),
RefedFieldName =
(Type#type.def)#'ObjectClassFieldType'.fieldname,
{[{Cname,RefedFieldName,
asn1ct_gen:mk_var(asn1ct_name:curr(term)),
asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
Prop}],PrevSt}
end
end;
gen_dec_line_special(Gen, Atype, TopType, Comp, DecInfObj) ->
case gen_dec_line_other(Gen, Atype, TopType, Comp) of
Fun when is_function(Fun, 1) ->
fun({BytesVar,PrevSt}) ->
Fun(BytesVar),
gen_dec_line_dec_inf(Gen,Comp, DecInfObj),
{[],PrevSt}
end;
Imm0 ->
{imm,Imm0,
fun(Imm, {BytesVar,PrevSt}) ->
asn1ct_imm:dec_code_gen(Imm, BytesVar),
gen_dec_line_dec_inf(Gen, Comp, DecInfObj),
{[],PrevSt}
end}
end.
gen_dec_line_dec_inf(Gen, Comp, DecInfObj) ->
#'ComponentType'{name=Cname} = Comp,
case DecInfObj of
{Cname,{_,_OSet,_UniqueFName,ValIndex}} ->
Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
ValueMatch = value_match(Gen, ValIndex,Term),
emit([",",nl,
"ObjFun = ",ValueMatch]);
_ ->
ok
end.
gen_dec_line_other(Erule, Atype, TopType, Comp) ->
#'ComponentType'{name=Cname,typespec=Type} = Comp,
case asn1ct_gen:type(Atype) of
#'Externaltypereference'{}=Etype ->
fun(BytesVar) ->
asn1ct_gen_per:gen_dec_external(Etype, BytesVar)
end;
{primitive,bif} ->
asn1ct_gen_per:gen_dec_imm(Erule, Type);
'ASN1_OPEN_TYPE' ->
case Type#type.def of
#'ObjectClassFieldType'{type=OpenType} ->
asn1ct_gen_per:gen_dec_imm(Erule, #type{def=OpenType});
_ ->
asn1ct_gen_per:gen_dec_imm(Erule, Type)
end;
{constructed,bif} ->
NewTypename = [Cname|TopType],
case Type#type.tablecinf of
[{objfun,_}|_R] ->
fun(BytesVar) ->
emit({"'dec_",asn1ct_gen:list2name(NewTypename),
"'(",BytesVar,", ObjFun)"})
end;
_ ->
fun(BytesVar) ->
emit({"'dec_",asn1ct_gen:list2name(NewTypename),
"'(",BytesVar,")"})
end
end
end.
gen_enc_choice(Erule, TopType, {Root,Exts}, Ext) ->
Constr = choice_constraint(Root),
gen_enc_choices(Root, Erule, TopType, 0, Constr, Ext) ++
gen_enc_choices(Exts, Erule, TopType, 0, ext, Ext);
gen_enc_choice(Erule, TopType, {Root,Exts,[]}, Ext) ->
gen_enc_choice(Erule, TopType, {Root,Exts}, Ext);
gen_enc_choice(Erule, TopType, Root, Ext) when is_list(Root) ->
Constr = choice_constraint(Root),
gen_enc_choices(Root, Erule, TopType, 0, Constr, Ext).
choice_constraint(L) ->
case length(L) of
0 -> [{'SingleValue',0}];
Len -> [{'ValueRange',{0,Len-1}}]
end.
gen_enc_choices([H|T], Erule, TopType, Pos, Constr, Ext) ->
#'ComponentType'{name=Cname,typespec=Type} = H,
Aligned = is_aligned(Erule),
EncObj =
case asn1ct_gen:get_constraint(Type#type.constraint,
componentrelation) of
no ->
case Type#type.tablecinf of
[{objfun,_}|_] ->
{"got objfun through args",{var,"ObjFun"}};
_ ->
false
end;
_ ->
{no_attr,{var,"ObjFun"}}
end,
DoExt = case Constr of
ext -> Ext;
_ -> noext
end,
Tag = case {Ext,Constr} of
{noext,_} ->
asn1ct_imm:per_enc_integer(Pos, Constr, Aligned);
{{ext,_,_},ext} ->
[{put_bits,1,1,[1]}|
asn1ct_imm:per_enc_small_number(Pos, Aligned)];
{{ext,_,_},_} ->
[{put_bits,0,1,[1]}|
asn1ct_imm:per_enc_integer(Pos, Constr, Aligned)]
end,
Body = gen_enc_line_imm(Erule, TopType, Cname, Type, {var,"ChoiceVal"},
EncObj, DoExt),
Imm = Tag ++ Body,
[{Cname,Imm}|gen_enc_choices(T, Erule, TopType, Pos+1, Constr, Ext)];
gen_enc_choices([], _, _, _, _, _) -> [].
%% Generate the code for CHOICE. If the CHOICE is extensible,
%% the structure of the generated code is as follows:
%%
%% case Bytes of
%% <<0:1,Bytes1/bitstring>> ->
%% Choice = <Decode INTEGER (0..LastRootChoice) from Bytes1>
%% case Choice of
%% 0 -> <Decode>;
%% :
%% LastRootChoice -> <Decode>
%% end;
%% <<1:1,Bytes1/bitstring>> ->
%% Choice = <Decode normally small number from Bytes1>
%% TmpVal = <Decode open type>
%% case Choice of
%% 0 -> <Decode TmpVal>;
%% :
%% LastExtension -> <Decode TmpVal>;
%% _ -> <Return TmpVal since the type is unknown>
%% end
%% end
%%
%% The return value from the generated function always looks like:
%% {{ChoiceTag,Value},RemainingBuffer}
%% where ChoiceTag will be 'asn1_ExtAlt' for an unknown extension.
%%
%% If the CHOICE is not extensible, the top-level case is omitted
%% and only the code in the first case arm is generated.
gen_dec_choice(Erule, TopType, CompList, {ext,_,_}=Ext) ->
{RootList,ExtList} = split_complist(CompList),
emit(["case Bytes of",nl]),
case RootList of
[] ->
ok;
[_|_] ->
emit(["<<0:1,Bytes1/bitstring>> ->",nl]),
asn1ct_name:new(bytes),
gen_dec_choice1(Erule, TopType, RootList, noext),
emit([";",nl,nl])
end,
emit(["<<1:1,Bytes1/bitstring>> ->",nl]),
asn1ct_name:clear(),
asn1ct_name:new(bytes),
asn1ct_name:new(bytes),
gen_dec_choice1(Erule, TopType, ExtList, Ext),
emit([nl,"end"]);
gen_dec_choice(Erule, TopType, CompList, noext) ->
gen_dec_choice1(Erule, TopType, CompList, noext).
split_complist({Root1,Ext,Root2}) ->
{Root1++Root2,Ext};
split_complist({_,_}=CompList) ->
CompList.
gen_dec_choice1(Erule, TopType, CompList, noext=Ext) ->
emit_getchoice(Erule, CompList, Ext),
emit(["case Choice of",nl]),
Pre = {safe,fun(St) ->
{asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
fun() -> St end}
end},
gen_dec_choice2(Erule, TopType, CompList, Pre),
emit([nl,"end"]);
gen_dec_choice1(Erule, TopType, CompList, {ext,_,_}=Ext) ->
emit_getchoice(Erule, CompList, Ext),
Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
emit(["begin",nl]),
BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
{Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
emit([nl,
"end,",nl,
"case Choice of",nl]),
Pre = {safe,fun(St) ->
emit(["{TmpVal,_} = "]),
{Dst,
fun() ->
emit([",",nl,
"{TmpVal,",DstBuf,"}"]),
St
end}
end},
gen_dec_choice2(Erule, TopType, CompList, Pre),
case CompList of
[] -> ok;
[_|_] -> emit([";",nl])
end,
emit(["_ ->",nl,
"{{asn1_ExtAlt,",Dst,"},",DstBuf,"}",nl,
"end"]).
emit_getchoice(Erule, CompList, Ext) ->
Al = is_aligned(Erule),
Imm = case {Ext,CompList} of
{noext,[_]} ->
{value,0};
{noext,_} ->
asn1ct_imm:per_dec_constrained(0, length(CompList)-1, Al);
{{ext,_,_},_} ->
asn1ct_imm:per_dec_normally_small_number(Al)
end,
emit(["{Choice,",{curr,bytes},"} = ",nl]),
BytesVar = asn1ct_gen:mk_var(asn1ct_name:prev(bytes)),
asn1ct_imm:dec_code_gen(Imm, BytesVar),
emit([com,nl]).
gen_dec_choice2(Erule,TopType,L,Ext) ->
gen_dec_choice2(Erule, TopType, L, 0, [], Ext).
gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Pre) ->
#'ComponentType'{name=Cname,typespec=Type} = H0,
H = H0#'ComponentType'{prop=mandatory},
emit([Sep0,Pos," ->",nl]),
case Type#type.def of
#'ObjectClassFieldType'{type={typefield,_}} ->
emit("{Cname,{Val,NewBytes}} = begin\n"),
gen_dec_choice_line(Erule, TopType, H, Pre),
emit([nl,
"end,",nl,
"{{Cname,Val},NewBytes}"]);
_ ->
emit("{Val,NewBytes} = begin\n"),
gen_dec_choice_line(Erule, TopType, H, Pre),
emit([nl,
"end,",nl,
"{{",{asis,Cname},",Val},NewBytes}"])
end,
Sep = [";",nl],
gen_dec_choice2(Erule, TopType, T, Pos+1, Sep, Pre);
gen_dec_choice2(_, _, [], _, _, _) -> ok.
get_input_vars(Val, I, N) ->
L = get_input_vars_1(Val, I, N),
lists:join(",", L).
get_input_vars_1(_Val, _I, 0) ->
[];
get_input_vars_1(Val, I, N) ->
[get_input_var(Val, I)|get_input_vars_1(Val, I+1, N-1)].
get_input_var(Val, I) ->
lists:flatten(io_lib:format("element(~w, ~s)", [I+1,Val])).
emit_extaddgroupTerms(VarSeries,[_]) ->
asn1ct_name:new(VarSeries),
emit({curr,VarSeries}),
ok;
emit_extaddgroupTerms(VarSeries,[_|Rest]) ->
asn1ct_name:new(VarSeries),
emit({{curr,VarSeries},","}),
emit_extaddgroupTerms(VarSeries,Rest);
emit_extaddgroupTerms(_,[]) ->
ok.
flat_complist({Rl1,El,Rl2}) -> Rl1 ++ El ++ Rl2;
flat_complist({Rl,El}) -> Rl ++ El;
flat_complist(CompList) -> CompList.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Convert all componentTypes following 'ExtensionAdditionGroup'
%% up to the matching 'ExtensionAdditionGroupEnd' into one componentType
%% of type SEQUENCE with the componentTypes as components.
%%
wrap_extensionAdditionGroups(ExtCompList, ExtGroupPosLen) ->
wrap_eags(ExtCompList, ExtGroupPosLen, 0, 0).
wrap_eags([{'ExtensionAdditionGroup',_Number}|T0],
[{ActualPos,_,_}|Gs], _ExtAddGroupDiff, ExtGroupNum) ->
{ExtGroupCompList,['ExtensionAdditionGroupEnd'|T]} =
lists:splitwith(fun(#'ComponentType'{}) -> true;
(_) -> false
end, T0),
Name = list_to_atom(lists:concat(["ExtAddGroup",ExtGroupNum+1])),
Seq = #type{def=#'SEQUENCE'{extaddgroup=ExtGroupNum+1,
components=ExtGroupCompList}},
Comp = #'ComponentType'{name=Name,
typespec=Seq,
textual_order=ActualPos,
prop='OPTIONAL'},
[Comp|wrap_eags(T, Gs, length(ExtGroupCompList)-1, ExtGroupNum+1)];
wrap_eags([#'ComponentType'{textual_order=Tord}=H|T],
ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)
when is_integer(Tord) ->
Comp = H#'ComponentType'{textual_order=Tord - ExtAddGroupDiff},
[Comp|wrap_eags(T, ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)];
wrap_eags([H|T], ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum) ->
[H|wrap_eags(T, ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)];
wrap_eags([], _, _, _) ->
[].
value_match(#gen{pack=record}, VIs, Value) ->
value_match_rec(VIs, Value);
value_match(#gen{pack=map}, VIs, Value) ->
value_match_map(VIs, Value).
value_match_rec([], Value) ->
Value;
value_match_rec([{VI,_}|VIs], Value0) ->
Value = value_match_rec(VIs, Value0),
lists:concat(["element(",VI,", ",Value,")"]).
value_match_map([], Value) ->
Value;
value_match_map([{_,Name}|VIs], Value0) ->
Value = value_match_map(VIs, Value0),
lists:concat(["maps:get(",Name,", ",Value,")"]).
enc_dig_out_value(_Gen, [], Value) ->
{[],Value};
enc_dig_out_value(#gen{pack=record}=Gen, [{N,_}|T], Value) ->
{Imm0,Dst0} = enc_dig_out_value(Gen, T, Value),
{Imm,Dst} = asn1ct_imm:enc_element(N, Dst0),
{Imm0++Imm,Dst};
enc_dig_out_value(#gen{pack=map}, [{N,'ASN1_top'}], _Value) ->
{[],{var,lists:concat(["Input@",N-1])}};
enc_dig_out_value(#gen{pack=map}=Gen, [{_,Name}|T], Value) ->
{Imm0,Dst0} = enc_dig_out_value(Gen, T, Value),
{Imm,Dst} = asn1ct_imm:enc_maps_get(Name, Dst0),
{Imm0++Imm,Dst}.
make_var(Base) ->
{var,atom_to_list(asn1ct_gen:mk_var(asn1ct_name:curr(Base)))}.
attribute_comment(InnerType, TextPos, Cname) ->
DispType = case InnerType of
#'Externaltypereference'{type=T} -> T;
IT when is_tuple(IT) -> element(2,IT);
_ -> InnerType
end,
Comment = ["attribute ",Cname,"(",TextPos,") with type ",DispType],
lists:concat(Comment).