%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2012. 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_imm).
-export([per_dec_boolean/0,per_dec_enumerated/2,per_dec_enumerated/3,
per_dec_integer/2,per_dec_length/3,per_dec_named_integer/3,
dec_slim_cg/2,dec_code_gen/2]).
-export([effective_constraint/2]).
-import(asn1ct_gen, [emit/1]).
-record(st, {var,
base}).
dec_slim_cg(Imm, BytesVar) ->
asn1ct_name:new(v),
[H|T] = atom_to_list(asn1ct_name:curr(v)) ++ "@",
VarBase = [H-($a-$A)|T],
St0 = #st{var=0,base=VarBase},
{Res,Pre,_} = flatten(Imm, BytesVar, St0),
dcg_list_outside(Pre),
Res.
dec_code_gen(Imm, BytesVar) ->
emit(["begin",nl]),
{Dst,DstBuf} = dec_slim_cg(Imm, BytesVar),
emit([",",nl,
"{",Dst,",",DstBuf,"}",nl,
"end"]),
ok.
per_dec_boolean() ->
{map,{get_bits,1,[1]},[{0,false},{1,true}]}.
per_dec_enumerated(NamedList0, Aligned) ->
Constraint = [{'ValueRange',{0,length(NamedList0)-1}}],
NamedList = per_dec_enumerated_fix_list(NamedList0, [enum_error], 0),
Int = per_dec_integer(Constraint, Aligned),
{map,Int,NamedList}.
per_dec_enumerated(BaseNamedList, NamedListExt0, Aligned) ->
Base = per_dec_enumerated(BaseNamedList, Aligned),
NamedListExt = per_dec_enumerated_fix_list(NamedListExt0,
[enum_default], 0),
Ext = {map,per_dec_normally_small_number(Aligned),NamedListExt},
bit_case(Base, Ext).
per_dec_integer(Constraint0, Aligned) ->
Constraint = effective_constraint(integer, Constraint0),
per_dec_integer_1(Constraint, Aligned).
per_dec_length(SingleValue, _, _Aligned) when is_integer(SingleValue) ->
{value,SingleValue};
per_dec_length({S,S}, _, _Aligned) when is_integer(S) ->
{value,S};
per_dec_length({{_,_}=Constr,_}, AllowZero, Aligned) ->
bit_case(per_dec_length(Constr, AllowZero, Aligned),
per_dec_length(undefined, AllowZero, Aligned));
per_dec_length({Lb,Ub}, _AllowZero, Aligned) when is_integer(Lb),
is_integer(Lb),
Ub =< 65535 ->
per_dec_constrained(Lb, Ub, Aligned);
per_dec_length({_,_}, AllowZero, Aligned) ->
decode_unconstrained_length(AllowZero, Aligned);
per_dec_length(undefined, AllowZero, Aligned) ->
decode_unconstrained_length(AllowZero, Aligned).
per_dec_named_integer(Constraint, NamedList0, Aligned) ->
Int = per_dec_integer(Constraint, Aligned),
NamedList = [{K,V} || {V,K} <- NamedList0] ++ [integer_default],
{map,Int,NamedList}.
%%%
%%% Local functions.
%%%
per_dec_enumerated_fix_list([{V,_}|T], Tail, N) ->
[{N,V}|per_dec_enumerated_fix_list(T, Tail, N+1)];
per_dec_enumerated_fix_list([], Tail, _) -> Tail.
per_dec_integer_1([{'SingleValue',Value}], _Aligned) ->
{value,Value};
per_dec_integer_1([{'ValueRange',{Lb,'MAX'}}], Aligned) when is_integer(Lb) ->
per_dec_unconstrained(Aligned);
per_dec_integer_1([{'ValueRange',{Lb,Ub}}], Aligned) when is_integer(Lb),
is_integer(Ub) ->
per_dec_constrained(Lb, Ub, Aligned);
per_dec_integer_1([{{_,_}=Constr0,_}], Aligned) ->
Constr = effective_constraint(integer, [Constr0]),
bit_case(per_dec_integer(Constr, Aligned),
per_dec_unconstrained(Aligned));
per_dec_integer_1([], Aligned) ->
per_dec_unconstrained(Aligned).
per_dec_unconstrained(Aligned) ->
{get_bits,decode_unconstrained_length(false, Aligned),[8,signed]}.
per_dec_constrained(Lb, Ub, false) ->
Range = Ub - Lb + 1,
Get = {get_bits,uper_num_bits(Range),[1]},
add_lb(Lb, Get);
per_dec_constrained(Lb, Ub, true) ->
Range = Ub - Lb + 1,
Get = if
Range =< 255 ->
{get_bits,per_num_bits(Range),[1,unsigned]};
Range == 256 ->
{get_bits,1,[8,unsigned,{align,true}]};
Range =< 65536 ->
{get_bits,2,[8,unsigned,{align,true}]};
true ->
RangeOctLen = byte_size(binary:encode_unsigned(Range - 1)),
{get_bits,per_dec_length({1,RangeOctLen}, false, true),
[8,unsigned,{align,true}]}
end,
add_lb(Lb, Get).
add_lb(0, Get) -> Get;
add_lb(Lb, Get) -> {add,Get,Lb}.
per_dec_normally_small_number(Aligned) ->
Small = {get_bits,6,[1]},
Unlimited = per_decode_semi_constrained(0, Aligned),
bit_case(Small, Unlimited).
per_decode_semi_constrained(Lb, Aligned) ->
add_lb(Lb, {get_bits,decode_unconstrained_length(false, Aligned),[8]}).
bit_case(Base, Ext) ->
{'case',[{test,{get_bits,1,[1]},0,Base},
{test,{get_bits,1,[1]},1,Ext}]}.
decode_unconstrained_length(AllowZero, Aligned) ->
Al = [{align,Aligned}],
Zero = case AllowZero of
false -> [non_zero];
true -> []
end,
{'case',[{test,{get_bits,1,[1|Al]},0,
{value,{get_bits,7,[1|Zero]}}},
{test,{get_bits,1,[1|Al]},1,
{test,{get_bits,1,[1]},0,
{value,{get_bits,14,[1|Zero]}}}}]}.
uper_num_bits(N) ->
uper_num_bits(N, 1, 0).
uper_num_bits(N, T, B) when N =< T -> B;
uper_num_bits(N, T, B) -> uper_num_bits(N, T bsl 1, B+1).
per_num_bits(2) -> 1;
per_num_bits(N) when N =< 4 -> 2;
per_num_bits(N) when N =< 8 -> 3;
per_num_bits(N) when N =< 16 -> 4;
per_num_bits(N) when N =< 32 -> 5;
per_num_bits(N) when N =< 64 -> 6;
per_num_bits(N) when N =< 128 -> 7;
per_num_bits(N) when N =< 255 -> 8.
%%%
%%% Flatten the intermediate format and assign temporaries.
%%%
flatten({get_bits,I,U}, Buf0, St0) when is_integer(I) ->
{Dst,St} = new_var_pair(St0),
Gb = {get_bits,{I,Buf0},U,Dst},
flatten_align(Gb, [], St);
flatten({get_bits,E0,U}, Buf0, St0) ->
{E,Pre,St1} = flatten(E0, Buf0, St0),
{Dst,St2} = new_var_pair(St1),
Gb = {get_bits,E,U,Dst},
flatten_align(Gb, Pre, St2);
flatten({test,{get_bits,I,U},V,E0}, Buf0, St0) when is_integer(I) ->
{DstBuf0,St1} = new_var("Buf", St0),
Gb = {get_bits,{I,Buf0},U,{V,DstBuf0}},
{{_Dst,DstBuf},Pre0,St2} = flatten_align(Gb, [], St1),
{E,Pre1,St3} = flatten(E0, DstBuf, St2),
{E,Pre0++Pre1,St3};
flatten({add,E0,I}, Buf0, St0) ->
{{Src,Buf},Pre,St1} = flatten(E0, Buf0, St0),
{Dst,St} = new_var("Add", St1),
{{Dst,Buf},Pre++[{add,Src,I,Dst}],St};
flatten({'case',Cs0}, Buf0, St0) ->
{Dst,St1} = new_var_pair(St0),
{Cs1,St} = flatten_cs(Cs0, Buf0, St1),
{Al,Cs2} = flatten_hoist_align(Cs1),
{Dst,Al++[{'case',Buf0,Cs2,Dst}],St};
flatten({map,E0,Cs0}, Buf0, St0) ->
{{E,DstBuf},Pre,St1} = flatten(E0, Buf0, St0),
{Dst,St2} = new_var("Int", St1),
Cs = flatten_map_cs(Cs0, E),
{{Dst,DstBuf},Pre++[{'map',E,Cs,{Dst,DstBuf}}],St2};
flatten({value,V0}, Buf0, St0) when is_integer(V0) ->
{{V0,Buf0},[],St0};
flatten({value,V0}, Buf0, St0) ->
flatten(V0, Buf0, St0).
flatten_cs([C0|Cs0], Buf, St0) ->
{C,Pre,St1} = flatten(C0, Buf, St0),
{Cs,St2} = flatten_cs(Cs0, Buf, St0),
St3 = St2#st{var=max(St1#st.var, St2#st.var)},
{[Pre++[{return,C}]|Cs],St3};
flatten_cs([], _, St) -> {[],St}.
flatten_map_cs(Cs, Var) ->
flatten_map_cs_1(Cs, {Var,Cs}).
flatten_map_cs_1([{K,V}|Cs], DefData) ->
[{{asis,K},{asis,V}}|flatten_map_cs_1(Cs, DefData)];
flatten_map_cs_1([integer_default], {Int,_}) ->
[{'_',Int}];
flatten_map_cs_1([enum_default], {Int,_}) ->
[{'_',["{asn1_enum,",Int,"}"]}];
flatten_map_cs_1([enum_error], {Var,Cs}) ->
Vs = [V || {_,V} <- Cs],
[{'_',["exit({error,{asn1,{decode_enumerated,{",Var,",",
{asis,Vs},"}}}})"]}];
flatten_map_cs_1([], _) -> [].
flatten_hoist_align([[{align_bits,_,_}=Ab|T]|Cs]) ->
flatten_hoist_align_1(Cs, Ab, [T]);
flatten_hoist_align(Cs) -> {[],Cs}.
flatten_hoist_align_1([[Ab|T]|Cs], Ab, Acc) ->
flatten_hoist_align_1(Cs, Ab, [T|Acc]);
flatten_hoist_align_1([], Ab, Acc) ->
{[Ab],lists:reverse(Acc)}.
flatten_align({get_bits,{SrcBits,SrcBuf},U,Dst}=Gb0, Pre, St0) ->
case is_aligned(U) of
false ->
flatten_align_1(U, Dst, Pre++[Gb0], St0);
true ->
{PadBits,St1} = new_var("Pad", St0),
{DstBuf,St2} = new_var("Buf", St1),
Ab = {align_bits,SrcBuf,PadBits},
Agb = {get_bits,{PadBits,SrcBuf},[1],{'_',DstBuf}},
Gb = {get_bits,{SrcBits,DstBuf},U,Dst},
flatten_align_1(U, Dst, Pre++[Ab,Agb,Gb], St2)
end.
flatten_align_1(U, {D,_}=Dst, Pre, St) ->
case is_non_zero(U) of
false ->
{Dst,Pre,St};
true ->
{Dst,Pre++[{non_zero,D}],St}
end.
new_var_pair(St0) ->
{Var,St1} = new_var("V", St0),
{Buf,St2} = new_var("Buf", St1),
{{Var,Buf},St2}.
new_var(Tag, #st{base=VarBase,var=N}=St) ->
{VarBase++Tag++integer_to_list(N),St#st{var=N+1}}.
is_aligned(Fl) ->
proplists:get_bool(align, Fl).
is_non_zero(Fl) ->
lists:member(non_zero, Fl).
%%%
%%% Generate Erlang code from the flattened intermediate format.
%%%
dcg_list_outside([{align_bits,Buf,SzVar}|T]) ->
emit([SzVar," = bit_size(",Buf,") band 7"]),
iter_dcg_list_outside(T);
dcg_list_outside([{'case',Buf,Cs,Dst}|T]) ->
dcg_case(Buf, Cs, Dst),
iter_dcg_list_outside(T);
dcg_list_outside([{'map',Val,Cs,Dst}|T]) ->
dcg_map(Val, Cs, Dst),
iter_dcg_list_outside(T);
dcg_list_outside([{add,S1,S2,Dst}|T]) ->
emit([Dst," = ",S1," + ",S2]),
iter_dcg_list_outside(T);
dcg_list_outside([{return,{V,Buf}}|T]) ->
emit(["{",V,",",Buf,"}"]),
iter_dcg_list_outside(T);
dcg_list_outside([{get_bits,{_,Buf0},_,_}|_]=L0) ->
emit("<<"),
{L,Buf} = dcg_list_inside(L0, buf),
emit([Buf,"/bitstring>> = ",Buf0]),
iter_dcg_list_outside(L);
dcg_list_outside([]) ->
emit("ignore"),
ok.
iter_dcg_list_outside([_|_]=T) ->
emit([",",nl]),
dcg_list_outside(T);
iter_dcg_list_outside([]) -> ok.
dcg_case(Buf, Cs, {Dst,DstBuf}) ->
emit(["{",Dst,",",DstBuf,"} = case ",Buf," of",nl]),
dcg_case_cs(Cs),
emit("end").
dcg_case_cs([C|Cs]) ->
emit("<<"),
{T0,DstBuf} = dcg_list_inside(C, buf),
emit([DstBuf,"/bitstring>>"]),
T1 = dcg_guard(T0),
dcg_list_outside(T1),
case Cs of
[] -> emit([nl]);
[_|_] -> emit([";",nl])
end,
dcg_case_cs(Cs);
dcg_case_cs([]) -> ok.
dcg_guard([{non_zero,Src}|T]) ->
emit([" when ",Src," =/= 0 ->",nl]),
T;
dcg_guard(T) ->
emit([" ->",nl]),
T.
dcg_map(Val, Cs, {Dst,_}) ->
emit([Dst," = case ",Val," of",nl]),
dcg_map_cs(Cs),
emit("end").
dcg_map_cs([{K,V}]) ->
emit([K," -> ",V,nl]);
dcg_map_cs([{K,V}|Cs]) ->
emit([K," -> ",V,";",nl]),
dcg_map_cs(Cs).
dcg_list_inside([{get_bits,{Sz,_},Fl0,{Dst,DstBuf}}|T], _) ->
Fl = bit_flags(Fl0, []),
emit([mk_dest(Dst),":",Sz,Fl,","]),
dcg_list_inside(T, DstBuf);
dcg_list_inside(L, Dst) -> {L,Dst}.
bit_flags([1|T], Acc) ->
bit_flags(T, Acc);
bit_flags([{align,_}|T], Acc) ->
bit_flags(T, Acc);
bit_flags([non_zero|T], Acc) ->
bit_flags(T, Acc);
bit_flags([U|T], Acc) when is_integer(U) ->
bit_flags(T, ["unit:"++integer_to_list(U)|Acc]);
bit_flags([H|T], Acc) ->
bit_flags(T, [atom_to_list(H)|Acc]);
bit_flags([], []) ->
"";
bit_flags([], Acc) ->
"/" ++ bit_flags_1(Acc, "").
bit_flags_1([H|T], Sep) ->
Sep ++ H ++ bit_flags_1(T, "-");
bit_flags_1([], _) -> [].
mk_dest(I) when is_integer(I) ->
integer_to_list(I);
mk_dest(S) -> S.
%% 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];
effective_constraint(integer, C) ->
SVs = get_constraints(C, 'SingleValue'),
SV = effective_constr('SingleValue', SVs),
VRs = get_constraints(C, 'ValueRange'),
VR = effective_constr('ValueRange', VRs),
greatest_common_range(SV, VR);
effective_constraint(bitstring, C) ->
get_constraint(C, 'SizeConstraint').
effective_constr(_, []) -> [];
effective_constr('SingleValue', List) ->
SVList = lists:flatten(lists:map(fun(X) -> element(2, X) end, List)),
%% Sort and remove duplicates before generating SingleValue or ValueRange
%% In case of ValueRange, also check for 'MIN and 'MAX'
case lists:usort(SVList) of
[N] ->
[{'SingleValue',N}];
[_|_]=L ->
[{'ValueRange',{least_Lb(L),greatest_Ub(L)}}]
end;
effective_constr('ValueRange', List) ->
LBs = lists:map(fun({_,{Lb,_}}) -> Lb end, List),
UBs = lists:map(fun({_,{_,Ub}}) -> Ub end, List),
Lb = least_Lb(LBs),
[{'ValueRange',{Lb,lists:max(UBs)}}].
greatest_common_range([], VR) ->
VR;
greatest_common_range(SV, []) ->
SV;
greatest_common_range([{_,Int}], [{_,{'MIN',Ub}}])
when is_integer(Int), Int > Ub ->
[{'ValueRange',{'MIN',Int}}];
greatest_common_range([{_,Int}],[{_,{Lb,Ub}}])
when is_integer(Int), Int < Lb ->
[{'ValueRange',{Int,Ub}}];
greatest_common_range([{_,Int}],VR=[{_,{_Lb,_Ub}}]) when is_integer(Int) ->
VR;
greatest_common_range([{_,L}],[{_,{Lb,Ub}}]) when is_list(L) ->
Min = least_Lb([Lb|L]),
Max = greatest_Ub([Ub|L]),
[{'ValueRange',{Min,Max}}];
greatest_common_range([{_,{Lb1,Ub1}}], [{_,{Lb2,Ub2}}]) ->
Min = least_Lb([Lb1,Lb2]),
Max = greatest_Ub([Ub1,Ub2]),
[{'ValueRange',{Min,Max}}].
least_Lb(L) ->
case lists:member('MIN', L) of
true -> 'MIN';
false -> lists:min(L)
end.
greatest_Ub(L) ->
case lists:member('MAX', L) of
true -> 'MAX';
false -> lists:max(L)
end.
get_constraint(C, Key) ->
case lists:keyfind(Key, 1, C) of
false -> no;
{_,V} -> V
end.
get_constraints([{Key,_}=Pair|T], Key) ->
[Pair|get_constraints(T, Key)];
get_constraints([_|T], Key) ->
get_constraints(T, Key);
get_constraints([], _) -> [].