blob: e6045e2e14cb4f1d0b1632fff8fc4925f5940f22 [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,
David Reiss58a961a2008-06-11 01:13:19 +00006 read/3,
David Reissac549552008-06-10 22:56:59 +00007 skip/2,
David Reiss90b40832008-06-10 22:58:52 +00008 flush_transport/1,
David Reissc11734e2008-06-11 00:59:48 +00009 close_transport/1,
David Reiss6b3e40f2008-06-11 00:59:03 +000010 typeid_to_atom/1
11 ]).
David Reissac549552008-06-10 22:56:59 +000012
David Reiss6b3e40f2008-06-11 00:59:03 +000013-export([behaviour_info/1]).
David Reissac549552008-06-10 22:56:59 +000014
15-include("thrift_constants.hrl").
16-include("thrift_protocol.hrl").
17
18-record(protocol, {module, data}).
19
20behaviour_info(callbacks) ->
21 [
22 {read, 2},
David Reiss90b40832008-06-10 22:58:52 +000023 {write, 2},
David Reissc11734e2008-06-11 00:59:48 +000024 {flush_transport, 1},
25 {close_transport, 1}
David Reissac549552008-06-10 22:56:59 +000026 ];
27behaviour_info(_Else) -> undefined.
28
David Reissac549552008-06-10 22:56:59 +000029new(Module, Data) when is_atom(Module) ->
30 {ok, #protocol{module = Module,
31 data = Data}}.
32
David Reiss90b40832008-06-10 22:58:52 +000033flush_transport(#protocol{module = Module,
34 data = Data}) ->
35 Module:flush_transport(Data).
36
David Reissc11734e2008-06-11 00:59:48 +000037close_transport(#protocol{module = Module,
38 data = Data}) ->
39 Module:close_transport(Data).
40
David Reissac549552008-06-10 22:56:59 +000041typeid_to_atom(?tType_STOP) -> field_stop;
42typeid_to_atom(?tType_VOID) -> void;
43typeid_to_atom(?tType_BOOL) -> bool;
44typeid_to_atom(?tType_BYTE) -> byte;
45typeid_to_atom(?tType_DOUBLE) -> double;
46typeid_to_atom(?tType_I16) -> i16;
47typeid_to_atom(?tType_I32) -> i32;
48typeid_to_atom(?tType_I64) -> i64;
49typeid_to_atom(?tType_STRING) -> string;
50typeid_to_atom(?tType_STRUCT) -> struct;
51typeid_to_atom(?tType_MAP) -> map;
52typeid_to_atom(?tType_SET) -> set;
53typeid_to_atom(?tType_LIST) -> list.
David Reissae756f42008-06-10 22:57:11 +000054
55term_to_typeid(void) -> ?tType_VOID;
56term_to_typeid(bool) -> ?tType_BOOL;
57term_to_typeid(byte) -> ?tType_BYTE;
58term_to_typeid(double) -> ?tType_DOUBLE;
59term_to_typeid(i16) -> ?tType_I16;
60term_to_typeid(i32) -> ?tType_I32;
61term_to_typeid(i64) -> ?tType_I64;
62term_to_typeid(string) -> ?tType_STRING;
63term_to_typeid({struct, _}) -> ?tType_STRUCT;
64term_to_typeid({map, _, _}) -> ?tType_MAP;
65term_to_typeid({set, _}) -> ?tType_SET;
66term_to_typeid({list, _}) -> ?tType_LIST.
67
David Reissae756f42008-06-10 22:57:11 +000068%% Structure is like:
69%% [{Fid, Type}, ...]
David Reiss58a961a2008-06-11 01:13:19 +000070read(IProto, {struct, Structure}, Tag)
71 when is_list(Structure), is_atom(Tag) ->
72
73 % If we want a tagged tuple, we need to offset all the tuple indices
74 % by 1 to avoid overwriting the tag.
75 Offset = if Tag =/= undefined -> 1; true -> 0 end,
David Reisseea82982008-06-10 22:58:21 +000076 IndexList = case length(Structure) of
David Reiss58a961a2008-06-11 01:13:19 +000077 N when N > 0 -> lists:seq(1 + Offset, N + Offset);
David Reisseea82982008-06-10 22:58:21 +000078 _ -> []
79 end,
80
David Reissae756f42008-06-10 22:57:11 +000081 SWithIndices = [{Fid, {Type, Index}} ||
82 {{Fid, Type}, Index} <-
David Reisseea82982008-06-10 22:58:21 +000083 lists:zip(Structure, IndexList)],
David Reissae756f42008-06-10 22:57:11 +000084 % Fid -> {Type, Index}
85 SDict = dict:from_list(SWithIndices),
86
David Reissae756f42008-06-10 22:57:11 +000087 ok = read(IProto, struct_begin),
David Reiss58a961a2008-06-11 01:13:19 +000088 RTuple0 = erlang:make_tuple(length(Structure) + Offset, undefined),
89 RTuple1 = if Tag =/= undefined -> setelement(1, RTuple0, Tag);
90 true -> RTuple0
91 end,
David Reissae756f42008-06-10 22:57:11 +000092
David Reiss58a961a2008-06-11 01:13:19 +000093 RTuple2 = read_struct_loop(IProto, SDict, RTuple1),
94 {ok, RTuple2}.
David Reissae756f42008-06-10 22:57:11 +000095
David Reiss76f0d112008-06-10 22:57:35 +000096read(IProto, {struct, {Module, StructureName}}) when is_atom(Module),
97 is_atom(StructureName) ->
David Reiss58a961a2008-06-11 01:13:19 +000098 read(IProto, Module:struct_info(StructureName), StructureName);
99
100read(IProto, S={struct, Structure}) when is_list(Structure) ->
101 read(IProto, S, undefined);
David Reiss76f0d112008-06-10 22:57:35 +0000102
David Reissae756f42008-06-10 22:57:11 +0000103read(IProto, {list, Type}) ->
104 #protocol_list_begin{etype = EType, size = Size} =
105 read(IProto, list_begin),
David Reiss6b3e40f2008-06-11 00:59:03 +0000106 List = [Result || {ok, Result} <-
David Reisseea82982008-06-10 22:58:21 +0000107 [read(IProto, Type) || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000108 ok = read(IProto, list_end),
109 {ok, List};
110
111read(IProto, {map, KeyType, ValType}) ->
112 #protocol_map_begin{size = Size} =
113 read(IProto, map_begin),
114
David Reiss6b3e40f2008-06-11 00:59:03 +0000115 List = [{Key, Val} || {{ok, Key}, {ok, Val}} <-
David Reissae756f42008-06-10 22:57:11 +0000116 [{read(IProto, KeyType),
David Reisseea82982008-06-10 22:58:21 +0000117 read(IProto, ValType)} || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000118 ok = read(IProto, map_end),
119 {ok, dict:from_list(List)};
120
121read(IProto, {set, Type}) ->
122 #protocol_set_begin{etype = _EType,
123 size = Size} =
124 read(IProto, set_begin),
David Reiss6b3e40f2008-06-11 00:59:03 +0000125 List = [Result || {ok, Result} <-
David Reisseea82982008-06-10 22:58:21 +0000126 [read(IProto, Type) || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000127 ok = read(IProto, set_end),
128 {ok, sets:from_list(List)};
129
David Reissac549552008-06-10 22:56:59 +0000130read(#protocol{module = Module,
131 data = ModuleData}, ProtocolType) ->
132 Module:read(ModuleData, ProtocolType).
133
David Reissa863db62008-06-11 01:13:12 +0000134read_struct_loop(IProto, SDict, RTuple) ->
David Reissae756f42008-06-10 22:57:11 +0000135 #protocol_field_begin{type = FType, id = Fid, name = Name} =
136 thrift_protocol:read(IProto, field_begin),
137 case {FType, Fid} of
138 {?tType_STOP, _} ->
David Reissa863db62008-06-11 01:13:12 +0000139 RTuple;
David Reissae756f42008-06-10 22:57:11 +0000140 _Else ->
141 case dict:find(Fid, SDict) of
142 {ok, {Type, Index}} ->
David Reiss233ace52009-03-30 20:46:47 +0000143 case term_to_typeid(Type) of
144 FType ->
145 {ok, Val} = read(IProto, Type),
146 thrift_protocol:read(IProto, field_end),
147 NewRTuple = setelement(Index, RTuple, Val),
148 read_struct_loop(IProto, SDict, NewRTuple);
149 Expected ->
150 error_logger:info_msg(
151 "Skipping field ~p with wrong type (~p != ~p)~n",
152 [Fid, FType, Expected]),
153 skip_field(FType, IProto, SDict, RTuple)
154 end;
David Reissae756f42008-06-10 22:57:11 +0000155 _Else2 ->
David Reiss233ace52009-03-30 20:46:47 +0000156 error_logger:info_msg("Skipping field ~p with unknown fid~n", [Fid]),
157 skip_field(FType, IProto, SDict, RTuple)
David Reissae756f42008-06-10 22:57:11 +0000158 end
159 end.
David Reissac549552008-06-10 22:56:59 +0000160
David Reiss233ace52009-03-30 20:46:47 +0000161skip_field(FType, IProto, SDict, RTuple) ->
162 FTypeAtom = thrift_protocol:typeid_to_atom(FType),
163 thrift_protocol:skip(IProto, FTypeAtom),
164 read(IProto, field_end),
165 read_struct_loop(IProto, SDict, RTuple).
166
David Reissac549552008-06-10 22:56:59 +0000167
168skip(Proto, struct) ->
169 ok = read(Proto, struct_begin),
170 ok = skip_struct_loop(Proto),
171 ok = read(Proto, struct_end);
172
173skip(Proto, map) ->
174 Map = read(Proto, map_begin),
175 ok = skip_map_loop(Proto, Map),
176 ok = read(Proto, map_end);
177
178skip(Proto, set) ->
179 Set = read(Proto, set_begin),
180 ok = skip_set_loop(Proto, Set),
181 ok = read(Proto, set_end);
182
183skip(Proto, list) ->
184 List = read(Proto, list_begin),
185 ok = skip_list_loop(Proto, List),
David Reiss6b3e40f2008-06-11 00:59:03 +0000186 ok = read(Proto, list_end);
David Reissac549552008-06-10 22:56:59 +0000187
188skip(Proto, Type) when is_atom(Type) ->
189 _Ignore = read(Proto, Type),
190 ok.
191
192
193skip_struct_loop(Proto) ->
194 #protocol_field_begin{type = Type} = read(Proto, field_begin),
195 case Type of
196 ?tType_STOP ->
197 ok;
198 _Else ->
199 skip(Proto, Type),
200 ok = read(Proto, field_end),
201 skip_struct_loop(Proto)
202 end.
203
204skip_map_loop(Proto, Map = #protocol_map_begin{ktype = Ktype,
205 vtype = Vtype,
206 size = Size}) ->
207 case Size of
208 N when N > 0 ->
209 skip(Proto, Ktype),
210 skip(Proto, Vtype),
211 skip_map_loop(Proto,
212 Map#protocol_map_begin{size = Size - 1});
213 0 -> ok
214 end.
215
216skip_set_loop(Proto, Map = #protocol_set_begin{etype = Etype,
217 size = Size}) ->
218 case Size of
219 N when N > 0 ->
220 skip(Proto, Etype),
221 skip_set_loop(Proto,
222 Map#protocol_set_begin{size = Size - 1});
223 0 -> ok
224 end.
225
226skip_list_loop(Proto, Map = #protocol_list_begin{etype = Etype,
227 size = Size}) ->
228 case Size of
229 N when N > 0 ->
230 skip(Proto, Etype),
231 skip_list_loop(Proto,
232 Map#protocol_list_begin{size = Size - 1});
233 0 -> ok
234 end.
David Reissae756f42008-06-10 22:57:11 +0000235
236
237%%--------------------------------------------------------------------
238%% Function: write(OProto, {Type, Data}) -> ok
David Reiss6b3e40f2008-06-11 00:59:03 +0000239%%
David Reissae756f42008-06-10 22:57:11 +0000240%% Type = {struct, StructDef} |
241%% {list, Type} |
242%% {map, KeyType, ValType} |
243%% {set, Type} |
244%% BaseType
245%%
246%% Data =
247%% tuple() -- for struct
248%% | list() -- for list
249%% | dictionary() -- for map
250%% | set() -- for set
251%% | term() -- for base types
252%%
David Reiss6b3e40f2008-06-11 00:59:03 +0000253%% Description:
David Reissae756f42008-06-10 22:57:11 +0000254%%--------------------------------------------------------------------
255write(Proto, {{struct, StructDef}, Data})
David Reiss76f0d112008-06-10 22:57:35 +0000256 when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 ->
257
258 [StructName | Elems] = tuple_to_list(Data),
259 ok = write(Proto, #protocol_struct_begin{name = StructName}),
260 ok = struct_write_loop(Proto, StructDef, Elems),
David Reissae756f42008-06-10 22:57:11 +0000261 ok = write(Proto, struct_end),
262 ok;
263
David Reiss76f0d112008-06-10 22:57:35 +0000264write(Proto, {{struct, {Module, StructureName}}, Data})
265 when is_atom(Module),
266 is_atom(StructureName),
267 element(1, Data) =:= StructureName ->
268 StructType = Module:struct_info(StructureName),
269 write(Proto, {Module:struct_info(StructureName), Data});
270
David Reissae756f42008-06-10 22:57:11 +0000271write(Proto, {{list, Type}, Data})
272 when is_list(Data) ->
273 ok = write(Proto,
274 #protocol_list_begin{
275 etype = term_to_typeid(Type),
276 size = length(Data)
277 }),
278 lists:foreach(fun(Elem) ->
279 ok = write(Proto, {Type, Elem})
280 end,
281 Data),
282 ok = write(Proto, list_end),
283 ok;
284
285write(Proto, {{map, KeyType, ValType}, Data}) ->
David Reissae756f42008-06-10 22:57:11 +0000286 ok = write(Proto,
287 #protocol_map_begin{
288 ktype = term_to_typeid(KeyType),
289 vtype = term_to_typeid(ValType),
David Reiss6b3e40f2008-06-11 00:59:03 +0000290 size = dict:size(Data)
291 }),
292 dict:fold(fun(KeyData, ValData, _Acc) ->
293 ok = write(Proto, {KeyType, KeyData}),
294 ok = write(Proto, {ValType, ValData})
295 end,
296 _AccO = ok,
297 Data),
David Reissae756f42008-06-10 22:57:11 +0000298 ok = write(Proto, map_end),
299 ok;
300
301write(Proto, {{set, Type}, Data}) ->
302 true = sets:is_set(Data),
David Reissae756f42008-06-10 22:57:11 +0000303 ok = write(Proto,
304 #protocol_set_begin{
305 etype = term_to_typeid(Type),
David Reiss6b3e40f2008-06-11 00:59:03 +0000306 size = sets:size(Data)
307 }),
308 sets:fold(fun(Elem, _Acc) ->
309 ok = write(Proto, {Type, Elem})
310 end,
311 _Acc0 = ok,
312 Data),
David Reissae756f42008-06-10 22:57:11 +0000313 ok = write(Proto, set_end),
314 ok;
315
316write(#protocol{module = Module,
317 data = ModuleData}, Data) ->
318 Module:write(ModuleData, Data).
319
David Reissae756f42008-06-10 22:57:11 +0000320struct_write_loop(Proto, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
321 case Data of
322 undefined ->
323 % null fields are skipped in response
324 skip;
325 _ ->
326 ok = write(Proto,
327 #protocol_field_begin{
328 type = term_to_typeid(Type),
329 id = Fid
330 }),
331 ok = write(Proto, {Type, Data}),
332 ok = write(Proto, field_end)
333 end,
334 struct_write_loop(Proto, RestStructDef, RestData);
335struct_write_loop(Proto, [], []) ->
336 ok = write(Proto, field_stop),
337 ok.