blob: 0f14221914e240244dc5866786544a54ec4c5956 [file] [log] [blame]
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +09001%%
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
20-module(thrift_compact_protocol).
21
22-behaviour(thrift_protocol).
23
24-include("thrift_constants.hrl").
25-include("thrift_protocol.hrl").
26
27-export([new/1, new/2,
28 read/2,
29 write/2,
30 flush_transport/1,
31 close_transport/1,
32 new_protocol_factory/2
33 ]).
34
35-define(ID_NONE, 16#10000).
36-define(CBOOL_NONE, 0).
Vince Foleyd150f8b2016-07-14 14:21:00 -070037-define(CBOOL_TRUE, 1).
38-define(CBOOL_FALSE, 2).
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090039
40-record(t_compact, {transport,
41 % state for pending boolean fields
42 read_stack=[],
43 read_value=?CBOOL_NONE,
44 write_stack=[],
45 write_id=?ID_NONE
46 }).
47-type state() :: #t_compact{}.
48-include("thrift_protocol_behaviour.hrl").
49
50-define(PROTOCOL_ID, 16#82).
51-define(VERSION_MASK, 16#1f).
52-define(VERSION_1, 16#01).
53-define(TYPE_MASK, 16#E0).
54-define(TYPE_BITS, 16#07).
55-define(TYPE_SHIFT_AMOUNT, 5).
56
57typeid_to_compact(?tType_STOP) -> 16#0;
Nobuaki Sukegawae1b85402016-07-18 21:27:27 +090058typeid_to_compact(?tType_BOOL) -> 16#2;
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090059typeid_to_compact(?tType_I8) -> 16#3;
60typeid_to_compact(?tType_I16) -> 16#4;
61typeid_to_compact(?tType_I32) -> 16#5;
62typeid_to_compact(?tType_I64) -> 16#6;
Nobuaki Sukegawae1b85402016-07-18 21:27:27 +090063typeid_to_compact(?tType_DOUBLE) -> 16#7;
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090064typeid_to_compact(?tType_STRING) -> 16#8;
65typeid_to_compact(?tType_STRUCT) -> 16#C;
66typeid_to_compact(?tType_MAP) -> 16#B;
67typeid_to_compact(?tType_SET) -> 16#A;
68typeid_to_compact(?tType_LIST) -> 16#9.
69
70compact_to_typeid(16#0) -> ?tType_STOP;
71compact_to_typeid(?CBOOL_FALSE) -> ?tType_BOOL;
72compact_to_typeid(?CBOOL_TRUE) -> ?tType_BOOL;
73compact_to_typeid(16#7) -> ?tType_DOUBLE;
74compact_to_typeid(16#3) -> ?tType_I8;
75compact_to_typeid(16#4) -> ?tType_I16;
76compact_to_typeid(16#5) -> ?tType_I32;
77compact_to_typeid(16#6) -> ?tType_I64;
78compact_to_typeid(16#8) -> ?tType_STRING;
79compact_to_typeid(16#C) -> ?tType_STRUCT;
80compact_to_typeid(16#B) -> ?tType_MAP;
81compact_to_typeid(16#A) -> ?tType_SET;
82compact_to_typeid(16#9) -> ?tType_LIST.
83
84bool_to_cbool(Value) when Value -> ?CBOOL_TRUE;
85bool_to_cbool(_) -> ?CBOOL_FALSE.
86cbool_to_bool(Value) -> Value =:= ?CBOOL_TRUE.
87
88new(Transport) -> new(Transport, _Options = []).
89
90new(Transport, _Options) ->
91 State = #t_compact{transport = Transport},
92 thrift_protocol:new(?MODULE, State).
93
94flush_transport(This = #t_compact{transport = Transport}) ->
95 {NewTransport, Result} = thrift_transport:flush(Transport),
96 {This#t_compact{transport = NewTransport}, Result}.
97
98close_transport(This = #t_compact{transport = Transport}) ->
99 {NewTransport, Result} = thrift_transport:close(Transport),
100 {This#t_compact{transport = NewTransport}, Result}.
101
102%%%
103%%% instance methods
104%%%
105
106write_field_begin(This0 = #t_compact{write_stack=[LastId|T]}, CompactType, Id) ->
107 IdDiff = Id - LastId,
108 This1 = This0#t_compact{write_stack=[Id|T]},
109 case (IdDiff > 0) and (IdDiff < 16) of
110 true -> write(This1, {byte, (IdDiff bsl 4) bor CompactType});
111 false ->
112 {This2, ok} = write(This1, {byte, CompactType}),
113 write(This2, {i16, Id})
114 end.
115
116-spec to_zigzag(integer()) -> non_neg_integer().
Nobuaki Sukegawae1b85402016-07-18 21:27:27 +0900117to_zigzag(Value) -> 16#FFFFFFFFFFFFFFFF band ((Value bsl 1) bxor (Value bsr 63)).
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900118
119-spec from_zigzag(non_neg_integer()) -> integer().
120from_zigzag(Value) -> (Value bsr 1) bxor -(Value band 1).
121
122-spec to_varint(non_neg_integer(), iolist()) -> iolist().
123to_varint(Value, Acc) when (Value < 16#80) -> [Acc, Value];
124to_varint(Value, Acc) ->
125 to_varint(Value bsr 7, [Acc, ((Value band 16#7F) bor 16#80)]).
126
127-spec read_varint(#t_compact{}, non_neg_integer(), non_neg_integer()) -> non_neg_integer().
128read_varint(This0, Acc, Count) ->
129 {This1, {ok, Byte}} = read(This0, byte),
130 case (Byte band 16#80) of
131 0 -> {This1, {ok, (Byte bsl (7 * Count)) + Acc}};
132 _ -> read_varint(This1, ((Byte band 16#7f) bsl (7 * Count)) + Acc, Count + 1)
133 end.
134
135write(This0, #protocol_message_begin{
136 name = Name,
137 type = Type,
138 seqid = Seqid}) ->
139 {This1, ok} = write(This0, {byte, ?PROTOCOL_ID}),
140 {This2, ok} = write(This1, {byte, (?VERSION_1 band ?VERSION_MASK) bor (Type bsl ?TYPE_SHIFT_AMOUNT)}),
141 {This3, ok} = write(This2, {ui32, Seqid}),
142 {This4, ok} = write(This3, {string, Name}),
143 {This4, ok};
144
145write(This, message_end) -> {This, ok};
146
147write(This0, #protocol_field_begin{
148 name = _Name,
149 type = Type,
150 id = Id})
151when (Type =:= ?tType_BOOL) -> {This0#t_compact{write_id = Id}, ok};
152
153write(This0, #protocol_field_begin{
154 name = _Name,
155 type = Type,
156 id = Id}) ->
157 write_field_begin(This0, typeid_to_compact(Type), Id);
158
159write(This, field_stop) -> write(This, {byte, ?tType_STOP});
160
161write(This, field_end) -> {This, ok};
162
163write(This0, #protocol_map_begin{
164 ktype = _Ktype,
165 vtype = _Vtype,
166 size = Size})
167when Size =:= 0 ->
168 write(This0, {byte, 0});
169
170write(This0, #protocol_map_begin{
171 ktype = Ktype,
172 vtype = Vtype,
173 size = Size}) ->
174 {This1, ok} = write(This0, {ui32, Size}),
175 write(This1, {byte, (typeid_to_compact(Ktype) bsl 4) bor typeid_to_compact(Vtype)});
176
177write(This, map_end) -> {This, ok};
178
179write(This0, #protocol_list_begin{
180 etype = Etype,
181 size = Size})
182when Size < 16#f ->
183 write(This0, {byte, (Size bsl 4) bor typeid_to_compact(Etype)});
184
185write(This0, #protocol_list_begin{
186 etype = Etype,
187 size = Size}) ->
188 {This1, ok} = write(This0, {byte, 16#f0 bor typeid_to_compact(Etype)}),
189 write(This1, {ui32, Size});
190
191write(This, list_end) -> {This, ok};
192
193write(This0, #protocol_set_begin{
194 etype = Etype,
195 size = Size}) ->
196 write(This0, #protocol_list_begin{etype = Etype, size = Size});
197
198write(This, set_end) -> {This, ok};
199
200write(This = #t_compact{write_stack = Stack}, #protocol_struct_begin{}) ->
201 {This#t_compact{write_stack = [0|Stack]}, ok};
202write(This = #t_compact{write_stack = [_|T]}, struct_end) ->
203 {This#t_compact{write_stack = T}, ok};
204
205write(This = #t_compact{write_id = ?ID_NONE}, {bool, Value}) ->
206 write(This, {byte, bool_to_cbool(Value)});
207
208write(This0 = #t_compact{write_id = Id}, {bool, Value}) ->
209 {This1, ok} = write_field_begin(This0, bool_to_cbool(Value), Id),
210 {This1#t_compact{write_id = ?ID_NONE}, ok};
211
212write(This, {byte, Value}) when is_integer(Value) ->
213 write(This, <<Value:8/big-signed>>);
214
215write(This, {i16, Value}) when is_integer(Value) -> write(This, to_varint(to_zigzag(Value), []));
216write(This, {ui32, Value}) when is_integer(Value) -> write(This, to_varint(Value, []));
217write(This, {i32, Value}) when is_integer(Value) ->
218 write(This, to_varint(to_zigzag(Value), []));
219write(This, {i64, Value}) when is_integer(Value) -> write(This, to_varint(to_zigzag(Value), []));
220
221write(This, {double, Double}) ->
Nobuaki Sukegawae1b85402016-07-18 21:27:27 +0900222 write(This, <<Double:64/float-signed-little>>);
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900223
224write(This0, {string, Str}) when is_list(Str) ->
225 % TODO: limit length
226 {This1, ok} = write(This0, {ui32, length(Str)}),
227 {This2, ok} = write(This1, list_to_binary(Str)),
228 {This2, ok};
229
230write(This0, {string, Bin}) when is_binary(Bin) ->
231 % TODO: limit length
232 {This1, ok} = write(This0, {ui32, size(Bin)}),
233 {This2, ok} = write(This1, Bin),
234 {This2, ok};
235
236%% Data :: iolist()
237write(This = #t_compact{transport = Trans}, Data) ->
238 {NewTransport, Result} = thrift_transport:write(Trans, Data),
239 {This#t_compact{transport = NewTransport}, Result}.
240
241%%
242%%
243
244read(This0, message_begin) ->
245 {This1, {ok, ?PROTOCOL_ID}} = read(This0, ubyte),
246 {This2, {ok, VerAndType}} = read(This1, ubyte),
247 ?VERSION_1 = VerAndType band ?VERSION_MASK,
248 {This3, {ok, SeqId}} = read(This2, ui32),
249 {This4, {ok, Name}} = read(This3, string),
250 {This4, #protocol_message_begin{
251 name = binary_to_list(Name),
252 type = (VerAndType bsr ?TYPE_SHIFT_AMOUNT) band ?TYPE_BITS,
253 seqid = SeqId}};
254
255read(This, message_end) -> {This, ok};
256
257read(This = #t_compact{read_stack = Stack}, struct_begin) ->
258 {This#t_compact{read_stack = [0|Stack]}, ok};
259read(This = #t_compact{read_stack = [_H|T]}, struct_end) ->
260 {This#t_compact{read_stack = T}, ok};
261
262read(This0 = #t_compact{read_stack = [LastId|T]}, field_begin) ->
263 {This1, {ok, Byte}} = read(This0, ubyte),
264 case Byte band 16#f of
265 CompactType = ?tType_STOP ->
266 {This1, #protocol_field_begin{type = CompactType}};
267 CompactType ->
268 {This2, {ok, Id}} = case Byte bsr 4 of
269 0 -> read(This1, i16);
270 IdDiff ->
271 {This1, {ok, LastId + IdDiff}}
272 end,
273 case compact_to_typeid(CompactType) of
274 ?tType_BOOL ->
275 {This2#t_compact{read_stack = [Id|T], read_value = cbool_to_bool(CompactType)},
276 #protocol_field_begin{type = ?tType_BOOL, id = Id}};
277 Type ->
278 {This2#t_compact{read_stack = [Id|T]},
279 #protocol_field_begin{type = Type, id = Id}}
280 end
281 end;
282
283read(This, field_end) -> {This, ok};
284
285read(This0, map_begin) ->
286 {This1, {ok, Size}} = read(This0, ui32),
287 {This2, {ok, KV}} = case Size of
288 0 -> {This1, {ok, 0}};
289 _ -> read(This1, ubyte)
290 end,
291 {This2, #protocol_map_begin{ktype = compact_to_typeid(KV bsr 4),
292 vtype = compact_to_typeid(KV band 16#f),
293 size = Size}};
294read(This, map_end) -> {This, ok};
295
296read(This0, list_begin) ->
297 {This1, {ok, SizeAndType}} = read(This0, ubyte),
298 {This2, {ok, Size}} = case (SizeAndType bsr 4) band 16#f of
299 16#f -> read(This1, ui32);
300 Else -> {This1, {ok, Else}}
301 end,
302 {This2, #protocol_list_begin{etype = compact_to_typeid(SizeAndType band 16#f),
303 size = Size}};
304
305read(This, list_end) -> {This, ok};
306
307read(This0, set_begin) ->
308 {This1, {ok, SizeAndType}} = read(This0, ubyte),
309 {This2, {ok, Size}} = case (SizeAndType bsr 4) band 16#f of
310 16#f -> read(This1, ui32);
311 Else -> {This1, {ok, Else}}
312 end,
313 {This2, #protocol_set_begin{etype = compact_to_typeid(SizeAndType band 16#f),
314 size = Size}};
315
316read(This, set_end) -> {This, ok};
317
318read(This0, field_stop) ->
319 {This1, {ok, ?tType_STOP}} = read(This0, ubyte),
320 {This1, ok};
321
322%%
323
324read(This0 = #t_compact{read_value = ?CBOOL_NONE}, bool) ->
325 {This1, {ok, Byte}} = read(This0, ubyte),
326 {This1, {ok, cbool_to_bool(Byte)}};
327
328read(This0 = #t_compact{read_value = Bool}, bool) ->
329 {This0#t_compact{read_value = ?CBOOL_NONE}, {ok, Bool}};
330
331read(This0, ubyte) ->
332 {This1, {ok, <<Val:8/integer-unsigned-big, _/binary>>}} = read_data(This0, 1),
333 {This1, {ok, Val}};
334
335read(This0, byte) ->
336 {This1, Bytes} = read_data(This0, 1),
337 case Bytes of
338 {ok, <<Val:8/integer-signed-big, _/binary>>} -> {This1, {ok, Val}};
339 Else -> {This1, Else}
340 end;
341
342read(This0, i16) ->
343 {This1, {ok, Zigzag}} = read_varint(This0, 0, 0),
344 {This1, {ok, from_zigzag(Zigzag)}};
345
346read(This0, ui32) -> read_varint(This0, 0, 0);
347
348read(This0, i32) ->
349 {This1, {ok, Zigzag}} = read_varint(This0, 0, 0),
350 {This1, {ok, from_zigzag(Zigzag)}};
351
352read(This0, i64) ->
353 {This1, {ok, Zigzag}} = read_varint(This0, 0, 0),
354 {This1, {ok, from_zigzag(Zigzag)}};
355
356read(This0, double) ->
357 {This1, Bytes} = read_data(This0, 8),
358 case Bytes of
Nobuaki Sukegawae1b85402016-07-18 21:27:27 +0900359 {ok, <<Val:64/float-signed-little, _/binary>>} -> {This1, {ok, Val}};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900360 Else -> {This1, Else}
361 end;
362
363% returns a binary directly, call binary_to_list if necessary
364read(This0, string) ->
365 {This1, {ok, Sz}} = read(This0, ui32),
366 read_data(This1, Sz).
367
368-spec read_data(#t_compact{}, non_neg_integer()) ->
369 {#t_compact{}, {ok, binary()} | {error, _Reason}}.
370read_data(This, 0) -> {This, {ok, <<>>}};
371read_data(This = #t_compact{transport = Trans}, Len) when is_integer(Len) andalso Len > 0 ->
372 {NewTransport, Result} = thrift_transport:read(Trans, Len),
373 {This#t_compact{transport = NewTransport}, Result}.
374
375
376%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
377
378%% returns a (fun() -> thrift_protocol())
379new_protocol_factory(TransportFactory, _Options) ->
380 F = fun() ->
381 case TransportFactory() of
382 {ok, Transport} ->
383 thrift_compact_protocol:new(
384 Transport,
385 []);
386 {error, Error} ->
387 {error, Error}
388 end
389 end,
390 {ok, F}.