%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2013. 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%
%%
%%
-module(asn1ct_gen_per_rt2ct).
%% Handle encoding of primitives for aligned PER.
-include("asn1_records.hrl").
-export([gen_encode_prim/4]).
-import(asn1ct_gen, [emit/1,demit/1]).
-import(asn1ct_func, [call/3]).
gen_encode_prim(Erules,D,DoTag,Value) when is_record(D,type) ->
Constraint = D#type.constraint,
case D#type.def of
'INTEGER' ->
EffectiveConstr = effective_constraint(integer,Constraint),
emit([" %%INTEGER with effective constraint: ",
{asis,EffectiveConstr},nl]),
emit_enc_integer(Erules,EffectiveConstr,Value);
{'INTEGER',NamedNumberList} ->
EffectiveConstr = effective_constraint(integer,Constraint),
%% maybe an emit_enc_NNL_integer
emit([" %%INTEGER with effective constraint: ",
{asis,EffectiveConstr},nl]),
emit_enc_integer_NNL(Erules,EffectiveConstr,Value,NamedNumberList);
'REAL' ->
emit_enc_real(Erules, Value);
{'BIT STRING',NamedNumberList} ->
EffectiveC = effective_constraint(bitstring,Constraint),
case EffectiveC of
0 ->
emit({"[]"});
_ ->
call(Erules, encode_bit_string,
[{asis,EffectiveC},Value,
{asis,NamedNumberList}])
end;
'NULL' ->
emit("[]");
'OBJECT IDENTIFIER' ->
call(Erules, encode_object_identifier, [Value]);
'RELATIVE-OID' ->
call(Erules, encode_relative_oid, [Value]);
'ObjectDescriptor' ->
call(Erules, encode_ObjectDescriptor,
[{asis,Constraint},Value]);
'BOOLEAN' ->
emit({"case ",Value," of",nl,
" true -> [1];",nl,
" false -> [0];",nl,
" _ -> exit({error,{asn1,{encode_boolean,",Value,"}}})",nl,
"end"});
'OCTET STRING' ->
emit_enc_octet_string(Erules,Constraint,Value);
'NumericString' ->
emit_enc_known_multiplier_string('NumericString',Constraint,Value);
TString when TString == 'TeletexString';
TString == 'T61String' ->
call(Erules, encode_TeletexString, [{asis,Constraint},Value]);
'VideotexString' ->
call(Erules, encode_VideotexString, [{asis,Constraint},Value]);
'UTCTime' ->
emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
'GeneralizedTime' ->
emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
'GraphicString' ->
call(Erules, encode_GraphicString, [{asis,Constraint},Value]);
'VisibleString' ->
emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
'GeneralString' ->
call(Erules, encode_GeneralString, [{asis,Constraint},Value]);
'PrintableString' ->
emit_enc_known_multiplier_string('PrintableString',Constraint,Value);
'IA5String' ->
emit_enc_known_multiplier_string('IA5String',Constraint,Value);
'BMPString' ->
emit_enc_known_multiplier_string('BMPString',Constraint,Value);
'UniversalString' ->
emit_enc_known_multiplier_string('UniversalString',Constraint,Value);
'UTF8String' ->
call(Erules, encode_UTF8String, [Value]);
'ANY' ->
call(Erules, encode_open_type, [Value]);
'ASN1_OPEN_TYPE' ->
NewValue = case Constraint of
[#'Externaltypereference'{type=Tname}] ->
asn1ct_func:need({Erules,complete,1}),
io_lib:format(
"complete(enc_~s(~s))",[Tname,Value]);
[#type{def=#'Externaltypereference'{type=Tname}}] ->
asn1ct_func:need({Erules,complete,1}),
io_lib:format(
"complete(enc_~s(~s))",
[Tname,Value]);
_ -> Value
end,
call(Erules, encode_open_type, [NewValue]);
#'ObjectClassFieldType'{} ->
case asn1ct_gen:get_inner(D#type.def) of
{fixedtypevaluefield,_,InnerType} ->
gen_encode_prim(Erules,InnerType,DoTag,Value);
T -> %% 'ASN1_OPEN_TYPE'
gen_encode_prim(Erules,D#type{def=T},DoTag,Value)
end;
XX ->
exit({asn1_error,nyi,XX})
end.
emit_enc_real(Erules, Real) ->
asn1ct_name:new(tmpval),
asn1ct_name:new(tmplen),
emit(["begin",nl,
"{",{curr,tmpval},com,{curr,tmplen},"} = ",
{call,real_common,encode_real,[Real]},com,nl,
"[",{call,Erules,encode_length,[{curr,tmplen}]},",",nl,
{call,Erules,octets_to_complete,
[{curr,tmplen},{curr,tmpval}]},"]",nl,
"end"]).
emit_enc_known_multiplier_string(StringType,C,Value) ->
SizeC =
case get_constraint(C,'SizeConstraint') of
L when is_list(L) -> {lists:min(L),lists:max(L)};
L -> L
end,
PAlphabC = get_constraint(C,'PermittedAlphabet'),
case {StringType,PAlphabC} of
{'UniversalString',{_,_}} ->
exit({error,{asn1,{'not implemented',"UniversalString with "
"PermittedAlphabet constraint"}}});
{'BMPString',{_,_}} ->
exit({error,{asn1,{'not implemented',"BMPString with "
"PermittedAlphabet constraint"}}});
_ -> ok
end,
NumBits = get_NumBits(C,StringType),
CharOutTab = get_CharOutTab(C,StringType),
%% NunBits and CharOutTab for chars_encode
emit_enc_k_m_string(SizeC, NumBits, CharOutTab, Value).
emit_enc_k_m_string(0, _NumBits, _CharOutTab, _Value) ->
emit({"[]"});
emit_enc_k_m_string(SizeC, NumBits, CharOutTab, Value) ->
call(per, encode_known_multiplier_string,
[{asis,SizeC},NumBits,{asis,CharOutTab},Value]).
%% copied from run time module
get_CharOutTab(C, StringType) ->
case get_constraint(C,'PermittedAlphabet') of
{'SingleValue',Sv} ->
get_CharTab2(C, StringType, hd(Sv), lists:max(Sv), Sv);
no ->
case StringType of
'IA5String' ->
{0,16#7F,notab};
'VisibleString' ->
get_CharTab2(C, StringType, 16#20, 16#7F, notab);
'PrintableString' ->
Chars = lists:sort(
" '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
get_CharTab2(C, StringType, hd(Chars),
lists:max(Chars), Chars);
'NumericString' ->
get_CharTab2(C, StringType, 16#20, $9, " 0123456789");
'UniversalString' ->
{0,16#FFFFFFFF,notab};
'BMPString' ->
{0,16#FFFF,notab}
end
end.
get_CharTab2(C, StringType, Min, Max, Chars) ->
BitValMax = (1 bsl get_NumBits(C,StringType))-1,
if
Max =< BitValMax ->
{0,Max,notab};
true ->
{Min,Max,create_char_tab(Min,Chars)}
end.
create_char_tab(Min,L) ->
list_to_tuple(create_char_tab(Min,L,0)).
create_char_tab(Min,[Min|T],V) ->
[V|create_char_tab(Min+1,T,V+1)];
create_char_tab(_Min,[],_V) ->
[];
create_char_tab(Min,L,V) ->
[false|create_char_tab(Min+1,L,V)].
get_NumBits(C,StringType) ->
case get_constraint(C,'PermittedAlphabet') of
{'SingleValue',Sv} ->
charbits(length(Sv),aligned);
no ->
case StringType of
'IA5String' ->
charbits(128,aligned); % 16#00..16#7F
'VisibleString' ->
charbits(95,aligned); % 16#20..16#7E
'PrintableString' ->
charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
'NumericString' ->
charbits(11,aligned); % $ ,"0123456789"
'UniversalString' ->
32;
'BMPString' ->
16
end
end.
charbits(NumOfChars,aligned) ->
case charbits(NumOfChars) of
1 -> 1;
2 -> 2;
B when B =< 4 -> 4;
B when B =< 8 -> 8;
B when B =< 16 -> 16;
B when B =< 32 -> 32
end.
charbits(NumOfChars) when NumOfChars =< 2 -> 1;
charbits(NumOfChars) when NumOfChars =< 4 -> 2;
charbits(NumOfChars) when NumOfChars =< 8 -> 3;
charbits(NumOfChars) when NumOfChars =< 16 -> 4;
charbits(NumOfChars) when NumOfChars =< 32 -> 5;
charbits(NumOfChars) when NumOfChars =< 64 -> 6;
charbits(NumOfChars) when NumOfChars =< 128 -> 7;
charbits(NumOfChars) when NumOfChars =< 256 -> 8;
charbits(NumOfChars) when NumOfChars =< 512 -> 9;
charbits(NumOfChars) when NumOfChars =< 1024 -> 10;
charbits(NumOfChars) when NumOfChars =< 2048 -> 11;
charbits(NumOfChars) when NumOfChars =< 4096 -> 12;
charbits(NumOfChars) when NumOfChars =< 8192 -> 13;
charbits(NumOfChars) when NumOfChars =< 16384 -> 14;
charbits(NumOfChars) when NumOfChars =< 32768 -> 15;
charbits(NumOfChars) when NumOfChars =< 65536 -> 16;
charbits(NumOfChars) when is_integer(NumOfChars) ->
16 + charbits1(NumOfChars bsr 16).
charbits1(0) ->
0;
charbits1(NumOfChars) ->
1 + charbits1(NumOfChars bsr 1).
%% copied from run time module
emit_enc_octet_string(Erules, Constraint, Value) ->
case get_constraint(Constraint,'SizeConstraint') of
0 ->
emit({" []"});
1 ->
asn1ct_name:new(tmpval),
emit({" begin",nl}),
emit({" [",{curr,tmpval},"] = ",Value,",",nl}),
emit([" [[10,8],",{curr,tmpval},"]",nl]),
emit(" end");
2 ->
asn1ct_name:new(tmpval),
emit([" begin",nl,
" ",{curr,tmpval}," = ",Value,",",nl,
" case length(",{curr,tmpval},") of",nl,
" 2 ->",nl,
" [[45,16,2]|",{curr,tmpval},"];",nl,
" _ ->",nl,
" exit({error,{value_out_of_bounds,",
{curr,tmpval},"}})",nl,
" end",nl,
" end"]);
Sv when is_integer(Sv), Sv < 256 ->
asn1ct_name:new(tmpval),
asn1ct_name:new(tmplen),
emit([" begin",nl,
" ",{curr,tmpval}," = ",Value,",",nl,
" case length(",{curr,tmpval},") of",nl,
" ",Sv,"=",{curr,tmplen}," ->",nl,
" [20,",{curr,tmplen},"|",{curr,tmpval},"];",nl,
" _ ->",nl,
" exit({error,{value_out_of_bounds,",
{curr,tmpval},"}})",nl,
" end",nl,
" end"]);
Sv when is_integer(Sv),Sv =< 65535 ->
asn1ct_name:new(tmpval),
asn1ct_name:new(tmplen),
emit([" begin",nl,
" ",{curr,tmpval}," = ",Value,",",nl,
" case length(",{curr,tmpval},") of",nl,
" ",Sv,"=",{curr,tmplen}," ->",nl,
" [<<21,",{curr,tmplen},":16>>|",Value,"];",nl,
" _ ->",nl,
" exit({error,{value_out_of_bounds,",
{curr,tmpval},"}})",nl,
" end",nl,
" end"]);
C ->
call(Erules, encode_octet_string,
[{asis,C},Value])
end.
emit_enc_integer_case(Value) ->
case get(component_type) of
{true,#'ComponentType'{prop=Prop}} ->
emit({" begin",nl}),
case Prop of
Opt when Opt=='OPTIONAL';
is_tuple(Opt),element(1,Opt)=='DEFAULT' ->
emit({" case ",Value," of",nl}),
ok;
_ ->
emit({" ",{curr,tmpval},"=",Value,",",nl}),
emit({" case ",{curr,tmpval}," of",nl}),
asn1ct_name:new(tmpval)
end;
% asn1ct_name:new(tmpval);
_ ->
emit({" case ",Value," of ",nl})
end.
emit_enc_integer_end_case() ->
case get(component_type) of
{true,_} ->
emit({nl," end"}); % end of begin ... end
_ -> ok
end.
emit_enc_integer_NNL(Erules,C,Value,NNL) ->
EncVal = enc_integer_NNL_cases(Value,NNL),
emit_enc_integer(Erules,C,EncVal).
enc_integer_NNL_cases(Value,NNL) ->
asn1ct_name:new(tmpval),
TmpVal = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
Cases=enc_integer_NNL_cases1(NNL),
lists:flatten(io_lib:format("(case ~s of "++Cases++
"~s when is_atom(~s)->exit({error,{asn1,{namednumber,~s}}});_->~s end)",[Value,TmpVal,TmpVal,TmpVal,Value])).
enc_integer_NNL_cases1([{NNo,No}|Rest]) ->
io_lib:format("~w->~w;",[NNo,No])++enc_integer_NNL_cases1(Rest);
enc_integer_NNL_cases1([]) ->
"".
emit_enc_integer(_Erule,[{'SingleValue',Int}],Value) ->
asn1ct_name:new(tmpval),
emit_enc_integer_case(Value),% emit([" case ",Value," of",nl]),
emit([" ",Int," -> [];",nl]),
emit([" ",{curr,tmpval}," ->",nl]),
emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
nl," end",nl]),
emit_enc_integer_end_case();
emit_enc_integer(_Erule,[{_,{Lb,Ub},_Range,{bits,NoBs}}],Value) -> % Range =< 255
asn1ct_name:new(tmpval),
emit_enc_integer_case(Value),
emit([" ",{curr,tmpval}," when ",{curr,tmpval},"=<",Ub,",",
{curr,tmpval},">=",Lb," ->",nl]),
emit([" [10,",NoBs,",",{curr,tmpval},"- ",Lb,"];",nl]),
emit([" ",{curr,tmpval}," ->",nl]),
emit([" exit({error,{value_out_of_bounds,",
{curr,tmpval},"}})",nl," end",nl]),
emit_enc_integer_end_case();
emit_enc_integer(_Erule,[{_,{Lb,Ub},Range,_}],Value) when Range =< 256 ->
asn1ct_name:new(tmpval),
emit_enc_integer_case(Value),
emit([" ",{curr,tmpval}," when ",{curr,tmpval},"=<",Ub,",",
{curr,tmpval},">=",Lb," ->",nl]),
emit([" [20,1,",{curr,tmpval},"- ",Lb,"];",nl]),
emit([" ",{curr,tmpval}," ->",nl]),
emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
nl," end",nl]),
emit_enc_integer_end_case();
emit_enc_integer(_Erule,[{_,{Lb,Ub},Range,_}],Value) when Range =< 65536 ->
asn1ct_name:new(tmpval),
emit_enc_integer_case(Value),
emit([" ",{curr,tmpval}," when ",{curr,tmpval},"=<",Ub,",",
{curr,tmpval},">=",Lb," ->",nl]),
emit([" [20,2,<<(",{curr,tmpval},"- ",Lb,"):16>>];",nl]),
emit([" ",{curr,tmpval}," ->",nl]),
emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
nl," end",nl]),
emit_enc_integer_end_case();
emit_enc_integer(Erule, [{'ValueRange',{Lb,Ub}=VR}], Value)
when is_integer(Lb), is_integer(Ub) ->
call(Erule, encode_constrained_number, [{asis,VR},Value]);
emit_enc_integer(Erule, C, Value) ->
call(Erule, encode_integer, [{asis,C},Value]).
get_constraint([{Key,V}],Key) ->
V;
get_constraint([],_) ->
no;
get_constraint(C,Key) ->
case lists:keysearch(Key,1,C) of
false ->
no;
{value,{_,V}} ->
V
end.
%% effective_constraint(Type,C)
%% Type = atom()
%% C = [C1,...]
%% C1 = {'SingleValue',SV} | {'ValueRange',VR} | {atom(),term()}
%% SV = integer() | [integer(),...]
%% VR = {Lb,Ub}
%% Lb = 'MIN' | integer()
%% Ub = 'MAX' | integer()
%% Returns a single value if C only has a single value constraint, and no
%% value range constraints, that constrains to a single value, otherwise
%% 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]; %% [C|effective_constraint(integer,Rest)]; XXX what is possible ???
effective_constraint(integer,C) ->
pre_encode(integer, asn1ct_imm:effective_constraint(integer, C));
effective_constraint(bitstring,C) ->
asn1ct_imm:effective_constraint(bitstring, C).
pre_encode(integer,[]) ->
[];
pre_encode(integer,C=[{'SingleValue',_}]) ->
C;
pre_encode(integer,C=[{'ValueRange',VR={Lb,Ub}}]) when is_integer(Lb),is_integer(Ub)->
Range = Ub-Lb+1,
if
Range =< 255 ->
NoBits = no_bits(Range),
[{'ValueRange',VR,Range,{bits,NoBits}}];
Range =< 256 ->
[{'ValueRange',VR,Range,{octets,1}}];
Range =< 65536 ->
[{'ValueRange',VR,Range,{octets,2}}];
true ->
C
end;
pre_encode(integer,C) ->
C.
no_bits(2) -> 1;
no_bits(N) when N=<4 -> 2;
no_bits(N) when N=<8 -> 3;
no_bits(N) when N=<16 -> 4;
no_bits(N) when N=<32 -> 5;
no_bits(N) when N=<64 -> 6;
no_bits(N) when N=<128 -> 7;
no_bits(N) when N=<255 -> 8.