-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, <>] ). -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(<>), gleam_stdlib:bit_array_concat( [<>, 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 -> <>; false -> Byte = erlang:'bor'(erlang:'band'(Value, 16#7F), 16#80), gleam_stdlib:bit_array_concat( [<>, 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( [<>, Pre_pad, Padding] ), Crc = packkit@checksum:crc32(Body_with_size), gleam_stdlib:bit_array_concat([Body_with_size, <>]). -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; _ -> 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(<>), 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, 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 <> -> 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, <>) 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 <> -> 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( <> ), 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 <> -> 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 => <>, 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; _assert_fail@1 -> erlang:error( #{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, 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 => <>, 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 <> -> 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, <>]). -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 => <>, 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 => <>, 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 => <>, 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 => <>, 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 = <>, 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 <> -> 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 => <>, 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 => <>, 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}}; <> 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 => <>, 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; <> when Control@1 >= 16#80 -> decode_lzma2_lzma_chunk( Payload, Control@1, Output, Consumed, Props, Limits ); <> -> {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 <> -> 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 => <>, 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 => <>, 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()).