blob: 30ed6c66d35d6400072492419642c21adc5a9334 [file] [log] [blame]
% ---------------------------------------------------------------------------
% Copyright (C) 2008 0x6e6562
%
% 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.
% ---------------------------------------------------------------------------
-module(cotton_hessian).
-include("hessian.hrl").
-export([encode/2, encode/3, encode/4, encode/5]).
-export([decode/2]).
-export([init/0]).
-export([get_deftype/2]).
-record(decoding_state,{type_pool = dict:new(), reference_pool = dict:new(),hash_pool = dict:new()}).
%---------------------------------------------------------------------------
% encode/2
%---------------------------------------------------------------------------
encode(undefined, _State) -> <<$N>>;
encode(null, _State) -> <<$N>>;
encode(true, _State) -> <<$T>>;
encode(false, _State) -> <<$F>>;
encode(Value, State) when is_integer(Value) -> encode(int, Value, State);
encode(Value, State) when is_atom(Value) -> encode(string, erlang:atom_to_binary(Value,utf8), State);
encode(Value, State) when is_float(Value) -> encode(double, Value, State);
encode(Value, State) when is_list(Value) -> encode(string, Value, State);
encode(Value, State) when is_binary(Value) -> encode(string, Value, State);
encode(Value, State) when is_tuple(Value) -> encode(struct, Value, State).
%---------------------------------------------------------------------------
% encode/3
%---------------------------------------------------------------------------
encode(struct, Input, State) ->
case Input of
#list{} -> encode(list, Input, State);
#map{} -> encode(map, Input, State);
#object{} -> encode(object, Input, State);
#class{} -> encode(class_store, Input, State);
{binary, Bin} -> encode(binary, Bin, State);
{date, Timestamp} -> encode(date, Timestamp, State);
{ref, Index} ->
IndexBin = encode(int, Index, State),
<<$Q, IndexBin/binary>>;
{K, V} ->
{BK,SK} = encode(K, State),
{BV,SV} = encode(V, SK),
{<<BK/binary, BV/binary>>, SV};
_Object ->
logger:debug("[encode] object ~p",[Input]),
encode(class_object, Input, State)
end;
encode(int, Int, _State) when Int >= -16, Int =< 47 ->
_Int = Int + 16#90,
<<_Int:8>>;
encode(int, Int, _State) when Int >= -2048, Int =< 2047 ->
<<B1:8,B0:8>> = <<Int:16>>,
_B1 = B1 + 16#c8,
<<_B1,B0>>;
encode(int, Int, _State) when Int >= -262144, Int =< 262143 ->
<<B2:8,B1:8,B0:8>> = <<Int:24>>,
_B2 = B2 + 16#d4,
<<_B2,B1,B0>>;
encode(int, Int, _State) when Int > -16#80000001, Int < 16#80000000 ->
<<$I,Int:32/signed>>;
encode(int, Int, State) ->
encode(long, Int, State);
encode(long, Long, _State) when Long >= -8, Long =< 15 ->
_Long = Long + 16#e0,
<<_Long:8>>;
encode(long, Long, _State) when Long >= -2048, Long =< 2047 ->
<<B1:8,B0:8>> = <<Long:16>>,
_B1 = B1 + 16#f8,
<<_B1,B0>>;
encode(long, Long, _State) when Long >= -262144, Long =< 262143 ->
<<B2:8,B1:8,B0:8>> = <<Long:24>>,
_B2 = B2 + 16#3c,
<<_B2,B1,B0>>;
encode(long, Long, _State) when Long > -16#80000001, Long < 16#80000000 ->
<<16#59,Long:32/signed>>;
encode(long, Long, _State) ->
<<$L,Long:64/signed>>;
encode(double, 0.0, _State) ->
<<16#5b>>;
encode(double, 1.0, _State) ->
<<16#5c>>;
encode(double, Double, _State) when Double >= -128.0, Double =< 127.0, Double == round(Double) ->
Byte = round(Double),
<<16#5d, Byte/signed>>;
encode(double, Double, _State) when Double >= -32768.0, Double =< 32767.0, Double == round(Double) ->
Byte = round(Double),
<<16#5e, Byte:16/signed>>;
encode(double, Double, _State) ->
case <<Double/float>> of
<<B24,B16,B8,B0,0,0,0,0>> -> <<16#5f,B24,B16,B8,B0>>;
Other -> <<$D,Other/binary>>
end;
encode(string, <<>>, _State) ->
<<0>>;
encode(string, [], _State) ->
<<0>>;
encode(string, String, State) when is_list(String)->
UTF8 = xmerl_ucs:from_utf8(String),
Length = length(UTF8),
encode(sub, Length, String, [UTF8|State]);
encode(string, String, State) when is_binary(String)->
encode(string, binary_to_list(String), State);
encode(binary, <<>>, _State) ->
<<16#20>>;
encode(binary, Value, _State) when size(Value) < 15 ->
Size = 16#20 + size(Value),
<<Size:8/unsigned,Value/binary>>;
encode(binary, Value, State) ->
encode(binary, Value, <<>>, State);
encode(date, {MegaSeconds,Seconds,MicroSeconds}, State) ->
MilliSeconds = MegaSeconds * ?MegaSeconds + Seconds * ?Seconds + MicroSeconds div ?MicroSeconds,
encode(date, MilliSeconds, State);
encode(date, MilliSeconds, _State) ->
MinuteRemain = MilliSeconds rem (?Seconds * 60),
case MinuteRemain of
0 ->
Minutes = MilliSeconds div (?Seconds * 60),
<<16#4b,Minutes:32/unsigned>>;
_ -> <<16#4a,MilliSeconds:64/unsigned>>
end;
encode(list, Input, [{set,SetInfo},_T]=State) when is_record(Input, list) ->
case find_set_info(Input#list.refNo, SetInfo) of
not_found -> encode(list, Input#list.len, Input#list.type, Input#list.values, State);
List -> encode(list, List#list.len, List#list.type, List#list.values, State)
end;
encode(list, Input, State) when is_record(Input, list) ->
encode(list, Input#list.len, Input#list.type, Input#list.values, State);
encode(list, List, State) ->
ListLength = length(List),
encode(fixedlist, ListLength, List, State);
encode(vlist, List, State) ->
case List of
[] -> <<$W,$Z>>;
_ -> encode(vlist, List, <<$W>>, State)
end;
encode(map, Input, [{set,SetInfo},_T]=State) when is_record(Input, map) ->
case find_set_info(Input#map.refNo, Input#map.type, SetInfo) of
not_found -> encode(map, Input#map.dict, State);
Map -> encode(map, Map#map.dict, State)
end;
encode(map, Input, State) when is_record(Input, map) ->
encode(map, Input#map.dict, State);
encode(map, Dict, State) ->
List = dict:to_list(Dict),
Encoder = fun({Key, Value}, {AccIn, StateIn}) ->
% Key
RK = encode(Key, StateIn),
{KeyBin, NewStateKey} = case RK of
{_, _} -> RK;
KeyB -> {KeyB, StateIn}
end,
% Value
RV = encode(Value, NewStateKey),
{ValueBin, NewStateValue} = case RV of
{_, _} -> RV;
ValueB -> {ValueB, NewStateKey}
end,
% Acc Out
{<<AccIn/binary,KeyBin/binary,ValueBin/binary>>, NewStateValue}
end,
{AccOut, NewState} = lists:foldl(Encoder, {<<$H>>, State}, List),
{<<AccOut/binary,$Z>>, NewState};
encode(class, #class{name=Name, fields=Fields}, State) ->
% type
NameBin = encode(Name, State),
% fields length
FieldsLen = length(Fields),
FieldsLenBin = encode(int, FieldsLen, State),
% fields
encode(list, Fields, <<$C,NameBin/binary,FieldsLenBin/binary>>, State);
encode(class_store, Input, [{set,SetInfo},ClassList]=State) ->
{Bin, _} = encode(class, Input, State),
{Bin, [{set,SetInfo},[Input#class{encoded=true}|ClassList]]};
encode(class_store, Input, State) ->
{Bin, _} = encode(class, Input, State),
%% {Bin, [Input#class{encoded=true}|State]};
{Bin, State};
encode(object, Input, [{set,SetInfo},T]=State) when is_record(Input, object) ->
ClassRes = class(Input#object.typeRef, Input#object.class, [], T),
{BaseBin, TypeNum, C, NewState} = case ClassRes of
{encoded, Class, TypeNo} -> {<<>>, TypeNo, Class, State};
{Class, TypeNo, NewST} ->
{Bin, NewClassST} = encode(class, Class, NewST),
{Bin, TypeNo, Class, [{set,SetInfo},NewClassST]}
end,
case find_set_info(Input#object.refNo, C#class.name, SetInfo) of
not_found -> encode(object, BaseBin, TypeNum, Input#object.values, NewState);
Obj -> encode(object, BaseBin, TypeNum, Obj#object.values, NewState)
end;
encode(object, Input, State) when is_record(Input, object) ->
ClassRes = class(Input#object.typeRef, Input#object.class, [], State),
{BaseBin, TypeNum, NewState} = case ClassRes of
{encoded, _Class, TypeNo} -> {<<>>, TypeNo, State};
{Class, TypeNo, NewST} ->
{Bin, NewClassST} = encode(class, Class, NewST),
{Bin, TypeNo, NewClassST}
end,
encode(object, BaseBin, TypeNum, Input#object.values, NewState);
encode(class_object,Input,State)->
[NativeType|Values] = tuple_to_list(Input),
{ClassEncodingBin, EncodedRef, NewState} =
case dubbo_type_encoding:visit(NativeType,State) of
{ref, Ref} ->
%% encode_object(type_information, {ref, Ref}, State);
%% todo need check
{<<>>,Ref,State};
{hash, Ref, Typedef , State0} ->
Class = typedef_to_class(Typedef,Ref),
{Bin,NewStateClass} = encode(class_store,Class,State0),
{Bin,Ref,NewStateClass}
%% encode(type_information, {hash, Ref,Typedef }, State0)
end,
encode(object, ClassEncodingBin, EncodedRef, Values, NewState);
%% {AccOut, _NewState} = lists:foldl(fun encode/2,{<<>>, NewState},Values),
%% {<<ClassEncodingBin/binary,$o,EncodedRef/binary,AccOut/binary>>, _NewState};
encode(method, Method, State) when is_atom(Method) ->
String = atom_to_list(Method),
encode(method, String, State);
encode(method, Method, _State) when is_binary(Method) ->
CamMethod = erlang_to_camel_case(Method),
Size = size(CamMethod),
<<$m,Size:16/unsigned,CamMethod/binary>>;
encode(method, String, _State) when is_list(String) ->
CamMethod = erlang_to_camel_case(String),
Size = size(CamMethod),
<<$m,Size:16/unsigned,CamMethod/binary>>;
encode(reply, ok, _State) ->
<<$H,16#02,16#00,$R,16#01,$N>>;
encode(reply, {ok, Object}, State) ->
encode(reply, Object, State);
encode(reply, {error, {Error, Reason} }, State) ->
encode(fault, Error, Reason, State);
encode(reply, Object, State) ->
case encode(Object, State) of
{Bin, _NewState} -> Bin;
Bin -> Bin
end,
<<$H,16#02,16#00,$R,Bin/binary>>.
%---------------------------------------------------------------------------
% encode/4
%---------------------------------------------------------------------------
encode(sub, Length, String, _State) when Length < 32 ->
Bin = list_to_binary(String),
<<Length:8,Bin/binary>>;
encode(sub, Length, String, _State) when Length < 256 ->
Bin = list_to_binary(String),
<<16#30,Length:8,Bin/binary>>;
encode(sub, Length, String, _State) when Length < 512 ->
Bin = list_to_binary(String),
<<16#31,(Length-256):8,Bin/binary>>;
encode(sub, Length, String, _State) when Length < 768 ->
Bin = list_to_binary(String),
<<16#32,(Length-512):8,Bin/binary>>;
encode(sub, Length, String, _State) when Length < ?CHUNK_SIZE ->
Bin = list_to_binary(String),
<<16#33,(Length-768):8,Bin/binary>>;
encode(sub, Length, String, _State) when Length =:= ?CHUNK_SIZE ->
Bin = list_to_binary(String),
<<$S,Length:16,Bin/binary>>;
encode(sub, Length, _String, [UTF8|State]) ->
encode(string, Length, UTF8, <<>>, State);
encode(binary, Value, <<>>, _State) when size(Value) =< ?CHUNK_SIZE ->
Size = size(Value),
<<$B,Size:16,Value/binary>>;
encode(binary, Value, Acc, _State) when size(Value) =< ?CHUNK_SIZE ->
Size = size(Value),
<<Acc/binary,$B,Size:16,Value/binary>>;
encode(binary, Value, <<>>, State) ->
<<Chunk:?CHUNK_SIZE/binary,Rest/binary>> = Value,
encode(binary, Rest, <<$b,?CHUNK_SIZE:16,Chunk/binary>>, State);
encode(binary, Value, Acc, State) ->
<<Chunk:?CHUNK_SIZE/binary,Rest/binary>> = Value,
encode(binary, Rest, <<Acc/binary,$b,?CHUNK_SIZE:16,Chunk/binary>>, State);
encode(list, List, Acc0, State) when is_binary(Acc0) ->
lists:foldl(fun encode_accumulate/2, {Acc0,State}, List);
encode(list, Type, List, State) ->
ListLength = length(List),
encode(list, ListLength, Type, List, State);
encode(vlist, List, Acc0, State) when is_binary(Acc0) ->
{AccOut, NewState} = lists:foldl(fun encode_accumulate/2, {Acc0,State}, List),
{<<AccOut/binary,$Z>>, NewState};
encode(vlist, Type, List, State) ->
TypeBin = encode(Type, State),
case List of
[] -> <<$U,TypeBin/binary,$Z>>;
_ -> encode(vlist, List, <<$U,TypeBin/binary>>, State)
end;
encode(fixedlist, ListLength, List, State) ->
case ListLength < 8 of
true ->
ListLengthFlag = 16#78 + ListLength,
case List of
[] -> <<ListLengthFlag:8>>;
_ -> encode(list, List, <<ListLengthFlag:8>>, State)
end;
false ->
ListLengthBin = encode(int, ListLength, State),
encode(list, List, <<$X,ListLengthBin/binary>>, State)
end;
encode(fault, _Error, _Reason, State) ->
encode(fault, <<"ServiceException">>, _Error, _Reason, State);
encode(call, Method, Args, State) ->
encode(call, Method, Args, fun encode_accumulate/2, State).
%---------------------------------------------------------------------------
% encode/5
%---------------------------------------------------------------------------
encode(string, Length, UTF8, Acc, _State) when Length =< ?CHUNK_SIZE ->
Bin = list_to_binary(xmerl_ucs:to_utf8(UTF8)),
<<Acc/binary,$S,Length:16,Bin/binary>>;
encode(string, Length, UTF8, Acc, State) ->
{Chunk, Rest} = lists:split(?CHUNK_SIZE, UTF8),
ChunkBin = list_to_binary(xmerl_ucs:to_utf8(Chunk)),
encode(string, (Length-?CHUNK_SIZE), Rest, <<Acc/binary,$R,?CHUNK_SIZE:16,ChunkBin/binary>>, State);
encode(list, -1, untyped, List, State) ->
encode(vlist, List, State);
encode(list, -1, Type, List, State) ->
encode(vlist, Type, List, State);
encode(list, Len, untyped, List, State) ->
encode(fixedlist, Len, List, State);
encode(list, Len, Type, List, State) ->
TypeBin = encode(Type, State),
if
Len < 8 ->
ListLengthFlag = 16#70 + Len,
case List of
[] -> <<ListLengthFlag:8,TypeBin/binary>>;
_ -> encode(list, List, <<ListLengthFlag:8,TypeBin/binary>>, State)
end;
true ->
ListLengthBin = encode(int, Len, State),
encode(list, List, <<$V,TypeBin/binary,ListLengthBin/binary>>, State)
end;
encode(object, BaseBin, TypeNo, Values, State) ->
IndexBin = if
TypeNo < 16 ->
logger:debug("[encode] encode object TypeNo ~p",[TypeNo]),
IndexWrap = TypeNo + 16#60,
<<IndexWrap:8>>;
true ->
IndexWrap = encode(int, TypeNo, State),
<<$O, IndexWrap/binary>>
end,
{Bin, NewState} = encode(list, Values, IndexBin, State),
{<<BaseBin/binary,Bin/binary>>, NewState};
encode(call, Method, Args, Fun, State) when is_function(Fun) ->
MethodBin = encode(string, Method, State),
ArgsCount = encode(int, erlang:length(Args), State),
{Bin, _NewState} = lists:foldl(Fun, {<<>>, State}, Args),
<<$H,2,0,$C,MethodBin/binary,ArgsCount/binary,Bin/binary>>;
encode(fault, Code, _Error, _Reason, State) ->
EncodedCode = encode(string,Code, State),
<<131,100,_L2:16/unsigned,Error/binary>> = term_to_binary(_Error),
EncodedError = encode(string,Error, State),
<<$H,16#02,16#00,$F,$H,4,"code",EncodedCode/binary,7,"message",EncodedError/binary,6,"detail",31,"Stack trace not yet implemented",$Z>>.
%---------------------------------------------------------------------------
% Utility methods
%---------------------------------------------------------------------------
erlang_to_camel_case(String) when is_binary(String) ->
AsList = binary_to_list(String),
erlang_to_camel_case(AsList);
erlang_to_camel_case(String) when is_atom(String) ->
AsList = atom_to_list(String),
erlang_to_camel_case(AsList);
erlang_to_camel_case(String) ->
AsCamel = lists:foldl(fun camelize/2,[],String),
list_to_binary(AsCamel).
camelize(Element,Acc) when Element == $_ -> [$_|Acc];
camelize(Element,[$_|Acc]) -> lists:append(Acc,[Element - 16#20]);
camelize(Element,Acc) -> lists:append(Acc,[Element]).
encode_accumulate(Value, {Acc, State}) ->
logger:debug("[encode] encode_accumulate value ~p",[Value]),
case encode(Value, State) of
{Encoded,NewState} -> {<<Acc/binary,Encoded/binary>>,NewState};
Encoded -> {<<Acc/binary,Encoded/binary>>,State}
end.
find_set_info(RefNo, SetInfo) ->
find_set_info(RefNo, untyped, SetInfo).
find_set_info(_RefNo, _Type, []) ->
not_found;
find_set_info(RefNo, Type, [Set|SetInfo]) ->
if
RefNo =:= Set#set.ref -> Set#set.value;
true ->
case equal(Type, Set#set.ref) of
true -> Set#set.value;
false -> find_set_info(RefNo, Type, SetInfo)
end
end.
type_index(Type, [CurrentType|RestTypes]) when is_record(Type, class) ->
case equal(Type#class.name, CurrentType#class.name) of
true -> length(RestTypes);
false -> type_index(Type, RestTypes)
end;
type_index(Type, [CurrentType|RestTypes]) ->
case equal(Type, CurrentType#class.name) of
true -> length(RestTypes);
false -> type_index(Type, RestTypes)
end.
get_type_no(C, RestClasses) when is_record(C, class) ->
case C#class.typeNo of
-1 -> length(RestClasses);
_ -> C#class.typeNo
end.
get_type(CListH, C, CListE) when is_record(C, class) ->
TypeNo = get_type_no(C, CListE),
case C#class.encoded of
true -> {encoded, C, TypeNo};
false -> {C, TypeNo, CListH++[C#class{encoded=true}|CListE]}
end.
class(-1, auto, [], [C|CListE]) ->
get_type([], C, CListE);
class(-1, Class, CListH, [C|CListE]) when is_record(Class, class) ->
case Class#class.name of
auto ->
case CListH of
[] -> get_type([], C, CListE);
[H|T] ->get_type([], H, T++[C|CListE])
end;
_ ->
case equal(Class#class.name, C#class.name) of
true -> get_type(CListH, C, CListE);
false -> class(-1, Class, CListH++[C], CListE)
end
end;
class(-1, Class, CListH, [C|CListE]) ->
case equal(Class, C#class.name) of
true -> get_type(CListH, C, CListE);
false -> class(-1, Class, CListH++[C], CListE)
end;
class(RefNo, _Class, CListH, [C|CListE]) ->
if
RefNo =:= length(CListE) -> get_type(CListH, C, CListE);
true -> class(RefNo, _Class, CListH++[C], CListE)
end.
equal(S1, S2) when is_binary(S1) ->
if
S1 =:= S2 -> true;
true ->
S1List = binary_to_list(S1),
if
S1List =:= S2 -> true;
true -> false
end
end;
equal(S1, S2) when is_list(S1) ->
if
S1 =:= S2 -> true;
true ->
S1Binary = list_to_binary(S1),
if
S1Binary =:= S2 -> true;
true -> false
end
end;
equal(_S1, _S2) ->
false.
typedef_to_class(Typedef,DefineNo)->
#class{typeNo=DefineNo, name=Typedef#type_def.foreign_type, fields=Typedef#type_def.fieldnames}.
%---------------------------------------------------------------------------
% Decoding
%---------------------------------------------------------------------------
decode_str(Rest, 0, R) ->
{list_to_binary(xmerl_ucs:to_utf8(lists:reverse(R))), Rest};
decode_str(<<C/utf8, Rest/binary>>, N, R) ->
decode_str(Rest, N-1, [C|R]).
% Call
decode(<<$H,?M,?m,$C,Bin/binary>>, State) ->
{Rest, Function, _State} = decode(Bin,State),
{Rest2, Count, __State} = decode(Rest,_State),
case decode(list, Rest2,Count,[], __State) of
{error, Encoded} ->
{error, Encoded};
{not_found, Hash} ->
{not_found, Hash};
{_Rest, Arguments, _NewState} ->
[Function, Arguments]
end;
% Fault
decode(<<$H,?M,?m,$F,
Rest/binary>>, State) ->
{_Rest,#map{dict=Dict},_State} = decode(Rest, State),
{ok, Message} = dict:find(<<"message">>, Dict),
{error, Message};
% Reply
decode(<<$H,?M,?m,$R,Args/binary>>, State) ->
case decode(Args,[], State) of
{<<>>, Decoded,_State} ->
TypeSet = get_type_set(_State),
case Decoded of
[Value] ->
{Value,TypeSet};
[H|T] ->
{[H|T],TypeSet}
end;
{error, Encoded} ->
{error, Encoded}
end;
% Binaries
decode(<<16#20,Rest/binary>>, State) -> {Rest, <<>>, State};
decode(<<Len:8/unsigned,Rest/binary>>, State) when Len =< 16#2f, 16#20 < Len ->
_Len = Len - 16#20,
<<Bin:_Len/binary,_Rest/binary>> = Rest,
{_Rest, Bin, State};
decode(<<$B,Len:16/unsigned,Bin:Len/binary,Rest/binary>>, State) -> {Rest, Bin, State};
decode(<<$b,Rest/binary>>,State) -> decode(<<$b,Rest/binary>>, [], State);
%% Booleans
decode(<<$T,Rest/binary>>, State) -> {Rest, true, State};
decode(<<$F,Rest/binary>>, State) -> {Rest, false, State};
%% Dates
decode(<<16#4a,Date:64/unsigned,Rest/binary>>, State) ->
MegaSecs = Date div ?MegaSeconds,
Secs = (Date - MegaSecs * ?MegaSeconds) div ?Seconds,
MicroSecs = (Date - MegaSecs * ?MegaSeconds - Secs * ?Seconds) * ?MicroSeconds,
{Rest, {MegaSecs, Secs, MicroSecs}, State};
decode(<<16#4b,Date:32/unsigned,Rest/binary>>, State) ->
MegaSecs = (Date * 60000) div ?MegaSeconds,
Secs = (Date * 60000 - MegaSecs * ?MegaSeconds) div ?Seconds,
{Rest, {MegaSecs, Secs, 0}, State};
%% Doubles
decode(<<16#5b,Rest/binary>>, State)-> {Rest, 0.0, State};
decode(<<16#5c,Rest/binary>>, State)-> {Rest, 1.0, State};
decode(<<16#5d,Int:8/signed,Rest/binary>>, State)-> {Rest, float(Int), State};
decode(<<16#5e,Int:16/signed,Rest/binary>>, State)-> {Rest, float(Int), State};
decode(<<16#5f,Int:32/signed,Rest/binary>>, State)->
<<Double:64/float>> = <<Int:32,0,0,0,0>>,
{Rest, Double, State};
decode(<<$D,Double:64/float,Rest/binary>>, State)-> {Rest, Double, State};
%% Ints
decode(<<$I,Int:32/signed,Rest/binary>>, State)-> {Rest, Int, State};
decode(<<Int:8,Rest/binary>>, State) when Int >= 16#80, Int =< 16#bf -> {Rest, Int - 16#90, State};
decode(<<B2:8,B1:8,B0:8,Rest/binary>>, State) when B2 >= 16#d0, B2 =< 16#d7 -> {Rest, ((B2 - 16#d4) bsl 16) + (B1 bsl 8) + B0, State};
decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 >= 16#c0, B1 =< 16#cf -> {Rest, ((B1 - 16#c8) bsl 8) + B0, State};
%% Longs
decode(<<$L,Long:64/signed,Rest/binary>>, State)-> {Rest, Long, State};
decode(<<16#59,Long:32/signed,Rest/binary>>, State) -> {Rest, Long, State};
decode(<<Long:8,Rest/binary>>, State) when Long >= 16#d8, Long =< 16#ef -> {Rest, Long - 16#e0, State};
decode(<<B2:8,B1:8,B0:8,Rest/binary>>, State) when B2 >= 16#38, B2 =< 16#3f -> {Rest, ((B2 - 16#3c) bsl 16) + (B1 bsl 8) + B0, State};
decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 >= 16#f0, B1 =< 16#ff -> {Rest, ((B1 - 16#f8) bsl 8) + B0, State};
%% Strings
decode(<<0,Rest/binary>>, State) -> {Rest, <<>>, State};
decode(<<Len:8,Rest/binary>>, State) when Len < 32 ->
{String, NewRest} = decode_str(Rest, Len, []),
{NewRest, String, State};
decode(<<B1:8,B0:8,Rest/binary>>, State) when B1 =< 16#33,B1 >= 16#30 ->
Len = ((B1 - 16#30) bsl 8) + B0,
{String, NewRest} = decode_str(Rest, Len, []),
{NewRest, String, State};
decode(<<$S,Len:16/unsigned,Rest/binary>>, State) ->
{String, NewRest} = decode_str(Rest, Len, []),
{NewRest, String, State};
decode(<<$R,Rest/binary>>, State) -> decode(<<$R,Rest/binary>>,[], State);
%% Nulls
decode(<<$N,Rest/binary>>, State) -> {Rest, undefined, State};
%% References
decode(<<$Q,Bin/binary>>, State)->
{Rest, IntRef, _State} = decode(Bin,State),
{Rest, {ref, IntRef}, _State};
%% Maps
decode(<<$M,Map/binary>>, State) ->
{Rest,Type,_State} = decode(Map, State),
{_Refnum,__State} = visit(#type_def{foreign_type = map},_State),
{_Rest,Dict, NewState} = decode(map, Rest, dict:new(), __State),
{_Rest,#map{refNo=_Refnum,type=Type,dict=Dict},NewState};
decode(<<$H,Map/binary>>, State) ->
{_Refnum,_State} = visit(#type_def{foreign_type = map},State),
{_Rest,Dict, NewState} = decode(map, Map, dict:new(), _State),
{_Rest,#map{refNo=_Refnum,dict=Dict},NewState};
%% Lists
% 'V' type int value* # fixed-length list
decode(<<$V,Bin/binary>>, State) ->
{Rest1,_Type,_State1} = decode(Bin,State),
{_Refnum,__State} = visit(#type_def{foreign_type = list},_State1),
{Rest2,Len,_State2} = decode(Rest1,__State),
{NewRest,List,NewState} = decode(list, Rest2, Len, [], _State2),
{NewRest,#list{refNo=_Refnum,len=Len,type=_Type,values=List},NewState};
% 'X' int value* # fixed-length untyped list
decode(<<$X,Bin/binary>>, State) ->
{Rest1,Len,_State} = decode(Bin,State),
{_Refnum,__State} = visit(#type_def{foreign_type = list},_State),
{NewRest,List,NewState} = decode(list, Rest1, Len, [], __State),
{NewRest,#list{refNo=_Refnum,len=Len,values=List},NewState};
% [x70-77] type value* # fixed-length typed list
decode(<<H:5,Len:3,Bin/binary>>, State) when H == 14 ->
{Rest1,_Type,_State} = decode(Bin,State),
{_Refnum,__State} = visit(#type_def{foreign_type = list},_State),
{NewRest,List,NewState} = decode(list, Rest1, Len, [], __State),
{NewRest,#list{refNo=_Refnum,len=Len,type=_Type,values=List},NewState};
% [x78-7f] value* # fixed-length untyped list
decode(<<H:5,Len:3,Bin/binary>>, State) when H == 15 ->
{_Refnum,_State} = visit(#type_def{foreign_type = list},State),
{NewRest,List,NewState} = decode(list, Bin, Len, [], _State),
{NewRest,#list{refNo=_Refnum,len=Len,values=List},NewState};
% 'U' type value* 'Z' # variable-length list
decode(<<$U,Bin/binary>>, State) ->
{Rest1,_Type,_State} = decode(Bin,State),
{_Refnum,__State} = visit(#type_def{foreign_type = list},_State),
{NewRest,List,NewState} = decode(list, Rest1, [], __State),
{NewRest,#list{refNo=_Refnum,type=_Type,values=List},NewState};
% 'W' value* 'Z' # variable-length untyped list
decode(<<$W,Bin/binary>>, State) ->
{_Refnum,_State} = visit(#type_def{foreign_type = list},State),
{NewRest,List,NewState} = decode(list, Bin, [], _State),
{NewRest,#list{refNo=_Refnum,values=List},NewState};
decode(<<$C,Bin/binary>>, State0) ->
{Rest1,Type,State1} = decode(Bin,State0),
{Rest, TypeDef, State} = decode(type_definition,Type,Rest1,State1),
NewState = hash_store(TypeDef,State),
decode(Rest,NewState);
decode(<<$O,Bin/binary>>, State) ->
{Rest,Ref,_State} = decode(Bin, State),
case hash_lookup(Ref, _State) of
{not_found, Ref} ->
{Rest, {not_found, Ref}, _State};
ForeignView ->
#type_def{fieldnames = ForeignFieldNames} = ForeignView,
{_Refnum,__State} = visit(ForeignView,_State),
Count = count_fields(ForeignView),
case decode(field, Rest, Count,[], __State) of
{not_found,Hash} ->
{not_found,Hash};
{_Rest,FieldValues, NewState} ->
Object = project_native_view(ForeignFieldNames,FieldValues,ForeignView),
{_Rest, #object{refNo=_Refnum, typeRef=Ref,values=Object}, NewState}
end
end;
decode(<<H:4,Ref:4,Rest/binary>>, _State) when H == 6 ->
logger:debug("decode data find ref ~p",[Ref]),
case hash_lookup(Ref, _State) of
{not_found, Ref} ->
{Rest, {not_found, Ref}, _State};
ForeignView ->
#type_def{fieldnames = ForeignFieldNames} = ForeignView,
{_Refnum,__State} = visit(ForeignView,_State),
Count = count_fields(ForeignView),
case decode(field, Rest, Count,[], __State) of
{not_found,Hash} ->
{not_found,Hash};
{_Rest,FieldValues, NewState} ->
Object = project_native_view(ForeignFieldNames,FieldValues,ForeignView),
{_Rest, #object{refNo=_Refnum, typeRef=Ref,values=Object}, NewState}
end
end;
%% Anything else
decode(<<Unexpected/binary>>, State) ->
{error, cotton_hessian:encode(fault, <<"ProtocolException">>, unexpected_byte_sequence, Unexpected, State) }.
decode(<<$b,Len:16/unsigned,Bin:Len/binary,$b,Rest/binary>>, Acc, State) ->
decode(<<$b,Rest/binary>>,Acc ++ [Bin], State);
decode(<<$b,Len:16/unsigned,Bin:Len/binary,$B,Rest/binary>>, Acc, State) ->
_Acc = Acc ++ [Bin],
{_Rest,_Bin, State} = decode(<<$B,Rest/binary>>, State),
{_Rest, list_to_binary(_Acc ++ [_Bin]), State};
decode(<<$R,Len:16/unsigned,Rest/binary>>,Acc, State) ->
{_String, NewRest} = decode_str(Rest, Len, []),
<<H:8,_/binary>> = NewRest,
case H of
$R -> decode(NewRest,list_to_binary([Acc|[_String]]), State);
$S -> {_Rest,_Bin, _State} = decode(NewRest, State),
_Acc2 =[Acc |[_String]],
{_Rest, list_to_binary(_Acc2 ++ [_Bin]), _State}
end;
decode(<<>>, List, State) -> {<<>>, List, State};
decode(<<$Z>>, List, State) -> {<<>>, List, State};
decode(Args, List, State) ->
case decode(Args,State) of
{Rest,{ref, Ref}, _State} ->
decode(Rest, List ++ [{ref, Ref}] , _State);
{Rest, [H|T], _State} ->
decode(Rest, List ++ [H|T], _State);
{Rest, Result, _State} ->
decode(Rest, List ++ [Result], _State);
{error, Encoded} ->
{error, Encoded}
end.
decode(map, <<$Z>>, Dict, State) -> {<<>>,Dict, State};
decode(map, <<$Z,Rest/binary>>, Dict, State) -> {Rest,Dict, State};
decode(map, Bin, Dict, State) ->
{_Rest, Key, _State} = decode(Bin, State),
case decode(_Rest, _State) of
{Rest, {ref, Ref}, __State} ->
%Value = lists:nth(Ref + 1, List),
Value = Ref,
decode(map, Rest, dict:store(Key, {ref, Value}, Dict), __State);
{Rest, Value, __State} ->
decode(map, Rest, dict:store(Key, Value, Dict), __State)
end;
decode(list, <<>>, List, State) -> {<<>>,lists:reverse(List), State};
decode(list, <<$Z>>, List, State) -> {<<>>,lists:reverse(List), State};
decode(list, <<$Z,Rest/binary>>, List, State) -> {Rest, lists:reverse(List), State};
decode(list, Bin, List, State) ->
case decode(Bin, State) of
{error, Encoded} ->
{error, Encoded};
{not_found,Hash} ->
{not_found, Hash};
{_Rest, {not_found, Hash}, _State} ->
{not_found, Hash};
{Rest, Value, _State} ->
decode(list, Rest, [Value|List], _State)
end;
decode(type_definition, ForeignType, Bin, State) ->
{Rest,Count, _State} = decode(Bin, State),
{NewRest,FieldNames, NewState} = decode(field, Rest, Count, [], _State),
{TypeDef,NewState2} = build_foreign_view(ForeignType,FieldNames,NewState),
{NewRest, TypeDef, NewState2}.
decode(list, Bin, 0, List, State) -> {Bin, lists:reverse(List), State};
decode(list, Bin, Len, List, State) ->
case decode(Bin, State) of
{error, Encoded} ->
{error, Encoded};
{not_found,Hash} ->
{not_found, Hash};
{_Rest, {not_found, Hash}, _State} ->
{not_found, Hash};
{Rest, Value, _State} ->
decode(list, Rest, Len - 1, [Value|List], _State)
end;
decode(field, Rest, 0, Acc, State) -> {Rest, Acc, State};
decode(field, <<$Z,Rest/binary>>, _Count, Acc, State) -> {Rest, Acc, State};
decode(field, Bin, Count, Acc, State) ->
{Rest,Field, _State} = decode(Bin, State),
case Field of
{not_found,Hash} ->
{not_found,Hash};
_ ->
decode(field, Rest, Count - 1, Acc ++ [Field], _State)
end.
get_type_set(#decoding_state{type_pool = Pool}) ->
case dict:to_list(Pool) of
[] -> [];
Data -> [Value || {_Key,Value} <- Data]
end.
build_foreign_view(ForeignType,FieldNames,State) ->
logger:debug("[DECODE] build_foreign_view ForeignType ~p FieldNames ~p",[ForeignType,FieldNames]),
ForeignView = FieldNames,
#decoding_state{type_pool = OldPool} = State,
Native = dict:size(OldPool),
NewPool = dict:store(Native,
#type_def{
defineNo = Native,
%% native_type = Native,
foreign_type = ForeignType,
fieldnames = ForeignView},
OldPool),
{
#type_def{defineNo = Native,
%% native_type = Native,
foreign_type = ForeignType,
fieldnames = ForeignView},
State#decoding_state{type_pool = NewPool}
}.
project_native_view(ForeignView,ForeignData,
#type_def{native_type = _NativeType, foreign_type = _ForeignType, fieldnames = NativeView}) ->
AsDict = dict:from_list(lists:zip(ForeignView,ForeignData)),
NativeData = [dict:fetch(Key,AsDict) || Key <- NativeView],
NativeData.
visit(TypeDef, State = #decoding_state{reference_pool = OldPool}) ->
logger:debug("[DECODE] visit typedef ~p",[TypeDef]),
Size = dict:size(OldPool),
NewPool = dict:store(Size, TypeDef, OldPool),
{Size,State#decoding_state{reference_pool = NewPool}}.
hash_lookup(Hash,#decoding_state{hash_pool = HashPool} = State) ->
case dict:find(Hash,HashPool) of
error->
{not_found, Hash};
{ok,TypeDef}->
TypeDef
end.
hash_store(TypeDef = #type_def{defineNo = Hash}, #decoding_state{hash_pool = HashPool} = State) ->
%% init(false),
%% ets:insert(hashes,{Hash,TypeDef}),
logger:debug("[DECODE] hash store typedef ~p",[TypeDef]),
NewPool = dict:store(Hash,TypeDef,HashPool),
State#decoding_state{hash_pool = NewPool}.
init() -> init(true).
init(Delete) when is_boolean(Delete) ->
%% case ets:info(hashes) of
%% undefined ->
%% ets:new(hashes,[private,named_table]);
%% _ ->
%% if
%% Delete ->
%% ets:delete(hashes),
%% ets:new(hashes,[private,named_table]);
%% true ->
%% ok
%% end
%% end,
#decoding_state{}.
count_fields(#type_def{fieldnames = FieldNames}) -> length(FieldNames).
%% extend func
get_deftype(Ref,#decoding_state{type_pool = Pool})->
dict:fetch(Ref,Pool).