%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2017. 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_gen_ber_bin_v2).
%% Generate erlang module which handles (PER) encode and decode for
%% all types in an ASN.1 module
-include("asn1_records.hrl").
-export([decode_class/1]).
-export([gen_encode/2,gen_encode/3,gen_decode/2,gen_decode/3]).
-export([gen_encode_prim/4]).
-export([gen_dec_prim/3]).
-export([gen_objectset_code/2, gen_obj_code/3]).
-export([encode_tag_val/3]).
-export([gen_inc_decode/2,gen_decode_selected/3]).
-export([extaddgroup2sequence/1]).
-export([dialyzer_suppressions/1]).
-import(asn1ct_gen, [emit/1]).
%% The encoding of class of tag bits 8 and 7
-define(UNIVERSAL, 0).
-define(APPLICATION, 16#40).
-define(CONTEXT, 16#80).
-define(PRIVATE, 16#C0).
%% Primitive or constructed encoding % bit 6
-define(PRIMITIVE, 0).
-define(CONSTRUCTED, 2#00100000).
-define(T_ObjectDescriptor, ?UNIVERSAL bor ?PRIMITIVE bor 7).
%% Restricted character string types
-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
%%===============================================================================
%%===============================================================================
%%===============================================================================
%% Generate ENCODING
%%===============================================================================
%%===============================================================================
%%===============================================================================
dialyzer_suppressions(_) ->
case asn1ct:use_legacy_types() of
false -> ok;
true -> suppress({ber,encode_bit_string,4})
end,
suppress({ber,decode_selective,2}),
emit([" ok.",nl]).
suppress({M,F,A}=MFA) ->
case asn1ct_func:is_used(MFA) of
false ->
ok;
true ->
Args = [lists:concat(["element(",I,", Arg)"]) || I <- lists:seq(1, A)],
emit([" ",{call,M,F,Args},com,nl])
end.
%%===============================================================================
%% encode #{typedef, {pos, name, typespec}}
%%===============================================================================
gen_encode(Erules, #typedef{}=D) ->
gen_encode_user(Erules, #typedef{}=D, true).
%%===============================================================================
%% encode #{type, {tag, def, constraint}}
%%===============================================================================
gen_encode(Erules,Typename,Type) when is_record(Type,type) ->
InnerType = asn1ct_gen:get_inner(Type#type.def),
ObjFun =
case lists:keysearch(objfun,1,Type#type.tablecinf) of
{value,{_,_Name}} ->
", ObjFun";
false ->
""
end,
case asn1ct_gen:type(InnerType) of
{constructed,bif} ->
Func = {asis,enc_func(asn1ct_gen:list2name(Typename))},
emit([nl,nl,nl,"%%================================",nl,
"%% ",asn1ct_gen:list2name(Typename),nl,
"%%================================",nl,
Func,"(Val, TagIn",ObjFun,") ->",nl,
" "]),
asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
_ ->
true
end;
%%===============================================================================
%% encode ComponentType
%%===============================================================================
gen_encode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) ->
NewTname = [Cname|Tname],
%% The tag is set to [] to avoid that it is
%% taken into account twice, both as a component/alternative (passed as
%% argument to the encode decode function and within the encode decode
%% function it self.
NewType = Type#type{tag=[]},
gen_encode(Erules,NewTname,NewType).
gen_encode_user(Erules, #typedef{}=D, Wrapper) ->
Typename = [D#typedef.name],
Type = D#typedef.typespec,
InnerType = asn1ct_gen:get_inner(Type#type.def),
emit([nl,nl,"%%================================"]),
emit([nl,"%% ",Typename]),
emit([nl,"%%================================",nl]),
FuncName = {asis,enc_func(asn1ct_gen:list2name(Typename))},
case Wrapper of
true ->
%% This is a top-level type. Generate an 'enc_Type'/1
%% wrapper.
OTag = Type#type.tag,
Tag0 = [encode_tag_val(decode_class(Class), Form, Number) ||
#tag{class=Class,form=Form,number=Number} <- OTag],
Tag = lists:reverse(Tag0),
emit([FuncName,"(Val) ->",nl,
" ",FuncName,"(Val, ",{asis,Tag},").",nl,nl]);
false ->
ok
end,
emit([FuncName,"(Val, TagIn) ->",nl]),
CurrentMod = get(currmod),
case asn1ct_gen:type(InnerType) of
{constructed,bif} ->
asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
{primitive,bif} ->
gen_encode_prim(ber,Type,"TagIn","Val"),
emit([".",nl]);
#'Externaltypereference'{module=CurrentMod,type=Etype} ->
emit([" ",{asis,enc_func(Etype)},"(Val, TagIn).",nl]);
#'Externaltypereference'{module=Emod,type=Etype} ->
emit([" ",{asis,Emod},":",{asis,enc_func(Etype)},
"(Val, TagIn).",nl]);
'ASN1_OPEN_TYPE' ->
emit(["%% OPEN TYPE",nl]),
gen_encode_prim(ber,
Type#type{def='ASN1_OPEN_TYPE'},
"TagIn","Val"),
emit([".",nl])
end.
gen_encode_prim(_Erules, #type{}=D, DoTag, Value) ->
BitStringConstraint = get_size_constraint(D#type.constraint),
MaxBitStrSize = case BitStringConstraint of
[] -> none;
{_,'MAX'} -> none;
{_,Max} -> Max;
Max when is_integer(Max) -> Max
end,
asn1ct_name:new(enumval),
Type = case D#type.def of
'OCTET STRING' -> restricted_string;
'ObjectDescriptor'-> restricted_string;
'NumericString' -> restricted_string;
'TeletexString' -> restricted_string;
'T61String' -> restricted_string;
'VideotexString' -> restricted_string;
'GraphicString' -> restricted_string;
'VisibleString' -> restricted_string;
'GeneralString' -> restricted_string;
'PrintableString' -> restricted_string;
'IA5String' -> restricted_string;
'UTCTime' -> restricted_string;
'GeneralizedTime' -> restricted_string;
Other -> Other
end,
case Type of
restricted_string ->
call(encode_restricted_string, [Value,DoTag]);
'BOOLEAN' ->
call(encode_boolean, [Value,DoTag]);
'INTEGER' ->
call(encode_integer, [Value,DoTag]);
{'INTEGER',NamedNumberList} ->
call(encode_integer, [Value,{asis,NamedNumberList}, DoTag]);
{'ENUMERATED',NamedNumberList={_,_}} ->
emit(["case ",Value," of",nl]),
emit_enc_enumerated_cases(NamedNumberList,DoTag);
{'ENUMERATED',NamedNumberList} ->
emit(["case ",Value," of",nl]),
emit_enc_enumerated_cases(NamedNumberList,DoTag);
'REAL' ->
asn1ct_name:new(realval),
asn1ct_name:new(realsize),
emit(["begin",nl,
{curr,realval}," = ",
{call,real_common,ber_encode_real,[Value]},com,nl,
{curr,realsize}," = ",
{call,erlang,byte_size,[{curr,realval}]},com,nl,
{call,ber,encode_tags,
[DoTag,{curr,realval},{curr,realsize}]},nl,
"end"]);
{'BIT STRING',[]} ->
case asn1ct:use_legacy_types() of
false when MaxBitStrSize =:= none ->
call(encode_unnamed_bit_string, [Value,DoTag]);
false ->
call(encode_unnamed_bit_string,
[{asis,MaxBitStrSize},Value,DoTag]);
true ->
call(encode_bit_string,
[{asis,BitStringConstraint},Value,
{asis,[]},DoTag])
end;
{'BIT STRING',NamedNumberList} ->
case asn1ct:use_legacy_types() of
false when MaxBitStrSize =:= none ->
call(encode_named_bit_string,
[Value,{asis,NamedNumberList},DoTag]);
false ->
call(encode_named_bit_string,
[{asis,MaxBitStrSize},Value,
{asis,NamedNumberList},DoTag]);
true ->
call(encode_bit_string,
[{asis,BitStringConstraint},Value,
{asis,NamedNumberList},DoTag])
end;
'NULL' ->
call(encode_null, [Value,DoTag]);
'OBJECT IDENTIFIER' ->
call(encode_object_identifier, [Value,DoTag]);
'RELATIVE-OID' ->
call(encode_relative_oid, [Value,DoTag]);
'UniversalString' ->
call(encode_universal_string, [Value,DoTag]);
'UTF8String' ->
call(encode_UTF8_string, [Value,DoTag]);
'BMPString' ->
call(encode_BMP_string, [Value,DoTag]);
'ASN1_OPEN_TYPE' ->
call(encode_open_type, [Value,DoTag])
end.
emit_enc_enumerated_cases({L1,L2}, Tags) ->
emit_enc_enumerated_cases(L1++L2, Tags, ext);
emit_enc_enumerated_cases(L, Tags) ->
emit_enc_enumerated_cases(L, Tags, noext).
emit_enc_enumerated_cases([{EnumName,EnumVal}|T], Tags, Ext) ->
{Bytes,Len} = encode_integer(EnumVal),
emit([{asis,EnumName}," -> ",
{call,ber,encode_tags,[Tags,{asis,Bytes},Len]},";",nl]),
emit_enc_enumerated_cases(T, Tags, Ext);
emit_enc_enumerated_cases([], _Tags, _Ext) ->
%% FIXME: Should extension be handled?
emit([{curr,enumval}," -> exit({error,{asn1, {enumerated_not_in_range,",{curr, enumval},"}}})"]),
emit([nl,"end"]).
encode_integer(Val) ->
Bytes =
if
Val >= 0 ->
encode_integer_pos(Val, []);
true ->
encode_integer_neg(Val, [])
end,
{Bytes,length(Bytes)}.
encode_integer_pos(0, [B|_Acc]=L) when B < 128 ->
L;
encode_integer_pos(N, Acc) ->
encode_integer_pos((N bsr 8), [N band 16#ff| Acc]).
encode_integer_neg(-1, [B1|_T]=L) when B1 > 127 ->
L;
encode_integer_neg(N, Acc) ->
encode_integer_neg(N bsr 8, [N band 16#ff|Acc]).
%%===============================================================================
%%===============================================================================
%%===============================================================================
%% Generate DECODING
%%===============================================================================
%%===============================================================================
%%===============================================================================
%%===============================================================================
%% decode #{typedef, {pos, name, typespec}}
%%===============================================================================
gen_decode(Erules,Type) when is_record(Type,typedef) ->
Def = Type#typedef.typespec,
InnerTag = Def#type.tag ,
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- InnerTag],
FuncName0 =
case {asn1ct:get_gen_state_field(active),
asn1ct:get_gen_state_field(prefix)} of
{true,Pref} ->
%% prevent duplicated function definitions
case asn1ct:current_sindex() of
I when is_integer(I), I > 0 ->
[Pref,Type#typedef.name,"_",I];
_->
[Pref,Type#typedef.name]
end;
{_,_} ->
["dec_",Type#typedef.name]
end,
FuncName = {asis,list_to_atom(lists:concat(FuncName0))},
emit([nl,nl,
FuncName,"(Tlv) ->",nl,
" ",FuncName,"(Tlv, ",{asis,Tag},").",nl,nl,
FuncName,"(Tlv, TagIn) ->",nl]),
gen_decode_user(Erules,Type).
gen_inc_decode(Erules,Type) when is_record(Type,typedef) ->
Prefix = asn1ct:get_gen_state_field(prefix),
Suffix = asn1ct_gen:index2suffix(asn1ct:current_sindex()),
FuncName0 = [Prefix,Type#typedef.name,Suffix],
FuncName = {asis,list_to_atom(lists:concat(FuncName0))},
emit([nl,nl,
FuncName,"(Tlv, TagIn) ->",nl]),
gen_decode_user(Erules,Type).
%% gen_decode_selected exported function for selected decode
gen_decode_selected(Erules,Type,FuncName) ->
emit([FuncName,"(Bin) ->",nl]),
Patterns = asn1ct:read_config_data(partial_decode),
Pattern =
case lists:keysearch(FuncName,1,Patterns) of
{value,{_,P}} -> P;
false -> exit({error,{internal,no_pattern_saved}})
end,
emit([" case ",{call,ber,decode_selective,
[{asis,Pattern},"Bin"]}," of",nl,
" {ok,Bin2} when is_binary(Bin2) ->",nl,
" {Tlv,_} = ", {call,ber,ber_decode_nif,["Bin2"]},com,nl]),
emit("{ok,"),
gen_decode_selected_type(Erules,Type),
emit(["};",nl," Err -> exit({error,{selective_decode,Err}})",nl,
" end.",nl]).
gen_decode_selected_type(_Erules,TypeDef) ->
Def = TypeDef#typedef.typespec,
InnerType = asn1ct_gen:get_inner(Def#type.def),
BytesVar = "Tlv",
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number ||
X <- Def#type.tag],
case asn1ct_gen:type(InnerType) of
'ASN1_OPEN_TYPE' ->
asn1ct_name:new(len),
gen_dec_prim(Def#type{def='ASN1_OPEN_TYPE'},
BytesVar, Tag);
{primitive,bif} ->
asn1ct_name:new(len),
gen_dec_prim(Def, BytesVar, Tag);
{constructed,bif} ->
TopType = case TypeDef#typedef.name of
A when is_atom(A) -> [A];
N -> N
end,
DecFunName = lists:concat(["'",dec,"_",
asn1ct_gen:list2name(TopType),"'"]),
emit([DecFunName,"(",BytesVar,
", ",{asis,Tag},")"]);
TheType ->
DecFunName = mkfuncname(TheType,dec),
emit([DecFunName,"(",BytesVar,
", ",{asis,Tag},")"])
end.
%%===============================================================================
%% decode #{type, {tag, def, constraint}}
%%===============================================================================
%% This gen_decode is called by the gen_decode/3 that decodes
%% ComponentType and the type of a SEQUENCE OF/SET OF for an inner
%% type of an exclusive decode top type..
gen_decode(Erules,Typename,Type) when is_record(Type,type) ->
InnerType = asn1ct_gen:get_inner(Type#type.def),
FunctionName =
case asn1ct:get_gen_state_field(active) of
true ->
Pattern = asn1ct:get_gen_state_field(namelist),
Suffix =
case asn1ct:maybe_saved_sindex(Typename,Pattern) of
I when is_integer(I),I>0 ->
lists:concat(["_",I]);
_ -> ""
end,
lists:concat(["'dec-inc-",
asn1ct_gen:list2name(Typename),Suffix]);
_ ->
lists:concat(["'dec_",asn1ct_gen:list2name(Typename)])
end,
case asn1ct_gen:type(InnerType) of
{constructed,bif} ->
ObjFun =
case Type#type.tablecinf of
[{objfun,_}|_R] ->
", ObjFun";
_ ->
""
end,
emit([FunctionName,"'(Tlv, TagIn",ObjFun,") ->",nl]),
asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
Rec when is_record(Rec,'Externaltypereference') ->
case {Typename,asn1ct:get_gen_state_field(namelist)} of
{[Cname|_],[{Cname,_}|_]} -> %%
%% This referenced type must only be generated
%% once as incomplete partial decode. Therefore we
%% have to check whether this function already is
%% generated.
case asn1ct:is_function_generated(Typename) of
true ->
ok;
_ ->
asn1ct:generated_refed_func(Typename),
#'Externaltypereference'{module=M,type=Name}=Rec,
TypeDef = asn1_db:dbget(M,Name),
gen_decode(Erules,TypeDef)
end;
_ ->
true
end;
_ ->
true
end;
%%===============================================================================
%% decode ComponentType
%%===============================================================================
gen_decode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) ->
NewTname = [Cname|Tname],
%% The tag is set to [] to avoid that it is taken into account
%% twice, both as a component/alternative (passed as argument to
%% the encode/decode function), and within the encode decode
%% function itself.
NewType = Type#type{tag=[]},
case {asn1ct:get_gen_state_field(active),
asn1ct:get_tobe_refed_func(NewTname)} of
{true,{_,NameList}} ->
asn1ct:update_gen_state(namelist,NameList),
%% remove to gen_refed_funcs list from tobe_refed_funcs later
gen_decode(Erules,NewTname,NewType);
{No,_} when No == false; No == undefined ->
gen_decode(Erules,NewTname,NewType);
_ ->
ok
end.
gen_decode_user(Erules,D) when is_record(D,typedef) ->
Typename = [D#typedef.name],
Def = D#typedef.typespec,
InnerType = asn1ct_gen:get_inner(Def#type.def),
BytesVar = "Tlv",
case asn1ct_gen:type(InnerType) of
'ASN1_OPEN_TYPE' ->
asn1ct_name:new(len),
gen_dec_prim(Def#type{def='ASN1_OPEN_TYPE'},
BytesVar, {string,"TagIn"}),
emit([".",nl,nl]);
{primitive,bif} ->
asn1ct_name:new(len),
gen_dec_prim(Def, BytesVar, {string,"TagIn"}),
emit([".",nl,nl]);
{constructed,bif} ->
asn1ct:update_namelist(D#typedef.name),
asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
TheType ->
DecFunName = mkfuncname(TheType,dec),
emit([DecFunName,"(",BytesVar,
", TagIn).",nl,nl])
end.
gen_dec_prim(Att, BytesVar, DoTag) ->
Typename = Att#type.def,
Constraint = get_size_constraint(Att#type.constraint),
IntConstr = int_constr(Att#type.constraint),
NewTypeName = case Typename of
'NumericString' -> restricted_string;
'TeletexString' -> restricted_string;
'T61String' -> restricted_string;
'VideotexString' -> restricted_string;
'GraphicString' -> restricted_string;
'VisibleString' -> restricted_string;
'GeneralString' -> restricted_string;
'PrintableString' -> restricted_string;
'IA5String' -> restricted_string;
'ObjectDescriptor'-> restricted_string;
'UTCTime' -> restricted_string;
'GeneralizedTime' -> restricted_string;
'OCTET STRING' ->
case asn1ct:use_legacy_types() of
true -> restricted_string;
false -> Typename
end;
_ -> Typename
end,
TagStr = case DoTag of
{string,Tag1} -> Tag1;
_ when is_list(DoTag) -> {asis,DoTag}
end,
case NewTypeName of
'BOOLEAN'->
call(decode_boolean, [BytesVar,TagStr]);
'INTEGER' ->
check_constraint(decode_integer, [BytesVar,TagStr],
IntConstr,
identity,
identity);
{'INTEGER',NNL} ->
check_constraint(decode_integer,
[BytesVar,TagStr],
IntConstr,
identity,
fun(Val) ->
asn1ct_name:new(val),
emit([{curr,val}," = "]),
Val(),
emit([com,nl,
{call,ber,number2name,
[{curr,val},{asis,NNL}]}])
end);
{'ENUMERATED',NNL} ->
gen_dec_enumerated(BytesVar, NNL, TagStr);
'REAL' ->
asn1ct_name:new(tmpbuf),
emit(["begin",nl,
{curr,tmpbuf}," = ",
{call,ber,match_tags,[BytesVar,TagStr]},com,nl,
{call,real_common,decode_real,[{curr,tmpbuf}]},nl,
"end",nl]);
{'BIT STRING',NNL} ->
gen_dec_bit_string(BytesVar, Constraint, NNL, TagStr);
'NULL' ->
call(decode_null, [BytesVar,TagStr]);
'OBJECT IDENTIFIER' ->
call(decode_object_identifier, [BytesVar,TagStr]);
'RELATIVE-OID' ->
call(decode_relative_oid, [BytesVar,TagStr]);
'OCTET STRING' ->
check_constraint(decode_octet_string, [BytesVar,TagStr],
Constraint, {erlang,byte_size}, identity);
restricted_string ->
check_constraint(decode_restricted_string, [BytesVar,TagStr],
Constraint,
{erlang,byte_size},
fun(Val) ->
emit("binary_to_list("),
Val(),
emit(")")
end);
'UniversalString' ->
check_constraint(decode_universal_string, [BytesVar,TagStr],
Constraint, {erlang,length}, identity);
'UTF8String' ->
call(decode_UTF8_string, [BytesVar,TagStr]);
'BMPString' ->
check_constraint(decode_BMP_string, [BytesVar,TagStr],
Constraint, {erlang,length}, identity);
'ASN1_OPEN_TYPE' ->
call(decode_open_type_as_binary, [BytesVar,TagStr])
end.
%% Simplify an integer constraint so that we can efficiently test it.
-spec int_constr(term()) -> [] | {integer(),integer()|'MAX'}.
int_constr(C) ->
case asn1ct_imm:effective_constraint(integer, C) of
[{_,[]}] ->
%% Extension - ignore constraint.
[];
[{'ValueRange',{'MIN',_}}] ->
%% Tricky to implement efficiently - ignore it.
[];
[{'ValueRange',{_,_}=Range}] ->
Range;
[{'SingleValue',Sv}] ->
Sv;
[] ->
[]
end.
gen_dec_bit_string(BytesVar, _Constraint, [_|_]=NNL, TagStr) ->
call(decode_named_bit_string,
[BytesVar,{asis,NNL},TagStr]);
gen_dec_bit_string(BytesVar, Constraint, [], TagStr) ->
case asn1ct:get_bit_string_format() of
compact ->
check_constraint(decode_compact_bit_string,
[BytesVar,TagStr],
Constraint,
{ber,compact_bit_string_size},
identity);
legacy ->
check_constraint(decode_native_bit_string,
[BytesVar,TagStr],
Constraint,
{erlang,bit_size},
fun(Val) ->
asn1ct_name:new(val),
emit([{curr,val}," = "]),
Val(),
emit([com,nl,
{call,ber,native_to_legacy_bit_string,
[{curr,val}]}])
end);
bitstring ->
check_constraint(decode_native_bit_string,
[BytesVar,TagStr],
Constraint,
{erlang,bit_size},
identity)
end.
check_constraint(F, Args, Constr, PreConstr0, ReturnVal0) ->
PreConstr = case PreConstr0 of
identity ->
fun(V) -> V end;
{Mod,Name} ->
fun(V) ->
asn1ct_name:new(c),
emit([{curr,c}," = ",
{call,Mod,Name,[V]},com,nl]),
{curr,c}
end
end,
ReturnVal = case ReturnVal0 of
identity -> fun(Val) -> Val() end;
_ -> ReturnVal0
end,
case Constr of
[] when ReturnVal0 =:= identity ->
%% No constraint, no complications.
call(F, Args);
[] ->
%% No constraint, but the return value could consist
%% of more than one statement.
emit(["begin",nl]),
ReturnVal(fun() -> call(F, Args) end),
emit([nl,
"end",nl]);
_ ->
%% There is a constraint.
asn1ct_name:new(val),
emit(["begin",nl,
{curr,val}," = ",{call,ber,F,Args},com,nl]),
PreVal0 = asn1ct_gen:mk_var(asn1ct_name:curr(val)),
PreVal = PreConstr(PreVal0),
emit("if "),
case Constr of
{Min,Max} ->
emit([{asis,Min}," =< ",PreVal,", ",
PreVal," =< ",{asis,Max}]);
Sv when is_integer(Sv) ->
emit([PreVal," =:= ",{asis,Sv}])
end,
emit([" ->",nl]),
ReturnVal(fun() -> emit(PreVal0) end),
emit([";",nl,
"true ->",nl,
"exit({error,{asn1,bad_range}})",nl,
"end",nl,
"end"])
end.
gen_dec_enumerated(BytesVar, NNL0, TagStr) ->
asn1ct_name:new(enum),
emit(["case ",
{call,ber,decode_integer,[BytesVar,TagStr]},
" of",nl]),
NNL = case NNL0 of
{L1,L2} ->
L1 ++ L2 ++ [accept];
[_|_] ->
NNL0 ++ [error]
end,
gen_dec_enumerated_1(NNL),
emit("end").
gen_dec_enumerated_1([accept]) ->
asn1ct_name:new(default),
emit([{curr,default}," -> {asn1_enum,",{curr,default},"}",nl]);
gen_dec_enumerated_1([error]) ->
asn1ct_name:new(default),
emit([{curr,default}," -> exit({error,{asn1,{illegal_enumerated,",
{curr,default},"}}})",nl]);
gen_dec_enumerated_1([{V,K}|T]) ->
emit([{asis,K}," -> ",{asis,V},";",nl]),
gen_dec_enumerated_1(T).
%% Object code generating for encoding and decoding
%% ------------------------------------------------
gen_obj_code(Erules,_Module,Obj) when is_record(Obj,typedef) ->
ObjName = Obj#typedef.name,
Def = Obj#typedef.typespec,
#'Externaltypereference'{module=M,type=ClName} = Def#'Object'.classname,
Class = asn1_db:dbget(M,ClName),
{object,_,Fields} = Def#'Object'.def,
emit([nl,nl,nl,
"%%================================",nl,
"%% ",ObjName,nl,
"%%================================",nl]),
EncConstructed =
gen_encode_objectfields(ClName,get_class_fields(Class),
ObjName,Fields,[]),
emit(nl),
gen_encode_constr_type(Erules,EncConstructed),
emit(nl),
DecConstructed =
gen_decode_objectfields(ClName,get_class_fields(Class),
ObjName,Fields,[]),
emit(nl),
gen_decode_constr_type(Erules,DecConstructed),
emit_tlv_format_function().
gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
ObjName,ObjectFields,ConstrAcc) ->
EmitFuncClause =
fun(Arg) ->
emit([{asis,enc_func(ObjName)},"(",{asis,Name},
", ",Arg,", _RestPrimFieldName) ->",nl])
end,
MaybeConstr=
case {get_object_field(Name,ObjectFields),OptOrMand} of
{false,'OPTIONAL'} ->
EmitFuncClause("Val"),
emit([" {Val,0}"]),
[];
{false,{'DEFAULT',DefaultType}} ->
EmitFuncClause("Val"),
gen_encode_default_call(ClassName,Name,DefaultType);
{{Name,TypeSpec},_} ->
%% A specified field owerwrites any 'DEFAULT' or
%% 'OPTIONAL' field in the class
EmitFuncClause("Val"),
gen_encode_field_call(ObjName,Name,TypeSpec)
end,
case more_genfields(Rest) of
true ->
emit([";",nl]);
false ->
emit([".",nl])
end,
gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,
MaybeConstr++ConstrAcc);
gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
ObjName,ObjectFields,ConstrAcc) ->
CurrentMod = get(currmod),
EmitFuncClause =
fun(Args) ->
emit([{asis,enc_func(ObjName)},"(",{asis,Name},
", ",Args,") ->",nl])
end,
case {get_object_field(Name,ObjectFields),OptOrMand} of
{false,'OPTIONAL'} ->
EmitFuncClause("_,_"),
emit([" exit({error,{'use of missing field in object', ",{asis,Name},
"}})"]);
{false,{'DEFAULT',_DefaultObject}} ->
exit({error,{asn1,{"not implemented yet",Name}}});
{{Name,#'Externalvaluereference'{module=CurrentMod,
value=TypeName}},_} ->
EmitFuncClause(" Val, [H|T]"),
emit([indent(3),{asis,enc_func(TypeName)},"(H, Val, T)"]);
{{Name,#'Externalvaluereference'{module=M,value=TypeName}},_} ->
EmitFuncClause(" Val, [H|T]"),
emit([indent(3),{asis,M},":",{asis,enc_func(TypeName)},
"(H, Val, T)"]);
{{Name,#typedef{name=TypeName}},_} when is_atom(TypeName) ->
EmitFuncClause(" Val, [H|T]"),
emit([indent(3),{asis,enc_func(TypeName)},"(H, Val, T)"])
end,
case more_genfields(Rest) of
true ->
emit([";",nl]);
false ->
emit([".",nl])
end,
gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
gen_encode_objectfields(ClassName,[_C|Cs],O,OF,Acc) ->
gen_encode_objectfields(ClassName,Cs,O,OF,Acc);
gen_encode_objectfields(_,[],_,_,Acc) ->
Acc.
gen_encode_constr_type(Erules,[TypeDef|Rest]) when is_record(TypeDef,typedef) ->
case is_already_generated(enc,TypeDef#typedef.name) of
true -> ok;
false -> gen_encode_user(Erules, TypeDef, false)
end,
gen_encode_constr_type(Erules,Rest);
gen_encode_constr_type(_,[]) ->
ok.
gen_encode_field_call(_ObjName,_FieldName,
#'Externaltypereference'{module=M,type=T}) ->
CurrentMod = get(currmod),
TDef = asn1_db:dbget(M,T),
Def = TDef#typedef.typespec,
OTag = Def#type.tag,
Tag = [encode_tag_val(decode_class(X#tag.class),
X#tag.form,X#tag.number)||
X <- OTag],
if
M == CurrentMod ->
emit([" ",{asis,enc_func(T)},"(Val, ",{asis,Tag},")"]),
[];
true ->
emit([" ",{asis,M},":",{asis,enc_func(T)},
"(Val, ",{asis,Tag},")"]),
[]
end;
gen_encode_field_call(ObjName,FieldName,Type) ->
Def = Type#typedef.typespec,
OTag = Def#type.tag,
Tag = [encode_tag_val(decode_class(X#tag.class),
X#tag.form,X#tag.number)||
X <- OTag],
case Type#typedef.name of
{primitive,bif} -> %tag should be the primitive tag
gen_encode_prim(ber,Def,{asis,lists:reverse(Tag)},
"Val"),
[];
{constructed,bif} ->
Name = lists:concat([ObjName,'_',FieldName]),
emit([" ",{asis,enc_func(Name)},"(Val,",{asis,Tag},")"]),
[Type#typedef{name=list_to_atom(Name)}];
{ExtMod,TypeName} ->
emit([" ",{asis,ExtMod},":",{asis,enc_func(TypeName)},
"(Val,",{asis,Tag},")"]),
[];
TypeName ->
emit([" ",{asis,enc_func(TypeName)},
"(Val,",{asis,Tag},")"]),
[]
end.
gen_encode_default_call(ClassName,FieldName,Type) ->
CurrentMod = get(currmod),
InnerType = asn1ct_gen:get_inner(Type#type.def),
OTag = Type#type.tag,
Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
case asn1ct_gen:type(InnerType) of
{constructed,bif} ->
Name = lists:concat([ClassName,'_',FieldName]),
emit([" ",{asis,enc_func(Name)},
"(Val, ",{asis,Tag},")"]),
[#typedef{name=list_to_atom(Name),typespec=Type}];
{primitive,bif} ->
gen_encode_prim(ber,Type,{asis,lists:reverse(Tag)},"Val"),
[];
#'Externaltypereference'{module=CurrentMod,type=Etype} ->
emit([" 'enc_",Etype,"'(Val, ",{asis,Tag},")",nl]),
[];
#'Externaltypereference'{module=Emod,type=Etype} ->
emit([" '",Emod,"':'enc_",Etype,"'(Val, ",{asis,Tag},")",nl]),
[]
end.
%%%%%%%%%%%%%%%%
gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
ObjName,ObjectFields,ConstrAcc) ->
EmitFuncClause =
fun(Arg) ->
emit([{asis,dec_func(ObjName)},"(",{asis,Name},
", ",Arg,",_) ->",nl])
end,
MaybeConstr=
case {get_object_field(Name,ObjectFields),OptOrMand} of
{false,'OPTIONAL'} ->
EmitFuncClause(" Bytes"),
emit([" Bytes"]),
[];
{false,{'DEFAULT',DefaultType}} ->
EmitFuncClause("Bytes"),
emit_tlv_format("Bytes"),
gen_decode_default_call(ClassName,Name,"Tlv",DefaultType);
{{Name,TypeSpec},_} ->
%% A specified field owerwrites any 'DEFAULT' or
%% 'OPTIONAL' field in the class
EmitFuncClause("Bytes"),
emit_tlv_format("Bytes"),
gen_decode_field_call(ObjName,Name,"Tlv",TypeSpec)
end,
case more_genfields(Rest) of
true ->
emit([";",nl]);
false ->
emit([".",nl])
end,
gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc);
gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
ObjName,ObjectFields,ConstrAcc) ->
CurrentMod = get(currmod),
EmitFuncClause =
fun(Args) ->
emit([{asis,dec_func(ObjName)},"(",{asis,Name},
", ",Args,") ->",nl])
end,
case {get_object_field(Name,ObjectFields),OptOrMand} of
{false,'OPTIONAL'} ->
EmitFuncClause("_,_"),
emit([" exit({error,{'illegal use of missing field in object', ",{asis,Name},
"}})"]);
{false,{'DEFAULT',_DefaultObject}} ->
exit({error,{asn1,{"not implemented yet",Name}}});
{{Name,#'Externalvaluereference'{module=CurrentMod,
value=TypeName}},_} ->
EmitFuncClause("Bytes,[H|T]"),
emit([indent(3),{asis,dec_func(TypeName)},"(H, Bytes, T)"]);
{{Name,#'Externalvaluereference'{module=M,value=TypeName}},_} ->
EmitFuncClause("Bytes,[H|T]"),
emit([indent(3),{asis,M},":",{asis,dec_func(TypeName)},
"(H, Bytes, T)"]);
{{Name,#typedef{name=TypeName}},_} when is_atom(TypeName) ->
EmitFuncClause("Bytes,[H|T]"),
emit([indent(3),{asis,dec_func(TypeName)},"(H, Bytes, T)"])
end,
case more_genfields(Rest) of
true ->
emit([";",nl]);
false ->
emit([".",nl])
end,
gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
gen_decode_objectfields(CN,[_C|Cs],O,OF,CAcc) ->
gen_decode_objectfields(CN,Cs,O,OF,CAcc);
gen_decode_objectfields(_,[],_,_,CAcc) ->
CAcc.
emit_tlv_format(Bytes) ->
notice_tlv_format_gen(), % notice for generating of tlv_format/1
emit([" Tlv = tlv_format(",Bytes,"),",nl]).
notice_tlv_format_gen() ->
Module = get(currmod),
case get(tlv_format) of
{done,Module} ->
ok;
_ -> % true or undefined
put(tlv_format,true)
end.
emit_tlv_format_function() ->
Module = get(currmod),
case get(tlv_format) of
true ->
emit_tlv_format_function1(),
put(tlv_format,{done,Module});
_ ->
ok
end.
emit_tlv_format_function1() ->
emit(["tlv_format(Bytes) when is_binary(Bytes) ->",nl,
" {Tlv,_} = ",{call,ber,ber_decode_nif,["Bytes"]},com,nl,
" Tlv;",nl,
"tlv_format(Bytes) ->",nl,
" Bytes.",nl]).
gen_decode_constr_type(Erules,[TypeDef|Rest]) when is_record(TypeDef,typedef) ->
case is_already_generated(dec,TypeDef#typedef.name) of
true -> ok;
_ ->
emit([nl,nl,
"'dec_",TypeDef#typedef.name,
"'(Tlv, TagIn) ->",nl]),
gen_decode_user(Erules, TypeDef)
end,
gen_decode_constr_type(Erules,Rest);
gen_decode_constr_type(_,[]) ->
ok.
%%%%%%%%%%%
gen_decode_field_call(_ObjName,_FieldName,Bytes,
#'Externaltypereference'{module=M,type=T}) ->
CurrentMod = get(currmod),
TDef = asn1_db:dbget(M,T),
Def = TDef#typedef.typespec,
OTag = Def#type.tag,
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number ||
X <- OTag],
if
M == CurrentMod ->
emit([" ",{asis,dec_func(T)},"(",Bytes,
", ",{asis,Tag},")"]),
[];
true ->
emit([" ",{asis,M},":",{asis,dec_func(T)},
"(",Bytes,", ",{asis,Tag},")"]),
[]
end;
gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
Def = Type#typedef.typespec,
OTag = Def#type.tag,
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number ||
X <- OTag],
case Type#typedef.name of
{primitive,bif} ->
gen_dec_prim(Def, Bytes, Tag),
[];
{constructed,bif} ->
Name = lists:concat([ObjName,"_",FieldName]),
emit([" ",{asis,dec_func(Name)},
"(",Bytes,",",{asis,Tag},")"]),
[Type#typedef{name=list_to_atom(Name)}];
{ExtMod,TypeName} ->
emit([" ",{asis,ExtMod},":",{asis,dec_func(TypeName)},
"(",Bytes,",",{asis,Tag},")"]),
[];
TypeName ->
emit([" ",{asis,dec_func(TypeName)},
"(",Bytes,",",{asis,Tag},")"]),
[]
end.
gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
CurrentMod = get(currmod),
InnerType = asn1ct_gen:get_inner(Type#type.def),
OTag = Type#type.tag,
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
case asn1ct_gen:type(InnerType) of
{constructed,bif} ->
emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,",",
{asis,Tag},")"]),
[#typedef{name=list_to_atom(lists:concat([ClassName,'_',
FieldName])),
typespec=Type}];
{primitive,bif} ->
gen_dec_prim(Type, Bytes, Tag),
[];
#'Externaltypereference'{module=CurrentMod,type=Etype} ->
emit([" 'dec_",Etype,"'(",Bytes, " ,",{asis,Tag},")",nl]),
[];
#'Externaltypereference'{module=Emod,type=Etype} ->
emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", ",
{asis,Tag},")",nl]),
[]
end.
%%%%%%%%%%%
is_already_generated(Operation,Name) ->
case get(class_default_type) of
undefined ->
put(class_default_type,[{Operation,Name}]),
false;
GeneratedList ->
case lists:member({Operation,Name},GeneratedList) of
true ->
true;
false ->
put(class_default_type,[{Operation,Name}|GeneratedList]),
false
end
end.
more_genfields([]) ->
false;
more_genfields([Field|Fields]) ->
case element(1,Field) of
typefield ->
true;
objectfield ->
true;
_ ->
more_genfields(Fields)
end.
%% Object Set code generating for encoding and decoding
%% ----------------------------------------------------
gen_objectset_code(Erules,ObjSet) ->
ObjSetName = ObjSet#typedef.name,
Def = ObjSet#typedef.typespec,
#'Externaltypereference'{module=ClassModule,
type=ClassName} = Def#'ObjectSet'.class,
ClassDef = asn1_db:dbget(ClassModule,ClassName),
UniqueFName = Def#'ObjectSet'.uniquefname,
Set = Def#'ObjectSet'.set,
emit([nl,nl,nl,
"%%================================",nl,
"%% ",ObjSetName,nl,
"%%================================",nl]),
case ClassName of
{_Module,ExtClassName} ->
gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ExtClassName,ClassDef);
_ ->
gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)
end,
emit(nl).
gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)->
ClassFields = get_class_fields(ClassDef),
InternalFuncs=gen_objset_enc(Erules,ObjSetName,UniqueFName,Set,
ClassName,ClassFields,1,[]),
gen_objset_dec(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassFields,1),
gen_internal_funcs(Erules,InternalFuncs).
%% gen_objset_enc iterates over the objects of the object set
gen_objset_enc(_,_,{unique,undefined},_,_,_,_,_) ->
%% There is no unique field in the class of this object set
%% don't bother about the constraint
[];
gen_objset_enc(Erules, ObjSetName, UniqueName,
[{ObjName,Val,Fields}|T], ClName, ClFields,
NthObj,Acc)->
CurrMod = get(currmod),
{InternalFunc,NewNthObj}=
case ObjName of
{no_mod,no_name} ->
gen_inlined_enc_funs(Fields, ClFields, ObjSetName, Val, NthObj);
{CurrMod,Name} ->
emit([asis_atom(["getenc_",ObjSetName]),
"(Id) when Id =:= ",{asis,Val}," ->",nl,
" fun ",asis_atom(["enc_",Name]),"/3;",nl]),
{[],NthObj};
{ModuleName,Name} ->
emit([asis_atom(["getenc_",ObjSetName]),
"(Id) when Id =:= ",{asis,Val}," ->",nl]),
emit_ext_fun(enc,ModuleName,Name),
emit([";",nl]),
{[],NthObj};
_ ->
emit([asis_atom(["getenc_",ObjSetName]),
"(",{asis,Val},") ->",nl,
" fun ",asis_atom(["enc_",ObjName]),"/3;",nl]),
{[],NthObj}
end,
gen_objset_enc(Erules, ObjSetName, UniqueName, T, ClName, ClFields,
NewNthObj, InternalFunc ++ Acc);
%% See X.681 Annex E for the following case
gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
_ClFields,_NthObj,Acc) ->
emit([asis_atom(["getenc_",ObjSetName]),"(_) ->",nl,
indent(2),"fun(_, Val, _RestPrimFieldName) ->",nl]),
emit_enc_open_type(4),
emit([nl,
indent(2),"end.",nl,nl]),
Acc;
gen_objset_enc(_, ObjSetName, UniqueName, [], _, _, _, Acc) ->
emit_default_getenc(ObjSetName, UniqueName),
emit([".",nl,nl]),
Acc.
emit_ext_fun(EncDec,ModuleName,Name) ->
emit([indent(3),"fun(T,V,O) -> '",ModuleName,"':'",EncDec,"_",
Name,"'(T,V,O) end"]).
emit_default_getenc(ObjSetName,UniqueName) ->
emit([asis_atom(["getenc_",ObjSetName]),"(ErrV) ->",nl,
indent(3),"fun(C,V,_) ->",nl,
"exit({'Type not compatible with table constraint',{component,C},{value,V}, {unique_name_and_value,",{asis,UniqueName},", ErrV}}) end"]).
%% gen_inlined_enc_funs for each object iterates over all fields of a
%% class, and for each typefield it checks if the object has that
%% field and emits the proper code.
gen_inlined_enc_funs(Fields, [{typefield,_,_}|_]=T, ObjSetName, Val, NthObj) ->
emit([asis_atom(["getenc_",ObjSetName]),"(",{asis,Val},") ->",nl,
indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl,
indent(6),"case Type of",nl]),
gen_inlined_enc_funs1(Fields, T, ObjSetName, [], NthObj, []);
gen_inlined_enc_funs(Fields, [_|Rest], ObjSetName, Val, NthObj) ->
gen_inlined_enc_funs(Fields, Rest, ObjSetName, Val, NthObj);
gen_inlined_enc_funs(_, [], _, _, NthObj) ->
{[],NthObj}.
gen_inlined_enc_funs1(Fields, [{typefield,Name,_}|Rest], ObjSetName,
Sep0, NthObj, Acc0) ->
emit(Sep0),
Sep = [";",nl],
CurrMod = get(currmod),
InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
{Acc,NAdd} =
case lists:keyfind(Name,1,Fields) of
{_,#type{}=Type} ->
{Ret,N} = emit_inner_of_fun(Type,InternalDefFunName),
{Ret++Acc0,N};
{_,#typedef{}=Type} ->
emit([indent(9),{asis,Name}," ->",nl]),
{Ret,N} = emit_inner_of_fun(Type, InternalDefFunName),
{Ret++Acc0,N};
{_,#'Externaltypereference'{module=M,type=T}} ->
emit([indent(9),{asis,Name}," ->",nl]),
if
M =:= CurrMod ->
emit([indent(12),"'enc_",T,"'(Val)"]);
true ->
#typedef{typespec=Type} = asn1_db:dbget(M,T),
OTag = Type#type.tag,
Tag = [encode_tag_val(decode_class(X#tag.class),
X#tag.form,X#tag.number) ||
X <- OTag],
emit([indent(12),"'",M,"':'enc_",T,"'(Val, ",
{asis,Tag},")"])
end,
{Acc0,0};
false ->
%% This field was not present in the object; thus, there
%% was no type in the table and we therefore generate
%% code that returns the input for application
%% treatment.
emit([indent(9),{asis,Name}," ->",nl]),
emit_enc_open_type(11),
{Acc0,0}
end,
gen_inlined_enc_funs1(Fields, Rest, ObjSetName, Sep, NthObj+NAdd, Acc);
gen_inlined_enc_funs1(Fields,[_|Rest], ObjSetName, Sep, NthObj, Acc)->
gen_inlined_enc_funs1(Fields, Rest, ObjSetName, Sep, NthObj, Acc);
gen_inlined_enc_funs1(_, [], _, _, NthObj, Acc) ->
emit([nl,indent(6),"end",nl,
indent(3),"end;",nl]),
{Acc,NthObj}.
emit_enc_open_type(I) ->
Indent = indent(I),
S = [Indent, "case Val of",nl,
Indent,indent(2),"{asn1_OPENTYPE,Bin} when is_binary(Bin) ->",nl,
Indent,indent(4),"{Bin,byte_size(Bin)}"|
case asn1ct:use_legacy_types() of
false ->
[nl,
Indent,"end"];
true ->
[";",nl,
Indent,indent(2),"Bin when is_binary(Bin) ->",nl,
Indent,indent(4),"{Bin,byte_size(Bin)};",nl,
Indent,indent(2),"_ ->",nl,
Indent,indent(4),"{Val,length(Val)}",nl,
Indent, "end"]
end],
emit(S).
emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
InternalDefFunName) ->
OTag = Type#type.tag,
Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
case {ExtMod,Name} of
{primitive,bif} ->
emit(indent(12)),
gen_encode_prim(ber,Type,[{asis,lists:reverse(Tag)}],"Val"),
{[],0};
{constructed,bif} ->
emit([indent(12),"'enc_",
InternalDefFunName,"'(Val, ",{asis,Tag},")"]),
{[TDef#typedef{name=InternalDefFunName}],1};
_ ->
emit([indent(12),"'",ExtMod,"':'enc_",Name,"'(Val",{asis,Tag},")"]),
{[],0}
end;
emit_inner_of_fun(#typedef{name=Name},_) ->
emit([indent(12),"'enc_",Name,"'(Val)"]),
{[],0};
emit_inner_of_fun(Type,_) when is_record(Type,type) ->
CurrMod = get(currmod),
case Type#type.def of
Def when is_atom(Def) ->
OTag = Type#type.tag,
Tag = [encode_tag_val(decode_class(X#tag.class),
X#tag.form,X#tag.number)||X <- OTag],
emit([indent(9),Def," ->",nl,indent(12)]),
gen_encode_prim(ber,Type,{asis,lists:reverse(Tag)},"Val");
#'Externaltypereference'{module=CurrMod,type=T} ->
emit([indent(9),T," ->",nl,indent(12),"'enc_",T,
"'(Val)"]);
#'Externaltypereference'{module=ExtMod,type=T} ->
#typedef{typespec=ExtType} = asn1_db:dbget(ExtMod,T),
OTag = ExtType#type.tag,
Tag = [encode_tag_val(decode_class(X#tag.class),
X#tag.form,X#tag.number) ||
X <- OTag],
emit([indent(9),T," ->",nl,indent(12),ExtMod,":'enc_",
T,"'(Val, ",{asis,Tag},")"])
end,
{[],0}.
indent(N) ->
lists:duplicate(N,32). % 32 = space
gen_objset_dec(_,_,{unique,undefined},_,_,_,_) ->
%% There is no unique field in the class of this object set
%% don't bother about the constraint
ok;
gen_objset_dec(Erules, ObjSName, UniqueName, [{ObjName,Val,Fields}|T],
ClName, ClFields, NthObj)->
CurrMod = get(currmod),
NewNthObj=
case ObjName of
{no_mod,no_name} ->
gen_inlined_dec_funs(Fields,ClFields,ObjSName,Val,NthObj);
{CurrMod,Name} ->
emit([asis_atom(["getdec_",ObjSName]),
"(Id) when Id =:= ",{asis,Val}," ->",nl,
" fun 'dec_",Name,"'/3;", nl]),
NthObj;
{ModuleName,Name} ->
emit([asis_atom(["getdec_",ObjSName]),
"(Id) when Id =:= ",{asis,Val}," ->",nl]),
emit_ext_fun(dec,ModuleName,Name),
emit([";",nl]),
NthObj;
_ ->
emit([asis_atom(["getdec_",ObjSName]),
"(",{asis,Val},") ->",nl,
" fun 'dec_",ObjName,"'/3;", nl]),
NthObj
end,
gen_objset_dec(Erules, ObjSName, UniqueName, T, ClName,
ClFields, NewNthObj);
gen_objset_dec(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
_ClFields,_NthObj) ->
emit([asis_atom(["getdec_",ObjSetName]),"(_) ->",nl,
indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]),
emit_dec_open_type(4),
emit([nl,
indent(2),"end.",nl,nl]),
ok;
gen_objset_dec(_, ObjSetName, UniqueName, [], _, _, _) ->
emit_default_getdec(ObjSetName, UniqueName),
emit([".",nl,nl]),
ok.
emit_default_getdec(ObjSetName,UniqueName) ->
emit(["'getdec_",ObjSetName,"'(ErrV) ->",nl]),
emit([indent(2), "fun(C,V,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},", ErrV}}) end"]).
gen_inlined_dec_funs(Fields, [{typefield,_,_}|_]=ClFields, ObjSetName, Val, NthObj) ->
emit(["'getdec_",ObjSetName,"'(",{asis,Val},") ->",nl]),
emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->",nl,
indent(6),"case Type of",nl]),
gen_inlined_dec_funs1(Fields, ClFields, ObjSetName, "", NthObj);
gen_inlined_dec_funs(Fields, [_|ClFields], ObjSetName, Val, NthObj) ->
gen_inlined_dec_funs(Fields, ClFields, ObjSetName, Val, NthObj);
gen_inlined_dec_funs(_, _, _, _,NthObj) ->
NthObj.
gen_inlined_dec_funs1(Fields, [{typefield,Name,Prop}|Rest],
ObjSetName, Sep0, NthObj) ->
emit(Sep0),
Sep = [";",nl],
DecProp = case Prop of
'OPTIONAL' -> opt_or_default;
{'DEFAULT',_} -> opt_or_default;
_ -> mandatory
end,
InternalDefFunName = [NthObj,Name,ObjSetName],
N = case lists:keyfind(Name, 1, Fields) of
{_,#type{}=Type} ->
emit_inner_of_decfun(Type,DecProp,InternalDefFunName);
{_,#typedef{}=Type} ->
emit([indent(9),{asis,Name}," ->",nl]),
emit_inner_of_decfun(Type,DecProp,InternalDefFunName);
{_,#'Externaltypereference'{module=M,type=T}} ->
emit([indent(9),{asis,Name}," ->",nl]),
CurrMod = get(currmod),
if
M =:= CurrMod ->
emit([indent(12),"'dec_",T,"'(Bytes)"]);
true ->
#typedef{typespec=Type} = asn1_db:dbget(M,T),
OTag = Type#type.tag,
Tag = [(decode_class(X#tag.class) bsl 10) +
X#tag.number || X <- OTag],
emit([indent(12),"'",M,"':'dec_",T,"'(Bytes, ",{asis,Tag},")"])
end,
0;
false ->
emit([indent(9),{asis,Name}," ->",nl]),
emit_dec_open_type(11),
0
end,
gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N);
gen_inlined_dec_funs1(Fields, [_|Rest], ObjSetName, Sep, NthObj)->
gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj);
gen_inlined_dec_funs1(_, [], _, _, NthObj) ->
emit([nl,indent(6),"end",nl,
indent(3),"end;",nl]),
NthObj.
emit_dec_open_type(I) ->
Indent = indent(I),
S = case asn1ct:use_legacy_types() of
false ->
[Indent, "case Bytes of",nl,
Indent,indent(2),"Bin when is_binary(Bin) -> ",nl,
Indent,indent(4),"{asn1_OPENTYPE,Bin};",nl,
Indent,indent(2),"_ ->",nl,
Indent,indent(4),"{asn1_OPENTYPE,",
{call,ber,ber_encode,["Bytes"]},"}",nl,
Indent, "end"];
true ->
[Indent, "case Bytes of",nl,
Indent,indent(2),"Bin when is_binary(Bin) -> ",nl,
Indent,indent(4),"Bin;",nl,
Indent,indent(2),"_ ->",nl,
Indent,indent(4),{call,ber,ber_encode,["Bytes"]},nl,
Indent, "end"]
end,
emit(S).
emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type}, _Prop,
InternalDefFunName) ->
OTag = Type#type.tag,
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
case {ExtName,Name} of
{primitive,bif} ->
emit(indent(12)),
gen_dec_prim(Type, "Bytes", Tag),
0;
{constructed,bif} ->
emit([indent(12),"'dec_",
asn1ct_gen:list2name(InternalDefFunName),"'(Bytes, ",
{asis,Tag},")"]),
1;
_ ->
emit([indent(12),"'",ExtName,"':'dec_",Name,"'(Bytes, ",
{asis,Tag},")"]),
0
end;
emit_inner_of_decfun(#typedef{name=Name},_Prop,_) ->
emit([indent(12),"'dec_",Name,"'(Bytes)"]),
0;
emit_inner_of_decfun(#type{}=Type, _Prop, _) ->
OTag = Type#type.tag,
Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
CurrMod = get(currmod),
Def = Type#type.def,
InnerType = asn1ct_gen:get_inner(Def),
WhatKind = asn1ct_gen:type(InnerType),
case WhatKind of
{primitive,bif} ->
emit([indent(9),Def," ->",nl,indent(12)]),
gen_dec_prim(Type, "Bytes", Tag);
#'Externaltypereference'{module=CurrMod,type=T} ->
emit([indent(9),T," ->",nl,indent(12),"'dec_",T,
"'(Bytes)"]);
#'Externaltypereference'{module=ExtMod,type=T} ->
emit([indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
T,"'(Bytes, ",{asis,Tag},")"])
end,
0.
gen_internal_funcs(_,[]) ->
ok;
gen_internal_funcs(Erules,[TypeDef|Rest]) ->
gen_encode_user(Erules, TypeDef, false),
emit([nl,nl,
"'dec_",TypeDef#typedef.name,"'(Tlv, TagIn) ->",nl]),
gen_decode_user(Erules,TypeDef),
gen_internal_funcs(Erules,Rest).
decode_class('UNIVERSAL') ->
?UNIVERSAL;
decode_class('APPLICATION') ->
?APPLICATION;
decode_class('CONTEXT') ->
?CONTEXT;
decode_class('PRIVATE') ->
?PRIVATE.
mkfuncname(#'Externaltypereference'{module=Mod,type=EType}, DecOrEnc) ->
CurrMod = get(currmod),
case CurrMod of
Mod ->
lists:concat(["'",DecOrEnc,"_",EType,"'"]);
_ ->
lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"])
end.
get_size_constraint(C) ->
case lists:keyfind('SizeConstraint', 1, C) of
false -> [];
{_,{_,[]}} -> []; %Extensible.
{_,{Sv,Sv}} -> Sv;
{_,{_,_}=Tc} -> Tc
end.
get_class_fields(#classdef{typespec=ObjClass}) ->
ObjClass#objectclass.fields;
get_class_fields(#objectclass{fields=Fields}) ->
Fields;
get_class_fields(_) ->
[].
get_object_field(Name,ObjectFields) ->
case lists:keysearch(Name,1,ObjectFields) of
{value,Field} -> Field;
false -> false
end.
%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
%% 8bit Int | binary
encode_tag_val(Class, Form, TagNo) when (TagNo =< 30) ->
<<(Class bsr 6):2,(Form bsr 5):1,TagNo:5>>;
encode_tag_val(Class, Form, TagNo) ->
{Octets,_Len} = mk_object_val(TagNo),
BinOct = list_to_binary(Octets),
<<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>.
%%%%%%%%%%%
%% mk_object_val(Value) -> {OctetList, Len}
%% returns a Val as a list of octets, the 8 bit is always set to one except
%% for the last octet, where its 0
%%
mk_object_val(Val) when Val =< 127 ->
{[255 band Val], 1};
mk_object_val(Val) ->
mk_object_val(Val bsr 7, [Val band 127], 1).
mk_object_val(0, Ack, Len) ->
{Ack, Len};
mk_object_val(Val, Ack, Len) ->
mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
%% For BER the ExtensionAdditionGroup notation has no impact on the
%% encoding/decoding. Therefore we can filter away the
%% ExtensionAdditionGroup start and end markers.
extaddgroup2sequence(ExtList) when is_list(ExtList) ->
lists:filter(fun(#'ExtensionAdditionGroup'{}) ->
false;
('ExtensionAdditionGroupEnd') ->
false;
(_) ->
true
end, ExtList).
call(F, Args) ->
asn1ct_func:call(ber, F, Args).
enc_func(Tname) ->
list_to_atom(lists:concat(["enc_",Tname])).
dec_func(Tname) ->
list_to_atom(lists:concat(["dec_",Tname])).
asis_atom(List) ->
{asis,list_to_atom(lists:concat(List))}.