aboutsummaryrefslogtreecommitdiffstats
path: root/lib/megaco/src/binary/megaco_binary_term_id_gen.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/megaco/src/binary/megaco_binary_term_id_gen.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/megaco/src/binary/megaco_binary_term_id_gen.erl')
-rw-r--r--lib/megaco/src/binary/megaco_binary_term_id_gen.erl436
1 files changed, 436 insertions, 0 deletions
diff --git a/lib/megaco/src/binary/megaco_binary_term_id_gen.erl b/lib/megaco/src/binary/megaco_binary_term_id_gen.erl
new file mode 100644
index 0000000000..c8192c79b6
--- /dev/null
+++ b/lib/megaco/src/binary/megaco_binary_term_id_gen.erl
@@ -0,0 +1,436 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2009. 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%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% 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.
+
+