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