Current section
Files
Jump to
Current section
Files
src/thrifty@container.erl
-module(thrifty@container).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/thrifty/container.gleam").
-export([element_type_to_code/1, code_to_element_type/1, encode_list_header/2, decode_list_header/2, encode_map_header/3, decode_map_header/2]).
-export_type([element_type/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 element_type() :: bool_type |
i8_type |
i16_type |
i32_type |
i64_type |
double_type |
binary_type |
list_type |
set_type |
map_type |
struct_type.
-file("src/thrifty/container.gleam", 83).
?DOC(
" Convert ElementType to compact protocol type code (4 bits).\n"
"\n"
" Inputs\n"
" - `t`: the `ElementType` to convert.\n"
"\n"
" Outputs\n"
" - Returns the 4-bit integer code used in Compact Protocol headers.\n"
"\n"
" Complexity\n"
" - O(1)\n"
).
-spec element_type_to_code(element_type()) -> integer().
element_type_to_code(T) ->
case T of
bool_type ->
2;
i8_type ->
3;
i16_type ->
4;
i32_type ->
5;
i64_type ->
6;
double_type ->
7;
binary_type ->
8;
list_type ->
9;
set_type ->
10;
map_type ->
11;
struct_type ->
12
end.
-file("src/thrifty/container.gleam", 112).
?DOC(
" Convert a compact protocol 4-bit type code to `ElementType`.\n"
"\n"
" Inputs\n"
" - `code`: integer value extracted from a header nibble (0-15).\n"
"\n"
" Outputs\n"
" - `Ok(ElementType)` when the code is recognized.\n"
" - `Error(types.UnsupportedType(code))` when the code is unknown.\n"
"\n"
" Error modes\n"
" - Returns `types.UnsupportedType` for unknown codes.\n"
).
-spec code_to_element_type(integer()) -> {ok, element_type()} |
{error, thrifty@types:decode_error()}.
code_to_element_type(Code) ->
case Code of
1 ->
{ok, bool_type};
2 ->
{ok, bool_type};
3 ->
{ok, i8_type};
4 ->
{ok, i16_type};
5 ->
{ok, i32_type};
6 ->
{ok, i64_type};
7 ->
{ok, double_type};
8 ->
{ok, binary_type};
9 ->
{ok, list_type};
10 ->
{ok, set_type};
11 ->
{ok, map_type};
12 ->
{ok, struct_type};
_ ->
{error, {unsupported_type, Code}}
end.
-file("src/thrifty/container.gleam", 143).
?DOC(
" Encode a list/set header consisting of size and element type.\n"
"\n"
" Inputs\n"
" - `size`: number of elements in the list/set (non-negative).\n"
" - `elem_type`: the `ElementType` of elements.\n"
"\n"
" Outputs\n"
" - Returns a `BitArray` containing either the short-form header (1 byte)\n"
" when `size < 15` or the long-form header (1 byte + varint) otherwise.\n"
"\n"
" Complexity\n"
" - O(1) (plus cost of varint encoding when required).\n"
).
-spec encode_list_header(integer(), element_type()) -> bitstring().
encode_list_header(Size, Elem_type) ->
Type_code = element_type_to_code(Elem_type),
case Size < 15 of
true ->
Header_byte = (Size * 16) + Type_code,
<<Header_byte:8/integer>>;
false ->
Header_byte@1 = (15 * 16) + Type_code,
Size_varint = thrifty@varint:encode_varint(Size),
<<Header_byte@1:8/integer, Size_varint/bitstring>>
end.
-file("src/thrifty/container.gleam", 171).
?DOC(
" Decode a list/set header from a `BitArray` at `byte_position`.\n"
"\n"
" Inputs\n"
" - `data`: the `BitArray` containing the encoded header and payload.\n"
" - `byte_position`: byte offset where the header begins.\n"
"\n"
" Outputs\n"
" - `Ok(#(size, element_type, next_byte_position))` on success where\n"
" `next_byte_position` is the byte index immediately after the header.\n"
" - `Error(types.DecodeError)` on malformed input or unexpected end.\n"
).
-spec decode_list_header(bitstring(), integer()) -> {ok,
{integer(), element_type(), integer()}} |
{error, thrifty@types:decode_error()}.
decode_list_header(Data, Byte_position) ->
case gleam_stdlib:bit_array_slice(Data, Byte_position, 1) of
{error, _} ->
{error, unexpected_end_of_input};
{ok, Header_bits} ->
case Header_bits of
<<Header_byte:8/integer>> ->
Size_nibble = Header_byte div 16,
Type_nibble = Header_byte rem 16,
case code_to_element_type(Type_nibble) of
{error, E} ->
{error, E};
{ok, Elem_type} ->
case Size_nibble =:= 15 of
true ->
case thrifty@varint:decode_varint(
Data,
Byte_position + 1
) of
{error, E@1} ->
{error, E@1};
{ok, {Size, Next_pos}} ->
{ok, {Size, Elem_type, Next_pos}}
end;
false ->
{ok,
{Size_nibble,
Elem_type,
Byte_position + 1}}
end
end;
_ ->
{error,
{invalid_wire_format,
<<"Invalid list header byte"/utf8>>}}
end
end.
-file("src/thrifty/container.gleam", 219).
?DOC(
" Encode a map header (size + key type + value type).\n"
"\n"
" Inputs\n"
" - `size`: number of entries in the map (non-negative).\n"
" - `key_type`: the `ElementType` used for keys.\n"
" - `value_type`: the `ElementType` used for values.\n"
"\n"
" Outputs\n"
" - For empty maps returns a single zero byte.\n"
" - For non-empty maps returns varint(size) followed by a single types byte\n"
" where the high nibble is key type and the low nibble is value type.\n"
).
-spec encode_map_header(integer(), element_type(), element_type()) -> bitstring().
encode_map_header(Size, Key_type, Value_type) ->
case Size =:= 0 of
true ->
<<0:8/integer>>;
false ->
Size_varint = thrifty@varint:encode_varint(Size),
Key_code = element_type_to_code(Key_type),
Value_code = element_type_to_code(Value_type),
Types_byte = (Key_code * 16) + Value_code,
<<Size_varint/bitstring, Types_byte:8/integer>>
end.
-file("src/thrifty/container.gleam", 246).
?DOC(
" Decode a map header from `data` at `byte_position`.\n"
"\n"
" Inputs\n"
" - `data`: the `BitArray` containing the encoded header and payload.\n"
" - `byte_position`: byte offset where the header begins.\n"
"\n"
" Outputs\n"
" - `Ok(#(size, key_type, value_type, next_byte_position))` where `next_byte_position`\n"
" points after the types byte (or after the varint for empty maps).\n"
" - `Error(types.DecodeError)` on malformed input or unexpected end.\n"
).
-spec decode_map_header(bitstring(), integer()) -> {ok,
{integer(), element_type(), element_type(), integer()}} |
{error, thrifty@types:decode_error()}.
decode_map_header(Data, Byte_position) ->
case thrifty@varint:decode_varint(Data, Byte_position) of
{error, E} ->
{error, E};
{ok, {Size, Next_pos}} ->
case Size =:= 0 of
true ->
{ok, {0, i32_type, i32_type, Next_pos}};
false ->
case gleam_stdlib:bit_array_slice(Data, Next_pos, 1) of
{error, _} ->
{error, unexpected_end_of_input};
{ok, Types_bits} ->
case Types_bits of
<<Types_byte:8/integer>> ->
Key_code = Types_byte div 16,
Value_code = Types_byte rem 16,
case code_to_element_type(Key_code) of
{error, E@1} ->
{error, E@1};
{ok, Key_type} ->
case code_to_element_type(
Value_code
) of
{error, E@2} ->
{error, E@2};
{ok, Value_type} ->
{ok,
{Size,
Key_type,
Value_type,
Next_pos + 1}}
end
end;
_ ->
{error,
{invalid_wire_format,
<<"Invalid map types byte"/utf8>>}}
end
end
end
end.