%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2001-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% %%---------------------------------------------------------------------- %% Purpose : Handle ASN.1 BER encoding of Megaco/H.248 %%---------------------------------------------------------------------- -module(megaco_binary_term_id_gen). %%---------------------------------------------------------------------- %% Include files %%---------------------------------------------------------------------- -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/src/engine/megaco_message_internal.hrl"). %%---------------------------------------------------------------------- %% External exports %%---------------------------------------------------------------------- -export([encode_without_wildcards/2, encode_with_wildcards/2, decode_without_wildcards/2, decode_with_wildcards/3]). %%---------------------------------------------------------------------- %% Internal exports %%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Macros %%---------------------------------------------------------------------- -define(asn_choose, ?megaco_choose). -define(asn_all, ?megaco_all). %%---------------------------------------------------------------------- %% Encode without wildcards %%---------------------------------------------------------------------- encode_without_wildcards(IDs,LevelConfig) when is_list(LevelConfig) -> EncodedIDs = encode_ids(false,IDs,LevelConfig), #'TerminationID'{wildcard = [], id = EncodedIDs}. %%---------------------------------------------------------------------- %% Encode with wildcards %%---------------------------------------------------------------------- encode_with_wildcards(IDs,LevelConfig) when is_list(LevelConfig) -> Wildcards = encode_wildcards(IDs,LevelConfig), EncodedIDs = encode_ids(true,IDs,LevelConfig), #'TerminationID'{wildcard = Wildcards, id = EncodedIDs}. %%---------------------------------------------------------------------- %% Decode without wildcards %%---------------------------------------------------------------------- decode_without_wildcards(IDs,Lc) -> DecodedIDs = decode_ids(IDs,Lc), #megaco_term_id{contains_wildcards = false, id = DecodedIDs}. %%---------------------------------------------------------------------- %% Decode with wildcards %%---------------------------------------------------------------------- decode_with_wildcards(Wildcards,IDs,Lc) -> DecodedIDs = decode_ids(Wildcards,IDs,Lc), #megaco_term_id{contains_wildcards = true, id = DecodedIDs}. %%---------------------------------------------------------------------- %% Convert an internal TermId to an external %%---------------------------------------------------------------------- encode_wildcards(IDs,LevelConfig) -> case (catch encode_wildcards1(IDs,LevelConfig)) of {'EXIT',id_config_mismatch} -> exit({id_config_mismatch,IDs,LevelConfig}); {'EXIT',Reason} -> exit(Reason); Wildcards -> encode_wildcards2(Wildcards) end. encode_wildcards1(IDs,LevelConfig) -> encode_wildcards3(IDs,LevelConfig). encode_wildcards2(Ws) -> F = fun(no_wildcard) -> false; (_) -> true end, lists:filter(F,Ws). encode_wildcards3(IDs,LevelConfig) -> encode_wildcards3(IDs,LevelConfig,1,lists:sum(LevelConfig)). encode_wildcards3([],[],_,_) -> []; encode_wildcards3([Level|Levels],[BitsInLevel|BitsRest],LevelNo,TotSize) -> case (catch encode_wildcard(Level,BitsInLevel,TotSize-BitsInLevel, length(Levels))) of {'EXIT',{Reason,Info}} -> exit({Reason,{LevelNo,Info}}); no_wildcard -> encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel); {level,Wl} -> [Wl| encode_wildcards3(Levels,BitsRest,LevelNo+1,TotSize-BitsInLevel)]; {recursive,Wr} -> [Wr]; Else -> exit({wildcard_decode_error,Else}) end; encode_wildcards3(Levels,[],LevelNo,TotSize) -> exit({id_config_mismatch,{Levels,LevelNo,TotSize}}); encode_wildcards3(L,B,N,S) -> exit({wildcard_encode_error,{L,B,N,S}}). encode_wildcard([],0,_TotBits,_RemainingIdLevels) -> no_wildcard; encode_wildcard([],More,_TotBits,_RemainingIdLevels) -> exit({to_few_bits_in_level,More}); encode_wildcard(More,0,_TotBits,_RemainingIdLevels) -> exit({to_many_bits_in_level,More}); encode_wildcard([$0|R],Pos,TotBits,RemainingIdLevels) -> encode_wildcard(R,Pos-1,TotBits,RemainingIdLevels); encode_wildcard([$1|R],Pos,TotBits,RemainingIdLevels) -> encode_wildcard(R,Pos-1,TotBits,RemainingIdLevels); encode_wildcard([?asn_choose],Pos,TotBits,RemainingIdLevels) -> encode_choose(Pos-1,TotBits,RemainingIdLevels); encode_wildcard([?asn_all],Pos,TotBits,RemainingIdLevels) -> encode_all(Pos-1,TotBits,RemainingIdLevels); encode_wildcard([Val|_Rest],Pos,_TotBits,_RemainingIdLevels) -> exit({invalid_level_content,{Pos-1,Val}}). %% This is the last level specified in the id list. %% Therefor it is a 'recursive' wildcard, i.e. the 'choose' %% apply to this level and all remaining levels. encode_choose(BitPosInLevel,BitsInRemainingConfigLevels,0) when BitsInRemainingConfigLevels > 0 -> {recursive,[16#40 + BitPosInLevel + BitsInRemainingConfigLevels]}; %% The fact that there is no more bits in the level config %% means that this is actually the last level. %% It should not be a 'recursive' case but a 'level' case. %% Although it is (propably) correct with both. encode_choose(BitPosInLevel,0,0) -> {recursive,[16#00 + BitPosInLevel]}; %% There are more levels specified in the id list. %% Therefor it is a 'level' wildcard, i.e. the 'choose' %% apply to this level only. encode_choose(BitPosInLevel,BitsInRemainingConfigLevels,RemainingIdLevels) when RemainingIdLevels > 0 -> {level,[16#00 + BitPosInLevel + BitsInRemainingConfigLevels]}. %% This is the last level specified in the id list. %% Therefor it is a 'recursive' wildcard, i.e. the 'all' %% apply to this level and all remaining levels. encode_all(BitPosInLevel,BitsInRemainingConfigLevels,0) when BitsInRemainingConfigLevels > 0 -> {recursive,[16#c0 + BitPosInLevel + BitsInRemainingConfigLevels]}; %% The fact that there is no more bits in the level config %% means that this is actually the last level. %% It should not be a 'recursive' case but a 'level' case. %% Although it is (propably) correct with both. encode_all(BitPosInLevel,0,0) -> {recursive,[16#80 + BitPosInLevel]}; %% There are more levels specified in the id list. %% Therefor it is a 'level' wildcard, i.e. the 'all' %% apply to this level only. encode_all(BitPosInLevel,BitsInRemainingConfigLevels,RemainingIdLevels) when RemainingIdLevels > 0 -> {level,[16#80 + BitPosInLevel + BitsInRemainingConfigLevels]}. encode_ids(W,IDs,Config) -> encode_ids(W,IDs,Config,8,[0],false). encode_ids(_,[],[],8,[0|EncodedIDs],_) -> lists:reverse(EncodedIDs); encode_ids(W,IDs,Config,0,EncodedIDs,Wf) -> encode_ids(W,IDs,Config,8,[0|EncodedIDs],Wf); encode_ids(W,[L|Ls],[C|Cs],R,E,_) -> case (catch encode_id_level(W,L,C,R,E)) of {'EXIT',Reason} -> exit(Reason); {true,R1,E1} when (length(Ls) =:= 0) -> encode_ids2(Cs,encode_ids1(R1,E1)); {WildcardFound1,R1,E1} -> encode_ids(W,Ls,Cs,R1,E1,WildcardFound1); {true,E2} when (length(Ls) =:= 0) -> encode_ids2(Cs,E2); {WildcardFound2,E2} -> encode_ids(W,Ls,Cs,8,[0|E2],WildcardFound2) end; encode_ids(W,[[]],C,R,E,Wf) when length(C) > 0 -> exit({empty_last_level,{W,C,R,E,Wf}}); encode_ids(W,[],C,R,E,false) when length(C) > 0 -> exit({unexpected_eof_data,{W,C,R,E}}). encode_ids1(_R,[0|Es]) -> [0|Es]; encode_ids1(R,[E|Es]) -> [(E bsl R)|Es]. encode_ids2([],Es) -> lists:reverse(Es); encode_ids2(Cs,Es) -> Fill = lists:duplicate(lists:sum(Cs) div 8,0), lists:reverse(Fill ++ Es). encode_id_level(W,L,C,R,[E|Es]) -> case encode_id_level1(W,L,C,R,E) of %% End Of Byte (more bits in level) {eob,_WildcardFound,L1,C1,E1} -> encode_id_level(W,L1,C1,8,[0,E1|Es]); %% End Of Level (more room in byte) {eol,WildcardFound,R1,E1} -> {WildcardFound,R1,[E1|Es]}; %% Done; Level used up all of the byte {done,WildcardFound,E1} -> {WildcardFound,[E1|Es]} end. encode_id_level1(_W,[],0,0,E) -> {done,false,E}; encode_id_level1(_W,[],0,R,E) -> {eol,false,R,E}; encode_id_level1(_W,L,C,0,E) -> {eob,false,L,C,E}; encode_id_level1(W,[$0|L],C,R,E) -> encode_id_level1(W,L,C-1,R-1,E bsl 1); encode_id_level1(W,[$1|L],C,R,E) -> encode_id_level1(W,L,C-1,R-1,(E bsl 1) + 1); encode_id_level1(true,[$$],C,R,E) -> encode_id_level2(C,R,E,$$); encode_id_level1(true,[$*],C,R,E) -> encode_id_level2(C,R,E,$*); encode_id_level1(false,[$$],C,R,_E) -> exit({wildcard_error,{$$,C,R}}); encode_id_level1(false,[$*],C,R,_E) -> exit({wildcard_error,{$*,C,R}}); encode_id_level1(_W,[L|_Ls],C,R,_E) -> exit({invalid_level_content,{C,R,L}}). encode_id_level2(C,C,E,_W) -> {done,true,E bsl C}; encode_id_level2(C,R,E,W) when C > R -> {eob,true,[W],C-R,E bsl R}; encode_id_level2(C,R,E,_W) -> {eol,true,R-C,E bsl C}. %%---------------------------------------------------------------------- %% Convert an external TermId to an internal %%---------------------------------------------------------------------- %% Decode ID with wildcards decode_ids(Ws,IDs,Config) when is_list(Config) -> IDs1 = decode_ids(IDs,Config), Ws1 = decode_wildcards(Ws,(length(IDs)*8) - 1), decode_ids1(Ws1,IDs1); %% This is only temporary. Eventually a proper encoder for this case %% should be implemented. %% This is the case when each level is 8 bits = 1 byte and the config %% simply indicates the number of (1 byte) levels decode_ids(Ws,IDs,Config) when is_integer(Config) -> decode_ids(Ws,IDs,lists:duplicate(Config,8)). %% Decode ID without wildcards decode_ids(E,Config) when is_list(Config) -> decode_ids(0,E,Config,[]); %% This is only temporary. Eventually a proper encoder for this case %% should be implemented. %% This is the case when each level is 8 bits = 1 byte and the config %% simply indicates the number of (1 byte) levels decode_ids(E,Config) when is_integer(Config) -> decode_ids(E,lists:duplicate(Config,8)). %% The [0] is the result of all the bits of the byte has been shifted out. decode_ids(_B,[0],[],Acc) -> lists:reverse(Acc); decode_ids(_B,[E],[],Acc) -> exit({id_config_mismatch,{two_much_data,E,Acc}}); decode_ids(_B,E,[],Acc) -> exit({id_config_mismatch,{two_much_data,E,Acc}}); decode_ids(B,E,[L|Ls],Acc) -> case (catch decode_id_level(B,E,L,[])) of {Level,E1,B1} -> decode_ids(B1,E1,Ls,[Level|Acc]); {'EXIT',{id_config_mismatch,{Bx,Ex,Lx,Accx}}} -> exit({id_level_mismatch,{B,Bx,E,Ex,L,Ls,Lx,Acc,Accx}}) end. decode_wildcards(Ws,NofBits) -> lists:keysort(3,[decode_wildcard(W,NofBits) || W <- Ws]). %% ---------------------------------------------------------------------- %% A decoded wildcard is a three tuple: %% {wildcard_type(), wildcard_level(), wildcard_offset()} %% wildcard_type() -> $ | * %% wildcard_level() -> level | recursive %% wildcard_offset() -> integer() %% %% The "raw" wildcard offset is measured from the end of the id bytes: %% %% 0 1 2 3 4 5 6 7 %% ----------------- %% | | | | | | | | | %% ----------------- %% %% |<--------| %% 5 %% %% The decoded wildcard offset in contrast is measured from the start %% of the id field: %% %% 0 1 2 3 4 5 6 7 %% ----------------- %% | | | | | | | | | %% ----------------- %% %% |---->| %% 3 %% decode_wildcard([W],NofBits) -> {decode_wildcard_t(W band 16#80), decode_wildcard_l(W band 16#40), NofBits - (W band 16#3F)}. decode_wildcard_t(16#80) -> ?asn_all; decode_wildcard_t(16#00) -> ?asn_choose. decode_wildcard_l(16#00) -> level; decode_wildcard_l(16#40) -> recursive. decode_ids1(W,IDs) -> lists:reverse(decode_ids1(W,IDs,0,[])). decode_ids1([],IDs,_,Acc) -> lists:reverse(IDs) ++ Acc; decode_ids1([{Type,recursive,Offset}],IDs,Bp,Acc) -> decode_ids2(Type,Offset,IDs,Bp,Acc); decode_ids1([{Type,level,Offset}|Ws],IDs,Bp,Acc) -> {IDs1,IDs2,Bp1} = decode_ids3(Type,Offset,IDs,Bp,[]), decode_ids1(Ws,IDs2,Bp1,IDs1++Acc); decode_ids1(Ws,_,_,_) -> exit({invalid_wildcards,Ws}). %% Called when recursive wildcard found decode_ids2(Type,Offset,[ID|IDs],Bp,Acc) -> LevelSz = length(ID), Bp1 = Offset-Bp, case Bp1 >= LevelSz of true -> decode_ids2(Type,Offset,IDs,Bp+LevelSz,[ID|Acc]); false -> [decode_ids4(Type,Bp1,ID)|Acc] end. decode_ids3(Type,Offset,[ID|IDs],Bp,Acc) -> LevelSz = length(ID), Bp1 = Offset-Bp, case Bp1 > LevelSz of true -> decode_ids3(Type,Offset,IDs,Bp+LevelSz,[ID|Acc]); false -> A1 = decode_ids4(Type,Bp1,ID), {[A1|Acc],IDs,Bp+LevelSz} end. decode_ids4(Type,0,_ID) -> [Type]; decode_ids4(Type,O,[H|T]) -> [H|decode_ids4(Type,O-1,T)]. %% E: Encoded bits -> [byte()] %% L: Number of nits in level decode_id_level(B,E,0,Acc) -> {lists:reverse(Acc),E,B}; decode_id_level(8,[_H|T],L,Acc) -> decode_id_level(0,T,L,Acc); decode_id_level(B,[H|T],L,Acc) -> Acc1 = [decode_id_level1(H band 16#80) | Acc], decode_id_level(B+1,[((H bsl 1) band 16#FF) |T],L-1,Acc1); decode_id_level(B,E,L,Acc) -> exit({id_config_mismatch,{B,E,L,Acc}}). decode_id_level1(16#80) -> $1; decode_id_level1(16#00) -> $0.