blob: 1bfb0a42665d47797cfd783ead79d73207a0c735 [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001%%
2%% Licensed to the Apache Software Foundation (ASF) under one
3%% or more contributor license agreements. See the NOTICE file
4%% distributed with this work for additional information
5%% regarding copyright ownership. The ASF licenses this file
6%% to you under the Apache License, Version 2.0 (the
7%% "License"); you may not use this file except in compliance
8%% with the License. You may obtain a copy of the License at
9%%
10%% http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing,
13%% software distributed under the License is distributed on an
14%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15%% KIND, either express or implied. See the License for the
16%% specific language governing permissions and limitations
17%% under the License.
18%%
19
David Reissac549552008-06-10 22:56:59 +000020-module(thrift_protocol).
21
22-export([new/2,
23 write/2,
24 read/2,
David Reiss58a961a2008-06-11 01:13:19 +000025 read/3,
David Reissac549552008-06-10 22:56:59 +000026 skip/2,
David Reiss90b40832008-06-10 22:58:52 +000027 flush_transport/1,
David Reissc11734e2008-06-11 00:59:48 +000028 close_transport/1,
David Reiss6b3e40f2008-06-11 00:59:03 +000029 typeid_to_atom/1
30 ]).
David Reissac549552008-06-10 22:56:59 +000031
David Reiss6b3e40f2008-06-11 00:59:03 +000032-export([behaviour_info/1]).
David Reissac549552008-06-10 22:56:59 +000033
34-include("thrift_constants.hrl").
35-include("thrift_protocol.hrl").
36
37-record(protocol, {module, data}).
38
39behaviour_info(callbacks) ->
40 [
41 {read, 2},
David Reiss90b40832008-06-10 22:58:52 +000042 {write, 2},
David Reissc11734e2008-06-11 00:59:48 +000043 {flush_transport, 1},
44 {close_transport, 1}
David Reissac549552008-06-10 22:56:59 +000045 ];
46behaviour_info(_Else) -> undefined.
47
David Reissac549552008-06-10 22:56:59 +000048new(Module, Data) when is_atom(Module) ->
49 {ok, #protocol{module = Module,
50 data = Data}}.
51
David Reiss90b40832008-06-10 22:58:52 +000052flush_transport(#protocol{module = Module,
53 data = Data}) ->
54 Module:flush_transport(Data).
55
David Reissc11734e2008-06-11 00:59:48 +000056close_transport(#protocol{module = Module,
57 data = Data}) ->
58 Module:close_transport(Data).
59
David Reissac549552008-06-10 22:56:59 +000060typeid_to_atom(?tType_STOP) -> field_stop;
61typeid_to_atom(?tType_VOID) -> void;
62typeid_to_atom(?tType_BOOL) -> bool;
63typeid_to_atom(?tType_BYTE) -> byte;
64typeid_to_atom(?tType_DOUBLE) -> double;
65typeid_to_atom(?tType_I16) -> i16;
66typeid_to_atom(?tType_I32) -> i32;
67typeid_to_atom(?tType_I64) -> i64;
68typeid_to_atom(?tType_STRING) -> string;
69typeid_to_atom(?tType_STRUCT) -> struct;
70typeid_to_atom(?tType_MAP) -> map;
71typeid_to_atom(?tType_SET) -> set;
72typeid_to_atom(?tType_LIST) -> list.
David Reissae756f42008-06-10 22:57:11 +000073
74term_to_typeid(void) -> ?tType_VOID;
75term_to_typeid(bool) -> ?tType_BOOL;
76term_to_typeid(byte) -> ?tType_BYTE;
77term_to_typeid(double) -> ?tType_DOUBLE;
78term_to_typeid(i16) -> ?tType_I16;
79term_to_typeid(i32) -> ?tType_I32;
80term_to_typeid(i64) -> ?tType_I64;
81term_to_typeid(string) -> ?tType_STRING;
82term_to_typeid({struct, _}) -> ?tType_STRUCT;
83term_to_typeid({map, _, _}) -> ?tType_MAP;
84term_to_typeid({set, _}) -> ?tType_SET;
85term_to_typeid({list, _}) -> ?tType_LIST.
86
David Reissae756f42008-06-10 22:57:11 +000087%% Structure is like:
88%% [{Fid, Type}, ...]
David Reiss58a961a2008-06-11 01:13:19 +000089read(IProto, {struct, Structure}, Tag)
90 when is_list(Structure), is_atom(Tag) ->
91
92 % If we want a tagged tuple, we need to offset all the tuple indices
93 % by 1 to avoid overwriting the tag.
94 Offset = if Tag =/= undefined -> 1; true -> 0 end,
David Reisseea82982008-06-10 22:58:21 +000095 IndexList = case length(Structure) of
David Reiss58a961a2008-06-11 01:13:19 +000096 N when N > 0 -> lists:seq(1 + Offset, N + Offset);
David Reisseea82982008-06-10 22:58:21 +000097 _ -> []
98 end,
99
David Reissae756f42008-06-10 22:57:11 +0000100 SWithIndices = [{Fid, {Type, Index}} ||
101 {{Fid, Type}, Index} <-
David Reisseea82982008-06-10 22:58:21 +0000102 lists:zip(Structure, IndexList)],
David Reissae756f42008-06-10 22:57:11 +0000103 % Fid -> {Type, Index}
104 SDict = dict:from_list(SWithIndices),
105
David Reissae756f42008-06-10 22:57:11 +0000106 ok = read(IProto, struct_begin),
David Reiss58a961a2008-06-11 01:13:19 +0000107 RTuple0 = erlang:make_tuple(length(Structure) + Offset, undefined),
108 RTuple1 = if Tag =/= undefined -> setelement(1, RTuple0, Tag);
109 true -> RTuple0
110 end,
David Reissae756f42008-06-10 22:57:11 +0000111
David Reiss58a961a2008-06-11 01:13:19 +0000112 RTuple2 = read_struct_loop(IProto, SDict, RTuple1),
113 {ok, RTuple2}.
David Reissae756f42008-06-10 22:57:11 +0000114
David Reiss76f0d112008-06-10 22:57:35 +0000115read(IProto, {struct, {Module, StructureName}}) when is_atom(Module),
116 is_atom(StructureName) ->
David Reiss58a961a2008-06-11 01:13:19 +0000117 read(IProto, Module:struct_info(StructureName), StructureName);
118
119read(IProto, S={struct, Structure}) when is_list(Structure) ->
120 read(IProto, S, undefined);
David Reiss76f0d112008-06-10 22:57:35 +0000121
David Reissae756f42008-06-10 22:57:11 +0000122read(IProto, {list, Type}) ->
123 #protocol_list_begin{etype = EType, size = Size} =
124 read(IProto, list_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, list_end),
128 {ok, List};
129
130read(IProto, {map, KeyType, ValType}) ->
131 #protocol_map_begin{size = Size} =
132 read(IProto, map_begin),
133
David Reiss6b3e40f2008-06-11 00:59:03 +0000134 List = [{Key, Val} || {{ok, Key}, {ok, Val}} <-
David Reissae756f42008-06-10 22:57:11 +0000135 [{read(IProto, KeyType),
David Reisseea82982008-06-10 22:58:21 +0000136 read(IProto, ValType)} || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000137 ok = read(IProto, map_end),
138 {ok, dict:from_list(List)};
139
140read(IProto, {set, Type}) ->
141 #protocol_set_begin{etype = _EType,
142 size = Size} =
143 read(IProto, set_begin),
David Reiss6b3e40f2008-06-11 00:59:03 +0000144 List = [Result || {ok, Result} <-
David Reisseea82982008-06-10 22:58:21 +0000145 [read(IProto, Type) || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000146 ok = read(IProto, set_end),
147 {ok, sets:from_list(List)};
148
David Reissac549552008-06-10 22:56:59 +0000149read(#protocol{module = Module,
150 data = ModuleData}, ProtocolType) ->
151 Module:read(ModuleData, ProtocolType).
152
David Reissa863db62008-06-11 01:13:12 +0000153read_struct_loop(IProto, SDict, RTuple) ->
David Reissae756f42008-06-10 22:57:11 +0000154 #protocol_field_begin{type = FType, id = Fid, name = Name} =
155 thrift_protocol:read(IProto, field_begin),
156 case {FType, Fid} of
157 {?tType_STOP, _} ->
David Reissa863db62008-06-11 01:13:12 +0000158 RTuple;
David Reissae756f42008-06-10 22:57:11 +0000159 _Else ->
160 case dict:find(Fid, SDict) of
161 {ok, {Type, Index}} ->
David Reiss233ace52009-03-30 20:46:47 +0000162 case term_to_typeid(Type) of
163 FType ->
164 {ok, Val} = read(IProto, Type),
165 thrift_protocol:read(IProto, field_end),
166 NewRTuple = setelement(Index, RTuple, Val),
167 read_struct_loop(IProto, SDict, NewRTuple);
168 Expected ->
169 error_logger:info_msg(
170 "Skipping field ~p with wrong type (~p != ~p)~n",
171 [Fid, FType, Expected]),
172 skip_field(FType, IProto, SDict, RTuple)
173 end;
David Reissae756f42008-06-10 22:57:11 +0000174 _Else2 ->
David Reiss233ace52009-03-30 20:46:47 +0000175 error_logger:info_msg("Skipping field ~p with unknown fid~n", [Fid]),
176 skip_field(FType, IProto, SDict, RTuple)
David Reissae756f42008-06-10 22:57:11 +0000177 end
178 end.
David Reissac549552008-06-10 22:56:59 +0000179
David Reiss233ace52009-03-30 20:46:47 +0000180skip_field(FType, IProto, SDict, RTuple) ->
181 FTypeAtom = thrift_protocol:typeid_to_atom(FType),
182 thrift_protocol:skip(IProto, FTypeAtom),
183 read(IProto, field_end),
184 read_struct_loop(IProto, SDict, RTuple).
185
David Reissac549552008-06-10 22:56:59 +0000186
187skip(Proto, struct) ->
188 ok = read(Proto, struct_begin),
189 ok = skip_struct_loop(Proto),
190 ok = read(Proto, struct_end);
191
192skip(Proto, map) ->
193 Map = read(Proto, map_begin),
194 ok = skip_map_loop(Proto, Map),
195 ok = read(Proto, map_end);
196
197skip(Proto, set) ->
198 Set = read(Proto, set_begin),
199 ok = skip_set_loop(Proto, Set),
200 ok = read(Proto, set_end);
201
202skip(Proto, list) ->
203 List = read(Proto, list_begin),
204 ok = skip_list_loop(Proto, List),
David Reiss6b3e40f2008-06-11 00:59:03 +0000205 ok = read(Proto, list_end);
David Reissac549552008-06-10 22:56:59 +0000206
207skip(Proto, Type) when is_atom(Type) ->
208 _Ignore = read(Proto, Type),
209 ok.
210
211
212skip_struct_loop(Proto) ->
213 #protocol_field_begin{type = Type} = read(Proto, field_begin),
214 case Type of
215 ?tType_STOP ->
216 ok;
217 _Else ->
218 skip(Proto, Type),
219 ok = read(Proto, field_end),
220 skip_struct_loop(Proto)
221 end.
222
223skip_map_loop(Proto, Map = #protocol_map_begin{ktype = Ktype,
224 vtype = Vtype,
225 size = Size}) ->
226 case Size of
227 N when N > 0 ->
228 skip(Proto, Ktype),
229 skip(Proto, Vtype),
230 skip_map_loop(Proto,
231 Map#protocol_map_begin{size = Size - 1});
232 0 -> ok
233 end.
234
235skip_set_loop(Proto, Map = #protocol_set_begin{etype = Etype,
236 size = Size}) ->
237 case Size of
238 N when N > 0 ->
239 skip(Proto, Etype),
240 skip_set_loop(Proto,
241 Map#protocol_set_begin{size = Size - 1});
242 0 -> ok
243 end.
244
245skip_list_loop(Proto, Map = #protocol_list_begin{etype = Etype,
246 size = Size}) ->
247 case Size of
248 N when N > 0 ->
249 skip(Proto, Etype),
250 skip_list_loop(Proto,
251 Map#protocol_list_begin{size = Size - 1});
252 0 -> ok
253 end.
David Reissae756f42008-06-10 22:57:11 +0000254
255
256%%--------------------------------------------------------------------
257%% Function: write(OProto, {Type, Data}) -> ok
David Reiss6b3e40f2008-06-11 00:59:03 +0000258%%
David Reissae756f42008-06-10 22:57:11 +0000259%% Type = {struct, StructDef} |
260%% {list, Type} |
261%% {map, KeyType, ValType} |
262%% {set, Type} |
263%% BaseType
264%%
265%% Data =
266%% tuple() -- for struct
267%% | list() -- for list
268%% | dictionary() -- for map
269%% | set() -- for set
270%% | term() -- for base types
271%%
David Reiss6b3e40f2008-06-11 00:59:03 +0000272%% Description:
David Reissae756f42008-06-10 22:57:11 +0000273%%--------------------------------------------------------------------
274write(Proto, {{struct, StructDef}, Data})
David Reiss76f0d112008-06-10 22:57:35 +0000275 when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 ->
276
277 [StructName | Elems] = tuple_to_list(Data),
278 ok = write(Proto, #protocol_struct_begin{name = StructName}),
279 ok = struct_write_loop(Proto, StructDef, Elems),
David Reissae756f42008-06-10 22:57:11 +0000280 ok = write(Proto, struct_end),
281 ok;
282
David Reiss76f0d112008-06-10 22:57:35 +0000283write(Proto, {{struct, {Module, StructureName}}, Data})
284 when is_atom(Module),
285 is_atom(StructureName),
286 element(1, Data) =:= StructureName ->
287 StructType = Module:struct_info(StructureName),
288 write(Proto, {Module:struct_info(StructureName), Data});
289
David Reissae756f42008-06-10 22:57:11 +0000290write(Proto, {{list, Type}, Data})
291 when is_list(Data) ->
292 ok = write(Proto,
293 #protocol_list_begin{
294 etype = term_to_typeid(Type),
295 size = length(Data)
296 }),
297 lists:foreach(fun(Elem) ->
298 ok = write(Proto, {Type, Elem})
299 end,
300 Data),
301 ok = write(Proto, list_end),
302 ok;
303
304write(Proto, {{map, KeyType, ValType}, Data}) ->
David Reissae756f42008-06-10 22:57:11 +0000305 ok = write(Proto,
306 #protocol_map_begin{
307 ktype = term_to_typeid(KeyType),
308 vtype = term_to_typeid(ValType),
David Reiss6b3e40f2008-06-11 00:59:03 +0000309 size = dict:size(Data)
310 }),
311 dict:fold(fun(KeyData, ValData, _Acc) ->
312 ok = write(Proto, {KeyType, KeyData}),
313 ok = write(Proto, {ValType, ValData})
314 end,
315 _AccO = ok,
316 Data),
David Reissae756f42008-06-10 22:57:11 +0000317 ok = write(Proto, map_end),
318 ok;
319
320write(Proto, {{set, Type}, Data}) ->
321 true = sets:is_set(Data),
David Reissae756f42008-06-10 22:57:11 +0000322 ok = write(Proto,
323 #protocol_set_begin{
324 etype = term_to_typeid(Type),
David Reiss6b3e40f2008-06-11 00:59:03 +0000325 size = sets:size(Data)
326 }),
327 sets:fold(fun(Elem, _Acc) ->
328 ok = write(Proto, {Type, Elem})
329 end,
330 _Acc0 = ok,
331 Data),
David Reissae756f42008-06-10 22:57:11 +0000332 ok = write(Proto, set_end),
333 ok;
334
335write(#protocol{module = Module,
336 data = ModuleData}, Data) ->
337 Module:write(ModuleData, Data).
338
David Reissae756f42008-06-10 22:57:11 +0000339struct_write_loop(Proto, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
340 case Data of
341 undefined ->
342 % null fields are skipped in response
343 skip;
344 _ ->
345 ok = write(Proto,
346 #protocol_field_begin{
347 type = term_to_typeid(Type),
348 id = Fid
349 }),
350 ok = write(Proto, {Type, Data}),
351 ok = write(Proto, field_end)
352 end,
353 struct_write_loop(Proto, RestStructDef, RestData);
354struct_write_loop(Proto, [], []) ->
355 ok = write(Proto, field_stop),
356 ok.