%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2012-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(asn1rtt_real_common). -export([encode_real/1,decode_real/1, ber_encode_real/1]). %%============================================================================ %% %% Real value, ITU_T X.690 Chapter 8.5 %%============================================================================ %% %% encode real value %%============================================================================ ber_encode_real(0) -> {[],0}; ber_encode_real('PLUS-INFINITY') -> {[64],1}; ber_encode_real('MINUS-INFINITY') -> {[65],1}; ber_encode_real(Val) when is_tuple(Val); is_list(Val) -> encode_real(Val). %%%%%%%%%%%%%% %% only base 2 encoding! %% binary encoding: %% +------------+ +------------+ +-+-+-+-+---+---+ %% | (tag)9 | | n + p + 1 | |1|S|BB |FF |EE | %% +------------+ +------------+ +-+-+-+-+---+---+ %% %% +------------+ +------------+ %% | | | | %% +------------+ ...+------------+ %% n octets for exponent %% %% +------------+ +------------+ %% | | | | %% +------------+ ...+------------+ %% p octets for pos mantissa %% %% S is 0 for positive sign %% 1 for negative sign %% BB: encoding base, 00 = 2, (01 = 8, 10 = 16) %% 01 and 10 not used %% FF: scale factor 00 = 0 (used in base 2 encoding) %% EE: encoding of the exponent: %% 00 - on the following octet %% 01 - on the 2 following octets %% 10 - on the 3 following octets %% 11 - encoding of the length of the two's-complement encoding of %% exponent on the following octet, and two's-complement %% encoding of exponent on the other octets. %% %% In DER and base 2 encoding the mantissa is encoded as value 0 or %% bit shifted until it is an odd number. Thus, do this for BER as %% well. encode_real(Real) -> encode_real([], Real). encode_real(_C, {Mantissa, Base, Exponent}) when Base =:= 2 -> %% io:format("Mantissa: ~w Base: ~w, Exp: ~w~n",[Man, Base, Exp]), {Man,ExpAdd} = truncate_zeros(Mantissa), %% DER adjustment Exp = Exponent + ExpAdd, OctExp = if Exp >= 0 -> list_to_binary(encode_pos_integer(Exp, [])); true -> list_to_binary(encode_neg_integer(Exp, [])) end, %% ok = io:format("OctExp: ~w~n",[OctExp]), SignBit = if Man > 0 -> 0; % bit 7 is pos or neg, no Zeroval true -> 1 end, %% ok = io:format("SignBitMask: ~w~n",[SignBitMask]), SFactor = 0, OctExpLen = byte_size(OctExp), if OctExpLen > 255 -> exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}}); true -> true %% make real assert later.. end, {LenCode, EOctets} = case OctExpLen of % bit 2,1 1 -> {0, OctExp}; 2 -> {1, OctExp}; 3 -> {2, OctExp}; _ -> {3, <<OctExpLen, OctExp/binary>>} end, BB = 0, %% 00 for base 2 FirstOctet = <<1:1,SignBit:1,BB:2,SFactor:2,LenCode:2>>, OctMantissa = if Man > 0 -> list_to_binary(real_mininum_octets(Man)); true -> list_to_binary(real_mininum_octets(-(Man))) % signbit keeps track of sign end, %% ok = io:format("LenMask: ~w EOctets: ~w~nFirstOctet: ~w OctMantissa: ~w OctExpLen: ~w~n", [LenMask, EOctets, FirstOctet, OctMantissa, OctExpLen]), <<FirstOctet/binary, EOctets/binary, OctMantissa/binary>>; encode_real(C, {Mantissa,Base,Exponent}) when Base =:= 10, is_integer(Mantissa), is_integer(Exponent) -> %% always encode as NR3 due to DER on the format %% mmmm.Eseeee where %% m := digit %% s := '-' | '+' | [] %% '+' only allowed in +0 %% e := digit %% ex: 1234.E-5679 ManStr = integer_to_list(Mantissa), encode_real_as_string(C,ManStr,Exponent); encode_real(_C, {_,Base,_}) -> exit({error,{asn1, {encode_real_non_supported_encoding, Base}}}); %% base 10 encode_real(C, Real) when is_list(Real) -> %% The Real string may come in as a NR1, NR2 or NR3 string. {Mantissa, Exponent} = case string:tokens(Real,"Ee") of [NR2] -> {NR2,0}; [NR3MB,NR3E] -> %% remove beginning zeros {NR3MB,list_to_integer(NR3E)} end, %% .Decimal | Number | Number.Decimal ZeroDecimal = fun("0") -> ""; (L) -> L end, {NewMantissa,LenDecimal} = case Mantissa of [$.|Dec] -> NewMan = remove_trailing_zeros(Dec), {NewMan,length(ZeroDecimal(NewMan))}; _ -> case string:tokens(Mantissa,",.") of [Num] -> %% No decimal-mark {integer_to_list(list_to_integer(Num)),0}; [Num,Dec] -> NewDec = ZeroDecimal(remove_trailing_zeros(Dec)), NewMan = integer_to_list(list_to_integer(Num)) ++ NewDec, {integer_to_list(list_to_integer(NewMan)), length(NewDec)} end end, encode_real_as_string(C, NewMantissa, Exponent - LenDecimal). encode_real_as_string(_C, Mantissa, Exponent) when is_list(Mantissa), is_integer(Exponent) -> %% Remove trailing zeros in Mantissa and add this to Exponent TruncMant = remove_trailing_zeros(Mantissa), ExpIncr = length(Mantissa) - length(TruncMant), ExpStr = integer_to_list(Exponent + ExpIncr), ExpBin = case ExpStr of "0" -> <<"E+0">>; _ -> ExpB = list_to_binary(ExpStr), <<$E,ExpB/binary>> end, ManBin = list_to_binary(TruncMant), NR3 = 3, <<NR3,ManBin/binary,$.,ExpBin/binary>>. remove_trailing_zeros(IntStr) -> case lists:dropwhile(fun($0)-> true; (_) -> false end, lists:reverse(IntStr)) of [] -> "0"; ReversedIntStr -> lists:reverse(ReversedIntStr) end. truncate_zeros(Num) -> truncate_zeros(Num, 0). truncate_zeros(0, Sum) -> {0,Sum}; truncate_zeros(M, Sum) -> case M band 16#f =:= M band 16#e of true -> truncate_zeros(M bsr 1, Sum+1); _ -> {M,Sum} end. %%============================================================================ %% decode real value %% %% decode_real([OctetBufferList], tuple|value, tag|notag) -> %% {{Mantissa, Base, Exp} | realval | PLUS-INFINITY | MINUS-INFINITY | 0, %% RestBuff} %% %% only for base 2 decoding sofar!! %%============================================================================ decode_real(Buffer) -> Sz = byte_size(Buffer), {RealVal,<<>>,Sz} = decode_real2(Buffer, [], Sz, 0), RealVal. decode_real2(Buffer, _C, 0, _RemBytes) -> {0,Buffer}; decode_real2(Buffer0, _C, Len, RemBytes1) -> <<First, Buffer2/binary>> = Buffer0, if First =:= 2#01000000 -> {'PLUS-INFINITY', Buffer2}; First =:= 2#01000001 -> {'MINUS-INFINITY', Buffer2}; First =:= 1 orelse First =:= 2 orelse First =:= 3 -> %% charcter string encoding of base 10 {NRx,Rest} = split_binary(Buffer2,Len-1), {binary_to_list(NRx),Rest,Len}; true -> %% have some check here to verify only supported bases (2) %% not base 8 or 16 <<_B7:1,Sign:1,BB:2,_FF:2,EE:2>> = <<First>>, Base = case BB of 0 -> 2; % base 2, only one so far _ -> exit({error,{asn1, {non_supported_base, BB}}}) end, {FirstLen, {Exp, Buffer3,_Rb2}, RemBytes2} = case EE of 0 -> {2, decode_integer2(1, Buffer2, RemBytes1), RemBytes1+1}; 1 -> {3, decode_integer2(2, Buffer2, RemBytes1), RemBytes1+2}; 2 -> {4, decode_integer2(3, Buffer2, RemBytes1), RemBytes1+3}; 3 -> <<ExpLen1,RestBuffer/binary>> = Buffer2, { ExpLen1 + 2, decode_integer2(ExpLen1, RestBuffer, RemBytes1), RemBytes1+ExpLen1} end, %% io:format("FirstLen: ~w, Exp: ~w, Buffer3: ~w ~n", Length = Len - FirstLen, <<LongInt:Length/unit:8,RestBuff/binary>> = Buffer3, {{Mantissa, Buffer4}, RemBytes3} = if Sign =:= 0 -> %% io:format("sign plus~n"), {{LongInt, RestBuff}, 1 + Length}; true -> %% io:format("sign minus~n"), {{-LongInt, RestBuff}, 1 + Length} end, {{Mantissa, Base, Exp}, Buffer4, RemBytes2+RemBytes3} end. encode_pos_integer(0, [B|_Acc]=L) when B < 128 -> L; encode_pos_integer(N, Acc) -> encode_pos_integer(N bsr 8, [N band 16#ff| Acc]). encode_neg_integer(-1, [B1|_T]=L) when B1 > 127 -> L; encode_neg_integer(N, Acc) -> encode_neg_integer(N bsr 8, [N band 16#ff|Acc]). %% Val must be >= 0 real_mininum_octets(Val) -> real_mininum_octets(Val, []). real_mininum_octets(0, Acc) -> Acc; real_mininum_octets(Val, Acc) -> real_mininum_octets(Val bsr 8, [Val band 16#FF | Acc]). %% decoding postitive integer values. decode_integer2(Len, <<0:1,_:7,_Bs/binary>> = Bin, RemovedBytes) -> <<Int:Len/unit:8,Buffer2/binary>> = Bin, {Int,Buffer2,RemovedBytes}; %% decoding negative integer values. decode_integer2(Len, <<1:1,B2:7,Bs/binary>>, RemovedBytes) -> <<N:Len/unit:8,Buffer2/binary>> = <<B2,Bs/binary>>, Int = N - (1 bsl (8 * Len - 1)), {Int,Buffer2,RemovedBytes}.