blob: 7db79291605e71423bd935c65693e644e86f3268 [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 Reissac549552008-06-10 22:56:59 +00008
9 typeid_to_atom/1,
10
11 behaviour_info/1]).
12
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
26
27new(Module, Data) when is_atom(Module) ->
28 {ok, #protocol{module = Module,
29 data = Data}}.
30
31
David Reiss90b40832008-06-10 22:58:52 +000032flush_transport(#protocol{module = Module,
33 data = Data}) ->
34 Module:flush_transport(Data).
35
David Reissac549552008-06-10 22:56:59 +000036typeid_to_atom(?tType_STOP) -> field_stop;
37typeid_to_atom(?tType_VOID) -> void;
38typeid_to_atom(?tType_BOOL) -> bool;
39typeid_to_atom(?tType_BYTE) -> byte;
40typeid_to_atom(?tType_DOUBLE) -> double;
41typeid_to_atom(?tType_I16) -> i16;
42typeid_to_atom(?tType_I32) -> i32;
43typeid_to_atom(?tType_I64) -> i64;
44typeid_to_atom(?tType_STRING) -> string;
45typeid_to_atom(?tType_STRUCT) -> struct;
46typeid_to_atom(?tType_MAP) -> map;
47typeid_to_atom(?tType_SET) -> set;
48typeid_to_atom(?tType_LIST) -> list.
49
David Reissae756f42008-06-10 22:57:11 +000050
51term_to_typeid(void) -> ?tType_VOID;
52term_to_typeid(bool) -> ?tType_BOOL;
53term_to_typeid(byte) -> ?tType_BYTE;
54term_to_typeid(double) -> ?tType_DOUBLE;
55term_to_typeid(i16) -> ?tType_I16;
56term_to_typeid(i32) -> ?tType_I32;
57term_to_typeid(i64) -> ?tType_I64;
58term_to_typeid(string) -> ?tType_STRING;
59term_to_typeid({struct, _}) -> ?tType_STRUCT;
60term_to_typeid({map, _, _}) -> ?tType_MAP;
61term_to_typeid({set, _}) -> ?tType_SET;
62term_to_typeid({list, _}) -> ?tType_LIST.
63
64
65%% Structure is like:
66%% [{Fid, Type}, ...]
67read(IProto, {struct, Structure}) when is_list(Structure) ->
David Reisseea82982008-06-10 22:58:21 +000068 IndexList = case length(Structure) of
69 N when N > 0 -> lists:seq(1, N);
70 _ -> []
71 end,
72
David Reissae756f42008-06-10 22:57:11 +000073 SWithIndices = [{Fid, {Type, Index}} ||
74 {{Fid, Type}, Index} <-
David Reisseea82982008-06-10 22:58:21 +000075 lists:zip(Structure, IndexList)],
David Reissae756f42008-06-10 22:57:11 +000076 % Fid -> {Type, Index}
77 SDict = dict:from_list(SWithIndices),
78
79
80 ok = read(IProto, struct_begin),
81 RDict = read_struct_loop(IProto, SDict, dict:new()),
82
83 List = [case dict:find(Index, RDict) of
84 {ok, Val} -> Val;
85 error -> undefined
David Reisseea82982008-06-10 22:58:21 +000086 end || Index <- IndexList],
David Reissae756f42008-06-10 22:57:11 +000087 {ok, list_to_tuple(List)};
88
David Reiss76f0d112008-06-10 22:57:35 +000089read(IProto, {struct, {Module, StructureName}}) when is_atom(Module),
90 is_atom(StructureName) ->
91 case read(IProto, Module:struct_info(StructureName)) of
92 {ok, StructureElems} ->
93 {ok, list_to_tuple([StructureName | tuple_to_list(StructureElems)])};
94 Else -> Else
95 end;
96
David Reissae756f42008-06-10 22:57:11 +000097read(IProto, {list, Type}) ->
98 #protocol_list_begin{etype = EType, size = Size} =
99 read(IProto, list_begin),
100 List = [Result || {ok, Result} <-
David Reisseea82982008-06-10 22:58:21 +0000101 [read(IProto, Type) || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000102 ok = read(IProto, list_end),
103 {ok, List};
104
105read(IProto, {map, KeyType, ValType}) ->
106 #protocol_map_begin{size = Size} =
107 read(IProto, map_begin),
108
109 List = [{Key, Val} || {{ok, Key}, {ok, Val}} <-
110 [{read(IProto, KeyType),
David Reisseea82982008-06-10 22:58:21 +0000111 read(IProto, ValType)} || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000112 ok = read(IProto, map_end),
113 {ok, dict:from_list(List)};
114
115read(IProto, {set, Type}) ->
116 #protocol_set_begin{etype = _EType,
117 size = Size} =
118 read(IProto, set_begin),
119 List = [Result || {ok, Result} <-
David Reisseea82982008-06-10 22:58:21 +0000120 [read(IProto, Type) || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000121 ok = read(IProto, set_end),
122 {ok, sets:from_list(List)};
123
David Reissac549552008-06-10 22:56:59 +0000124read(#protocol{module = Module,
125 data = ModuleData}, ProtocolType) ->
126 Module:read(ModuleData, ProtocolType).
127
David Reissae756f42008-06-10 22:57:11 +0000128read_struct_loop(IProto, SDict, RDict) ->
129 #protocol_field_begin{type = FType, id = Fid, name = Name} =
130 thrift_protocol:read(IProto, field_begin),
131 case {FType, Fid} of
132 {?tType_STOP, _} ->
133 RDict;
134 _Else ->
135 case dict:find(Fid, SDict) of
136 {ok, {Type, Index}} ->
137 {ok, Val} = read(IProto, Type),
138 thrift_protocol:read(IProto, field_end),
139 NewRDict = dict:store(Index, Val, RDict),
140 read_struct_loop(IProto, SDict, NewRDict);
141 _Else2 ->
142 error_logger:info_msg("Skipping fid ~p~n", [Fid]),
143 FTypeAtom = thrift_protocol:typeid_to_atom(FType),
144 thrift_protocol:skip(IProto, FTypeAtom),
145 read(IProto, field_end),
146 read_struct_loop(IProto, SDict, RDict)
147 end
148 end.
David Reissac549552008-06-10 22:56:59 +0000149
150
151skip(Proto, struct) ->
152 ok = read(Proto, struct_begin),
153 ok = skip_struct_loop(Proto),
154 ok = read(Proto, struct_end);
155
156skip(Proto, map) ->
157 Map = read(Proto, map_begin),
158 ok = skip_map_loop(Proto, Map),
159 ok = read(Proto, map_end);
160
161skip(Proto, set) ->
162 Set = read(Proto, set_begin),
163 ok = skip_set_loop(Proto, Set),
164 ok = read(Proto, set_end);
165
166skip(Proto, list) ->
167 List = read(Proto, list_begin),
168 ok = skip_list_loop(Proto, List),
169 ok = read(Proto, list_end);
170
171skip(Proto, Type) when is_atom(Type) ->
172 _Ignore = read(Proto, Type),
173 ok.
174
175
176skip_struct_loop(Proto) ->
177 #protocol_field_begin{type = Type} = read(Proto, field_begin),
178 case Type of
179 ?tType_STOP ->
180 ok;
181 _Else ->
182 skip(Proto, Type),
183 ok = read(Proto, field_end),
184 skip_struct_loop(Proto)
185 end.
186
187skip_map_loop(Proto, Map = #protocol_map_begin{ktype = Ktype,
188 vtype = Vtype,
189 size = Size}) ->
190 case Size of
191 N when N > 0 ->
192 skip(Proto, Ktype),
193 skip(Proto, Vtype),
194 skip_map_loop(Proto,
195 Map#protocol_map_begin{size = Size - 1});
196 0 -> ok
197 end.
198
199skip_set_loop(Proto, Map = #protocol_set_begin{etype = Etype,
200 size = Size}) ->
201 case Size of
202 N when N > 0 ->
203 skip(Proto, Etype),
204 skip_set_loop(Proto,
205 Map#protocol_set_begin{size = Size - 1});
206 0 -> ok
207 end.
208
209skip_list_loop(Proto, Map = #protocol_list_begin{etype = Etype,
210 size = Size}) ->
211 case Size of
212 N when N > 0 ->
213 skip(Proto, Etype),
214 skip_list_loop(Proto,
215 Map#protocol_list_begin{size = Size - 1});
216 0 -> ok
217 end.
David Reissae756f42008-06-10 22:57:11 +0000218
219
220%%--------------------------------------------------------------------
221%% Function: write(OProto, {Type, Data}) -> ok
222%%
223%% Type = {struct, StructDef} |
224%% {list, Type} |
225%% {map, KeyType, ValType} |
226%% {set, Type} |
227%% BaseType
228%%
229%% Data =
230%% tuple() -- for struct
231%% | list() -- for list
232%% | dictionary() -- for map
233%% | set() -- for set
234%% | term() -- for base types
235%%
236%% Description:
237%%--------------------------------------------------------------------
238write(Proto, {{struct, StructDef}, Data})
David Reiss76f0d112008-06-10 22:57:35 +0000239 when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 ->
240
241 [StructName | Elems] = tuple_to_list(Data),
242 ok = write(Proto, #protocol_struct_begin{name = StructName}),
243 ok = struct_write_loop(Proto, StructDef, Elems),
David Reissae756f42008-06-10 22:57:11 +0000244 ok = write(Proto, struct_end),
245 ok;
246
David Reiss76f0d112008-06-10 22:57:35 +0000247write(Proto, {{struct, {Module, StructureName}}, Data})
248 when is_atom(Module),
249 is_atom(StructureName),
250 element(1, Data) =:= StructureName ->
251 StructType = Module:struct_info(StructureName),
252 write(Proto, {Module:struct_info(StructureName), Data});
253
David Reissae756f42008-06-10 22:57:11 +0000254write(Proto, {{list, Type}, Data})
255 when is_list(Data) ->
256 ok = write(Proto,
257 #protocol_list_begin{
258 etype = term_to_typeid(Type),
259 size = length(Data)
260 }),
261 lists:foreach(fun(Elem) ->
262 ok = write(Proto, {Type, Elem})
263 end,
264 Data),
265 ok = write(Proto, list_end),
266 ok;
267
268write(Proto, {{map, KeyType, ValType}, Data}) ->
269 DataList = dict:to_list(Data),
270 ok = write(Proto,
271 #protocol_map_begin{
272 ktype = term_to_typeid(KeyType),
273 vtype = term_to_typeid(ValType),
274 size = length(DataList)
275 }),
276 lists:foreach(fun({KeyData, ValData}) ->
277 ok = write(Proto, {KeyType, KeyData}),
278 ok = write(Proto, {ValType, ValData})
279 end,
280 DataList),
281 ok = write(Proto, map_end),
282 ok;
283
284write(Proto, {{set, Type}, Data}) ->
285 true = sets:is_set(Data),
286 DataList = sets:to_list(Data),
287 ok = write(Proto,
288 #protocol_set_begin{
289 etype = term_to_typeid(Type),
290 size = length(DataList)
291 }),
292 lists:foreach(fun(Elem) ->
293 ok = write(Proto, {Type, Elem})
294 end,
295 DataList),
296 ok = write(Proto, set_end),
297 ok;
298
299write(#protocol{module = Module,
300 data = ModuleData}, Data) ->
301 Module:write(ModuleData, Data).
302
303
304struct_write_loop(Proto, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
305 case Data of
306 undefined ->
307 % null fields are skipped in response
308 skip;
309 _ ->
310 ok = write(Proto,
311 #protocol_field_begin{
312 type = term_to_typeid(Type),
313 id = Fid
314 }),
315 ok = write(Proto, {Type, Data}),
316 ok = write(Proto, field_end)
317 end,
318 struct_write_loop(Proto, RestStructDef, RestData);
319struct_write_loop(Proto, [], []) ->
320 ok = write(Proto, field_stop),
321 ok.