Current section
Files
Jump to
Current section
Files
src/thrifty@varint.erl
-module(thrifty@varint).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/thrifty/varint.gleam").
-export([encode_varint/1, decode_varint/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/thrifty/varint.gleam", 75).
?DOC(
" Convert a list of byte integers to a `BitArray` by serial concatenation.\n"
"\n"
" Inputs\n"
" - `bytes`: list of integers each 0..255.\n"
"\n"
" Outputs\n"
" - `BitArray` concatenating the provided bytes.\n"
).
-spec list_to_bitarray(list(integer())) -> bitstring().
list_to_bitarray(Bytes) ->
case Bytes of
[] ->
<<>>;
[H | T] ->
Rest_bits = list_to_bitarray(T),
<<H:8/integer, Rest_bits/bitstring>>
end.
-file("src/thrifty/varint.gleam", 52).
?DOC(
" Internal loop that emits varint bytes (LSB-first) into an accumulator.\n"
"\n"
" Inputs\n"
" - `n`: remaining unsigned integer to encode.\n"
" - `acc`: accumulator list of emitted bytes (in reverse order).\n"
"\n"
" Outputs\n"
" - List of byte integers representing the varint, in emission order when\n"
" reversed by the caller.\n"
).
-spec encode_varint_loop(integer(), list(integer())) -> list(integer()).
encode_varint_loop(N, Acc) ->
Low7 = N rem 128,
Rest = N div 128,
case Rest > 0 of
true ->
Byte = Low7 + 128,
encode_varint_loop(Rest, [Byte | Acc]);
false ->
[Low7 | Acc]
end.
-file("src/thrifty/varint.gleam", 29).
?DOC(
" Encode an unsigned integer as a varint BitArray.\n"
" Returns a BitArray of 1-10 bytes (for i64).\n"
"\n"
" Varint encoding: 7 bits per byte, MSB as continuation bit.\n"
" Bytes are emitted LSB first.\n"
"\n"
" Panics if `n` is negative. Use callers that guarantee non-negative input\n"
" (e.g. ZigZag-encode signed integers before calling this function).\n"
).
-spec encode_varint(integer()) -> bitstring().
encode_varint(N) ->
case N < 0 of
true ->
erlang:error(#{gleam_error => panic,
message => (<<"encode_varint requires a non-negative integer, got "/utf8,
(erlang:integer_to_binary(N))/binary>>),
file => <<?FILEPATH/utf8>>,
module => <<"thrifty/varint"/utf8>>,
function => <<"encode_varint"/utf8>>,
line => 32});
false ->
_pipe = encode_varint_loop(N, []),
_pipe@1 = lists:reverse(_pipe),
list_to_bitarray(_pipe@1)
end.
-file("src/thrifty/varint.gleam", 110).
?DOC(
" Recursive helper that decodes varint bytes accumulating the result.\n"
"\n"
" Inputs\n"
" - `data`: source `BitArray`.\n"
" - `byte_pos`: current byte offset to read.\n"
" - `value`: accumulated integer value so far.\n"
" - `multiplier`: current multiplier (powers of 128) for incoming 7-bit groups.\n"
" - `byte_count`: number of bytes consumed so far (used to bound length).\n"
"\n"
" Outputs\n"
" - `Ok(#(value, next_byte_pos))` on successful termination.\n"
" - `Error(types.InvalidVarint)` when maximum byte length is exceeded.\n"
" - `Error(types.UnexpectedEndOfInput)` when data runs out mid-varint.\n"
).
-spec decode_varint_loop(
bitstring(),
integer(),
integer(),
integer(),
integer()
) -> {ok, {integer(), integer()}} | {error, thrifty@types:decode_error()}.
decode_varint_loop(Data, Byte_pos, Value, Multiplier, Byte_count) ->
case Byte_count >= 10 of
true ->
{error, invalid_varint};
false ->
case gleam_stdlib:bit_array_slice(Data, Byte_pos, 1) of
{error, _} ->
{error, unexpected_end_of_input};
{ok, Byte_bits} ->
case Byte_bits of
<<B:8/integer>> ->
Low7 = B rem 128,
New_value = Value + (Low7 * Multiplier),
Has_more = B >= 128,
case Has_more of
true ->
decode_varint_loop(
Data,
Byte_pos + 1,
New_value,
Multiplier * 128,
Byte_count + 1
);
false ->
Next_byte_pos = Byte_pos + 1,
{ok, {New_value, Next_byte_pos}}
end;
_ ->
{error,
{invalid_wire_format,
<<"Invalid byte in varint"/utf8>>}}
end
end
end.
-file("src/thrifty/varint.gleam", 90).
?DOC(
" Decode a varint from a BitArray starting at a given byte position.\n"
" Returns Ok(#(value, next_byte_position)) on success, or Error on failure.\n"
"\n"
" Max length: 10 bytes for i64, 5 bytes for i32.\n"
" Reads bytes LSB first, accumulating 7-bit groups.\n"
).
-spec decode_varint(bitstring(), integer()) -> {ok, {integer(), integer()}} |
{error, thrifty@types:decode_error()}.
decode_varint(Data, Byte_position) ->
decode_varint_loop(Data, Byte_position, 0, 1, 0).