blob: a514d8fe955623bdb40e7d150d3dd53ea48c8daa [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}} ->
143 {ok, Val} = read(IProto, Type),
144 thrift_protocol:read(IProto, field_end),
David Reissa863db62008-06-11 01:13:12 +0000145 NewRTuple = setelement(Index, RTuple, Val),
146 read_struct_loop(IProto, SDict, NewRTuple);
David Reissae756f42008-06-10 22:57:11 +0000147 _Else2 ->
148 error_logger:info_msg("Skipping fid ~p~n", [Fid]),
149 FTypeAtom = thrift_protocol:typeid_to_atom(FType),
150 thrift_protocol:skip(IProto, FTypeAtom),
151 read(IProto, field_end),
David Reissa863db62008-06-11 01:13:12 +0000152 read_struct_loop(IProto, SDict, RTuple)
David Reissae756f42008-06-10 22:57:11 +0000153 end
154 end.
David Reissac549552008-06-10 22:56:59 +0000155
156
157skip(Proto, struct) ->
158 ok = read(Proto, struct_begin),
159 ok = skip_struct_loop(Proto),
160 ok = read(Proto, struct_end);
161
162skip(Proto, map) ->
163 Map = read(Proto, map_begin),
164 ok = skip_map_loop(Proto, Map),
165 ok = read(Proto, map_end);
166
167skip(Proto, set) ->
168 Set = read(Proto, set_begin),
169 ok = skip_set_loop(Proto, Set),
170 ok = read(Proto, set_end);
171
172skip(Proto, list) ->
173 List = read(Proto, list_begin),
174 ok = skip_list_loop(Proto, List),
David Reiss6b3e40f2008-06-11 00:59:03 +0000175 ok = read(Proto, list_end);
David Reissac549552008-06-10 22:56:59 +0000176
177skip(Proto, Type) when is_atom(Type) ->
178 _Ignore = read(Proto, Type),
179 ok.
180
181
182skip_struct_loop(Proto) ->
183 #protocol_field_begin{type = Type} = read(Proto, field_begin),
184 case Type of
185 ?tType_STOP ->
186 ok;
187 _Else ->
188 skip(Proto, Type),
189 ok = read(Proto, field_end),
190 skip_struct_loop(Proto)
191 end.
192
193skip_map_loop(Proto, Map = #protocol_map_begin{ktype = Ktype,
194 vtype = Vtype,
195 size = Size}) ->
196 case Size of
197 N when N > 0 ->
198 skip(Proto, Ktype),
199 skip(Proto, Vtype),
200 skip_map_loop(Proto,
201 Map#protocol_map_begin{size = Size - 1});
202 0 -> ok
203 end.
204
205skip_set_loop(Proto, Map = #protocol_set_begin{etype = Etype,
206 size = Size}) ->
207 case Size of
208 N when N > 0 ->
209 skip(Proto, Etype),
210 skip_set_loop(Proto,
211 Map#protocol_set_begin{size = Size - 1});
212 0 -> ok
213 end.
214
215skip_list_loop(Proto, Map = #protocol_list_begin{etype = Etype,
216 size = Size}) ->
217 case Size of
218 N when N > 0 ->
219 skip(Proto, Etype),
220 skip_list_loop(Proto,
221 Map#protocol_list_begin{size = Size - 1});
222 0 -> ok
223 end.
David Reissae756f42008-06-10 22:57:11 +0000224
225
226%%--------------------------------------------------------------------
227%% Function: write(OProto, {Type, Data}) -> ok
David Reiss6b3e40f2008-06-11 00:59:03 +0000228%%
David Reissae756f42008-06-10 22:57:11 +0000229%% Type = {struct, StructDef} |
230%% {list, Type} |
231%% {map, KeyType, ValType} |
232%% {set, Type} |
233%% BaseType
234%%
235%% Data =
236%% tuple() -- for struct
237%% | list() -- for list
238%% | dictionary() -- for map
239%% | set() -- for set
240%% | term() -- for base types
241%%
David Reiss6b3e40f2008-06-11 00:59:03 +0000242%% Description:
David Reissae756f42008-06-10 22:57:11 +0000243%%--------------------------------------------------------------------
244write(Proto, {{struct, StructDef}, Data})
David Reiss76f0d112008-06-10 22:57:35 +0000245 when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 ->
246
247 [StructName | Elems] = tuple_to_list(Data),
248 ok = write(Proto, #protocol_struct_begin{name = StructName}),
249 ok = struct_write_loop(Proto, StructDef, Elems),
David Reissae756f42008-06-10 22:57:11 +0000250 ok = write(Proto, struct_end),
251 ok;
252
David Reiss76f0d112008-06-10 22:57:35 +0000253write(Proto, {{struct, {Module, StructureName}}, Data})
254 when is_atom(Module),
255 is_atom(StructureName),
256 element(1, Data) =:= StructureName ->
257 StructType = Module:struct_info(StructureName),
258 write(Proto, {Module:struct_info(StructureName), Data});
259
David Reissae756f42008-06-10 22:57:11 +0000260write(Proto, {{list, Type}, Data})
261 when is_list(Data) ->
262 ok = write(Proto,
263 #protocol_list_begin{
264 etype = term_to_typeid(Type),
265 size = length(Data)
266 }),
267 lists:foreach(fun(Elem) ->
268 ok = write(Proto, {Type, Elem})
269 end,
270 Data),
271 ok = write(Proto, list_end),
272 ok;
273
274write(Proto, {{map, KeyType, ValType}, Data}) ->
David Reissae756f42008-06-10 22:57:11 +0000275 ok = write(Proto,
276 #protocol_map_begin{
277 ktype = term_to_typeid(KeyType),
278 vtype = term_to_typeid(ValType),
David Reiss6b3e40f2008-06-11 00:59:03 +0000279 size = dict:size(Data)
280 }),
281 dict:fold(fun(KeyData, ValData, _Acc) ->
282 ok = write(Proto, {KeyType, KeyData}),
283 ok = write(Proto, {ValType, ValData})
284 end,
285 _AccO = ok,
286 Data),
David Reissae756f42008-06-10 22:57:11 +0000287 ok = write(Proto, map_end),
288 ok;
289
290write(Proto, {{set, Type}, Data}) ->
291 true = sets:is_set(Data),
David Reissae756f42008-06-10 22:57:11 +0000292 ok = write(Proto,
293 #protocol_set_begin{
294 etype = term_to_typeid(Type),
David Reiss6b3e40f2008-06-11 00:59:03 +0000295 size = sets:size(Data)
296 }),
297 sets:fold(fun(Elem, _Acc) ->
298 ok = write(Proto, {Type, Elem})
299 end,
300 _Acc0 = ok,
301 Data),
David Reissae756f42008-06-10 22:57:11 +0000302 ok = write(Proto, set_end),
303 ok;
304
305write(#protocol{module = Module,
306 data = ModuleData}, Data) ->
307 Module:write(ModuleData, Data).
308
David Reissae756f42008-06-10 22:57:11 +0000309struct_write_loop(Proto, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
310 case Data of
311 undefined ->
312 % null fields are skipped in response
313 skip;
314 _ ->
315 ok = write(Proto,
316 #protocol_field_begin{
317 type = term_to_typeid(Type),
318 id = Fid
319 }),
320 ok = write(Proto, {Type, Data}),
321 ok = write(Proto, field_end)
322 end,
323 struct_write_loop(Proto, RestStructDef, RestData);
324struct_write_loop(Proto, [], []) ->
325 ok = write(Proto, field_stop),
326 ok.