1%% 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_TRUE, 1). 38-define(CBOOL_FALSE, 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_BOOL) -> 16#2; 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_DOUBLE) -> 16#7; 64typeid_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(). 117to_zigzag(Value) -> 16#FFFFFFFFFFFFFFFF band ((Value bsl 1) bxor (Value bsr 63)). 118 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}) -> 222 write(This, <<Double:64/float-signed-little>>); 223 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 359 {ok, <<Val:64/float-signed-little, _/binary>>} -> {This1, {ok, Val}}; 360 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}. 391