Current section
Files
Jump to
Current section
Files
src/thrifty@message.erl
-module(thrifty@message).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/thrifty/message.gleam").
-export([encode_message_header/1, decode_message_header/2]).
-export_type([message_type/0, message_header/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type message_type() :: call | reply | exception | oneway.
-type message_header() :: {message_header, binary(), message_type(), integer()}.
-file("src/thrifty/message.gleam", 181).
?DOC(
" Convert a `MessageType` to the protocol numeric value used in the\n"
" version/type byte.\n"
"\n"
" Inputs\n"
" - `mt`: message type enum.\n"
"\n"
" Outputs\n"
" - Integer value used in encoding the version/type byte.\n"
).
-spec message_type_to_int(message_type()) -> integer().
message_type_to_int(Mt) ->
case Mt of
call ->
1;
reply ->
2;
exception ->
3;
oneway ->
4
end.
-file("src/thrifty/message.gleam", 50).
?DOC(
" Encode a Thrift message header to a `BitArray` using Compact Protocol\n"
" framing.\n"
"\n"
" Inputs\n"
" - `header`: a `MessageHeader` record with name, message_type and sequence id.\n"
"\n"
" Outputs\n"
" - Returns a `BitArray` containing the protocol id, version/type byte,\n"
" varint-encoded sequence id, varint length of method name and UTF-8\n"
" bytes for the method name.\n"
"\n"
" Format\n"
" - Protocol ID: 0x82 (1 byte)\n"
" - Version & Type: (version << 5) | type (1 byte, version=1)\n"
" - Sequence ID: varint i32\n"
" - Method name: length (varint) + UTF-8 bytes\n"
).
-spec encode_message_header(message_header()) -> bitstring().
encode_message_header(Header) ->
Protocol_id = 16#82,
Version = 1,
Type_value = message_type_to_int(erlang:element(3, Header)),
Version_and_type = (Version * 32) + Type_value,
Seq_id_u32 = ((erlang:element(4, Header) rem 4294967296) + 4294967296) rem 4294967296,
Seq_id_varint = thrifty@varint:encode_varint(Seq_id_u32),
Name_bytes = begin
_pipe = gleam@string:to_utf_codepoints(erlang:element(2, Header)),
_pipe@1 = gleam_stdlib:utf_codepoint_list_to_string(_pipe),
gleam_stdlib:identity(_pipe@1)
end,
Name_length = erlang:byte_size(Name_bytes),
Name_length_varint = thrifty@varint:encode_varint(Name_length),
<<Protocol_id:8/integer,
Version_and_type:8/integer,
Seq_id_varint/bitstring,
Name_length_varint/bitstring,
Name_bytes/bitstring>>.
-file("src/thrifty/message.gleam", 198).
?DOC(
" Convert a numeric message type to `MessageType`.\n"
"\n"
" Inputs\n"
" - `n`: numeric type extracted from the version/type byte.\n"
"\n"
" Outputs\n"
" - `Ok(MessageType)` when recognized.\n"
" - `Error(types.UnsupportedType(n))` when unknown.\n"
).
-spec int_to_message_type(integer()) -> {ok, message_type()} |
{error, thrifty@types:decode_error()}.
int_to_message_type(N) ->
case N of
1 ->
{ok, call};
2 ->
{ok, reply};
3 ->
{ok, exception};
4 ->
{ok, oneway};
_ ->
{error, {unsupported_type, N}}
end.
-file("src/thrifty/message.gleam", 89).
?DOC(
" Decode a Thrift message header from `data` starting at `byte_pos`.\n"
"\n"
" Inputs\n"
" - `data`: the `BitArray` containing the encoded message header and payload.\n"
" - `byte_pos`: byte offset where the header begins.\n"
"\n"
" Outputs\n"
" - `Ok(#(MessageHeader, next_byte_position))` on success where\n"
" `next_byte_position` points after the method name bytes.\n"
" - `Error(types.DecodeError)` on protocol mismatch, unsupported version,\n"
" invalid varint encodings, or invalid UTF-8 in the method name.\n"
).
-spec decode_message_header(bitstring(), integer()) -> {ok,
{message_header(), integer()}} |
{error, thrifty@types:decode_error()}.
decode_message_header(Data, Byte_pos) ->
case gleam_stdlib:bit_array_slice(Data, Byte_pos, 1) of
{error, _} ->
{error, unexpected_end_of_input};
{ok, <<Protocol_id:8/integer>>} ->
case Protocol_id =:= 16#82 of
false ->
{error,
{invalid_wire_format, <<"Invalid protocol ID"/utf8>>}};
true ->
case gleam_stdlib:bit_array_slice(Data, Byte_pos + 1, 1) of
{error, _} ->
{error, unexpected_end_of_input};
{ok, <<Version_and_type:8/integer>>} ->
Version = Version_and_type div 32,
Type_value = Version_and_type rem 32,
case Version =:= 1 of
false ->
{error,
{invalid_wire_format,
<<"Unsupported version"/utf8>>}};
true ->
case int_to_message_type(Type_value) of
{error, E} ->
{error, E};
{ok, Msg_type} ->
case thrifty@varint:decode_varint(
Data,
Byte_pos + 2
) of
{error, E@1} ->
{error, E@1};
{ok,
{Seq_id_raw, Pos_after_seq}} ->
Seq_id = case Seq_id_raw > 2147483647 of
true ->
Seq_id_raw - 4294967296;
false ->
Seq_id_raw
end,
case thrifty@varint:decode_varint(
Data,
Pos_after_seq
) of
{error, E@2} ->
{error, E@2};
{ok,
{Name_length,
Name_bytes_start}} ->
case gleam_stdlib:bit_array_slice(
Data,
Name_bytes_start,
Name_length
) of
{error, _} ->
{error,
unexpected_end_of_input};
{ok, Name_bytes} ->
case gleam@bit_array:to_string(
Name_bytes
) of
{error,
_} ->
{error,
{invalid_wire_format,
<<"Invalid UTF-8 in method name"/utf8>>}};
{ok,
Name} ->
Next_pos = Name_bytes_start
+ Name_length,
Header = {message_header,
Name,
Msg_type,
Seq_id},
{ok,
{Header,
Next_pos}}
end
end
end
end
end
end;
{ok, _} ->
{error,
{invalid_wire_format,
<<"Invalid version/type byte"/utf8>>}}
end
end;
{ok, _} ->
{error, {invalid_wire_format, <<"Invalid protocol ID byte"/utf8>>}}
end.