src/marina_types.erl

-module(marina_types).
-include("marina_internal.hrl").

-compile(inline).
-compile({inline_size, 512}).

-export([
    decode_bytes/1,
    decode_double/1,
    decode_float/1,
    decode_int/1,
    decode_long/1,
    decode_long_string/1,
    decode_long_string_set/1,
    decode_number_list/1,
    decode_short/1,
    decode_short_bytes/1,
    decode_string/1,
    decode_string_list/1,
    decode_string_map/1,
    decode_string_multimap/1,
    decode_tinyint/1,
    decode_uuid/1,
    encode_boolean/1,
    encode_bytes/1,
    encode_double/1,
    encode_float/1,
    encode_int/1,
    encode_list/1,
    encode_long/1,
    encode_long_string/1,
    encode_short/1,
    encode_short_bytes/1,
    encode_string/1,
    encode_string_list/1,
    encode_string_map/1,
    encode_string_multimap/1,
    encode_tinyint/1
]).

%% public
-spec decode_bytes(binary()) -> {null, binary()} | {binary(), binary()}.

decode_bytes(<<255, 255, 255, 255, Rest/binary>>) ->
    {null, Rest};
decode_bytes(<<Pos:32, Value:Pos/binary, Rest/binary>>) ->
    {Value, Rest}.

-spec decode_double(binary()) -> {float(), binary()}.

decode_double(<<Value:64/float, Rest/binary>>) ->
    {Value, Rest}.

-spec decode_float(binary()) -> {float(), binary()}.

decode_float(<<Value:32/float, Rest/binary>>) ->
    {Value, Rest}.

-spec decode_int(binary()) -> {integer(), binary()}.

decode_int(<<Value:32/signed, Rest/binary>>) ->
    {Value, Rest}.

-spec decode_long(binary()) -> {integer(), binary()}.

decode_long(<<Value:64/signed, Rest/binary>>) ->
    {Value, Rest}.

-spec decode_long_string(binary()) -> {binary(), binary()}.

decode_long_string(Bin) ->
    decode_bytes(Bin).

-spec decode_long_string_set(binary()) -> {[binary()], binary()}.

decode_long_string_set(<<Length:32, Rest/binary>>) ->
    decode_long_string_set(Rest, Length, []).

-spec decode_number_list(binary()) -> {[integer()], binary()}.

decode_number_list(<<Length:32, Rest/binary>>) ->
    decode_number_list(Rest, Length, []).

-spec decode_short(binary()) -> {integer(), binary()}.

decode_short(<<Value:16/signed, Rest/binary>>) ->
    {Value, Rest}.

-spec decode_short_bytes(binary()) -> {binary(), binary()}.

decode_short_bytes(<<255, 255, Rest/binary>>) ->
    {null, Rest};
decode_short_bytes(<<Pos:16, Value:Pos/binary, Rest/binary>>) ->
    {Value, Rest}.

-spec decode_string(binary()) -> {binary(), binary()}.

decode_string(Bin) ->
    decode_short_bytes(Bin).

-spec decode_string_list(binary()) -> {[binary()], binary()}.

decode_string_list(<<Length:16, Rest/binary>>) ->
    decode_string_list(Rest, Length, []).

-spec decode_string_map(binary()) -> {[{binary(), binary()}], binary()}.

decode_string_map(<<Length:16, Rest/binary>>) ->
    decode_string_map(Rest, Length, []).

-spec decode_string_multimap(binary()) -> {[{binary(), [binary()]}], binary()}.

decode_string_multimap(<<Length:16, Rest/binary>>) ->
    decode_string_multimap(Rest, Length, []).

-spec decode_tinyint(binary()) -> {integer(), binary()}.

decode_tinyint(<<Value:8/signed, Rest/binary>>) ->
    {Value, Rest}.

-spec decode_uuid(binary()) -> {binary(), binary()}.

decode_uuid(<<Value:16/binary, Rest/binary>>) ->
    {Value, Rest}.

-spec encode_boolean(boolean()) -> binary().

encode_boolean(false) -> <<0>>;
encode_boolean(true) -> <<1>>.

-spec encode_bytes(binary()) -> binary().

encode_bytes(null) ->
    <<255, 255, 255, 255>>;
encode_bytes(Value) ->
    <<(encode_int(size(Value)))/binary, Value/binary>>.

-spec encode_double(float()) -> binary().

encode_double(Value) ->
    <<Value:64/float>>.

-spec encode_float(float()) -> binary().

encode_float(Value) ->
    <<Value:32/float>>.

-spec encode_int(integer()) -> binary().

encode_int(Value) ->
    <<Value:32/signed>>.

-spec encode_list([binary()]) -> binary().

encode_list(Values) ->
    iolist_to_binary([encode_short(length(Values)), Values]).

-spec encode_long(integer()) -> binary().

encode_long(Value) ->
    <<Value:64/signed>>.

-spec encode_long_string(binary()) -> binary().

encode_long_string(Value) ->
    <<(encode_int(size(Value)))/binary, Value/binary>>.

-spec encode_short(integer()) -> binary().
encode_short(Value) ->
    <<Value:16/signed>>.

-spec encode_short_bytes(binary()) -> binary().

encode_short_bytes(null) ->
    <<255, 255>>;
encode_short_bytes(Value) ->
    <<(encode_short(size(Value)))/binary, Value/binary>>.

-spec encode_string(binary()) -> binary().

encode_string(Value) ->
    <<(encode_short(size(Value)))/binary, Value/binary>>.

-spec encode_string_list([binary()]) -> binary().

encode_string_list(Values) ->
    EncodedValues = [encode_string(Value) || Value <- Values],
    iolist_to_binary([encode_short(length(Values)), EncodedValues]).

-spec encode_string_map([{binary(), binary()}]) -> binary().

encode_string_map(KeyValues) ->
    encode_string_map(KeyValues, []).

-spec encode_string_multimap([{binary(), [binary()]}]) -> binary().

encode_string_multimap(KeyValues) ->
    encode_string_multimap(KeyValues, []).

-spec encode_tinyint(integer()) -> binary().

encode_tinyint(Value) ->
    <<Value:8/signed>>.

%% private
decode_number_list(Bin, 0, Acc) ->
    {lists:reverse(Acc), Bin};
decode_number_list(<<Pos:32, Number:(Pos * 8), Rest/binary>>, Length, Acc) ->
    decode_number_list(Rest, Length - 1, [Number | Acc]).

decode_long_string_set(Bin, 0, Acc) ->
    {lists:reverse(Acc), Bin};
decode_long_string_set(Bin, Length, Acc) ->
    {String, Rest} = decode_bytes(Bin),
    decode_long_string_set(Rest, Length - 1, [String | Acc]).

decode_string_list(Bin, 0, Acc) ->
    {lists:reverse(Acc), Bin};
decode_string_list(<<Pos:16, String:Pos/binary, Rest/binary>>, Length, Acc) ->
    decode_string_list(Rest, Length - 1, [String | Acc]).

decode_string_map(Bin, 0, Acc) ->
    {lists:reverse(Acc), Bin};
decode_string_map(<<Pos:16, Key:Pos/binary, Pos2:16, Value:Pos2/binary,
    Rest/binary>>, Length, Acc) ->

    decode_string_map(Rest, Length - 1, [{Key, Value} | Acc]).

decode_string_multimap(Bin, 0, Acc) ->
    {lists:reverse(Acc), Bin};
decode_string_multimap(<<Pos:16, Key:Pos/binary, Rest/binary>>, Length, Acc) ->
    {Values, Rest2} = decode_string_list(Rest),
    decode_string_multimap(Rest2, Length - 1, [{Key, Values} | Acc]).

encode_string_map([], Acc) ->
    iolist_to_binary([encode_short(length(Acc)), lists:reverse(Acc)]);
encode_string_map([{Key, Value} | Rest], Acc) ->
    encode_string_map(Rest, [[encode_string(Key), encode_string(Value)] | Acc]).

encode_string_multimap([], Acc) ->
    encode_string_map([], Acc);
encode_string_multimap([{Key, Values} | Rest], Acc) ->
    encode_string_multimap(Rest, [[encode_string(Key),
    encode_string_list(Values)] | Acc]).