%% @private -module(thrift_protocol_binary). -include("thrift_protocol.hrl"). -export([encode_message/1, decode_message/1]). -define(VERSION, 1). -spec decode_message(binary()) -> {thrift_protocol:message(), binary()}. decode_message(<<1:1, ?VERSION:15, _:8, 0:5, TypeByte:3, NameLen:32, Name:NameLen/binary, SeqId:32/signed, Rest0/binary>>) -> Type = thrift_protocol_byte:to_message_type(TypeByte), {Body, Rest1} = decode_struct(Rest0, #{}), Message = #thrift_protocol_message{ method_name = Name, message_type = Type, sequence_id = SeqId, body = Body }, {Message, Rest1}. -spec decode_struct(binary(), Fields) -> {thrift_protocol:struct(), binary()} when Fields :: #{thrift_protocol:field_id() => thrift_protocol:data()}. decode_struct(<<0:8, Rest/binary>>, Fields) -> {#thrift_protocol_struct{fields = Fields}, Rest}; decode_struct(<>, Fields0) -> Type = thrift_protocol_byte:to_data_type(TypeByte), {Data, Rest1} = decode_data(Type, Rest0), Fields1 = maps:put(Id, Data, Fields0), decode_struct(Rest1, Fields1). -spec decode_elements(binary(), thrift_protocol:data_type(), non_neg_integer(), [thrift_protocol:data()]) -> {[thrift_protocol:data()], binary()}. decode_elements(Bin, _Type, 0, Elements) -> {lists:reverse(Elements), Bin}; decode_elements(Bin, Type, Size, Elements) -> {Element, Rest} = decode_data(Type, Bin), decode_elements(Rest, Type, Size - 1, [Element | Elements]). -spec decode_pairs(binary(), KeyType, ValueType, Size, Acc) -> {Acc, binary()} when KeyType :: thrift_protocol:data_type(), ValueType :: thrift_protocol:data_type(), Size :: non_neg_integer(), Acc :: #{thrift_protocol:data() => thrift_protocol:data()}. decode_pairs(Bin, _, _, 0, Pairs) -> {Pairs, Bin}; decode_pairs(Bin, KeyType, ValueType, Size, Pairs0) -> {Key, Rest0} = decode_data(KeyType, Bin), {Value, Rest1} = decode_data(ValueType, Rest0), Pairs1 = maps:put(Key, Value, Pairs0), decode_pairs(Rest1, KeyType, ValueType, Size - 1, Pairs1). -spec decode_data(thrift_protocol:data_type(), binary()) -> {thrift_protocol:data(), binary()}. decode_data(boolean, <<0, Rest/binary>>) -> {false, Rest}; decode_data(boolean, <<1, Rest/binary>>) -> {true, Rest}; decode_data(i8, <>) -> {{i8, N}, Rest}; decode_data(i16, <>) -> {{i16, N}, Rest}; decode_data(i32, <>) -> {{i32, N}, Rest}; decode_data(i64, <>) -> {{i64, N}, Rest}; decode_data(float, <>) -> {N, Rest}; decode_data(binary, <>) -> {Bin, Rest}; decode_data(struct, Bin) -> decode_struct(Bin, #{}); decode_data(map, <>) -> KeyType = thrift_protocol_byte:to_data_type(K), ValueType = thrift_protocol_byte:to_data_type(V), {Pairs, Rest} = decode_pairs(Bin, KeyType, ValueType, Size, #{}), {#thrift_protocol_map{key_type = KeyType, value_type = ValueType, elements = Pairs}, Rest}; decode_data(set, <>) -> Type = thrift_protocol_byte:to_data_type(TypeByte), {Elements, Rest} = decode_elements(Bin, Type, Size, []), {#thrift_protocol_set{element_type = Type, elements = Elements}, Rest}; decode_data(list, <>) -> Type = thrift_protocol_byte:to_data_type(TypeByte), {Elements, Rest} = decode_elements(Bin, Type, Size, []), {#thrift_protocol_list{element_type = Type, elements = Elements}, Rest}. -spec encode_message(thrift_protocol:message()) -> iodata(). encode_message(Message) -> #thrift_protocol_message{method_name = Name, sequence_id = SeqId, body = Body} = Message, Type = thrift_protocol_byte:from_message_type(Message#thrift_protocol_message.message_type), [<<1:1, ?VERSION:15, 0:8, 0:5, Type:3, (byte_size(Name)):32, Name/binary, SeqId:32>> | encode_struct(Body)]. -spec encode_struct(thrift_protocol:struct()) -> iodata(). encode_struct(#thrift_protocol_struct{fields = Fields}) -> maps:fold( fun (Id, Data, Acc) -> Type = thrift_protocol_byte:from_data_type(thrift_protocol:data_type(Data)), [<>, encode_data(Data) | Acc] end, [<<0:8>>], Fields). -spec encode_data(thrift_protocol:data()) -> iodata(). encode_data(true) -> <<1:8>>; encode_data(false) -> <<0:8>>; encode_data({i8, N}) -> <>; encode_data({i16, N}) -> <>; encode_data({i32, N}) -> <>; encode_data({i64, N}) -> <>; encode_data(N) when is_float(N)-> <>; encode_data(B) when is_binary(B) -> [<<(byte_size(B)):32>> | B]; encode_data(X = #thrift_protocol_struct{}) -> encode_struct(X); encode_data(#thrift_protocol_map{key_type = KeyType, value_type = ValueType, elements = Elements}) -> IoData = maps:fold( fun (K, V, Acc) -> KeyType = thrift_protocol:data_type(K), ValueType = thrift_protocol:data_type(V), [encode_data(K), encode_data(V) | Acc] end, [], Elements), [<<(thrift_protocol_byte:from_data_type(KeyType)):8, (thrift_protocol_byte:from_data_type(ValueType)):8, (maps:size(Elements)):32>> | IoData]; encode_data(#thrift_protocol_set{element_type = Type, elements = Elements}) -> Size = length(Elements), [<<(thrift_protocol_byte:from_data_type(Type)):8, Size:32>> | [begin Type = thrift_protocol:data_type(E), encode_data(E) end || E <- Elements]]; encode_data(#thrift_protocol_list{element_type = Type, elements = Elements}) -> Size = length(Elements), [<<(thrift_protocol_byte:from_data_type(Type)):8, Size:32>> | [begin Type = thrift_protocol:data_type(E), encode_data(E) end || E <- Elements]].