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