blob: f1b2bcc9be293fb4b63e59c85c5101bd67ee9980 [file] [log] [blame]
David Reissac549552008-06-10 22:56:59 +00001-module(thrift_protocol).
2
3-export([new/2,
4 write/2,
5 read/2,
6 skip/2,
David Reiss90b40832008-06-10 22:58:52 +00007 flush_transport/1,
David Reiss6b3e40f2008-06-11 00:59:03 +00008 typeid_to_atom/1
9 ]).
David Reissac549552008-06-10 22:56:59 +000010
David Reiss6b3e40f2008-06-11 00:59:03 +000011-export([behaviour_info/1]).
David Reissac549552008-06-10 22:56:59 +000012
13-include("thrift_constants.hrl").
14-include("thrift_protocol.hrl").
15
16-record(protocol, {module, data}).
17
18behaviour_info(callbacks) ->
19 [
20 {read, 2},
David Reiss90b40832008-06-10 22:58:52 +000021 {write, 2},
22 {flush_transport, 1}
David Reissac549552008-06-10 22:56:59 +000023 ];
24behaviour_info(_Else) -> undefined.
25
David Reissac549552008-06-10 22:56:59 +000026new(Module, Data) when is_atom(Module) ->
27 {ok, #protocol{module = Module,
28 data = Data}}.
29
David Reiss90b40832008-06-10 22:58:52 +000030flush_transport(#protocol{module = Module,
31 data = Data}) ->
32 Module:flush_transport(Data).
33
David Reissac549552008-06-10 22:56:59 +000034typeid_to_atom(?tType_STOP) -> field_stop;
35typeid_to_atom(?tType_VOID) -> void;
36typeid_to_atom(?tType_BOOL) -> bool;
37typeid_to_atom(?tType_BYTE) -> byte;
38typeid_to_atom(?tType_DOUBLE) -> double;
39typeid_to_atom(?tType_I16) -> i16;
40typeid_to_atom(?tType_I32) -> i32;
41typeid_to_atom(?tType_I64) -> i64;
42typeid_to_atom(?tType_STRING) -> string;
43typeid_to_atom(?tType_STRUCT) -> struct;
44typeid_to_atom(?tType_MAP) -> map;
45typeid_to_atom(?tType_SET) -> set;
46typeid_to_atom(?tType_LIST) -> list.
David Reissae756f42008-06-10 22:57:11 +000047
48term_to_typeid(void) -> ?tType_VOID;
49term_to_typeid(bool) -> ?tType_BOOL;
50term_to_typeid(byte) -> ?tType_BYTE;
51term_to_typeid(double) -> ?tType_DOUBLE;
52term_to_typeid(i16) -> ?tType_I16;
53term_to_typeid(i32) -> ?tType_I32;
54term_to_typeid(i64) -> ?tType_I64;
55term_to_typeid(string) -> ?tType_STRING;
56term_to_typeid({struct, _}) -> ?tType_STRUCT;
57term_to_typeid({map, _, _}) -> ?tType_MAP;
58term_to_typeid({set, _}) -> ?tType_SET;
59term_to_typeid({list, _}) -> ?tType_LIST.
60
David Reissae756f42008-06-10 22:57:11 +000061%% Structure is like:
62%% [{Fid, Type}, ...]
63read(IProto, {struct, Structure}) when is_list(Structure) ->
David Reisseea82982008-06-10 22:58:21 +000064 IndexList = case length(Structure) of
65 N when N > 0 -> lists:seq(1, N);
66 _ -> []
67 end,
68
David Reissae756f42008-06-10 22:57:11 +000069 SWithIndices = [{Fid, {Type, Index}} ||
70 {{Fid, Type}, Index} <-
David Reisseea82982008-06-10 22:58:21 +000071 lists:zip(Structure, IndexList)],
David Reissae756f42008-06-10 22:57:11 +000072 % Fid -> {Type, Index}
73 SDict = dict:from_list(SWithIndices),
74
75
76 ok = read(IProto, struct_begin),
77 RDict = read_struct_loop(IProto, SDict, dict:new()),
78
79 List = [case dict:find(Index, RDict) of
80 {ok, Val} -> Val;
81 error -> undefined
David Reisseea82982008-06-10 22:58:21 +000082 end || Index <- IndexList],
David Reissae756f42008-06-10 22:57:11 +000083 {ok, list_to_tuple(List)};
84
David Reiss76f0d112008-06-10 22:57:35 +000085read(IProto, {struct, {Module, StructureName}}) when is_atom(Module),
86 is_atom(StructureName) ->
87 case read(IProto, Module:struct_info(StructureName)) of
88 {ok, StructureElems} ->
89 {ok, list_to_tuple([StructureName | tuple_to_list(StructureElems)])};
90 Else -> Else
91 end;
92
David Reissae756f42008-06-10 22:57:11 +000093read(IProto, {list, Type}) ->
94 #protocol_list_begin{etype = EType, size = Size} =
95 read(IProto, list_begin),
David Reiss6b3e40f2008-06-11 00:59:03 +000096 List = [Result || {ok, Result} <-
David Reisseea82982008-06-10 22:58:21 +000097 [read(IProto, Type) || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +000098 ok = read(IProto, list_end),
99 {ok, List};
100
101read(IProto, {map, KeyType, ValType}) ->
102 #protocol_map_begin{size = Size} =
103 read(IProto, map_begin),
104
David Reiss6b3e40f2008-06-11 00:59:03 +0000105 List = [{Key, Val} || {{ok, Key}, {ok, Val}} <-
David Reissae756f42008-06-10 22:57:11 +0000106 [{read(IProto, KeyType),
David Reisseea82982008-06-10 22:58:21 +0000107 read(IProto, ValType)} || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000108 ok = read(IProto, map_end),
109 {ok, dict:from_list(List)};
110
111read(IProto, {set, Type}) ->
112 #protocol_set_begin{etype = _EType,
113 size = Size} =
114 read(IProto, set_begin),
David Reiss6b3e40f2008-06-11 00:59:03 +0000115 List = [Result || {ok, Result} <-
David Reisseea82982008-06-10 22:58:21 +0000116 [read(IProto, Type) || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000117 ok = read(IProto, set_end),
118 {ok, sets:from_list(List)};
119
David Reissac549552008-06-10 22:56:59 +0000120read(#protocol{module = Module,
121 data = ModuleData}, ProtocolType) ->
122 Module:read(ModuleData, ProtocolType).
123
David Reissae756f42008-06-10 22:57:11 +0000124read_struct_loop(IProto, SDict, RDict) ->
125 #protocol_field_begin{type = FType, id = Fid, name = Name} =
126 thrift_protocol:read(IProto, field_begin),
127 case {FType, Fid} of
128 {?tType_STOP, _} ->
129 RDict;
130 _Else ->
131 case dict:find(Fid, SDict) of
132 {ok, {Type, Index}} ->
133 {ok, Val} = read(IProto, Type),
134 thrift_protocol:read(IProto, field_end),
135 NewRDict = dict:store(Index, Val, RDict),
136 read_struct_loop(IProto, SDict, NewRDict);
137 _Else2 ->
138 error_logger:info_msg("Skipping fid ~p~n", [Fid]),
139 FTypeAtom = thrift_protocol:typeid_to_atom(FType),
140 thrift_protocol:skip(IProto, FTypeAtom),
141 read(IProto, field_end),
142 read_struct_loop(IProto, SDict, RDict)
143 end
144 end.
David Reissac549552008-06-10 22:56:59 +0000145
146
147skip(Proto, struct) ->
148 ok = read(Proto, struct_begin),
149 ok = skip_struct_loop(Proto),
150 ok = read(Proto, struct_end);
151
152skip(Proto, map) ->
153 Map = read(Proto, map_begin),
154 ok = skip_map_loop(Proto, Map),
155 ok = read(Proto, map_end);
156
157skip(Proto, set) ->
158 Set = read(Proto, set_begin),
159 ok = skip_set_loop(Proto, Set),
160 ok = read(Proto, set_end);
161
162skip(Proto, list) ->
163 List = read(Proto, list_begin),
164 ok = skip_list_loop(Proto, List),
David Reiss6b3e40f2008-06-11 00:59:03 +0000165 ok = read(Proto, list_end);
David Reissac549552008-06-10 22:56:59 +0000166
167skip(Proto, Type) when is_atom(Type) ->
168 _Ignore = read(Proto, Type),
169 ok.
170
171
172skip_struct_loop(Proto) ->
173 #protocol_field_begin{type = Type} = read(Proto, field_begin),
174 case Type of
175 ?tType_STOP ->
176 ok;
177 _Else ->
178 skip(Proto, Type),
179 ok = read(Proto, field_end),
180 skip_struct_loop(Proto)
181 end.
182
183skip_map_loop(Proto, Map = #protocol_map_begin{ktype = Ktype,
184 vtype = Vtype,
185 size = Size}) ->
186 case Size of
187 N when N > 0 ->
188 skip(Proto, Ktype),
189 skip(Proto, Vtype),
190 skip_map_loop(Proto,
191 Map#protocol_map_begin{size = Size - 1});
192 0 -> ok
193 end.
194
195skip_set_loop(Proto, Map = #protocol_set_begin{etype = Etype,
196 size = Size}) ->
197 case Size of
198 N when N > 0 ->
199 skip(Proto, Etype),
200 skip_set_loop(Proto,
201 Map#protocol_set_begin{size = Size - 1});
202 0 -> ok
203 end.
204
205skip_list_loop(Proto, Map = #protocol_list_begin{etype = Etype,
206 size = Size}) ->
207 case Size of
208 N when N > 0 ->
209 skip(Proto, Etype),
210 skip_list_loop(Proto,
211 Map#protocol_list_begin{size = Size - 1});
212 0 -> ok
213 end.
David Reissae756f42008-06-10 22:57:11 +0000214
215
216%%--------------------------------------------------------------------
217%% Function: write(OProto, {Type, Data}) -> ok
David Reiss6b3e40f2008-06-11 00:59:03 +0000218%%
David Reissae756f42008-06-10 22:57:11 +0000219%% Type = {struct, StructDef} |
220%% {list, Type} |
221%% {map, KeyType, ValType} |
222%% {set, Type} |
223%% BaseType
224%%
225%% Data =
226%% tuple() -- for struct
227%% | list() -- for list
228%% | dictionary() -- for map
229%% | set() -- for set
230%% | term() -- for base types
231%%
David Reiss6b3e40f2008-06-11 00:59:03 +0000232%% Description:
David Reissae756f42008-06-10 22:57:11 +0000233%%--------------------------------------------------------------------
234write(Proto, {{struct, StructDef}, Data})
David Reiss76f0d112008-06-10 22:57:35 +0000235 when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 ->
236
237 [StructName | Elems] = tuple_to_list(Data),
238 ok = write(Proto, #protocol_struct_begin{name = StructName}),
239 ok = struct_write_loop(Proto, StructDef, Elems),
David Reissae756f42008-06-10 22:57:11 +0000240 ok = write(Proto, struct_end),
241 ok;
242
David Reiss76f0d112008-06-10 22:57:35 +0000243write(Proto, {{struct, {Module, StructureName}}, Data})
244 when is_atom(Module),
245 is_atom(StructureName),
246 element(1, Data) =:= StructureName ->
247 StructType = Module:struct_info(StructureName),
248 write(Proto, {Module:struct_info(StructureName), Data});
249
David Reissae756f42008-06-10 22:57:11 +0000250write(Proto, {{list, Type}, Data})
251 when is_list(Data) ->
252 ok = write(Proto,
253 #protocol_list_begin{
254 etype = term_to_typeid(Type),
255 size = length(Data)
256 }),
257 lists:foreach(fun(Elem) ->
258 ok = write(Proto, {Type, Elem})
259 end,
260 Data),
261 ok = write(Proto, list_end),
262 ok;
263
264write(Proto, {{map, KeyType, ValType}, Data}) ->
David Reissae756f42008-06-10 22:57:11 +0000265 ok = write(Proto,
266 #protocol_map_begin{
267 ktype = term_to_typeid(KeyType),
268 vtype = term_to_typeid(ValType),
David Reiss6b3e40f2008-06-11 00:59:03 +0000269 size = dict:size(Data)
270 }),
271 dict:fold(fun(KeyData, ValData, _Acc) ->
272 ok = write(Proto, {KeyType, KeyData}),
273 ok = write(Proto, {ValType, ValData})
274 end,
275 _AccO = ok,
276 Data),
David Reissae756f42008-06-10 22:57:11 +0000277 ok = write(Proto, map_end),
278 ok;
279
280write(Proto, {{set, Type}, Data}) ->
281 true = sets:is_set(Data),
David Reissae756f42008-06-10 22:57:11 +0000282 ok = write(Proto,
283 #protocol_set_begin{
284 etype = term_to_typeid(Type),
David Reiss6b3e40f2008-06-11 00:59:03 +0000285 size = sets:size(Data)
286 }),
287 sets:fold(fun(Elem, _Acc) ->
288 ok = write(Proto, {Type, Elem})
289 end,
290 _Acc0 = ok,
291 Data),
David Reissae756f42008-06-10 22:57:11 +0000292 ok = write(Proto, set_end),
293 ok;
294
295write(#protocol{module = Module,
296 data = ModuleData}, Data) ->
297 Module:write(ModuleData, Data).
298
David Reissae756f42008-06-10 22:57:11 +0000299struct_write_loop(Proto, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
300 case Data of
301 undefined ->
302 % null fields are skipped in response
303 skip;
304 _ ->
305 ok = write(Proto,
306 #protocol_field_begin{
307 type = term_to_typeid(Type),
308 id = Fid
309 }),
310 ok = write(Proto, {Type, Data}),
311 ok = write(Proto, field_end)
312 end,
313 struct_write_loop(Proto, RestStructDef, RestData);
314struct_write_loop(Proto, [], []) ->
315 ok = write(Proto, field_stop),
316 ok.