blob: c94fde144fcea4634f62ad1a5bfba5251371610c [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) ->
62 SWithIndices = [{Fid, {Type, Index}} ||
63 {{Fid, Type}, Index} <-
64 lists:zip(Structure, lists:seq(1, length(Structure)))],
65 % Fid -> {Type, Index}
66 SDict = dict:from_list(SWithIndices),
67
68
69 ok = read(IProto, struct_begin),
70 RDict = read_struct_loop(IProto, SDict, dict:new()),
71
72 List = [case dict:find(Index, RDict) of
73 {ok, Val} -> Val;
74 error -> undefined
75 end || Index <- lists:seq(1, length(Structure))],
76 {ok, list_to_tuple(List)};
77
David Reiss76f0d112008-06-10 22:57:35 +000078read(IProto, {struct, {Module, StructureName}}) when is_atom(Module),
79 is_atom(StructureName) ->
80 case read(IProto, Module:struct_info(StructureName)) of
81 {ok, StructureElems} ->
82 {ok, list_to_tuple([StructureName | tuple_to_list(StructureElems)])};
83 Else -> Else
84 end;
85
David Reissae756f42008-06-10 22:57:11 +000086read(IProto, {list, Type}) ->
87 #protocol_list_begin{etype = EType, size = Size} =
88 read(IProto, list_begin),
89 List = [Result || {ok, Result} <-
90 [read(IProto, Type) || _X <- lists:seq(1, Size)]],
91 ok = read(IProto, list_end),
92 {ok, List};
93
94read(IProto, {map, KeyType, ValType}) ->
95 #protocol_map_begin{size = Size} =
96 read(IProto, map_begin),
97
98 List = [{Key, Val} || {{ok, Key}, {ok, Val}} <-
99 [{read(IProto, KeyType),
100 read(IProto, ValType)} || _X <- lists:seq(1, Size)]],
101 ok = read(IProto, map_end),
102 {ok, dict:from_list(List)};
103
104read(IProto, {set, Type}) ->
105 #protocol_set_begin{etype = _EType,
106 size = Size} =
107 read(IProto, set_begin),
108 List = [Result || {ok, Result} <-
109 [read(IProto, Type) || _X <- lists:seq(1, Size)]],
110 ok = read(IProto, set_end),
111 {ok, sets:from_list(List)};
112
David Reissac549552008-06-10 22:56:59 +0000113read(#protocol{module = Module,
114 data = ModuleData}, ProtocolType) ->
115 Module:read(ModuleData, ProtocolType).
116
David Reissae756f42008-06-10 22:57:11 +0000117read_struct_loop(IProto, SDict, RDict) ->
118 #protocol_field_begin{type = FType, id = Fid, name = Name} =
119 thrift_protocol:read(IProto, field_begin),
120 case {FType, Fid} of
121 {?tType_STOP, _} ->
122 RDict;
123 _Else ->
124 case dict:find(Fid, SDict) of
125 {ok, {Type, Index}} ->
126 {ok, Val} = read(IProto, Type),
127 thrift_protocol:read(IProto, field_end),
128 NewRDict = dict:store(Index, Val, RDict),
129 read_struct_loop(IProto, SDict, NewRDict);
130 _Else2 ->
131 error_logger:info_msg("Skipping fid ~p~n", [Fid]),
132 FTypeAtom = thrift_protocol:typeid_to_atom(FType),
133 thrift_protocol:skip(IProto, FTypeAtom),
134 read(IProto, field_end),
135 read_struct_loop(IProto, SDict, RDict)
136 end
137 end.
David Reissac549552008-06-10 22:56:59 +0000138
139
140skip(Proto, struct) ->
141 ok = read(Proto, struct_begin),
142 ok = skip_struct_loop(Proto),
143 ok = read(Proto, struct_end);
144
145skip(Proto, map) ->
146 Map = read(Proto, map_begin),
147 ok = skip_map_loop(Proto, Map),
148 ok = read(Proto, map_end);
149
150skip(Proto, set) ->
151 Set = read(Proto, set_begin),
152 ok = skip_set_loop(Proto, Set),
153 ok = read(Proto, set_end);
154
155skip(Proto, list) ->
156 List = read(Proto, list_begin),
157 ok = skip_list_loop(Proto, List),
158 ok = read(Proto, list_end);
159
160skip(Proto, Type) when is_atom(Type) ->
161 _Ignore = read(Proto, Type),
162 ok.
163
164
165skip_struct_loop(Proto) ->
166 #protocol_field_begin{type = Type} = read(Proto, field_begin),
167 case Type of
168 ?tType_STOP ->
169 ok;
170 _Else ->
171 skip(Proto, Type),
172 ok = read(Proto, field_end),
173 skip_struct_loop(Proto)
174 end.
175
176skip_map_loop(Proto, Map = #protocol_map_begin{ktype = Ktype,
177 vtype = Vtype,
178 size = Size}) ->
179 case Size of
180 N when N > 0 ->
181 skip(Proto, Ktype),
182 skip(Proto, Vtype),
183 skip_map_loop(Proto,
184 Map#protocol_map_begin{size = Size - 1});
185 0 -> ok
186 end.
187
188skip_set_loop(Proto, Map = #protocol_set_begin{etype = Etype,
189 size = Size}) ->
190 case Size of
191 N when N > 0 ->
192 skip(Proto, Etype),
193 skip_set_loop(Proto,
194 Map#protocol_set_begin{size = Size - 1});
195 0 -> ok
196 end.
197
198skip_list_loop(Proto, Map = #protocol_list_begin{etype = Etype,
199 size = Size}) ->
200 case Size of
201 N when N > 0 ->
202 skip(Proto, Etype),
203 skip_list_loop(Proto,
204 Map#protocol_list_begin{size = Size - 1});
205 0 -> ok
206 end.
David Reissae756f42008-06-10 22:57:11 +0000207
208
209%%--------------------------------------------------------------------
210%% Function: write(OProto, {Type, Data}) -> ok
211%%
212%% Type = {struct, StructDef} |
213%% {list, Type} |
214%% {map, KeyType, ValType} |
215%% {set, Type} |
216%% BaseType
217%%
218%% Data =
219%% tuple() -- for struct
220%% | list() -- for list
221%% | dictionary() -- for map
222%% | set() -- for set
223%% | term() -- for base types
224%%
225%% Description:
226%%--------------------------------------------------------------------
227write(Proto, {{struct, StructDef}, Data})
David Reiss76f0d112008-06-10 22:57:35 +0000228 when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 ->
229
230 [StructName | Elems] = tuple_to_list(Data),
231 ok = write(Proto, #protocol_struct_begin{name = StructName}),
232 ok = struct_write_loop(Proto, StructDef, Elems),
David Reissae756f42008-06-10 22:57:11 +0000233 ok = write(Proto, struct_end),
234 ok;
235
David Reiss76f0d112008-06-10 22:57:35 +0000236write(Proto, {{struct, {Module, StructureName}}, Data})
237 when is_atom(Module),
238 is_atom(StructureName),
239 element(1, Data) =:= StructureName ->
240 StructType = Module:struct_info(StructureName),
241 write(Proto, {Module:struct_info(StructureName), Data});
242
David Reissae756f42008-06-10 22:57:11 +0000243write(Proto, {{list, Type}, Data})
244 when is_list(Data) ->
245 ok = write(Proto,
246 #protocol_list_begin{
247 etype = term_to_typeid(Type),
248 size = length(Data)
249 }),
250 lists:foreach(fun(Elem) ->
251 ok = write(Proto, {Type, Elem})
252 end,
253 Data),
254 ok = write(Proto, list_end),
255 ok;
256
257write(Proto, {{map, KeyType, ValType}, Data}) ->
258 DataList = dict:to_list(Data),
259 ok = write(Proto,
260 #protocol_map_begin{
261 ktype = term_to_typeid(KeyType),
262 vtype = term_to_typeid(ValType),
263 size = length(DataList)
264 }),
265 lists:foreach(fun({KeyData, ValData}) ->
266 ok = write(Proto, {KeyType, KeyData}),
267 ok = write(Proto, {ValType, ValData})
268 end,
269 DataList),
270 ok = write(Proto, map_end),
271 ok;
272
273write(Proto, {{set, Type}, Data}) ->
274 true = sets:is_set(Data),
275 DataList = sets:to_list(Data),
276 ok = write(Proto,
277 #protocol_set_begin{
278 etype = term_to_typeid(Type),
279 size = length(DataList)
280 }),
281 lists:foreach(fun(Elem) ->
282 ok = write(Proto, {Type, Elem})
283 end,
284 DataList),
285 ok = write(Proto, set_end),
286 ok;
287
288write(#protocol{module = Module,
289 data = ModuleData}, Data) ->
290 Module:write(ModuleData, Data).
291
292
293struct_write_loop(Proto, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
294 case Data of
295 undefined ->
296 % null fields are skipped in response
297 skip;
298 _ ->
299 ok = write(Proto,
300 #protocol_field_begin{
301 type = term_to_typeid(Type),
302 id = Fid
303 }),
304 ok = write(Proto, {Type, Data}),
305 ok = write(Proto, field_end)
306 end,
307 struct_write_loop(Proto, RestStructDef, RestData);
308struct_write_loop(Proto, [], []) ->
309 ok = write(Proto, field_stop),
310 ok.