blob: f50c612febecc965ad6588b936a319ef14d96d9d [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,
7
8 typeid_to_atom/1,
9
10 behaviour_info/1]).
11
12-include("thrift_constants.hrl").
13-include("thrift_protocol.hrl").
14
15-record(protocol, {module, data}).
16
17behaviour_info(callbacks) ->
18 [
19 {read, 2},
20 {write, 2}
21 ];
22behaviour_info(_Else) -> undefined.
23
24
25new(Module, Data) when is_atom(Module) ->
26 {ok, #protocol{module = Module,
27 data = Data}}.
28
29
David Reissac549552008-06-10 22:56:59 +000030typeid_to_atom(?tType_STOP) -> field_stop;
31typeid_to_atom(?tType_VOID) -> void;
32typeid_to_atom(?tType_BOOL) -> bool;
33typeid_to_atom(?tType_BYTE) -> byte;
34typeid_to_atom(?tType_DOUBLE) -> double;
35typeid_to_atom(?tType_I16) -> i16;
36typeid_to_atom(?tType_I32) -> i32;
37typeid_to_atom(?tType_I64) -> i64;
38typeid_to_atom(?tType_STRING) -> string;
39typeid_to_atom(?tType_STRUCT) -> struct;
40typeid_to_atom(?tType_MAP) -> map;
41typeid_to_atom(?tType_SET) -> set;
42typeid_to_atom(?tType_LIST) -> list.
43
David Reissae756f42008-06-10 22:57:11 +000044
45term_to_typeid(void) -> ?tType_VOID;
46term_to_typeid(bool) -> ?tType_BOOL;
47term_to_typeid(byte) -> ?tType_BYTE;
48term_to_typeid(double) -> ?tType_DOUBLE;
49term_to_typeid(i16) -> ?tType_I16;
50term_to_typeid(i32) -> ?tType_I32;
51term_to_typeid(i64) -> ?tType_I64;
52term_to_typeid(string) -> ?tType_STRING;
53term_to_typeid({struct, _}) -> ?tType_STRUCT;
54term_to_typeid({map, _, _}) -> ?tType_MAP;
55term_to_typeid({set, _}) -> ?tType_SET;
56term_to_typeid({list, _}) -> ?tType_LIST.
57
58
59%% Structure is like:
60%% [{Fid, Type}, ...]
61read(IProto, {struct, Structure}) when is_list(Structure) ->
David Reisseea82982008-06-10 22:58:21 +000062 IndexList = case length(Structure) of
63 N when N > 0 -> lists:seq(1, N);
64 _ -> []
65 end,
66
David Reissae756f42008-06-10 22:57:11 +000067 SWithIndices = [{Fid, {Type, Index}} ||
68 {{Fid, Type}, Index} <-
David Reisseea82982008-06-10 22:58:21 +000069 lists:zip(Structure, IndexList)],
David Reissae756f42008-06-10 22:57:11 +000070 % Fid -> {Type, Index}
71 SDict = dict:from_list(SWithIndices),
72
73
74 ok = read(IProto, struct_begin),
75 RDict = read_struct_loop(IProto, SDict, dict:new()),
76
77 List = [case dict:find(Index, RDict) of
78 {ok, Val} -> Val;
79 error -> undefined
David Reisseea82982008-06-10 22:58:21 +000080 end || Index <- IndexList],
David Reissae756f42008-06-10 22:57:11 +000081 {ok, list_to_tuple(List)};
82
David Reiss76f0d112008-06-10 22:57:35 +000083read(IProto, {struct, {Module, StructureName}}) when is_atom(Module),
84 is_atom(StructureName) ->
85 case read(IProto, Module:struct_info(StructureName)) of
86 {ok, StructureElems} ->
87 {ok, list_to_tuple([StructureName | tuple_to_list(StructureElems)])};
88 Else -> Else
89 end;
90
David Reissae756f42008-06-10 22:57:11 +000091read(IProto, {list, Type}) ->
92 #protocol_list_begin{etype = EType, size = Size} =
93 read(IProto, list_begin),
94 List = [Result || {ok, Result} <-
David Reisseea82982008-06-10 22:58:21 +000095 [read(IProto, Type) || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +000096 ok = read(IProto, list_end),
97 {ok, List};
98
99read(IProto, {map, KeyType, ValType}) ->
100 #protocol_map_begin{size = Size} =
101 read(IProto, map_begin),
102
103 List = [{Key, Val} || {{ok, Key}, {ok, Val}} <-
104 [{read(IProto, KeyType),
David Reisseea82982008-06-10 22:58:21 +0000105 read(IProto, ValType)} || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000106 ok = read(IProto, map_end),
107 {ok, dict:from_list(List)};
108
109read(IProto, {set, Type}) ->
110 #protocol_set_begin{etype = _EType,
111 size = Size} =
112 read(IProto, set_begin),
113 List = [Result || {ok, Result} <-
David Reisseea82982008-06-10 22:58:21 +0000114 [read(IProto, Type) || _X <- lists:duplicate(Size, 0)]],
David Reissae756f42008-06-10 22:57:11 +0000115 ok = read(IProto, set_end),
116 {ok, sets:from_list(List)};
117
David Reissac549552008-06-10 22:56:59 +0000118read(#protocol{module = Module,
119 data = ModuleData}, ProtocolType) ->
120 Module:read(ModuleData, ProtocolType).
121
David Reissae756f42008-06-10 22:57:11 +0000122read_struct_loop(IProto, SDict, RDict) ->
123 #protocol_field_begin{type = FType, id = Fid, name = Name} =
124 thrift_protocol:read(IProto, field_begin),
125 case {FType, Fid} of
126 {?tType_STOP, _} ->
127 RDict;
128 _Else ->
129 case dict:find(Fid, SDict) of
130 {ok, {Type, Index}} ->
131 {ok, Val} = read(IProto, Type),
132 thrift_protocol:read(IProto, field_end),
133 NewRDict = dict:store(Index, Val, RDict),
134 read_struct_loop(IProto, SDict, NewRDict);
135 _Else2 ->
136 error_logger:info_msg("Skipping fid ~p~n", [Fid]),
137 FTypeAtom = thrift_protocol:typeid_to_atom(FType),
138 thrift_protocol:skip(IProto, FTypeAtom),
139 read(IProto, field_end),
140 read_struct_loop(IProto, SDict, RDict)
141 end
142 end.
David Reissac549552008-06-10 22:56:59 +0000143
144
145skip(Proto, struct) ->
146 ok = read(Proto, struct_begin),
147 ok = skip_struct_loop(Proto),
148 ok = read(Proto, struct_end);
149
150skip(Proto, map) ->
151 Map = read(Proto, map_begin),
152 ok = skip_map_loop(Proto, Map),
153 ok = read(Proto, map_end);
154
155skip(Proto, set) ->
156 Set = read(Proto, set_begin),
157 ok = skip_set_loop(Proto, Set),
158 ok = read(Proto, set_end);
159
160skip(Proto, list) ->
161 List = read(Proto, list_begin),
162 ok = skip_list_loop(Proto, List),
163 ok = read(Proto, list_end);
164
165skip(Proto, Type) when is_atom(Type) ->
166 _Ignore = read(Proto, Type),
167 ok.
168
169
170skip_struct_loop(Proto) ->
171 #protocol_field_begin{type = Type} = read(Proto, field_begin),
172 case Type of
173 ?tType_STOP ->
174 ok;
175 _Else ->
176 skip(Proto, Type),
177 ok = read(Proto, field_end),
178 skip_struct_loop(Proto)
179 end.
180
181skip_map_loop(Proto, Map = #protocol_map_begin{ktype = Ktype,
182 vtype = Vtype,
183 size = Size}) ->
184 case Size of
185 N when N > 0 ->
186 skip(Proto, Ktype),
187 skip(Proto, Vtype),
188 skip_map_loop(Proto,
189 Map#protocol_map_begin{size = Size - 1});
190 0 -> ok
191 end.
192
193skip_set_loop(Proto, Map = #protocol_set_begin{etype = Etype,
194 size = Size}) ->
195 case Size of
196 N when N > 0 ->
197 skip(Proto, Etype),
198 skip_set_loop(Proto,
199 Map#protocol_set_begin{size = Size - 1});
200 0 -> ok
201 end.
202
203skip_list_loop(Proto, Map = #protocol_list_begin{etype = Etype,
204 size = Size}) ->
205 case Size of
206 N when N > 0 ->
207 skip(Proto, Etype),
208 skip_list_loop(Proto,
209 Map#protocol_list_begin{size = Size - 1});
210 0 -> ok
211 end.
David Reissae756f42008-06-10 22:57:11 +0000212
213
214%%--------------------------------------------------------------------
215%% Function: write(OProto, {Type, Data}) -> ok
216%%
217%% Type = {struct, StructDef} |
218%% {list, Type} |
219%% {map, KeyType, ValType} |
220%% {set, Type} |
221%% BaseType
222%%
223%% Data =
224%% tuple() -- for struct
225%% | list() -- for list
226%% | dictionary() -- for map
227%% | set() -- for set
228%% | term() -- for base types
229%%
230%% Description:
231%%--------------------------------------------------------------------
232write(Proto, {{struct, StructDef}, Data})
David Reiss76f0d112008-06-10 22:57:35 +0000233 when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 ->
234
235 [StructName | Elems] = tuple_to_list(Data),
236 ok = write(Proto, #protocol_struct_begin{name = StructName}),
237 ok = struct_write_loop(Proto, StructDef, Elems),
David Reissae756f42008-06-10 22:57:11 +0000238 ok = write(Proto, struct_end),
239 ok;
240
David Reiss76f0d112008-06-10 22:57:35 +0000241write(Proto, {{struct, {Module, StructureName}}, Data})
242 when is_atom(Module),
243 is_atom(StructureName),
244 element(1, Data) =:= StructureName ->
245 StructType = Module:struct_info(StructureName),
246 write(Proto, {Module:struct_info(StructureName), Data});
247
David Reissae756f42008-06-10 22:57:11 +0000248write(Proto, {{list, Type}, Data})
249 when is_list(Data) ->
250 ok = write(Proto,
251 #protocol_list_begin{
252 etype = term_to_typeid(Type),
253 size = length(Data)
254 }),
255 lists:foreach(fun(Elem) ->
256 ok = write(Proto, {Type, Elem})
257 end,
258 Data),
259 ok = write(Proto, list_end),
260 ok;
261
262write(Proto, {{map, KeyType, ValType}, Data}) ->
263 DataList = dict:to_list(Data),
264 ok = write(Proto,
265 #protocol_map_begin{
266 ktype = term_to_typeid(KeyType),
267 vtype = term_to_typeid(ValType),
268 size = length(DataList)
269 }),
270 lists:foreach(fun({KeyData, ValData}) ->
271 ok = write(Proto, {KeyType, KeyData}),
272 ok = write(Proto, {ValType, ValData})
273 end,
274 DataList),
275 ok = write(Proto, map_end),
276 ok;
277
278write(Proto, {{set, Type}, Data}) ->
279 true = sets:is_set(Data),
280 DataList = sets:to_list(Data),
281 ok = write(Proto,
282 #protocol_set_begin{
283 etype = term_to_typeid(Type),
284 size = length(DataList)
285 }),
286 lists:foreach(fun(Elem) ->
287 ok = write(Proto, {Type, Elem})
288 end,
289 DataList),
290 ok = write(Proto, set_end),
291 ok;
292
293write(#protocol{module = Module,
294 data = ModuleData}, Data) ->
295 Module:write(ModuleData, Data).
296
297
298struct_write_loop(Proto, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
299 case Data of
300 undefined ->
301 % null fields are skipped in response
302 skip;
303 _ ->
304 ok = write(Proto,
305 #protocol_field_begin{
306 type = term_to_typeid(Type),
307 id = Fid
308 }),
309 ok = write(Proto, {Type, Data}),
310 ok = write(Proto, field_end)
311 end,
312 struct_write_loop(Proto, RestStructDef, RestData);
313struct_write_loop(Proto, [], []) ->
314 ok = write(Proto, field_stop),
315 ok.