diff options
Diffstat (limited to 'lib/asn1/src')
-rw-r--r-- | lib/asn1/src/Makefile | 4 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct.erl | 40 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_check.erl | 196 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl | 3 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_constructed_per.erl | 213 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_func.erl | 2 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_gen.erl | 232 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_gen_ber_bin_v2.erl | 122 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_gen_per.erl | 28 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_imm.erl | 927 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_parser2.erl | 8 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_value.erl | 23 | ||||
-rw-r--r-- | lib/asn1/src/asn1rt_nif.erl | 2 | ||||
-rw-r--r-- | lib/asn1/src/asn1rtt_ber.erl | 65 | ||||
-rw-r--r-- | lib/asn1/src/asn1rtt_check.erl | 91 | ||||
-rw-r--r-- | lib/asn1/src/asn1rtt_ext.erl | 2 | ||||
-rw-r--r-- | lib/asn1/src/asn1rtt_per_common.erl | 76 | ||||
-rw-r--r-- | lib/asn1/src/prepare_templates.erl | 72 |
18 files changed, 1488 insertions, 618 deletions
diff --git a/lib/asn1/src/Makefile b/lib/asn1/src/Makefile index 3f24e15c04..500f4a1358 100644 --- a/lib/asn1/src/Makefile +++ b/lib/asn1/src/Makefile @@ -135,7 +135,7 @@ $(EBIN)/asn1ct_func.$(EMULATOR): asn1ct_func.erl asn1ct_eval_%.erl: asn1ct_eval_%.funcs $(gen_verbose)erl -pa $(EBIN) -noshell -noinput \ - -run prepare_templates gen_asn1ct_eval $< >$@ + -run prepare_templates gen_asn1ct_eval $< $(APP_TARGET): $(APP_SRC) ../vsn.mk $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ @@ -180,7 +180,7 @@ RT_TEMPLATES_TARGET = $(RT_TEMPLATES:%=%.$(EMULATOR)) asn1ct_rtt.erl: prepare_templates.$(EMULATOR) $(RT_TEMPLATES_TARGET) $(gen_verbose)erl -noshell -noinput -run prepare_templates gen_asn1ct_rtt \ - $(RT_TEMPLATES_TARGET) >asn1ct_rtt.erl + $(RT_TEMPLATES_TARGET) prepare_templates.$(EMULATOR): prepare_templates.erl $(V_ERLC) prepare_templates.erl diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index f2ccf5f212..531a4935fe 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -40,7 +40,7 @@ maybe_rename_function/3,current_sindex/0, set_current_sindex/1,maybe_saved_sindex/2, parse_and_save/2,verbose/3,warning/3,warning/4,error/3]). --export([get_bit_string_format/0]). +-export([get_bit_string_format/0,use_legacy_types/0]). -include("asn1_records.hrl"). -include_lib("stdlib/include/erl_compile.hrl"). @@ -333,8 +333,7 @@ print_structured_errors([_|_]=Errors) -> print_structured_errors(_) -> ok. compile1(File, #st{opts=Opts}=St0) -> - verbose("Erlang ASN.1 version ~p, compiling ~p~n", [?vsn,File], Opts), - verbose("Compiler Options: ~p~n", [Opts], Opts), + compiler_verbose(File, Opts), Passes = single_passes(), Base = filename:rootname(filename:basename(File)), OutFile = outfile(Base, "", Opts), @@ -349,8 +348,7 @@ compile1(File, #st{opts=Opts}=St0) -> %% compile_set/3 merges and compiles a number of asn1 modules %% specified in a .set.asn file to one .erl file. compile_set(SetBase, Files, #st{opts=Opts}=St0) -> - verbose("Erlang ASN.1 version ~p compiling ~p ~n", [?vsn,Files], Opts), - verbose("Compiler Options: ~p~n",[Opts], Opts), + compiler_verbose(Files, Opts), OutFile = outfile(SetBase, "", Opts), DbFile = outfile(SetBase, "asn1db", Opts), InputModules = [begin @@ -363,6 +361,11 @@ compile_set(SetBase, Files, #st{opts=Opts}=St0) -> Passes = set_passes(), run_passes(Passes, St). +compiler_verbose(What, Opts) -> + verbose("Erlang ASN.1 compiler ~s\n", [?vsn], Opts), + verbose("Compiling: ~p\n", [What], Opts), + verbose("Options: ~p\n", [Opts], Opts). + %% merge_modules/2 -> returns a module record where the typeorval lists are merged, %% the exports lists are merged, the imports lists are merged when the %% elements come from other modules than the merge set, the tagdefault @@ -838,6 +841,7 @@ delete_double_of_symbol1([],Acc) -> generate({M,GenTOrV}, OutFile, EncodingRule, Options) -> debug_on(Options), setup_bit_string_format(Options), + setup_legacy_erlang_types(Options), put(encoding_options,Options), asn1ct_table:new(check_functions), @@ -866,6 +870,31 @@ generate({M,GenTOrV}, OutFile, EncodingRule, Options) -> asn1ct_table:delete(check_functions), Result. +setup_legacy_erlang_types(Opts) -> + F = case lists:member(legacy_erlang_types, Opts) of + false -> + case get_bit_string_format() of + bitstring -> + false; + compact -> + legacy_forced_info(compact_bit_string), + true; + legacy -> + legacy_forced_info(legacy_bit_string), + true + end; + true -> + true + end, + put(use_legacy_erlang_types, F). + +legacy_forced_info(Opt) -> + io:format("Info: The option 'legacy_erlang_types' " + "is implied by the '~s' option.\n", [Opt]). + +use_legacy_types() -> + get(use_legacy_erlang_types). + setup_bit_string_format(Opts) -> Format = case {lists:member(compact_bit_string, Opts), lists:member(legacy_bit_string, Opts)} of @@ -1072,6 +1101,7 @@ remove_asn_flags(Options) -> X /= optimize, X /= compact_bit_string, X /= legacy_bit_string, + X /= legacy_erlang_types, X /= debug, X /= asn1config, X /= record_name_prefix]. diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index eddcda0018..f94f3b56bc 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -2534,89 +2534,66 @@ normalize_integer(S,Int=#'Externalvaluereference'{value=Name},Type) -> normalize_integer(_,Int,_) -> exit({'Unknown INTEGER value',Int}). -normalize_bitstring(S,Value,Type)-> - %% There are four different Erlang formats of BIT STRING: - %% 1 - a list of ones and zeros. - %% 2 - a list of atoms. - %% 3 - as an integer, for instance in hexadecimal form. - %% 4 - as a tuple {Unused, Binary} where Unused is an integer - %% and tells how many bits of Binary are unused. - %% - %% normalize_bitstring/3 transforms Value according to: - %% A to 3, - %% B to 1, - %% C to 1 or 3 - %% D to 2, - %% Value can be on format: - %% A - {hstring, String}, where String is a hexadecimal string. - %% B - {bstring, String}, where String is a string on bit format - %% C - #'Externalvaluereference'{value=V}, where V is a defined value - %% D - list of #'Externalvaluereference', where each value component - %% is an identifier corresponing to NamedBits in Type. - %% E - list of ones and zeros, if Value already is normalized. +%% normalize_bitstring(S, Value, Type) -> bitstring() +%% Convert a literal value for a BIT STRING to an Erlang bit string. +%% +normalize_bitstring(S, Value, Type)-> case Value of {hstring,String} when is_list(String) -> - hstring_to_int(String); + hstring_to_bitstring(String); {bstring,String} when is_list(String) -> - bstring_to_bitlist(String); - Rec when is_record(Rec,'Externalvaluereference') -> - get_normalized_value(S,Value,Type, - fun normalize_bitstring/3,[]); + bstring_to_bitstring(String); + #'Externalvaluereference'{} -> + get_normalized_value(S, Value, Type, + fun normalize_bitstring/3, []); RecList when is_list(RecList) -> - case Type of - NBL when is_list(NBL) -> - F = fun(#'Externalvaluereference'{value=Name}) -> - case lists:keysearch(Name,1,NBL) of - {value,{Name,_}} -> - Name; - Other -> - throw({error,Other}) - end; - (I) when I =:= 1; I =:= 0 -> - I; - (Other) -> - throw({error,Other}) - end, - case catch lists:map(F,RecList) of - {error,Reason} -> - asn1ct:warning("default value not " - "compatible with type definition ~p~n", - [Reason],S, - "default value not " - "compatible with type definition"), - Value; - NewList -> - NewList - end; - _ -> + F = fun(#'Externalvaluereference'{value=Name}) -> + case lists:keymember(Name, 1, Type) of + true -> Name; + false -> throw({error,false}) + end; + (Name) when is_atom(Name) -> + %% Already normalized. + Name; + (Other) -> + throw({error,Other}) + end, + try + lists:map(F, RecList) + catch + throw:{error,Reason} -> asn1ct:warning("default value not " "compatible with type definition ~p~n", - [RecList],S, + [Reason],S, "default value not " "compatible with type definition"), Value end; - {Name,String} when is_atom(Name) -> - normalize_bitstring(S,String,Type); - Other -> - asn1ct:warning("illegal default value ~p~n",[Other],S, - "illegal default value"), - Value + Bs when is_bitstring(Bs) -> + %% Already normalized. + Bs end. -hstring_to_int(L) when is_list(L) -> - hstring_to_int(L,0). -hstring_to_int([H|T],Acc) when H >= $A, H =< $F -> - hstring_to_int(T,(Acc bsl 4) + (H - $A + 10) ) ; -hstring_to_int([H|T],Acc) when H >= $0, H =< $9 -> - hstring_to_int(T,(Acc bsl 4) + (H - $0)); -hstring_to_int([],Acc) -> - Acc. +hstring_to_binary(L) -> + byte_align(hstring_to_bitstring(L)). -bstring_to_bitlist([H|T]) when H == $0; H == $1 -> - [H - $0 | bstring_to_bitlist(T)]; -bstring_to_bitlist([]) -> - []. +bstring_to_binary(L) -> + byte_align(bstring_to_bitstring(L)). + +byte_align(Bs) -> + case bit_size(Bs) rem 8 of + 0 -> Bs; + N -> <<Bs/bitstring,0:(8-N)>> + end. + +hstring_to_bitstring(L) -> + << <<(hex_to_int(D)):4>> || D <- L >>. + +bstring_to_bitstring(L) -> + << <<(D-$0):1>> || D <- L >>. + +hex_to_int(D) when $0 =< D, D =< $9 -> D - $0; +hex_to_int(D) when $A =< D, D =< $F -> D - ($A - 10). %% normalize_octetstring/1 changes representation of input Value to a %% list of octets. @@ -2627,9 +2604,9 @@ bstring_to_bitlist([]) -> normalize_octetstring(S,Value,CType) -> case Value of {bstring,String} -> - bstring_to_octetlist(String); + bstring_to_binary(String); {hstring,String} -> - hstring_to_octetlist(String); + hstring_to_binary(String); Rec when is_record(Rec,'Externalvaluereference') -> get_normalized_value(S,Value,CType, fun normalize_octetstring/3,[]); @@ -2651,35 +2628,6 @@ normalize_octetstring(S,Value,CType) -> Value end. - -bstring_to_octetlist([]) -> - []; -bstring_to_octetlist([H|T]) when H == $0 ; H == $1 -> - bstring_to_octetlist(T,6,[(H - $0) bsl 7]). -bstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H == $0; H == $1 -> - bstring_to_octetlist(T, 7, [0,Hacc + (H -$0)| Tacc]); -bstring_to_octetlist([H|T],BSL,[Hacc|Tacc]) when H == $0; H == $1 -> - bstring_to_octetlist(T, BSL-1, [Hacc + ((H - $0) bsl BSL)| Tacc]); -bstring_to_octetlist([],7,[0|Acc]) -> - lists:reverse(Acc); -bstring_to_octetlist([],_,Acc) -> - lists:reverse(Acc). - -hstring_to_octetlist([]) -> - []; -hstring_to_octetlist(L) -> - hstring_to_octetlist(L,4,[]). -hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $A, H =< $F -> - hstring_to_octetlist(T,4,[Hacc + (H - $A + 10)|Tacc]); -hstring_to_octetlist([H|T],BSL,Acc) when H >= $A, H =< $F -> - hstring_to_octetlist(T,0,[(H - $A + 10) bsl BSL|Acc]); -hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $0; H =< $9 -> - hstring_to_octetlist(T,4,[Hacc + (H - $0)|Tacc]); -hstring_to_octetlist([H|T],BSL,Acc) when H >= $0; H =< $9 -> - hstring_to_octetlist(T,0,[(H - $0) bsl BSL|Acc]); -hstring_to_octetlist([],_,Acc) -> - lists:reverse(Acc). - normalize_objectidentifier(S, Value) -> {ok,Val} = validate_objectidentifier(S, o_id, Value, []), Val. @@ -4229,9 +4177,10 @@ constraint_union(S,C) when is_list(C) -> constraint_union(_S,C) -> [C]. -constraint_union1(S,[A={'ValueRange',_},union,B={'ValueRange',_}|Rest],Acc) -> - AunionB = constraint_union_vr([A,B]), - constraint_union1(S, AunionB++Rest, Acc); +constraint_union1(S, [{'ValueRange',{Lb1,Ub1}},union, + {'ValueRange',{Lb2,Ub2}}|Rest], Acc) -> + AunionB = {'ValueRange',{c_min(Lb1, Lb2),max(Ub1, Ub2)}}, + constraint_union1(S, [AunionB|Rest], Acc); constraint_union1(S,[A={'SingleValue',_},union,B={'SingleValue',_}|Rest],Acc) -> AunionB = constraint_union_sv(S,[A,B]), constraint_union1(S,Rest,Acc ++ AunionB); @@ -4255,42 +4204,9 @@ constraint_union_sv(_S,SV) -> [N] -> [{'SingleValue',N}]; L -> [{'SingleValue',L}] end. - -%% REMOVE???? -%%constraint_union(S,VR,'ValueRange') -> -%% constraint_union_vr(VR). - -%% constraint_union_vr(VR) -%% VR = [{'ValueRange',{Lb,Ub}},...] -%% Lb = 'MIN' | integer() -%% Ub = 'MAX' | integer() -%% Returns if possible only one ValueRange tuple with a range that -%% is a union of all ranges in VR. -constraint_union_vr(VR) -> - %% Sort VR by Lb in first hand and by Ub in second hand - Fun=fun({_,{'MIN',_B1}},{_,{A2,_B2}}) when is_integer(A2)->true; - ({_,{A1,_B1}},{_,{'MAX',_B2}}) when is_integer(A1) -> true; - ({_,{A1,_B1}},{_,{A2,_B2}}) when is_integer(A1),is_integer(A2),A1<A2 -> true; - ({_,{A,B1}},{_,{A,B2}}) when B1=<B2->true; - (_,_)->false end, - SortedVR = lists:usort(Fun,VR), - constraint_union_vr(SortedVR, []). - -constraint_union_vr([],Acc) -> - lists:reverse(Acc); -constraint_union_vr([C|Rest],[]) -> - constraint_union_vr(Rest,[C]); -constraint_union_vr([{_,{Lb,Ub2}}|Rest],[{_,{Lb,_Ub1}}|Acc]) -> %Ub2 > Ub1 - constraint_union_vr(Rest,[{'ValueRange',{Lb,Ub2}}|Acc]); -constraint_union_vr([{_,{_,Ub}}|Rest],A=[{_,{_,Ub}}|_Acc]) -> - constraint_union_vr(Rest,A); -constraint_union_vr([{_,{Lb2,Ub2}}|Rest], [{_,{Lb1,Ub1}}|Acc]) - when Ub1 =< Lb2, Ub1 < Ub2 -> - constraint_union_vr(Rest,[{'ValueRange',{Lb1,Ub2}}|Acc]); -constraint_union_vr([{_,{_,Ub2}}|Rest],A=[{_,{_,Ub1}}|_Acc]) when Ub2=<Ub1-> - constraint_union_vr(Rest,A); -constraint_union_vr([VR|Rest],Acc) -> - constraint_union_vr(Rest,[VR|Acc]). +c_min('MIN', _) -> 'MIN'; +c_min(_, 'MIN') -> 'MIN'; +c_min(A, B) -> min(A, B). union_sv_vr(_S,{'SingleValue',SV},VR) when is_integer(SV) -> diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index 8359b81b33..a38da8bcc2 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -1155,7 +1155,8 @@ gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) -> emit([indent(4),"_ ->",nl]), case OptOrMand of - {'DEFAULT', Def} -> + {'DEFAULT', Def0} -> + Def = asn1ct_gen:conform_value(Type, Def0), emit([indent(8),"{",{asis,Def},",",{prev,tlv},"}",nl]); 'OPTIONAL' -> emit([indent(8),"{ asn1_NOVALUE, ",{prev,tlv},"}",nl]) diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 8d4afc0a0b..ed3f6f886e 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -79,7 +79,7 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> [] end, Aligned = is_aligned(Erule), - Value0 = asn1ct_gen:mk_var(asn1ct_name:curr(val)), + Value0 = make_var(val), Optionals = optionals(to_textual_order(CompList)), ImmOptionals = [asn1ct_imm:per_enc_optional(Value0, Opt, Aligned) || Opt <- Optionals], @@ -87,7 +87,7 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> ExtImm = case Ext of {ext,ExtPos,NumExt} when NumExt > 0 -> gen_encode_extaddgroup(CompList), - Value = asn1ct_gen:mk_var(asn1ct_name:curr(val)), + Value = make_var(val), asn1ct_imm:per_enc_extensions(Value, ExtPos, NumExt, Aligned); _ -> @@ -106,19 +106,17 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> c_index=N, usedclassfield=UniqueFieldName, uniqueclassfield=UniqueFieldName, - valueindex=ValueIndex + valueindex=ValueIndex0 } -> %% N is index of attribute that determines constraint {Module,ObjSetName} = ObjectSet, #typedef{typespec=#'ObjectSet'{gen=Gen}} = asn1_db:dbget(Module, ObjSetName), case Gen of true -> - ObjectEncode = - asn1ct_gen:un_hyphen_var(lists:concat(['Obj',AttrN])), - El = make_element(N+1, asn1ct_gen:mk_var(asn1ct_name:curr(val))), - ValueMatch = value_match(ValueIndex, El), - ObjSetImm0 = [{assign,{var,ObjectEncode},ValueMatch}], - {{AttrN,ObjectEncode},ObjSetImm0}; + ValueIndex = ValueIndex0 ++ [{N+1,top}], + Val = make_var(val), + {ObjSetImm0,Dst} = enc_dig_out_value(ValueIndex, Val), + {{AttrN,Dst},ObjSetImm0}; false -> {false,[]} end; @@ -128,7 +126,7 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> %% when the simpletableattributes was at an outer %% level and the objfun has been passed through the %% function call - {{"got objfun through args","ObjFun"},[]}; + {{"got objfun through args",{var,"ObjFun"}},[]}; _ -> {false,[]} end @@ -136,7 +134,7 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> ImmSetExt = case Ext of {ext,_Pos,NumExt2} when NumExt2 > 0 -> - asn1ct_imm:per_enc_extension_bit('Extensions', Aligned); + asn1ct_imm:per_enc_extension_bit({var,"Extensions"}, Aligned); {ext,_Pos,_} -> asn1ct_imm:per_enc_extension_bit([], Aligned); _ -> @@ -452,8 +450,13 @@ dec_objset_default(N, C, LeadingAttr, false) -> "{value,Bytes}," "{unique_name_and_value,",{asis,LeadingAttr},",Id}}}).",nl,nl]); dec_objset_default(N, _, _, true) -> - emit([{asis,N},"(Bytes, Id) ->",nl, - "Bytes.",nl,nl]). + 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, ",{asis,Id},") ->",nl]), @@ -540,7 +543,7 @@ gen_encode_choice_imm(Erule, TopType, #type{def={'CHOICE',CompList}}) -> Aligned = is_aligned(Erule), Cs = gen_enc_choice(Erule, TopType, CompList, Ext), [{assign,{expr,"{ChoiceTag,ChoiceVal}"},"Val"}| - asn1ct_imm:per_enc_choice('ChoiceTag', Cs, Aligned)]. + asn1ct_imm:per_enc_choice({var,"ChoiceTag"}, Cs, Aligned)]. gen_decode_choice(Erules,Typename,D) when is_record(D,type) -> asn1ct_name:start(), @@ -562,14 +565,14 @@ gen_encode_sof(Erule, Typename, SeqOrSetOf, D) -> gen_encode_sof_imm(Erule, Typename, SeqOrSetOf, #type{}=D) -> {_SeqOrSetOf,ComponentType} = D#type.def, Aligned = is_aligned(Erule), - Constructed_Suffix = - asn1ct_gen:constructed_suffix(SeqOrSetOf, - ComponentType#type.def), - Conttype = asn1ct_gen:get_inner(ComponentType#type.def), + 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('Comp', ComponentType, Aligned); + asn1ct_gen_per:gen_encode_prim_imm({var,"Comp"}, + ComponentType, Aligned); {constructed,bif} -> TypeName = [Constructed_Suffix|Typename], Enc = enc_func(asn1ct_gen:list2name(TypeName)), @@ -577,17 +580,19 @@ gen_encode_sof_imm(Erule, Typename, SeqOrSetOf, #type{}=D) -> [{objfun,_}|_] -> [{var,"ObjFun"}]; _ -> [] end, - [{apply,Enc,[{var,"Comp"}|ObjArg]}]; + [{apply,{local,Enc,CompType}, + [{var,"Comp"}|ObjArg]}]; #'Externaltypereference'{module=Currmod,type=Ename} -> - [{apply,enc_func(Ename),[{var,"Comp"}]}]; + [{apply,{local,enc_func(Ename),CompType},[{var,"Comp"}]}]; #'Externaltypereference'{module=EMod,type=Ename} -> - [{apply,{EMod,enc_func(Ename)},[{var,"Comp"}]}]; + [{apply,{EMod,enc_func(Ename),CompType},[{var,"Comp"}]}]; 'ASN1_OPEN_TYPE' -> - asn1ct_gen_per:gen_encode_prim_imm('Comp', + asn1ct_gen_per:gen_encode_prim_imm({var,"Comp"}, #type{def='ASN1_OPEN_TYPE'}, Aligned) end, - asn1ct_imm:per_enc_sof('Val', D#type.constraint, 'Comp', Imm0, Aligned). + asn1ct_imm:per_enc_sof({var,"Val"}, D#type.constraint, 'Comp', + Imm0, Aligned). gen_decode_sof(Erules, Typename, SeqOrSetOf, #type{}=D) -> asn1ct_name:start(), @@ -770,8 +775,10 @@ optionals(L) -> optionals(L,[],2). optionals([#'ComponentType'{prop='OPTIONAL'}|Rest], Acc, Pos) -> optionals(Rest, [Pos|Acc], Pos+1); -optionals([#'ComponentType'{prop={'DEFAULT',Val}}|Rest], Acc, Pos) -> - optionals(Rest, [{Pos,Val}|Acc], Pos+1); +optionals([#'ComponentType'{typespec=T,prop={'DEFAULT',Val}}|Rest], + Acc, Pos) -> + Vals = def_values(T, Val), + optionals(Rest, [{Pos,Vals}|Acc], Pos+1); optionals([#'ComponentType'{}|Rest], Acc, Pos) -> optionals(Rest, Acc, Pos+1); optionals([], Acc, _) -> @@ -869,8 +876,8 @@ gen_enc_components_call1(Erule,TopType, CanonicalNum -> CanonicalNum end, - Element0 = make_element(TermNo+1, asn1ct_gen:mk_var(asn1ct_name:curr(val))), - {Imm0,Element} = asn1ct_imm:enc_bind_var(Element0), + Val = make_var(val), + {Imm0,Element} = asn1ct_imm:enc_element(TermNo+1, Val), Imm1 = gen_enc_line_imm(Erule, TopType, Cname, Type, Element, DynamicEnc, Ext), Category = case {Prop,Ext} of {'OPTIONAL',_} -> @@ -888,7 +895,8 @@ gen_enc_components_call1(Erule,TopType, optional -> asn1ct_imm:enc_absent(Element, [asn1_NOVALUE], Imm1); {default,Def} -> - asn1ct_imm:enc_absent(Element, [asn1_DEFAULT,Def], Imm1) + DefValues = def_values(Type, Def), + asn1ct_imm:enc_absent(Element, DefValues, Imm1) end, Imm = case Imm2 of [] -> []; @@ -899,6 +907,48 @@ gen_enc_components_call1(_Erule,_TopType,[],Pos,_,_, Acc) -> ImmList = lists:reverse(Acc), {ImmList,Pos}. +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), @@ -932,9 +982,9 @@ gen_enc_line_imm_1(Erule, TopType, Cname, Type, Element, DynamicEnc) -> CurrMod = get(currmod), case asn1ct_gen:type(Atype) of #'Externaltypereference'{module=CurrMod,type=EType} -> - [{apply,enc_func(EType),[{expr,Element}]}]; + [{apply,{local,enc_func(EType),Atype},[Element]}]; #'Externaltypereference'{module=Mod,type=EType} -> - [{apply,{Mod,enc_func(EType)},[{expr,Element}]}]; + [{apply,{Mod,enc_func(EType),Atype},[Element]}]; {primitive,bif} -> asn1ct_gen_per:gen_encode_prim_imm(Element, Type, Aligned); 'ASN1_OPEN_TYPE' -> @@ -953,9 +1003,9 @@ gen_enc_line_imm_1(Erule, TopType, Cname, Type, Element, DynamicEnc) -> Enc = enc_func(asn1ct_gen:list2name(NewTypename)), case {Type#type.tablecinf,DynamicEnc} of {[{objfun,_}|_R],{_,EncFun}} -> - [{apply,Enc,[{expr,Element},{var,EncFun}]}]; + [{apply,{local,Enc,Type},[Element,EncFun]}]; _ -> - [{apply,Enc,[{expr,Element}]}] + [{apply,{local,Enc,Type},[Element]}] end end end. @@ -979,13 +1029,16 @@ enc_var_type_call(Erule, Name, RestFieldNames, {_,Key,Code} <- ObjSet1], ObjSet = lists:sort([P || {_,B}=P <- ObjSet2, B =/= none]), Key = erlang:md5(term_to_binary({encode,ObjSet,RestFieldNames,Extensible})), + Imm = enc_objset_imm(Erule, Name, ObjSet, RestFieldNames, Extensible), + Lambda = {lambda,[{var,"Val"},{var,"Id"}],Imm}, Gen = fun(_Fd, N) -> - enc_objset(Erule, Name, N, ObjSet, - RestFieldNames, Extensible) + 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]), - F = asn1ct_func:call_gen(Prefix, Key, Gen), - [{apply,F,[{var,atom_to_list(Val)},{var,Fun}]}]. + [{call_gen,Prefix,Key,Gen,Lambda,[Val,Fun]}]. fix_object_code(Name, [{Name,B}|_], _ClassFields) -> B; @@ -1007,9 +1060,7 @@ fix_object_code(Name, [], ClassFields) -> end end. - -enc_objset(Erule, Component, Name, ObjSet, RestFieldNames, Extensible) -> - asn1ct_name:start(), +enc_objset_imm(Erule, Component, ObjSet, RestFieldNames, Extensible) -> Aligned = is_aligned(Erule), E = {error, fun() -> @@ -1018,22 +1069,28 @@ enc_objset(Erule, Component, Name, ObjSet, RestFieldNames, Extensible) -> "{value,Val}," "{unique_name_and_value,'_'}})",nl]) end}, - Imm = [{'cond', - [[{eq,{var,"Id"},Key}| - enc_obj(Erule, Obj, RestFieldNames, Aligned)] || - {Key,Obj} <- ObjSet] ++ - [['_',case Extensible of - false -> E; - true -> {put_bits,{var,"Val"},binary,[1]} - end]]}], - emit([{asis,Name},"(Val, Id) ->",nl]), - asn1ct_imm:enc_cg(Imm, Aligned), - emit([".",nl]). + [{'cond', + [[{eq,{var,"Id"},Key}| + enc_obj(Erule, Obj, 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, RestFieldNames0, Aligned) -> case Obj of #typedef{name={primitive,bif},typespec=Def} -> - asn1ct_gen_per:gen_encode_prim_imm('Val', Def, Aligned); + asn1ct_gen_per:gen_encode_prim_imm({var,"Val"}, Def, Aligned); #typedef{name={constructed,bif},typespec=Def} -> InnerType = asn1ct_gen:get_inner(Def#type.def), case InnerType of @@ -1049,7 +1106,7 @@ enc_obj(Erule, Obj, RestFieldNames0, Aligned) -> gen_encode_sof_imm(Erule, name, InnerType, Def) end; #typedef{name=Type} -> - [{apply,enc_func(Type),[{var,"Val"}]}]; + [{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}} -> @@ -1062,9 +1119,9 @@ enc_obj(Erule, Obj, RestFieldNames0, Aligned) -> Func = enc_func(Type), case get(currmod) of Mod -> - [{apply,Func,[{var,"Val"}]}]; + [{apply,{local,Func,Obj},[{var,"Val"}]}]; _ -> - [{apply,{Mod,Func},[{var,"Val"}]}] + [{apply,{Mod,Func,Obj},[{var,"Val"}]}] end end. @@ -1207,7 +1264,8 @@ gen_dec_comp_call(Comp, Erule, TopType, Tpos, OptTable, DecInfObj, comp_call_pre_post(noext, mandatory, _, _, _, _, _, _) -> {[],[]}; -comp_call_pre_post(noext, Prop, _, _, TextPos, OptTable, NumOptionals, Ext) -> +comp_call_pre_post(noext, Prop, _, Type, TextPos, + OptTable, NumOptionals, Ext) -> %% OPTIONAL or DEFAULT OptPos = get_optionality_pos(TextPos, OptTable), Element = case NumOptionals - OptPos of @@ -1225,7 +1283,7 @@ comp_call_pre_post(noext, Prop, _, _, TextPos, OptTable, NumOptionals, Ext) -> emit([";",nl, "0 ->",nl, "{"]), - gen_dec_component_no_val(Ext, Prop), + gen_dec_component_no_val(Ext, Type, Prop), emit([",",{curr,bytes},"}",nl, "end"]), St @@ -1247,10 +1305,10 @@ comp_call_pre_post({ext,_,_}, Prop, Pos, Type, _, _, _, Ext) -> components=ExtGroupCompList2}} when is_integer(Number2)-> emit("{extAddGroup,"), - gen_dec_extaddGroup_no_val(Ext, ExtGroupCompList2), + gen_dec_extaddGroup_no_val(Ext, Type, ExtGroupCompList2), emit("}"); _ -> - gen_dec_component_no_val(Ext, Prop) + gen_dec_component_no_val(Ext, Type, Prop) end, emit([",",{curr,bytes},"}",nl, "end"]), @@ -1265,21 +1323,22 @@ is_mandatory_predef_tab_c(_, _, {"got objfun through args","ObjFun"}) -> is_mandatory_predef_tab_c(_,_,_) -> true. -gen_dec_extaddGroup_no_val(Ext,[#'ComponentType'{prop=Prop}])-> - gen_dec_component_no_val(Ext,Prop), +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,[#'ComponentType'{prop=Prop}|Rest])-> - gen_dec_component_no_val(Ext,Prop), - emit({","}), - gen_dec_extaddGroup_no_val(Ext,Rest); -gen_dec_extaddGroup_no_val(_, []) -> +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(_,{'DEFAULT',DefVal}) -> +gen_dec_component_no_val(_, Type, {'DEFAULT',DefVal0}) -> + DefVal = asn1ct_gen:conform_value(Type, DefVal0), emit([{asis,DefVal}]); -gen_dec_component_no_val(_,'OPTIONAL') -> +gen_dec_component_no_val(_, _, 'OPTIONAL') -> emit({"asn1_NOVALUE"}); -gen_dec_component_no_val({ext,_,_},mandatory) -> +gen_dec_component_no_val({ext,_,_}, _, mandatory) -> emit({"asn1_NOVALUE"}). @@ -1503,12 +1562,12 @@ gen_enc_choices([H|T], Erule, TopType, Pos, Constr, Ext) -> no -> case Type#type.tablecinf of [{objfun,_}|_] -> - {"got objfun through args","ObjFun"}; + {"got objfun through args",{var,"ObjFun"}}; _ -> false end; _ -> - {no_attr,"ObjFun"} + {no_attr,{var,"ObjFun"}} end, DoExt = case Constr of ext -> Ext; @@ -1524,7 +1583,7 @@ gen_enc_choices([H|T], Erule, TopType, Pos, Constr, Ext) -> [{put_bits,0,1,[1]}| asn1ct_imm:per_enc_integer(Pos, Constr, Aligned)] end, - Body = gen_enc_line_imm(Erule, TopType, Cname, Type, 'ChoiceVal', + 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)]; @@ -1741,3 +1800,13 @@ value_match1(Value,[],Acc,Depth) -> Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")")); value_match1(Value,[{VI,_}|VIs],Acc,Depth) -> value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1). + +enc_dig_out_value([], Value) -> + {[],Value}; +enc_dig_out_value([{N,_}|T], Value) -> + {Imm0,Dst0} = enc_dig_out_value(T, Value), + {Imm,Dst} = asn1ct_imm:enc_element(N, Dst0), + {Imm0++Imm,Dst}. + +make_var(Base) -> + {var,atom_to_list(asn1ct_gen:mk_var(asn1ct_name:curr(Base)))}. diff --git a/lib/asn1/src/asn1ct_func.erl b/lib/asn1/src/asn1ct_func.erl index dbadedb683..33f998722a 100644 --- a/lib/asn1/src/asn1ct_func.erl +++ b/lib/asn1/src/asn1ct_func.erl @@ -48,7 +48,7 @@ need(MFA) -> call_gen(Prefix, Key, Gen, Args) when is_function(Gen, 2) -> F = req({gen_func,Prefix,Key,Gen}), - asn1ct_gen:emit([F,"(",call_args(Args, ""),")"]). + asn1ct_gen:emit([{asis,F},"(",call_args(Args, ""),")"]). call_gen(Prefix, Key, Gen) when is_function(Gen, 2) -> req({gen_func,Prefix,Key,Gen}). diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index e6ec0cb12b..71d870b4ce 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -23,6 +23,7 @@ -export([demit/1, emit/1, + open_output_file/1,close_output_file/0, get_inner/1,type/1,def_to_tag/1,prim_bif/1, list2name/1, list2rname/1, @@ -33,7 +34,9 @@ insert_once/2, ct_gen_module/1, index2suffix/1, - get_record_name_prefix/0]). + get_record_name_prefix/0, + conform_value/2, + named_bitstring_value/2]). -export([pgen/5, mk_var/1, un_hyphen_var/1]). @@ -68,8 +71,7 @@ pgen_module(OutFile,Erules,Module, HrlGenerated = pgen_hrl(Erules,Module,TypeOrVal,Options,Indent), asn1ct_name:start(), ErlFile = lists:concat([OutFile,".erl"]), - Fid = fopen(ErlFile), - put(gen_file_out,Fid), + open_output_file(ErlFile), asn1ct_func:start_link(), gen_head(Erules,Module,HrlGenerated), pgen_exports(Erules,Module,TypeOrVal), @@ -83,8 +85,10 @@ pgen_module(OutFile,Erules,Module, "%%%",nl, "%%% Run-time functions.",nl, "%%%",nl]), - asn1ct_func:generate(Fid), - file:close(Fid), + Fd = get(gen_file_out), + asn1ct_func:generate(Fd), + close_output_file(), + _ = erase(outfile), asn1ct:verbose("--~p--~n",[{generated,ErlFile}],Options). @@ -786,7 +790,8 @@ gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,typedef) -> pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) -> - emit(["-export([encoding_rule/0,bit_string_format/0]).",nl]), + emit(["-export([encoding_rule/0,bit_string_format/0," + " legacy_erlang_types/0]).",nl]), case Types of [] -> ok; _ -> @@ -907,49 +912,45 @@ pgen_dispatcher(Erules,_Module,{[],_Values,_,_,_Objects,_ObjectSets}) -> pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) -> emit(["-export([encode/2,decode/2]).",nl,nl]), gen_info_functions(Erules), - NoFinalPadding = lists:member(no_final_padding,get(encoding_options)), - {Call,BytesAsBinary} = - case Erules of - per -> - asn1ct_func:need({Erules,complete,1}), - {["complete(encode_disp(Type, Data))"],"Bytes"}; - ber -> - {"encode_disp(Type,Data)","iolist_to_binary(Bytes)"}; - uper when NoFinalPadding == true -> - asn1ct_func:need({Erules,complete_NFP,1}), - {"complete_NFP(encode_disp(Type, Data))","Bytes"}; - uper -> - asn1ct_func:need({Erules,complete,1}), - {["complete(encode_disp(Type, Data))"],"Bytes"} - end, - emit(["encode(Type,Data) ->",nl, - "try ",Call," of",nl, - case erule(Erules) of - ber -> - [" {Bytes,_Len} ->",nl, - " {ok,",BytesAsBinary,"}",nl]; - per -> - [" Bytes ->",nl, - " {ok,",BytesAsBinary,"}",nl] - end, - " catch",nl, - " Class:Exception when Class =:= error; Class =:= exit ->",nl, - " case Exception of",nl, - " {error,Reason}=Error ->",nl, - " Error;",nl, - " Reason ->",nl, - " {error,{asn1,Reason}}",nl, - " end",nl, - "end.",nl,nl]), - - Return_rest = lists:member(undec_rest,get(encoding_options)), + + Options = get(encoding_options), + NoFinalPadding = lists:member(no_final_padding, Options), + NoOkWrapper = proplists:get_bool(no_ok_wrapper, Options), + + Call = case Erules of + per -> + asn1ct_func:need({Erules,complete,1}), + "complete(encode_disp(Type, Data))"; + ber -> + "iolist_to_binary(element(1, encode_disp(Type, Data)))"; + uper when NoFinalPadding == true -> + asn1ct_func:need({Erules,complete_NFP,1}), + "complete_NFP(encode_disp(Type, Data))"; + uper -> + asn1ct_func:need({Erules,complete,1}), + "complete(encode_disp(Type, Data))" + end, + + emit(["encode(Type, Data) ->",nl]), + case NoOkWrapper of + true -> + emit([" ",Call,"."]); + false -> + emit(["try ",Call," of",nl, + " Bytes ->",nl, + " {ok,Bytes}",nl, + try_catch()]) + end, + emit([nl,nl]), + + Return_rest = proplists:get_bool(undec_rest, Options), Data = case {Erules,Return_rest} of {ber,true} -> "Data0"; _ -> "Data" end, emit(["decode(Type,",Data,") ->",nl]), - DecAnonymous = + DecWrap = case {Erules,Return_rest} of {ber,false} -> asn1ct_func:need({ber,ber_decode_nif,1}), @@ -961,49 +962,26 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) -> _ -> "Data" end, - DecWrap = case Erules of - ber -> - DecAnonymous; - _ -> "Data" - end, - - emit(["case catch decode_disp(Type,",DecWrap,") of",nl, - " {'EXIT',{error,Reason}} ->",nl, - " {error,Reason};",nl, - " {'EXIT',Reason} ->",nl, - " {error,{asn1,Reason}};",nl]), - case {Erules,Return_rest} of - {ber,false} -> - emit([" Result ->",nl, - " {ok,Result}",nl]); - {ber,true} -> - emit([" Result ->",nl, - " {ok,Result,Rest}",nl]); - {_,false} -> - emit([" {X,_Rest} ->",nl, - " {ok,X};",nl, - " {X,_Rest,_Len} ->",nl, - " {ok,X}",nl]); - {per,true} -> - emit([" {X,{_,Rest}} ->",nl, - " {ok,X,Rest};",nl, - " {X,{_,Rest},_Len} ->",nl, - " {ok,X,Rest};",nl, - " {X,Rest} ->",nl, - " {ok,X,Rest};",nl, - " {X,Rest,_Len} ->",nl, - " {ok,X,Rest}",nl]); - {uper,true} -> - emit([" {X,{_,Rest}} ->",nl, - " {ok,X,Rest};",nl, - " {X,{_,Rest},_Len} ->",nl, - " {ok,X,Rest};",nl, - " {X,Rest} ->",nl, - " {ok,X,Rest};",nl, - " {X,Rest,_Len} ->",nl, - " {ok,X,Rest}",nl]) + emit([case NoOkWrapper of + false -> "try"; + true -> "case" + end, " decode_disp(Type, ",DecWrap,") of",nl]), + case erule(Erules) of + ber -> + emit([" Result ->",nl]); + per -> + emit([" {Result,Rest} ->",nl]) + end, + case Return_rest of + false -> result_line(NoOkWrapper, ["Result"]); + true -> result_line(NoOkWrapper, ["Result","Rest"]) + end, + case NoOkWrapper of + false -> + emit([nl,try_catch(),nl,nl]); + true -> + emit([nl,"end.",nl,nl]) end, - emit(["end.",nl,nl]), gen_decode_partial_incomplete(Erules), @@ -1016,14 +994,38 @@ pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) -> gen_dispatcher(Types,"encode_disp","enc_",""), gen_dispatcher(Types,"decode_disp","dec_","") end, - emit([nl]), - emit({nl,nl}). + emit([nl,nl]). + +result_line(NoOkWrapper, Items) -> + S = [" "|case NoOkWrapper of + false -> result_line_1(["ok"|Items]); + true -> result_line_1(Items) + end], + emit(lists:flatten(S)). + +result_line_1([SingleItem]) -> + SingleItem; +result_line_1(Items) -> + ["{",string:join(Items, ","),"}"]. + +try_catch() -> + [" catch",nl, + " Class:Exception when Class =:= error; Class =:= exit ->",nl, + " case Exception of",nl, + " {error,Reason}=Error ->",nl, + " Error;",nl, + " Reason ->",nl, + " {error,{asn1,Reason}}",nl, + " end",nl, + "end."]. gen_info_functions(Erules) -> emit(["encoding_rule() -> ", {asis,Erules},".",nl,nl, "bit_string_format() -> ", - {asis,asn1ct:get_bit_string_format()},".",nl,nl]). + {asis,asn1ct:get_bit_string_format()},".",nl,nl, + "legacy_erlang_types() -> ", + {asis,asn1ct:use_legacy_types()},".",nl,nl]). gen_decode_partial_incomplete(ber) -> case {asn1ct:read_config_data(partial_incomplete_decode), @@ -1122,8 +1124,7 @@ pgen_info() -> open_hrl(OutFile,Module) -> File = lists:concat([OutFile,".hrl"]), - Fid = fopen(File), - put(gen_file_out,Fid), + open_output_file(File), gen_hrlhead(Module). %% EMIT functions ************************ @@ -1196,15 +1197,19 @@ call_args([A|As], Sep) -> [Sep,do_emit(A)|call_args(As, ", ")]; call_args([], _) -> []. -fopen(F) -> +open_output_file(F) -> case file:open(F, [write,raw,delayed_write]) of - {ok, Fd} -> + {ok,Fd} -> + put(gen_file_out, Fd), Fd; {error, Reason} -> io:format("** Can't open file ~p ~n", [F]), exit({error,Reason}) end. +close_output_file() -> + ok = file:close(erase(gen_file_out)). + pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) -> put(currmod,Module), {Types,Values,Ptypes,_,_,_} = TypeOrVal, @@ -1227,8 +1232,7 @@ pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) -> 0 -> 0; Y -> - Fid = get(gen_file_out), - file:close(Fid), + close_output_file(), asn1ct:verbose("--~p--~n", [{generated,lists:concat([get(outfile),".hrl"])}], Options), @@ -1488,8 +1492,14 @@ gen_prim_check_call(PrimType, Default, Element, Type) -> end, check_call(check_int, [Default,Element,{asis,NNL}]); 'BIT STRING' -> - {_,NBL} = Type#type.def, - check_call(check_bitstring, [Default,Element,{asis,NBL}]); + case Type#type.def of + {_,[]} -> + check_call(check_bitstring, + [Default,Element]); + {_,[_|_]=NBL} -> + check_call(check_named_bitstring, + [Default,Element,{asis,NBL}]) + end; 'OCTET STRING' -> check_call(check_octetstring, [Default,Element]); 'NULL' -> @@ -1643,9 +1653,33 @@ unify_if_string(PrimType) -> Other -> Other end. - - - +conform_value(#type{def={'BIT STRING',[]}}, Bs) -> + case asn1ct:get_bit_string_format() of + compact when is_binary(Bs) -> + {0,Bs}; + compact when is_bitstring(Bs) -> + Sz = bit_size(Bs), + Unused = 8 - bit_size(Bs), + {Unused,<<Bs:Sz/bits,0:Unused>>}; + legacy -> + [B || <<B:1>> <= Bs]; + bitstring when is_bitstring(Bs) -> + Bs + end; +conform_value(_, Value) -> Value. + +named_bitstring_value(List, Names) -> + Int = lists:foldl(fun(N, A) -> + {N,Pos} = lists:keyfind(N, 1, Names), + A bor (1 bsl Pos) + end, 0, List), + named_bitstring_value_1(<<>>, Int). + +named_bitstring_value_1(Bs, 0) -> + Bs; +named_bitstring_value_1(Bs, Int) -> + B = Int band 1, + named_bitstring_value_1(<<Bs/bitstring,B:1>>, Int bsr 1). get_inner(A) when is_atom(A) -> A; get_inner(Ext) when is_record(Ext,'Externaltypereference') -> Ext; diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index de81259fcb..bea0ec8968 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -206,10 +206,32 @@ gen_encode_prim(_Erules, #type{}=D, DoTag, Value) -> {call,ber,encode_tags, [DoTag,{curr,realval},{curr,realsize}]},nl, "end"]); + {'BIT STRING',[]} -> + case asn1ct:use_legacy_types() of + false when BitStringConstraint =:= [] -> + call(encode_unnamed_bit_string, [Value,DoTag]); + false -> + call(encode_unnamed_bit_string, + [{asis,BitStringConstraint},Value,DoTag]); + true -> + call(encode_bit_string, + [{asis,BitStringConstraint},Value, + {asis,[]},DoTag]) + end; {'BIT STRING',NamedNumberList} -> - call(encode_bit_string, - [{asis,BitStringConstraint},Value, - {asis,NamedNumberList},DoTag]); + case asn1ct:use_legacy_types() of + false when BitStringConstraint =:= [] -> + call(encode_named_bit_string, + [Value,{asis,NamedNumberList},DoTag]); + false -> + call(encode_named_bit_string, + [{asis,BitStringConstraint},Value, + {asis,NamedNumberList},DoTag]); + true -> + call(encode_bit_string, + [{asis,BitStringConstraint},Value, + {asis,NamedNumberList},DoTag]) + end; 'NULL' -> call(encode_null, [Value,DoTag]); 'OBJECT IDENTIFIER' -> @@ -471,7 +493,6 @@ gen_dec_prim(_Erules, Att, BytesVar, DoTag, _TagIn, _Form, _OptOrMand) -> _ -> "" end, NewTypeName = case Typename of - 'OCTET STRING' -> restricted_string; 'NumericString' -> restricted_string; 'TeletexString' -> restricted_string; 'T61String' -> restricted_string; @@ -529,6 +550,19 @@ gen_dec_prim(_Erules, Att, BytesVar, DoTag, _TagIn, _Form, _OptOrMand) -> 'RELATIVE-OID' -> emit(["decode_relative_oid(",BytesVar,","]), need(decode_relative_oid, 2); + 'OCTET STRING' -> + F = case asn1ct:use_legacy_types() of + false -> decode_octet_string; + true -> decode_restricted_string + end, + emit([{asis,F},"(",BytesVar,","]), + case Constraint of + [] -> + need(F, 2); + _ -> + emit([{asis,Constraint},","]), + need(F, 3) + end; restricted_string -> emit(["decode_restricted_string",AsBin,"(",BytesVar,","]), case Constraint of @@ -1090,13 +1124,11 @@ gen_objset_enc(Erules, ObjSetName, UniqueName, %% See X.681 Annex E for the following case gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, _ClFields,_NthObj,Acc) -> - emit(["'getenc_",ObjSetName,"'(_) ->",nl]), - emit({indent(3),"fun(_, Val, _RestPrimFieldName) ->",nl}), - emit({indent(6),"Len = case Val of",nl,indent(9), - "Bin when is_binary(Bin) -> byte_size(Bin);",nl,indent(9), - "_ -> length(Val)",nl,indent(6),"end,"}), - emit({indent(6),"{Val,Len}",nl}), - emit({indent(3),"end.",nl,nl}), + emit(["'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), @@ -1158,13 +1190,8 @@ gen_inlined_enc_funs1(Fields, [{typefield,Name,_}|Rest], ObjSetName, %% were no type in the table and we therefore generate %% code that returns the input for application %% treatment. - emit([indent(9),{asis,Name}," ->",nl, - indent(12),"Len = case Val of",nl, - indent(15),"Bin when is_binary(Bin) -> " - "byte_size(Bin);",nl, - indent(15),"_ -> length(Val)",nl, - indent(12),"end,",nl, - indent(12),"{Val,Len}"]), + 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); @@ -1175,6 +1202,25 @@ gen_inlined_enc_funs1(_, [], _, _, NthObj, Acc) -> indent(3),"end"]), {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, @@ -1258,14 +1304,9 @@ gen_objset_dec(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, _ClFields,_NthObj) -> emit(["'getdec_",ObjSetName,"'(_) ->",nl]), emit([indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]), - - emit([indent(4),"case Bytes of",nl, - indent(6),"Bin when is_binary(Bin) -> ",nl, - indent(8),"Bin;",nl, - indent(6),"_ ->",nl, - indent(8),{call,ber,ber_encode,["Bytes"]},nl, - indent(4),"end",nl]), - emit([indent(2),"end.",nl,nl]), + emit_dec_open_type(4), + emit([nl, + indent(2),"end.",nl,nl]), ok; gen_objset_dec(_, ObjSetName, UniqueName, [], _, _, _) -> emit_default_getdec(ObjSetName, UniqueName), @@ -1312,12 +1353,8 @@ gen_inlined_dec_funs1(Fields, [{typefield,Name,Prop}|Rest], end, 0; false -> - emit([indent(9),{asis,Name}," ->",nl, - indent(12),"Len = case Bytes of",nl, - indent(15),"B when is_binary(B) -> byte_size(B);",nl, - indent(15),"_ -> length(Bytes)",nl, - indent(12),"end,",nl, - indent(12),"{Bytes,[],Len}"]), + emit([indent(9),{asis,Name}," ->",nl]), + emit_dec_open_type(11), 0 end, gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N); @@ -1328,6 +1365,27 @@ gen_inlined_dec_funs1(_, [], _, _, NthObj) -> indent(3),"end"]), 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, diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 8b999ddbf0..519ce9f054 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -99,7 +99,7 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> gen_encode_prim(Erules, D) -> - Value = asn1ct_gen:mk_var(asn1ct_name:curr(val)), + Value = {var,atom_to_list(asn1ct_gen:mk_var(asn1ct_name:curr(val)))}, gen_encode_prim(Erules, D, Value). gen_encode_prim(Erules, #type{}=D, Value) -> @@ -132,7 +132,14 @@ gen_encode_prim_imm(Val, #type{def=Type0,constraint=Constraint}, Aligned) -> ToBinary = {real_common,encode_real}, asn1ct_imm:per_enc_restricted_string(Val, ToBinary, Aligned); {'BIT STRING',NNL} -> - asn1ct_imm:per_enc_bit_string(Val, NNL, Constraint, Aligned); + case asn1ct:use_legacy_types() of + false -> + asn1ct_imm:per_enc_bit_string(Val, NNL, + Constraint, Aligned); + true -> + asn1ct_imm:per_enc_legacy_bit_string(Val, NNL, + Constraint, Aligned) + end; 'NULL' -> asn1ct_imm:per_enc_null(Val, Aligned); 'OBJECT IDENTIFIER' -> @@ -144,15 +151,21 @@ gen_encode_prim_imm(Val, #type{def=Type0,constraint=Constraint}, Aligned) -> 'BOOLEAN' -> asn1ct_imm:per_enc_boolean(Val, Aligned); 'OCTET STRING' -> - asn1ct_imm:per_enc_octet_string(Val, Constraint, Aligned); + case asn1ct:use_legacy_types() of + false -> + asn1ct_imm:per_enc_octet_string(Val, Constraint, Aligned); + true -> + asn1ct_imm:per_enc_legacy_octet_string(Val, Constraint, + Aligned) + end; 'ASN1_OPEN_TYPE' -> case Constraint of [#'Externaltypereference'{type=Tname}] -> EncFunc = enc_func(Tname), - Imm = [{apply,EncFunc,[{expr,Val}]}], + Imm = [{apply,{local,EncFunc,[]},[Val]}], asn1ct_imm:per_enc_open_type(Imm, Aligned); [] -> - Imm = [{call,erlang,iolist_to_binary,[{expr,Val}]}], + Imm = [{call,erlang,iolist_to_binary,[Val]}], asn1ct_imm:per_enc_open_type(Imm, Aligned) end end. @@ -325,7 +338,10 @@ gen_dec_imm_1('GeneralizedTime', Constraint, Aligned) -> gen_dec_imm_1('OCTET STRING', Constraint, Aligned) -> SzConstr = asn1ct_imm:effective_constraint(bitstring, Constraint), Imm = asn1ct_imm:per_dec_octet_string(SzConstr, Aligned), - {convert,binary_to_list,Imm}; + case asn1ct:use_legacy_types() of + false -> {convert,{binary,copy},Imm}; + true -> {convert,binary_to_list,Imm} + end; gen_dec_imm_1('TeletexString', _Constraint, Aligned) -> gen_dec_restricted_string(Aligned); gen_dec_imm_1('T61String', _Constraint, Aligned) -> diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl index 892178f61b..fde39c674e 100644 --- a/lib/asn1/src/asn1ct_imm.erl +++ b/lib/asn1/src/asn1ct_imm.erl @@ -26,17 +26,19 @@ per_dec_octet_string/2,per_dec_open_type/1,per_dec_real/1, per_dec_restricted_string/1]). -export([per_dec_constrained/3,per_dec_normally_small_number/1]). --export([per_enc_bit_string/4,per_enc_boolean/2, +-export([per_enc_bit_string/4,per_enc_legacy_bit_string/4, + per_enc_boolean/2, per_enc_choice/3,per_enc_enumerated/3, per_enc_integer/3,per_enc_integer/4, per_enc_null/2, per_enc_k_m_string/4,per_enc_octet_string/3, + per_enc_legacy_octet_string/3, per_enc_open_type/2, per_enc_restricted_string/3, per_enc_small_number/2]). -export([per_enc_extension_bit/2,per_enc_extensions/4,per_enc_optional/3]). -export([per_enc_sof/5]). --export([enc_absent/3,enc_append/1,enc_bind_var/1]). +-export([enc_absent/3,enc_append/1,enc_element/2]). -export([enc_cg/2]). -export([optimize_alignment/1,optimize_alignment/2, dec_slim_cg/2,dec_code_gen/2]). @@ -157,7 +159,35 @@ per_dec_restricted_string(Aligned) -> %%% Encoding. %%% -per_enc_bit_string(Val0, [], Constraint0, Aligned) -> +per_enc_bit_string(Val, [], Constraint0, Aligned) -> + {B,[[],Bits]} = mk_vars([], [bits]), + Constraint = effective_constraint(bitstring, Constraint0), + B ++ [{call,erlang,bit_size,[Val],Bits}| + per_enc_length(Val, 1, Bits, Constraint, Aligned, 'BIT STRING')]; +per_enc_bit_string(Val0, NNL0, Constraint0, Aligned) -> + {B,[Val,Bs,Bits,Positions]} = mk_vars(Val0, [bs,bits,positions]), + NNL = lists:keysort(2, NNL0), + Constraint = effective_constraint(bitstring, Constraint0), + ExtraArgs = case constr_min_size(Constraint) of + no -> []; + Lb -> [Lb] + end, + ToBs = case ExtraArgs of + [] -> + {call,per_common,bs_drop_trailing_zeroes,[Val]}; + [Lower] -> + {call,per_common,adjust_trailing_zeroes,[Val,Lower]} + end, + B ++ [{'try', + [bit_string_name2pos_fun(NNL, Val)], + {Positions, + [{call,per_common,bitstring_from_positions, + [Positions|ExtraArgs]}]}, + [ToBs],Bs}, + {call,erlang,bit_size,[Bs],Bits}| + per_enc_length(Bs, 1, Bits, Constraint, Aligned, 'BIT STRING')]. + +per_enc_legacy_bit_string(Val0, [], Constraint0, Aligned) -> {B,[Val,Bs,Bits]} = mk_vars(Val0, [bs,bits]), Constraint = effective_constraint(bitstring, Constraint0), ExtraArgs = case constr_min_size(Constraint) of @@ -167,7 +197,7 @@ per_enc_bit_string(Val0, [], Constraint0, Aligned) -> B ++ [{call,per_common,to_bitstring,[Val|ExtraArgs],Bs}, {call,erlang,bit_size,[Bs],Bits}| per_enc_length(Bs, 1, Bits, Constraint, Aligned, 'BIT STRING')]; -per_enc_bit_string(Val0, NNL0, Constraint0, Aligned) -> +per_enc_legacy_bit_string(Val0, NNL0, Constraint0, Aligned) -> {B,[Val,Bs,Bits,Positions]} = mk_vars(Val0, [bs,bits,positions]), NNL = lists:keysort(2, NNL0), Constraint = effective_constraint(bitstring, Constraint0), @@ -256,35 +286,33 @@ per_enc_k_m_string(Val0, StringType, Constraint, Aligned) -> B ++ [{call,erlang,length,[Val],Len},Enc] end ++ per_enc_length(Bin, Unit, Len, SzConstraint, Aligned, k_m_string). -per_enc_open_type([], Aligned) -> - [{put_bits,1,8,unit(1, Aligned)},{put_bits,0,8,[1]}]; -per_enc_open_type([{'cond', - [['_', - {put_bits,0,0,_}, - {call,per_common,encode_unconstrained_number,_}=Call]]}], - Aligned) -> - %% We KNOW that encode_unconstrained_number/1 will return an IO list; - %% therefore the call to complete/1 can be replaced with a cheaper - %% call to iolist_to_binary/1. - {Dst,Imm} = per_enc_open_type_output([Call], []), - ToBin = {erlang,iolist_to_binary}, - Imm ++ per_enc_open_type(Dst, ToBin, Aligned); -per_enc_open_type([{call,erlang,iolist_to_binary,Args}], Aligned) -> - {_,[_,Bin,Len]} = mk_vars('dummy', [bin,len]), - [{call,erlang,iolist_to_binary,Args,Bin}, - {call,erlang,byte_size,[Bin],Len}|per_enc_length(Bin, 8, Len, Aligned)]; per_enc_open_type(Imm0, Aligned) -> - try - {Prefix,Imm1} = split_off_nonbuilding(Imm0), - Prefix ++ enc_open_type(Imm1, Aligned) - catch - throw:impossible -> - {Dst,Imm} = per_enc_open_type_output(Imm0, []), - ToBin = {enc_mod(Aligned),complete}, - Imm ++ per_enc_open_type(Dst, ToBin, Aligned) - end. + Imm = case Aligned of + true -> + %% Temporarily make the implicit 'align' done by + %% complete/1 explicit to facilitate later + %% optimizations: the absence of 'align' can be used + %% as an indication that complete/1 can be replaced + %% with a cheaper operation such as + %% iolist_to_binary/1. The redundant 'align' will be + %% optimized away later. + Imm0 ++ [{put_bits,0,0,[1,align]}]; + false -> + Imm0 + end, + {[],[[],Val,Len,Bin]} = mk_vars([], [output,len,bin]), + [{list,Imm,Val}, + {call,enc_mod(Aligned),complete,[Val],Bin}, + {call,erlang,byte_size,[Bin],Len}| + per_enc_length(Bin, 8, Len, Aligned)]. + +per_enc_octet_string(Bin, Constraint0, Aligned) -> + {B,[[],Len]} = mk_vars([], [len]), + Constraint = effective_constraint(bitstring, Constraint0), + B ++ [{call,erlang,byte_size,[Bin],Len}| + per_enc_length(Bin, 8, Len, Constraint, Aligned, 'OCTET STRING')]. -per_enc_octet_string(Val0, Constraint0, Aligned) -> +per_enc_legacy_octet_string(Val0, Constraint0, Aligned) -> {B,[Val,Bin,Len]} = mk_vars(Val0, [bin,len]), Constraint = effective_constraint(bitstring, Constraint0), B ++ [{call,erlang,iolist_to_binary,[Val],Bin}, @@ -316,20 +344,27 @@ per_enc_extensions(Val0, Pos0, NumBits, Aligned) when NumBits > 0 -> _ -> [{put_bits,Bitmap,NumBits,[1]}] end, B++[{call,per_common,extension_bitmap,[Val,Pos,Pos+NumBits],Bitmap}, - {'cond',[[{eq,Bitmap,0}], - ['_'|Length ++ PutBits]],{var,"Extensions"}}]. + {list,[{'cond',[[{eq,Bitmap,0}], + ['_'|Length ++ PutBits]]}], + {var,"Extensions"}}]. -per_enc_optional(Val0, {Pos,Def}, _Aligned) when is_integer(Pos) -> - Val1 = lists:concat(["element(",Pos,", ",Val0,")"]), - {B,[Val]} = mk_vars(Val1, []), +per_enc_optional(Val0, {Pos,DefVals}, _Aligned) when is_integer(Pos), + is_list(DefVals) -> + {B,Val} = enc_element(Pos, Val0), Zero = {put_bits,0,1,[1]}, One = {put_bits,1,1,[1]}, - B++[{'cond',[[{eq,Val,asn1_DEFAULT},Zero], - [{eq,Val,Def},Zero], - ['_',One]]}]; + B++[{'cond', + [[{eq,Val,DefVal},Zero] || DefVal <- DefVals] ++ [['_',One]]}]; +per_enc_optional(Val0, {Pos,{call,M,F,A}}, _Aligned) when is_integer(Pos) -> + {B,Val} = enc_element(Pos, Val0), + {[],[[],Tmp]} = mk_vars([], [tmp]), + Zero = {put_bits,0,1,[1]}, + One = {put_bits,1,1,[1]}, + B++[{call,M,F,[Val|A],Tmp}, + {'cond', + [[{eq,Tmp,true},Zero],['_',One]]}]; per_enc_optional(Val0, Pos, _Aligned) when is_integer(Pos) -> - Val1 = lists:concat(["element(",Pos,", ",Val0,")"]), - {B,[Val]} = mk_vars(Val1, []), + {B,Val} = enc_element(Pos, Val0), Zero = {put_bits,0,1,[1]}, One = {put_bits,1,1,[1]}, B++[{'cond',[[{eq,Val,asn1_NOVALUE},Zero], @@ -352,7 +387,12 @@ per_enc_sof(Val0, Constraint, ElementVar, ElementImm, Aligned) -> PreBlock ++ EncLen ++ Lc end. -enc_absent(Val0, AbsVals, Body) -> +enc_absent(Val0, {call,M,F,A}, Body) -> + {B,[Var,Tmp]} = mk_vars(Val0, [tmp]), + B++[{call,M,F,[Var|A],Tmp}, + {'cond', + [[{eq,Tmp,true}],['_'|Body]]}]; +enc_absent(Val0, AbsVals, Body) when is_list(AbsVals) -> {B,[Var]} = mk_vars(Val0, []), Cs = [[{eq,Var,Aval}] || Aval <- AbsVals] ++ [['_'|Body]], B++build_cond(Cs). @@ -378,20 +418,22 @@ enc_append([H|T]) -> [{block,H}|enc_append(T)]; enc_append([]) -> []. -enc_bind_var(Val) -> - {B,[{var,Var}]} = mk_vars(Val, []), - {B,list_to_atom(Var)}. +enc_element(N, Val0) -> + {[],[Val,Dst]} = mk_vars(Val0, [element]), + {[{call,erlang,element,[N,Val],Dst}],Dst}. enc_cg(Imm0, false) -> Imm1 = enc_cse(Imm0), - Imm = enc_pre_cg(Imm1), + Imm2 = enc_pre_cg(Imm1), + Imm = enc_opt(Imm2), enc_cg(Imm); enc_cg(Imm0, true) -> Imm1 = enc_cse(Imm0), Imm2 = enc_hoist_align(Imm1), Imm3 = enc_opt_al(Imm2), Imm4 = per_fixup(Imm3), - Imm = enc_pre_cg(Imm4), + Imm5 = enc_pre_cg(Imm4), + Imm = enc_opt(Imm5), enc_cg(Imm). %%% @@ -868,6 +910,9 @@ dcg_list_outside([{call,Fun,{V,Buf},{Dst,DstBuf}}|T]) -> emit(["{",Dst,",",DstBuf,"} = "]), Fun(V, Buf), iter_dcg_list_outside(T); +dcg_list_outside([{convert,{M,F},V,Dst}|T]) -> + emit([Dst," = ",{asis,M},":",{asis,F},"(",V,")"]), + iter_dcg_list_outside(T); dcg_list_outside([{convert,Op,V,Dst}|T]) -> emit([Dst," = ",Op,"(",V,")"]), iter_dcg_list_outside(T); @@ -959,11 +1004,11 @@ mk_dest(S) -> S. split_off_nonbuilding(Imm) -> lists:splitwith(fun is_nonbuilding/1, Imm). -is_nonbuilding({apply,_,_,_}) -> true; is_nonbuilding({assign,_,_}) -> true; is_nonbuilding({call,_,_,_,_}) -> true; -is_nonbuilding({'cond',_,_}) -> true; is_nonbuilding({lc,_,_,_,_}) -> true; +is_nonbuilding({set,_,_}) -> true; +is_nonbuilding({list,_,_}) -> true; is_nonbuilding({sub,_,_,_}) -> true; is_nonbuilding({'try',_,_,_,_}) -> true; is_nonbuilding(_) -> false. @@ -973,17 +1018,13 @@ mk_vars(Input0, Temps) -> Curr = asn1ct_name:curr(enc), [H|T] = atom_to_list(Curr), Base = [H - ($a - $A)|T ++ "@"], - if - is_atom(Input0) -> - Input = {var,atom_to_list(Input0)}, - {[],[Input|mk_vars_1(Base, Temps)]}; - is_integer(Input0) -> + case Input0 of + {var,Name} when is_list(Name) -> {[],[Input0|mk_vars_1(Base, Temps)]}; - Input0 =:= [] -> + [] -> {[],[Input0|mk_vars_1(Base, Temps)]}; - true -> - Input = mk_var(Base, input), - {[{assign,Input,Input0}],[Input|mk_vars_1(Base, Temps)]} + _ when is_integer(Input0) -> + {[],[Input0|mk_vars_1(Base, Temps)]} end. mk_vars_1(Base, Vars) -> @@ -994,6 +1035,25 @@ mk_var(Base, V) -> per_enc_integer_1(Val, [], Aligned) -> [{'cond',[['_'|per_enc_unconstrained(Val, Aligned)]]}]; +per_enc_integer_1(Val, [{{'SingleValue',[_|_]=Svs}=Constr,[]}], Aligned) -> + %% An extensible constraint such as (1|17, ...). + %% + %% A subtle detail is that the extension root as described in the + %% ASN.1 spec should be used to determine whether a particular value + %% belongs to the extension root (as opposed to the effective + %% constraint, which will be used for the actual encoding). + %% + %% So for the example above, only the integers 1 and 17 should be + %% encoded as root values (extension bit = 0). + + [{'ValueRange',{Lb,Ub}}] = effective_constraint(integer, [Constr]), + Root = [begin + {[],_,Put} = per_enc_constrained(Sv, Lb, Ub, Aligned), + [{eq,Val,Sv},{put_bits,0,1,[1]}|Put] + end || Sv <- Svs], + Cs = Root ++ [['_',{put_bits,1,1,[1]}| + per_enc_unconstrained(Val, Aligned)]], + build_cond(Cs); per_enc_integer_1(Val0, [{{_,_}=Constr,[]}], Aligned) -> {Prefix,Check,Action} = per_enc_integer_2(Val0, Constr, Aligned), Prefix++build_cond([[Check,{put_bits,0,1,[1]}|Action], @@ -1004,7 +1064,7 @@ per_enc_integer_1(Val0, [Constr], Aligned) -> Prefix++build_cond([[Check|Action], ['_',{error,Val0}]]). -per_enc_integer_2(Val, {'SingleValue',Sv}, Aligned) -> +per_enc_integer_2(Val, {'SingleValue',Sv}, Aligned) when is_integer(Sv) -> per_enc_constrained(Val, Sv, Sv, Aligned); per_enc_integer_2(Val0, {'ValueRange',{Lb,'MAX'}}, Aligned) when is_integer(Lb) -> @@ -1111,8 +1171,15 @@ per_enc_length(Bin, Unit, Len, {Lb,Ub}, Aligned, Type) U = unit(Unit, Aligned, Type, Lb*Unit, Ub*Unit), PutBits = [{put_bits,Bin,binary,U}], build_length_cond(Prefix, [[Check|PutLen++PutBits]]); -per_enc_length(Bin, Unit, Len, Sv, Aligned, Type) when is_integer(Sv) -> - NumBits = Sv*Unit, +per_enc_length(Bin, Unit0, Len, Sv, Aligned, Type) when is_integer(Sv) -> + NumBits = Sv*Unit0, + Unit = case NumBits rem 8 of + 0 -> + %% Help out the alignment optimizer. + 8; + _ -> + Unit0 + end, U = unit(Unit, Aligned, Type, NumBits, NumBits), Pb = {put_bits,Bin,binary,U}, [{'cond',[[{eq,Len,Sv},Pb]]}]. @@ -1326,58 +1393,6 @@ opt_choice_2([_|_], _) -> throw(impossible); opt_choice_2([], _) -> []. - -%%% -%%% Helper functions for code generation of open types. -%%% - -per_enc_open_type(Val0, {ToBinMod,ToBinFunc}, Aligned) -> - {B,[Val,Len,Bin]} = mk_vars(Val0, [len,bin]), - B ++ [{call,ToBinMod,ToBinFunc,[Val],Bin}, - {call,erlang,byte_size,[Bin],Len}| - per_enc_length(Bin, 8, Len, Aligned)]. - -enc_open_type([{'cond',Cs}], Aligned) -> - [{'cond',[[C|enc_open_type_1(Act, Aligned)] || [C|Act] <- Cs]}]; -enc_open_type(_, _) -> - throw(impossible). - -enc_open_type_1([{error,_}]=Imm, _) -> - Imm; -enc_open_type_1(Imm, Aligned) -> - NumBits = num_bits(Imm, 0), - Pad = case 8 - (NumBits rem 8) of - 8 -> []; - Pad0 -> [{put_bits,0,Pad0,[1]}] - end, - NumBytes = (NumBits+7) div 8, - enc_length(NumBytes, no, Aligned) ++ Imm ++ Pad. - -num_bits([{put_bits,_,N,[U|_]}|T], Sum) when is_integer(N) -> - num_bits(T, Sum+N*U); -num_bits([_|_], _) -> - throw(impossible); -num_bits([], Sum) -> Sum. - -per_enc_open_type_output([{apply,F,A}], Acc) -> - Dst = output_var(), - {Dst,lists:reverse(Acc, [{apply,F,A,{var,atom_to_list(Dst)}}])}; -per_enc_open_type_output([{call,M,F,A}], Acc) -> - Dst = output_var(), - {Dst,lists:reverse(Acc, [{call,M,F,A,{var,atom_to_list(Dst)}}])}; -per_enc_open_type_output([{'cond',Cs}], Acc) -> - Dst = output_var(), - {Dst,lists:reverse(Acc, [{'cond',Cs,{var,atom_to_list(Dst)}}])}; -per_enc_open_type_output([H|T], Acc) -> - per_enc_open_type_output(T, [H|Acc]). - -output_var() -> - asn1ct_name:new(enc), - Curr = asn1ct_name:curr(enc), - [H|T] = atom_to_list(Curr), - list_to_atom([H - ($a - $A)|T ++ "@output"]). - - %%% %%% Optimize list comprehensions (SEQUENCE OF/SET OF). %%% @@ -1555,16 +1570,16 @@ collect_put_bits(Imm) -> %%% the same element twice. %%% -enc_cse([{assign,{var,V},E}=H|T]) -> - [H|enc_cse_1(T, E, V)]; +enc_cse([{call,erlang,element,Args,V}=H|T]) -> + [H|enc_cse_1(T, Args, V)]; enc_cse(Imm) -> Imm. -enc_cse_1([{assign,Dst,E}|T], E, V) -> - [{assign,Dst,V}|enc_cse_1(T, E, V)]; -enc_cse_1([{block,Bl}|T], E, V) -> - [{block,enc_cse_1(Bl, E, V)}|enc_cse_1(T, E, V)]; -enc_cse_1([H|T], E, V) -> - [H|enc_cse_1(T, E, V)]; +enc_cse_1([{call,erlang,element,Args,Dst}|T], Args, V) -> + [{set,V,Dst}|enc_cse_1(T, Args, V)]; +enc_cse_1([{block,Bl}|T], Args, V) -> + [{block,enc_cse_1(Bl, Args, V)}|enc_cse_1(T, Args, V)]; +enc_cse_1([H|T], Args, V) -> + [H|enc_cse_1(T, Args, V)]; enc_cse_1([], _, _) -> []. @@ -1605,7 +1620,7 @@ enc_pre_cg_2({block,Bl0}, StL, StB) -> enc_pre_cg_1(Bl0, StL, StB); enc_pre_cg_2({call,_,_,_}=Imm, _, _) -> Imm; -enc_pre_cg_2({call_gen,_,_,_,_}=Imm, _, _) -> +enc_pre_cg_2({call_gen,_,_,_,_,_}=Imm, _, _) -> Imm; enc_pre_cg_2({'cond',Cs0}, StL, _StB) -> Cs = [{C,enc_pre_cg_1(Act, StL, outside_seq)} || [C|Act] <- Cs0], @@ -1630,18 +1645,22 @@ enc_pre_cg_2({var,_}=Imm, _, _) -> Imm. enc_make_cons({binary,H}, {binary,T}) -> {binary,H++T}; enc_make_cons({binary,H0}, {cons,{binary,H1},T}) -> - {cons,{binary,H0++H1},T}; + enc_make_cons({binary,H0++H1}, T); +enc_make_cons({binary,H}, {cons,{integer,Int},T}) -> + enc_make_cons({binary,H++[{put_bits,Int,8,[1]}]}, T); enc_make_cons({integer,Int}, {binary,T}) -> {binary,[{put_bits,Int,8,[1]}|T]}; +enc_make_cons({integer,Int}, {cons,{binary,H},T}) -> + enc_make_cons({binary,[{put_bits,Int,8,[1]}|H]}, T); enc_make_cons(H, T) -> {cons,H,T}. -enc_pre_cg_nonbuilding({'cond',Cs0,Dst}, StL) -> - Cs = [{C,enc_pre_cg_1(Act, StL, outside_seq)} || [C|Act] <- Cs0], - {'cond',Cs,Dst}; enc_pre_cg_nonbuilding({lc,B0,Var,List,Dst}, StL) -> B = enc_pre_cg_1(B0, StL, outside_seq), {lc,B,Var,List,Dst}; +enc_pre_cg_nonbuilding({list,List0,Dst}, _StL) -> + List = enc_pre_cg_1(List0, outside_list, outside_seq), + {list,List,Dst}; enc_pre_cg_nonbuilding({'try',Try0,{P,Succ0},Else0,Dst}, StL) -> Try = enc_pre_cg_1(Try0, StL, outside_seq), Succ = enc_pre_cg_1(Succ0, StL, outside_seq), @@ -1649,6 +1668,562 @@ enc_pre_cg_nonbuilding({'try',Try0,{P,Succ0},Else0,Dst}, StL) -> {'try',Try,{P,Succ},Else,Dst}; enc_pre_cg_nonbuilding(Imm, _) -> Imm. +%%% +%%% Optimize calls to complete/1 and surrounding code. There are +%%% several opportunities for optimizations. +%%% +%%% It may be possible to replace the call to complete/1 with +%%% something cheaper (most important for the PER back-end which has +%%% an expensive complete/1 implementation). If we can be sure that +%%% complete/1 will be called with an iolist (no 'align' atoms or +%%% bitstrings in the list), we can call iolist_to_binary/1 +%%% instead. If the list may include bitstrings, we can can call +%%% list_to_bitstring/1 (note that list_to_bitstring/1 does not accept +%%% a binary or bitstring, so we MUST be sure that we only pass it a +%%% list). If complete/1 is called with a binary, we can omit the +%%% call altogether. +%%% +%%% A call to byte_size/1 that follows complete/1 can be eliminated +%%% if the size of the binary produced by complete/1 can be determined +%%% and is constant. +%%% +%%% The code that encodes the length descriptor (a 'cond' instruction) +%%% for a binary produced by complete/1 can be simplified if the lower +%%% and upper bounds for the size of the binary are known. +%%% + +-record(ost, + {sym, + t + }). + +enc_opt(Imm0) -> + {Imm,_} = enc_opt(Imm0, #ost{sym=gb_trees:empty()}), + Imm. + +enc_opt(align, St) -> + {align,St#ost{t=t_align({0,7})}}; +enc_opt({apply,What,As}, St) -> + {{apply,What,subst_list(As, St)},St#ost{t=t_any()}}; +enc_opt({assign,_,_}=Imm, St) -> + {Imm,St}; +enc_opt({binary,PutBits0}, St) -> + PutBits = [{put_bits,subst(V, St),Sz,F} || + {put_bits,V,Sz,F} <- PutBits0], + NumBits = lists:foldl(fun({put_bits,_,Bits,_}, Sum) -> + Sum+Bits + end, 0, PutBits), + {{binary,PutBits},St#ost{t=t_bitstring(NumBits)}}; +enc_opt({block,Bl0}, St0) -> + {Bl,St} = enc_opt(Bl0, St0), + {{block,Bl},St}; +enc_opt({call,binary,encode_unsigned,[Int],Bin}=Imm, St0) -> + Type = get_type(Int, St0), + St = case t_range(Type) of + any -> + set_type(Bin, t_binary(), St0); + {Lb0,Ub0} -> + Lb = bit_size(binary:encode_unsigned(Lb0)), + Ub = bit_size(binary:encode_unsigned(Ub0)), + set_type(Bin, t_binary({Lb,Ub}), St0) + end, + {Imm,St}; +enc_opt({call,erlang,bit_size,[Bin],Dst}=Imm0, St0) -> + Type = get_type(Bin, St0), + case t_range(Type) of + any -> + St1 = set_type(Bin, t_bitstring(), St0), + St = propagate(Dst, + fun(T, S) -> + bit_size_propagate(Bin, T, S) + end, St1), + {Imm0,St}; + {Lb,Ub}=Range -> + St = set_type(Dst, t_integer(Range), St0), + Imm = case Lb of + Ub -> none; + _ -> Imm0 + end, + {Imm,St} + end; +enc_opt({call,erlang,byte_size,[Bin],Dst}=Imm0, St0) -> + Type = get_type(Bin, St0), + case t_range(Type) of + any -> + St1 = set_type(Bin, t_binary(), St0), + St = propagate(Dst, + fun(T, S) -> + byte_size_propagate(Bin, T, S) + end, St1), + {Imm0,St}; + {Lb0,Ub0} -> + Lb = (Lb0+7) div 8, + Ub = (Ub0+7) div 8, + St = set_type(Dst, t_integer({Lb,Ub}), St0), + Imm = case Lb of + Ub -> none; + _ -> Imm0 + end, + {Imm,St} + end; +enc_opt({call,erlang,iolist_to_binary,_}=Imm, St) -> + {Imm,St#ost{t=t_binary()}}; +enc_opt({call,erlang,length,[List],Dst}=Imm0, St0) -> + St1 = propagate(Dst, + fun(T, S) -> + length_propagate(List, T, S) + end, St0), + {Imm0,St1}; +enc_opt({call,per,complete,[Data],Dst}, St0) -> + Type = get_type(Data, St0), + St = set_type(Dst, t_binary(t_range(Type)), St0), + case t_type(Type) of + binary -> + {{set,Data,Dst},St}; + bitlist -> + %% We KNOW that list_to_bitstring/1 will construct + %% a binary (the number of bits is divisible by 8) + %% because per_enc_open_type/2 added an 'align' atom + %% at the end. If that 'align' atom had not been + %% optimized away, the type would have been 'align' + %% instead of 'bitlist'. + {{call,erlang,list_to_bitstring,[Data],Dst},St}; + iolist -> + {{call,erlang,iolist_to_binary,[Data],Dst},St}; + nil -> + Imm = {list,{binary,[{put_bits,0,8,[1]}]},Dst}, + enc_opt(Imm, St0); + _ -> + {{call,per,complete,[Data],Dst},St} + end; +enc_opt({call,uper,complete,[Data],Dst}, St0) -> + Type = get_type(Data, St0), + St = set_type(Dst, t_binary(t_range(Type)), St0), + case t_type(Type) of + binary -> + {{set,Data,Dst},St0}; + iolist -> + {{call,erlang,iolist_to_binary,[Data],Dst},St}; + nil -> + Imm = {list,{binary,[{put_bits,0,8,[1]}]},Dst}, + enc_opt(Imm, St0); + _ -> + %% 'bitlist' or 'any'. + {{call,uper,complete,[Data],Dst},St} + end; +enc_opt({call,per_common,encode_chars,[List,NumBits|_],Dst}=Imm, St0) -> + %% Note: Never used when NumBits =:= 8 (list_to_binary/1 will + %% be used instead). + St1 = set_type(Dst, t_bitstring(), St0), + St = propagate(List, + fun(T, S) -> + char_propagate(Dst, T, NumBits, S) + end, St1), + {Imm,St}; +enc_opt({call,per_common,encode_chars_16bit,[List],Dst}=Imm, St0) -> + St1 = set_type(Dst, t_binary(), St0), + St = propagate(List, + fun(T, S) -> + char_propagate(Dst, T, 16, S) + end, St1), + {Imm,St}; +enc_opt({call,per_common,encode_big_chars,[List],Dst}=Imm, St0) -> + St1 = set_type(Dst, t_binary(), St0), + St = propagate(List, + fun(T, S) -> + char_propagate(Dst, T, 32, S) + end, St1), + {Imm,St}; +enc_opt({call,per_common,encode_fragmented,[_,Unit]}=Imm, St) -> + T = case Unit rem 8 of + 0 -> t_iolist(); + _ -> t_bitlist() + end, + {Imm,St#ost{t=T}}; +enc_opt({call,per_common,encode_unconstrained_number,_}=Imm, St) -> + {Imm,St#ost{t=t_iolist()}}; +enc_opt({call,per_common,bitstring_from_positions,_}=Imm, St) -> + {Imm,St#ost{t=t_bitstring()}}; +enc_opt({call,per_common,to_named_bitstring,_}=Imm, St) -> + {Imm,St#ost{t=t_bitstring()}}; +enc_opt({call,_,_,_}=Imm, St) -> + {Imm,St#ost{t=t_any()}}; +enc_opt({call,_,_,_,_}=Imm, St) -> + {Imm,St#ost{t=undefined}}; +enc_opt({call_gen,N,K,F,L,As}, St) -> + {{call_gen,N,K,F,L,subst(As, St)},St#ost{t=t_any()}}; +enc_opt({'cond',Cs0}, St0) -> + case enc_opt_cs(Cs0, St0) of + [{'_',Imm,Type}] -> + {Imm,St0#ost{t=Type}}; + [{Cond,Imm,Type0}|Cs1] -> + {Cs,Type} = enc_opt_cond_1(Cs1, Type0, [{Cond,Imm}]), + {{'cond',Cs},St0#ost{t=Type}} + end; +enc_opt({cons,H0,T0}, St0) -> + {H,#ost{t=TypeH}=St1} = enc_opt(H0, St0), + {T,#ost{t=TypeT}=St} = enc_opt(T0, St1), + {{cons,H,T},St#ost{t=t_cons(TypeH, TypeT)}}; +enc_opt({error,_}=Imm, St) -> + {Imm,St#ost{t=t_any()}}; +enc_opt({integer,V}, St) -> + {{integer,subst(V, St)},St#ost{t=t_integer()}}; +enc_opt({lc,E0,B,C}, St) -> + {E,_} = enc_opt(E0, St), + {{lc,E,B,C},St#ost{t=t_any()}}; +enc_opt({lc,E0,B,C,Dst}, St) -> + {E,_} = enc_opt(E0, St), + {{lc,E,B,C,Dst},St#ost{t=undefined}}; +enc_opt({list,Imm0,Dst}, St0) -> + {Imm,#ost{t=Type}=St1} = enc_opt(Imm0, St0), + St = set_type(Dst, Type, St1), + {{list,Imm,Dst},St#ost{t=undefined}}; +enc_opt(nil, St) -> + {nil,St#ost{t=t_nil()}}; +enc_opt({seq,H0,T0}, St0) -> + {H,St1} = enc_opt(H0, St0), + {T,St} = enc_opt(T0, St1), + case {H,T} of + {none,_} -> + {T,St}; + {{list,Imm,Data}, + {seq,{call,per,complete,[Data],_},_}} -> + %% Get rid of any explicit 'align' added by per_enc_open_type/2. + {{seq,{list,remove_trailing_align(Imm),Data},T},St}; + {_,_} -> + {{seq,H,T},St} + end; +enc_opt({set,_,_}=Imm, St) -> + {Imm,St#ost{t=undefined}}; +enc_opt({sub,Src0,Int,Dst}, St0) -> + Src = subst(Src0, St0), + Type = get_type(Src, St0), + St = case t_range(Type) of + any -> + propagate(Dst, + fun(T, S) -> + set_type(Src, t_add(T, Int), S) + end, + St0); + {Lb,Ub} -> + set_type(Dst, t_integer({Lb-Int,Ub-Int}), St0) + end, + {{sub,Src,Int,Dst},St#ost{t=undefined}}; +enc_opt({'try',Try0,{P,Succ0},Else0,Dst}, St0) -> + {Try,_} = enc_opt(Try0, St0), + {Succ,_} = enc_opt(Succ0, St0), + {Else,_} = enc_opt(Else0, St0), + {{'try',Try,{P,Succ},Else,Dst},St0#ost{t=undefined}}; +enc_opt({var,_}=Imm, St) -> + Type = get_type(Imm, St), + {subst(Imm, St),St#ost{t=Type}}. + +remove_trailing_align({block,Bl}) -> + {block,remove_trailing_align(Bl)}; +remove_trailing_align({cons,H,{cons,align,nil}}) -> + H; +remove_trailing_align({seq,H,T}) -> + {seq,H,remove_trailing_align(T)}; +remove_trailing_align(Imm) -> Imm. + +bit_size_propagate(Bin, Type, St) -> + case t_range(Type) of + any -> + St; + {Lb,Ub} -> + set_type(Bin, t_bitstring({Lb,Ub}), St) + end. + +byte_size_propagate(Bin, Type, St) -> + case t_range(Type) of + any -> + St; + {Lb,Ub} -> + set_type(Bin, t_binary({Lb*8,Ub*8}), St) + end. + +char_propagate(Dst, T, NumBits, St) -> + case t_range(T) of + any -> + St; + {Sz,Sz} when Sz*NumBits rem 8 =:= 0 -> + Bits = Sz*NumBits, + set_type(Dst, t_binary({Bits,Bits}), St); + {Lb,Ub} -> + Range = {Lb*NumBits,Ub*NumBits}, + case NumBits rem 8 of + 0 -> + set_type(Dst, t_binary(Range), St); + _ -> + set_type(Dst, t_bitstring(Range), St) + end + end. + +length_propagate(List, Type, St) -> + set_type(List, t_list(t_range(Type)), St). + +enc_opt_cond_1([{Cond,{error,_}=Imm,_}|T], St, Acc) -> + enc_opt_cond_1(T, St, [{Cond,Imm}|Acc]); +enc_opt_cond_1([{Cond,Imm,Curr0}|T], Curr1, Acc) -> + Curr = t_join(Curr0, Curr1), + enc_opt_cond_1(T, Curr, [{Cond,Imm}|Acc]); +enc_opt_cond_1([], St, Acc) -> + {lists:reverse(Acc),St}. + +enc_opt_cs([{Cond,Imm0}|T], St0) -> + case eo_eval_cond(Cond, St0) of + false -> + enc_opt_cs(T, St0); + true -> + {Imm,#ost{t=Type}} = enc_opt(Imm0, St0), + [{'_',Imm,Type}]; + maybe -> + St = update_type_info(Cond, St0), + {Imm,#ost{t=Type}} = enc_opt(Imm0, St), + [{Cond,Imm,Type}|enc_opt_cs(T, St0)] + end; +enc_opt_cs([], _) -> []. + +eo_eval_cond('_', _) -> + true; +eo_eval_cond({Op,{var,_}=Var,Val}, St) -> + Type = get_type(Var, St), + case t_range(Type) of + any -> maybe; + {_,_}=Range -> eval_cond_range(Op, Range, Val) + end; +eo_eval_cond({_Op,{expr,_},_Val}, _St) -> maybe. + +eval_cond_range(lt, {Lb,Ub}, Val) -> + if + Ub < Val -> true; + Val =< Lb -> false; + true -> maybe + end; +eval_cond_range(_Op, _Range, _Val) -> maybe. + +update_type_info({ult,{var,_}=Var,Val}, St) -> + Int = t_integer({0,Val-1}), + Type = t_meet(get_type(Var, St), Int), + set_type(Var, Type, St); +update_type_info({lt,{var,_}=Var,Val}, St) -> + Int = t_integer({0,Val-1}), + Type = t_meet(get_type(Var, St), Int), + set_type(Var, Type, St); +update_type_info({eq,{var,_}=Var,Val}, St) when is_integer(Val) -> + Int = t_integer(Val), + Type = t_meet(get_type(Var, St), Int), + set_type(Var, Type, St); +update_type_info({eq,_,_}, St) -> + St; +update_type_info({ge,_,_}, St) -> St. + +subst_list(As, St) -> + [subst(A, St) || A <- As]. + +subst({var,_}=Var, St) -> + Type = get_type(Var, St), + case t_type(Type) of + integer -> + case t_range(Type) of + any -> Var; + {Val,Val} -> Val; + {_,_} -> Var + end; + _ -> + Var + end; +subst(V, _St) -> V. + +set_type({var,Var}, {_,_}=Type, #ost{sym=Sym0}=St0) -> + Sym1 = gb_trees:enter(Var, Type, Sym0), + case gb_trees:lookup({propagate,Var}, Sym1) of + none -> + St0#ost{sym=Sym1}; + {value,Propagate} -> + Sym = gb_trees:delete({propagate,Var}, Sym1), + St = St0#ost{sym=Sym}, + Propagate(Type, St) + end. + +get_type({var,V}, #ost{sym=Sym}) -> + case gb_trees:lookup(V, Sym) of + none -> t_any(); + {value,T} -> T + end. + +propagate({var,Var}, Propagate, #ost{sym=Sym0}=St) when is_function(Propagate, 2) -> + Sym = gb_trees:enter({propagate,Var}, Propagate, Sym0), + St#ost{sym=Sym}. + +%%% +%%% A simple type system. +%%% +%%% Each type descriptions is a tuple {Type,Range}. +%%% Type is one of the following atoms: +%%% +%%% Type name Description +%%% --------- ----------- +%%% any Anything. +%%% +%%% align Basically iodata, but the list may contain bitstrings +%%% and the the atom 'align'. Can be passed to complete/1 +%%% to construct a binary. Only used for aligned PER (per). +%%% +%%% bitstring An Erlang bitstring. +%%% +%%% bitlist A list that may be passed to list_to_bitstring/1 to +%%% construct a bitstring. +%%% NOTE: When analysing aligned PER (per), the number +%%% of bits in the bitlist is always divisible by 8 (if +%%% not, the type will be 'align' instead). +%%% +%%% binary An Erlang binary (the number of bits is divisible by 8). +%%% +%%% iolist An Erlang iolist. +%%% +%%% nil [] +%%% +%%% integer An integer. +%%% +%%% +%%% Range is one of: +%%% +%%% any +%%% {LowerBound,UpperBound} +%%% +%%% + +t_align(Range) -> + {align,t__range(Range)}. + +t_any() -> + {any,any}. + +t_binary() -> + {binary,any}. + +t_binary(Range) -> + {binary,t__range(Range)}. + +t_bitlist() -> + {bitlist,any}. + +t_bitstring() -> + {bitstring,any}. + +t_bitstring(Range0) -> + case t__range(Range0) of + {Bits,Bits}=Range when Bits rem 8 =:= 0 -> + {binary,Range}; + Range -> + {bitstring,Range} + end. + +t_add({integer,{Lb,Ub}}, N) -> + {integer,{Lb+N,Ub+N}}. + +t_cons({_,_}=T1, {_,_}=T2) -> + T = case {t__cons_type(T1),t__cons_type(T2)} of + {_,any} -> any; + {any,_} -> any; + {align,_} -> align; + {_,align} -> align; + {binary,binary} -> iolist; + {binary,bitstring} -> bitlist; + {bitstring,binary} -> bitlist; + {bitstring,bitstring} -> bitlist + end, + {T,t__cons_ranges(t__cons_range(T1), t__cons_range(T2))}. + +t_integer() -> + {integer,any}. + +t_integer(Range) -> + {integer,t__range(Range)}. + +t_iolist() -> + {iolist,any}. + +t_list(Range) -> + {list,t__range(Range)}. + +t_nil() -> + {nil,{0,0}}. + +t_meet({T1,Range1}, {T2,Range2}) -> + {t_meet_types(T1, T2),t_meet_ranges(Range1, Range2)}. + +t_meet_types(integer, integer) -> integer; +t_meet_types(any, integer) -> integer. + +t_meet_ranges(any, Range) -> + Range; +t_meet_ranges({Lb1,Ub1}, {Lb2,Ub2}) -> + if + Lb1 =< Ub2, Lb2 =< Ub1 -> + {max(Lb1, Lb2),Ub1}; + Lb2 =< Ub1, Lb1 =< Ub2 -> + {max(Lb1, Lb2),Ub2} + end. + +t_join({T1,Range1}, {T2,Range2}) -> + T = t_join_types(lists:sort([T1,T2])), + Range = t_join_ranges(Range1, Range2), + {T,Range}. + +t_join_ranges({Lb1,Ub1}, {Lb2,Ub2}) -> + {min(Lb1, Lb2),max(Ub1, Ub2)}; +t_join_ranges(any, _) -> any; +t_join_ranges(_, any) -> any. + +t_join_types([T,T]) -> T; +t_join_types([align,any]) -> any; +t_join_types([align,_]) -> align; +t_join_types([any,_]) -> any; +t_join_types([bitlist,bitstring]) -> any; +t_join_types([bitlist,integer]) -> any; +t_join_types([bitlist,iolist]) -> bitlist; +t_join_types([bitlist,nil]) -> bitlist; +t_join_types([binary,bitlist]) -> bitlist; +t_join_types([binary,bitstring]) -> bitstring; +t_join_types([binary,integer]) -> binary; +t_join_types([binary,iolist]) -> iolist; +t_join_types([binary,nil]) -> iolist; +t_join_types([bitstring,integer]) -> any; +t_join_types([bitstring,iolist]) -> any; +t_join_types([bitstring,nil]) -> any; +t_join_types([integer,_]) -> any; +t_join_types([iolist,nil]) -> iolist. + +t_type({T,_}) -> T. + +t_range({_,Range}) -> Range. + +t__cons_type({align,_}) -> align; +t__cons_type({any,_}) -> any; +t__cons_type({binary,_}) -> binary; +t__cons_type({bitstring,_}) -> bitstring; +t__cons_type({bitlist,_}) -> bitstring; +t__cons_type({integer,_}) -> binary; +t__cons_type({iolist,_}) -> binary; +t__cons_type({nil,_}) -> binary. + +t__cons_range({integer,_}) -> {8,8}; +t__cons_range({_,Range}) -> Range. + +t__cons_ranges({Lb1,Ub1}, {Lb2,Ub2}) -> + {Lb1+Lb2,Ub1+Ub2}; +t__cons_ranges(any, _) -> any; +t__cons_ranges(_, any) -> any. + +t__range({Lb,Ub}=Range) when is_integer(Lb), is_integer(Ub) -> + Range; +t__range(any) -> + any; +t__range(Val) when is_integer(Val) -> + {Val,Val}. + %%% %%% Code generation for encoding. @@ -1670,19 +2245,10 @@ enc_cg(align) -> enc_cg({apply,F0,As0}) -> As = enc_call_args(As0, ""), case F0 of - {M,F} -> - emit([{asis,M},":",{asis,F},"(",As,")"]); - F when is_atom(F) -> - emit([{asis,F},"(",As,")"]) - end; -enc_cg({apply,F0,As0,Dst}) -> - As = enc_call_args(As0, ""), - emit([mk_val(Dst)," = "]), - case F0 of - {M,F} -> - emit([{asis,M},":",{asis,F},"(",As,")"]); - F when is_atom(F) -> - emit([{asis,F},"(",As,")"]) + {local,F,_} when is_atom(F) -> + emit([{asis,F},"(",As,")"]); + {M,F,_} -> + emit([{asis,M},":",{asis,F},"(",As,")"]) end; enc_cg({assign,Dst0,Expr}) -> Dst = mk_val(Dst0), @@ -1696,15 +2262,11 @@ enc_cg({call,M,F,As0,Dst}) -> As = [mk_val(A) || A <- As0], emit([mk_val(Dst)," = "]), asn1ct_func:call(M, F, As); -enc_cg({call_gen,Prefix,Key,Gen,As0}) -> +enc_cg({call_gen,Prefix,Key,Gen,_,As0}) -> As = [mk_val(A) || A <- As0], asn1ct_func:call_gen(Prefix, Key, Gen, As); enc_cg({'cond',Cs}) -> enc_cg_cond(Cs); -enc_cg({'cond',Cs,Dst0}) -> - Dst = mk_val(Dst0), - emit([Dst," = "]), - enc_cg_cond(Cs); enc_cg({error,Error}) when is_function(Error, 0) -> Error(); enc_cg({error,Var0}) -> @@ -1720,12 +2282,17 @@ enc_cg({lc,Body,Var,List,Dst}) -> emit([mk_val(Dst)," = ["]), enc_cg(Body), emit([" || ",mk_val(Var)," <- ",mk_val(List),"]"]); +enc_cg({list,List,Dst}) -> + emit([mk_val(Dst)," = "]), + enc_cg(List); enc_cg(nil) -> emit("[]"); enc_cg({sub,Src0,Int,Dst0}) -> Src = mk_val(Src0), Dst = mk_val(Dst0), emit([Dst," = ",Src," - ",Int]); +enc_cg({set,{var,Src},{var,Dst}}) -> + emit([Dst," = ",Src]); enc_cg({'try',Try,{P,Succ},Else,Dst}) -> emit([mk_val(Dst)," = try "]), enc_cg(Try), @@ -1760,8 +2327,6 @@ enc_call_args([A|As], Sep) -> [Sep,mk_val(A)|enc_call_args(As, ", ")]; enc_call_args([], _) -> []. -enc_cg_cond([{'_',Action}]) -> - enc_cg(Action); enc_cg_cond(Cs) -> emit("if "), enc_cg_cond(Cs, ""), @@ -1817,7 +2382,7 @@ mk_val(Other) -> {asis,Other}. bit_string_name2pos_fun(NNL, Src) -> {call_gen,"bit_string_name2pos_",NNL, - fun(Fd, Name) -> gen_name2pos(Fd, Name, NNL) end,[Src]}. + fun(Fd, Name) -> gen_name2pos(Fd, Name, NNL) end,[],[Src]}. gen_name2pos(Fd, Name, Names) -> Cs0 = gen_name2pos_cs(Names, Name), @@ -1946,19 +2511,12 @@ enc_opt_al(Imm0) -> {Imm,_} = enc_opt_al_1(Imm0, unknown), Imm. -enc_opt_al_1([{'cond',Cs0,Dst},{call,per,complete,[Dst],Bin}|T0], Al0) -> - {Cs1,{M,F}} = enc_opt_al_prepare_cond(Cs0), - {Cs,_} = enc_opt_al_cond(Cs1, 0), - {T,Al} = enc_opt_al_1([{call,M,F,[Dst],Bin}|T0], Al0), - {[{'cond',Cs,Dst}|T],Al}; enc_opt_al_1([H0|T0], Al0) -> {H,Al1} = enc_opt_al(H0, Al0), {T,Al} = enc_opt_al_1(T0, Al1), {H++T,Al}; enc_opt_al_1([], Al) -> {[],Al}. -enc_opt_al({apply,_,_,_}=Imm, Al) -> - {[Imm],Al}; enc_opt_al({assign,_,_}=Imm, Al) -> {[Imm],Al}; enc_opt_al({block,Bl0}, Al0) -> @@ -1980,6 +2538,10 @@ enc_opt_al({'cond',Cs0}, Al0) -> {[{'cond',Cs}],Al}; enc_opt_al({error,_}=Imm, Al) -> {[Imm],Al}; +enc_opt_al({list,Imm0,Dst}, Al) -> + Imm1 = enc_opt_hoist_align(Imm0), + {Imm,_} = enc_opt_al_1(Imm1, 0), + {[{list,Imm,Dst}],Al}; enc_opt_al({put_bits,V,N,[U,align]}, Al0) when Al0 rem 8 =:= 0 -> Al = if is_integer(N) -> N*U; @@ -2006,8 +2568,12 @@ enc_opt_al({put_bits,_,N,[U]}=PutBits, Al) when is_integer(N), is_integer(Al) -> {[PutBits],Al+N*U}; enc_opt_al({put_bits,_,binary,[U]}=PutBits, Al) when U rem 8 =:= 0 -> {[PutBits],Al}; +enc_opt_al({set,_,_}=Imm, Al) -> + {[Imm],Al}; enc_opt_al({sub,_,_,_}=Imm, Al) -> {[Imm],Al}; +enc_opt_al({'try',_,_,_,_}=Imm, Al) -> + {[Imm],Al}; enc_opt_al(Imm, _) -> {[Imm],unknown}. @@ -2031,29 +2597,25 @@ enc_opt_al_cond_1([], _, CAcc, AAcc) -> end, {lists:reverse(CAcc),Al}. -enc_opt_al_prepare_cond(Cs0) -> - try enc_opt_al_prepare_cond_1(Cs0) of - Cs -> - {Cs,{erlang,iolist_to_binary}} +enc_opt_hoist_align([{'cond',Cs0},{put_bits,0,0,[1,align]}]=Imm) -> + try + Cs = [insert_align_last(C) || C <- Cs0], + [{'cond',Cs}] catch throw:impossible -> - {Cs0,{per,complete}} - end. - -enc_opt_al_prepare_cond_1(Cs) -> - [[C|enc_opt_al_prepare_cond_2(Act)] || [C|Act] <- Cs]. - -enc_opt_al_prepare_cond_2([{put_bits,_,binary,[U|_]}|_]) when U rem 8 =/= 0 -> - throw(impossible); -enc_opt_al_prepare_cond_2([{put_bits,_,_,_}=H|T]) -> - [H|enc_opt_al_prepare_cond_2(T)]; -enc_opt_al_prepare_cond_2([{call,per_common,encode_fragmented,_}=H|T]) -> - [H|enc_opt_al_prepare_cond_2(T)]; -enc_opt_al_prepare_cond_2([_|_]) -> - throw(impossible); -enc_opt_al_prepare_cond_2([]) -> - [{put_bits,0,0,[1,align]}]. + Imm + end; +enc_opt_hoist_align(Imm) -> Imm. +insert_align_last([_,{error,_}]=C) -> + C; +insert_align_last([H|T]) -> + case lists:last(T) of + {put_bits,_,_,_} -> + [H|T ++ [{put_bits,0,0,[1,align]}]]; + _ -> + throw(impossible) + end. %%% %%% For the aligned PER format, fix up the intermediate format @@ -2063,8 +2625,6 @@ enc_opt_al_prepare_cond_2([]) -> per_fixup([{apply,_,_}=H|T]) -> [H|per_fixup(T)]; -per_fixup([{apply,_,_,_}=H|T]) -> - [H|per_fixup(T)]; per_fixup([{block,Block}|T]) -> [{block,per_fixup(Block)}|per_fixup(T)]; per_fixup([{'assign',_,_}=H|T]) -> @@ -2072,14 +2632,11 @@ per_fixup([{'assign',_,_}=H|T]) -> per_fixup([{'cond',Cs0}|T]) -> Cs = [[C|per_fixup(Act)] || [C|Act] <- Cs0], [{'cond',Cs}|per_fixup(T)]; -per_fixup([{'cond',Cs0,Dst}|T]) -> - Cs = [[C|per_fixup(Act)] || [C|Act] <- Cs0], - [{'cond',Cs,Dst}|per_fixup(T)]; per_fixup([{call,_,_,_}=H|T]) -> [H|per_fixup(T)]; per_fixup([{call,_,_,_,_}=H|T]) -> [H|per_fixup(T)]; -per_fixup([{call_gen,_,_,_,_}=H|T]) -> +per_fixup([{call_gen,_,_,_,_,_}=H|T]) -> [H|per_fixup(T)]; per_fixup([{error,_}=H|T]) -> [H|per_fixup(T)]; @@ -2087,6 +2644,10 @@ per_fixup([{lc,B,V,L}|T]) -> [{lc,per_fixup(B),V,L}|per_fixup(T)]; per_fixup([{lc,B,V,L,Dst}|T]) -> [{lc,per_fixup(B),V,L,Dst}|per_fixup(T)]; +per_fixup([{list,Imm,Dst}|T]) -> + [{list,per_fixup(Imm),Dst}|per_fixup(T)]; +per_fixup([{set,_,_}=H|T]) -> + [H|per_fixup(T)]; per_fixup([{sub,_,_,_}=H|T]) -> [H|per_fixup(T)]; per_fixup([{'try',Try0,{P,Succ0},Else0,Dst}|T]) -> @@ -2123,8 +2684,10 @@ fixup_put_bits(Other) -> per_fixup(Other). %% returns a value range that has the lower bound set to the lowest value %% of all single values and lower bound values in C and the upper bound to %% the greatest value. -effective_constraint(integer,[C={{_,_},_}|_Rest]) -> % extension - [C]; +effective_constraint(integer, [{{_,_}=Root,_}|_Rest]) -> + %% Normalize extension. Note that any range given for the + %% extension should be ignored anyway. + [{Root,[]}]; effective_constraint(integer, C) -> SVs = get_constraints(C, 'SingleValue'), SV = effective_constr('SingleValue', SVs), diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl index 1abccc8626..283616b157 100644 --- a/lib/asn1/src/asn1ct_parser2.erl +++ b/lib/asn1/src/asn1ct_parser2.erl @@ -38,6 +38,7 @@ parse(Tokens) -> {error,{Reason,hd(Tokens)}}; {ModuleDefinition,Rest1} -> {Types,Rest2} = parse_AssignmentList(Rest1), + clean_process_dictionary(), case Rest2 of [{'END',_}|_Rest3] -> {ok,ModuleDefinition#module{typeorval = Types}}; @@ -48,6 +49,13 @@ parse(Tokens) -> end end. +clean_process_dictionary() -> + Mod = erase(asn1_module), + _ = erase({Mod,imports}), + _ = erase(tagdefault), + _ = erase(extensiondefault), + ok. + parse_ModuleDefinition([{typereference,L1,ModuleIdentifier}|Rest0]) -> put(asn1_module,ModuleIdentifier), {_DefinitiveIdentifier,Rest02} = diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl index 992210232f..a86c963b9d 100644 --- a/lib/asn1/src/asn1ct_value.erl +++ b/lib/asn1/src/asn1ct_value.erl @@ -167,17 +167,16 @@ from_type_prim(M, D) -> case D#type.def of 'INTEGER' -> i_random(C); - {'INTEGER',NamedNumberList} -> - NN = [X||{X,_} <- NamedNumberList], - case NN of + {'INTEGER',[_|_]=NNL} -> + case C of [] -> - i_random(C); + {N,_} = lists:nth(random(length(NNL)), NNL), + N; _ -> - case C of - [] -> - lists:nth(random(length(NN)),NN); - _ -> - lists:nth((fun(0)->1;(X)->X end(i_random(C))),NN) + V = i_random(C), + case lists:keyfind(V, 2, NNL) of + false -> V; + {N,V} -> N end end; Enum when is_tuple(Enum),element(1,Enum)=='ENUMERATED' -> @@ -261,7 +260,11 @@ from_type_prim(M, D) -> 'BOOLEAN' -> true; 'OCTET STRING' -> - adjust_list(size_random(C),c_string(C,"OCTET STRING")); + S0 = adjust_list(size_random(C), c_string(C, "OCTET STRING")), + case M:legacy_erlang_types() of + false -> list_to_binary(S0); + true -> S0 + end; 'NumericString' -> adjust_list(size_random(C),c_string(C,"0123456789")); 'TeletexString' -> diff --git a/lib/asn1/src/asn1rt_nif.erl b/lib/asn1/src/asn1rt_nif.erl index c1879e3dcf..1a44f1a27c 100644 --- a/lib/asn1/src/asn1rt_nif.erl +++ b/lib/asn1/src/asn1rt_nif.erl @@ -30,7 +30,7 @@ -define(ASN1_NIF_VSN,1). load_nif() -> - LibBaseName = "asn1_erl_nif", + LibBaseName = "asn1rt_nif", PrivDir = code:priv_dir(asn1), LibName = case erlang:system_info(build_type) of opt -> diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl index 583ff790b7..4bd814769f 100644 --- a/lib/asn1/src/asn1rtt_ber.erl +++ b/lib/asn1/src/asn1rtt_ber.erl @@ -29,6 +29,8 @@ decode_integer/2,decode_integer/3, decode_named_integer/3,decode_named_integer/4, encode_enumerated/2,decode_enumerated/3, + encode_unnamed_bit_string/2,encode_unnamed_bit_string/3, + encode_named_bit_string/3,encode_named_bit_string/4, encode_bit_string/4, decode_named_bit_string/3, decode_compact_bit_string/3, @@ -38,6 +40,7 @@ encode_relative_oid/2,decode_relative_oid/2, encode_object_identifier/2,decode_object_identifier/2, encode_restricted_string/2, + decode_octet_string/2,decode_octet_string/3, decode_restricted_string/2,decode_restricted_string/3, encode_universal_string/2,decode_universal_string/3, encode_UTF8_string/2,decode_UTF8_string/2, @@ -780,6 +783,55 @@ decode_enumerated1(Val, NamedNumberList) -> {asn1_enum,Val} end. +%%============================================================================ +%% Bitstring value, ITU_T X.690 Chapter 8.6 +%% +%% encode bitstring value +%%============================================================================ + +encode_unnamed_bit_string(Bits, TagIn) -> + Unused = (8 - (bit_size(Bits) band 7)) band 7, + Bin = <<Unused,Bits/bitstring,0:Unused>>, + encode_tags(TagIn, Bin, byte_size(Bin)). + +encode_unnamed_bit_string(C, Bits, TagIn) -> + NumBits = bit_size(Bits), + Unused = (8 - (NumBits band 7)) band 7, + Bin = <<Unused,Bits/bitstring,0:Unused>>, + case C of + {_Min,Max} -> + if + NumBits > Max -> + exit({error,{asn1, + {bitstring_length, + {{was,NumBits},{maximum,Max}}}}}); + true -> + ok + end; + Size -> + if NumBits =< Size -> + ok; + true -> + exit({error,{asn1, + {bitstring_length, + {{was,NumBits},{should_be,Size}}}}}) + end + end, + encode_tags(TagIn, Bin, byte_size(Bin)). + +encode_named_bit_string([H|_]=Bits, NamedBitList, TagIn) when is_atom(H) -> + encode_bit_string_named([], Bits, NamedBitList, TagIn); +encode_named_bit_string([{bit,_}|_]=Bits, NamedBitList, TagIn) -> + encode_bit_string_named([], Bits, NamedBitList, TagIn); +encode_named_bit_string(Bits, _NamedBitList, TagIn) when is_bitstring(Bits) -> + encode_unnamed_bit_string(Bits, TagIn). + +encode_named_bit_string(C, [H|_]=Bits, NamedBitList, TagIn) when is_atom(H) -> + encode_bit_string_named(C, Bits, NamedBitList, TagIn); +encode_named_bit_string(C, [{bit,_}|_]=Bits, NamedBitList, TagIn) -> + encode_bit_string_named(C, Bits, NamedBitList, TagIn); +encode_named_bit_string(C, Bits, _NamedBitList, TagIn) when is_bitstring(Bits) -> + encode_unnamed_bit_string(C, Bits, TagIn). %%============================================================================ %% Bitstring value, ITU_T X.690 Chapter 8.6 @@ -1251,6 +1303,19 @@ encode_restricted_string(OctetList, TagIn) when is_list(OctetList) -> encode_tags(TagIn, OctetList, length(OctetList)). %%============================================================================ +%% decode OCTET STRING to binary +%%============================================================================ + +decode_octet_string(Tlv, TagsIn) -> + Bin = match_and_collect(Tlv, TagsIn), + binary:copy(Bin). + +decode_octet_string(Tlv, Range, TagsIn) -> + Bin0 = match_and_collect(Tlv, TagsIn), + Bin = binary:copy(Bin0), + check_restricted_string(Bin, byte_size(Bin), Range). + +%%============================================================================ %% decode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings %%============================================================================ diff --git a/lib/asn1/src/asn1rtt_check.erl b/lib/asn1/src/asn1rtt_check.erl index e78b65a8fb..be4f9c8bff 100644 --- a/lib/asn1/src/asn1rtt_check.erl +++ b/lib/asn1/src/asn1rtt_check.erl @@ -20,7 +20,7 @@ -export([check_bool/2, check_int/3, - check_bitstring/3, + check_bitstring/2,check_named_bitstring/3, check_octetstring/2, check_null/2, check_objectidentifier/2, @@ -50,31 +50,54 @@ check_int(DefValue, Value, NNL) when is_atom(Value) -> check_int(DefaultValue, _Value, _) -> throw({error,DefaultValue}). -%% Two equal lists or integers -check_bitstring(_, asn1_DEFAULT, _) -> +%% check_bitstring(Default, UserBitstring) -> true|false +%% Default = bitstring() +%% UserBitstring = integeger() | list(0|1) | {Unused,binary()} | bitstring() +check_bitstring(_, asn1_DEFAULT) -> true; -check_bitstring(V, V, _) -> - true; -%% Default value as a list of 1 and 0 and user value as an integer -check_bitstring(L=[H|T], Int, _) when is_integer(Int), is_integer(H) -> - case bit_list_to_int(L, length(T)) of - Int -> true; - _ -> throw({error,L,Int}) +check_bitstring(DefVal, {Unused,Binary}) -> + %% User value in compact format. + Sz = bit_size(Binary) - Unused, + <<Val:Sz/bitstring,_:Unused>> = Binary, + check_bitstring(DefVal, Val); +check_bitstring(DefVal, Val) when is_bitstring(Val) -> + case Val =:= DefVal of + false -> throw(error); + true -> true end; -%% Default value as an integer, val as list -check_bitstring(Int, Val, NBL) when is_integer(Int), is_list(Val) -> - BL = int_to_bit_list(Int, [], length(Val)), - check_bitstring(BL, Val, NBL); +check_bitstring(Def, Val) when is_list(Val) -> + check_bitstring_list(Def, Val); +check_bitstring(Def, Val) when is_integer(Val) -> + check_bitstring_integer(Def, Val). + +check_bitstring_list(<<H:1,T1/bitstring>>, [H|T2]) -> + check_bitstring_list(T1, T2); +check_bitstring_list(<<>>, []) -> + true; +check_bitstring_list(_, _) -> + throw(error). + +check_bitstring_integer(<<H:1,T1/bitstring>>, Int) when H =:= Int band 1 -> + check_bitstring_integer(T1, Int bsr 1); +check_bitstring_integer(<<>>, 0) -> + true; +check_bitstring_integer(_, _) -> + throw(error). + +check_named_bitstring(_, asn1_DEFAULT, _) -> + true; +check_named_bitstring(V, V, _) -> + true; %% Default value and user value as lists of ones and zeros -check_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL=[_H|_T]) when is_integer(H1), is_integer(H2) -> +check_named_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL=[_H|_T]) when is_integer(H1), is_integer(H2) -> L2new = remove_trailing_zeros(L2), - check_bitstring(L1, L2new, NBL); + check_named_bitstring(L1, L2new, NBL); %% Default value as a list of 1 and 0 and user value as a list of atoms -check_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL) when is_integer(H1), is_atom(H2) -> +check_named_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL) when is_integer(H1), is_atom(H2) -> L3 = bit_list_to_nbl(L1, NBL, 0, []), - check_bitstring(L3, L2, NBL); + check_named_bitstring(L3, L2, NBL); %% Both default value and user value as a list of atoms -check_bitstring(L1=[H1|T1], L2=[H2|_T2], _) +check_named_bitstring(L1=[H1|T1], L2=[H2|_T2], _) when is_atom(H1), is_atom(H2), length(L1) =:= length(L2) -> case lists:member(H1, L2) of true -> @@ -82,27 +105,29 @@ check_bitstring(L1=[H1|T1], L2=[H2|_T2], _) false -> throw({error,L2}) end; %% Default value as a list of atoms and user value as a list of 1 and 0 -check_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL) when is_atom(H1), is_integer(H2) -> +check_named_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL) when is_atom(H1), is_integer(H2) -> L3 = bit_list_to_nbl(L2, NBL, 0, []), - check_bitstring(L1, L3, NBL); + check_named_bitstring(L1, L3, NBL); %% User value in compact format -check_bitstring(DefVal,CBS={_,_}, NBL) -> +check_named_bitstring(DefVal,CBS={_,_}, NBL) -> NewVal = cbs_to_bit_list(CBS), - check_bitstring(DefVal, NewVal, NBL); -check_bitstring(DV, V, _) -> + check_named_bitstring(DefVal, NewVal, NBL); +%% User value as a binary +check_named_bitstring(DefVal, CBS, NBL) when is_binary(CBS) -> + NewVal = cbs_to_bit_list({0,CBS}), + check_named_bitstring(DefVal, NewVal, NBL); +%% User value as a bitstring +check_named_bitstring(DefVal, CBS, NBL) when is_bitstring(CBS) -> + BitSize = bit_size(CBS), + Unused = 8 - (BitSize band 7), + NewVal = cbs_to_bit_list({Unused,<<CBS:BitSize/bits,0:Unused>>}), + check_named_bitstring(DefVal, NewVal, NBL); +check_named_bitstring(DV, V, _) -> throw({error,DV,V}). - -bit_list_to_int([0|Bs], ShL)-> - bit_list_to_int(Bs, ShL-1) + 0; -bit_list_to_int([1|Bs], ShL) -> - bit_list_to_int(Bs, ShL-1) + (1 bsl ShL); -bit_list_to_int([], _) -> - 0. - int_to_bit_list(0, Acc, 0) -> Acc; -int_to_bit_list(Int, Acc, Len) -> +int_to_bit_list(Int, Acc, Len) when Len > 0 -> int_to_bit_list(Int bsr 1, [Int band 1|Acc], Len - 1). bit_list_to_nbl([0|T], NBL, Pos, Acc) -> diff --git a/lib/asn1/src/asn1rtt_ext.erl b/lib/asn1/src/asn1rtt_ext.erl index 46adb2007d..f3eee1cdd5 100644 --- a/lib/asn1/src/asn1rtt_ext.erl +++ b/lib/asn1/src/asn1rtt_ext.erl @@ -38,7 +38,7 @@ transform_to_EXTERNAL1990([{'context-negotiation',Context_negot}|Rest], Acc) -> transform_to_EXTERNAL1990([asn1_NOVALUE|Rest], Acc) -> transform_to_EXTERNAL1990(Rest, [asn1_NOVALUE|Acc]); transform_to_EXTERNAL1990([Data_val_desc,Data_value], Acc) - when is_list(Data_value)-> + when is_list(Data_value); is_binary(Data_value) -> list_to_tuple(lists:reverse([{'octet-aligned',Data_value}, Data_val_desc|Acc])); transform_to_EXTERNAL1990([Data_val_desc,Data_value], Acc) diff --git a/lib/asn1/src/asn1rtt_per_common.erl b/lib/asn1/src/asn1rtt_per_common.erl index 9e9fd87ec3..71fec411a0 100644 --- a/lib/asn1/src/asn1rtt_per_common.erl +++ b/lib/asn1/src/asn1rtt_per_common.erl @@ -37,7 +37,10 @@ bitstring_from_positions/1,bitstring_from_positions/2, to_bitstring/1,to_bitstring/2, to_named_bitstring/1,to_named_bitstring/2, - extension_bitmap/3]). + bs_drop_trailing_zeroes/1,adjust_trailing_zeroes/2, + is_default_bitstring/3,is_default_bitstring/5, + extension_bitmap/3, + open_type_to_binary/1,legacy_open_type_to_binary/1]). -define('16K',16384). @@ -271,10 +274,69 @@ to_named_bitstring(Val, Lb) -> %% for correctness, not speed. adjust_trailing_zeroes(to_bitstring(Val), Lb). +is_default_bitstring(asn1_DEFAULT, _, _) -> + true; +is_default_bitstring(Named, Named, _) -> + true; +is_default_bitstring(Bs, _, Bs) -> + true; +is_default_bitstring(Val, _, Def) when is_bitstring(Val) -> + Sz = bit_size(Def), + case Val of + <<Def:Sz/bitstring,T/bitstring>> -> + NumZeroes = bit_size(T), + case T of + <<0:NumZeroes>> -> true; + _ -> false + end; + _ -> + false + end. + +is_default_bitstring(asn1_DEFAULT, _, _, _, _) -> + true; +is_default_bitstring({Unused,Bin}, V0, V1, V2, V3) when is_integer(Unused) -> + %% Convert compact bitstring to a bitstring. + Sz = bit_size(Bin) - Unused, + <<Bs:Sz/bitstring,_:Unused>> = Bin, + is_default_bitstring(Bs, V0, V1, V2, V3); +is_default_bitstring(Named, Named, _, _, _) -> + true; +is_default_bitstring(Bs, _, Bs, _, _) -> + true; +is_default_bitstring(List, _, _, List, _) -> + true; +is_default_bitstring(Int, _, _, _, Int) -> + true; +is_default_bitstring(Val, _, Def, _, _) when is_bitstring(Val) -> + Sz = bit_size(Def), + case Val of + <<Def:Sz/bitstring,T/bitstring>> -> + NumZeroes = bit_size(T), + case T of + <<0:NumZeroes>> -> true; + _ -> false + end; + _ -> + false + end; +is_default_bitstring(Val, _, _, List, _) when is_list(Val) -> + is_default_bitstring_list(List, Val); +is_default_bitstring(_, _, _, _, _) -> false. extension_bitmap(Val, Pos, Limit) -> extension_bitmap(Val, Pos, Limit, 0). +open_type_to_binary({asn1_OPENTYPE,Bin}) when is_binary(Bin) -> + Bin. + +legacy_open_type_to_binary({asn1_OPENTYPE,Bin}) when is_binary(Bin) -> + Bin; +legacy_open_type_to_binary(Bin) when is_binary(Bin) -> + Bin; +legacy_open_type_to_binary(List) when is_list(List) -> + List. + %%% %%% Internal functions. %%% @@ -407,6 +469,8 @@ adjust_trailing_zeroes(Bs0, Lb) -> bs_drop_trailing_zeroes(Bs) -> bs_drop_trailing_zeroes(Bs, bit_size(Bs)). +bs_drop_trailing_zeroes(Bs, 0) -> + Bs; bs_drop_trailing_zeroes(Bs0, Sz0) when Sz0 < 8 -> <<Byte:Sz0>> = Bs0, Sz = Sz0 - ntz(Byte), @@ -447,6 +511,16 @@ ntz(Byte) -> 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0}, element(Byte+1, T). +is_default_bitstring_list([H|Def], [H|Val]) -> + is_default_bitstring_list(Def, Val); +is_default_bitstring_list([], []) -> + true; +is_default_bitstring_list([], [_|_]=Val) -> + lists:all(fun(0) -> true; + (_) -> false + end, Val); +is_default_bitstring_list(_, _) -> false. + extension_bitmap(_Val, Pos, Limit, Acc) when Pos >= Limit -> Acc; extension_bitmap(Val, Pos, Limit, Acc) -> diff --git a/lib/asn1/src/prepare_templates.erl b/lib/asn1/src/prepare_templates.erl index 83155b2e52..ccd15548d8 100644 --- a/lib/asn1/src/prepare_templates.erl +++ b/lib/asn1/src/prepare_templates.erl @@ -21,69 +21,77 @@ -export([gen_asn1ct_rtt/1,gen_asn1ct_eval/1]). gen_asn1ct_rtt(Ms) -> - io:format("%% Generated by ~s. DO NOT EDIT THIS FILE.\n" + {ok,Fd} = file:open("asn1ct_rtt.erl", [write]), + io:format(Fd, + "%% Generated by ~s. DO NOT EDIT THIS FILE.\n" "%%\n" "%% Input files:\n", [?MODULE]), - [io:put_chars(["%% ",M,$\n]) || M <- Ms], - io:nl(), - io:put_chars("-module(asn1ct_rtt).\n" + [io:put_chars(Fd, ["%% ",M,$\n]) || M <- Ms], + io:nl(Fd), + io:put_chars(Fd, + "-module(asn1ct_rtt).\n" "-export([assert_defined/1,dependencies/1,code/0]).\n" "\n"), Forms = lists:sort(lists:append([abstract(M) || M <- Ms])), Exp = lists:sort(exports(Forms)), - defined(Exp), - io:nl(), + defined(Fd, Exp), + io:nl(Fd), Calls = calls(Forms), R = sofs:relation(Calls), Fam0 = sofs:relation_to_family(R), Fam = sofs:to_external(Fam0), - dependencies(Fam), - io:nl(), + dependencies(Fd, Fam), + io:nl(Fd), Funcs = [begin Bin = list_to_binary([$\n|erl_pp:function(Func)]), {{M,F,A},Bin} end || {M,{function,_,F,A,_}=Func} <- Forms], - io:format("code() ->\n~p.\n\n", [Funcs]), + io:format(Fd, "code() ->\n~p.\n\n", [Funcs]), + ok = file:close(Fd), halt(0). gen_asn1ct_eval([File]) -> + Output = filename:rootname(File, ".funcs") ++ ".erl", + {ok,Fd} = file:open(Output, [write]), {ok,Funcs} = file:consult(File), asn1ct_func:start_link(), [asn1ct_func:need(MFA) || MFA <- Funcs], - io:format("%% Generated by ~s. DO NOT EDIT THIS FILE.\n" + io:format(Fd, + "%% Generated by ~s. DO NOT EDIT THIS FILE.\n" "%%\n" "%% Input file: ~s\n\n", [?MODULE,File]), - io:format("-module(~s).\n", [filename:rootname(File)]), - gen_asn1ct_eval_exp(Funcs), - asn1ct_func:generate(group_leader()), + io:format(Fd, "-module(~s).\n", [filename:rootname(File)]), + gen_asn1ct_eval_exp(Fd, Funcs), + asn1ct_func:generate(Fd), + ok = file:close(Fd), halt(0). -gen_asn1ct_eval_exp(Funcs) -> - io:put_chars("-export(["), - gen_asn1ct_eval_exp_1(Funcs, ""), - io:put_chars("]).\n"). +gen_asn1ct_eval_exp(Fd, Funcs) -> + io:put_chars(Fd, "-export(["), + gen_asn1ct_eval_exp_1(Fd, Funcs, ""), + io:put_chars(Fd, "]).\n"). -gen_asn1ct_eval_exp_1([{_,F,A}|T], Sep) -> - io:put_chars(Sep), - io:format("~p/~p", [F,A]), - gen_asn1ct_eval_exp_1(T, ",\n"); -gen_asn1ct_eval_exp_1([], _) -> ok. +gen_asn1ct_eval_exp_1(Fd, [{_,F,A}|T], Sep) -> + io:put_chars(Fd, Sep), + io:format(Fd, "~p/~p", [F,A]), + gen_asn1ct_eval_exp_1(Fd, T, ",\n"); +gen_asn1ct_eval_exp_1(_, [], _) -> ok. -defined([H|T]) -> - io:format("assert_defined(~p) -> ok", [H]), +defined(Fd, [H|T]) -> + io:format(Fd, "assert_defined(~p) -> ok", [H]), case T of [] -> - io:put_chars(".\n"); + io:put_chars(Fd, ".\n"); [_|_] -> - io:put_chars(";\n"), - defined(T) + io:put_chars(Fd, ";\n"), + defined(Fd, T) end. -dependencies([{K,V}|T]) -> - io:format("dependencies(~p) ->\n~p;\n", [K,V]), - dependencies(T); -dependencies([]) -> - io:put_chars("dependencies(_) -> [].\n"). +dependencies(Fd, [{K,V}|T]) -> + io:format(Fd, "dependencies(~p) ->\n~p;\n", [K,V]), + dependencies(Fd, T); +dependencies(Fd, []) -> + io:put_chars(Fd, "dependencies(_) -> [].\n"). abstract(File) -> {ok,{M0,[{abstract_code,Abstract}]}} = |