blob: e53f5ef7b9e4c62f6e533bcd637e5bb599f33c5d [file] [log] [blame]
David Reissac549552008-06-10 22:56:59 +00001%%% Copyright (c) 2007- Facebook
2%%% Distributed under the Thrift Software License
3%%%
4%%% See accompanying file LICENSE or visit the Thrift site at:
5%%% http://developers.facebook.com/thrift/
6
7-module(thrift_binary_protocol).
8
9-behavior(thrift_protocol).
10
11-include("thrift_constants.hrl").
12-include("thrift_protocol.hrl").
13
David Reiss914ebb42008-06-11 01:01:48 +000014-export([new/1, new/2,
David Reissac549552008-06-10 22:56:59 +000015 read/2,
David Reiss90b40832008-06-10 22:58:52 +000016 write/2,
David Reissc11734e2008-06-11 00:59:48 +000017 flush_transport/1,
David Reissfc427af2008-06-11 01:11:57 +000018 close_transport/1,
19
20 new_protocol_factory/2
David Reiss914ebb42008-06-11 01:01:48 +000021 ]).
David Reissac549552008-06-10 22:56:59 +000022
David Reiss914ebb42008-06-11 01:01:48 +000023-record(binary_protocol, {transport,
24 strict_read=true,
25 strict_write=true
26 }).
David Reissac549552008-06-10 22:56:59 +000027
28-define(VERSION_MASK, 16#FFFF0000).
29-define(VERSION_1, 16#80010000).
David Reiss914ebb42008-06-11 01:01:48 +000030-define(TYPE_MASK, 16#000000ff).
David Reissac549552008-06-10 22:56:59 +000031
32new(Transport) ->
David Reiss914ebb42008-06-11 01:01:48 +000033 new(Transport, _Options = []).
34
35new(Transport, Options) ->
36 State = #binary_protocol{transport = Transport},
37 State1 = parse_options(Options, State),
38 thrift_protocol:new(?MODULE, State1).
39
40parse_options([], State) ->
41 State;
42parse_options([{strict_read, Bool} | Rest], State) when is_boolean(Bool) ->
43 parse_options(Rest, State#binary_protocol{strict_read=Bool});
44parse_options([{strict_write, Bool} | Rest], State) when is_boolean(Bool) ->
45 parse_options(Rest, State#binary_protocol{strict_write=Bool}).
46
David Reissac549552008-06-10 22:56:59 +000047
David Reiss90b40832008-06-10 22:58:52 +000048flush_transport(#binary_protocol{transport = Transport}) ->
49 thrift_transport:flush(Transport).
David Reissac549552008-06-10 22:56:59 +000050
David Reissc11734e2008-06-11 00:59:48 +000051close_transport(#binary_protocol{transport = Transport}) ->
David Reissc11734e2008-06-11 00:59:48 +000052 thrift_transport:close(Transport).
53
David Reissac549552008-06-10 22:56:59 +000054%%%
55%%% instance methods
56%%%
57
58write(This, #protocol_message_begin{
59 name = Name,
60 type = Type,
61 seqid = Seqid}) ->
David Reiss914ebb42008-06-11 01:01:48 +000062 case This#binary_protocol.strict_write of
63 true ->
64 write(This, {i32, ?VERSION_1 bor Type}),
65 write(This, {string, Name}),
66 write(This, {i32, Seqid});
67 false ->
68 write(This, {string, Name}),
69 write(This, {byte, Type}),
70 write(This, {i32, Seqid})
71 end,
David Reissac549552008-06-10 22:56:59 +000072 ok;
73
74write(This, message_end) -> ok;
75
76write(This, #protocol_field_begin{
77 name = _Name,
78 type = Type,
79 id = Id}) ->
80 write(This, {byte, Type}),
81 write(This, {i16, Id}),
82 ok;
83
84write(This, field_stop) ->
85 write(This, {byte, ?tType_STOP}),
86 ok;
87
88write(This, field_end) -> ok;
89
90write(This, #protocol_map_begin{
91 ktype = Ktype,
92 vtype = Vtype,
93 size = Size}) ->
94 write(This, {byte, Ktype}),
95 write(This, {byte, Vtype}),
96 write(This, {i32, Size}),
97 ok;
98
99write(This, map_end) -> ok;
100
101write(This, #protocol_list_begin{
102 etype = Etype,
103 size = Size}) ->
104 write(This, {byte, Etype}),
105 write(This, {i32, Size}),
106 ok;
107
108write(This, list_end) -> ok;
109
110write(This, #protocol_set_begin{
111 etype = Etype,
112 size = Size}) ->
113 write(This, {byte, Etype}),
114 write(This, {i32, Size}),
115 ok;
116
117write(This, set_end) -> ok;
118
David Reissae756f42008-06-10 22:57:11 +0000119write(This, #protocol_struct_begin{}) -> ok;
David Reissac549552008-06-10 22:56:59 +0000120write(This, struct_end) -> ok;
121
David Reissac549552008-06-10 22:56:59 +0000122write(This, {bool, true}) -> write(This, {byte, 1});
123write(This, {bool, false}) -> write(This, {byte, 0});
124
125write(This, {byte, Byte}) ->
David Reiss07a725f2008-06-10 22:57:59 +0000126 write(This, <<Byte:8/big-signed>>);
David Reissac549552008-06-10 22:56:59 +0000127
128write(This, {i16, I16}) ->
David Reiss07a725f2008-06-10 22:57:59 +0000129 write(This, <<I16:16/big-signed>>);
David Reissac549552008-06-10 22:56:59 +0000130
131write(This, {i32, I32}) ->
David Reiss07a725f2008-06-10 22:57:59 +0000132 write(This, <<I32:32/big-signed>>);
David Reissac549552008-06-10 22:56:59 +0000133
David Reiss07a725f2008-06-10 22:57:59 +0000134write(This, {i64, I64}) ->
135 write(This, <<I64:64/big-signed>>);
David Reissac549552008-06-10 22:56:59 +0000136
137write(This, {double, Double}) ->
David Reiss07a725f2008-06-10 22:57:59 +0000138 write(This, <<Double:64/big-signed-float>>);
David Reissac549552008-06-10 22:56:59 +0000139
140write(This, {string, Str}) when is_list(Str) ->
141 write(This, {i32, length(Str)}),
142 write(This, list_to_binary(Str));
143
David Reiss225db732008-06-11 00:58:48 +0000144write(This, {string, Bin}) when is_binary(Bin) ->
145 write(This, {i32, size(Bin)}),
146 write(This, Bin);
147
David Reiss914ebb42008-06-11 01:01:48 +0000148%% Data :: iolist()
149write(This, Data) ->
150 thrift_transport:write(This#binary_protocol.transport, Data).
David Reissac549552008-06-10 22:56:59 +0000151
152%%
153
154read(This, message_begin) ->
David Reiss9ad6a312008-06-11 01:12:45 +0000155 case read(This, ui32) of
David Reiss914ebb42008-06-11 01:01:48 +0000156 {ok, Sz} when Sz band ?VERSION_MASK =:= ?VERSION_1 ->
157 %% we're at version 1
David Reissac549552008-06-10 22:56:59 +0000158 {ok, Name} = read(This, string),
David Reiss914ebb42008-06-11 01:01:48 +0000159 Type = Sz band ?TYPE_MASK,
David Reissac549552008-06-10 22:56:59 +0000160 {ok, SeqId} = read(This, i32),
David Reiss4ec777e2008-06-11 01:01:29 +0000161 #protocol_message_begin{name = binary_to_list(Name),
162 type = Type,
David Reissac549552008-06-10 22:56:59 +0000163 seqid = SeqId};
David Reiss914ebb42008-06-11 01:01:48 +0000164
165 {ok, Sz} when Sz < 0 ->
166 %% there's a version number but it's unexpected
167 {error, {bad_binary_protocol_version, Sz}};
168
169 {ok, Sz} when This#binary_protocol.strict_read =:= true ->
170 %% strict_read is true and there's no version header; that's an error
171 {error, no_binary_protocol_version};
172
173 {ok, Sz} when This#binary_protocol.strict_read =:= false ->
174 %% strict_read is false, so just read the old way
175 {ok, Name} = read(This, Sz),
176 {ok, Type} = read(This, byte),
177 {ok, SeqId} = read(This, i32),
178 #protocol_message_begin{name = binary_to_list(Name),
179 type = Type,
180 seqid = SeqId};
181
David Reissae756f42008-06-10 22:57:11 +0000182 Err = {error, closed} -> Err;
David Reiss7956f232008-06-11 01:02:47 +0000183 Err = {error, timeout}-> Err;
David Reissae756f42008-06-10 22:57:11 +0000184 Err = {error, ebadf} -> Err
David Reissac549552008-06-10 22:56:59 +0000185 end;
186
187read(This, message_end) -> ok;
188
189read(This, struct_begin) -> ok;
190read(This, struct_end) -> ok;
191
192read(This, field_begin) ->
193 case read(This, byte) of
194 {ok, Type = ?tType_STOP} ->
David Reissfe7ce1b2008-06-11 01:03:10 +0000195 #protocol_field_begin{type = Type};
David Reissac549552008-06-10 22:56:59 +0000196 {ok, Type} ->
David Reissae756f42008-06-10 22:57:11 +0000197 {ok, Id} = read(This, i16),
David Reissac549552008-06-10 22:56:59 +0000198 #protocol_field_begin{type = Type,
199 id = Id}
200 end;
201
202read(This, field_end) -> ok;
203
204read(This, map_begin) ->
205 {ok, Ktype} = read(This, byte),
206 {ok, Vtype} = read(This, byte),
207 {ok, Size} = read(This, i32),
208 #protocol_map_begin{ktype = Ktype,
209 vtype = Vtype,
210 size = Size};
211read(This, map_end) -> ok;
212
213read(This, list_begin) ->
214 {ok, Etype} = read(This, byte),
215 {ok, Size} = read(This, i32),
216 #protocol_list_begin{etype = Etype,
217 size = Size};
218read(This, list_end) -> ok;
219
220read(This, set_begin) ->
221 {ok, Etype} = read(This, byte),
222 {ok, Size} = read(This, i32),
223 #protocol_set_begin{etype = Etype,
224 size = Size};
225read(This, set_end) -> ok;
226
227read(This, field_stop) ->
228 {ok, ?tType_STOP} = read(This, byte),
David Reiss225db732008-06-11 00:58:48 +0000229 ok;
David Reissac549552008-06-10 22:56:59 +0000230
231%%
232
233read(This, bool) ->
234 Byte = read(This, byte),
235 {ok, (Byte /= 0)};
236
237
238read(This, byte) ->
239 case read(This, 1) of
240 {ok, <<Val:8/integer-signed-big, _/binary>>} -> {ok, Val};
241 Else -> Else
242 end;
243
244read(This, i16) ->
245 case read(This, 2) of
246 {ok, <<Val:16/integer-signed-big, _/binary>>} -> {ok, Val};
247 Else -> Else
248 end;
249
250read(This, i32) ->
251 case read(This, 4) of
252 {ok, <<Val:32/integer-signed-big, _/binary>>} -> {ok, Val};
253 Else -> Else
254 end;
255
David Reiss9ad6a312008-06-11 01:12:45 +0000256%% unsigned ints aren't used by thrift itself, but it's used for the parsing
257%% of the packet version header. Without this special function BEAM works fine
258%% but hipe thinks it received a bad version header.
259read(This, ui32) ->
260 case read(This, 4) of
261 {ok, <<Val:32/integer-unsigned-big, _/binary>>} -> {ok, Val};
262 Else -> Else
263 end;
264
David Reissac549552008-06-10 22:56:59 +0000265read(This, i64) ->
266 case read(This, 8) of
267 {ok, <<Val:64/integer-signed-big, _/binary>>} -> {ok, Val};
268 Else -> Else
269 end;
270
271read(This, double) ->
272 case read(This, 8) of
273 {ok, <<Val:64/float-signed-big, _/binary>>} -> {ok, Val};
274 Else -> Else
275 end;
276
David Reiss4ec777e2008-06-11 01:01:29 +0000277% returns a binary directly, call binary_to_list if necessary
David Reissac549552008-06-10 22:56:59 +0000278read(This, string) ->
279 {ok, Sz} = read(This, i32),
David Reiss4ec777e2008-06-11 01:01:29 +0000280 {ok, Bin} = read(This, Sz);
David Reissac549552008-06-10 22:56:59 +0000281
David Reissd74b0232008-06-11 01:02:55 +0000282read(This, 0) -> {ok, <<>>};
David Reissac549552008-06-10 22:56:59 +0000283read(This, Len) when is_integer(Len), Len >= 0 ->
284 thrift_transport:read(This#binary_protocol.transport, Len).
David Reissfc427af2008-06-11 01:11:57 +0000285
286
287%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
288
289-record(tbp_opts, {strict_read = true,
290 strict_write = true}).
291
292parse_factory_options([], Opts) ->
293 Opts;
294parse_factory_options([{strict_read, Bool} | Rest], Opts) when is_boolean(Bool) ->
295 parse_factory_options(Rest, Opts#tbp_opts{strict_read=Bool});
296parse_factory_options([{strict_write, Bool} | Rest], Opts) when is_boolean(Bool) ->
297 parse_factory_options(Rest, Opts#tbp_opts{strict_write=Bool}).
298
299
300%% returns a (fun() -> thrift_protocol())
301new_protocol_factory(TransportFactory, Options) ->
302 ParsedOpts = parse_factory_options(Options, #tbp_opts{}),
303 F = fun() ->
304 {ok, Transport} = TransportFactory(),
305 thrift_binary_protocol:new(
306 Transport,
307 [{strict_read, ParsedOpts#tbp_opts.strict_read},
308 {strict_write, ParsedOpts#tbp_opts.strict_write}])
309 end,
310 {ok, F}.
David Reiss1a2f2182008-06-11 01:14:01 +0000311