blob: c75712ad7d85305983d9289741990171f1f9af9a [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
Sergei Elin45764092022-09-23 23:21:31 +030022-export([
23 new/2,
24 write/2,
25 read/2,
26 read/3,
27 skip/2,
28 flush_transport/1,
29 close_transport/1,
30 typeid_to_atom/1
31]).
David Reissac549552008-06-10 22:56:59 +000032
33-include("thrift_constants.hrl").
34-include("thrift_protocol.hrl").
35
Sergei Elin45764092022-09-23 23:21:31 +030036-record(protocol, {
37 module :: module(),
38 data :: term()
39}).
David Reissac549552008-06-10 22:56:59 +000040
Sergei Elin45764092022-09-23 23:21:31 +030041%%%=========================================================================
42%%% API
43%%%=========================================================================
44-type state() :: term().
45-export_type([state/0]).
46-type reason() :: term().
47-export_type([reason/0]).
48
49%% NOTE: keep this in sync with read/2 spec
50-callback read
51 (state(), {struct, _Info}) -> {state(), {ok, tuple()} | {error, reason()}};
52 (state(), tprot_cont_tag()) -> {state(), {ok, any()} | {error, reason()}};
53 (state(), tprot_empty_tag()) -> {state(), ok | {error, reason()}};
54 (state(), tprot_header_tag()) -> {state(), tprot_header_val() | {error, reason()}};
55 (state(), tprot_data_tag()) -> {state(), {ok, any()} | {error, reason()}}.
56
57-callback write(state(), any()) -> {state(), ok | {error, reason()}}.
58
59-callback flush_transport(state()) -> {state(), ok | {error, reason()}}.
60-callback close_transport(state()) -> {state(), ok | {error, reason()}}.
David Reissac549552008-06-10 22:56:59 +000061
David Reissac549552008-06-10 22:56:59 +000062new(Module, Data) when is_atom(Module) ->
Sergei Elin45764092022-09-23 23:21:31 +030063 {ok, #protocol{
64 module = Module,
65 data = Data
66 }}.
David Reissac549552008-06-10 22:56:59 +000067
David Reissc4657992010-08-30 22:05:31 +000068-spec flush_transport(#protocol{}) -> {#protocol{}, ok}.
Sergei Elin45764092022-09-23 23:21:31 +030069flush_transport(
70 Proto = #protocol{
71 module = Module,
72 data = Data
73 }
74) ->
David Reissc4657992010-08-30 22:05:31 +000075 {NewData, Result} = Module:flush_transport(Data),
76 {Proto#protocol{data = NewData}, Result}.
David Reiss90b40832008-06-10 22:58:52 +000077
David Reiss5e6637b2010-08-30 22:05:18 +000078-spec close_transport(#protocol{}) -> ok.
Sergei Elin45764092022-09-23 23:21:31 +030079close_transport(#protocol{
80 module = Module,
81 data = Data
82}) ->
David Reissc11734e2008-06-11 00:59:48 +000083 Module:close_transport(Data).
84
David Reissac549552008-06-10 22:56:59 +000085typeid_to_atom(?tType_STOP) -> field_stop;
86typeid_to_atom(?tType_VOID) -> void;
87typeid_to_atom(?tType_BOOL) -> bool;
David Reissac549552008-06-10 22:56:59 +000088typeid_to_atom(?tType_DOUBLE) -> double;
Jens Geyer40c28d32015-10-20 23:13:02 +020089typeid_to_atom(?tType_I8) -> byte;
David Reissac549552008-06-10 22:56:59 +000090typeid_to_atom(?tType_I16) -> i16;
91typeid_to_atom(?tType_I32) -> i32;
92typeid_to_atom(?tType_I64) -> i64;
93typeid_to_atom(?tType_STRING) -> string;
94typeid_to_atom(?tType_STRUCT) -> struct;
95typeid_to_atom(?tType_MAP) -> map;
96typeid_to_atom(?tType_SET) -> set;
97typeid_to_atom(?tType_LIST) -> list.
David Reissae756f42008-06-10 22:57:11 +000098
99term_to_typeid(void) -> ?tType_VOID;
100term_to_typeid(bool) -> ?tType_BOOL;
Jens Geyer40c28d32015-10-20 23:13:02 +0200101term_to_typeid(byte) -> ?tType_I8;
David Reissae756f42008-06-10 22:57:11 +0000102term_to_typeid(double) -> ?tType_DOUBLE;
Jens Geyer40c28d32015-10-20 23:13:02 +0200103term_to_typeid(i8) -> ?tType_I8;
David Reissae756f42008-06-10 22:57:11 +0000104term_to_typeid(i16) -> ?tType_I16;
105term_to_typeid(i32) -> ?tType_I32;
106term_to_typeid(i64) -> ?tType_I64;
107term_to_typeid(string) -> ?tType_STRING;
108term_to_typeid({struct, _}) -> ?tType_STRUCT;
109term_to_typeid({map, _, _}) -> ?tType_MAP;
110term_to_typeid({set, _}) -> ?tType_SET;
111term_to_typeid({list, _}) -> ?tType_LIST.
112
David Reissae756f42008-06-10 22:57:11 +0000113%% Structure is like:
114%% [{Fid, Type}, ...]
David Reiss6c187532010-08-30 22:05:33 +0000115-spec read(#protocol{}, {struct, _StructDef}, atom()) -> {#protocol{}, {ok, tuple()}}.
Sergei Elin45764092022-09-23 23:21:31 +0300116read(IProto0, {struct, Structure}, Tag) when
117 is_list(Structure), is_atom(Tag)
118->
David Reiss58a961a2008-06-11 01:13:19 +0000119 % If we want a tagged tuple, we need to offset all the tuple indices
120 % by 1 to avoid overwriting the tag.
Sergei Elin45764092022-09-23 23:21:31 +0300121 Offset =
122 if
123 Tag =/= undefined -> 1;
124 true -> 0
125 end,
126 IndexList =
127 case length(Structure) of
128 N when N > 0 -> lists:seq(1 + Offset, N + Offset);
129 _ -> []
130 end,
David Reisseea82982008-06-10 22:58:21 +0000131
Sergei Elin45764092022-09-23 23:21:31 +0300132 SWithIndices = [
133 {Fid, {Type, Index}}
134 || {{Fid, Type}, Index} <-
135 lists:zip(Structure, IndexList)
136 ],
David Reissae756f42008-06-10 22:57:11 +0000137 % Fid -> {Type, Index}
138 SDict = dict:from_list(SWithIndices),
139
David Reiss6c187532010-08-30 22:05:33 +0000140 {IProto1, ok} = read(IProto0, struct_begin),
David Reiss58a961a2008-06-11 01:13:19 +0000141 RTuple0 = erlang:make_tuple(length(Structure) + Offset, undefined),
Sergei Elin45764092022-09-23 23:21:31 +0300142 RTuple1 =
143 if
144 Tag =/= undefined -> setelement(1, RTuple0, Tag);
145 true -> RTuple0
146 end,
David Reissae756f42008-06-10 22:57:11 +0000147
David Reiss6c187532010-08-30 22:05:33 +0000148 {IProto2, RTuple2} = read_struct_loop(IProto1, SDict, RTuple1),
149 {IProto2, {ok, RTuple2}}.
David Reissae756f42008-06-10 22:57:11 +0000150
Sergei Elin45764092022-09-23 23:21:31 +0300151%% NOTE: Keep this in sync with read callback
David Reiss1cb979b2010-08-30 22:05:25 +0000152-spec read
Sergei Elin45764092022-09-23 23:21:31 +0300153 (#protocol{}, {struct, _Info}) -> {#protocol{}, {ok, tuple()} | {error, _Reason}};
154 (#protocol{}, tprot_cont_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}};
155 (#protocol{}, tprot_empty_tag()) -> {#protocol{}, ok | {error, _Reason}};
156 (#protocol{}, tprot_header_tag()) -> {#protocol{}, tprot_header_val() | {error, _Reason}};
157 (#protocol{}, tprot_data_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}}.
David Reiss5e6637b2010-08-30 22:05:18 +0000158
Sergei Elin45764092022-09-23 23:21:31 +0300159read(IProto, {struct, {Module, StructureName}}) when
160 is_atom(Module),
161 is_atom(StructureName)
162->
David Reiss58a961a2008-06-11 01:13:19 +0000163 read(IProto, Module:struct_info(StructureName), StructureName);
Sergei Elin45764092022-09-23 23:21:31 +0300164read(IProto, S = {struct, Structure}) when is_list(Structure) ->
David Reiss58a961a2008-06-11 01:13:19 +0000165 read(IProto, S, undefined);
David Reiss6c187532010-08-30 22:05:33 +0000166read(IProto0, {list, Type}) ->
167 {IProto1, #protocol_list_begin{etype = EType, size = Size}} =
168 read(IProto0, list_begin),
David Reissb4ab0082010-08-30 22:05:58 +0000169 {EType, EType} = {term_to_typeid(Type), EType},
Sergei Elin45764092022-09-23 23:21:31 +0300170 {List, IProto2} = lists:mapfoldl(
171 fun(_, ProtoS0) ->
172 {ProtoS1, {ok, Item}} = read(ProtoS0, Type),
173 {Item, ProtoS1}
174 end,
175 IProto1,
176 lists:duplicate(Size, 0)
177 ),
David Reiss6c187532010-08-30 22:05:33 +0000178 {IProto3, ok} = read(IProto2, list_end),
179 {IProto3, {ok, List}};
David Reiss6c187532010-08-30 22:05:33 +0000180read(IProto0, {map, KeyType, ValType}) ->
David Reissb4ab0082010-08-30 22:05:58 +0000181 {IProto1, #protocol_map_begin{size = Size, ktype = KType, vtype = VType}} =
David Reiss6c187532010-08-30 22:05:33 +0000182 read(IProto0, map_begin),
Sergei Elin45764092022-09-23 23:21:31 +0300183 _ =
184 case Size of
185 0 ->
186 0;
187 _ ->
188 {KType, KType} = {term_to_typeid(KeyType), KType},
189 {VType, VType} = {term_to_typeid(ValType), VType}
190 end,
191 {List, IProto2} = lists:mapfoldl(
192 fun(_, ProtoS0) ->
193 {ProtoS1, {ok, Key}} = read(ProtoS0, KeyType),
194 {ProtoS2, {ok, Val}} = read(ProtoS1, ValType),
195 {{Key, Val}, ProtoS2}
196 end,
197 IProto1,
198 lists:duplicate(Size, 0)
199 ),
David Reiss6c187532010-08-30 22:05:33 +0000200 {IProto3, ok} = read(IProto2, map_end),
201 {IProto3, {ok, dict:from_list(List)}};
David Reiss6c187532010-08-30 22:05:33 +0000202read(IProto0, {set, Type}) ->
203 {IProto1, #protocol_set_begin{etype = EType, size = Size}} =
204 read(IProto0, set_begin),
David Reissb4ab0082010-08-30 22:05:58 +0000205 {EType, EType} = {term_to_typeid(Type), EType},
Sergei Elin45764092022-09-23 23:21:31 +0300206 {List, IProto2} = lists:mapfoldl(
207 fun(_, ProtoS0) ->
208 {ProtoS1, {ok, Item}} = read(ProtoS0, Type),
209 {Item, ProtoS1}
210 end,
211 IProto1,
212 lists:duplicate(Size, 0)
213 ),
David Reiss6c187532010-08-30 22:05:33 +0000214 {IProto3, ok} = read(IProto2, set_end),
215 {IProto3, {ok, sets:from_list(List)}};
David Reiss480d5ab2010-08-30 22:05:26 +0000216read(Protocol, ProtocolType) ->
217 read_specific(Protocol, ProtocolType).
218
Sergei Elin45764092022-09-23 23:21:31 +0300219%% NOTE: Keep this in sync with read/2 spec
David Reiss480d5ab2010-08-30 22:05:26 +0000220-spec read_specific
Sergei Elin45764092022-09-23 23:21:31 +0300221 (#protocol{}, {struct, _Info}) -> {#protocol{}, {ok, tuple()} | {error, _Reason}};
222 (#protocol{}, tprot_cont_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}};
223 (#protocol{}, tprot_empty_tag()) -> {#protocol{}, ok | {error, _Reason}};
224 (#protocol{}, tprot_header_tag()) -> {#protocol{}, tprot_header_val() | {error, _Reason}};
225 (#protocol{}, tprot_data_tag()) -> {#protocol{}, {ok, any()} | {error, _Reason}}.
226read_specific(
227 Proto = #protocol{
228 module = Module,
229 data = ModuleData
230 },
231 ProtocolType
232) ->
David Reiss6c187532010-08-30 22:05:33 +0000233 {NewData, Result} = Module:read(ModuleData, ProtocolType),
234 {Proto#protocol{data = NewData}, Result}.
David Reissac549552008-06-10 22:56:59 +0000235
David Reiss6c187532010-08-30 22:05:33 +0000236read_struct_loop(IProto0, SDict, RTuple) ->
David Reiss5ed313d2010-08-30 22:05:57 +0000237 {IProto1, #protocol_field_begin{type = FType, id = Fid}} =
David Reiss6c187532010-08-30 22:05:33 +0000238 thrift_protocol:read(IProto0, field_begin),
David Reissae756f42008-06-10 22:57:11 +0000239 case {FType, Fid} of
240 {?tType_STOP, _} ->
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900241 {IProto2, ok} = read(IProto1, struct_end),
242 {IProto2, RTuple};
David Reissae756f42008-06-10 22:57:11 +0000243 _Else ->
244 case dict:find(Fid, SDict) of
245 {ok, {Type, Index}} ->
David Reiss233ace52009-03-30 20:46:47 +0000246 case term_to_typeid(Type) of
247 FType ->
David Reiss6c187532010-08-30 22:05:33 +0000248 {IProto2, {ok, Val}} = read(IProto1, Type),
249 {IProto3, ok} = thrift_protocol:read(IProto2, field_end),
David Reiss233ace52009-03-30 20:46:47 +0000250 NewRTuple = setelement(Index, RTuple, Val),
David Reiss6c187532010-08-30 22:05:33 +0000251 read_struct_loop(IProto3, SDict, NewRTuple);
David Reiss233ace52009-03-30 20:46:47 +0000252 Expected ->
253 error_logger:info_msg(
Sergei Elin45764092022-09-23 23:21:31 +0300254 "Skipping field ~p with wrong type (~p != ~p)~n",
255 [Fid, FType, Expected]
256 ),
David Reiss6c187532010-08-30 22:05:33 +0000257 skip_field(FType, IProto1, SDict, RTuple)
David Reiss233ace52009-03-30 20:46:47 +0000258 end;
David Reissae756f42008-06-10 22:57:11 +0000259 _Else2 ->
David Reiss6c187532010-08-30 22:05:33 +0000260 skip_field(FType, IProto1, SDict, RTuple)
David Reissae756f42008-06-10 22:57:11 +0000261 end
262 end.
David Reissac549552008-06-10 22:56:59 +0000263
David Reiss6c187532010-08-30 22:05:33 +0000264skip_field(FType, IProto0, SDict, RTuple) ->
David Hulle544a892017-07-27 02:15:00 +0200265 {IProto1, ok} = skip(IProto0, typeid_to_atom(FType)),
David Reiss6c187532010-08-30 22:05:33 +0000266 {IProto2, ok} = read(IProto1, field_end),
267 read_struct_loop(IProto2, SDict, RTuple).
David Reiss233ace52009-03-30 20:46:47 +0000268
David Hulle544a892017-07-27 02:15:00 +0200269-spec skip(#protocol{}, atom()) -> {#protocol{}, ok}.
David Reissac549552008-06-10 22:56:59 +0000270
David Reiss6c187532010-08-30 22:05:33 +0000271skip(Proto0, struct) ->
272 {Proto1, ok} = read(Proto0, struct_begin),
273 {Proto2, ok} = skip_struct_loop(Proto1),
274 {Proto3, ok} = read(Proto2, struct_end),
275 {Proto3, ok};
David Reiss6c187532010-08-30 22:05:33 +0000276skip(Proto0, map) ->
277 {Proto1, Map} = read(Proto0, map_begin),
278 {Proto2, ok} = skip_map_loop(Proto1, Map),
279 {Proto3, ok} = read(Proto2, map_end),
280 {Proto3, ok};
David Reiss6c187532010-08-30 22:05:33 +0000281skip(Proto0, set) ->
282 {Proto1, Set} = read(Proto0, set_begin),
283 {Proto2, ok} = skip_set_loop(Proto1, Set),
284 {Proto3, ok} = read(Proto2, set_end),
285 {Proto3, ok};
David Reiss6c187532010-08-30 22:05:33 +0000286skip(Proto0, list) ->
287 {Proto1, List} = read(Proto0, list_begin),
288 {Proto2, ok} = skip_list_loop(Proto1, List),
289 {Proto3, ok} = read(Proto2, list_end),
290 {Proto3, ok};
David Reiss6c187532010-08-30 22:05:33 +0000291skip(Proto0, Type) when is_atom(Type) ->
292 {Proto1, _Ignore} = read(Proto0, Type),
293 {Proto1, ok}.
David Reissf32d0fb2010-08-30 22:05:00 +0000294
David Reiss6c187532010-08-30 22:05:33 +0000295skip_struct_loop(Proto0) ->
296 {Proto1, #protocol_field_begin{type = Type}} = read(Proto0, field_begin),
David Reissac549552008-06-10 22:56:59 +0000297 case Type of
298 ?tType_STOP ->
David Reiss6c187532010-08-30 22:05:33 +0000299 {Proto1, ok};
David Reissac549552008-06-10 22:56:59 +0000300 _Else ->
David Hulle544a892017-07-27 02:15:00 +0200301 {Proto2, ok} = skip(Proto1, typeid_to_atom(Type)),
David Reiss6c187532010-08-30 22:05:33 +0000302 {Proto3, ok} = read(Proto2, field_end),
303 skip_struct_loop(Proto3)
David Reissac549552008-06-10 22:56:59 +0000304 end.
305
Sergei Elin45764092022-09-23 23:21:31 +0300306skip_map_loop(
307 Proto0,
308 Map = #protocol_map_begin{
309 ktype = Ktype,
310 vtype = Vtype,
311 size = Size
312 }
313) ->
David Reissac549552008-06-10 22:56:59 +0000314 case Size of
315 N when N > 0 ->
David Hulle544a892017-07-27 02:15:00 +0200316 {Proto1, ok} = skip(Proto0, typeid_to_atom(Ktype)),
317 {Proto2, ok} = skip(Proto1, typeid_to_atom(Vtype)),
Sergei Elin45764092022-09-23 23:21:31 +0300318 skip_map_loop(
319 Proto2,
320 Map#protocol_map_begin{size = Size - 1}
321 );
322 0 ->
323 {Proto0, ok}
David Reissac549552008-06-10 22:56:59 +0000324 end.
325
Sergei Elin45764092022-09-23 23:21:31 +0300326skip_set_loop(
327 Proto0,
328 Map = #protocol_set_begin{
329 etype = Etype,
330 size = Size
331 }
332) ->
David Reissac549552008-06-10 22:56:59 +0000333 case Size of
334 N when N > 0 ->
David Hulle544a892017-07-27 02:15:00 +0200335 {Proto1, ok} = skip(Proto0, typeid_to_atom(Etype)),
Sergei Elin45764092022-09-23 23:21:31 +0300336 skip_set_loop(
337 Proto1,
338 Map#protocol_set_begin{size = Size - 1}
339 );
340 0 ->
341 {Proto0, ok}
David Reissac549552008-06-10 22:56:59 +0000342 end.
343
Sergei Elin45764092022-09-23 23:21:31 +0300344skip_list_loop(
345 Proto0,
346 Map = #protocol_list_begin{
347 etype = Etype,
348 size = Size
349 }
350) ->
David Reissac549552008-06-10 22:56:59 +0000351 case Size of
352 N when N > 0 ->
David Hulle544a892017-07-27 02:15:00 +0200353 {Proto1, ok} = skip(Proto0, typeid_to_atom(Etype)),
Sergei Elin45764092022-09-23 23:21:31 +0300354 skip_list_loop(
355 Proto1,
356 Map#protocol_list_begin{size = Size - 1}
357 );
358 0 ->
359 {Proto0, ok}
David Reissac549552008-06-10 22:56:59 +0000360 end.
David Reissae756f42008-06-10 22:57:11 +0000361
David Reissae756f42008-06-10 22:57:11 +0000362%%--------------------------------------------------------------------
363%% Function: write(OProto, {Type, Data}) -> ok
David Reiss6b3e40f2008-06-11 00:59:03 +0000364%%
David Reissae756f42008-06-10 22:57:11 +0000365%% Type = {struct, StructDef} |
366%% {list, Type} |
367%% {map, KeyType, ValType} |
368%% {set, Type} |
369%% BaseType
370%%
371%% Data =
372%% tuple() -- for struct
373%% | list() -- for list
374%% | dictionary() -- for map
375%% | set() -- for set
David Reissf4494ee2010-08-30 22:06:03 +0000376%% | any() -- for base types
David Reissae756f42008-06-10 22:57:11 +0000377%%
David Reiss6b3e40f2008-06-11 00:59:03 +0000378%% Description:
David Reissae756f42008-06-10 22:57:11 +0000379%%--------------------------------------------------------------------
David Reissf4494ee2010-08-30 22:06:03 +0000380-spec write(#protocol{}, any()) -> {#protocol{}, ok | {error, _Reason}}.
David Reiss5e6637b2010-08-30 22:05:18 +0000381
Sergei Elin45764092022-09-23 23:21:31 +0300382write(Proto0, {{struct, StructDef}, Data}) when
383 is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1
384->
David Reiss76f0d112008-06-10 22:57:35 +0000385 [StructName | Elems] = tuple_to_list(Data),
David Reissc4657992010-08-30 22:05:31 +0000386 {Proto1, ok} = write(Proto0, #protocol_struct_begin{name = StructName}),
387 {Proto2, ok} = struct_write_loop(Proto1, StructDef, Elems),
388 {Proto3, ok} = write(Proto2, struct_end),
389 {Proto3, ok};
Sergei Elin45764092022-09-23 23:21:31 +0300390write(Proto, {{struct, {Module, StructureName}}, Data}) when
391 is_atom(Module),
392 is_atom(StructureName),
393 element(1, Data) =:= StructureName
394->
David Reiss76f0d112008-06-10 22:57:35 +0000395 write(Proto, {Module:struct_info(StructureName), Data});
Sergei Elin45764092022-09-23 23:21:31 +0300396write(_, {{struct, {Module, StructureName}}, Data}) when
397 is_atom(Module),
398 is_atom(StructureName)
399->
400 erlang:error(struct_unmatched, {{provided, element(1, Data)}, {expected, StructureName}});
401write(Proto0, {{list, Type}, Data}) when
402 is_list(Data)
403->
404 {Proto1, ok} = write(
405 Proto0,
406 #protocol_list_begin{
407 etype = term_to_typeid(Type),
408 size = length(Data)
409 }
410 ),
411 Proto2 = lists:foldl(
412 fun(Elem, ProtoIn) ->
413 {ProtoOut, ok} = write(ProtoIn, {Type, Elem}),
414 ProtoOut
415 end,
416 Proto1,
417 Data
418 ),
David Reissc4657992010-08-30 22:05:31 +0000419 {Proto3, ok} = write(Proto2, list_end),
420 {Proto3, ok};
David Reissc4657992010-08-30 22:05:31 +0000421write(Proto0, {{map, KeyType, ValType}, Data}) ->
Sergei Elin45764092022-09-23 23:21:31 +0300422 {Proto1, ok} = write(
423 Proto0,
424 #protocol_map_begin{
425 ktype = term_to_typeid(KeyType),
426 vtype = term_to_typeid(ValType),
427 size = dict:size(Data)
428 }
429 ),
430 Proto2 = dict:fold(
431 fun(KeyData, ValData, ProtoS0) ->
432 {ProtoS1, ok} = write(ProtoS0, {KeyType, KeyData}),
433 {ProtoS2, ok} = write(ProtoS1, {ValType, ValData}),
434 ProtoS2
435 end,
436 Proto1,
437 Data
438 ),
David Reissc4657992010-08-30 22:05:31 +0000439 {Proto3, ok} = write(Proto2, map_end),
440 {Proto3, ok};
David Reissc4657992010-08-30 22:05:31 +0000441write(Proto0, {{set, Type}, Data}) ->
David Reissae756f42008-06-10 22:57:11 +0000442 true = sets:is_set(Data),
Sergei Elin45764092022-09-23 23:21:31 +0300443 {Proto1, ok} = write(
444 Proto0,
445 #protocol_set_begin{
446 etype = term_to_typeid(Type),
447 size = sets:size(Data)
448 }
449 ),
450 Proto2 = sets:fold(
451 fun(Elem, ProtoIn) ->
452 {ProtoOut, ok} = write(ProtoIn, {Type, Elem}),
453 ProtoOut
454 end,
455 Proto1,
456 Data
457 ),
David Reissc4657992010-08-30 22:05:31 +0000458 {Proto3, ok} = write(Proto2, set_end),
459 {Proto3, ok};
Sergei Elin45764092022-09-23 23:21:31 +0300460write(
461 Proto = #protocol{
462 module = Module,
463 data = ModuleData
464 },
465 Data
466) ->
David Reissc4657992010-08-30 22:05:31 +0000467 {NewData, Result} = Module:write(ModuleData, Data),
468 {Proto#protocol{data = NewData}, Result}.
David Reissae756f42008-06-10 22:57:11 +0000469
David Reissc4657992010-08-30 22:05:31 +0000470struct_write_loop(Proto0, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
Sergei Elin45764092022-09-23 23:21:31 +0300471 NewProto =
472 case Data of
473 undefined ->
474 % null fields are skipped in response
475 Proto0;
476 _ ->
477 {Proto1, ok} = write(
478 Proto0,
479 #protocol_field_begin{
480 type = term_to_typeid(Type),
481 id = Fid
482 }
483 ),
484 {Proto2, ok} = write(Proto1, {Type, Data}),
485 {Proto3, ok} = write(Proto2, field_end),
486 Proto3
487 end,
David Reissc4657992010-08-30 22:05:31 +0000488 struct_write_loop(NewProto, RestStructDef, RestData);
David Reissae756f42008-06-10 22:57:11 +0000489struct_write_loop(Proto, [], []) ->
David Reissc4657992010-08-30 22:05:31 +0000490 write(Proto, field_stop).