diff options
Diffstat (limited to 'lib/asn1/src')
-rw-r--r-- | lib/asn1/src/asn1.appup.src | 30 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct.erl | 56 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_check.erl | 160 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_constructed_per.erl | 178 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_func.erl | 2 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_gen.erl | 33 | ||||
-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 | 887 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_table.erl | 34 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_tok.erl | 2 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_value.erl | 9 | ||||
-rw-r--r-- | lib/asn1/src/asn1rt.erl | 3 | ||||
-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_ext.erl | 2 | ||||
-rw-r--r-- | lib/asn1/src/asn1rtt_per_common.erl | 37 |
17 files changed, 1189 insertions, 461 deletions
diff --git a/lib/asn1/src/asn1.appup.src b/lib/asn1/src/asn1.appup.src index 2d11eddfbf..e4b3508cc4 100644 --- a/lib/asn1/src/asn1.appup.src +++ b/lib/asn1/src/asn1.appup.src @@ -1,11 +1,21 @@ +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% {"%VSN%", -% This version does not change anything of the runtime modules -% Only changes in compile time modules and thus no need for upgrade on target -[ - ], - [ - ]}. - - - - + [{<<".*">>,[{restart_application, asn1}]}], + [{<<".*">>,[{restart_application, asn1}]}] +}. diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index f2ccf5f212..9ec43197bf 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -19,6 +19,10 @@ %% %% -module(asn1ct). +-deprecated([decode/3,encode/3]). +-compile([{nowarn_deprecated_function,{asn1rt,decode,3}}, + {nowarn_deprecated_function,{asn1rt,encode,2}}, + {nowarn_deprecated_function,{asn1rt,encode,3}}]). %% Compile Time functions for ASN.1 (e.g ASN.1 compiler). @@ -40,7 +44,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 +337,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 +352,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 +365,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 +845,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 +874,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 @@ -1011,7 +1044,7 @@ get_file_list1(Stream,Dir,Includes,Acc) -> Ret = io:get_line(Stream,''), case Ret of eof -> - file:close(Stream), + ok = file:close(Stream), lists:reverse(Acc); FileName -> SuffixedNameList = @@ -1072,6 +1105,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]. @@ -1896,8 +1930,9 @@ read_config_file(ModuleName) -> Includes = [I || {i,I} <- Options], read_config_file1(ModuleName,Includes); {error,Reason} -> - file:format_error(Reason), - throw({error,{"error reading asn1 config file",Reason}}) + Error = "error reading asn1 config file: " ++ + file:format_error(Reason), + throw({error,Error}) end. read_config_file1(ModuleName,[]) -> case filename:extension(ModuleName) of @@ -1915,8 +1950,9 @@ read_config_file1(ModuleName,[H|T]) -> {error,enoent} -> read_config_file1(ModuleName,T); {error,Reason} -> - file:format_error(Reason), - throw({error,{"error reading asn1 config file",Reason}}) + Error = "error reading asn1 config file: " ++ + file:format_error(Reason), + throw({error,Error}) end. get_config_info(CfgList,InfoType) -> diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 04227fd23b..b9f2cb876a 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -2464,7 +2464,7 @@ normalize_value(S0, Type, {'DEFAULT',Value}, NameList) -> {'BIT STRING',CType,_} -> normalize_bitstring(S,Value,CType); {'OCTET STRING',CType,_} -> - normalize_octetstring(S,Value,CType); + normalize_octetstring(S0, Value, CType); {'NULL',_CType,_} -> %%normalize_null(Value); 'NULL'; @@ -2574,6 +2574,18 @@ normalize_bitstring(S, Value, Type)-> Bs end. +hstring_to_binary(L) -> + byte_align(hstring_to_bitstring(L)). + +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 >>. @@ -2592,59 +2604,19 @@ hex_to_int(D) when $A =< D, D =< $F -> D - ($A - 10). 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,[]); {Name,String} when is_atom(Name) -> normalize_octetstring(S,String,CType); - List when is_list(List) -> - %% check if list elements are valid octet values - lists:map(fun([])-> ok; - (H)when H > 255-> - asn1ct:warning("not legal octet value ~p in OCTET STRING, ~p~n", - [H,List],S, - "not legal octet value ~p in OCTET STRING"); - (_)-> ok - end, List), - List; - Other -> - asn1ct:warning("unknown default value ~p~n",[Other],S, - "unknown default value"), - Value + _ -> + Item = S#state.value, + throw(asn1_error(S, Item, illegal_octet_string_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. @@ -2659,16 +2631,19 @@ normalize_objectdescriptor(Value) -> normalize_real(Value) -> Value. -normalize_enumerated(S, Id, {Base,Ext}) -> +normalize_enumerated(S, Id0, NNL) -> + {Id,_} = lookup_enum_value(S, Id0, NNL), + Id. + +lookup_enum_value(S, Id, {Base,Ext}) -> %% Extensible ENUMERATED. - normalize_enumerated(S, Id, Base++Ext); -normalize_enumerated(S, #'Externalvaluereference'{value=Id}, - NamedNumberList) -> - normalize_enumerated(S, Id, NamedNumberList); -normalize_enumerated(S, Id, NamedNumberList) when is_atom(Id) -> - case lists:keymember(Id, 1, NamedNumberList) of - true -> - Id; + lookup_enum_value(S, Id, Base++Ext); +lookup_enum_value(S, #'Externalvaluereference'{value=Id}, NNL) -> + lookup_enum_value(S, Id, NNL); +lookup_enum_value(S, Id, NNL) when is_atom(Id) -> + case lists:keyfind(Id, 1, NNL) of + {_,_}=Ret -> + Ret; false -> throw(asn1_error(S, S#state.value, {undefined,Id})) end. @@ -3105,7 +3080,6 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) -> Ct=maybe_illicit_implicit_tag(open_type,Tag), TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct}; 'INTEGER' -> - check_integer(S,[],Constr), TempNewDef#newt{tag= merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))}; @@ -3803,8 +3777,9 @@ resolv_value(S,Val) -> resolv_value1(S,Id). resolv_value1(S, ERef = #'Externalvaluereference'{value=Name}) -> - case catch resolve_namednumber(S,S#state.type,Name) of - V when is_integer(V) -> V; + case catch resolve_namednumber(S, S#state.type, Name) of + V when is_integer(V) -> + V; _ -> case get_referenced_type(S,ERef) of {Err,_Reason} when Err == error; Err == 'EXIT' -> @@ -3857,21 +3832,20 @@ resolve_value_from_object(S,Object,FieldName) -> end. - resolve_namednumber(S,#typedef{typespec=Type},Name) -> case Type#type.def of {'ENUMERATED',NameList} -> - NamedNumberList=check_enumerated(S,NameList,Type#type.constraint), - N = normalize_enumerated(S,Name,NamedNumberList), - {value,{_,V}} = lists:keysearch(N,1,NamedNumberList), - V; + resolve_namednumber_1(S, Name, NameList, Type); {'INTEGER',NameList} -> - NamedNumberList = check_enumerated(S,NameList,Type#type.constraint), - {value,{_,V}} = lists:keysearch(Name,1,NamedNumberList), - V; + resolve_namednumber_1(S, Name, NameList, Type); _ -> not_enumerated end. + +resolve_namednumber_1(S, Name, NameList, Type) -> + NamedNumberList = check_enumerated(S, NameList, Type#type.constraint), + {_,N} = lookup_enum_value(S, Name, NamedNumberList), + N. check_constraints(S,[{'ContainedSubtype',Type} | Rest], Acc) -> {RefMod,CTDef} = get_referenced_type(S,Type#type.def), @@ -3952,9 +3926,9 @@ check_constraint(S,{simpletable,Type}) -> #'Externaltypereference'{} -> ERef = check_externaltypereference(S,C), {simpletable,ERef#'Externaltypereference'.type}; - #type{def=#'Externaltypereference'{type=T}} -> - check_externaltypereference(S,C#type.def), - {simpletable,T}; + #type{def=#'Externaltypereference'{}=ExtTypeRef} -> + ERef = check_externaltypereference(S, ExtTypeRef), + {simpletable,ERef#'Externaltypereference'.type}; {valueset,#type{def=ERef=#'Externaltypereference'{}}} -> % this is an object set {_,TDef} = get_referenced_type(S,ERef), case TDef#typedef.typespec of @@ -4194,9 +4168,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); @@ -4220,42 +4195,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) -> @@ -6860,6 +6802,8 @@ asn1_error(#state{mname=Where}, Item, Error) -> format_error({already_defined,Name,PrevLine}) -> io_lib:format("the name ~p has already been defined at line ~p", [Name,PrevLine]); +format_error(illegal_octet_string_value) -> + "expecting a bstring or an hstring as value for an OCTET STRING"; format_error({invalid_fields,Fields,Obj}) -> io_lib:format("invalid ~s in ~p", [format_fields(Fields),Obj]); format_error({missing_mandatory_fields,Fields,Obj}) -> diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 4672f7edd3..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(), @@ -871,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',_} -> @@ -906,26 +911,36 @@ 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) -> - 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]; + 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), - ListBs = [B || <<B:1>> <= Bs], - IntBs = lists:foldl(fun(B, A) -> - (A bsl 1) bor B - end, 0, lists:reverse(ListBs)), - Args = [List,Bs,ListBs,IntBs], - {call,per_common,is_default_bitstring,Args}; + 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 -> []; @@ -967,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' -> @@ -988,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. @@ -1014,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; @@ -1042,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() -> @@ -1053,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 @@ -1084,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}} -> @@ -1097,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. @@ -1540,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; @@ -1561,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)]; @@ -1778,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 30d337635b..4707e517b4 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, @@ -70,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), @@ -85,9 +85,9 @@ pgen_module(OutFile,Erules,Module, "%%%",nl, "%%% Run-time functions.",nl, "%%%",nl]), - asn1ct_func:generate(Fid), - file:close(Fid), - _ = erase(gen_file_out), + Fd = get(gen_file_out), + asn1ct_func:generate(Fd), + close_output_file(), _ = erase(outfile), asn1ct:verbose("--~p--~n",[{generated,ErlFile}],Options). @@ -190,7 +190,7 @@ pgen_check_defaultval(Erules,Module) -> "********~n~n",[X]) end, lists:foreach(Fun,CheckObjects), - file:close(IoDevice); + ok = file:close(IoDevice); _ -> ok end, gen_check_defaultval(Erules,Module,CheckObjects). @@ -790,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; _ -> @@ -1022,7 +1023,9 @@ 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), @@ -1121,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 ************************ @@ -1195,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, @@ -1226,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), 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 20785cda8c..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,28 +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,DefVals}, _Aligned) when is_integer(Pos), is_list(DefVals) -> - 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,DefVal},Zero] || DefVal <- DefVals] ++ [['_',One]]}]; per_enc_optional(Val0, {Pos,{call,M,F,A}}, _Aligned) when is_integer(Pos) -> - Val1 = lists:concat(["element(",Pos,", ",Val0,")"]), - {B,[Val,Tmp]} = mk_vars(Val1, [tmp]), + {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], @@ -391,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). %%% @@ -881,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); @@ -972,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. @@ -986,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) -> @@ -1143,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]]}]. @@ -1358,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). %%% @@ -1587,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([], _, _) -> []. @@ -1637,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], @@ -1662,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), @@ -1681,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. @@ -1702,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), @@ -1728,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}) -> @@ -1752,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), @@ -1792,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, ""), @@ -1849,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), @@ -1978,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) -> @@ -2012,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; @@ -2038,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}. @@ -2063,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 @@ -2095,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]) -> @@ -2104,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)]; @@ -2119,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]) -> @@ -2155,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_table.erl b/lib/asn1/src/asn1ct_table.erl index a5eb6d0413..2eca80eda3 100644 --- a/lib/asn1/src/asn1ct_table.erl +++ b/lib/asn1/src/asn1ct_table.erl @@ -22,34 +22,25 @@ %% Table abstraction module for ASN.1 compiler -export([new/1]). --export([new/2]). -export([new_reuse/1]). --export([new_reuse/2]). -export([exists/1]). -export([size/1]). -export([insert/2]). -export([lookup/2]). -export([match/2]). -export([to_list/1]). --export([delete/1]). % TODO: Remove (since we run in a separate process) +-export([delete/1]). -%% Always creates a new table -new(Table) -> new(Table, []). -new(Table, Options) -> - TableId = case get(Table) of - undefined -> - ets:new(Table, Options); - _ -> - delete(Table), - ets:new(Table, Options) - end, +%% Always create a new table. +new(Table) -> + undefined = get(Table), %Assertion. + TableId = ets:new(Table, []), put(Table, TableId). -%% Only create it if it doesn't exist yet -new_reuse(Table) -> new_reuse(Table, []). -new_reuse(Table, Options) -> - not exists(Table) andalso new(Table, Options). +%% Only create it if it doesn't exist yet. +new_reuse(Table) -> + not exists(Table) andalso new(Table). exists(Table) -> get(Table) =/= undefined. @@ -63,14 +54,17 @@ match(Table, MatchSpec) -> ets:match(get(Table), MatchSpec). to_list(Table) -> ets:tab2list(get(Table)). +%% Deleting tables is no longer strictly necessary since each compilation +%% runs in separate process, but it will reduce memory consumption +%% especially when many compilations are run in parallel. + delete(Tables) when is_list(Tables) -> [delete(T) || T <- Tables], true; delete(Table) when is_atom(Table) -> - case get(Table) of + case erase(Table) of undefined -> true; TableId -> - ets:delete(TableId), - erase(Table) + ets:delete(TableId) end. diff --git a/lib/asn1/src/asn1ct_tok.erl b/lib/asn1/src/asn1ct_tok.erl index 85199c65ec..33f4379173 100644 --- a/lib/asn1/src/asn1ct_tok.erl +++ b/lib/asn1/src/asn1ct_tok.erl @@ -36,7 +36,7 @@ process(Stream,Lno,R) -> process(io:get_line(Stream, ''), Stream,Lno+1,R). process(eof, Stream,Lno,R) -> - file:close(Stream), + ok = file:close(Stream), lists:flatten(lists:reverse([{'$end',Lno}|R])); diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl index 862b3c4ea5..221cd991a7 100644 --- a/lib/asn1/src/asn1ct_value.erl +++ b/lib/asn1/src/asn1ct_value.erl @@ -18,6 +18,7 @@ %% %% -module(asn1ct_value). +-compile([{nowarn_deprecated_function,{asn1rt,utf8_list_to_binary,1}}]). %% Generate Erlang values for ASN.1 types. %% The value is randomized within it's constraints @@ -260,7 +261,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' -> @@ -348,7 +353,7 @@ random_unnamed_bit_string(M, C) -> random(Upper) -> {A1,A2,A3} = erlang:now(), - random:seed(A1,A2,A3), + _ = random:seed(A1, A2, A3), random:uniform(Upper). size_random(C) -> diff --git a/lib/asn1/src/asn1rt.erl b/lib/asn1/src/asn1rt.erl index d18f81346a..ad8b879c38 100644 --- a/lib/asn1/src/asn1rt.erl +++ b/lib/asn1/src/asn1rt.erl @@ -18,14 +18,13 @@ %% %% -module(asn1rt). +-deprecated(module). %% Runtime functions for ASN.1 (i.e encode, decode) -export([encode/2,encode/3,decode/3,load_driver/0,unload_driver/0,info/1]). -export([utf8_binary_to_list/1,utf8_list_to_binary/1]). - --deprecated([load_driver/0,unload_driver/0]). encode(Module,{Type,Term}) -> encode(Module,Type,Term). 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_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 3309e6a4ca..71fec411a0 100644 --- a/lib/asn1/src/asn1rtt_per_common.erl +++ b/lib/asn1/src/asn1rtt_per_common.erl @@ -37,8 +37,10 @@ bitstring_from_positions/1,bitstring_from_positions/2, to_bitstring/1,to_bitstring/2, to_named_bitstring/1,to_named_bitstring/2, - is_default_bitstring/5, - 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). @@ -272,6 +274,25 @@ 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) -> @@ -306,6 +327,16 @@ 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. %%% @@ -438,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), |