Skip to main content

src/packkit@xz.erl

-module(packkit@xz).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit/xz.gleam").
-export([codec/0, encode/1, decode_with_limits/2, decode/1]).
-export_type([size_field/0, uncompressed_field/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(
    " xz codec โ€” decoder for `.xz` streams.\n"
    "\n"
    " The decoder validates the xz magic, parses stream and block\n"
    " headers, walks the LZMA2 chunk sequence (uncompressed and\n"
    " LZMA-compressed chunks), validates the index plus stream footer,\n"
    " and emits the concatenated block payloads.  The LZMA range coder\n"
    " itself lives in `packkit/internal/lzma`.\n"
).

-type size_field() :: {compressed_known, integer()} | compressed_unknown.

-type uncompressed_field() :: {uncompressed_known, integer()} |
    uncompressed_unknown.

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

-file("src/packkit/xz.gleam", 56).
-spec encode_stream_header() -> bitstring().
encode_stream_header() ->
    Flags = <<16#00, 16#01>>,
    Crc = packkit@checksum:crc32(Flags),
    gleam_stdlib:bit_array_concat(
        [<<16#FD, 16#37, 16#7A, 16#58, 16#5A, 16#00>>, Flags, <<Crc:32/little>>]
    ).

-file("src/packkit/xz.gleam", 181).
-spec encode_stream_footer(integer()) -> bitstring().
encode_stream_footer(Index_size) ->
    Backward = (Index_size div 4) - 1,
    Flags = <<16#00, 16#01>>,
    Crc = packkit@checksum:crc32(<<Backward:32/little, Flags/bitstring>>),
    gleam_stdlib:bit_array_concat(
        [<<Crc:32/little, Backward:32/little>>, Flags, <<16#59, 16#5A>>]
    ).

-file("src/packkit/xz.gleam", 192).
-spec encode_varint(integer()) -> bitstring().
encode_varint(Value) ->
    case Value < 16#80 of
        true ->
            <<Value>>;

        false ->
            Byte = erlang:'bor'(erlang:'band'(Value, 16#7F), 16#80),
            gleam_stdlib:bit_array_concat(
                [<<Byte>>, encode_varint(erlang:'bsr'(Value, 7))]
            )
    end.

-file("src/packkit/xz.gleam", 205).
-spec pad_zero_bytes(integer()) -> bitstring().
pad_zero_bytes(Count) ->
    case Count of
        0 ->
            <<>>;

        _ ->
            gleam_stdlib:bit_array_concat([<<0>>, pad_zero_bytes(Count - 1)])
    end.

-file("src/packkit/xz.gleam", 212).
-spec round_up_to_4(integer()) -> integer().
round_up_to_4(Value) ->
    case Value rem 4 of
        0 ->
            Value;

        N ->
            (Value + 4) - N
    end.

-file("src/packkit/xz.gleam", 67).
-spec encode_block_header(integer(), integer()) -> bitstring().
encode_block_header(Compressed_size, Uncompressed_size) ->
    Comp_vi = encode_varint(Compressed_size),
    Pre_pad = gleam_stdlib:bit_array_concat(
        [<<16#C0>>,
            Comp_vi,
            encode_varint(Uncompressed_size),
            <<16#21, 16#01, 16#16>>]
    ),
    Body_len_no_size_byte = erlang:byte_size(Pre_pad) + 1,
    Total_no_pad = Body_len_no_size_byte + 4,
    Total_size = round_up_to_4(Total_no_pad),
    Header_size_byte = (Total_size div 4) - 1,
    Padding = pad_zero_bytes(Total_size - Total_no_pad),
    Body_with_size = gleam_stdlib:bit_array_concat(
        [<<Header_size_byte>>, Pre_pad, Padding]
    ),
    Crc = packkit@checksum:crc32(Body_with_size),
    gleam_stdlib:bit_array_concat([Body_with_size, <<Crc:32/little>>]).

-file("src/packkit/xz.gleam", 281).
?DOC(
    " Inter-stream padding is 0x00 bytes, always a multiple of 4.  Per\n"
    " the spec, the bytes between two streams must all be zero and the\n"
    " total count must be divisible by 4; we drop them so the next\n"
    " header parse starts at the right offset.\n"
).
-spec skip_stream_padding(bitstring()) -> bitstring().
skip_stream_padding(Bytes) ->
    case Bytes of
        <<16#00, 16#00, 16#00, 16#00, Rest/binary>> ->
            skip_stream_padding(Rest);

        _ ->
            Bytes
    end.

-file("src/packkit/xz.gleam", 325).
-spec bit_array_to_u32_le(bitstring()) -> integer().
bit_array_to_u32_le(Bytes) ->
    case Bytes of
        <<Value:32/little-unsigned>> ->
            Value;

        _ ->
            0
    end.

-file("src/packkit/xz.gleam", 290).
-spec parse_stream_header(bitstring()) -> {ok, {integer(), bitstring()}} |
    {error, packkit@error:codec_error()}.
parse_stream_header(Bytes) ->
    case Bytes of
        <<16#FD,
            16#37,
            16#7A,
            16#58,
            16#5A,
            16#00,
            Flag_zero,
            Check_type,
            Crc:4/binary,
            Rest/binary>> ->
            gleam@bool:guard(
                Flag_zero /= 0,
                {error,
                    {codec_invalid_data,
                        <<"xz stream header reserved byte is non-zero"/utf8>>}},
                fun() ->
                    Expected = packkit@checksum:crc32(<<Flag_zero, Check_type>>),
                    gleam@bool:guard(
                        Expected /= bit_array_to_u32_le(Crc),
                        {error,
                            {codec_invalid_data,
                                <<"xz stream header CRC mismatch"/utf8>>}},
                        fun() -> {ok, {Check_type, Rest}} end
                    )
                end
            );

        _ ->
            {error, {codec_invalid_data, <<"invalid xz stream header"/utf8>>}}
    end.

-file("src/packkit/xz.gleam", 336).
?DOC(
    " Decode 8 little-endian bytes as a `#(low_u32, high_u32)` pair.\n"
    " Kept separate from a single-Int return so the JavaScript target's\n"
    " 53-bit safe-integer ceiling does not silently corrupt the high\n"
    " bytes; CRC-64 verification needs every bit to be exact.\n"
).
-spec bit_array_to_u64_le_pair(bitstring()) -> {integer(), integer()}.
bit_array_to_u64_le_pair(Bytes) ->
    case Bytes of
        <<Low:32/little-unsigned, High:32/little-unsigned>> ->
            {Low, High};

        _ ->
            {0, 0}
    end.

-file("src/packkit/xz.gleam", 381).
-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/xz.gleam", 667).
-spec validate_pre_filters(list({integer(), integer()})) -> {ok, nil} |
    {error, packkit@error:codec_error()}.
validate_pre_filters(Filters) ->
    case Filters of
        [] ->
            {ok, nil};

        [{16#03, _} | Rest] ->
            validate_pre_filters(Rest);

        [{16#04, _} | Rest@1] ->
            validate_pre_filters(Rest@1);

        [{16#05, _} | Rest@2] ->
            validate_pre_filters(Rest@2);

        [{16#06, _} | Rest@3] ->
            validate_pre_filters(Rest@3);

        [{16#07, _} | Rest@4] ->
            validate_pre_filters(Rest@4);

        [{16#08, _} | Rest@5] ->
            validate_pre_filters(Rest@5);

        [{16#09, _} | Rest@6] ->
            validate_pre_filters(Rest@6);

        [{16#0A, _} | Rest@7] ->
            validate_pre_filters(Rest@7);

        [{16#0B, _} | Rest@8] ->
            validate_pre_filters(Rest@8);

        [{Id, _} | _] ->
            {error,
                {codec_not_implemented,
                    <<"xz pre-processor filter id "/utf8,
                        (erlang:integer_to_binary(Id))/binary>>}}
    end.

-file("src/packkit/xz.gleam", 642).
?DOC(
    " XZ blocks always terminate with the compression filter (LZMA2,\n"
    " id 0x21).  Any earlier filters in the chain are pre-processors\n"
    " that ran before LZMA2 saw the bytes; the decoder must apply\n"
    " their inverse to LZMA2's output in REVERSE chain order.  Split\n"
    " the parsed filter list into \"LZMA2 properties\" and \"the\n"
    " pre-filter chain (already in encode-order)\".\n"
).
-spec split_filter_chain(list({integer(), integer()})) -> {ok,
        {integer(), list({integer(), integer()})}} |
    {error, packkit@error:codec_error()}.
split_filter_chain(Filters) ->
    case Filters of
        [] ->
            {error, {codec_invalid_data, <<"xz block has no filters"/utf8>>}};

        _ ->
            Reversed = lists:reverse(Filters),
            case Reversed of
                [] ->
                    {error,
                        {codec_invalid_data, <<"xz block has no filters"/utf8>>}};

                [{Id, _} | _] when Id =/= 16#21 ->
                    {error,
                        {codec_not_implemented,
                            <<"xz blocks whose final filter is not LZMA2"/utf8>>}};

                [{_, Props} | Pre_reversed] ->
                    Pre_filters = lists:reverse(Pre_reversed),
                    gleam@result:'try'(
                        validate_pre_filters(Pre_filters),
                        fun(_) -> {ok, {Props, Pre_filters}} end
                    )
            end
    end.

-file("src/packkit/xz.gleam", 785).
-spec nth_from_end(list(integer()), integer()) -> integer().
nth_from_end(Reversed_list, Index) ->
    case {Reversed_list, Index} of
        {[], _} ->
            0;

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

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

-file("src/packkit/xz.gleam", 761).
-spec delta_decode_loop(list(integer()), integer(), list(integer()), integer()) -> list(integer()).
delta_decode_loop(Remaining, Distance, Acc_reversed, Count) ->
    case Remaining of
        [] ->
            lists:reverse(Acc_reversed);

        [B | Rest] ->
            Prev = case Count >= Distance of
                true ->
                    nth_from_end(Acc_reversed, Distance - 1);

                false ->
                    0
            end,
            Decoded_byte = erlang:'band'(B + Prev, 16#FF),
            delta_decode_loop(
                Rest,
                Distance,
                [Decoded_byte | Acc_reversed],
                Count + 1
            )
    end.

-file("src/packkit/xz.gleam", 796).
-spec bit_array_to_byte_list(bitstring(), list(integer())) -> list(integer()).
bit_array_to_byte_list(Bytes, Acc) ->
    case Bytes of
        <<B, Rest/binary>> ->
            bit_array_to_byte_list(Rest, [B | Acc]);

        _ ->
            lists:reverse(Acc)
    end.

-file("src/packkit/xz.gleam", 803).
-spec byte_list_to_bit_array(list(integer()), bitstring()) -> bitstring().
byte_list_to_bit_array(Bytes, Acc) ->
    case Bytes of
        [] ->
            Acc;

        [B | Rest] ->
            byte_list_to_bit_array(Rest, <<Acc/bitstring, B>>)
    end.

-file("src/packkit/xz.gleam", 755).
?DOC(
    " Delta decode: output[i] = input[i] + output[i - distance] mod 256.\n"
    " For the first `distance` bytes there's no predecessor so they\n"
    " pass through unchanged.\n"
).
-spec delta_decode(bitstring(), integer()) -> bitstring().
delta_decode(Bytes, Distance) ->
    Bytes_list = bit_array_to_byte_list(Bytes, []),
    Decoded = delta_decode_loop(Bytes_list, Distance, [], 0),
    byte_list_to_bit_array(Decoded, <<>>).

-file("src/packkit/xz.gleam", 698).
-spec apply_pre_filters_loop(bitstring(), list({integer(), integer()})) -> {ok,
        bitstring()} |
    {error, packkit@error:codec_error()}.
apply_pre_filters_loop(Bytes, Reversed_filters) ->
    case Reversed_filters of
        [] ->
            {ok, Bytes};

        [{16#03, Props} | Rest] ->
            Distance = Props + 1,
            apply_pre_filters_loop(delta_decode(Bytes, Distance), Rest);

        [{16#04, _} | Rest@1] ->
            apply_pre_filters_loop(
                packkit@internal@bcj:x86_decode(Bytes, 0),
                Rest@1
            );

        [{16#05, _} | Rest@2] ->
            apply_pre_filters_loop(
                packkit@internal@bcj:powerpc_decode(Bytes, 0),
                Rest@2
            );

        [{16#07, _} | Rest@3] ->
            apply_pre_filters_loop(
                packkit@internal@bcj:arm_decode(Bytes, 0),
                Rest@3
            );

        [{16#08, _} | Rest@4] ->
            apply_pre_filters_loop(
                packkit@internal@bcj:armthumb_decode(Bytes, 0),
                Rest@4
            );

        [{16#09, _} | Rest@5] ->
            apply_pre_filters_loop(
                packkit@internal@bcj:sparc_decode(Bytes, 0),
                Rest@5
            );

        [{16#0A, _} | Rest@6] ->
            apply_pre_filters_loop(
                packkit@internal@bcj:arm64_decode(Bytes, 0),
                Rest@6
            );

        [{16#06, _} | Rest@7] ->
            apply_pre_filters_loop(
                packkit@internal@bcj:ia64_decode(Bytes, 0),
                Rest@7
            );

        [{16#0B, _} | Rest@8] ->
            apply_pre_filters_loop(
                packkit@internal@bcj:riscv_decode(Bytes, 0),
                Rest@8
            );

        [{Id, _} | _] ->
            {error,
                {codec_not_implemented,
                    <<"xz pre-processor filter id "/utf8,
                        (erlang:integer_to_binary(Id))/binary>>}}
    end.

-file("src/packkit/xz.gleam", 691).
?DOC(
    " Apply every pre-filter in REVERSE order so the bytes seen by\n"
    " the user match the encoder's input.  Delta is the only\n"
    " pre-processor we currently invert.\n"
).
-spec apply_pre_filters_reverse(bitstring(), list({integer(), integer()})) -> {ok,
        bitstring()} |
    {error, packkit@error:codec_error()}.
apply_pre_filters_reverse(Bytes, Filters) ->
    apply_pre_filters_loop(Bytes, lists:reverse(Filters)).

-file("src/packkit/xz.gleam", 962).
-spec check_size_for(integer()) -> integer().
check_size_for(Check_type) ->
    case Check_type of
        0 ->
            0;

        1 ->
            4;

        4 ->
            8;

        10 ->
            32;

        _ ->
            0
    end.

-file("src/packkit/xz.gleam", 972).
-spec verify_block_check(bitstring(), integer(), bitstring()) -> {ok, nil} |
    {error, packkit@error:codec_error()}.
verify_block_check(Plain, Check_type, Check_bytes) ->
    case Check_type of
        0 ->
            {ok, nil};

        1 ->
            Expected = packkit@checksum:crc32(Plain),
            case Expected =:= bit_array_to_u32_le(Check_bytes) of
                true ->
                    {ok, nil};

                false ->
                    {error,
                        {codec_invalid_data, <<"xz block CRC32 mismatch"/utf8>>}}
            end;

        4 ->
            case erlang:byte_size(Check_bytes) of
                8 ->
                    Expected@1 = packkit@checksum:crc64_xz(Plain),
                    case Expected@1 =:= bit_array_to_u64_le_pair(Check_bytes) of
                        true ->
                            {ok, nil};

                        false ->
                            {error,
                                {codec_invalid_data,
                                    <<"xz block CRC64 mismatch"/utf8>>}}
                    end;

                _ ->
                    {error,
                        {codec_invalid_data, <<"xz block CRC64 length"/utf8>>}}
            end;

        10 ->
            case erlang:byte_size(Check_bytes) of
                32 ->
                    Expected@2 = packkit@checksum:sha256(Plain),
                    case Expected@2 =:= Check_bytes of
                        true ->
                            {ok, nil};

                        false ->
                            {error,
                                {codec_invalid_data,
                                    <<"xz block SHA-256 mismatch"/utf8>>}}
                    end;

                _ ->
                    {error,
                        {codec_invalid_data, <<"xz block SHA-256 length"/utf8>>}}
            end;

        _ ->
            {error,
                {codec_not_implemented,
                    <<"xz check type "/utf8,
                        (erlang:integer_to_binary(Check_type))/binary>>}}
    end.

-file("src/packkit/xz.gleam", 1083).
-spec prepend_indicator_for_crc(bitstring(), integer()) -> bitstring().
prepend_indicator_for_crc(Index_after_indicator, Size_without_indicator) ->
    case gleam_stdlib:bit_array_slice(
        Index_after_indicator,
        0,
        Size_without_indicator
    ) of
        {ok, Slice} ->
            gleam_stdlib:bit_array_concat([<<16#00>>, Slice]);

        {error, _} ->
            <<>>
    end.

-file("src/packkit/xz.gleam", 1110).
-spec verify_stream_footer(bitstring(), integer()) -> {ok, nil} |
    {error, packkit@error:codec_error()}.
verify_stream_footer(Footer, Check_type) ->
    case Footer of
        <<Crc:4/binary,
            Backward_size:32/little-unsigned,
            Flag_zero,
            Footer_check,
            16#59,
            16#5A>> ->
            gleam@bool:guard(
                Flag_zero /= 0,
                {error,
                    {codec_invalid_data,
                        <<"xz stream footer reserved byte non-zero"/utf8>>}},
                fun() ->
                    gleam@bool:guard(
                        Footer_check /= Check_type,
                        {error,
                            {codec_invalid_data,
                                <<"xz stream footer check type does not match header"/utf8>>}},
                        fun() ->
                            Expected = packkit@checksum:crc32(
                                <<Backward_size:32/little,
                                    Flag_zero,
                                    Footer_check>>
                            ),
                            gleam@bool:guard(
                                Expected /= bit_array_to_u32_le(Crc),
                                {error,
                                    {codec_invalid_data,
                                        <<"xz stream footer CRC mismatch"/utf8>>}},
                                fun() -> {ok, nil} end
                            )
                        end
                    )
                end
            );

        _ ->
            {error, {codec_invalid_data, <<"invalid xz stream footer"/utf8>>}}
    end.

-file("src/packkit/xz.gleam", 1167).
-spec read_varint_loop(bitstring(), integer(), integer()) -> {ok,
        {integer(), bitstring()}} |
    {error, packkit@error:codec_error()}.
read_varint_loop(Bytes, Value, Shift) ->
    case Bytes of
        <<B, Rest/binary>> ->
            Chunk = erlang:'band'(B, 16#7F),
            Value@1 = erlang:'bor'(Value, erlang:'bsl'(Chunk, Shift)),
            case erlang:'band'(B, 16#80) of
                0 ->
                    {ok, {Value@1, Rest}};

                _ ->
                    case Shift >= 56 of
                        true ->
                            {error,
                                {codec_invalid_data,
                                    <<"xz varint overflow"/utf8>>}};

                        false ->
                            read_varint_loop(Rest, Value@1, Shift + 7)
                    end
            end;

        _ ->
            {error, {codec_invalid_data, <<"truncated xz varint"/utf8>>}}
    end.

-file("src/packkit/xz.gleam", 1163).
-spec read_varint(bitstring()) -> {ok, {integer(), bitstring()}} |
    {error, packkit@error:codec_error()}.
read_varint(Bytes) ->
    read_varint_loop(Bytes, 0, 0).

-file("src/packkit/xz.gleam", 1095).
-spec read_index_records(bitstring(), integer(), list({integer(), integer()})) -> {ok,
        {list({integer(), integer()}), bitstring()}} |
    {error, packkit@error:codec_error()}.
read_index_records(Bytes, Remaining, Acc) ->
    case Remaining of
        0 ->
            {ok, {lists:reverse(Acc), Bytes}};

        _ ->
            gleam@result:'try'(
                read_varint(Bytes),
                fun(_use0) ->
                    {Unpadded, Bytes@1} = _use0,
                    gleam@result:'try'(
                        read_varint(Bytes@1),
                        fun(_use0@1) ->
                            {Uncomp, Bytes@2} = _use0@1,
                            read_index_records(
                                Bytes@2,
                                Remaining - 1,
                                [{Unpadded, Uncomp} | Acc]
                            )
                        end
                    )
                end
            )
    end.

-file("src/packkit/xz.gleam", 1190).
-spec varint_size(integer()) -> integer().
varint_size(Value) ->
    case Value of
        N when N < 16#80 ->
            1;

        N@1 when N@1 < 16#4000 ->
            2;

        N@2 when N@2 < 16#200000 ->
            3;

        N@3 when N@3 < 16#10000000 ->
            4;

        N@4 when N@4 < 16#800000000 ->
            5;

        N@5 when N@5 < 16#40000000000 ->
            6;

        N@6 when N@6 < 16#2000000000000 ->
            7;

        _ ->
            8
    end.

-file("src/packkit/xz.gleam", 1153).
-spec records_size(list({integer(), integer()})) -> integer().
records_size(Records) ->
    case Records of
        [] ->
            0;

        [{Unp, Unc} | Rest] ->
            (varint_size(Unp) + varint_size(Unc)) + records_size(Rest)
    end.

-file("src/packkit/xz.gleam", 1208).
-spec slice_required(bitstring(), integer(), integer(), binary()) -> {ok,
        bitstring()} |
    {error, packkit@error:codec_error()}.
slice_required(Bytes, Offset, Length, Label) ->
    case {Offset, Length} of
        {-1, _} ->
            {ok, Bytes};

        {_, -1} ->
            {ok, Bytes};

        {_, _} ->
            case gleam_stdlib:bit_array_slice(Bytes, Offset, Length) of
                {ok, Value} ->
                    {ok, Value};

                {error, _} ->
                    {error,
                        {codec_invalid_data,
                            <<"truncated "/utf8, Label/binary>>}}
            end
    end.

-file("src/packkit/xz.gleam", 600).
-spec parse_filters(bitstring(), integer(), list({integer(), integer()})) -> {ok,
        {list({integer(), integer()}), bitstring()}} |
    {error, packkit@error:codec_error()}.
parse_filters(Bytes, Remaining, Acc) ->
    case Remaining of
        0 ->
            {ok, {lists:reverse(Acc), Bytes}};

        _ ->
            gleam@result:'try'(
                read_varint(Bytes),
                fun(_use0) ->
                    {Filter_id, Bytes@1} = _use0,
                    gleam@result:'try'(
                        read_varint(Bytes@1),
                        fun(_use0@1) ->
                            {Props_size, Bytes@2} = _use0@1,
                            gleam@result:'try'(
                                slice_required(
                                    Bytes@2,
                                    0,
                                    Props_size,
                                    <<"xz filter properties"/utf8>>
                                ),
                                fun(_) ->
                                    Rest@1 = case gleam_stdlib:bit_array_slice(
                                        Bytes@2,
                                        Props_size,
                                        erlang:byte_size(Bytes@2) - Props_size
                                    ) of
                                        {ok, Rest} -> Rest;
                                        _assert_fail ->
                                            erlang:error(
                                                    #{gleam_error => let_assert,
                                                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                        file => <<?FILEPATH/utf8>>,
                                                        module => <<"packkit/xz"/utf8>>,
                                                        function => <<"parse_filters"/utf8>>,
                                                        line => 616,
                                                        value => _assert_fail,
                                                        start => 20123,
                                                        'end' => 20269,
                                                        pattern_start => 20134,
                                                        pattern_end => 20142}
                                                )
                                    end,
                                    Props_int = case Props_size of
                                        1 ->
                                            V@1 = case Bytes@2 of
                                                <<V, _/binary>> -> V;
                                                _assert_fail@1 ->
                                                    erlang:error(
                                                            #{gleam_error => let_assert,
                                                                message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                                file => <<?FILEPATH/utf8>>,
                                                                module => <<"packkit/xz"/utf8>>,
                                                                function => <<"parse_filters"/utf8>>,
                                                                line => 624,
                                                                value => _assert_fail@1,
                                                                start => 20335,
                                                                'end' => 20368,
                                                                pattern_start => 20346,
                                                                pattern_end => 20360}
                                                        )
                                            end,
                                            V@1;

                                        _ ->
                                            0
                                    end,
                                    parse_filters(
                                        Rest@1,
                                        Remaining - 1,
                                        [{Filter_id, Props_int} | Acc]
                                    )
                                end
                            )
                        end
                    )
                end
            )
    end.

-file("src/packkit/xz.gleam", 532).
-spec parse_block_header(bitstring(), integer()) -> {ok,
        {integer(),
            size_field(),
            uncompressed_field(),
            list({integer(), integer()}),
            bitstring()}} |
    {error, packkit@error:codec_error()}.
parse_block_header(Header, Size) ->
    gleam@bool:guard(
        Size < 8,
        {error, {codec_invalid_data, <<"xz block header too small"/utf8>>}},
        fun() ->
            Crc_offset = Size - 4,
            gleam@result:'try'(
                slice_required(
                    Header,
                    0,
                    Crc_offset,
                    <<"xz block header pre-CRC"/utf8>>
                ),
                fun(Header_no_crc) ->
                    gleam@result:'try'(
                        slice_required(
                            Header,
                            Crc_offset,
                            4,
                            <<"xz block header CRC"/utf8>>
                        ),
                        fun(Crc_bytes) ->
                            Expected = packkit@checksum:crc32(Header_no_crc),
                            gleam@bool:guard(
                                Expected /= bit_array_to_u32_le(Crc_bytes),
                                {error,
                                    {codec_invalid_data,
                                        <<"xz block header CRC mismatch"/utf8>>}},
                                fun() ->
                                    After_size@1 = case gleam_stdlib:bit_array_slice(
                                        Header_no_crc,
                                        1,
                                        Crc_offset - 1
                                    ) of
                                        {ok, After_size} -> After_size;
                                        _assert_fail ->
                                            erlang:error(
                                                    #{gleam_error => let_assert,
                                                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                        file => <<?FILEPATH/utf8>>,
                                                        module => <<"packkit/xz"/utf8>>,
                                                        function => <<"parse_block_header"/utf8>>,
                                                        line => 566,
                                                        value => _assert_fail,
                                                        start => 18329,
                                                        'end' => 18406,
                                                        pattern_start => 18340,
                                                        pattern_end => 18354}
                                                )
                                    end,
                                    case After_size@1 of
                                        <<Flags, Rest/binary>> ->
                                            Filter_count = erlang:'band'(
                                                Flags,
                                                16#03
                                            )
                                            + 1,
                                            Has_comp_size = erlang:'band'(
                                                Flags,
                                                16#40
                                            )
                                            /= 0,
                                            Has_uncomp_size = erlang:'band'(
                                                Flags,
                                                16#80
                                            )
                                            /= 0,
                                            Reserved_bits = erlang:'band'(
                                                Flags,
                                                16#3C
                                            ),
                                            gleam@bool:guard(
                                                Reserved_bits /= 0,
                                                {error,
                                                    {codec_invalid_data,
                                                        <<"xz block header has reserved bits set"/utf8>>}},
                                                fun() ->
                                                    gleam@result:'try'(
                                                        case Has_comp_size of
                                                            true ->
                                                                gleam@result:'try'(
                                                                    read_varint(
                                                                        Rest
                                                                    ),
                                                                    fun(_use0) ->
                                                                        {Value,
                                                                            Rest@1} = _use0,
                                                                        {ok,
                                                                            {{compressed_known,
                                                                                    Value},
                                                                                Rest@1}}
                                                                    end
                                                                );

                                                            false ->
                                                                {ok,
                                                                    {compressed_unknown,
                                                                        Rest}}
                                                        end,
                                                        fun(_use0@1) ->
                                                            {Comp_size, Rest@2} = _use0@1,
                                                            gleam@result:'try'(
                                                                case Has_uncomp_size of
                                                                    true ->
                                                                        gleam@result:'try'(
                                                                            read_varint(
                                                                                Rest@2
                                                                            ),
                                                                            fun(
                                                                                _use0@2
                                                                            ) ->
                                                                                {Value@1,
                                                                                    Rest@3} = _use0@2,
                                                                                {ok,
                                                                                    {{uncompressed_known,
                                                                                            Value@1},
                                                                                        Rest@3}}
                                                                            end
                                                                        );

                                                                    false ->
                                                                        {ok,
                                                                            {uncompressed_unknown,
                                                                                Rest@2}}
                                                                end,
                                                                fun(_use0@3) ->
                                                                    {Uncomp_size,
                                                                        Rest@4} = _use0@3,
                                                                    gleam@result:'try'(
                                                                        parse_filters(
                                                                            Rest@4,
                                                                            Filter_count,
                                                                            []
                                                                        ),
                                                                        fun(
                                                                            _use0@4
                                                                        ) ->
                                                                            {Filters,
                                                                                Rest@5} = _use0@4,
                                                                            {ok,
                                                                                {Flags,
                                                                                    Comp_size,
                                                                                    Uncomp_size,
                                                                                    Filters,
                                                                                    Rest@5}}
                                                                        end
                                                                    )
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            );

                                        _ ->
                                            {error,
                                                {codec_invalid_data,
                                                    <<"xz block header missing flags"/utf8>>}}
                                    end
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/packkit/xz.gleam", 1233).
-spec all_zero(bitstring()) -> boolean().
all_zero(Bytes) ->
    case Bytes of
        <<>> ->
            true;

        <<0, Rest/binary>> ->
            all_zero(Rest);

        _ ->
            false
    end.

-file("src/packkit/xz.gleam", 1226).
-spec slice_is_zero(bitstring(), integer(), integer()) -> boolean().
slice_is_zero(Bytes, Offset, Length) ->
    case gleam_stdlib:bit_array_slice(Bytes, Offset, Length) of
        {ok, Chunk} ->
            all_zero(Chunk);

        {error, _} ->
            true
    end.

-file("src/packkit/xz.gleam", 1241).
-spec padding_to_align(integer(), integer()) -> integer().
padding_to_align(Used, Alignment) ->
    Leftover = case Alignment of
        0 -> 0;
        Gleam@denominator -> Used rem Gleam@denominator
    end,
    case Leftover of
        0 ->
            0;

        _ ->
            Alignment - Leftover
    end.

-file("src/packkit/xz.gleam", 165).
-spec encode_index(integer(), integer()) -> bitstring().
encode_index(Unpadded_size, Uncompressed_size) ->
    Body = gleam_stdlib:bit_array_concat(
        [<<16#00, 16#01>>,
            encode_varint(Unpadded_size),
            encode_varint(Uncompressed_size)]
    ),
    Body_with_padding = gleam_stdlib:bit_array_concat(
        [Body, pad_zero_bytes(padding_to_align(erlang:byte_size(Body), 4))]
    ),
    Crc = packkit@checksum:crc32(Body_with_padding),
    gleam_stdlib:bit_array_concat([Body_with_padding, <<Crc:32/little>>]).

-file("src/packkit/xz.gleam", 1023).
-spec finalize_stream(
    bitstring(),
    integer(),
    bitstring(),
    list({integer(), integer()})
) -> {ok, {bitstring(), bitstring()}} | {error, packkit@error:codec_error()}.
finalize_stream(Bytes, Check_type, Output, Records) ->
    gleam@result:'try'(
        read_varint(Bytes),
        fun(_use0) ->
            {Num_records, Rest} = _use0,
            gleam@bool:guard(
                Num_records /= erlang:length(Records),
                {error,
                    {codec_invalid_data,
                        <<"xz index record count does not match blocks"/utf8>>}},
                fun() ->
                    gleam@result:'try'(
                        read_index_records(Rest, Num_records, []),
                        fun(_use0@1) ->
                            {Parsed_records, Rest@1} = _use0@1,
                            gleam@bool:guard(
                                Parsed_records /= Records,
                                {error,
                                    {codec_invalid_data,
                                        <<"xz index records do not match block sizes"/utf8>>}},
                                fun() ->
                                    Bytes_after_indicator = varint_size(
                                        Num_records
                                    )
                                    + records_size(Records),
                                    Pad = padding_to_align(
                                        1 + Bytes_after_indicator,
                                        4
                                    ),
                                    gleam@bool:guard(
                                        (Pad > 0) andalso not slice_is_zero(
                                            Rest@1,
                                            0,
                                            Pad
                                        ),
                                        {error,
                                            {codec_invalid_data,
                                                <<"xz index padding has non-zero bytes"/utf8>>}},
                                        fun() ->
                                            gleam@result:'try'(
                                                slice_required(
                                                    Rest@1,
                                                    Pad,
                                                    4,
                                                    <<"xz index CRC32"/utf8>>
                                                ),
                                                fun(Crc_bytes) ->
                                                    Crc_input = prepend_indicator_for_crc(
                                                        Bytes,
                                                        Bytes_after_indicator + Pad
                                                    ),
                                                    Expected_crc = packkit@checksum:crc32(
                                                        Crc_input
                                                    ),
                                                    gleam@bool:guard(
                                                        Expected_crc /= bit_array_to_u32_le(
                                                            Crc_bytes
                                                        ),
                                                        {error,
                                                            {codec_invalid_data,
                                                                <<"xz index CRC mismatch"/utf8>>}},
                                                        fun() ->
                                                            After_index@1 = case gleam_stdlib:bit_array_slice(
                                                                Rest@1,
                                                                Pad + 4,
                                                                (erlang:byte_size(
                                                                    Rest@1
                                                                )
                                                                - Pad)
                                                                - 4
                                                            ) of
                                                                {ok,
                                                                    After_index} -> After_index;
                                                                _assert_fail ->
                                                                    erlang:error(
                                                                            #{gleam_error => let_assert,
                                                                                message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                                                file => <<?FILEPATH/utf8>>,
                                                                                module => <<"packkit/xz"/utf8>>,
                                                                                function => <<"finalize_stream"/utf8>>,
                                                                                line => 1060,
                                                                                value => _assert_fail,
                                                                                start => 34748,
                                                                                'end' => 34848,
                                                                                pattern_start => 34759,
                                                                                pattern_end => 34774}
                                                                        )
                                                            end,
                                                            case gleam_stdlib:bit_array_slice(
                                                                After_index@1,
                                                                0,
                                                                12
                                                            ) of
                                                                {ok, Footer} ->
                                                                    gleam@result:'try'(
                                                                        verify_stream_footer(
                                                                            Footer,
                                                                            Check_type
                                                                        ),
                                                                        fun(_) ->
                                                                            After_footer@1 = case gleam_stdlib:bit_array_slice(
                                                                                After_index@1,
                                                                                12,
                                                                                erlang:byte_size(
                                                                                    After_index@1
                                                                                )
                                                                                - 12
                                                                            ) of
                                                                                {ok,
                                                                                    After_footer} -> After_footer;
                                                                                _assert_fail@1 ->
                                                                                    erlang:error(
                                                                                            #{gleam_error => let_assert,
                                                                                                message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                                                                file => <<?FILEPATH/utf8>>,
                                                                                                module => <<"packkit/xz"/utf8>>,
                                                                                                function => <<"finalize_stream"/utf8>>,
                                                                                                line => 1068,
                                                                                                value => _assert_fail@1,
                                                                                                start => 35196,
                                                                                                'end' => 35378,
                                                                                                pattern_start => 35207,
                                                                                                pattern_end => 35223}
                                                                                        )
                                                                            end,
                                                                            {ok,
                                                                                {Output,
                                                                                    After_footer@1}}
                                                                        end
                                                                    );

                                                                _ ->
                                                                    {error,
                                                                        {codec_invalid_data,
                                                                            <<"xz stream footer truncated (need 12 bytes)"/utf8>>}}
                                                            end
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/packkit/xz.gleam", 111).
-spec emit_lzma_chunks(
    bitstring(),
    integer(),
    packkit@internal@lzma:properties(),
    bitstring()
) -> bitstring().
emit_lzma_chunks(Remaining, Remaining_size, Props, Acc) ->
    case Remaining_size of
        0 ->
            gleam_stdlib:bit_array_concat([Acc, <<16#00>>]);

        N ->
            Chunk_size = case N > 16#8000 of
                true ->
                    16#8000;

                false ->
                    N
            end,
            Payload@1 = case gleam_stdlib:bit_array_slice(
                Remaining,
                0,
                Chunk_size
            ) of
                {ok, Payload} -> Payload;
                _assert_fail ->
                    erlang:error(#{gleam_error => let_assert,
                                message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                file => <<?FILEPATH/utf8>>,
                                module => <<"packkit/xz"/utf8>>,
                                function => <<"emit_lzma_chunks"/utf8>>,
                                line => 124,
                                value => _assert_fail,
                                start => 4533,
                                'end' => 4599,
                                pattern_start => 4544,
                                pattern_end => 4555})
            end,
            Rest@1 = case gleam_stdlib:bit_array_slice(
                Remaining,
                Chunk_size,
                erlang:byte_size(Remaining) - Chunk_size
            ) of
                {ok, Rest} -> Rest;
                _assert_fail@1 ->
                    erlang:error(#{gleam_error => let_assert,
                                message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                file => <<?FILEPATH/utf8>>,
                                module => <<"packkit/xz"/utf8>>,
                                function => <<"emit_lzma_chunks"/utf8>>,
                                line => 125,
                                value => _assert_fail@1,
                                start => 4606,
                                'end' => 4760,
                                pattern_start => 4617,
                                pattern_end => 4625})
            end,
            Compressed = packkit@internal@lzma:encode_with_lz77(
                Payload@1,
                Props
            ),
            Csize = erlang:byte_size(Compressed),
            Prop_byte = packkit@internal@lzma:properties_to_byte(Props),
            Usize_minus_1 = Chunk_size - 1,
            Csize_minus_1 = Csize - 1,
            Control = erlang:'bor'(
                16#E0,
                erlang:'band'(erlang:'bsr'(Usize_minus_1, 16), 16#1F)
            ),
            Header = <<Control,
                (erlang:'band'(erlang:'bsr'(Usize_minus_1, 8), 16#FF)),
                (erlang:'band'(Usize_minus_1, 16#FF)),
                (erlang:'band'(erlang:'bsr'(Csize_minus_1, 8), 16#FF)),
                (erlang:'band'(Csize_minus_1, 16#FF)),
                Prop_byte>>,
            emit_lzma_chunks(
                Rest@1,
                Remaining_size - Chunk_size,
                Props,
                gleam_stdlib:bit_array_concat([Acc, Header, Compressed])
            )
    end.

-file("src/packkit/xz.gleam", 101).
-spec encode_lzma2_compressed(bitstring(), integer()) -> bitstring().
encode_lzma2_compressed(Bytes, Size) ->
    case Size of
        0 ->
            <<16#00>>;

        _ ->
            Props = {properties, 3, 0, 2},
            emit_lzma_chunks(Bytes, Size, Props, <<>>)
    end.

-file("src/packkit/xz.gleam", 36).
?DOC(
    " Encode `bytes` as an xz stream.  The encoder produces a single\n"
    " block whose LZMA2 filter chain contains real LZMA1-compressed\n"
    " chunks โ€” each chunk routes through the literal-only LZMA1 encoder\n"
    " in `packkit/internal/lzma`, so the output is a fully conforming\n"
    " `.xz` file that any decoder accepts.  Compression ratio is bounded\n"
    " by the literal-only LZMA1 encoder (no LZ77 match search yet);\n"
    " plugging in a hash-chain match finder is the next obvious\n"
    " incremental improvement.\n"
).
-spec encode(bitstring()) -> {ok, bitstring()} |
    {error, packkit@error:codec_error()}.
encode(Bytes) ->
    Size = erlang:byte_size(Bytes),
    Stream_header = encode_stream_header(),
    Lzma2_payload = encode_lzma2_compressed(Bytes, Size),
    Block_header = encode_block_header(erlang:byte_size(Lzma2_payload), Size),
    Block_check = <<(packkit@checksum:crc32(Bytes)):32/little>>,
    Block_body = gleam_stdlib:bit_array_concat([Block_header, Lzma2_payload]),
    Block_padding = pad_zero_bytes(
        padding_to_align(erlang:byte_size(Block_body), 4)
    ),
    Block_full = gleam_stdlib:bit_array_concat(
        [Block_body, Block_padding, Block_check]
    ),
    Unpadded = (erlang:byte_size(Block_header) + erlang:byte_size(Lzma2_payload))
    + erlang:byte_size(Block_check),
    Index = encode_index(Unpadded, Size),
    Footer = encode_stream_footer(erlang:byte_size(Index)),
    {ok,
        gleam_stdlib:bit_array_concat(
            [Stream_header, Block_full, Index, Footer]
        )}.

-file("src/packkit/xz.gleam", 879).
-spec decode_lzma2_lzma_chunk(
    bitstring(),
    integer(),
    bitstring(),
    integer(),
    packkit@internal@lzma:properties(),
    packkit@limit:limits()
) -> {ok, {bitstring(), integer()}} | {error, packkit@error:codec_error()}.
decode_lzma2_lzma_chunk(Payload, Control, Output, Consumed, Props, Limits) ->
    Has_new_props = Control >= 16#C0,
    case Payload of
        <<_, Usize_high, Usize_low, Csize_high, Csize_low, Rest/binary>> ->
            Usize = erlang:'bor'(
                erlang:'bsl'(erlang:'band'(Control, 16#1F), 16),
                erlang:'bor'(erlang:'bsl'(Usize_high, 8), Usize_low)
            )
            + 1,
            Csize = erlang:'bor'(erlang:'bsl'(Csize_high, 8), Csize_low) + 1,
            gleam@result:'try'(case Has_new_props of
                    true ->
                        case Rest of
                            <<Props_byte, Rest_after_props/binary>> ->
                                gleam@result:'try'(
                                    packkit@internal@lzma:properties_of_byte(
                                        Props_byte
                                    ),
                                    fun(Parsed_props) ->
                                        gleam@result:'try'(
                                            slice_required(
                                                Rest_after_props,
                                                0,
                                                Csize,
                                                <<"lzma2 LZMA data"/utf8>>
                                            ),
                                            fun(Lzma_data) ->
                                                After@1 = case gleam_stdlib:bit_array_slice(
                                                    Rest_after_props,
                                                    Csize,
                                                    erlang:byte_size(
                                                        Rest_after_props
                                                    )
                                                    - Csize
                                                ) of
                                                    {ok, After} -> After;
                                                    _assert_fail ->
                                                        erlang:error(
                                                                #{gleam_error => let_assert,
                                                                    message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                                    file => <<?FILEPATH/utf8>>,
                                                                    module => <<"packkit/xz"/utf8>>,
                                                                    function => <<"decode_lzma2_lzma_chunk"/utf8>>,
                                                                    line => 912,
                                                                    value => _assert_fail,
                                                                    start => 30088,
                                                                    'end' => 30297,
                                                                    pattern_start => 30099,
                                                                    pattern_end => 30108}
                                                            )
                                                end,
                                                {ok,
                                                    {Parsed_props,
                                                        Lzma_data,
                                                        After@1,
                                                        1}}
                                            end
                                        )
                                    end
                                );

                            _ ->
                                {error,
                                    {codec_invalid_data,
                                        <<"truncated lzma2 properties byte"/utf8>>}}
                        end;

                    false ->
                        gleam@result:'try'(
                            slice_required(
                                Rest,
                                0,
                                Csize,
                                <<"lzma2 LZMA data"/utf8>>
                            ),
                            fun(Lzma_data@1) ->
                                After@3 = case gleam_stdlib:bit_array_slice(
                                    Rest,
                                    Csize,
                                    erlang:byte_size(Rest) - Csize
                                ) of
                                    {ok, After@2} -> After@2;
                                    _assert_fail@1 ->
                                        erlang:error(
                                                #{gleam_error => let_assert,
                                                    message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                    file => <<?FILEPATH/utf8>>,
                                                    module => <<"packkit/xz"/utf8>>,
                                                    function => <<"decode_lzma2_lzma_chunk"/utf8>>,
                                                    line => 932,
                                                    value => _assert_fail@1,
                                                    start => 30726,
                                                    'end' => 30826,
                                                    pattern_start => 30737,
                                                    pattern_end => 30746}
                                            )
                                end,
                                {ok, {Props, Lzma_data@1, After@3, 0}}
                            end
                        )
                end, fun(_use0) ->
                    {New_props, Lzma_input, After_chunk, Props_byte_count} = _use0,
                    gleam@result:'try'(
                        packkit@internal@lzma:new(
                            Lzma_input,
                            New_props,
                            packkit@limit:max_output_bytes(Limits)
                        ),
                        fun(Decoder) ->
                            gleam@result:'try'(
                                packkit@internal@lzma:decode_into(
                                    Decoder,
                                    Usize
                                ),
                                fun(_use0@1) ->
                                    {Decoded, _} = _use0@1,
                                    gleam@result:'try'(
                                        append_with_limit(
                                            Output,
                                            Decoded,
                                            Limits
                                        ),
                                        fun(New_output) ->
                                            Chunk_bytes = (5 + Props_byte_count)
                                            + Csize,
                                            decode_lzma2_loop(
                                                After_chunk,
                                                New_output,
                                                Consumed + Chunk_bytes,
                                                New_props,
                                                Limits
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end);

        _ ->
            {error,
                {codec_invalid_data,
                    <<"truncated lzma2 LZMA chunk header"/utf8>>}}
    end.

-file("src/packkit/xz.gleam", 831).
-spec decode_lzma2_loop(
    bitstring(),
    bitstring(),
    integer(),
    packkit@internal@lzma:properties(),
    packkit@limit:limits()
) -> {ok, {bitstring(), integer()}} | {error, packkit@error:codec_error()}.
decode_lzma2_loop(Payload, Output, Consumed, Props, Limits) ->
    case Payload of
        <<16#00, _/binary>> ->
            {ok, {Output, Consumed + 1}};

        <<Control, _/binary>> when (Control =:= 16#01) orelse (Control =:= 16#02) ->
            case Payload of
                <<_, Size_high, Size_low, Rest/binary>> ->
                    Size = erlang:'bor'(erlang:'bsl'(Size_high, 8), Size_low) + 1,
                    gleam@result:'try'(
                        slice_required(
                            Rest,
                            0,
                            Size,
                            <<"lzma2 uncompressed chunk"/utf8>>
                        ),
                        fun(Data) ->
                            gleam@result:'try'(
                                append_with_limit(Output, Data, Limits),
                                fun(New_output) ->
                                    Next@1 = case gleam_stdlib:bit_array_slice(
                                        Rest,
                                        Size,
                                        erlang:byte_size(Rest) - Size
                                    ) of
                                        {ok, Next} -> Next;
                                        _assert_fail ->
                                            erlang:error(
                                                    #{gleam_error => let_assert,
                                                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                        file => <<?FILEPATH/utf8>>,
                                                        module => <<"packkit/xz"/utf8>>,
                                                        function => <<"decode_lzma2_loop"/utf8>>,
                                                        line => 852,
                                                        value => _assert_fail,
                                                        start => 28086,
                                                        'end' => 28181,
                                                        pattern_start => 28097,
                                                        pattern_end => 28105}
                                                )
                                    end,
                                    decode_lzma2_loop(
                                        Next@1,
                                        New_output,
                                        (Consumed + 3) + Size,
                                        Props,
                                        Limits
                                    )
                                end
                            )
                        end
                    );

                _ ->
                    {error,
                        {codec_invalid_data,
                            <<"truncated lzma2 uncompressed chunk"/utf8>>}}
            end;

        <<Control@1, _/binary>> when Control@1 >= 16#80 ->
            decode_lzma2_lzma_chunk(
                Payload,
                Control@1,
                Output,
                Consumed,
                Props,
                Limits
            );

        <<Other, _/binary>> ->
            {error,
                {codec_invalid_data,
                    <<"invalid lzma2 control byte "/utf8,
                        (erlang:integer_to_binary(Other))/binary>>}};

        _ ->
            {error, {codec_invalid_data, <<"truncated lzma2 stream"/utf8>>}}
    end.

-file("src/packkit/xz.gleam", 819).
?DOC(
    " Decode an LZMA2 stream and return the decoded payload plus the\n"
    " number of bytes consumed from `payload` (inclusive of the 0x00\n"
    " end-of-stream marker but excluding any block padding that follows).\n"
    " The caller uses the consumed count when the surrounding xz block\n"
    " header omits the optional `compressed_size` field โ€” there is no\n"
    " pre-known slice boundary in that case, only the self-terminating\n"
    " LZMA2 stream itself.\n"
).
-spec decode_lzma2(bitstring(), integer(), packkit@limit:limits()) -> {ok,
        {bitstring(), integer()}} |
    {error, packkit@error:codec_error()}.
decode_lzma2(Payload, Default_props, Limits) ->
    Initial = case packkit@internal@lzma:properties_of_byte(Default_props) of
        {ok, P} ->
            P;

        {error, _} ->
            {properties, 3, 0, 2}
    end,
    decode_lzma2_loop(Payload, <<>>, 0, Initial, Limits).

-file("src/packkit/xz.gleam", 399).
-spec decode_block(bitstring(), integer(), packkit@limit:limits()) -> {ok,
        {bitstring(), integer(), integer(), bitstring()}} |
    {error, packkit@error:codec_error()}.
decode_block(Bytes, Check_type, Limits) ->
    case Bytes of
        <<Size_byte, _/binary>> ->
            Header_size = (Size_byte + 1) * 4,
            case gleam_stdlib:bit_array_slice(Bytes, 0, Header_size) of
                {error, _} ->
                    {error,
                        {codec_invalid_data,
                            <<"truncated xz block header"/utf8>>}};

                {ok, Header_chunk} ->
                    gleam@result:'try'(
                        parse_block_header(Header_chunk, Header_size),
                        fun(_use0) ->
                            {Flags, Comp_size, Uncomp_size, Filters, _} = _use0,
                            gleam@result:'try'(
                                split_filter_chain(Filters),
                                fun(_use0@1) ->
                                    {Lzma2_props, Pre_filters} = _use0@1,
                                    _ = Flags,
                                    Payload_offset = Header_size,
                                    gleam@result:'try'(case Comp_size of
                                            {compressed_known, V} ->
                                                slice_required(
                                                    Bytes,
                                                    Payload_offset,
                                                    V,
                                                    <<"xz block data"/utf8>>
                                                );

                                            compressed_unknown ->
                                                Total = erlang:byte_size(Bytes),
                                                case Total >= Payload_offset of
                                                    true ->
                                                        B@1 = case gleam_stdlib:bit_array_slice(
                                                            Bytes,
                                                            Payload_offset,
                                                            Total - Payload_offset
                                                        ) of
                                                            {ok, B} -> B;
                                                            _assert_fail ->
                                                                erlang:error(
                                                                        #{gleam_error => let_assert,
                                                                            message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                                            file => <<?FILEPATH/utf8>>,
                                                                            module => <<"packkit/xz"/utf8>>,
                                                                            function => <<"decode_block"/utf8>>,
                                                                            line => 441,
                                                                            value => _assert_fail,
                                                                            start => 14493,
                                                                            'end' => 14683,
                                                                            pattern_start => 14504,
                                                                            pattern_end => 14509}
                                                                    )
                                                        end,
                                                        {ok, B@1};

                                                    false ->
                                                        {error,
                                                            {codec_invalid_data,
                                                                <<"truncated xz block data"/utf8>>}}
                                                end
                                        end, fun(Payload) ->
                                            gleam@result:'try'(
                                                decode_lzma2(
                                                    Payload,
                                                    Lzma2_props,
                                                    Limits
                                                ),
                                                fun(_use0@2) ->
                                                    {Lzma_out, Lzma2_consumed} = _use0@2,
                                                    Payload_size = case Comp_size of
                                                        {compressed_known, V@1} ->
                                                            V@1;

                                                        compressed_unknown ->
                                                            Lzma2_consumed
                                                    end,
                                                    gleam@bool:guard(
                                                        case Comp_size of
                                                            {compressed_known,
                                                                V@2} ->
                                                                Lzma2_consumed
                                                                /= V@2;

                                                            compressed_unknown ->
                                                                false
                                                        end,
                                                        {error,
                                                            {codec_invalid_data,
                                                                <<"xz block compressed_size mismatch"/utf8>>}},
                                                        fun() ->
                                                            gleam@result:'try'(
                                                                apply_pre_filters_reverse(
                                                                    Lzma_out,
                                                                    Pre_filters
                                                                ),
                                                                fun(Plain) ->
                                                                    gleam@bool:guard(
                                                                        case Uncomp_size of
                                                                            {uncompressed_known,
                                                                                V@3} ->
                                                                                erlang:byte_size(
                                                                                    Plain
                                                                                )
                                                                                /= V@3;

                                                                            uncompressed_unknown ->
                                                                                false
                                                                        end,
                                                                        {error,
                                                                            {codec_invalid_data,
                                                                                <<"xz block uncompressed size mismatch"/utf8>>}},
                                                                        fun() ->
                                                                            Used = Payload_offset
                                                                            + Payload_size,
                                                                            Pad = padding_to_align(
                                                                                Used,
                                                                                4
                                                                            ),
                                                                            After_padding = (Payload_offset
                                                                            + Payload_size)
                                                                            + Pad,
                                                                            gleam@bool:guard(
                                                                                (Pad
                                                                                > 0)
                                                                                andalso not slice_is_zero(
                                                                                    Bytes,
                                                                                    Payload_offset
                                                                                    + Payload_size,
                                                                                    Pad
                                                                                ),
                                                                                {error,
                                                                                    {codec_invalid_data,
                                                                                        <<"xz block padding has non-zero bytes"/utf8>>}},
                                                                                fun(
                                                                                    
                                                                                ) ->
                                                                                    Check_size = check_size_for(
                                                                                        Check_type
                                                                                    ),
                                                                                    gleam@result:'try'(
                                                                                        slice_required(
                                                                                            Bytes,
                                                                                            After_padding,
                                                                                            Check_size,
                                                                                            <<"xz block check"/utf8>>
                                                                                        ),
                                                                                        fun(
                                                                                            Check_bytes
                                                                                        ) ->
                                                                                            gleam@result:'try'(
                                                                                                verify_block_check(
                                                                                                    Plain,
                                                                                                    Check_type,
                                                                                                    Check_bytes
                                                                                                ),
                                                                                                fun(
                                                                                                    _
                                                                                                ) ->
                                                                                                    Total_block = After_padding
                                                                                                    + Check_size,
                                                                                                    Unpadded = (Header_size
                                                                                                    + Payload_size)
                                                                                                    + Check_size,
                                                                                                    Rest@1 = case gleam_stdlib:bit_array_slice(
                                                                                                        Bytes,
                                                                                                        Total_block,
                                                                                                        erlang:byte_size(
                                                                                                            Bytes
                                                                                                        )
                                                                                                        - Total_block
                                                                                                    ) of
                                                                                                        {ok,
                                                                                                            Rest} -> Rest;
                                                                                                        _assert_fail@1 ->
                                                                                                            erlang:error(
                                                                                                                    #{gleam_error => let_assert,
                                                                                                                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                                                                                        file => <<?FILEPATH/utf8>>,
                                                                                                                        module => <<"packkit/xz"/utf8>>,
                                                                                                                        function => <<"decode_block"/utf8>>,
                                                                                                                        line => 508,
                                                                                                                        value => _assert_fail@1,
                                                                                                                        start => 16894,
                                                                                                                        'end' => 17062,
                                                                                                                        pattern_start => 16905,
                                                                                                                        pattern_end => 16913}
                                                                                                                )
                                                                                                    end,
                                                                                                    {ok,
                                                                                                        {Plain,
                                                                                                            Unpadded,
                                                                                                            erlang:byte_size(
                                                                                                                Plain
                                                                                                            ),
                                                                                                            Rest@1}}
                                                                                                end
                                                                                            )
                                                                                        end
                                                                                    )
                                                                                end
                                                                            )
                                                                        end
                                                                    )
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end)
                                end
                            )
                        end
                    )
            end;

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

-file("src/packkit/xz.gleam", 348).
-spec decode_blocks(
    bitstring(),
    integer(),
    bitstring(),
    list({integer(), integer()}),
    packkit@limit:limits()
) -> {ok, {bitstring(), bitstring()}} | {error, packkit@error:codec_error()}.
decode_blocks(Bytes, Check_type, Output, Records_acc, Limits) ->
    case Bytes of
        <<16#00, Rest/binary>> ->
            finalize_stream(
                Rest,
                Check_type,
                Output,
                lists:reverse(Records_acc)
            );

        <<_, _/binary>> ->
            gleam@result:'try'(
                decode_block(Bytes, Check_type, Limits),
                fun(_use0) ->
                    {Plain, Unpadded, Uncompressed, Rest@1} = _use0,
                    gleam@result:'try'(
                        append_with_limit(Output, Plain, Limits),
                        fun(New_output) ->
                            decode_blocks(
                                Rest@1,
                                Check_type,
                                New_output,
                                [{Unpadded, Uncompressed} | Records_acc],
                                Limits
                            )
                        end
                    )
                end
            );

        _ ->
            {error,
                {codec_invalid_data,
                    <<"truncated xz stream before block or index"/utf8>>}}
    end.

-file("src/packkit/xz.gleam", 245).
-spec decode_streams_loop(
    bitstring(),
    bitstring(),
    integer(),
    packkit@limit:limits()
) -> {ok, bitstring()} | {error, packkit@error:codec_error()}.
decode_streams_loop(Bytes, Acc, Accumulated_size, Limits) ->
    gleam@result:'try'(
        parse_stream_header(Bytes),
        fun(_use0) ->
            {Header_flags, Rest} = _use0,
            gleam@result:'try'(
                decode_blocks(Rest, Header_flags, <<>>, [], Limits),
                fun(_use0@1) ->
                    {Payload, Rest@1} = _use0@1,
                    Next_size = Accumulated_size + erlang:byte_size(Payload),
                    case Next_size > packkit@limit:max_output_bytes(Limits) of
                        true ->
                            {error,
                                {codec_limit_exceeded,
                                    <<"max_output_bytes"/utf8>>,
                                    Next_size}};

                        false ->
                            Acc@1 = gleam_stdlib:bit_array_concat(
                                [Acc, Payload]
                            ),
                            Rest@2 = skip_stream_padding(Rest@1),
                            case erlang:byte_size(Rest@2) of
                                0 ->
                                    {ok, Acc@1};

                                _ ->
                                    decode_streams_loop(
                                        Rest@2,
                                        Acc@1,
                                        Next_size,
                                        Limits
                                    )
                            end
                    end
                end
            )
        end
    ).

-file("src/packkit/xz.gleam", 230).
?DOC(
    " Decode an xz stream using explicit limits.\n"
    "\n"
    " Handles multi-stream files (per `xz-file-format.txt` ยง1: a `.xz`\n"
    " file is a concatenation of one or more independent streams, each\n"
    " optionally followed by stream padding aligned to 4 bytes).  The\n"
    " concatenated payloads are returned as a single `BitArray`.\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() -> decode_streams_loop(Bytes, <<>>, 0, Limits) end
    ).

-file("src/packkit/xz.gleam", 220).
?DOC(" Decode an xz stream using default limits.\n").
-spec decode(bitstring()) -> {ok, bitstring()} |
    {error, packkit@error:codec_error()}.
decode(Bytes) ->
    decode_with_limits(Bytes, packkit@limit:default()).