Skip to main content

src/packkit@internal@huf.erl

-module(packkit@internal@huf).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit/internal/huf.gleam").
-export([decode_stream/3, decode_four_streams/3, read_tree/1]).
-export_type([huf_error/0, tree/0, fwd_bit_reader/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.

?MODULEDOC(false).

-type huf_error() :: {huf_truncated, binary()} |
    {huf_invalid_weights, binary()} |
    {huf_bitstream_error, packkit@internal@fse:fse_error()} |
    {huf_unsupported, binary()}.

-type tree() :: {tree,
        integer(),
        gleam@dict:dict(integer(), {integer(), integer()})}.

-type fwd_bit_reader() :: {fwd_bit_reader,
        bitstring(),
        integer(),
        integer(),
        integer()}.

-file("src/packkit/internal/huf.gleam", 77).
?DOC(false).
-spec unpack_direct_weights(bitstring(), integer(), list(integer())) -> list(integer()).
unpack_direct_weights(Bytes, Remaining, Acc) ->
    case {Bytes, Remaining} of
        {_, 0} ->
            lists:reverse(Acc);

        {<<Byte, _/binary>>, 1} ->
            High = erlang:'bsr'(Byte, 4),
            lists:reverse([High | Acc]);

        {<<Byte@1, Rest/binary>>, _} ->
            High@1 = erlang:'bsr'(Byte@1, 4),
            Low = erlang:'band'(Byte@1, 16#0F),
            unpack_direct_weights(Rest, Remaining - 2, [Low, High@1 | Acc]);

        {_, _} ->
            lists:reverse(Acc)
    end.

-file("src/packkit/internal/huf.gleam", 129).
?DOC(false).
-spec sum_powers(list(integer()), integer()) -> integer().
sum_powers(Weights, Acc) ->
    case Weights of
        [] ->
            Acc;

        [0 | Rest] ->
            sum_powers(Rest, Acc);

        [W | Rest@1] ->
            sum_powers(Rest@1, Acc + erlang:'bsl'(1, W - 1))
    end.

-file("src/packkit/internal/huf.gleam", 141).
?DOC(false).
-spec high_bit_loop(integer(), integer()) -> integer().
high_bit_loop(Value, Acc) ->
    case Value of
        0 ->
            Acc;

        _ ->
            high_bit_loop(erlang:'bsr'(Value, 1), Acc + 1)
    end.

-file("src/packkit/internal/huf.gleam", 137).
?DOC(false).
-spec high_bit(integer()) -> integer().
high_bit(Value) ->
    high_bit_loop(Value, -1).

-file("src/packkit/internal/huf.gleam", 108).
?DOC(false).
-spec weights_with_implied_last(list(integer())) -> {ok,
        {list(integer()), integer()}} |
    {error, nil}.
weights_with_implied_last(Weights) ->
    Weight_total = sum_powers(Weights, 0),
    case Weight_total of
        0 ->
            {error, nil};

        _ ->
            Max_bits = high_bit(Weight_total) + 1,
            Total = erlang:'bsl'(1, Max_bits),
            Rest = Total - Weight_total,
            case (Rest > 0) andalso (erlang:'bsl'(1, high_bit(Rest)) =:= Rest) of
                false ->
                    {error, nil};

                true ->
                    Last_weight = high_bit(Rest) + 1,
                    {ok, {lists:append(Weights, [Last_weight]), Max_bits}}
            end
    end.

-file("src/packkit/internal/huf.gleam", 200).
?DOC(false).
-spec fill_span(
    gleam@dict:dict(integer(), {integer(), integer()}),
    integer(),
    integer(),
    integer(),
    integer()
) -> gleam@dict:dict(integer(), {integer(), integer()}).
fill_span(Acc, Start, Span, Sym, Bits) ->
    case Span of
        0 ->
            Acc;

        _ ->
            fill_span(
                gleam@dict:insert(Acc, Start, {Sym, Bits}),
                Start + 1,
                Span - 1,
                Sym,
                Bits
            )
    end.

-file("src/packkit/internal/huf.gleam", 184).
?DOC(false).
-spec fill_lookup(
    list({integer(), integer()}),
    integer(),
    integer(),
    gleam@dict:dict(integer(), {integer(), integer()})
) -> gleam@dict:dict(integer(), {integer(), integer()}).
fill_lookup(Sorted, Max_bits, Next_code, Acc) ->
    case Sorted of
        [] ->
            Acc;

        [{Sym, Bits} | Rest] ->
            Span = erlang:'bsl'(1, Max_bits - Bits),
            Updated = fill_span(Acc, Next_code, Span, Sym, Bits),
            fill_lookup(Rest, Max_bits, Next_code + Span, Updated)
    end.

-file("src/packkit/internal/huf.gleam", 148).
?DOC(false).
-spec build_lookup_table(list(integer()), integer()) -> gleam@dict:dict(integer(), {integer(),
    integer()}).
build_lookup_table(Weights, Max_bits) ->
    With_bits = begin
        _pipe = gleam@list:index_fold(
            Weights,
            [],
            fun(Acc, Weight, Symbol) -> case Weight of
                    0 ->
                        Acc;

                    _ ->
                        [{Symbol, (Max_bits + 1) - Weight} | Acc]
                end end
        ),
        lists:reverse(_pipe)
    end,
    Sorted = gleam@list:sort(
        With_bits,
        fun(A, B) ->
            {Sym_a, Bits_a} = A,
            {Sym_b, Bits_b} = B,
            case gleam@int:compare(Bits_b, Bits_a) of
                eq ->
                    gleam@int:compare(Sym_a, Sym_b);

                Other ->
                    Other
            end
        end
    ),
    fill_lookup(Sorted, Max_bits, 0, maps:new()).

-file("src/packkit/internal/huf.gleam", 98).
?DOC(false).
-spec build_tree(list(integer())) -> {ok, tree()} | {error, huf_error()}.
build_tree(Weights) ->
    case weights_with_implied_last(Weights) of
        {error, _} ->
            {error,
                {huf_invalid_weights, <<"huf: invalid weight stream"/utf8>>}};

        {ok, {All_weights, Max_bits}} ->
            Lookup = build_lookup_table(All_weights, Max_bits),
            {ok, {tree, Max_bits, Lookup}}
    end.

-file("src/packkit/internal/huf.gleam", 60).
?DOC(false).
-spec read_direct_weights(integer(), bitstring()) -> {ok, {tree(), integer()}} |
    {error, huf_error()}.
read_direct_weights(Header_byte, Rest) ->
    Num_symbols = Header_byte - 127,
    Weights_byte_count = (Num_symbols + 1) div 2,
    case gleam_stdlib:bit_array_slice(Rest, 0, Weights_byte_count) of
        {error, _} ->
            {error,
                {huf_truncated, <<"huf: direct-weight body truncated"/utf8>>}};

        {ok, Weights_bits} ->
            Weights = unpack_direct_weights(Weights_bits, Num_symbols, []),
            gleam@result:'try'(
                build_tree(Weights),
                fun(Tree) -> {ok, {Tree, 1 + Weights_byte_count}} end
            )
    end.

-file("src/packkit/internal/huf.gleam", 247).
?DOC(false).
-spec decode_symbols_loop(
    tree(),
    packkit@internal@fse:backward_reader(),
    integer(),
    bitstring()
) -> {ok, bitstring()} | {error, huf_error()}.
decode_symbols_loop(Tree, Reader, Remaining, Acc) ->
    case Remaining of
        0 ->
            {ok, Acc};

        _ ->
            case begin
                _pipe = packkit@internal@fse:read_backward_bits_padded(
                    Reader,
                    erlang:element(2, Tree)
                ),
                gleam@result:map_error(
                    _pipe,
                    fun(Field@0) -> {huf_bitstream_error, Field@0} end
                )
            end of
                {error, E} ->
                    {error, E};

                {ok, {Index, _}} ->
                    case gleam_stdlib:map_get(erlang:element(3, Tree), Index) of
                        {error, _} ->
                            {error,
                                {huf_invalid_weights,
                                    <<"huf: bitstream index out of table"/utf8>>}};

                        {ok, {Sym, Used_bits}} ->
                            case begin
                                _pipe@1 = packkit@internal@fse:read_backward_bits_padded(
                                    Reader,
                                    Used_bits
                                ),
                                gleam@result:map_error(
                                    _pipe@1,
                                    fun(Field@0) -> {huf_bitstream_error, Field@0} end
                                )
                            end of
                                {error, E@1} ->
                                    {error, E@1};

                                {ok, {_, Reader_consumed}} ->
                                    decode_symbols_loop(
                                        Tree,
                                        Reader_consumed,
                                        Remaining - 1,
                                        <<Acc/bitstring, Sym>>
                                    )
                            end
                    end
            end
    end.

-file("src/packkit/internal/huf.gleam", 224).
?DOC(false).
-spec decode_stream(tree(), bitstring(), integer()) -> {ok, bitstring()} |
    {error, huf_error()}.
decode_stream(Tree, Bytes, Num_symbols) ->
    gleam@result:'try'(
        begin
            _pipe = packkit@internal@fse:new_backward_reader(Bytes),
            gleam@result:map_error(
                _pipe,
                fun(Field@0) -> {huf_bitstream_error, Field@0} end
            )
        end,
        fun(Reader) -> decode_symbols_loop(Tree, Reader, Num_symbols, <<>>) end
    ).

-file("src/packkit/internal/huf.gleam", 354).
?DOC(false).
-spec slice_stream(bitstring(), integer(), integer()) -> {ok, bitstring()} |
    {error, huf_error()}.
slice_stream(Bytes, Offset, Length) ->
    case gleam_stdlib:bit_array_slice(Bytes, Offset, Length) of
        {ok, Value} ->
            {ok, Value};

        {error, _} ->
            {error, {huf_truncated, <<"huf: stream slice out of range"/utf8>>}}
    end.

-file("src/packkit/internal/huf.gleam", 335).
?DOC(false).
-spec decode_split_streams(
    tree(),
    bitstring(),
    {integer(), integer(), integer(), integer()},
    {integer(), integer(), integer(), integer()}
) -> {ok, bitstring()} | {error, huf_error()}.
decode_split_streams(Tree, Streams, Byte_sizes, Symbol_counts) ->
    {B1, B2, B3, B4} = Byte_sizes,
    {C1, C2, C3, C4} = Symbol_counts,
    gleam@result:'try'(
        slice_stream(Streams, 0, B1),
        fun(S1) ->
            gleam@result:'try'(
                slice_stream(Streams, B1, B2),
                fun(S2) ->
                    gleam@result:'try'(
                        slice_stream(Streams, B1 + B2, B3),
                        fun(S3) ->
                            gleam@result:'try'(
                                slice_stream(Streams, (B1 + B2) + B3, B4),
                                fun(S4) ->
                                    gleam@result:'try'(
                                        decode_stream(Tree, S1, C1),
                                        fun(O1) ->
                                            gleam@result:'try'(
                                                decode_stream(Tree, S2, C2),
                                                fun(O2) ->
                                                    gleam@result:'try'(
                                                        decode_stream(
                                                            Tree,
                                                            S3,
                                                            C3
                                                        ),
                                                        fun(O3) ->
                                                            gleam@result:'try'(
                                                                decode_stream(
                                                                    Tree,
                                                                    S4,
                                                                    C4
                                                                ),
                                                                fun(O4) ->
                                                                    {ok,
                                                                        gleam_stdlib:bit_array_concat(
                                                                            [O1,
                                                                                O2,
                                                                                O3,
                                                                                O4]
                                                                        )}
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/packkit/internal/huf.gleam", 294).
?DOC(false).
-spec decode_four_streams(tree(), bitstring(), integer()) -> {ok, bitstring()} |
    {error, huf_error()}.
decode_four_streams(Tree, Bytes, Regenerated_size) ->
    Total = erlang:byte_size(Bytes),
    case Total < 6 of
        true ->
            {error,
                {huf_truncated, <<"huf: 4-stream jump table truncated"/utf8>>}};

        false ->
            case Bytes of
                <<Size1:16/little,
                    Size2:16/little,
                    Size3:16/little,
                    Streams/binary>> ->
                    Body_size = Total - 6,
                    Size4 = ((Body_size - Size1) - Size2) - Size3,
                    gleam@bool:guard(
                        Size4 < 0,
                        {error,
                            {huf_invalid_weights,
                                <<"huf: 4-stream jump table sums past body"/utf8>>}},
                        fun() ->
                            Per_stream = (Regenerated_size + 3) div 4,
                            Stream4_size = Regenerated_size - (Per_stream * 3),
                            decode_split_streams(
                                Tree,
                                Streams,
                                {Size1, Size2, Size3, Size4},
                                {Per_stream,
                                    Per_stream,
                                    Per_stream,
                                    Stream4_size}
                            )
                        end
                    );

                _ ->
                    {error,
                        {huf_truncated,
                            <<"huf: 4-stream header malformed"/utf8>>}}
            end
    end.

-file("src/packkit/internal/huf.gleam", 471).
?DOC(false).
-spec lookup_symbol(
    gleam@dict:dict(integer(), packkit@internal@fse:state_entry()),
    integer()
) -> integer().
lookup_symbol(Table, State) ->
    _pipe = gleam_stdlib:map_get(Table, State),
    _pipe@1 = gleam@result:map(
        _pipe,
        fun(Entry) -> erlang:element(2, Entry) end
    ),
    gleam@result:unwrap(_pipe@1, 0).

-file("src/packkit/internal/huf.gleam", 493).
?DOC(false).
-spec new_fwd_reader(bitstring()) -> fwd_bit_reader().
new_fwd_reader(Bytes) ->
    {fwd_bit_reader, Bytes, 0, 0, 0}.

-file("src/packkit/internal/huf.gleam", 497).
?DOC(false).
-spec fwd_refill(fwd_bit_reader(), integer()) -> fwd_bit_reader().
fwd_refill(Reader, Needed) ->
    gleam@bool:guard(
        erlang:element(4, Reader) >= Needed,
        Reader,
        fun() -> case erlang:element(2, Reader) of
                <<Byte, Rest/binary>> ->
                    fwd_refill(
                        {fwd_bit_reader,
                            Rest,
                            erlang:'bor'(
                                erlang:element(3, Reader),
                                erlang:'bsl'(Byte, erlang:element(4, Reader))
                            ),
                            erlang:element(4, Reader) + 8,
                            erlang:element(5, Reader)},
                        Needed
                    );

                _ ->
                    Reader
            end end
    ).

-file("src/packkit/internal/huf.gleam", 517).
?DOC(false).
-spec fwd_peek(fwd_bit_reader(), integer()) -> {integer(), fwd_bit_reader()}.
fwd_peek(Reader, Count) ->
    Refilled = fwd_refill(Reader, Count),
    Mask = erlang:'bsl'(1, Count) - 1,
    {erlang:'band'(erlang:element(3, Refilled), Mask), Refilled}.

-file("src/packkit/internal/huf.gleam", 523).
?DOC(false).
-spec fwd_drop(fwd_bit_reader(), integer()) -> fwd_bit_reader().
fwd_drop(Reader, Count) ->
    Refilled = fwd_refill(Reader, Count),
    case erlang:element(4, Refilled) >= Count of
        true ->
            {fwd_bit_reader,
                erlang:element(2, Refilled),
                erlang:'bsr'(erlang:element(3, Refilled), Count),
                erlang:element(4, Refilled) - Count,
                erlang:element(5, Refilled) + Count};

        false ->
            Refilled
    end.

-file("src/packkit/internal/huf.gleam", 537).
?DOC(false).
-spec fwd_read(fwd_bit_reader(), integer()) -> {ok,
        {integer(), fwd_bit_reader()}} |
    {error, huf_error()}.
fwd_read(Reader, Count) ->
    Refilled = fwd_refill(Reader, Count),
    case erlang:element(4, Refilled) >= Count of
        false ->
            {error, {huf_truncated, <<"huf: FSE distribution truncated"/utf8>>}};

        true ->
            {Value, Peeked} = fwd_peek(Refilled, Count),
            {ok, {Value, fwd_drop(Peeked, Count)}}
    end.

-file("src/packkit/internal/huf.gleam", 643).
?DOC(false).
-spec split_distribution_count(integer(), integer(), integer(), integer()) -> {integer(),
    integer()}.
split_distribution_count(Value, Threshold, Remaining, Bit_count) ->
    Max_val = ((2 * Threshold) - 1) - Remaining,
    case erlang:'band'(Value, Threshold - 1) < Max_val of
        true ->
            {erlang:'band'(Value, Threshold - 1), Bit_count - 1};

        false ->
            Raw = erlang:'band'(Value, (2 * Threshold) - 1),
            Adjusted = case Raw >= Threshold of
                true ->
                    Raw - Max_val;

                false ->
                    Raw
            end,
            {Adjusted, Bit_count}
    end.

-file("src/packkit/internal/huf.gleam", 663).
?DOC(false).
-spec shrink_threshold(integer(), integer(), integer()) -> {integer(),
    integer()}.
shrink_threshold(Threshold, Bit_count, Remaining) ->
    gleam@bool:guard(
        Remaining >= Threshold,
        {Threshold, Bit_count},
        fun() ->
            gleam@bool:guard(
                Threshold =< 1,
                {Threshold, Bit_count},
                fun() ->
                    shrink_threshold(Threshold div 2, Bit_count - 1, Remaining)
                end
            )
        end
    ).

-file("src/packkit/internal/huf.gleam", 676).
?DOC(false).
-spec read_zero_repeat(fwd_bit_reader(), integer()) -> {ok,
        {integer(), fwd_bit_reader()}} |
    {error, huf_error()}.
read_zero_repeat(Reader, Acc) ->
    gleam@result:'try'(
        fwd_read(Reader, 2),
        fun(_use0) ->
            {Chunk, Reader@1} = _use0,
            case Chunk of
                3 ->
                    read_zero_repeat(Reader@1, Acc + 3);

                N ->
                    {ok, {Acc + N, Reader@1}}
            end
        end
    ).

-file("src/packkit/internal/huf.gleam", 687).
?DOC(false).
-spec prepend_zeros(integer(), list(integer())) -> list(integer()).
prepend_zeros(Count, Acc) ->
    case Count of
        0 ->
            Acc;

        _ ->
            prepend_zeros(Count - 1, [0 | Acc])
    end.

-file("src/packkit/internal/huf.gleam", 590).
?DOC(false).
-spec decode_distribution_counts(
    fwd_bit_reader(),
    integer(),
    integer(),
    integer(),
    boolean(),
    integer(),
    integer(),
    list(integer())
) -> {ok, {list(integer()), fwd_bit_reader()}} | {error, huf_error()}.
decode_distribution_counts(
    Reader,
    Remaining,
    Threshold,
    Bit_count,
    Previous_is_zero,
    Charnum,
    Max_symbol,
    Acc
) ->
    gleam@bool:guard(
        not ((Remaining > 1) andalso (Charnum =< Max_symbol)),
        {ok, {lists:reverse(Acc), Reader}},
        fun() -> case Previous_is_zero of
                true ->
                    gleam@result:'try'(
                        read_zero_repeat(Reader, 0),
                        fun(_use0) ->
                            {Extra, Reader@1} = _use0,
                            Acc@1 = prepend_zeros(Extra, Acc),
                            decode_distribution_counts(
                                Reader@1,
                                Remaining,
                                Threshold,
                                Bit_count,
                                false,
                                Charnum + Extra,
                                Max_symbol,
                                Acc@1
                            )
                        end
                    );

                false ->
                    {Value, Reader@2} = fwd_peek(Reader, Bit_count),
                    {Count, Bits_consumed} = split_distribution_count(
                        Value,
                        Threshold,
                        Remaining,
                        Bit_count
                    ),
                    Reader@3 = fwd_drop(Reader@2, Bits_consumed),
                    Probability = Count - 1,
                    Abs_prob = gleam@int:absolute_value(Probability),
                    Next_remaining = Remaining - Abs_prob,
                    {Next_threshold, Next_bit_count} = shrink_threshold(
                        Threshold,
                        Bit_count,
                        Next_remaining
                    ),
                    decode_distribution_counts(
                        Reader@3,
                        Next_remaining,
                        Next_threshold,
                        Next_bit_count,
                        Probability =:= 0,
                        Charnum + 1,
                        Max_symbol,
                        [Probability | Acc]
                    )
            end end
    ).

-file("src/packkit/internal/huf.gleam", 702).
?DOC(false).
-spec repeat_zeros(integer(), list(integer())) -> list(integer()).
repeat_zeros(N, Acc) ->
    case N of
        0 ->
            Acc;

        _ ->
            repeat_zeros(N - 1, [0 | Acc])
    end.

-file("src/packkit/internal/huf.gleam", 694).
?DOC(false).
-spec pad_distribution(list(integer()), integer()) -> list(integer()).
pad_distribution(Counts, Target) ->
    Current = erlang:length(Counts),
    case Current >= Target of
        true ->
            Counts;

        false ->
            lists:append(Counts, repeat_zeros(Target - Current, []))
    end.

-file("src/packkit/internal/huf.gleam", 551).
?DOC(false).
-spec read_distribution(bitstring(), integer(), integer()) -> {ok,
        {list(integer()), integer(), bitstring()}} |
    {error, huf_error()}.
read_distribution(Bytes, Max_accuracy_log, Max_symbol) ->
    Reader = new_fwd_reader(Bytes),
    gleam@result:'try'(
        fwd_read(Reader, 4),
        fun(_use0) ->
            {Low4, Reader@1} = _use0,
            Accuracy_log = Low4 + 5,
            case Accuracy_log > Max_accuracy_log of
                true ->
                    {error,
                        {huf_invalid_weights,
                            <<"huf: FSE distribution accuracy_log too high"/utf8>>}};

                false ->
                    Table_size = erlang:'bsl'(1, Accuracy_log),
                    gleam@result:'try'(
                        decode_distribution_counts(
                            Reader@1,
                            Table_size + 1,
                            Table_size,
                            Accuracy_log + 1,
                            false,
                            0,
                            Max_symbol,
                            []
                        ),
                        fun(_use0@1) ->
                            {Counts, Reader@2} = _use0@1,
                            Bytes_consumed = (erlang:element(5, Reader@2) + 7)
                            div 8,
                            Total = erlang:byte_size(Bytes),
                            case gleam_stdlib:bit_array_slice(
                                Bytes,
                                Bytes_consumed,
                                Total - Bytes_consumed
                            ) of
                                {ok, Rest} ->
                                    {ok,
                                        {pad_distribution(
                                                Counts,
                                                Max_symbol + 1
                                            ),
                                            Accuracy_log,
                                            Rest}};

                                {error, _} ->
                                    {error,
                                        {huf_truncated,
                                            <<"huf: FSE distribution tail truncated"/utf8>>}}
                            end
                        end
                    )
            end
        end
    ).

-file("src/packkit/internal/huf.gleam", 433).
?DOC(false).
-spec finish_or_continue_weight_pair(
    {ok, {integer(), integer(), packkit@internal@fse:backward_reader()}} |
        {error, packkit@internal@fse:fse_error()},
    gleam@dict:dict(integer(), packkit@internal@fse:state_entry()),
    integer(),
    integer(),
    boolean(),
    list(integer()),
    integer()
) -> {ok, list(integer())} | {error, huf_error()}.
finish_or_continue_weight_pair(
    Decode_outcome,
    Table,
    State_a,
    State_b,
    Use_a,
    Acc,
    Active_state
) ->
    case Decode_outcome of
        {error, fse_truncated} ->
            Partner = case Use_a of
                true ->
                    State_b;

                false ->
                    State_a
            end,
            Active_symbol = lookup_symbol(Table, Active_state),
            Partner_symbol = lookup_symbol(Table, Partner),
            {ok, lists:reverse([Partner_symbol, Active_symbol | Acc])};

        {error, fse_empty_bitstream} ->
            Partner = case Use_a of
                true ->
                    State_b;

                false ->
                    State_a
            end,
            Active_symbol = lookup_symbol(Table, Active_state),
            Partner_symbol = lookup_symbol(Table, Partner),
            {ok, lists:reverse([Partner_symbol, Active_symbol | Acc])};

        {ok, {Symbol, Next_state, New_reader}} ->
            {Next_a, Next_b} = case Use_a of
                true ->
                    {Next_state, State_b};

                false ->
                    {State_a, Next_state}
            end,
            decode_fse_weight_pairs(
                Table,
                New_reader,
                Next_a,
                Next_b,
                not Use_a,
                [Symbol | Acc]
            )
    end.

-file("src/packkit/internal/huf.gleam", 410).
?DOC(false).
-spec decode_fse_weight_pairs(
    gleam@dict:dict(integer(), packkit@internal@fse:state_entry()),
    packkit@internal@fse:backward_reader(),
    integer(),
    integer(),
    boolean(),
    list(integer())
) -> {ok, list(integer())} | {error, huf_error()}.
decode_fse_weight_pairs(Table, Reader, State_a, State_b, Use_a, Acc) ->
    Active_state = case Use_a of
        true ->
            State_a;

        false ->
            State_b
    end,
    _pipe = packkit@internal@fse:decode_state(Table, Active_state, Reader),
    finish_or_continue_weight_pair(
        _pipe,
        Table,
        State_a,
        State_b,
        Use_a,
        Acc,
        Active_state
    ).

-file("src/packkit/internal/huf.gleam", 386).
?DOC(false).
-spec decode_fse_weight_stream(bitstring()) -> {ok, list(integer())} |
    {error, huf_error()}.
decode_fse_weight_stream(Bytes) ->
    gleam@result:'try'(
        read_distribution(Bytes, 6, 12),
        fun(_use0) ->
            {Counts, Accuracy_log, After_header} = _use0,
            Table = packkit@internal@fse:build_state_table(Counts, Accuracy_log),
            gleam@result:'try'(
                begin
                    _pipe = packkit@internal@fse:new_backward_reader(
                        After_header
                    ),
                    gleam@result:map_error(
                        _pipe,
                        fun(Field@0) -> {huf_bitstream_error, Field@0} end
                    )
                end,
                fun(Reader) ->
                    gleam@result:'try'(
                        begin
                            _pipe@1 = packkit@internal@fse:read_backward_bits(
                                Reader,
                                Accuracy_log
                            ),
                            gleam@result:map_error(
                                _pipe@1,
                                fun(Field@0) -> {huf_bitstream_error, Field@0} end
                            )
                        end,
                        fun(_use0@1) ->
                            {State_a, Reader@1} = _use0@1,
                            gleam@result:'try'(
                                begin
                                    _pipe@2 = packkit@internal@fse:read_backward_bits(
                                        Reader@1,
                                        Accuracy_log
                                    ),
                                    gleam@result:map_error(
                                        _pipe@2,
                                        fun(Field@0) -> {huf_bitstream_error, Field@0} end
                                    )
                                end,
                                fun(_use0@2) ->
                                    {State_b, Reader@2} = _use0@2,
                                    decode_fse_weight_pairs(
                                        Table,
                                        Reader@2,
                                        State_a,
                                        State_b,
                                        true,
                                        []
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/packkit/internal/huf.gleam", 365).
?DOC(false).
-spec read_fse_weights(integer(), bitstring()) -> {ok, {tree(), integer()}} |
    {error, huf_error()}.
read_fse_weights(I_size, Rest) ->
    case gleam_stdlib:bit_array_slice(Rest, 0, I_size) of
        {error, _} ->
            {error, {huf_truncated, <<"huf: FSE-weight body truncated"/utf8>>}};

        {ok, Fse_bytes} ->
            gleam@result:'try'(
                decode_fse_weight_stream(Fse_bytes),
                fun(Weights) ->
                    gleam@result:'try'(
                        build_tree(Weights),
                        fun(Tree) -> {ok, {Tree, 1 + I_size}} end
                    )
                end
            )
    end.

-file("src/packkit/internal/huf.gleam", 49).
?DOC(false).
-spec read_tree(bitstring()) -> {ok, {tree(), integer()}} | {error, huf_error()}.
read_tree(Bytes) ->
    case Bytes of
        <<Header_byte, Rest/binary>> ->
            case Header_byte >= 128 of
                true ->
                    read_direct_weights(Header_byte, Rest);

                false ->
                    read_fse_weights(Header_byte, Rest)
            end;

        _ ->
            {error,
                {huf_truncated, <<"huf: tree description header missing"/utf8>>}}
    end.