Skip to main content

src/packkit@deflate.erl

-module(packkit@deflate).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit/deflate.gleam").
-export([codec/0, decode_with_limits/2, decode/1, decode_with_remainder/2, encode_stored_only/1, encode/1, encode_dynamic/1]).
-export_type([reader/0, tree/0, inflate_step/0, huffman_step/0, writer/0, token/0, huff_tree/0, c_l_op/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(
    " Pure Gleam DEFLATE (RFC 1951) encoder and decoder.\n"
    "\n"
    " The decoder handles all three RFC 1951 block types (stored, fixed\n"
    " Huffman, dynamic Huffman) and enforces the `Limits` resource budget\n"
    " while decoding.  The encoder exposes three entry points:\n"
    "\n"
    " * `encode_stored_only` emits BTYPE=00 blocks for callers that want\n"
    "   to bypass the match-finder entirely.\n"
    " * `encode` runs the greedy LZ77 match-finder (3-byte hash chain, 32\n"
    "   KiB sliding window) and emits a single fixed-Huffman block\n"
    "   (BTYPE=01).\n"
    " * `encode_dynamic` reuses the same match-finder but builds\n"
    "   per-stream Huffman codes for the literal/length and distance\n"
    "   alphabets, plus the 19-symbol code-length alphabet, and emits a\n"
    "   single dynamic-Huffman block (BTYPE=10).  It falls back to the\n"
    "   fixed-Huffman path when the natural Huffman tree would exceed the\n"
    "   RFC 1951 15-bit code-length cap.\n"
).

-type reader() :: {reader, integer(), integer(), bitstring(), boolean()}.

-type tree() :: {tree, list(integer()), list(integer()), integer()}.

-type inflate_step() :: {inflate_done, bitstring(), reader()} |
    {inflate_continue, bitstring(), reader()}.

-type huffman_step() :: {huffman_done, bitstring(), reader()} |
    {huffman_continue, bitstring(), reader()}.

-type writer() :: {writer, list(integer()), integer(), integer()}.

-type token() :: {tok_lit, integer()} | {tok_match, integer(), integer()}.

-type huff_tree() :: {h_leaf, integer()} | {h_node, huff_tree(), huff_tree()}.

-type c_l_op() :: {c_l_lit, integer()} |
    {c_l_copy, integer()} |
    {c_l_zero3, integer()} |
    {c_l_zero11, integer()}.

-file("src/packkit/deflate.gleam", 45).
?DOC(" Raw deflate codec smart constructor.\n").
-spec codec() -> packkit@codec:codec().
codec() ->
    packkit@codec:deflate().

-file("src/packkit/deflate.gleam", 119).
-spec bytes_from_low_int(integer(), integer(), bitstring()) -> bitstring().
bytes_from_low_int(Value, Count, Acc) ->
    case Count of
        0 ->
            Acc;

        _ ->
            bytes_from_low_int(
                erlang:'bsr'(Value, 8),
                Count - 1,
                <<Acc/bitstring, (erlang:'band'(Value, 16#FF))>>
            )
    end.

-file("src/packkit/deflate.gleam", 108).
-spec recover_remaining_bytes(reader()) -> bitstring().
recover_remaining_bytes(Reader) ->
    Whole_bytes = erlang:element(3, Reader) div 8,
    Partial = erlang:element(3, Reader) - (Whole_bytes * 8),
    Buffer_after_partial = erlang:'bsr'(erlang:element(2, Reader), Partial),
    Prefix = bytes_from_low_int(Buffer_after_partial, Whole_bytes, <<>>),
    gleam_stdlib:bit_array_concat([Prefix, erlang:element(4, Reader)]).

-file("src/packkit/deflate.gleam", 175).
-spec refill(reader(), integer()) -> reader().
refill(Reader, Needed) ->
    case (erlang:element(3, Reader) >= Needed) orelse erlang:element(5, Reader) of
        true ->
            Reader;

        false ->
            case erlang:element(4, Reader) of
                <<B, Rest/binary>> ->
                    refill(
                        {reader,
                            erlang:'bor'(
                                erlang:element(2, Reader),
                                erlang:'bsl'(B, erlang:element(3, Reader))
                            ),
                            erlang:element(3, Reader) + 8,
                            Rest,
                            false},
                        Needed
                    );

                _ ->
                    {reader,
                        erlang:element(2, Reader),
                        erlang:element(3, Reader) + 8,
                        <<>>,
                        true}
            end
    end.

-file("src/packkit/deflate.gleam", 204).
-spec read_bits(reader(), integer()) -> {ok, {integer(), reader()}} |
    {error, packkit@error:codec_error()}.
read_bits(Reader, Count) ->
    R = refill(Reader, Count),
    case erlang:element(5, R) andalso (erlang:element(3, R) < Count) of
        true ->
            {error,
                {codec_invalid_data, <<"truncated deflate bit stream"/utf8>>}};

        false ->
            Mask = erlang:'bsl'(1, Count) - 1,
            Value = erlang:'band'(erlang:element(2, R), Mask),
            Next = {reader,
                erlang:'bsr'(erlang:element(2, R), Count),
                erlang:element(3, R) - Count,
                erlang:element(4, R),
                erlang:element(5, R)},
            {ok, {Value, Next}}
    end.

-file("src/packkit/deflate.gleam", 227).
-spec skip_to_byte_boundary(reader()) -> reader().
skip_to_byte_boundary(Reader) ->
    {reader, 0, 0, erlang:element(4, Reader), erlang:element(5, Reader)}.

-file("src/packkit/deflate.gleam", 260).
-spec single_symbol(list(integer())) -> integer().
single_symbol(Symbols) ->
    case Symbols of
        [Head | _] ->
            Head;

        [] ->
            0
    end.

-file("src/packkit/deflate.gleam", 267).
-spec find_max_symbol(list(integer()), integer(), integer(), integer()) -> integer().
find_max_symbol(Lengths, Index, Best, _) ->
    case Lengths of
        [] ->
            Best;

        [0 | Rest] ->
            find_max_symbol(Rest, Index + 1, Best, 0);

        [_ | Rest@1] ->
            find_max_symbol(Rest@1, Index + 1, Index, 0)
    end.

-file("src/packkit/deflate.gleam", 284).
-spec empty_counts() -> list(integer()).
empty_counts() ->
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].

-file("src/packkit/deflate.gleam", 360).
-spec enumerate_lengths(
    list(integer()),
    integer(),
    list({integer(), integer()})
) -> list({integer(), integer()}).
enumerate_lengths(Lengths, Index, Acc) ->
    case Lengths of
        [] ->
            lists:reverse(Acc);

        [Length | Rest] ->
            enumerate_lengths(Rest, Index + 1, [{Index, Length} | Acc])
    end.

-file("src/packkit/deflate.gleam", 392).
-spec prepend_reversed(list(integer()), list(integer())) -> list(integer()).
prepend_reversed(Values, Acc) ->
    case Values of
        [] ->
            Acc;

        [Head | Rest] ->
            prepend_reversed(Rest, [Head | Acc])
    end.

-file("src/packkit/deflate.gleam", 505).
-spec read_raw_bytes(reader(), integer()) -> {ok, {bitstring(), reader()}} |
    {error, packkit@error:codec_error()}.
read_raw_bytes(Reader, Length) ->
    case Length of
        0 ->
            {ok, {<<>>, Reader}};

        _ ->
            case erlang:element(4, Reader) of
                <<Chunk:Length/binary, Rest/binary>> ->
                    {ok, {Chunk, {reader, 0, 0, Rest, false}}};

                _ ->
                    {error,
                        {codec_invalid_data, <<"truncated stored block"/utf8>>}}
            end
    end.

-file("src/packkit/deflate.gleam", 580).
-spec empty_clcl_table() -> list(integer()).
empty_clcl_table() ->
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].

-file("src/packkit/deflate.gleam", 584).
-spec code_length_order() -> list(integer()).
code_length_order() ->
    [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15].

-file("src/packkit/deflate.gleam", 705).
-spec repeat_value(integer(), integer(), list(integer())) -> list(integer()).
repeat_value(Value, Count, Acc) ->
    case Count of
        0 ->
            Acc;

        _ ->
            repeat_value(Value, Count - 1, [Value | Acc])
    end.

-file("src/packkit/deflate.gleam", 712).
-spec pad_to_length(list(integer()), integer()) -> list(integer()).
pad_to_length(Values, Target) ->
    Current = erlang:length(Values),
    case Current >= Target of
        true ->
            Values;

        false ->
            lists:append(Values, repeat_value(0, Target - Current, []))
    end.

-file("src/packkit/deflate.gleam", 842).
-spec append_with_limit(bitstring(), bitstring(), packkit@limit:limits()) -> {ok,
        bitstring()} |
    {error, packkit@error:codec_error()}.
append_with_limit(Output, Chunk, Limits) ->
    Projected = erlang:byte_size(Output) + erlang:byte_size(Chunk),
    case Projected > packkit@limit:max_output_bytes(Limits) of
        true ->
            {error,
                {codec_limit_exceeded, <<"max_output_bytes"/utf8>>, Projected}};

        false ->
            {ok, gleam_stdlib:bit_array_concat([Output, Chunk])}
    end.

-file("src/packkit/deflate.gleam", 484).
-spec inflate_stored(reader(), bitstring(), packkit@limit:limits()) -> {ok,
        {bitstring(), reader()}} |
    {error, packkit@error:codec_error()}.
inflate_stored(Reader, Output, Limits) ->
    Reader@1 = skip_to_byte_boundary(Reader),
    gleam@result:'try'(
        read_bits(Reader@1, 16),
        fun(_use0) ->
            {Length, Reader@2} = _use0,
            gleam@result:'try'(
                read_bits(Reader@2, 16),
                fun(_use0@1) ->
                    {Invlength, Reader@3} = _use0@1,
                    gleam@bool:guard(
                        erlang:'band'(erlang:'bxor'(Length, Invlength), 16#FFFF)
                        /= 16#FFFF,
                        {error,
                            {codec_invalid_data,
                                <<"stored block length mismatch"/utf8>>}},
                        fun() ->
                            gleam@result:'try'(
                                read_raw_bytes(Reader@3, Length),
                                fun(_use0@2) ->
                                    {Bytes, Reader@4} = _use0@2,
                                    gleam@result:'try'(
                                        append_with_limit(Output, Bytes, Limits),
                                        fun(New_output) ->
                                            {ok, {New_output, Reader@4}}
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/packkit/deflate.gleam", 825).
-spec apply_backref_byte_by_byte(
    bitstring(),
    integer(),
    integer(),
    packkit@limit:limits()
) -> {ok, bitstring()} | {error, packkit@error:codec_error()}.
apply_backref_byte_by_byte(Output, Distance, Length, Limits) ->
    case Length of
        0 ->
            {ok, Output};

        _ ->
            Size = erlang:byte_size(Output),
            Byte_slice@1 = case gleam_stdlib:bit_array_slice(
                Output,
                Size - Distance,
                1
            ) of
                {ok, Byte_slice} -> Byte_slice;
                _assert_fail ->
                    erlang:error(#{gleam_error => let_assert,
                                message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                file => <<?FILEPATH/utf8>>,
                                module => <<"packkit/deflate"/utf8>>,
                                function => <<"apply_backref_byte_by_byte"/utf8>>,
                                line => 835,
                                value => _assert_fail,
                                start => 24846,
                                'end' => 24917,
                                pattern_start => 24857,
                                pattern_end => 24871})
            end,
            gleam@result:'try'(
                append_with_limit(Output, Byte_slice@1, Limits),
                fun(New_output) ->
                    apply_backref_byte_by_byte(
                        New_output,
                        Distance,
                        Length - 1,
                        Limits
                    )
                end
            )
    end.

-file("src/packkit/deflate.gleam", 804).
-spec apply_backref(bitstring(), integer(), integer(), packkit@limit:limits()) -> {ok,
        bitstring()} |
    {error, packkit@error:codec_error()}.
apply_backref(Output, Distance, Length, Limits) ->
    Size = erlang:byte_size(Output),
    gleam@bool:guard(
        (Distance =< 0) orelse (Distance > Size),
        {error, {codec_invalid_data, <<"back-reference out of range"/utf8>>}},
        fun() -> case Distance >= Length of
                true ->
                    Chunk@1 = case gleam_stdlib:bit_array_slice(
                        Output,
                        Size - Distance,
                        Length
                    ) of
                        {ok, Chunk} -> Chunk;
                        _assert_fail ->
                            erlang:error(#{gleam_error => let_assert,
                                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                        file => <<?FILEPATH/utf8>>,
                                        module => <<"packkit/deflate"/utf8>>,
                                        function => <<"apply_backref"/utf8>>,
                                        line => 818,
                                        value => _assert_fail,
                                        start => 24393,
                                        'end' => 24464,
                                        pattern_start => 24404,
                                        pattern_end => 24413})
                    end,
                    append_with_limit(Output, Chunk@1, Limits);

                false ->
                    apply_backref_byte_by_byte(Output, Distance, Length, Limits)
            end end
    ).

-file("src/packkit/deflate.gleam", 874).
-spec build_fixed_literal_lengths() -> list(integer()).
build_fixed_literal_lengths() ->
    lists:append(
        [gleam@list:repeat(8, 144),
            gleam@list:repeat(9, 112),
            gleam@list:repeat(7, 24),
            gleam@list:repeat(8, 8)]
    ).

-file("src/packkit/deflate.gleam", 889).
-spec length_extra_bits() -> list(integer()).
length_extra_bits() ->
    [0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        1,
        1,
        1,
        1,
        2,
        2,
        2,
        2,
        3,
        3,
        3,
        3,
        4,
        4,
        4,
        4,
        5,
        5,
        5,
        5,
        0].

-file("src/packkit/deflate.gleam", 923).
-spec length_base() -> list(integer()).
length_base() ->
    [3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        13,
        15,
        17,
        19,
        23,
        27,
        31,
        35,
        43,
        51,
        59,
        67,
        83,
        99,
        115,
        131,
        163,
        195,
        227,
        258].

-file("src/packkit/deflate.gleam", 957).
-spec distance_extra_bits() -> list(integer()).
distance_extra_bits() ->
    [0,
        0,
        0,
        0,
        1,
        1,
        2,
        2,
        3,
        3,
        4,
        4,
        5,
        5,
        6,
        6,
        7,
        7,
        8,
        8,
        9,
        9,
        10,
        10,
        11,
        11,
        12,
        12,
        13,
        13].

-file("src/packkit/deflate.gleam", 992).
-spec distance_base() -> list(integer()).
distance_base() ->
    [1,
        2,
        3,
        4,
        5,
        7,
        9,
        13,
        17,
        25,
        33,
        49,
        65,
        97,
        129,
        193,
        257,
        385,
        513,
        769,
        1025,
        1537,
        2049,
        3073,
        4097,
        6145,
        8193,
        12289,
        16385,
        24577].

-file("src/packkit/deflate.gleam", 1037).
-spec empty_stored_block() -> bitstring().
empty_stored_block() ->
    <<1, 0, 0, 16#FF, 16#FF>>.

-file("src/packkit/deflate.gleam", 1100).
-spec build_byte_table(
    bitstring(),
    integer(),
    gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
build_byte_table(Bytes, Index, Acc) ->
    case Bytes of
        <<B, Rest/binary>> ->
            build_byte_table(Rest, Index + 1, gleam@dict:insert(Acc, Index, B));

        _ ->
            Acc
    end.

-file("src/packkit/deflate.gleam", 1112).
-spec byte_at(gleam@dict:dict(integer(), integer()), integer()) -> integer().
byte_at(Table, Index) ->
    case gleam_stdlib:map_get(Table, Index) of
        {ok, Value} ->
            Value;

        {error, _} ->
            0
    end.

-file("src/packkit/deflate.gleam", 1119).
-spec hash3(integer(), integer(), integer()) -> integer().
hash3(B0, B1, B2) ->
    erlang:'band'(
        erlang:'bxor'(
            erlang:'bxor'(B0 * 2654435761, B1 * 40503),
            B2 * 2246822519
        ),
        16#FFFF
    ).

-file("src/packkit/deflate.gleam", 1209).
-spec match_length(
    gleam@dict:dict(integer(), integer()),
    integer(),
    integer(),
    integer(),
    integer(),
    integer()
) -> integer().
match_length(Table, Base, Cursor, Size, Max, Acc) ->
    case (Acc >= Max) orelse ((Cursor + Acc) >= Size) of
        true ->
            Acc;

        false ->
            case byte_at(Table, Base + Acc) =:= byte_at(Table, Cursor + Acc) of
                true ->
                    match_length(Table, Base, Cursor, Size, Max, Acc + 1);

                false ->
                    Acc
            end
    end.

-file("src/packkit/deflate.gleam", 1264).
-spec length_code(integer()) -> {integer(), integer(), integer()}.
length_code(Length) ->
    case Length of
        N when (N >= 3) andalso (N =< 10) ->
            {254 + N, 0, 0};

        N@1 when (N@1 >= 11) andalso (N@1 =< 18) ->
            {265 + ((N@1 - 11) div 2), 1, (N@1 - 11) rem 2};

        N@2 when (N@2 >= 19) andalso (N@2 =< 34) ->
            {269 + ((N@2 - 19) div 4), 2, (N@2 - 19) rem 4};

        N@3 when (N@3 >= 35) andalso (N@3 =< 66) ->
            {273 + ((N@3 - 35) div 8), 3, (N@3 - 35) rem 8};

        N@4 when (N@4 >= 67) andalso (N@4 =< 130) ->
            {277 + ((N@4 - 67) div 16), 4, (N@4 - 67) rem 16};

        N@5 when (N@5 >= 131) andalso (N@5 =< 257) ->
            {281 + ((N@5 - 131) div 32), 5, (N@5 - 131) rem 32};

        _ ->
            {285, 0, 0}
    end.

-file("src/packkit/deflate.gleam", 1276).
-spec distance_code(integer()) -> {integer(), integer(), integer()}.
distance_code(Distance) ->
    case Distance of
        N when (N >= 1) andalso (N =< 4) ->
            {N - 1, 0, 0};

        N@1 when (N@1 >= 5) andalso (N@1 =< 8) ->
            {4 + ((N@1 - 5) div 2), 1, (N@1 - 5) rem 2};

        N@2 when (N@2 >= 9) andalso (N@2 =< 16) ->
            {6 + ((N@2 - 9) div 4), 2, (N@2 - 9) rem 4};

        N@3 when (N@3 >= 17) andalso (N@3 =< 32) ->
            {8 + ((N@3 - 17) div 8), 3, (N@3 - 17) rem 8};

        N@4 when (N@4 >= 33) andalso (N@4 =< 64) ->
            {10 + ((N@4 - 33) div 16), 4, (N@4 - 33) rem 16};

        N@5 when (N@5 >= 65) andalso (N@5 =< 128) ->
            {12 + ((N@5 - 65) div 32), 5, (N@5 - 65) rem 32};

        N@6 when (N@6 >= 129) andalso (N@6 =< 256) ->
            {14 + ((N@6 - 129) div 64), 6, (N@6 - 129) rem 64};

        N@7 when (N@7 >= 257) andalso (N@7 =< 512) ->
            {16 + ((N@7 - 257) div 128), 7, (N@7 - 257) rem 128};

        N@8 when (N@8 >= 513) andalso (N@8 =< 1024) ->
            {18 + ((N@8 - 513) div 256), 8, (N@8 - 513) rem 256};

        N@9 when (N@9 >= 1025) andalso (N@9 =< 2048) ->
            {20 + ((N@9 - 1025) div 512), 9, (N@9 - 1025) rem 512};

        N@10 when (N@10 >= 2049) andalso (N@10 =< 4096) ->
            {22 + ((N@10 - 2049) div 1024), 10, (N@10 - 2049) rem 1024};

        N@11 when (N@11 >= 4097) andalso (N@11 =< 8192) ->
            {24 + ((N@11 - 4097) div 2048), 11, (N@11 - 4097) rem 2048};

        N@12 when (N@12 >= 8193) andalso (N@12 =< 16384) ->
            {26 + ((N@12 - 8193) div 4096), 12, (N@12 - 8193) rem 4096};

        N@13 when (N@13 >= 16385) andalso (N@13 =< 32768) ->
            {28 + ((N@13 - 16385) div 8192), 13, (N@13 - 16385) rem 8192};

        _ ->
            {0, 0, 0}
    end.

-file("src/packkit/deflate.gleam", 1320).
-spec fixed_literal_code(integer()) -> {integer(), integer()}.
fixed_literal_code(Symbol) ->
    case Symbol of
        S when (S >= 0) andalso (S =< 143) ->
            {48 + S, 8};

        S@1 when (S@1 >= 144) andalso (S@1 =< 255) ->
            {400 + (S@1 - 144), 9};

        S@2 when (S@2 >= 256) andalso (S@2 =< 279) ->
            {S@2 - 256, 7};

        S@3 when (S@3 >= 280) andalso (S@3 =< 287) ->
            {192 + (S@3 - 280), 8};

        _ ->
            {0, 8}
    end.

-file("src/packkit/deflate.gleam", 1347).
-spec reverse_bits_loop(integer(), integer(), integer()) -> integer().
reverse_bits_loop(Value, Count, Acc) ->
    case Count of
        0 ->
            Acc;

        _ ->
            reverse_bits_loop(
                erlang:'bsr'(Value, 1),
                Count - 1,
                erlang:'bor'(erlang:'bsl'(Acc, 1), erlang:'band'(Value, 1))
            )
    end.

-file("src/packkit/deflate.gleam", 1343).
-spec reverse_bits(integer(), integer()) -> integer().
reverse_bits(Value, Count) ->
    reverse_bits_loop(Value, Count, 0).

-file("src/packkit/deflate.gleam", 1368).
-spec new_writer() -> writer().
new_writer() ->
    {writer, [], 0, 0}.

-file("src/packkit/deflate.gleam", 1393).
-spec mask_for(integer()) -> integer().
mask_for(Count) ->
    erlang:'bsl'(1, Count) - 1.

-file("src/packkit/deflate.gleam", 1397).
-spec flush_full_bytes(writer()) -> writer().
flush_full_bytes(Writer) ->
    case erlang:element(4, Writer) >= 8 of
        false ->
            Writer;

        true ->
            flush_full_bytes(
                {writer,
                    [erlang:'band'(erlang:element(3, Writer), 16#FF) |
                        erlang:element(2, Writer)],
                    erlang:'bsr'(erlang:element(3, Writer), 8),
                    erlang:element(4, Writer) - 8}
            )
    end.

-file("src/packkit/deflate.gleam", 1372).
-spec write_bits(writer(), integer(), integer()) -> writer().
write_bits(Writer, Value, Count) ->
    case Count of
        0 ->
            Writer;

        _ ->
            Buffer = erlang:'bor'(
                erlang:element(3, Writer),
                erlang:'bsl'(
                    erlang:'band'(Value, mask_for(Count)),
                    erlang:element(4, Writer)
                )
            ),
            flush_full_bytes(
                {writer,
                    erlang:element(2, Writer),
                    Buffer,
                    erlang:element(4, Writer) + Count}
            )
    end.

-file("src/packkit/deflate.gleam", 1339).
-spec write_huffman_code(writer(), integer(), integer()) -> writer().
write_huffman_code(Writer, Code, Length) ->
    write_bits(Writer, reverse_bits(Code, Length), Length).

-file("src/packkit/deflate.gleam", 1330).
-spec write_fixed_literal_code(writer(), integer()) -> writer().
write_fixed_literal_code(Writer, Symbol) ->
    {Code, Length} = fixed_literal_code(Symbol),
    write_huffman_code(Writer, Code, Length).

-file("src/packkit/deflate.gleam", 1335).
-spec write_fixed_distance_code(writer(), integer()) -> writer().
write_fixed_distance_code(Writer, Symbol) ->
    write_huffman_code(Writer, Symbol, 5).

-file("src/packkit/deflate.gleam", 1254).
-spec write_match(writer(), integer(), integer()) -> writer().
write_match(Writer, Length, Distance) ->
    {Length_sym, Length_extra_count, Length_extra_value} = length_code(Length),
    Writer@1 = write_fixed_literal_code(Writer, Length_sym),
    Writer@2 = write_bits(Writer@1, Length_extra_value, Length_extra_count),
    {Dist_sym, Dist_extra_count, Dist_extra_value} = distance_code(Distance),
    Writer@3 = write_fixed_distance_code(Writer@2, Dist_sym),
    write_bits(Writer@3, Dist_extra_value, Dist_extra_count).

-file("src/packkit/deflate.gleam", 1422).
-spec list_to_bit_array(list(integer()), bitstring()) -> bitstring().
list_to_bit_array(Values, Acc) ->
    case Values of
        [] ->
            Acc;

        [Head | Rest] ->
            list_to_bit_array(Rest, <<Acc/bitstring, Head>>)
    end.

-file("src/packkit/deflate.gleam", 1409).
-spec flush_writer(writer()) -> bitstring().
flush_writer(Writer) ->
    Writer@1 = case erlang:element(4, Writer) of
        0 ->
            Writer;

        _ ->
            {writer,
                [erlang:'band'(erlang:element(3, Writer), 16#FF) |
                    erlang:element(2, Writer)],
                0,
                0}
    end,
    list_to_bit_array(lists:reverse(erlang:element(2, Writer@1)), <<>>).

-file("src/packkit/deflate.gleam", 1438).
-spec list_get_loop(list(integer()), integer(), integer()) -> integer().
list_get_loop(Values, Index, Default) ->
    case {Values, Index} of
        {[], _} ->
            Default;

        {[Head | _], 0} ->
            Head;

        {[_ | Rest], _} ->
            list_get_loop(Rest, Index - 1, Default)
    end.

-file("src/packkit/deflate.gleam", 1431).
-spec list_get(list(integer()), integer(), integer()) -> integer().
list_get(Values, Index, Default) ->
    case Index < 0 of
        true ->
            Default;

        false ->
            list_get_loop(Values, Index, Default)
    end.

-file("src/packkit/deflate.gleam", 1450).
-spec set_index_loop(list(integer()), integer(), integer(), list(integer())) -> list(integer()).
set_index_loop(Values, Index, Value, Acc) ->
    case {Values, Index} of
        {[], 0} ->
            lists:reverse([Value | Acc]);

        {[], N} ->
            set_index_loop([], N - 1, Value, [0 | Acc]);

        {[_ | Rest], 0} ->
            _pipe = lists:reverse([Value | Acc]),
            lists:append(_pipe, Rest);

        {[Head | Rest@1], N@1} ->
            set_index_loop(Rest@1, N@1 - 1, Value, [Head | Acc])
    end.

-file("src/packkit/deflate.gleam", 1446).
-spec set_index(list(integer()), integer(), integer()) -> list(integer()).
set_index(Values, Index, Value) ->
    set_index_loop(Values, Index, Value, []).

-file("src/packkit/deflate.gleam", 288).
-spec count_lengths_loop(list(integer()), list(integer())) -> list(integer()).
count_lengths_loop(Lengths, Counts) ->
    case Lengths of
        [] ->
            Counts;

        [0 | Rest] ->
            count_lengths_loop(Rest, Counts);

        [Length | Rest@1] ->
            count_lengths_loop(
                Rest@1,
                set_index(Counts, Length, list_get(Counts, Length, 0) + 1)
            )
    end.

-file("src/packkit/deflate.gleam", 280).
-spec count_lengths(list(integer())) -> list(integer()).
count_lengths(Lengths) ->
    count_lengths_loop(Lengths, empty_counts()).

-file("src/packkit/deflate.gleam", 588).
-spec read_code_lengths_loop(reader(), integer(), integer(), list(integer())) -> {ok,
        {list(integer()), reader()}} |
    {error, packkit@error:codec_error()}.
read_code_lengths_loop(Reader, Remaining, Index, Acc) ->
    case Remaining of
        0 ->
            {ok, {Acc, Reader}};

        _ ->
            gleam@result:'try'(
                read_bits(Reader, 3),
                fun(_use0) ->
                    {Value, Reader@1} = _use0,
                    Slot = list_get(code_length_order(), Index, 0),
                    read_code_lengths_loop(
                        Reader@1,
                        Remaining - 1,
                        Index + 1,
                        set_index(Acc, Slot, Value)
                    )
                end
            )
    end.

-file("src/packkit/deflate.gleam", 573).
-spec read_code_length_table(reader(), integer()) -> {ok,
        {list(integer()), reader()}} |
    {error, packkit@error:codec_error()}.
read_code_length_table(Reader, Count) ->
    read_code_lengths_loop(Reader, Count, 0, empty_clcl_table()).

-file("src/packkit/deflate.gleam", 1505).
-spec bytes_from_table_unused(bitstring()) -> bitstring().
bytes_from_table_unused(Bytes) ->
    Bytes.

-file("src/packkit/deflate.gleam", 1629).
-spec bump_index(list(integer()), integer()) -> list(integer()).
bump_index(Values, Index) ->
    set_index(Values, Index, list_get(Values, Index, 0) + 1).

-file("src/packkit/deflate.gleam", 1608).
-spec token_freq_loop(list(token()), list(integer()), list(integer())) -> {list(integer()),
    list(integer())}.
token_freq_loop(Tokens, Lit_freqs, Dist_freqs) ->
    case Tokens of
        [] ->
            {Lit_freqs, Dist_freqs};

        [{tok_lit, V} | Rest] ->
            token_freq_loop(Rest, bump_index(Lit_freqs, V), Dist_freqs);

        [{tok_match, L, D} | Rest@1] ->
            {Lit_sym, _, _} = length_code(L),
            {Dist_sym, _, _} = distance_code(D),
            token_freq_loop(
                Rest@1,
                bump_index(Lit_freqs, Lit_sym),
                bump_index(Dist_freqs, Dist_sym)
            )
    end.

-file("src/packkit/deflate.gleam", 1684).
-spec collect_nonzero(list(integer()), integer(), list({integer(), integer()})) -> list({integer(),
    integer()}).
collect_nonzero(Freqs, Index, Acc) ->
    case Freqs of
        [] ->
            lists:reverse(Acc);

        [0 | Rest] ->
            collect_nonzero(Rest, Index + 1, Acc);

        [N | Rest@1] ->
            collect_nonzero(Rest@1, Index + 1, [{Index, N} | Acc])
    end.

-file("src/packkit/deflate.gleam", 1696).
-spec sort_nonzero_pairs(list({integer(), integer()})) -> list({integer(),
    integer()}).
sort_nonzero_pairs(Pairs) ->
    gleam@list:sort(
        Pairs,
        fun(A, B) ->
            {Sym_a, Freq_a} = A,
            {Sym_b, Freq_b} = B,
            case gleam@int:compare(Freq_a, Freq_b) of
                eq ->
                    gleam@int:compare(Sym_a, Sym_b);

                Ord ->
                    Ord
            end
        end
    ).

-file("src/packkit/deflate.gleam", 1716).
-spec insert_sorted_node(
    {integer(), huff_tree()},
    list({integer(), huff_tree()})
) -> list({integer(), huff_tree()}).
insert_sorted_node(Item, Rest) ->
    {W, _} = Item,
    case Rest of
        [] ->
            [Item];

        [Head | Tail] ->
            {Hw, _} = Head,
            case W =< Hw of
                true ->
                    [Item | Rest];

                false ->
                    [Head | insert_sorted_node(Item, Tail)]
            end
    end.

-file("src/packkit/deflate.gleam", 1707).
-spec combine_huffman(list({integer(), huff_tree()})) -> huff_tree().
combine_huffman(Nodes) ->
    case Nodes of
        [] ->
            {h_leaf, 0};

        [{_, Tree}] ->
            Tree;

        [{W1, T1}, {W2, T2} | Rest] ->
            combine_huffman(
                insert_sorted_node({W1 + W2, {h_node, T1, T2}}, Rest)
            )
    end.

-file("src/packkit/deflate.gleam", 1733).
-spec collect_huffman_lengths(
    huff_tree(),
    integer(),
    list({integer(), integer()})
) -> list({integer(), integer()}).
collect_huffman_lengths(Tree, Depth, Acc) ->
    case Tree of
        {h_leaf, Sym} ->
            [{Sym, Depth} | Acc];

        {h_node, Left, Right} ->
            collect_huffman_lengths(
                Right,
                Depth + 1,
                collect_huffman_lengths(Left, Depth + 1, Acc)
            )
    end.

-file("src/packkit/deflate.gleam", 1749).
-spec list_max(list({integer(), integer()}), integer()) -> integer().
list_max(Pairs, Best) ->
    case Pairs of
        [] ->
            Best;

        [{_, N} | Rest] ->
            case N > Best of
                true ->
                    list_max(Rest, N);

                false ->
                    list_max(Rest, Best)
            end
    end.

-file("src/packkit/deflate.gleam", 1765).
-spec make_length_vector_loop(
    integer(),
    integer(),
    gleam@dict:dict(integer(), integer()),
    list(integer())
) -> list(integer()).
make_length_vector_loop(Size, Index, Lookup, Acc) ->
    case Index >= Size of
        true ->
            lists:reverse(Acc);

        false ->
            Value = case gleam_stdlib:map_get(Lookup, Index) of
                {ok, V} ->
                    V;

                {error, _} ->
                    0
            end,
            make_length_vector_loop(Size, Index + 1, Lookup, [Value | Acc])
    end.

-file("src/packkit/deflate.gleam", 1760).
-spec make_length_vector(integer(), list({integer(), integer()})) -> list(integer()).
make_length_vector(Alphabet_size, Pairs) ->
    Lookup = maps:from_list(Pairs),
    make_length_vector_loop(Alphabet_size, 0, Lookup, []).

-file("src/packkit/deflate.gleam", 1650).
?DOC(
    " Build code lengths for an alphabet of `alphabet_size` symbols given\n"
    " their frequencies (zero-frequency symbols get length 0).  Returns\n"
    " `Error(Nil)` when the natural Huffman tree would exceed `max_len`\n"
    " bits so the caller can fall back to a different block strategy.\n"
).
-spec huffman_code_lengths(list(integer()), integer(), integer()) -> {ok,
        list(integer())} |
    {error, nil}.
huffman_code_lengths(Freqs, Alphabet_size, Max_len) ->
    Nonzero = collect_nonzero(Freqs, 0, []),
    case Nonzero of
        [] ->
            {ok, gleam@list:repeat(0, Alphabet_size)};

        [{Sym, _}] ->
            {ok, make_length_vector(Alphabet_size, [{Sym, 1}])};

        _ ->
            Sorted_initial = sort_nonzero_pairs(Nonzero),
            Leaves = gleam@list:map(
                Sorted_initial,
                fun(Pair) ->
                    {Sym@1, Freq} = Pair,
                    {Freq, {h_leaf, Sym@1}}
                end
            ),
            Tree = combine_huffman(Leaves),
            Lengths = collect_huffman_lengths(Tree, 0, []),
            case list_max(Lengths, 0) > Max_len of
                true ->
                    {error, nil};

                false ->
                    {ok, make_length_vector(Alphabet_size, Lengths)}
            end
    end.

-file("src/packkit/deflate.gleam", 1809).
-spec assign_canonical(
    list({integer(), integer()}),
    integer(),
    integer(),
    gleam@dict:dict(integer(), {integer(), integer()})
) -> gleam@dict:dict(integer(), {integer(), integer()}).
assign_canonical(Remaining, Code, Prev_len, Acc) ->
    case Remaining of
        [] ->
            Acc;

        [{Sym, Len} | Rest] ->
            Shifted = erlang:'bsl'(Code, Len - Prev_len),
            assign_canonical(
                Rest,
                Shifted + 1,
                Len,
                gleam@dict:insert(Acc, Sym, {Shifted, Len})
            )
    end.

-file("src/packkit/deflate.gleam", 1788).
?DOC(
    " Map symbol → `(code_value, code_length)` for every symbol with\n"
    " non-zero length, using the canonical-Huffman recurrence.  Symbols\n"
    " with length 0 are omitted from the result.\n"
).
-spec canonical_codes_from_lengths(list(integer())) -> gleam@dict:dict(integer(), {integer(),
    integer()}).
canonical_codes_from_lengths(Lengths) ->
    Pairs = enumerate_lengths(Lengths, 0, []),
    Active = gleam@list:filter(
        Pairs,
        fun(P) ->
            {_, Len} = P,
            Len > 0
        end
    ),
    Sorted = gleam@list:sort(
        Active,
        fun(A, B) ->
            {Sa, La} = A,
            {Sb, Lb} = B,
            case gleam@int:compare(La, Lb) of
                eq ->
                    gleam@int:compare(Sa, Sb);

                Ord ->
                    Ord
            end
        end
    ),
    assign_canonical(Sorted, 0, 0, maps:new()).

-file("src/packkit/deflate.gleam", 1873).
-spec flush_zero_run(integer(), list(c_l_op())) -> list(c_l_op()).
flush_zero_run(Count, Acc) ->
    case Count of
        0 ->
            Acc;

        1 ->
            [{c_l_lit, 0} | Acc];

        2 ->
            [{c_l_lit, 0}, {c_l_lit, 0} | Acc];

        N when N =< 10 ->
            [{c_l_zero3, N - 3} | Acc];

        N@1 when N@1 =< 138 ->
            [{c_l_zero11, N@1 - 11} | Acc];

        N@2 ->
            flush_zero_run(N@2 - 138, [{c_l_zero11, 127} | Acc])
    end.

-file("src/packkit/deflate.gleam", 1901).
-spec emit_nonzero_run(integer(), integer(), list(c_l_op())) -> list(c_l_op()).
emit_nonzero_run(Value, Remaining, Acc) ->
    case Remaining of
        N when N =< 0 ->
            Acc;

        1 ->
            [{c_l_lit, Value} | Acc];

        2 ->
            [{c_l_lit, Value}, {c_l_lit, Value} | Acc];

        N@1 when N@1 =< 6 ->
            [{c_l_copy, N@1 - 3} | Acc];

        N@2 ->
            emit_nonzero_run(Value, N@2 - 6, [{c_l_copy, 3} | Acc])
    end.

-file("src/packkit/deflate.gleam", 1887).
-spec flush_nonzero_run(integer(), integer(), list(c_l_op())) -> list(c_l_op()).
flush_nonzero_run(Value, Count, Acc) ->
    case Count of
        0 ->
            Acc;

        _ ->
            emit_nonzero_run(Value, Count - 1, [{c_l_lit, Value} | Acc])
    end.

-file("src/packkit/deflate.gleam", 1862).
-spec flush_run(integer(), integer(), list(c_l_op())) -> list(c_l_op()).
flush_run(Value, Count, Acc) ->
    case Count of
        0 ->
            Acc;

        _ ->
            case Value of
                0 ->
                    flush_zero_run(Count, Acc);

                _ ->
                    flush_nonzero_run(Value, Count, Acc)
            end
    end.

-file("src/packkit/deflate.gleam", 1843).
-spec rle_loop(list(integer()), integer(), integer(), list(c_l_op())) -> list(c_l_op()).
rle_loop(Remaining, Current, Count, Acc) ->
    case Remaining of
        [] ->
            flush_run(Current, Count, Acc);

        [Head | Rest] ->
            case Head =:= Current of
                true ->
                    rle_loop(Rest, Current, Count + 1, Acc);

                false ->
                    Acc@1 = flush_run(Current, Count, Acc),
                    rle_loop(Rest, Head, 1, Acc@1)
            end
    end.

-file("src/packkit/deflate.gleam", 1838).
-spec rle_encode_lengths(list(integer())) -> list(c_l_op()).
rle_encode_lengths(Lengths) ->
    _pipe = rle_loop(Lengths, -1, 0, []),
    lists:reverse(_pipe).

-file("src/packkit/deflate.gleam", 1917).
-spec cl_freq_loop(list(c_l_op()), list(integer())) -> list(integer()).
cl_freq_loop(Rle, Freqs) ->
    case Rle of
        [] ->
            Freqs;

        [{c_l_lit, V} | Rest] ->
            cl_freq_loop(Rest, bump_index(Freqs, V));

        [{c_l_copy, _} | Rest@1] ->
            cl_freq_loop(Rest@1, bump_index(Freqs, 16));

        [{c_l_zero3, _} | Rest@2] ->
            cl_freq_loop(Rest@2, bump_index(Freqs, 17));

        [{c_l_zero11, _} | Rest@3] ->
            cl_freq_loop(Rest@3, bump_index(Freqs, 18))
    end.

-file("src/packkit/deflate.gleam", 1957).
-spec any_nonzero(list(integer())) -> boolean().
any_nonzero(Values) ->
    case Values of
        [] ->
            false;

        [0 | Rest] ->
            any_nonzero(Rest);

        _ ->
            true
    end.

-file("src/packkit/deflate.gleam", 2016).
-spec last_nonzero_index(list(integer()), integer(), integer()) -> integer().
last_nonzero_index(Values, Index, Best) ->
    case Values of
        [] ->
            Best;

        [0 | Rest] ->
            last_nonzero_index(Rest, Index + 1, Best);

        [_ | Rest@1] ->
            last_nonzero_index(Rest@1, Index + 1, Index)
    end.

-file("src/packkit/deflate.gleam", 2024).
-spec take_first(list(integer()), integer(), list(integer())) -> list(integer()).
take_first(Values, N, Acc) ->
    case {N, Values} of
        {0, _} ->
            lists:reverse(Acc);

        {_, []} ->
            take_first([], N - 1, [0 | Acc]);

        {_, [Head | Rest]} ->
            take_first(Rest, N - 1, [Head | Acc])
    end.

-file("src/packkit/deflate.gleam", 2032).
-spec highest_present_clcl(
    list(integer()),
    list(integer()),
    integer(),
    integer()
) -> integer().
highest_present_clcl(Lengths, Order, Index, Best) ->
    case Order of
        [] ->
            Best;

        [Slot | Rest] ->
            case list_get(Lengths, Slot, 0) of
                0 ->
                    highest_present_clcl(Lengths, Rest, Index + 1, Best);

                _ ->
                    highest_present_clcl(Lengths, Rest, Index + 1, Index)
            end
    end.

-file("src/packkit/deflate.gleam", 2048).
-spec max_int(integer(), integer()) -> integer().
max_int(A, B) ->
    case A > B of
        true ->
            A;

        false ->
            B
    end.

-file("src/packkit/deflate.gleam", 2055).
-spec write_clcl_lengths(
    writer(),
    list(integer()),
    list(integer()),
    integer(),
    integer()
) -> writer().
write_clcl_lengths(Writer, Order, Lengths, Hclen, Emitted) ->
    case {Emitted >= Hclen, Order} of
        {true, _} ->
            Writer;

        {_, []} ->
            Writer;

        {_, [Slot | Rest]} ->
            Length = list_get(Lengths, Slot, 0),
            write_clcl_lengths(
                write_bits(Writer, Length, 3),
                Rest,
                Lengths,
                Hclen,
                Emitted + 1
            )
    end.

-file("src/packkit/deflate.gleam", 2136).
-spec write_canonical_code(
    writer(),
    gleam@dict:dict(integer(), {integer(), integer()}),
    integer()
) -> writer().
write_canonical_code(Writer, Codes, Symbol) ->
    case gleam_stdlib:map_get(Codes, Symbol) of
        {ok, {Code, Length}} ->
            write_huffman_code(Writer, Code, Length);

        {error, _} ->
            Writer
    end.

-file("src/packkit/deflate.gleam", 2078).
-spec write_rle_stream(
    writer(),
    list(c_l_op()),
    gleam@dict:dict(integer(), {integer(), integer()})
) -> writer().
write_rle_stream(Writer, Rle, Cl_codes) ->
    case Rle of
        [] ->
            Writer;

        [{c_l_lit, V} | Rest] ->
            write_rle_stream(
                write_canonical_code(Writer, Cl_codes, V),
                Rest,
                Cl_codes
            );

        [{c_l_copy, Extra} | Rest@1] ->
            Writer@1 = write_canonical_code(Writer, Cl_codes, 16),
            Writer@2 = write_bits(Writer@1, Extra, 2),
            write_rle_stream(Writer@2, Rest@1, Cl_codes);

        [{c_l_zero3, Extra@1} | Rest@2] ->
            Writer@3 = write_canonical_code(Writer, Cl_codes, 17),
            Writer@4 = write_bits(Writer@3, Extra@1, 3),
            write_rle_stream(Writer@4, Rest@2, Cl_codes);

        [{c_l_zero11, Extra@2} | Rest@3] ->
            Writer@5 = write_canonical_code(Writer, Cl_codes, 18),
            Writer@6 = write_bits(Writer@5, Extra@2, 7),
            write_rle_stream(Writer@6, Rest@3, Cl_codes)
    end.

-file("src/packkit/deflate.gleam", 2109).
-spec write_token_stream(
    writer(),
    list(token()),
    gleam@dict:dict(integer(), {integer(), integer()}),
    gleam@dict:dict(integer(), {integer(), integer()})
) -> writer().
write_token_stream(Writer, Tokens, Lit_codes, Dist_codes) ->
    case Tokens of
        [] ->
            Writer;

        [{tok_lit, V} | Rest] ->
            write_token_stream(
                write_canonical_code(Writer, Lit_codes, V),
                Rest,
                Lit_codes,
                Dist_codes
            );

        [{tok_match, L, D} | Rest@1] ->
            {Lit_sym, Len_extra_bits, Len_extra} = length_code(L),
            Writer@1 = write_canonical_code(Writer, Lit_codes, Lit_sym),
            Writer@2 = write_bits(Writer@1, Len_extra, Len_extra_bits),
            {Dist_sym, Dist_extra_bits, Dist_extra} = distance_code(D),
            Writer@3 = write_canonical_code(Writer@2, Dist_codes, Dist_sym),
            Writer@4 = write_bits(Writer@3, Dist_extra, Dist_extra_bits),
            write_token_stream(Writer@4, Rest@1, Lit_codes, Dist_codes)
    end.

-file("src/packkit/deflate.gleam", 300).
-spec validate_counts(list(integer()), integer(), integer(), integer()) -> {ok,
        integer()} |
    {error, packkit@error:codec_error()}.
validate_counts(Counts, Index, Available, Total) ->
    case Index > 15 of
        true ->
            case (Total > 1) andalso (Available > 0) of
                true ->
                    {error,
                        {codec_invalid_data,
                            <<"incomplete Huffman code lengths"/utf8>>}};

                false ->
                    {ok, Total}
            end;

        false ->
            Used = list_get(Counts, Index, 0),
            case Used > Available of
                true ->
                    {error,
                        {codec_invalid_data,
                            <<"over-subscribed Huffman code lengths"/utf8>>}};

                false ->
                    validate_counts(
                        Counts,
                        Index + 1,
                        2 * (Available - Used),
                        Total + Used
                    )
            end
    end.

-file("src/packkit/deflate.gleam", 345).
-spec build_offsets(list(integer()), integer(), integer(), list(integer())) -> list(integer()).
build_offsets(Counts, Cumulative, Index, Acc) ->
    case Index > 15 of
        true ->
            lists:reverse(Acc);

        false ->
            Used = list_get(Counts, Index, 0),
            build_offsets(
                Counts,
                Cumulative + Used,
                Index + 1,
                [Cumulative | Acc]
            )
    end.

-file("src/packkit/deflate.gleam", 372).
-spec bucket_sort_symbols(
    list({integer(), integer()}),
    integer(),
    list(integer())
) -> list(integer()).
bucket_sort_symbols(Pairs, Length, Acc) ->
    case Length > 15 of
        true ->
            lists:reverse(Acc);

        false ->
            Bucket = gleam@list:filter_map(
                Pairs,
                fun(Pair) -> case erlang:element(2, Pair) =:= Length of
                        true ->
                            {ok, erlang:element(1, Pair)};

                        false ->
                            {error, nil}
                    end end
            ),
            bucket_sort_symbols(
                Pairs,
                Length + 1,
                prepend_reversed(Bucket, Acc)
            )
    end.

-file("src/packkit/deflate.gleam", 335).
-spec sort_symbols_by_length(list(integer()), list(integer())) -> list(integer()).
sort_symbols_by_length(Lengths, Counts) ->
    Offsets = build_offsets(Counts, 0, 1, []),
    Pairs = enumerate_lengths(Lengths, 0, []),
    _ = Offsets,
    bucket_sort_symbols(Pairs, 1, []).

-file("src/packkit/deflate.gleam", 237).
-spec build_tree(list(integer())) -> {ok, tree()} |
    {error, packkit@error:codec_error()}.
build_tree(Lengths) ->
    Max_sym = find_max_symbol(Lengths, 0, -1, 0),
    Counts = count_lengths(Lengths),
    gleam@result:'try'(
        validate_counts(Counts, 0, 1, 0),
        fun(_) ->
            Symbols = sort_symbols_by_length(Lengths, Counts),
            Counts_normalized = case {erlang:length(Symbols) =:= 1,
                list_get(Counts, 1, 0) =:= 1} of
                {true, true} ->
                    set_index(Counts, 1, 2);

                {_, _} ->
                    Counts
            end,
            Symbols_normalized = case erlang:length(Symbols) =:= 1 of
                true ->
                    [single_symbol(Symbols), Max_sym + 1 | []];

                false ->
                    Symbols
            end,
            {ok, {tree, Counts_normalized, Symbols_normalized, Max_sym}}
        end
    ).

-file("src/packkit/deflate.gleam", 406).
-spec decode_symbol_loop(reader(), tree(), integer(), integer(), integer()) -> {ok,
        {integer(), reader()}} |
    {error, packkit@error:codec_error()}.
decode_symbol_loop(Reader, Tree, Length, Base, Offset) ->
    case Length > 15 of
        true ->
            {error,
                {codec_invalid_data, <<"invalid Huffman code (overlong)"/utf8>>}};

        false ->
            gleam@result:'try'(
                read_bits(Reader, 1),
                fun(_use0) ->
                    {Bit, Reader@1} = _use0,
                    Offset@1 = (2 * Offset) + Bit,
                    Count = list_get(erlang:element(2, Tree), Length, 0),
                    case Offset@1 < Count of
                        true ->
                            {ok,
                                {list_get(
                                        erlang:element(3, Tree),
                                        Base + Offset@1,
                                        -1
                                    ),
                                    Reader@1}};

                        false ->
                            decode_symbol_loop(
                                Reader@1,
                                Tree,
                                Length + 1,
                                Base + Count,
                                Offset@1 - Count
                            )
                    end
                end
            )
    end.

-file("src/packkit/deflate.gleam", 399).
-spec decode_symbol(reader(), tree()) -> {ok, {integer(), reader()}} |
    {error, packkit@error:codec_error()}.
decode_symbol(Reader, Tree) ->
    decode_symbol_loop(Reader, Tree, 1, 0, 0).

-file("src/packkit/deflate.gleam", 629).
-spec decode_length_run(
    reader(),
    tree(),
    integer(),
    integer(),
    list(integer()),
    integer()
) -> {ok, {list(integer()), reader()}} | {error, packkit@error:codec_error()}.
decode_length_run(Reader, Tree, Total, Produced, Acc, Previous) ->
    case Produced >= Total of
        true ->
            {ok, {lists:reverse(Acc), Reader}};

        false ->
            gleam@result:'try'(
                decode_symbol(Reader, Tree),
                fun(_use0) ->
                    {Symbol, Reader@1} = _use0,
                    gleam@bool:guard(
                        (Symbol > 18) orelse (Symbol < 0),
                        {error,
                            {codec_invalid_data,
                                <<"invalid code-length symbol"/utf8>>}},
                        fun() -> case Symbol of
                                N when N < 16 ->
                                    decode_length_run(
                                        Reader@1,
                                        Tree,
                                        Total,
                                        Produced + 1,
                                        [N | Acc],
                                        N
                                    );

                                16 ->
                                    gleam@result:'try'(
                                        read_bits(Reader@1, 2),
                                        fun(_use0@1) ->
                                            {Extra, Reader@2} = _use0@1,
                                            Count = 3 + Extra,
                                            gleam@bool:guard(
                                                Produced =:= 0,
                                                {error,
                                                    {codec_invalid_data,
                                                        <<"repeat-previous symbol at start of run"/utf8>>}},
                                                fun() ->
                                                    gleam@bool:guard(
                                                        (Produced + Count) > Total,
                                                        {error,
                                                            {codec_invalid_data,
                                                                <<"code-length run exceeds declared total"/utf8>>}},
                                                        fun() ->
                                                            Updated = repeat_value(
                                                                Previous,
                                                                Count,
                                                                Acc
                                                            ),
                                                            decode_length_run(
                                                                Reader@2,
                                                                Tree,
                                                                Total,
                                                                Produced + Count,
                                                                Updated,
                                                                Previous
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    );

                                17 ->
                                    gleam@result:'try'(
                                        read_bits(Reader@1, 3),
                                        fun(_use0@2) ->
                                            {Extra@1, Reader@3} = _use0@2,
                                            Count@1 = 3 + Extra@1,
                                            gleam@bool:guard(
                                                (Produced + Count@1) > Total,
                                                {error,
                                                    {codec_invalid_data,
                                                        <<"zero-run symbol exceeds declared total"/utf8>>}},
                                                fun() ->
                                                    Updated@1 = repeat_value(
                                                        0,
                                                        Count@1,
                                                        Acc
                                                    ),
                                                    decode_length_run(
                                                        Reader@3,
                                                        Tree,
                                                        Total,
                                                        Produced + Count@1,
                                                        Updated@1,
                                                        0
                                                    )
                                                end
                                            )
                                        end
                                    );

                                _ ->
                                    gleam@result:'try'(
                                        read_bits(Reader@1, 7),
                                        fun(_use0@3) ->
                                            {Extra@2, Reader@4} = _use0@3,
                                            Count@2 = 11 + Extra@2,
                                            gleam@bool:guard(
                                                (Produced + Count@2) > Total,
                                                {error,
                                                    {codec_invalid_data,
                                                        <<"long zero-run exceeds declared total"/utf8>>}},
                                                fun() ->
                                                    Updated@2 = repeat_value(
                                                        0,
                                                        Count@2,
                                                        Acc
                                                    ),
                                                    decode_length_run(
                                                        Reader@4,
                                                        Tree,
                                                        Total,
                                                        Produced + Count@2,
                                                        Updated@2,
                                                        0
                                                    )
                                                end
                                            )
                                        end
                                    )
                            end end
                    )
                end
            )
    end.

-file("src/packkit/deflate.gleam", 609).
-spec decode_dynamic_lengths(reader(), tree(), integer(), integer()) -> {ok,
        {list(integer()), list(integer()), reader()}} |
    {error, packkit@error:codec_error()}.
decode_dynamic_lengths(Reader, Tree, Hlit, Hdist) ->
    Total = Hlit + Hdist,
    gleam@result:'try'(
        decode_length_run(Reader, Tree, Total, 0, [], 0),
        fun(_use0) ->
            {Combined, Reader@1} = _use0,
            Lit_lengths = pad_to_length(gleam@list:take(Combined, Hlit), 286),
            Dist_lengths = pad_to_length(gleam@list:drop(Combined, Hlit), 30),
            {ok, {Lit_lengths, Dist_lengths, Reader@1}}
        end
    ).

-file("src/packkit/deflate.gleam", 860).
-spec fixed_literal_tree() -> tree().
fixed_literal_tree() ->
    Lengths = build_fixed_literal_lengths(),
    Tree@1 = case build_tree(Lengths) of
        {ok, Tree} -> Tree;
        _assert_fail ->
            erlang:error(#{gleam_error => let_assert,
                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                        file => <<?FILEPATH/utf8>>,
                        module => <<"packkit/deflate"/utf8>>,
                        function => <<"fixed_literal_tree"/utf8>>,
                        line => 864,
                        value => _assert_fail,
                        start => 25783,
                        'end' => 25824,
                        pattern_start => 25794,
                        pattern_end => 25802})
    end,
    Tree@1.

-file("src/packkit/deflate.gleam", 868).
-spec fixed_distance_tree() -> tree().
fixed_distance_tree() ->
    Lengths = gleam@list:repeat(5, 32),
    Tree@1 = case build_tree(Lengths) of
        {ok, Tree} -> Tree;
        _assert_fail ->
            erlang:error(#{gleam_error => let_assert,
                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                        file => <<?FILEPATH/utf8>>,
                        module => <<"packkit/deflate"/utf8>>,
                        function => <<"fixed_distance_tree"/utf8>>,
                        line => 870,
                        value => _assert_fail,
                        start => 25907,
                        'end' => 25948,
                        pattern_start => 25918,
                        pattern_end => 25926})
    end,
    Tree@1.

-file("src/packkit/deflate.gleam", 749).
-spec inflate_huffman_step(
    reader(),
    bitstring(),
    tree(),
    tree(),
    packkit@limit:limits()
) -> {ok, huffman_step()} | {error, packkit@error:codec_error()}.
inflate_huffman_step(Reader, Output, Ltree, Dtree, Limits) ->
    gleam@result:'try'(
        decode_symbol(Reader, Ltree),
        fun(_use0) ->
            {Symbol, Reader@1} = _use0,
            case Symbol of
                S when S < 256 ->
                    gleam@result:'try'(
                        append_with_limit(Output, <<S>>, Limits),
                        fun(New_output) ->
                            {ok, {huffman_continue, New_output, Reader@1}}
                        end
                    );

                256 ->
                    {ok, {huffman_done, Output, Reader@1}};

                S@1 ->
                    case S@1 > 285 of
                        true ->
                            {error,
                                {codec_invalid_data,
                                    <<"literal/length code out of range"/utf8>>}};

                        false ->
                            Length_index = S@1 - 257,
                            Extra_bits = list_get(
                                length_extra_bits(),
                                Length_index,
                                0
                            ),
                            Base = list_get(length_base(), Length_index, 0),
                            gleam@result:'try'(
                                read_bits(Reader@1, Extra_bits),
                                fun(_use0@1) ->
                                    {Extra, Reader@2} = _use0@1,
                                    Length = Base + Extra,
                                    gleam@result:'try'(
                                        decode_symbol(Reader@2, Dtree),
                                        fun(_use0@2) ->
                                            {Dist_symbol, Reader@3} = _use0@2,
                                            gleam@bool:guard(
                                                Dist_symbol > 29,
                                                {error,
                                                    {codec_invalid_data,
                                                        <<"distance code out of range"/utf8>>}},
                                                fun() ->
                                                    Dist_extra_bits = list_get(
                                                        distance_extra_bits(),
                                                        Dist_symbol,
                                                        0
                                                    ),
                                                    Dist_base = list_get(
                                                        distance_base(),
                                                        Dist_symbol,
                                                        0
                                                    ),
                                                    gleam@result:'try'(
                                                        read_bits(
                                                            Reader@3,
                                                            Dist_extra_bits
                                                        ),
                                                        fun(_use0@3) ->
                                                            {Dist_extra,
                                                                Reader@4} = _use0@3,
                                                            Distance = Dist_base
                                                            + Dist_extra,
                                                            gleam@result:'try'(
                                                                apply_backref(
                                                                    Output,
                                                                    Distance,
                                                                    Length,
                                                                    Limits
                                                                ),
                                                                fun(
                                                                    New_output@1
                                                                ) ->
                                                                    {ok,
                                                                        {huffman_continue,
                                                                            New_output@1,
                                                                            Reader@4}}
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                    end
            end
        end
    ).

-file("src/packkit/deflate.gleam", 729).
-spec inflate_huffman_block(
    reader(),
    bitstring(),
    tree(),
    tree(),
    packkit@limit:limits()
) -> {ok, {bitstring(), reader()}} | {error, packkit@error:codec_error()}.
inflate_huffman_block(Reader, Output, Ltree, Dtree, Limits) ->
    case inflate_huffman_step(Reader, Output, Ltree, Dtree, Limits) of
        {error, Err} ->
            {error, Err};

        {ok, {huffman_done, Out, Rdr}} ->
            {ok, {Out, Rdr}};

        {ok, {huffman_continue, Out@1, Rdr@1}} ->
            inflate_huffman_block(Rdr@1, Out@1, Ltree, Dtree, Limits)
    end.

-file("src/packkit/deflate.gleam", 520).
-spec inflate_fixed(reader(), bitstring(), packkit@limit:limits()) -> {ok,
        {bitstring(), reader()}} |
    {error, packkit@error:codec_error()}.
inflate_fixed(Reader, Output, Limits) ->
    inflate_huffman_block(
        Reader,
        Output,
        fixed_literal_tree(),
        fixed_distance_tree(),
        Limits
    ).

-file("src/packkit/deflate.gleam", 534).
-spec inflate_dynamic(reader(), bitstring(), packkit@limit:limits()) -> {ok,
        {bitstring(), reader()}} |
    {error, packkit@error:codec_error()}.
inflate_dynamic(Reader, Output, Limits) ->
    gleam@result:'try'(
        read_bits(Reader, 5),
        fun(_use0) ->
            {Hlit_raw, Reader@1} = _use0,
            Hlit = Hlit_raw + 257,
            gleam@result:'try'(
                read_bits(Reader@1, 5),
                fun(_use0@1) ->
                    {Hdist_raw, Reader@2} = _use0@1,
                    Hdist = Hdist_raw + 1,
                    gleam@result:'try'(
                        read_bits(Reader@2, 4),
                        fun(_use0@2) ->
                            {Hclen_raw, Reader@3} = _use0@2,
                            Hclen = Hclen_raw + 4,
                            gleam@bool:guard(
                                (Hlit > 286) orelse (Hdist > 30),
                                {error,
                                    {codec_invalid_data,
                                        <<"dynamic header out of range"/utf8>>}},
                                fun() ->
                                    gleam@result:'try'(
                                        read_code_length_table(Reader@3, Hclen),
                                        fun(_use0@3) ->
                                            {Clcl_table, Reader@4} = _use0@3,
                                            gleam@result:'try'(
                                                build_tree(Clcl_table),
                                                fun(Clcl_tree) ->
                                                    gleam@result:'try'(
                                                        decode_dynamic_lengths(
                                                            Reader@4,
                                                            Clcl_tree,
                                                            Hlit,
                                                            Hdist
                                                        ),
                                                        fun(_use0@4) ->
                                                            {Lit_lengths,
                                                                Dist_lengths,
                                                                Reader@5} = _use0@4,
                                                            gleam@bool:guard(
                                                                list_get(
                                                                    Lit_lengths,
                                                                    256,
                                                                    0
                                                                )
                                                                =:= 0,
                                                                {error,
                                                                    {codec_invalid_data,
                                                                        <<"dynamic header missing end-of-block symbol"/utf8>>}},
                                                                fun() ->
                                                                    gleam@result:'try'(
                                                                        build_tree(
                                                                            Lit_lengths
                                                                        ),
                                                                        fun(
                                                                            Ltree
                                                                        ) ->
                                                                            gleam@result:'try'(
                                                                                build_tree(
                                                                                    Dist_lengths
                                                                                ),
                                                                                fun(
                                                                                    Dtree
                                                                                ) ->
                                                                                    inflate_huffman_block(
                                                                                        Reader@5,
                                                                                        Output,
                                                                                        Ltree,
                                                                                        Dtree,
                                                                                        Limits
                                                                                    )
                                                                                end
                                                                            )
                                                                        end
                                                                    )
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/packkit/deflate.gleam", 463).
-spec inflate_one_block(reader(), bitstring(), packkit@limit:limits()) -> {ok,
        inflate_step()} |
    {error, packkit@error:codec_error()}.
inflate_one_block(Reader, Output, Limits) ->
    gleam@result:'try'(
        read_bits(Reader, 1),
        fun(_use0) ->
            {Bfinal, Reader@1} = _use0,
            gleam@result:'try'(
                read_bits(Reader@1, 2),
                fun(_use0@1) ->
                    {Btype, Reader@2} = _use0@1,
                    gleam@result:'try'(case Btype of
                            0 ->
                                inflate_stored(Reader@2, Output, Limits);

                            1 ->
                                inflate_fixed(Reader@2, Output, Limits);

                            2 ->
                                inflate_dynamic(Reader@2, Output, Limits);

                            _ ->
                                {error,
                                    {codec_invalid_data,
                                        <<"reserved DEFLATE block type"/utf8>>}}
                        end, fun(_use0@2) ->
                            {Output@1, Reader@3} = _use0@2,
                            case Bfinal of
                                1 ->
                                    {ok, {inflate_done, Output@1, Reader@3}};

                                _ ->
                                    {ok, {inflate_continue, Output@1, Reader@3}}
                            end
                        end)
                end
            )
        end
    ).

-file("src/packkit/deflate.gleam", 446).
-spec inflate(reader(), bitstring(), packkit@limit:limits()) -> {ok,
        {bitstring(), reader()}} |
    {error, packkit@error:codec_error()}.
inflate(Reader, Output, Limits) ->
    case inflate_one_block(Reader, Output, Limits) of
        {error, Err} ->
            {error, Err};

        {ok, {inflate_done, Out, Rdr}} ->
            {ok, {Out, Rdr}};

        {ok, {inflate_continue, Out@1, Rdr@1}} ->
            inflate(Rdr@1, Out@1, Limits)
    end.

-file("src/packkit/deflate.gleam", 55).
?DOC(" Decode a raw DEFLATE byte stream using explicit limits.\n").
-spec decode_with_limits(bitstring(), packkit@limit:limits()) -> {ok,
        bitstring()} |
    {error, packkit@error:codec_error()}.
decode_with_limits(Bytes, Limits) ->
    gleam@bool:guard(
        erlang:byte_size(Bytes) > packkit@limit:max_input_bytes(Limits),
        {error,
            {codec_limit_exceeded,
                <<"max_input_bytes"/utf8>>,
                erlang:byte_size(Bytes)}},
        fun() ->
            Reader = {reader, 0, 0, Bytes, false},
            case inflate(Reader, <<>>, Limits) of
                {ok, {Output, _}} ->
                    {ok, Output};

                {error, Err} ->
                    {error, Err}
            end
        end
    ).

-file("src/packkit/deflate.gleam", 50).
?DOC(" Decode a raw DEFLATE byte stream using default limits.\n").
-spec decode(bitstring()) -> {ok, bitstring()} |
    {error, packkit@error:codec_error()}.
decode(Bytes) ->
    decode_with_limits(Bytes, packkit@limit:default()).

-file("src/packkit/deflate.gleam", 83).
?DOC(
    " Decode a DEFLATE stream AND return the byte slice that follows the\n"
    " last block in the input.  Useful for wrappers like gzip that need\n"
    " to know exactly where the deflate stream ends so they can read a\n"
    " trailer immediately after it (and, for multi-member streams, the\n"
    " next member that comes after the trailer).\n"
    "\n"
    " The remainder is byte-aligned: any partial bits left in the\n"
    " deflate decoder's buffer after the last block are discarded as\n"
    " inter-block padding per RFC 1952 §2.2.\n"
).
-spec decode_with_remainder(bitstring(), packkit@limit:limits()) -> {ok,
        {bitstring(), bitstring()}} |
    {error, packkit@error:codec_error()}.
decode_with_remainder(Bytes, Limits) ->
    gleam@bool:guard(
        erlang:byte_size(Bytes) > packkit@limit:max_input_bytes(Limits),
        {error,
            {codec_limit_exceeded,
                <<"max_input_bytes"/utf8>>,
                erlang:byte_size(Bytes)}},
        fun() ->
            Reader = {reader, 0, 0, Bytes, false},
            gleam@result:'try'(
                inflate(Reader, <<>>, Limits),
                fun(_use0) ->
                    {Output, Final_reader} = _use0,
                    Remainder = recover_remaining_bytes(Final_reader),
                    {ok, {Output, Remainder}}
                end
            )
        end
    ).

-file("src/packkit/deflate.gleam", 1041).
-spec encode_stored_blocks(bitstring(), integer(), integer(), list(bitstring())) -> bitstring().
encode_stored_blocks(Source, Remaining, Written, Acc) ->
    case Remaining of
        0 ->
            gleam_stdlib:bit_array_concat(lists:reverse(Acc));

        _ ->
            Chunk_size = case Remaining > 65535 of
                true ->
                    65535;

                false ->
                    Remaining
            end,
            Final_flag = case Chunk_size =:= Remaining of
                true ->
                    1;

                false ->
                    0
            end,
            Chunk@1 = case gleam_stdlib:bit_array_slice(
                Source,
                Written,
                Chunk_size
            ) of
                {ok, Chunk} -> Chunk;
                _assert_fail ->
                    erlang:error(#{gleam_error => let_assert,
                                message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                file => <<?FILEPATH/utf8>>,
                                module => <<"packkit/deflate"/utf8>>,
                                function => <<"encode_stored_blocks"/utf8>>,
                                line => 1058,
                                value => _assert_fail,
                                start => 28164,
                                'end' => 28231,
                                pattern_start => 28175,
                                pattern_end => 28184})
            end,
            Header = <<Final_flag,
                Chunk_size:16/little,
                (erlang:'bxor'(Chunk_size, 16#FFFF)):16/little>>,
            encode_stored_blocks(
                Source,
                Remaining - Chunk_size,
                Written + Chunk_size,
                [gleam_stdlib:bit_array_concat([Header, Chunk@1]) | Acc]
            )
    end.

-file("src/packkit/deflate.gleam", 1029).
-spec encode_stored(bitstring()) -> bitstring().
encode_stored(Bytes) ->
    Size = erlang:byte_size(Bytes),
    case Size of
        0 ->
            empty_stored_block();

        _ ->
            encode_stored_blocks(Bytes, Size, 0, [])
    end.

-file("src/packkit/deflate.gleam", 143).
?DOC(
    " Encode a byte stream as a sequence of stored (uncompressed)\n"
    " DEFLATE blocks.  Useful when the caller wants to bypass the\n"
    " match-finder, for instance to test the framing in isolation.\n"
).
-spec encode_stored_only(bitstring()) -> {ok, bitstring()} |
    {error, packkit@error:codec_error()}.
encode_stored_only(Bytes) ->
    {ok, encode_stored(Bytes)}.

-file("src/packkit/deflate.gleam", 1227).
-spec insert_hashes_in_range(
    gleam@dict:dict(integer(), integer()),
    gleam@dict:dict(integer(), integer()),
    integer(),
    integer(),
    integer()
) -> gleam@dict:dict(integer(), integer()).
insert_hashes_in_range(Table, Hashes, From, To, Size) ->
    case (From > To) orelse ((From + 3) > Size) of
        true ->
            Hashes;

        false ->
            Key = hash3(
                byte_at(Table, From),
                byte_at(Table, From + 1),
                byte_at(Table, From + 2)
            ),
            insert_hashes_in_range(
                Table,
                gleam@dict:insert(Hashes, Key, From),
                From + 1,
                To,
                Size
            )
    end.

-file("src/packkit/deflate.gleam", 1129).
-spec emit_lz77(
    gleam@dict:dict(integer(), integer()),
    integer(),
    integer(),
    gleam@dict:dict(integer(), integer()),
    writer()
) -> writer().
emit_lz77(Table, Size, Pos, Hashes, Writer) ->
    case Pos >= Size of
        true ->
            Writer;

        false ->
            case (Pos + 3) > Size of
                true ->
                    Writer@1 = write_fixed_literal_code(
                        Writer,
                        byte_at(Table, Pos)
                    ),
                    emit_lz77(Table, Size, Pos + 1, Hashes, Writer@1);

                false ->
                    B0 = byte_at(Table, Pos),
                    B1 = byte_at(Table, Pos + 1),
                    B2 = byte_at(Table, Pos + 2),
                    Key = hash3(B0, B1, B2),
                    case gleam_stdlib:map_get(Hashes, Key) of
                        {error, _} ->
                            Writer@2 = write_fixed_literal_code(Writer, B0),
                            emit_lz77(
                                Table,
                                Size,
                                Pos + 1,
                                gleam@dict:insert(Hashes, Key, Pos),
                                Writer@2
                            );

                        {ok, Prev} ->
                            Distance = Pos - Prev,
                            case (Distance =< 0) orelse (Distance > 32768) of
                                true ->
                                    Writer@3 = write_fixed_literal_code(
                                        Writer,
                                        B0
                                    ),
                                    emit_lz77(
                                        Table,
                                        Size,
                                        Pos + 1,
                                        gleam@dict:insert(Hashes, Key, Pos),
                                        Writer@3
                                    );

                                false ->
                                    Match = match_length(
                                        Table,
                                        Prev,
                                        Pos,
                                        Size,
                                        258,
                                        0
                                    ),
                                    case Match >= 3 of
                                        true ->
                                            Writer@4 = write_match(
                                                Writer,
                                                Match,
                                                Distance
                                            ),
                                            Next_hashes = insert_hashes_in_range(
                                                Table,
                                                gleam@dict:insert(
                                                    Hashes,
                                                    Key,
                                                    Pos
                                                ),
                                                Pos + 1,
                                                (Pos + Match) - 1,
                                                Size
                                            ),
                                            emit_lz77(
                                                Table,
                                                Size,
                                                Pos + Match,
                                                Next_hashes,
                                                Writer@4
                                            );

                                        false ->
                                            Writer@5 = write_fixed_literal_code(
                                                Writer,
                                                B0
                                            ),
                                            emit_lz77(
                                                Table,
                                                Size,
                                                Pos + 1,
                                                gleam@dict:insert(
                                                    Hashes,
                                                    Key,
                                                    Pos
                                                ),
                                                Writer@5
                                            )
                                    end
                            end
                    end
            end
    end.

-file("src/packkit/deflate.gleam", 1076).
-spec encode_huffman(bitstring()) -> bitstring().
encode_huffman(Bytes) ->
    Size = erlang:byte_size(Bytes),
    case Size of
        0 ->
            Writer = begin
                _pipe = new_writer(),
                _pipe@1 = write_bits(_pipe, 1, 1),
                _pipe@2 = write_bits(_pipe@1, 1, 2),
                write_fixed_literal_code(_pipe@2, 256)
            end,
            flush_writer(Writer);

        _ ->
            Byte_table = build_byte_table(Bytes, 0, maps:new()),
            Writer@1 = begin
                _pipe@3 = new_writer(),
                _pipe@4 = write_bits(_pipe@3, 1, 1),
                write_bits(_pipe@4, 1, 2)
            end,
            Writer@2 = emit_lz77(Byte_table, Size, 0, maps:new(), Writer@1),
            Writer@3 = write_fixed_literal_code(Writer@2, 256),
            flush_writer(Writer@3)
    end.

-file("src/packkit/deflate.gleam", 136).
?DOC(
    " Encode a byte stream as a fixed-Huffman DEFLATE block.\n"
    "\n"
    " The encoder uses a greedy LZ77 match-finder with a 3-byte hash\n"
    " chain and a 32 KiB sliding window, then emits the resulting\n"
    " literal/length/distance tokens through the RFC 1951 fixed\n"
    " Huffman table.\n"
).
-spec encode(bitstring()) -> {ok, bitstring()} |
    {error, packkit@error:codec_error()}.
encode(Bytes) ->
    {ok, encode_huffman(Bytes)}.

-file("src/packkit/deflate.gleam", 1509).
-spec empty_dynamic_block() -> bitstring().
empty_dynamic_block() ->
    encode_huffman(<<>>).

-file("src/packkit/deflate.gleam", 1517).
-spec collect_lz77_tokens(
    gleam@dict:dict(integer(), integer()),
    integer(),
    integer(),
    gleam@dict:dict(integer(), integer()),
    list(token())
) -> list(token()).
collect_lz77_tokens(Table, Size, Pos, Hashes, Acc) ->
    case Pos >= Size of
        true ->
            lists:reverse(Acc);

        false ->
            case (Pos + 3) > Size of
                true ->
                    collect_lz77_tokens(
                        Table,
                        Size,
                        Pos + 1,
                        Hashes,
                        [{tok_lit, byte_at(Table, Pos)} | Acc]
                    );

                false ->
                    B0 = byte_at(Table, Pos),
                    B1 = byte_at(Table, Pos + 1),
                    B2 = byte_at(Table, Pos + 2),
                    Key = hash3(B0, B1, B2),
                    case gleam_stdlib:map_get(Hashes, Key) of
                        {error, _} ->
                            collect_lz77_tokens(
                                Table,
                                Size,
                                Pos + 1,
                                gleam@dict:insert(Hashes, Key, Pos),
                                [{tok_lit, B0} | Acc]
                            );

                        {ok, Prev} ->
                            Distance = Pos - Prev,
                            case (Distance =< 0) orelse (Distance > 32768) of
                                true ->
                                    collect_lz77_tokens(
                                        Table,
                                        Size,
                                        Pos + 1,
                                        gleam@dict:insert(Hashes, Key, Pos),
                                        [{tok_lit, B0} | Acc]
                                    );

                                false ->
                                    M = match_length(
                                        Table,
                                        Prev,
                                        Pos,
                                        Size,
                                        258,
                                        0
                                    ),
                                    case M >= 3 of
                                        true ->
                                            Next_hashes = insert_hashes_in_range(
                                                Table,
                                                gleam@dict:insert(
                                                    Hashes,
                                                    Key,
                                                    Pos
                                                ),
                                                Pos + 1,
                                                (Pos + M) - 1,
                                                Size
                                            ),
                                            collect_lz77_tokens(
                                                Table,
                                                Size,
                                                Pos + M,
                                                Next_hashes,
                                                [{tok_match, M, Distance} | Acc]
                                            );

                                        false ->
                                            collect_lz77_tokens(
                                                Table,
                                                Size,
                                                Pos + 1,
                                                gleam@dict:insert(
                                                    Hashes,
                                                    Key,
                                                    Pos
                                                ),
                                                [{tok_lit, B0} | Acc]
                                            )
                                    end
                            end
                    end
            end
    end.

-file("src/packkit/deflate.gleam", 1602).
-spec token_frequencies(list(token())) -> {list(integer()), list(integer())}.
token_frequencies(Tokens) ->
    Lit_freqs = gleam@list:repeat(0, 286),
    Dist_freqs = gleam@list:repeat(0, 30),
    token_freq_loop(Tokens, Lit_freqs, Dist_freqs).

-file("src/packkit/deflate.gleam", 1913).
-spec cl_frequencies(list(c_l_op())) -> list(integer()).
cl_frequencies(Rle) ->
    cl_freq_loop(Rle, gleam@list:repeat(0, 19)).

-file("src/packkit/deflate.gleam", 1965).
-spec emit_dynamic_block(list(token()), list(integer()), list(integer())) -> {ok,
        bitstring()} |
    {error, nil}.
emit_dynamic_block(Tokens, Lit_lengths, Dist_lengths) ->
    Lit_codes = canonical_codes_from_lengths(Lit_lengths),
    Dist_codes = canonical_codes_from_lengths(Dist_lengths),
    Hlit = max_int(last_nonzero_index(Lit_lengths, 0, -1) + 1, 257),
    Hdist = max_int(last_nonzero_index(Dist_lengths, 0, -1) + 1, 1),
    Combined = lists:append(
        take_first(Lit_lengths, Hlit, []),
        take_first(Dist_lengths, Hdist, [])
    ),
    Rle = rle_encode_lengths(Combined),
    Cl_freqs = cl_frequencies(Rle),
    gleam@result:'try'(
        huffman_code_lengths(Cl_freqs, 19, 7),
        fun(Cl_lengths) ->
            Cl_codes = canonical_codes_from_lengths(Cl_lengths),
            Order = code_length_order(),
            Hclen = max_int(
                highest_present_clcl(Cl_lengths, Order, 0, -1) + 1,
                4
            ),
            Writer = begin
                _pipe = new_writer(),
                _pipe@1 = write_bits(_pipe, 1, 1),
                _pipe@2 = write_bits(_pipe@1, 2, 2),
                _pipe@3 = write_bits(_pipe@2, Hlit - 257, 5),
                _pipe@4 = write_bits(_pipe@3, Hdist - 1, 5),
                write_bits(_pipe@4, Hclen - 4, 4)
            end,
            Writer@1 = write_clcl_lengths(Writer, Order, Cl_lengths, Hclen, 0),
            Writer@2 = write_rle_stream(Writer@1, Rle, Cl_codes),
            Writer@3 = write_token_stream(
                Writer@2,
                Tokens,
                Lit_codes,
                Dist_codes
            ),
            Writer@4 = write_canonical_code(Writer@3, Lit_codes, 256),
            {ok, flush_writer(Writer@4)}
        end
    ).

-file("src/packkit/deflate.gleam", 1929).
-spec build_dynamic_block(list(token())) -> {ok, bitstring()} | {error, nil}.
build_dynamic_block(Tokens) ->
    {Lit_freqs, Dist_freqs} = token_frequencies(Tokens),
    Lit_freqs@1 = bump_index(Lit_freqs, 256),
    Dist_freqs@1 = case any_nonzero(Dist_freqs) of
        true ->
            Dist_freqs;

        false ->
            bump_index(Dist_freqs, 0)
    end,
    gleam@result:'try'(
        huffman_code_lengths(Lit_freqs@1, 286, 15),
        fun(Lit_lengths) ->
            gleam@result:'try'(
                huffman_code_lengths(Dist_freqs@1, 30, 15),
                fun(Dist_lengths) ->
                    emit_dynamic_block(Tokens, Lit_lengths, Dist_lengths)
                end
            )
        end
    ).

-file("src/packkit/deflate.gleam", 1487).
-spec encode_dynamic_or_fixed(bitstring()) -> bitstring().
encode_dynamic_or_fixed(Bytes) ->
    Size = erlang:byte_size(Bytes),
    case Size of
        0 ->
            empty_dynamic_block();

        _ ->
            Table = build_byte_table(Bytes, 0, maps:new()),
            Tokens = collect_lz77_tokens(Table, Size, 0, maps:new(), []),
            case build_dynamic_block(Tokens) of
                {ok, Bytes@1} ->
                    Bytes@1;

                {error, nil} ->
                    encode_huffman(bytes_from_table_unused(Bytes))
            end
    end.

-file("src/packkit/deflate.gleam", 163).
?DOC(
    " Encode a byte stream as a dynamic-Huffman DEFLATE block (BTYPE=10).\n"
    "\n"
    " Runs the same greedy LZ77 match-finder as [encode], but builds\n"
    " per-stream Huffman codes for the literal/length and distance\n"
    " alphabets from the observed symbol frequencies, then emits them as\n"
    " an RFC 1951 dynamic-Huffman block.  This usually compresses better\n"
    " than the fixed-Huffman path on real-world inputs because rare\n"
    " symbols get longer codes and common symbols get shorter ones.\n"
    "\n"
    " If the natural Huffman tree would exceed the RFC 1951 15-bit\n"
    " maximum code length for the literal/length or distance alphabet\n"
    " (which happens only on pathologically skewed inputs), the encoder\n"
    " transparently falls back to the fixed-Huffman path so the call\n"
    " still returns a valid stream.\n"
).
-spec encode_dynamic(bitstring()) -> {ok, bitstring()} |
    {error, packkit@error:codec_error()}.
encode_dynamic(Bytes) ->
    {ok, encode_dynamic_or_fixed(Bytes)}.