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()).