diff options
author | Björn Gustavsson <bjorn@erlang.org> | 2013-02-19 15:00:23 +0100 |
---|---|---|
committer | Björn Gustavsson <bjorn@erlang.org> | 2013-05-31 14:52:14 +0200 |
commit | f4c7a59d96b110f79cc0fcc16286ece770a166a3 (patch) | |
tree | 15e21db104b2e60de18ee166ab47614a09b4b1ea /lib | |
parent | 23a99bfc9cde454b91e75f9cb1801666581b7051 (diff) | |
download | otp-f4c7a59d96b110f79cc0fcc16286ece770a166a3.tar.gz otp-f4c7a59d96b110f79cc0fcc16286ece770a166a3.tar.bz2 otp-f4c7a59d96b110f79cc0fcc16286ece770a166a3.zip |
asn1_constructed_per: Optimize decoding of CHOICE
Optimize the decoding of CHOICE. Most important is to inline decoding
of the extension bit (if present) and decoding of the choice index
to give the BEAM compiler more opportunities for optimization.
We will also change the structure of the generated code. The current
code uses a flattened case for both the root and extension alternatives:
case Choice + NumRootChoices * Ext of
%% Root alternatives.
0 - ...;
:
LastRootAlternative -> ...;
%% Extension alternatives.
LastRootAlternative+1 -> ...;
:
%% Unknown extension.
_ -> ...;
end
We will instead generate nested cases:
case Ext of
0 ->
case Choice of
%% Root alternatives.
0 - ...;
:
LastRootAlternative -> ...
end;
1 ->
%% Extension alternatives.
<Decode the open type here>
case Choice of
0 -> ...;
:
LastExtensionAlternative -> ...;
%% Unknown extension.
_ -> ...;
end
end
Nested cases should be slightly faster. For decoding of the extensions,
it also makes it possible to hoist the decoding of the open type up
from each case to before the case switching on the extension index,
thus reducing the size of the generated code.
We will also do another change to the structure. Currently, the
big flat clase is wrapped in code that repackages the return values:
{Alt,{Value,RemainingEncodedData}} =
case Choice + NumRootChoices * Ext of
:
end,
{{Value,Alt},RemainingEncodedData}.
We still need to do the repackaging, but we can push it down to
the case arm for decoding each alternative. In many cases, that
will give the BEAM compiler the opportunity to avoid building the
temporary tuples.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/asn1/src/asn1ct_constructed_per.erl | 160 | ||||
-rw-r--r-- | lib/asn1/src/asn1ct_imm.erl | 1 | ||||
-rw-r--r-- | lib/asn1/src/asn1rtt_per.erl | 100 | ||||
-rw-r--r-- | lib/asn1/src/asn1rtt_uper.erl | 46 | ||||
-rw-r--r-- | lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 | 6 | ||||
-rw-r--r-- | lib/asn1/test/testChoExtension.erl | 5 |
6 files changed, 130 insertions, 188 deletions
diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 7fc904ec16..c3067e53be 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -1322,8 +1322,7 @@ gen_dec_component_no_val({ext,_,_},mandatory) -> emit({"asn1_NOVALUE"}). -gen_dec_choice_line(Erule, TopType, Comp, Pos, Ext) -> - Pre = gen_dec_line_open_type(Erule, Ext, Pos), +gen_dec_choice_line(Erule, TopType, Comp, Pre) -> Imm0 = gen_dec_line_imm(Erule, TopType, Comp, false, Pre), Init = {ignore,fun(_) -> {[],[]} end}, Imm = [{group,[Init|Imm0]}], @@ -1638,62 +1637,135 @@ gen_enc_choice2(Erule, TopType, [H|T], Pos, Sep0, Ext) -> gen_enc_choice2(Erule, TopType, T, Pos+1, Sep, Ext); gen_enc_choice2(_, _, [], _, _, _) -> ok. -gen_dec_choice(Erule,TopType,CompList,{ext,Pos,NumExt}) -> - emit(["{Ext,",{curr,bytes},"} = ", - {call,Erule,getbit,["Bytes"]},com,nl]), +%% Generate the code for CHOICE. If the CHOICE is extensible, +%% the structure of the generated code is as follows: +%% +%% case Bytes of +%% <<0:1,Bytes1/bitstring>> -> +%% Choice = <Decode INTEGER (0..LastRootChoice) from Bytes1> +%% case Choice of +%% 0 -> <Decode>; +%% : +%% LastRootChoice -> <Decode> +%% end; +%% <<1:1,Bytes1/bitstring>> -> +%% Choice = <Decode normally small number from Bytes1> +%% TmpVal = <Decode open type> +%% case Choice of +%% 0 -> <Decode TmpVal>; +%% : +%% LastExtension -> <Decode TmpVal>; +%% _ -> <Return TmpVal since the type is unknown> +%% end +%% end +%% +%% The return value from the generated function always looks like: +%% {{ChoiceTag,Value},RemainingBuffer} +%% where ChoiceTag will be 'asn1_ExtAlt' for an unknown extension. +%% +%% If the CHOICE is not extensible, the top-level case is omitted +%% and only the code in the first case arm is generated. + +gen_dec_choice(Erule, TopType, CompList, {ext,_,_}=Ext) -> + {RootList,ExtList} = split_complist(CompList), + emit(["case Bytes of",nl]), + case RootList of + [] -> + ok; + [_|_] -> + emit(["<<0:1,Bytes1/bitstring>> ->",nl]), + asn1ct_name:new(bytes), + gen_dec_choice1(Erule, TopType, RootList, noext), + emit([";",nl,nl]) + end, + emit(["<<1:1,Bytes1/bitstring>> ->",nl]), + asn1ct_name:clear(), + asn1ct_name:new(bytes), asn1ct_name:new(bytes), - gen_dec_choice1(Erule,TopType,CompList,{ext,Pos,NumExt}); -gen_dec_choice(Erule,TopType,CompList,noext) -> - gen_dec_choice1(Erule,TopType,CompList,noext). - -gen_dec_choice1(Erule,TopType,CompList,noext) -> - emit(["{Choice,",{curr,bytes}, - "} = ",{call,Erule,getchoice, - [{prev,bytes},length(CompList),"0"]},com,nl, - "{Cname,{Val,NewBytes}} = case Choice of",nl]), - gen_dec_choice2(Erule,TopType,CompList,noext), - emit({nl,"end,",nl}), - emit({nl,"{{Cname,Val},NewBytes}"}); -gen_dec_choice1(Erule,TopType,{RootList,ExtList},Ext) -> - NewList = RootList ++ ExtList, - gen_dec_choice1(Erule,TopType, NewList, Ext); -gen_dec_choice1(Erule,TopType,{RootList,ExtList,RootList2},Ext) -> - NewList = RootList ++ RootList2 ++ ExtList, - gen_dec_choice1(Erule,TopType, NewList, Ext); -gen_dec_choice1(Erule,TopType,CompList,{ext,ExtPos,ExtNum}) -> - emit(["{Choice,",{curr,bytes},"} = ", - {call,Erule,getchoice, - [{prev,bytes},length(CompList)-ExtNum,"Ext"]},com,nl]), - emit({"{Cname,{Val,NewBytes}} = case Choice + Ext*",ExtPos-1," of",nl}), - gen_dec_choice2(Erule,TopType,CompList,{ext,ExtPos,ExtNum}), + gen_dec_choice1(Erule, TopType, ExtList, Ext), + emit([nl,"end"]); +gen_dec_choice(Erule, TopType, CompList, noext) -> + gen_dec_choice1(Erule, TopType, CompList, noext). + +split_complist({Root1,Ext,Root2}) -> + {Root1++Root2,Ext}; +split_complist({_,_}=CompList) -> + CompList. + +gen_dec_choice1(Erule, TopType, CompList, noext=Ext) -> + emit_getchoice(Erule, CompList, Ext), + emit(["case Choice of",nl]), + Pre = {safe,fun(St) -> + {asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), + fun() -> St end} + end}, + gen_dec_choice2(Erule, TopType, CompList, Pre), + emit([nl,"end"]); +gen_dec_choice1(Erule, TopType, CompList, {ext,_,_}=Ext) -> + emit_getchoice(Erule, CompList, Ext), Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)), + emit(["begin",nl]), BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), - emit([";",nl, - "_ ->",nl]), - {TmpTerm,TmpBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar), - emit([com,nl, - "{asn1_ExtAlt,{",TmpTerm,com,TmpBuf,"}}",nl, - "end,",nl,nl, - "{{Cname,Val},NewBytes}"]). - + {Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar), + emit([nl, + "end,",nl, + "case Choice of",nl]), + Pre = {safe,fun(St) -> + emit(["{TmpVal,_} = "]), + {Dst, + fun() -> + emit([",",nl, + "{TmpVal,",DstBuf,"}"]), + St + end} + end}, + gen_dec_choice2(Erule, TopType, CompList, Pre), + case CompList of + [] -> ok; + [_|_] -> emit([";",nl]) + end, + emit(["_ ->",nl, + "{{asn1_ExtAlt,",Dst,"},",DstBuf,"}",nl, + "end"]). + +emit_getchoice(Erule, CompList, Ext) -> + Al = is_aligned(Erule), + Imm = case {Ext,CompList} of + {noext,[_]} -> + {value,0}; + {noext,_} -> + asn1ct_imm:per_dec_constrained(0, length(CompList)-1, Al); + {{ext,_,_},_} -> + asn1ct_imm:per_dec_normally_small_number(Al) + end, + emit(["{Choice,",{curr,bytes},"} = ",nl]), + BytesVar = asn1ct_gen:mk_var(asn1ct_name:prev(bytes)), + asn1ct_imm:dec_code_gen(Imm, BytesVar), + emit([com,nl]). gen_dec_choice2(Erule,TopType,L,Ext) -> gen_dec_choice2(Erule, TopType, L, 0, [], Ext). -gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Ext) -> +gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Pre) -> #'ComponentType'{name=Cname,typespec=Type} = H0, H = H0#'ComponentType'{prop=mandatory}, + emit([Sep0,Pos," ->",nl]), case Type#type.def of #'ObjectClassFieldType'{type={typefield,_}} -> - emit([Sep0,Pos," ->",nl]), - gen_dec_choice_line(Erule, TopType, H, Pos+1, Ext); + emit("{Cname,{Val,NewBytes}} = begin\n"), + gen_dec_choice_line(Erule, TopType, H, Pre), + emit([nl, + "end,",nl, + "{{Cname,Val},NewBytes}"]); _ -> - emit([Sep0,Pos," -> {",{asis,Cname},",",nl]), - gen_dec_choice_line(Erule, TopType, H, Pos+1, Ext), - emit("}") + emit("{Val,NewBytes} = begin\n"), + gen_dec_choice_line(Erule, TopType, H, Pre), + emit([nl, + "end,",nl, + "{{",{asis,Cname},",Val},NewBytes}"]) end, Sep = [";",nl], - gen_dec_choice2(Erule, TopType, T, Pos+1, Sep, Ext); + gen_dec_choice2(Erule, TopType, T, Pos+1, Sep, Pre); gen_dec_choice2(_, _, [], _, _, _) -> ok. indent(N) -> diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl index 4b2c3b1b65..2c5620d5c8 100644 --- a/lib/asn1/src/asn1ct_imm.erl +++ b/lib/asn1/src/asn1ct_imm.erl @@ -25,6 +25,7 @@ per_dec_length/3,per_dec_named_integer/3, 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([optimize_alignment/1,optimize_alignment/2, dec_slim_cg/2,dec_code_gen/2]). -export([effective_constraint/2]). diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl index aa6cf4da0a..1023be8ce1 100644 --- a/lib/asn1/src/asn1rtt_per.erl +++ b/lib/asn1/src/asn1rtt_per.erl @@ -19,7 +19,7 @@ -module(asn1rtt_per). -export([setext/1, fixextensions/2, - skipextensions/3, getbit/1, getchoice/3, + skipextensions/3, set_choice/3,encode_integer/2, encode_small_number/1, encode_constrained_number/2, @@ -88,23 +88,6 @@ skipextensions(Bytes0, Nr, ExtensionBitstr) when is_bitstring(ExtensionBitstr) - Bytes0 end. - -getchoice(Bytes, 1, 0) -> % only 1 alternative is not encoded - {0,Bytes}; -getchoice(Bytes, _, 1) -> - decode_small_number(Bytes); -getchoice(Bytes, NumChoices, 0) -> - decode_constrained_number(Bytes, {0,NumChoices-1}). - - -getbit(Buffer) -> - <<B:1,Rest/bitstring>> = Buffer, - {B,Rest}. - -getbits(Buffer, Num) when is_bitstring(Buffer) -> - <<Bs:Num,Rest/bitstring>> = Buffer, - {Bs,Rest}. - align(Bin) when is_binary(Bin) -> Bin; align(BitStr) when is_bitstring(BitStr) -> @@ -112,28 +95,6 @@ align(BitStr) when is_bitstring(BitStr) -> <<_:AlignBits,Rest/binary>> = BitStr, Rest. - -%% First align buffer, then pick the first Num octets. -%% Returns octets as an integer with bit significance as in buffer. -getoctets(Buffer, Num) when is_binary(Buffer) -> - <<Val:Num/integer-unit:8,RestBin/binary>> = Buffer, - {Val,RestBin}; -getoctets(Buffer, Num) when is_bitstring(Buffer) -> - AlignBits = bit_size(Buffer) rem 8, - <<_:AlignBits,Val:Num/integer-unit:8,RestBin/binary>> = Buffer, - {Val,RestBin}. - - -%% First align buffer, then pick the first Num octets. -%% Returns octets as a binary -getoctets_as_bin(Bin,Num) when is_binary(Bin) -> - <<Octets:Num/binary,RestBin/binary>> = Bin, - {Octets,RestBin}; -getoctets_as_bin(Bin,Num) when is_bitstring(Bin) -> - AlignBits = bit_size(Bin) rem 8, - <<_:AlignBits,Val:Num/binary,RestBin/binary>> = Bin, - {Val,RestBin}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% set_choice(Alt,Choices,Altnum) -> ListofBitSettings %% Alt = atom() @@ -238,15 +199,6 @@ encode_small_number(Val) when Val < 64 -> encode_small_number(Val) -> [1|encode_semi_constrained_number(0, Val)]. -decode_small_number(Bytes) -> - {Bit,Bytes2} = getbit(Bytes), - case Bit of - 0 -> - getbits(Bytes2, 6); - 1 -> - decode_semi_constrained_number(Bytes2) - end. - %% X.691:10.7 Encoding of a semi-constrained whole number encode_semi_constrained_number(Lb, Val) -> Val2 = Val - Lb, @@ -261,10 +213,6 @@ encode_semi_constrained_number(Lb, Val) -> [encode_length(Len),21,<<Len:16>>|Oct] end. -decode_semi_constrained_number(Bytes) -> - {Len,Bytes2} = decode_length(Bytes), - getoctets(Bytes2, Len). - encode_constrained_number({Lb,_Ub},_Range,{bits,N},Val) -> Val2 = Val-Lb, [10,N,Val2]; @@ -333,47 +281,6 @@ encode_constrained_number({Lb,Ub}, Val) when Val >= Lb, Ub >= Val -> encode_constrained_number({_,_},Val) -> exit({error,{asn1,{illegal_value,Val}}}). -decode_constrained_number(Buffer,VR={Lb,Ub}) -> - Range = Ub - Lb + 1, - decode_constrained_number(Buffer,VR,Range). - -decode_constrained_number(Buffer,{Lb,_Ub},Range) -> - % Val2 = Val - Lb, - {Val,Remain} = - if - Range == 1 -> - {0,Buffer}; - Range == 2 -> - getbits(Buffer,1); - Range =< 4 -> - getbits(Buffer,2); - Range =< 8 -> - getbits(Buffer,3); - Range =< 16 -> - getbits(Buffer,4); - Range =< 32 -> - getbits(Buffer,5); - Range =< 64 -> - getbits(Buffer,6); - Range =< 128 -> - getbits(Buffer,7); - Range =< 255 -> - getbits(Buffer,8); - Range =< 256 -> - getoctets(Buffer,1); - Range =< 65536 -> - getoctets(Buffer,2); - Range =< (1 bsl (255*8)) -> - OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)), - RangeOctLen = length(OList), - {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}), - {Octs, RestBytes} = getoctets_as_bin(Bytes, Len), - {binary:decode_unsigned(Octs), RestBytes}; - true -> - exit({not_supported,{integer_range,Range}}) - end, - {Val+Lb,Remain}. - %% For some reason the minimum bits needed in the length field in %% the encoding of constrained whole numbers must always be at least 2? minimum_bits(N) when N < 4 -> 2; @@ -476,11 +383,6 @@ decode_length(Buffer) -> % un-constrained exit({error,{asn1,{decode_length,{nyi,above_16k}}}}) end. -decode_length(Buffer, {Lb,Ub}) when Ub =< 65535, Lb >= 0 -> % constrained - decode_constrained_number(Buffer, {Lb,Ub}); -decode_length(Buffer, {Lb,_Ub}) when is_integer(Lb), Lb >= 0 -> % Ub > 65535 - decode_length(Buffer). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% bitstring NamedBitList %% Val can be of: diff --git a/lib/asn1/src/asn1rtt_uper.erl b/lib/asn1/src/asn1rtt_uper.erl index 8efe9a7b0f..e55535860f 100644 --- a/lib/asn1/src/asn1rtt_uper.erl +++ b/lib/asn1/src/asn1rtt_uper.erl @@ -21,7 +21,7 @@ -export([setext/1, fixoptionals/3, fixextensions/2, - skipextensions/3, getbit/1, getchoice/3 ]). + skipextensions/3]). -export([set_choice/3, encode_integer/2, encode_integer/3]). -export([encode_small_number/1, encode_constrained_number/2, encode_boolean/1, @@ -123,29 +123,6 @@ skipextensions(Bytes0, Nr, ExtensionBitstr) when is_bitstring(ExtensionBitstr) - end. -getchoice(Bytes,1,0) -> % only 1 alternative is not encoded - {0,Bytes}; -getchoice(Bytes,_,1) -> - decode_small_number(Bytes); -getchoice(Bytes,NumChoices,0) -> - decode_constrained_number(Bytes,{0,NumChoices-1}). - - -getbit(Buffer) -> - <<B:1,Rest/bitstring>> = Buffer, - {B,Rest}. - -getbits(Buffer, Num) when is_bitstring(Buffer) -> - <<Bs:Num,Rest/bitstring>> = Buffer, - {Bs,Rest}. - - -%% Pick the first Num octets. -%% Returns octets as an integer with bit significance as in buffer. -getoctets(Buffer, Num) when is_bitstring(Buffer) -> - <<Val:Num/integer-unit:8,RestBitStr/bitstring>> = Buffer, - {Val,RestBitStr}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% set_choice(Alt,Choices,Altnum) -> ListofBitSettings %% Alt = atom() @@ -265,15 +242,6 @@ encode_small_number(Val) when Val < 64 -> encode_small_number(Val) -> [<<1:1>>|encode_semi_constrained_number(0, Val)]. -decode_small_number(Bytes) -> - {Bit,Bytes2} = getbit(Bytes), - case Bit of - 0 -> - getbits(Bytes2,6); - 1 -> - decode_semi_constrained_number(Bytes2) - end. - %% X.691:10.7 Encoding of a semi-constrained whole number encode_semi_constrained_number(Lb, Val) -> %% encoding in minimum number of octets preceeded by a length @@ -289,11 +257,6 @@ encode_semi_constrained_number(Lb, Val) -> [encode_length(Size),Bin] end. -decode_semi_constrained_number(Bytes) -> - {Len,Bytes2} = decode_length(Bytes), - {V,Bytes3} = getoctets(Bytes2,Len), - {V,Bytes3}. - encode_constrained_number({Lb,Ub}, Val) when Val >= Lb, Ub >= Val -> Range = Ub - Lb + 1, Val2 = Val - Lb, @@ -302,13 +265,6 @@ encode_constrained_number({Lb,Ub}, Val) when Val >= Lb, Ub >= Val -> encode_constrained_number(Range,Val) -> exit({error,{asn1,{integer_range,Range,value,Val}}}). - -decode_constrained_number(Buffer, {Lb,Ub}) -> - Range = Ub - Lb + 1, - NumBits = num_bits(Range), - {Val,Remain} = getbits(Buffer,NumBits), - {Val+Lb,Remain}. - %% X.691:10.8 Encoding of an unconstrained whole number encode_unconstrained_number(Val) when Val >= 0 -> diff --git a/lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 b/lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 index 18473bae30..f6fe18be10 100644 --- a/lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/ChoExtension.asn1 @@ -41,4 +41,10 @@ ChoExt4 ::= CHOICE str OCTET STRING } +ChoEmptyRoot ::= CHOICE { + ..., + bool BOOLEAN, + int INTEGER (0..7) +} + END diff --git a/lib/asn1/test/testChoExtension.erl b/lib/asn1/test/testChoExtension.erl index 067d4d2bf7..5c67ff62ce 100644 --- a/lib/asn1/test/testChoExtension.erl +++ b/lib/asn1/test/testChoExtension.erl @@ -42,6 +42,11 @@ extension(_Rules) -> roundtrip('ChoExt3', {int,33}), roundtrip('ChoExt4', {str,"abc"}), + roundtrip('ChoEmptyRoot', {bool,false}), + roundtrip('ChoEmptyRoot', {bool,true}), + roundtrip('ChoEmptyRoot', {int,0}), + roundtrip('ChoEmptyRoot', {int,7}), + ok. |