blob: f852fcb49adeb698d6cb498ea9b34268b74b5628 [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
Sergei Elin45764092022-09-23 23:21:31 +030027-export([
28 new/1, new/2,
29 read/2,
30 write/2,
31 flush_transport/1,
32 close_transport/1,
33 new_protocol_factory/2
34]).
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090035
36-define(ID_NONE, 16#10000).
37-define(CBOOL_NONE, 0).
Vince Foleyd150f8b2016-07-14 14:21:00 -070038-define(CBOOL_TRUE, 1).
39-define(CBOOL_FALSE, 2).
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090040
Sergei Elin45764092022-09-23 23:21:31 +030041-type cbool() :: ?CBOOL_NONE | ?CBOOL_TRUE | ?CBOOL_FALSE.
42
43-record(t_compact, {
44 transport :: term(),
45 % state for pending boolean fields
46 read_stack = [] :: iodata(),
47 read_value = ?CBOOL_NONE :: cbool(),
48 write_stack = [] :: iodata(),
49 write_id = ?ID_NONE :: non_neg_integer()
50}).
51
52-type t_compact() :: #t_compact{}.
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090053
54-define(PROTOCOL_ID, 16#82).
55-define(VERSION_MASK, 16#1f).
56-define(VERSION_1, 16#01).
57-define(TYPE_MASK, 16#E0).
58-define(TYPE_BITS, 16#07).
59-define(TYPE_SHIFT_AMOUNT, 5).
60
61typeid_to_compact(?tType_STOP) -> 16#0;
Nobuaki Sukegawae1b85402016-07-18 21:27:27 +090062typeid_to_compact(?tType_BOOL) -> 16#2;
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090063typeid_to_compact(?tType_I8) -> 16#3;
64typeid_to_compact(?tType_I16) -> 16#4;
65typeid_to_compact(?tType_I32) -> 16#5;
66typeid_to_compact(?tType_I64) -> 16#6;
Nobuaki Sukegawae1b85402016-07-18 21:27:27 +090067typeid_to_compact(?tType_DOUBLE) -> 16#7;
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090068typeid_to_compact(?tType_STRING) -> 16#8;
69typeid_to_compact(?tType_STRUCT) -> 16#C;
70typeid_to_compact(?tType_MAP) -> 16#B;
71typeid_to_compact(?tType_SET) -> 16#A;
72typeid_to_compact(?tType_LIST) -> 16#9.
73
Sergei Elin45764092022-09-23 23:21:31 +030074compact_to_typeid(16#0) -> ?tType_STOP;
75compact_to_typeid(?CBOOL_FALSE) -> ?tType_BOOL;
76compact_to_typeid(?CBOOL_TRUE) -> ?tType_BOOL;
77compact_to_typeid(16#7) -> ?tType_DOUBLE;
78compact_to_typeid(16#3) -> ?tType_I8;
79compact_to_typeid(16#4) -> ?tType_I16;
80compact_to_typeid(16#5) -> ?tType_I32;
81compact_to_typeid(16#6) -> ?tType_I64;
82compact_to_typeid(16#8) -> ?tType_STRING;
83compact_to_typeid(16#C) -> ?tType_STRUCT;
84compact_to_typeid(16#B) -> ?tType_MAP;
85compact_to_typeid(16#A) -> ?tType_SET;
86compact_to_typeid(16#9) -> ?tType_LIST.
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090087
88bool_to_cbool(Value) when Value -> ?CBOOL_TRUE;
89bool_to_cbool(_) -> ?CBOOL_FALSE.
90cbool_to_bool(Value) -> Value =:= ?CBOOL_TRUE.
91
92new(Transport) -> new(Transport, _Options = []).
93
94new(Transport, _Options) ->
Sergei Elin45764092022-09-23 23:21:31 +030095 State = #t_compact{transport = Transport},
96 thrift_protocol:new(?MODULE, State).
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +090097
98flush_transport(This = #t_compact{transport = Transport}) ->
Sergei Elin45764092022-09-23 23:21:31 +030099 {NewTransport, Result} = thrift_transport:flush(Transport),
100 {This#t_compact{transport = NewTransport}, Result}.
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900101
102close_transport(This = #t_compact{transport = Transport}) ->
Sergei Elin45764092022-09-23 23:21:31 +0300103 {NewTransport, Result} = thrift_transport:close(Transport),
104 {This#t_compact{transport = NewTransport}, Result}.
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900105
106%%%
107%%% instance methods
108%%%
109
Sergei Elin45764092022-09-23 23:21:31 +0300110write_field_begin(This0 = #t_compact{write_stack = [LastId | T]}, CompactType, Id) ->
111 IdDiff = Id - LastId,
112 This1 = This0#t_compact{write_stack = [Id | T]},
113 case (IdDiff > 0) and (IdDiff < 16) of
114 true ->
115 write(This1, {byte, (IdDiff bsl 4) bor CompactType});
116 false ->
117 {This2, ok} = write(This1, {byte, CompactType}),
118 write(This2, {i16, Id})
119 end.
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900120
121-spec to_zigzag(integer()) -> non_neg_integer().
Nobuaki Sukegawae1b85402016-07-18 21:27:27 +0900122to_zigzag(Value) -> 16#FFFFFFFFFFFFFFFF band ((Value bsl 1) bxor (Value bsr 63)).
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900123
124-spec from_zigzag(non_neg_integer()) -> integer().
125from_zigzag(Value) -> (Value bsr 1) bxor -(Value band 1).
126
127-spec to_varint(non_neg_integer(), iolist()) -> iolist().
128to_varint(Value, Acc) when (Value < 16#80) -> [Acc, Value];
Sergei Elin45764092022-09-23 23:21:31 +0300129to_varint(Value, Acc) -> to_varint(Value bsr 7, [Acc, ((Value band 16#7F) bor 16#80)]).
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900130
Sergei Elin45764092022-09-23 23:21:31 +0300131-spec read_varint(t_compact(), non_neg_integer(), non_neg_integer()) ->
132 {t_compact(), {'ok', integer()}}.
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900133read_varint(This0, Acc, Count) ->
Sergei Elin45764092022-09-23 23:21:31 +0300134 {This1, {ok, Byte}} = read(This0, byte),
135 case (Byte band 16#80) of
136 0 -> {This1, {ok, (Byte bsl (7 * Count)) + Acc}};
137 _ -> read_varint(This1, ((Byte band 16#7f) bsl (7 * Count)) + Acc, Count + 1)
138 end.
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900139
140write(This0, #protocol_message_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300141 name = Name,
142 type = Type,
143 seqid = Seqid
144}) ->
145 {This1, ok} = write(This0, {byte, ?PROTOCOL_ID}),
146 {This2, ok} = write(
147 This1, {byte, (?VERSION_1 band ?VERSION_MASK) bor (Type bsl ?TYPE_SHIFT_AMOUNT)}
148 ),
149 {This3, ok} = write(This2, {ui32, Seqid}),
150 {This4, ok} = write(This3, {string, Name}),
151 {This4, ok};
152write(This, message_end) ->
153 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900154write(This0, #protocol_field_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300155 name = _Name,
156 type = Type,
157 id = Id
158}) when
159 (Type =:= ?tType_BOOL)
160->
161 {This0#t_compact{write_id = Id}, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900162write(This0, #protocol_field_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300163 name = _Name,
164 type = Type,
165 id = Id
166}) ->
167 write_field_begin(This0, typeid_to_compact(Type), Id);
168write(This, field_stop) ->
169 write(This, {byte, ?tType_STOP});
170write(This, field_end) ->
171 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900172write(This0, #protocol_map_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300173 ktype = _Ktype,
174 vtype = _Vtype,
175 size = Size
176}) when
177 Size =:= 0
178->
179 write(This0, {byte, 0});
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900180write(This0, #protocol_map_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300181 ktype = Ktype,
182 vtype = Vtype,
183 size = Size
184}) ->
185 {This1, ok} = write(This0, {ui32, Size}),
186 write(This1, {byte, (typeid_to_compact(Ktype) bsl 4) bor typeid_to_compact(Vtype)});
187write(This, map_end) ->
188 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900189write(This0, #protocol_list_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300190 etype = Etype,
191 size = Size
192}) when
193 Size < 16#f
194->
195 write(This0, {byte, (Size bsl 4) bor typeid_to_compact(Etype)});
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900196write(This0, #protocol_list_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300197 etype = Etype,
198 size = Size
199}) ->
200 {This1, ok} = write(This0, {byte, 16#f0 bor typeid_to_compact(Etype)}),
201 write(This1, {ui32, Size});
202write(This, list_end) ->
203 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900204write(This0, #protocol_set_begin{
Sergei Elin45764092022-09-23 23:21:31 +0300205 etype = Etype,
206 size = Size
207}) ->
208 write(This0, #protocol_list_begin{etype = Etype, size = Size});
209write(This, set_end) ->
210 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900211write(This = #t_compact{write_stack = Stack}, #protocol_struct_begin{}) ->
Sergei Elin45764092022-09-23 23:21:31 +0300212 {This#t_compact{write_stack = [0 | Stack]}, ok};
213write(This = #t_compact{write_stack = [_ | T]}, struct_end) ->
214 {This#t_compact{write_stack = T}, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900215write(This = #t_compact{write_id = ?ID_NONE}, {bool, Value}) ->
Sergei Elin45764092022-09-23 23:21:31 +0300216 write(This, {byte, bool_to_cbool(Value)});
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900217write(This0 = #t_compact{write_id = Id}, {bool, Value}) ->
Sergei Elin45764092022-09-23 23:21:31 +0300218 {This1, ok} = write_field_begin(This0, bool_to_cbool(Value), Id),
219 {This1#t_compact{write_id = ?ID_NONE}, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900220write(This, {byte, Value}) when is_integer(Value) ->
Sergei Elin45764092022-09-23 23:21:31 +0300221 write(This, <<Value:8/big-signed>>);
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900222write(This, {i16, Value}) when is_integer(Value) -> write(This, to_varint(to_zigzag(Value), []));
223write(This, {ui32, Value}) when is_integer(Value) -> write(This, to_varint(Value, []));
224write(This, {i32, Value}) when is_integer(Value) ->
Sergei Elin45764092022-09-23 23:21:31 +0300225 write(This, to_varint(to_zigzag(Value), []));
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900226write(This, {i64, Value}) when is_integer(Value) -> write(This, to_varint(to_zigzag(Value), []));
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900227write(This, {double, Double}) ->
Sergei Elin45764092022-09-23 23:21:31 +0300228 write(This, <<Double:64/float-signed-little>>);
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900229write(This0, {string, Str}) when is_list(Str) ->
Sergei Elin45764092022-09-23 23:21:31 +0300230 % TODO: limit length
231 {This1, ok} = write(This0, {ui32, length(Str)}),
232 {This2, ok} = write(This1, list_to_binary(Str)),
233 {This2, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900234write(This0, {string, Bin}) when is_binary(Bin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300235 % TODO: limit length
236 {This1, ok} = write(This0, {ui32, size(Bin)}),
237 {This2, ok} = write(This1, Bin),
238 {This2, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900239%% Data :: iolist()
240write(This = #t_compact{transport = Trans}, Data) ->
Sergei Elin45764092022-09-23 23:21:31 +0300241 {NewTransport, Result} = thrift_transport:write(Trans, Data),
242 {This#t_compact{transport = NewTransport}, Result}.
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900243
244%%
245%%
246
247read(This0, message_begin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300248 {This1, {ok, ?PROTOCOL_ID}} = read(This0, ubyte),
249 {This2, {ok, VerAndType}} = read(This1, ubyte),
250 ?VERSION_1 = VerAndType band ?VERSION_MASK,
251 {This3, {ok, SeqId}} = read(This2, ui32),
252 {This4, {ok, Name}} = read(This3, string),
253 {This4, #protocol_message_begin{
254 name = binary_to_list(Name),
255 type = (VerAndType bsr ?TYPE_SHIFT_AMOUNT) band ?TYPE_BITS,
256 seqid = SeqId
257 }};
258read(This, message_end) ->
259 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900260read(This = #t_compact{read_stack = Stack}, struct_begin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300261 {This#t_compact{read_stack = [0 | Stack]}, ok};
262read(This = #t_compact{read_stack = [_H | T]}, struct_end) ->
263 {This#t_compact{read_stack = T}, ok};
264read(This0 = #t_compact{read_stack = [LastId | T]}, field_begin) ->
265 {This1, {ok, Byte}} = read(This0, ubyte),
266 case Byte band 16#f of
267 CompactType = ?tType_STOP ->
268 {This1, #protocol_field_begin{type = CompactType}};
269 CompactType ->
270 {This2, {ok, Id}} =
271 case Byte bsr 4 of
272 0 -> read(This1, i16);
273 IdDiff -> {This1, {ok, LastId + IdDiff}}
274 end,
275 case compact_to_typeid(CompactType) of
276 ?tType_BOOL ->
277 {
278 This2#t_compact{
279 read_stack = [Id | T], read_value = CompactType
280 },
281 #protocol_field_begin{type = ?tType_BOOL, id = Id}
282 };
283 Type ->
284 {This2#t_compact{read_stack = [Id | T]}, #protocol_field_begin{
285 type = Type, id = Id
286 }}
287 end
288 end;
289read(This, field_end) ->
290 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900291read(This0, map_begin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300292 {This1, {ok, Size}} = read(This0, ui32),
293 {This2, {ok, KV}} =
294 case Size of
295 0 -> {This1, {ok, 0}};
296 _ -> read(This1, ubyte)
297 end,
298 {This2, #protocol_map_begin{
299 ktype = compact_to_typeid(KV bsr 4),
300 vtype = compact_to_typeid(KV band 16#f),
301 size = Size
302 }};
303read(This, map_end) ->
304 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900305read(This0, list_begin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300306 {This1, {ok, SizeAndType}} = read(This0, ubyte),
307 {This2, {ok, Size}} =
308 case (SizeAndType bsr 4) band 16#f of
309 16#f -> read(This1, ui32);
310 Else -> {This1, {ok, Else}}
311 end,
312 {This2, #protocol_list_begin{
313 etype = compact_to_typeid(SizeAndType band 16#f),
314 size = Size
315 }};
316read(This, list_end) ->
317 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900318read(This0, set_begin) ->
Sergei Elin45764092022-09-23 23:21:31 +0300319 {This1, {ok, SizeAndType}} = read(This0, ubyte),
320 {This2, {ok, Size}} =
321 case (SizeAndType bsr 4) band 16#f of
322 16#f -> read(This1, ui32);
323 Else -> {This1, {ok, Else}}
324 end,
325 {This2, #protocol_set_begin{
326 etype = compact_to_typeid(SizeAndType band 16#f),
327 size = Size
328 }};
329read(This, set_end) ->
330 {This, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900331read(This0, field_stop) ->
Sergei Elin45764092022-09-23 23:21:31 +0300332 {This1, {ok, ?tType_STOP}} = read(This0, ubyte),
333 {This1, ok};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900334%%
335
336read(This0 = #t_compact{read_value = ?CBOOL_NONE}, bool) ->
Sergei Elin45764092022-09-23 23:21:31 +0300337 {This1, {ok, Byte}} = read(This0, ubyte),
338 {This1, {ok, cbool_to_bool(Byte)}};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900339read(This0 = #t_compact{read_value = Bool}, bool) ->
Sergei Elin45764092022-09-23 23:21:31 +0300340 {This0#t_compact{read_value = ?CBOOL_NONE}, {ok, cbool_to_bool(Bool)}};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900341read(This0, ubyte) ->
Sergei Elin45764092022-09-23 23:21:31 +0300342 {This1, {ok, <<Val:8/integer-unsigned-big, _/binary>>}} = read_data(This0, 1),
343 {This1, {ok, Val}};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900344read(This0, byte) ->
Sergei Elin45764092022-09-23 23:21:31 +0300345 {This1, Bytes} = read_data(This0, 1),
346 case Bytes of
347 {ok, <<Val:8/integer-signed-big, _/binary>>} -> {This1, {ok, Val}};
348 Else -> {This1, Else}
349 end;
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900350read(This0, i16) ->
Sergei Elin45764092022-09-23 23:21:31 +0300351 {This1, {ok, Zigzag}} = read_varint(This0, 0, 0),
352 {This1, {ok, from_zigzag(Zigzag)}};
353read(This0, ui32) ->
354 read_varint(This0, 0, 0);
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900355read(This0, i32) ->
Sergei Elin45764092022-09-23 23:21:31 +0300356 {This1, {ok, Zigzag}} = read_varint(This0, 0, 0),
357 {This1, {ok, from_zigzag(Zigzag)}};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900358read(This0, i64) ->
Sergei Elin45764092022-09-23 23:21:31 +0300359 {This1, {ok, Zigzag}} = read_varint(This0, 0, 0),
360 {This1, {ok, from_zigzag(Zigzag)}};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900361read(This0, double) ->
Sergei Elin45764092022-09-23 23:21:31 +0300362 {This1, Bytes} = read_data(This0, 8),
363 case Bytes of
364 {ok, <<Val:64/float-signed-little, _/binary>>} -> {This1, {ok, Val}};
365 Else -> {This1, Else}
366 end;
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900367% returns a binary directly, call binary_to_list if necessary
368read(This0, string) ->
Sergei Elin45764092022-09-23 23:21:31 +0300369 {This1, {ok, Sz}} = read(This0, ui32),
370 read_data(This1, Sz).
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900371
372-spec read_data(#t_compact{}, non_neg_integer()) ->
373 {#t_compact{}, {ok, binary()} | {error, _Reason}}.
Sergei Elin45764092022-09-23 23:21:31 +0300374read_data(This, 0) ->
375 {This, {ok, <<>>}};
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900376read_data(This = #t_compact{transport = Trans}, Len) when is_integer(Len) andalso Len > 0 ->
377 {NewTransport, Result} = thrift_transport:read(Trans, Len),
378 {This#t_compact{transport = NewTransport}, Result}.
379
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900380%%%% FACTORY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
381
382%% returns a (fun() -> thrift_protocol())
383new_protocol_factory(TransportFactory, _Options) ->
Sergei Elin45764092022-09-23 23:21:31 +0300384 F = fun() ->
385 case TransportFactory() of
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900386 {ok, Transport} ->
Sergei Elin45764092022-09-23 23:21:31 +0300387 thrift_compact_protocol:new(
388 Transport,
389 []
390 );
Nobuaki Sukegawab31f0902015-11-01 17:00:34 +0900391 {error, Error} ->
Sergei Elin45764092022-09-23 23:21:31 +0300392 {error, Error}
393 end
394 end,
395 {ok, F}.