-module(packkit@bzip2).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit/bzip2.gleam").
-export([codec/0, decode_with_limits/2, decode/1, encode_with_level/2, encode/1]).
-export_type([huffman_table/0, build_acc/0, huffman_stream_step/0, reader/0, huff_node/0, writer/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(
" bzip2 codec — pure Gleam decoder for `.bz2` streams.\n"
"\n"
" The decoder consumes the `\"BZh\"` magic, a single-character\n"
" block-size label (1..9), one or more compressed blocks, and the\n"
" stream end-marker plus 32-bit combined CRC. Each block is\n"
" reversed through Huffman decoding, MTF inversion, inverse\n"
" Burrows-Wheeler, and the RLE1 expansion that bzip2 applies to the\n"
" raw input before transformation. The encoder is intentionally\n"
" deferred.\n"
).
-type huffman_table() :: {huffman_table,
integer(),
integer(),
gleam@dict:dict(integer(), integer()),
gleam@dict:dict(integer(), integer()),
gleam@dict:dict(integer(), integer())}.
-type build_acc() :: {build_acc,
gleam@dict:dict(integer(), integer()),
gleam@dict:dict(integer(), integer()),
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
integer()}.
-type huffman_stream_step() :: {huffman_stream_done,
list(integer()),
integer(),
reader()} |
{huffman_stream_continue,
reader(),
list(integer()),
integer(),
integer(),
integer(),
list(integer()),
integer()}.
-type reader() :: {reader, integer(), integer(), bitstring(), boolean()}.
-type huff_node() :: {huff_leaf, integer(), integer()} |
{huff_internal, integer(), huff_node(), huff_node()}.
-type writer() :: {writer, list(integer()), integer(), integer()}.
-file("src/packkit/bzip2.gleam", 38).
?DOC(" bzip2 codec smart constructor.\n").
-spec codec() -> packkit@codec:codec().
codec() ->
packkit@codec:bzip2().
-file("src/packkit/bzip2.gleam", 145).
-spec parse_stream_header(bitstring()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
parse_stream_header(Bytes) ->
case Bytes of
<<16#42, 16#5A, 16#68, Level, Rest/binary>> when (Level >= 16#31) andalso (Level =< 16#39) ->
{ok, Rest};
_ ->
{error,
{codec_invalid_data, <<"invalid bzip2 stream header"/utf8>>}}
end.
-file("src/packkit/bzip2.gleam", 196).
?DOC(
" Recover the byte-aligned tail of the bit reader so the caller can\n"
" look for another stream's `\"BZh\"` magic. bzip2 streams pad to\n"
" the next byte boundary after the stream CRC, so any partial bits\n"
" still sitting in `reader.buffer` are padding and we discard them.\n"
).
-spec byte_aligned_remainder(reader()) -> bitstring().
byte_aligned_remainder(Reader) ->
_ = erlang:element(3, Reader),
erlang:element(4, Reader).
-file("src/packkit/bzip2.gleam", 201).
-spec combine_block_crc(integer(), integer()) -> integer().
combine_block_crc(Combined, Block_crc) ->
Rotated = erlang:'bor'(
erlang:'band'(erlang:'bsl'(Combined, 1), 16#FFFFFFFF),
erlang:'bsr'(Combined, 31)
),
erlang:'band'(erlang:'bxor'(Rotated, Block_crc), 16#FFFFFFFF).
-file("src/packkit/bzip2.gleam", 210).
-spec verify_block_crc(bitstring(), integer()) -> {ok, nil} |
{error, packkit@error:codec_error()}.
verify_block_crc(Data, Expected) ->
case packkit@checksum:bzip2_crc32(Data) =:= Expected of
true ->
{ok, nil};
false ->
{error, {codec_invalid_data, <<"bzip2 block CRC mismatch"/utf8>>}}
end.
-file("src/packkit/bzip2.gleam", 220).
-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/bzip2.gleam", 350).
-spec collect_group_bytes(integer(), integer(), integer(), list(integer())) -> list(integer()).
collect_group_bytes(Bits, Group, Offset, Acc) ->
case Offset >= 16 of
true ->
Acc;
false ->
Mask = erlang:'bsl'(1, 15 - Offset),
Acc@1 = case erlang:'band'(Bits, Mask) of
0 ->
Acc;
_ ->
[(Group * 16) + Offset | Acc]
end,
collect_group_bytes(Bits, Group, Offset + 1, Acc@1)
end.
-file("src/packkit/bzip2.gleam", 380).
-spec init_selector_stack(integer(), integer(), list(integer())) -> list(integer()).
init_selector_stack(Num, Index, Acc) ->
case Index >= Num of
true ->
lists:reverse(Acc);
false ->
init_selector_stack(Num, Index + 1, [Index | Acc])
end.
-file("src/packkit/bzip2.gleam", 421).
-spec pick_at(list(integer()), integer(), list(integer())) -> {integer(),
list(integer())}.
pick_at(Stack, Index, Prefix) ->
case {Stack, Index} of
{[Head | Rest], 0} ->
{Head, lists:append(lists:reverse(Prefix), Rest)};
{[Head@1 | Rest@1], _} ->
pick_at(Rest@1, Index - 1, [Head@1 | Prefix]);
{[], _} ->
{0, lists:reverse(Prefix)}
end.
-file("src/packkit/bzip2.gleam", 557).
-spec canonical_loop(list({integer(), integer()}), build_acc()) -> build_acc().
canonical_loop(Sorted, Acc) ->
case Sorted of
[] ->
Acc;
[{Sym, Length} | Rest] ->
{Code, Base, Limit} = case Length =:= erlang:element(7, Acc) of
true ->
{erlang:element(6, Acc),
erlang:element(2, Acc),
erlang:element(3, Acc)};
false ->
case erlang:element(7, Acc) of
0 ->
{0,
gleam@dict:insert(
erlang:element(2, Acc),
Length,
erlang:element(5, Acc)
),
erlang:element(3, Acc)};
_ ->
Shift = Length - erlang:element(7, Acc),
Prev_limit = gleam@dict:insert(
erlang:element(3, Acc),
erlang:element(7, Acc),
erlang:element(6, Acc) - 1
),
New_code = erlang:'bsl'(
erlang:element(6, Acc),
Shift
),
{New_code,
gleam@dict:insert(
erlang:element(2, Acc),
Length,
erlang:element(5, Acc) - New_code
),
Prev_limit}
end
end,
Symbols = gleam@dict:insert(
erlang:element(4, Acc),
erlang:element(5, Acc),
Sym
),
canonical_loop(
Rest,
{build_acc,
Base,
Limit,
Symbols,
erlang:element(5, Acc) + 1,
Code + 1,
Length}
)
end.
-file("src/packkit/bzip2.gleam", 599).
-spec enumerate_lengths(
list(integer()),
integer(),
list({integer(), integer()})
) -> list({integer(), integer()}).
enumerate_lengths(Lengths, Index, Acc) ->
case Lengths of
[] ->
lists:reverse(Acc);
[Head | Rest] ->
enumerate_lengths(Rest, Index + 1, [{Index, Head} | Acc])
end.
-file("src/packkit/bzip2.gleam", 611).
-spec find_min_length(list(integer()), integer()) -> integer().
find_min_length(Lengths, Best) ->
case Lengths of
[] ->
case Best of
21 ->
1;
_ ->
Best
end;
[Head | Rest] ->
Best@1 = case Head < Best of
true ->
Head;
false ->
Best
end,
find_min_length(Rest, Best@1)
end.
-file("src/packkit/bzip2.gleam", 628).
-spec find_max_length(list(integer()), integer()) -> integer().
find_max_length(Lengths, Best) ->
case Lengths of
[] ->
Best;
[Head | Rest] ->
Best@1 = case Head > Best of
true ->
Head;
false ->
Best
end,
find_max_length(Rest, Best@1)
end.
-file("src/packkit/bzip2.gleam", 661).
-spec prepend_reversed(
list({integer(), integer()}),
list({integer(), integer()})
) -> list({integer(), integer()}).
prepend_reversed(Values, Acc) ->
case Values of
[] ->
Acc;
[Head | Rest] ->
prepend_reversed(Rest, [Head | Acc])
end.
-file("src/packkit/bzip2.gleam", 641).
-spec sort_pairs_by_length(
list({integer(), integer()}),
integer(),
integer(),
list({integer(), integer()})
) -> list({integer(), integer()}).
sort_pairs_by_length(Pairs, Length, Max_length, Acc) ->
case Length > Max_length of
true ->
lists:reverse(Acc);
false ->
Bucket = gleam@list:filter(
Pairs,
fun(Pair) -> erlang:element(2, Pair) =:= Length end
),
sort_pairs_by_length(
Pairs,
Length + 1,
Max_length,
prepend_reversed(Bucket, Acc)
)
end.
-file("src/packkit/bzip2.gleam", 673).
-spec list_to_dict(list(GXM), integer(), gleam@dict:dict(integer(), GXM)) -> gleam@dict:dict(integer(), GXM).
list_to_dict(Values, Index, Acc) ->
case Values of
[] ->
Acc;
[Head | Rest] ->
list_to_dict(Rest, Index + 1, gleam@dict:insert(Acc, Index, Head))
end.
-file("src/packkit/bzip2.gleam", 885).
-spec emit_byte(list(integer()), integer(), integer(), packkit@limit:limits()) -> {ok,
{list(integer()), integer()}} |
{error, packkit@error:codec_error()}.
emit_byte(Out_rev, Out_len, Byte, Limits) ->
gleam@bool:guard(
(Out_len + 1) > packkit@limit:max_output_bytes(Limits),
{error,
{codec_limit_exceeded, <<"max_output_bytes"/utf8>>, Out_len + 1}},
fun() -> {ok, {[Byte | Out_rev], Out_len + 1}} end
).
-file("src/packkit/bzip2.gleam", 901).
-spec repeat_prepend(integer(), integer(), list(integer())) -> list(integer()).
repeat_prepend(Value, Count, Acc) ->
case Count of
0 ->
Acc;
_ ->
repeat_prepend(Value, Count - 1, [Value | Acc])
end.
-file("src/packkit/bzip2.gleam", 858).
-spec flush_run(
list(integer()),
integer(),
list(integer()),
integer(),
packkit@limit:limits()
) -> {ok, {list(integer()), integer()}} | {error, packkit@error:codec_error()}.
flush_run(Out_rev, Out_len, Mtf, Pending, Limits) ->
case Pending of
0 ->
{ok, {Out_rev, Out_len}};
_ ->
Front = case Mtf of
[Head | _] ->
Head;
[] ->
0
end,
gleam@bool:guard(
(Out_len + Pending) > packkit@limit:max_output_bytes(Limits),
{error,
{codec_limit_exceeded,
<<"max_output_bytes"/utf8>>,
Out_len + Pending}},
fun() ->
Out_rev@1 = repeat_prepend(Front, Pending, Out_rev),
{ok, {Out_rev@1, Out_len + Pending}}
end
)
end.
-file("src/packkit/bzip2.gleam", 920).
-spec mtf_pick_loop(list(integer()), integer(), list(integer())) -> {ok,
{integer(), list(integer()), list(integer())}} |
{error, nil}.
mtf_pick_loop(Mtf, Index, Prefix) ->
case {Mtf, Index} of
{[Head | Rest], 0} ->
{ok, {Head, Prefix, Rest}};
{[Head@1 | Rest@1], _} ->
mtf_pick_loop(Rest@1, Index - 1, [Head@1 | Prefix]);
{[], _} ->
{error, nil}
end.
-file("src/packkit/bzip2.gleam", 908).
-spec mtf_pick_byte(list(integer()), integer()) -> {ok,
{integer(), list(integer())}} |
{error, packkit@error:codec_error()}.
mtf_pick_byte(Mtf, Index) ->
case mtf_pick_loop(Mtf, Index, []) of
{ok, {Byte, Prefix, Rest}} ->
{ok, {Byte, [Byte | lists:append(lists:reverse(Prefix), Rest)]}};
{error, _} ->
{error,
{codec_invalid_data, <<"bzip2 MTF index out of range"/utf8>>}}
end.
-file("src/packkit/bzip2.gleam", 932).
-spec list_to_indexed_dict(
list(integer()),
integer(),
gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
list_to_indexed_dict(Values, Index, Acc) ->
case Values of
[] ->
Acc;
[Head | Rest] ->
list_to_indexed_dict(
Rest,
Index + 1,
gleam@dict:insert(Acc, Index, Head)
)
end.
-file("src/packkit/bzip2.gleam", 1012).
-spec count_bytes(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
count_bytes(L_string, Index, Length, Acc) ->
case Index >= Length of
true ->
Acc;
false ->
Byte = case gleam_stdlib:map_get(L_string, Index) of
{ok, V} ->
V;
{error, _} ->
0
end,
Current = case gleam_stdlib:map_get(Acc, Byte) of
{ok, V@1} ->
V@1;
{error, _} ->
0
end,
count_bytes(
L_string,
Index + 1,
Length,
gleam@dict:insert(Acc, Byte, Current + 1)
)
end.
-file("src/packkit/bzip2.gleam", 1039).
-spec build_cumulative(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
build_cumulative(Counts, Symbol, Acc, Out) ->
case Symbol >= 256 of
true ->
Out;
false ->
Out@1 = gleam@dict:insert(Out, Symbol, Acc),
N = case gleam_stdlib:map_get(Counts, Symbol) of
{ok, V} ->
V;
{error, _} ->
0
end,
build_cumulative(Counts, Symbol + 1, Acc + N, Out@1)
end.
-file("src/packkit/bzip2.gleam", 1058).
-spec build_t(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
gleam@dict:dict(integer(), integer()),
gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
build_t(L_string, Index, Length, Cumulative, T) ->
case Index >= Length of
true ->
T;
false ->
Byte = case gleam_stdlib:map_get(L_string, Index) of
{ok, V} ->
V;
{error, _} ->
0
end,
Slot = case gleam_stdlib:map_get(Cumulative, Byte) of
{ok, V@1} ->
V@1;
{error, _} ->
0
end,
Cumulative@1 = gleam@dict:insert(Cumulative, Byte, Slot + 1),
build_t(
L_string,
Index + 1,
Length,
Cumulative@1,
gleam@dict:insert(T, Slot, Index)
)
end.
-file("src/packkit/bzip2.gleam", 1088).
-spec walk_bwt(
gleam@dict:dict(integer(), integer()),
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
integer(),
gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
walk_bwt(L_string, T, Pos, Index, Length, Out) ->
case Index >= Length of
true ->
Out;
false ->
Next = case gleam_stdlib:map_get(T, Pos) of
{ok, V} ->
V;
{error, _} ->
0
end,
Byte = case gleam_stdlib:map_get(L_string, Next) of
{ok, V@1} ->
V@1;
{error, _} ->
0
end,
walk_bwt(
L_string,
T,
Next,
Index + 1,
Length,
gleam@dict:insert(Out, Index, Byte)
)
end.
-file("src/packkit/bzip2.gleam", 1001).
-spec inverse_bwt(gleam@dict:dict(integer(), integer()), integer(), integer()) -> gleam@dict:dict(integer(), integer()).
inverse_bwt(L_string, Length, Orig_ptr) ->
Counts = count_bytes(L_string, 0, Length, maps:new()),
Cumulative = build_cumulative(Counts, 0, 0, maps:new()),
T = build_t(L_string, 0, Length, Cumulative, maps:new()),
walk_bwt(L_string, T, Orig_ptr, 0, Length, maps:new()).
-file("src/packkit/bzip2.gleam", 1127).
-spec rle1_decode_loop(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
integer(),
integer(),
list(integer())
) -> list(integer()).
rle1_decode_loop(Input, Index, Length, Last_byte, Run, Acc) ->
case Index >= Length of
true ->
Acc;
false ->
Byte = case gleam_stdlib:map_get(Input, Index) of
{ok, V} ->
V;
{error, _} ->
0
end,
case Run =:= 4 of
true ->
Acc@1 = repeat_prepend(Last_byte, Byte, Acc),
rle1_decode_loop(Input, Index + 1, Length, -1, 0, Acc@1);
false ->
case Byte =:= Last_byte of
true ->
rle1_decode_loop(
Input,
Index + 1,
Length,
Byte,
Run + 1,
[Byte | Acc]
);
false ->
rle1_decode_loop(
Input,
Index + 1,
Length,
Byte,
1,
[Byte | Acc]
)
end
end
end.
-file("src/packkit/bzip2.gleam", 1163).
-spec bytes_list_to_bit_array(list(integer()), bitstring()) -> bitstring().
bytes_list_to_bit_array(Bytes, Acc) ->
case Bytes of
[] ->
Acc;
[Head | Rest] ->
bytes_list_to_bit_array(Rest, <<Acc/bitstring, Head>>)
end.
-file("src/packkit/bzip2.gleam", 1121).
-spec rle1_decode(gleam@dict:dict(integer(), integer()), integer()) -> bitstring().
rle1_decode(Input, Length) ->
_pipe = rle1_decode_loop(Input, 0, Length, -1, 0, []),
_pipe@1 = lists:reverse(_pipe),
bytes_list_to_bit_array(_pipe@1, <<>>).
-file("src/packkit/bzip2.gleam", 1176).
-spec new_reader(bitstring()) -> reader().
new_reader(Source) ->
{reader, 0, 0, Source, false}.
-file("src/packkit/bzip2.gleam", 1180).
-spec refill(reader(), integer()) -> reader().
refill(Reader, Needed) ->
case (erlang:element(2, Reader) >= Needed) orelse erlang:element(5, Reader) of
true ->
Reader;
false ->
case erlang:element(4, Reader) of
<<B, Rest/binary>> ->
refill(
{reader,
erlang:element(2, Reader) + 8,
erlang:'bor'(
erlang:'bsl'(erlang:element(3, Reader), 8),
B
),
Rest,
false},
Needed
);
_ ->
{reader,
erlang:element(2, Reader),
erlang:element(3, Reader),
<<>>,
true}
end
end.
-file("src/packkit/bzip2.gleam", 1209).
-spec read_bits(reader(), integer()) -> {ok, {integer(), reader()}} |
{error, packkit@error:codec_error()}.
read_bits(Reader, Count) ->
Reader@1 = refill(Reader, Count),
case erlang:element(2, Reader@1) < Count of
true ->
{error, {codec_invalid_data, <<"truncated bzip2 stream"/utf8>>}};
false ->
New_bits = erlang:element(2, Reader@1) - Count,
Mask = erlang:'bsl'(1, Count) - 1,
Value = erlang:'band'(
erlang:'bsr'(erlang:element(3, Reader@1), New_bits),
Mask
),
Buffer_mask = erlang:'bsl'(1, New_bits) - 1,
New_buffer = erlang:'band'(erlang:element(3, Reader@1), Buffer_mask),
{ok,
{Value,
{reader,
New_bits,
New_buffer,
erlang:element(4, Reader@1),
erlang:element(5, Reader@1)}}}
end.
-file("src/packkit/bzip2.gleam", 328).
-spec read_symbol_groups(reader(), integer(), integer(), list(integer())) -> {ok,
{list(integer()), reader()}} |
{error, packkit@error:codec_error()}.
read_symbol_groups(Reader, Presence, Group, Acc) ->
case Group >= 16 of
true ->
{ok, {lists:reverse(Acc), Reader}};
false ->
Bit_mask = erlang:'bsl'(1, 15 - Group),
case erlang:'band'(Presence, Bit_mask) of
0 ->
read_symbol_groups(Reader, Presence, Group + 1, Acc);
_ ->
gleam@result:'try'(
read_bits(Reader, 16),
fun(_use0) ->
{Bits, Reader@1} = _use0,
Acc@1 = collect_group_bytes(Bits, Group, 0, Acc),
read_symbol_groups(
Reader@1,
Presence,
Group + 1,
Acc@1
)
end
)
end
end.
-file("src/packkit/bzip2.gleam", 321).
-spec read_symbol_map(reader()) -> {ok, {list(integer()), reader()}} |
{error, packkit@error:codec_error()}.
read_symbol_map(Reader) ->
gleam@result:'try'(
read_bits(Reader, 16),
fun(_use0) ->
{Present_groups, Reader@1} = _use0,
read_symbol_groups(Reader@1, Present_groups, 0, [])
end
).
-file("src/packkit/bzip2.gleam", 410).
-spec read_unary(reader(), integer()) -> {ok, {integer(), reader()}} |
{error, packkit@error:codec_error()}.
read_unary(Reader, Acc) ->
gleam@result:'try'(
read_bits(Reader, 1),
fun(_use0) ->
{Bit, Reader@1} = _use0,
case Bit of
0 ->
{ok, {Acc, Reader@1}};
_ ->
read_unary(Reader@1, Acc + 1)
end
end
).
-file("src/packkit/bzip2.gleam", 387).
-spec read_selectors_loop(reader(), integer(), list(integer()), list(integer())) -> {ok,
{list(integer()), reader()}} |
{error, packkit@error:codec_error()}.
read_selectors_loop(Reader, Remaining, Stack, Acc) ->
case Remaining of
0 ->
{ok, {lists:reverse(Acc), Reader}};
_ ->
gleam@result:'try'(
read_unary(Reader, 0),
fun(_use0) ->
{Index, Reader@1} = _use0,
gleam@bool:guard(
Index >= erlang:length(Stack),
{error,
{codec_invalid_data,
<<"bzip2 selector out of range"/utf8>>}},
fun() ->
{Picked, Remaining_stack} = pick_at(
Stack,
Index,
[]
),
Stack@1 = [Picked | Remaining_stack],
read_selectors_loop(
Reader@1,
Remaining - 1,
Stack@1,
[Picked | Acc]
)
end
)
end
)
end.
-file("src/packkit/bzip2.gleam", 371).
-spec read_selectors(reader(), integer(), integer()) -> {ok,
{list(integer()), reader()}} |
{error, packkit@error:codec_error()}.
read_selectors(Reader, Remaining, Num_tables) ->
Stack = init_selector_stack(Num_tables, 0, []),
read_selectors_loop(Reader, Remaining, Stack, []).
-file("src/packkit/bzip2.gleam", 492).
-spec adjust_length(reader(), integer()) -> {ok, {integer(), reader()}} |
{error, packkit@error:codec_error()}.
adjust_length(Reader, Current) ->
gleam@result:'try'(
read_bits(Reader, 1),
fun(_use0) ->
{Flag, Reader@1} = _use0,
case Flag of
0 ->
{ok, {Current, Reader@1}};
_ ->
gleam@result:'try'(
read_bits(Reader@1, 1),
fun(_use0@1) ->
{Direction, Reader@2} = _use0@1,
Updated = case Direction of
0 ->
Current + 1;
_ ->
Current - 1
end,
adjust_length(Reader@2, Updated)
end
)
end
end
).
-file("src/packkit/bzip2.gleam", 952).
-spec walk_huffman(reader(), huffman_table(), integer(), integer()) -> {ok,
{integer(), reader()}} |
{error, packkit@error:codec_error()}.
walk_huffman(Reader, Table, Length, Code) ->
case Length > erlang:element(3, Table) of
true ->
{error,
{codec_invalid_data, <<"bzip2 huffman code overflow"/utf8>>}};
false ->
Limit_value = case gleam_stdlib:map_get(
erlang:element(5, Table),
Length
) of
{ok, V} ->
V;
{error, _} ->
-1
end,
case Code =< Limit_value of
true ->
Base_value = case gleam_stdlib:map_get(
erlang:element(4, Table),
Length
) of
{ok, V@1} ->
V@1;
{error, _} ->
0
end,
Index = Base_value + Code,
Symbol = case gleam_stdlib:map_get(
erlang:element(6, Table),
Index
) of
{ok, V@2} ->
V@2;
{error, _} ->
-1
end,
case Symbol < 0 of
true ->
{error,
{codec_invalid_data,
<<"bzip2 huffman symbol lookup failed"/utf8>>}};
false ->
{ok, {Symbol, Reader}}
end;
false ->
gleam@result:'try'(
read_bits(Reader, 1),
fun(_use0) ->
{Bit, Reader@1} = _use0,
walk_huffman(
Reader@1,
Table,
Length + 1,
erlang:'bor'(erlang:'bsl'(Code, 1), Bit)
)
end
)
end
end.
-file("src/packkit/bzip2.gleam", 944).
-spec decode_one_symbol(reader(), huffman_table()) -> {ok,
{integer(), reader()}} |
{error, packkit@error:codec_error()}.
decode_one_symbol(Reader, Table) ->
gleam@result:'try'(
read_bits(Reader, erlang:element(2, Table)),
fun(_use0) ->
{Code, Reader@1} = _use0,
walk_huffman(Reader@1, Table, erlang:element(2, Table), Code)
end
).
-file("src/packkit/bzip2.gleam", 1274).
-spec rle1_loop(bitstring(), integer(), integer(), list(integer())) -> list(integer()).
rle1_loop(Bytes, Last, Run, Acc) ->
case Bytes of
<<B, Rest/binary>> ->
case B =:= Last of
true ->
case Run of
R when R < 4 ->
rle1_loop(Rest, Last, Run + 1, [B | Acc]);
R@1 when R@1 < 259 ->
case R@1 >= (4 + 254) of
true ->
rle1_loop(Rest, -1, 0, [255 | Acc]);
false ->
rle1_loop(Rest, Last, Run + 1, Acc)
end;
_ ->
rle1_loop(Rest, Last, Run + 1, Acc)
end;
false ->
case Run >= 4 of
true ->
Extra = Run - 4,
rle1_loop(Rest, B, 1, [B, Extra | Acc]);
false ->
rle1_loop(Rest, B, 1, [B | Acc])
end
end;
_ ->
case Run >= 4 of
true ->
Extra@1 = Run - 4,
lists:reverse([Extra@1 | Acc]);
false ->
lists:reverse(Acc)
end
end.
-file("src/packkit/bzip2.gleam", 1270).
-spec rle1_encode(bitstring()) -> list(integer()).
rle1_encode(Bytes) ->
rle1_loop(Bytes, -1, 0, []).
-file("src/packkit/bzip2.gleam", 1326).
-spec list_range(integer(), integer(), list(integer())) -> list(integer()).
list_range(Low, High, Acc) ->
case Low > High of
true ->
lists:reverse(Acc);
false ->
list_range(Low + 1, High, [Low | Acc])
end.
-file("src/packkit/bzip2.gleam", 1362).
-spec byte_at_index(gleam@dict:dict(integer(), integer()), integer()) -> integer().
byte_at_index(Table, Index) ->
case gleam_stdlib:map_get(Table, Index) of
{ok, V} ->
V;
{error, _} ->
0
end.
-file("src/packkit/bzip2.gleam", 1369).
-spec mod_index(integer(), integer()) -> integer().
mod_index(Value, N) ->
case N of
0 ->
0;
_ ->
R = Value - ((case N of
0 -> 0;
Gleam@denominator -> Value div Gleam@denominator
end) * N),
case R < 0 of
true ->
R + N;
false ->
R
end
end.
-file("src/packkit/bzip2.gleam", 1342).
-spec compare_loop(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
integer(),
integer()
) -> gleam@order:order().
compare_loop(Table, N, A, B, Offset) ->
case Offset >= N of
true ->
eq;
false ->
Ba = byte_at_index(Table, mod_index(A + Offset, N)),
Bb = byte_at_index(Table, mod_index(B + Offset, N)),
case Ba =:= Bb of
true ->
compare_loop(Table, N, A, B, Offset + 1);
false ->
gleam@int:compare(Ba, Bb)
end
end.
-file("src/packkit/bzip2.gleam", 1333).
-spec compare_rotations(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
integer()
) -> gleam@order:order().
compare_rotations(Table, N, A, B) ->
compare_loop(Table, N, A, B, 0).
-file("src/packkit/bzip2.gleam", 1382).
-spec build_l_dict(
list(integer()),
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
build_l_dict(Sorted, Table, N, Index, Acc) ->
case Sorted of
[] ->
Acc;
[Head | Rest] ->
Byte = byte_at_index(Table, mod_index((Head + N) - 1, N)),
build_l_dict(
Rest,
Table,
N,
Index + 1,
gleam@dict:insert(Acc, Index, Byte)
)
end.
-file("src/packkit/bzip2.gleam", 1398).
-spec find_index(list(integer()), integer(), integer()) -> integer().
find_index(Values, Target, Index) ->
case Values of
[Head | _] when Head =:= Target ->
Index;
[_ | Rest] ->
find_index(Rest, Target, Index + 1);
[] ->
0
end.
-file("src/packkit/bzip2.gleam", 1316).
-spec bwt_forward(list(integer()), integer()) -> {gleam@dict:dict(integer(), integer()),
integer()}.
bwt_forward(Input, N) ->
Table = list_to_dict(Input, 0, maps:new()),
Indices = list_range(0, N - 1, []),
Sorted = gleam@list:sort(
Indices,
fun(A, B) -> compare_rotations(Table, N, A, B) end
),
L_dict = build_l_dict(Sorted, Table, N, 0, maps:new()),
Orig_ptr = find_index(Sorted, 0, 0),
{L_dict, Orig_ptr}.
-file("src/packkit/bzip2.gleam", 1408).
-spec sorted_unique_bytes(gleam@dict:dict(integer(), integer())) -> list(integer()).
sorted_unique_bytes(L_string) ->
Unique_dict = gleam@dict:fold(
L_string,
maps:new(),
fun(Acc, _, Value) -> gleam@dict:insert(Acc, Value, true) end
),
Bytes = maps:keys(Unique_dict),
gleam@list:sort(Bytes, fun gleam@int:compare/2).
-file("src/packkit/bzip2.gleam", 1441).
-spec find_and_pop(list(integer()), integer(), integer(), list(integer())) -> {integer(),
list(integer())}.
find_and_pop(Stack, Target, Pos, Prefix) ->
case Stack of
[Head | Rest] when Head =:= Target ->
{Pos, lists:append(lists:reverse(Prefix), Rest)};
[Head@1 | Rest@1] ->
find_and_pop(Rest@1, Target, Pos + 1, [Head@1 | Prefix]);
[] ->
{Pos, lists:reverse(Prefix)}
end.
-file("src/packkit/bzip2.gleam", 1421).
-spec mtf_forward_loop(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
list(integer()),
list(integer())
) -> list(integer()).
mtf_forward_loop(L_string, Index, Length, Stack, Acc) ->
case Index >= Length of
true ->
lists:reverse(Acc);
false ->
Byte = byte_at_index(L_string, Index),
{Pos, New_stack} = find_and_pop(Stack, Byte, 0, []),
mtf_forward_loop(
L_string,
Index + 1,
Length,
[Byte | New_stack],
[Pos | Acc]
)
end.
-file("src/packkit/bzip2.gleam", 1417).
-spec mtf_forward(gleam@dict:dict(integer(), integer()), list(integer())) -> list(integer()).
mtf_forward(L_string, Unique) ->
mtf_forward_loop(L_string, 0, maps:size(L_string), Unique, []).
-file("src/packkit/bzip2.gleam", 1487).
-spec emit_runa_runb(integer(), list(integer())) -> list(integer()).
emit_runa_runb(Value, Acc) ->
case Value of
1 ->
Acc;
_ ->
Bit = erlang:'band'(Value, 1),
Next = erlang:'bsr'(Value, 1),
emit_runa_runb(Next, [Bit | Acc])
end.
-file("src/packkit/bzip2.gleam", 1480).
-spec flush_zero_run(integer(), list(integer())) -> list(integer()).
flush_zero_run(Count, Acc) ->
case Count of
0 ->
Acc;
_ ->
emit_runa_runb(Count + 1, Acc)
end.
-file("src/packkit/bzip2.gleam", 1461).
-spec rle2_loop(list(integer()), integer(), integer(), list(integer())) -> list(integer()).
rle2_loop(Mtf, Zero_run, Eob, Acc) ->
case Mtf of
[0 | Rest] ->
rle2_loop(Rest, Zero_run + 1, Eob, Acc);
[N | Rest@1] ->
Acc@1 = flush_zero_run(Zero_run, Acc),
rle2_loop(Rest@1, 0, Eob, [N + 1 | Acc@1]);
[] ->
Acc@2 = flush_zero_run(Zero_run, Acc),
lists:reverse([Eob | Acc@2])
end.
-file("src/packkit/bzip2.gleam", 1457).
-spec rle2_encode(list(integer()), integer()) -> list(integer()).
rle2_encode(Mtf, Eob) ->
rle2_loop(Mtf, 0, Eob, []).
-file("src/packkit/bzip2.gleam", 1511).
-spec count_frequencies(list(integer()), gleam@dict:dict(integer(), integer())) -> gleam@dict:dict(integer(), integer()).
count_frequencies(Symbols, Acc) ->
case Symbols of
[] ->
Acc;
[Head | Rest] ->
Current = case gleam_stdlib:map_get(Acc, Head) of
{ok, V} ->
V;
{error, _} ->
0
end,
count_frequencies(Rest, gleam@dict:insert(Acc, Head, Current + 1))
end.
-file("src/packkit/bzip2.gleam", 1527).
-spec pad_min_frequencies(
gleam@dict:dict(integer(), integer()),
integer(),
integer()
) -> gleam@dict:dict(integer(), integer()).
pad_min_frequencies(Freq, Symbol, Alphabet_size) ->
case Symbol >= Alphabet_size of
true ->
Freq;
false ->
Freq@1 = case gleam_stdlib:map_get(Freq, Symbol) of
{ok, _} ->
Freq;
{error, _} ->
gleam@dict:insert(Freq, Symbol, 1)
end,
pad_min_frequencies(Freq@1, Symbol + 1, Alphabet_size)
end.
-file("src/packkit/bzip2.gleam", 1564).
-spec flatten_frequencies(gleam@dict:dict(integer(), integer())) -> gleam@dict:dict(integer(), integer()).
flatten_frequencies(Freq) ->
gleam@dict:fold(
Freq,
maps:new(),
fun(Acc, Sym, Count) ->
New_count = case Count of
N when N =< 1 ->
1;
N@1 ->
((N@1 + 1) div 2) + 1
end,
gleam@dict:insert(Acc, Sym, New_count)
end
).
-file("src/packkit/bzip2.gleam", 1577).
-spec max_length_in_dict(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
integer()
) -> integer().
max_length_in_dict(Lengths, Symbol, Alphabet_size, Best) ->
case Symbol >= Alphabet_size of
true ->
Best;
false ->
Len = case gleam_stdlib:map_get(Lengths, Symbol) of
{ok, V} ->
V;
{error, _} ->
0
end,
Best@1 = case Len > Best of
true ->
Len;
false ->
Best
end,
max_length_in_dict(Lengths, Symbol + 1, Alphabet_size, Best@1)
end.
-file("src/packkit/bzip2.gleam", 1614).
-spec build_leaves(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
list(huff_node())
) -> list(huff_node()).
build_leaves(Freq, Symbol, Alphabet_size, Acc) ->
case Symbol >= Alphabet_size of
true ->
Acc;
false ->
Weight = case gleam_stdlib:map_get(Freq, Symbol) of
{ok, V} ->
V;
{error, _} ->
0
end,
build_leaves(
Freq,
Symbol + 1,
Alphabet_size,
[{huff_leaf, Symbol, Weight} | Acc]
)
end.
-file("src/packkit/bzip2.gleam", 1659).
-spec node_weight(huff_node()) -> integer().
node_weight(Node) ->
case Node of
{huff_leaf, _, W} ->
W;
{huff_internal, W@1, _, _} ->
W@1
end.
-file("src/packkit/bzip2.gleam", 1635).
-spec merge_nodes(list(huff_node())) -> huff_node().
merge_nodes(Nodes) ->
case Nodes of
[Single] ->
Single;
_ ->
Sorted = gleam@list:sort(
Nodes,
fun(A, B) ->
gleam@int:compare(node_weight(A), node_weight(B))
end
),
case Sorted of
[A@1, B@1 | Rest] ->
merge_nodes(
[{huff_internal,
node_weight(A@1) + node_weight(B@1),
A@1,
B@1} |
Rest]
);
_ ->
{huff_leaf, 0, 0}
end
end.
-file("src/packkit/bzip2.gleam", 1666).
-spec collect_lengths(
huff_node(),
integer(),
gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
collect_lengths(Node, Depth, Acc) ->
case Node of
{huff_leaf, Symbol, _} ->
Depth@1 = case Depth of
0 ->
1;
_ ->
Depth
end,
gleam@dict:insert(Acc, Symbol, Depth@1);
{huff_internal, _, Left, Right} ->
Acc@1 = collect_lengths(Left, Depth + 1, Acc),
collect_lengths(Right, Depth + 1, Acc@1)
end.
-file("src/packkit/bzip2.gleam", 1600).
-spec compute_huffman_lengths(gleam@dict:dict(integer(), integer()), integer()) -> gleam@dict:dict(integer(), integer()).
compute_huffman_lengths(Freq, Alphabet_size) ->
Nodes = build_leaves(Freq, 0, Alphabet_size, []),
Merged_root = merge_nodes(Nodes),
collect_lengths(Merged_root, 0, maps:new()).
-file("src/packkit/bzip2.gleam", 1547).
-spec package_merge_lengths(
gleam@dict:dict(integer(), integer()),
integer(),
integer()
) -> gleam@dict:dict(integer(), integer()).
package_merge_lengths(Freq, Alphabet_size, Max_length) ->
Lengths = compute_huffman_lengths(Freq, Alphabet_size),
case max_length_in_dict(Lengths, 0, Alphabet_size, 0) > Max_length of
false ->
Lengths;
true ->
package_merge_lengths(
flatten_frequencies(Freq),
Alphabet_size,
Max_length
)
end.
-file("src/packkit/bzip2.gleam", 1502).
-spec build_huffman_lengths(list(integer()), integer()) -> gleam@dict:dict(integer(), integer()).
build_huffman_lengths(Symbols, Alphabet_size) ->
Freq = count_frequencies(Symbols, maps:new()),
Freq@1 = pad_min_frequencies(Freq, 0, Alphabet_size),
package_merge_lengths(Freq@1, Alphabet_size, 17).
-file("src/packkit/bzip2.gleam", 1700).
-spec assign_codes(
list({integer(), integer()}),
integer(),
integer(),
gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
assign_codes(Pairs, Code, Prev_length, Acc) ->
case Pairs of
[] ->
Acc;
[{Sym, Length} | Rest] ->
Code@1 = case Prev_length of
0 ->
0;
_ ->
erlang:'bsl'(Code, Length - Prev_length)
end,
assign_codes(
Rest,
Code@1 + 1,
Length,
gleam@dict:insert(Acc, Sym, Code@1)
)
end.
-file("src/packkit/bzip2.gleam", 1688).
-spec canonical_codes(gleam@dict:dict(integer(), integer())) -> gleam@dict:dict(integer(), integer()).
canonical_codes(Lengths) ->
Pairs = maps:to_list(Lengths),
Sorted = gleam@list:sort(
Pairs,
fun(A, B) ->
case gleam@int:compare(erlang:element(2, A), erlang:element(2, B)) of
eq ->
gleam@int:compare(
erlang:element(1, A),
erlang:element(1, B)
);
Other ->
Other
end
end
),
assign_codes(Sorted, 0, 0, maps:new()).
-file("src/packkit/bzip2.gleam", 1727).
-spec bytes_to_group_dict(
list(integer()),
gleam@dict:dict(integer(), list(integer()))
) -> gleam@dict:dict(integer(), list(integer())).
bytes_to_group_dict(Bytes, Acc) ->
case Bytes of
[] ->
Acc;
[B | Rest] ->
Group = B div 16,
Existing = case gleam_stdlib:map_get(Acc, Group) of
{ok, V} ->
V;
{error, _} ->
[]
end,
bytes_to_group_dict(
Rest,
gleam@dict:insert(Acc, Group, [B | Existing])
)
end.
-file("src/packkit/bzip2.gleam", 1744).
-spec compute_group_used(
gleam@dict:dict(integer(), list(integer())),
integer(),
gleam@dict:dict(integer(), boolean())
) -> gleam@dict:dict(integer(), boolean()).
compute_group_used(Group_dict, Group, Acc) ->
case Group >= 16 of
true ->
Acc;
false ->
Used = case gleam_stdlib:map_get(Group_dict, Group) of
{ok, _} ->
true;
{error, _} ->
false
end,
compute_group_used(
Group_dict,
Group + 1,
gleam@dict:insert(Acc, Group, Used)
)
end.
-file("src/packkit/bzip2.gleam", 1935).
-spec new_writer() -> writer().
new_writer() ->
{writer, [], 0, 0}.
-file("src/packkit/bzip2.gleam", 1955).
-spec mask_for(integer()) -> integer().
mask_for(Count) ->
erlang:'bsl'(1, Count) - 1.
-file("src/packkit/bzip2.gleam", 1959).
-spec flush_writer_full_bytes(writer()) -> writer().
flush_writer_full_bytes(Writer) ->
case erlang:element(4, Writer) >= 8 of
false ->
Writer;
true ->
Shift = erlang:element(4, Writer) - 8,
Byte = erlang:'band'(
erlang:'bsr'(erlang:element(3, Writer), Shift),
16#FF
),
New_buffer = erlang:'band'(
erlang:element(3, Writer),
mask_for(Shift)
),
flush_writer_full_bytes(
{writer, [Byte | erlang:element(2, Writer)], New_buffer, Shift}
)
end.
-file("src/packkit/bzip2.gleam", 1939).
-spec write_bits_msb(writer(), integer(), integer()) -> writer().
write_bits_msb(Writer, Value, Count) ->
case Count of
0 ->
Writer;
_ ->
Masked = erlang:'band'(Value, mask_for(Count)),
Buffer = erlang:'bor'(
erlang:'bsl'(erlang:element(3, Writer), Count),
Masked
),
flush_writer_full_bytes(
{writer,
erlang:element(2, Writer),
Buffer,
erlang:element(4, Writer) + Count}
)
end.
-file("src/packkit/bzip2.gleam", 1761).
-spec emit_group_high(
writer(),
gleam@dict:dict(integer(), boolean()),
integer()
) -> writer().
emit_group_high(Writer, Group_used, Group) ->
case Group >= 16 of
true ->
Writer;
false ->
Bit = case gleam_stdlib:map_get(Group_used, Group) of
{ok, true} ->
1;
_ ->
0
end,
emit_group_high(
write_bits_msb(Writer, Bit, 1),
Group_used,
Group + 1
)
end.
-file("src/packkit/bzip2.gleam", 1801).
-spec emit_bitmap_for_group(writer(), list(integer()), integer(), integer()) -> writer().
emit_bitmap_for_group(Writer, Bytes, Group, Offset) ->
case Offset >= 16 of
true ->
Writer;
false ->
Target = (Group * 16) + Offset,
Bit = case gleam@list:contains(Bytes, Target) of
true ->
1;
false ->
0
end,
emit_bitmap_for_group(
write_bits_msb(Writer, Bit, 1),
Bytes,
Group,
Offset + 1
)
end.
-file("src/packkit/bzip2.gleam", 1778).
-spec emit_group_bitmaps(
writer(),
gleam@dict:dict(integer(), boolean()),
gleam@dict:dict(integer(), list(integer())),
integer()
) -> writer().
emit_group_bitmaps(Writer, Group_used, Group_dict, Group) ->
case Group >= 16 of
true ->
Writer;
false ->
case gleam_stdlib:map_get(Group_used, Group) of
{ok, true} ->
Bytes = case gleam_stdlib:map_get(Group_dict, Group) of
{ok, V} ->
V;
{error, _} ->
[]
end,
Writer@1 = emit_bitmap_for_group(Writer, Bytes, Group, 0),
emit_group_bitmaps(
Writer@1,
Group_used,
Group_dict,
Group + 1
);
_ ->
emit_group_bitmaps(
Writer,
Group_used,
Group_dict,
Group + 1
)
end
end.
-file("src/packkit/bzip2.gleam", 1720).
-spec emit_symbol_map(writer(), list(integer())) -> writer().
emit_symbol_map(Writer, Unique) ->
Group_dict = bytes_to_group_dict(Unique, maps:new()),
Group_used = compute_group_used(Group_dict, 0, maps:new()),
Writer@1 = emit_group_high(Writer, Group_used, 0),
emit_group_bitmaps(Writer@1, Group_used, Group_dict, 0).
-file("src/packkit/bzip2.gleam", 1831).
-spec emit_selectors_loop(writer(), integer()) -> writer().
emit_selectors_loop(Writer, Remaining) ->
case Remaining of
0 ->
Writer;
_ ->
emit_selectors_loop(write_bits_msb(Writer, 0, 1), Remaining - 1)
end.
-file("src/packkit/bzip2.gleam", 1825).
-spec emit_selectors(writer(), integer()) -> writer().
emit_selectors(Writer, Num_groups) ->
emit_selectors_loop(Writer, Num_groups).
-file("src/packkit/bzip2.gleam", 1883).
-spec emit_length_diff(writer(), integer(), integer()) -> writer().
emit_length_diff(Writer, Previous, Target) ->
case Target =:= Previous of
true ->
write_bits_msb(Writer, 0, 1);
false ->
case Target > Previous of
true ->
Writer@1 = write_bits_msb(Writer, 1, 1),
Writer@2 = write_bits_msb(Writer@1, 0, 1),
emit_length_diff(Writer@2, Previous + 1, Target);
false ->
Writer@3 = write_bits_msb(Writer, 1, 1),
Writer@4 = write_bits_msb(Writer@3, 1, 1),
emit_length_diff(Writer@4, Previous - 1, Target)
end
end.
-file("src/packkit/bzip2.gleam", 1863).
-spec emit_lengths_diff(
writer(),
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
integer()
) -> writer().
emit_lengths_diff(Writer, Lengths, Symbol, Alphabet_size, Previous) ->
case Symbol >= Alphabet_size of
true ->
Writer;
false ->
Target = case gleam_stdlib:map_get(Lengths, Symbol) of
{ok, V} ->
V;
{error, _} ->
Previous
end,
Writer@1 = emit_length_diff(Writer, Previous, Target),
emit_lengths_diff(
Writer@1,
Lengths,
Symbol + 1,
Alphabet_size,
Target
)
end.
-file("src/packkit/bzip2.gleam", 1847).
-spec emit_table_lengths(
writer(),
gleam@dict:dict(integer(), integer()),
integer()
) -> writer().
emit_table_lengths(Writer, Lengths, Alphabet_size) ->
First = case gleam_stdlib:map_get(Lengths, 0) of
{ok, V} ->
V;
{error, _} ->
1
end,
Writer@1 = write_bits_msb(Writer, First, 5),
emit_lengths_diff(Writer@1, Lengths, 0, Alphabet_size, First).
-file("src/packkit/bzip2.gleam", 1838).
-spec emit_two_tables(
writer(),
gleam@dict:dict(integer(), integer()),
integer()
) -> writer().
emit_two_tables(Writer, Lengths, Alphabet_size) ->
Writer@1 = emit_table_lengths(Writer, Lengths, Alphabet_size),
emit_table_lengths(Writer@1, Lengths, Alphabet_size).
-file("src/packkit/bzip2.gleam", 1902).
-spec emit_huffman_data(
writer(),
list(integer()),
gleam@dict:dict(integer(), integer()),
gleam@dict:dict(integer(), integer())
) -> writer().
emit_huffman_data(Writer, Symbols, Lengths, Codes) ->
case Symbols of
[] ->
Writer;
[Head | Rest] ->
Code = case gleam_stdlib:map_get(Codes, Head) of
{ok, V} ->
V;
{error, _} ->
0
end,
Length = case gleam_stdlib:map_get(Lengths, Head) of
{ok, V@1} ->
V@1;
{error, _} ->
1
end,
emit_huffman_data(
write_bits_msb(Writer, Code, Length),
Rest,
Lengths,
Codes
)
end.
-file("src/packkit/bzip2.gleam", 1989).
-spec list_to_bit_array(list(integer()), bitstring()) -> bitstring().
list_to_bit_array(Values, Acc) ->
case Values of
[] ->
Acc;
[Head | Rest] ->
list_to_bit_array(Rest, <<Acc/bitstring, Head>>)
end.
-file("src/packkit/bzip2.gleam", 1976).
-spec flush_writer_msb(writer()) -> bitstring().
flush_writer_msb(Writer) ->
Writer@1 = case erlang:element(4, Writer) of
0 ->
Writer;
_ ->
Pad = 8 - erlang:element(4, Writer),
Shifted = erlang:'bsl'(erlang:element(3, Writer), Pad),
Byte = erlang:'band'(Shifted, 16#FF),
{writer, [Byte | erlang:element(2, Writer)], 0, 0}
end,
list_to_bit_array(lists:reverse(erlang:element(2, Writer@1)), <<>>).
-file("src/packkit/bzip2.gleam", 79).
-spec write_eos_marker(writer(), integer()) -> writer().
write_eos_marker(Writer, Stream_crc) ->
_pipe = Writer,
_pipe@1 = write_bits_msb(_pipe, 16#177245, 24),
_pipe@2 = write_bits_msb(_pipe@1, 16#385090, 24),
write_bits_msb(_pipe@2, Stream_crc, 32).
-file("src/packkit/bzip2.gleam", 471).
-spec read_table_lengths(reader(), integer(), integer(), list(integer())) -> {ok,
{list(integer()), reader()}} |
{error, packkit@error:codec_error()}.
read_table_lengths(Reader, Remaining, Current, Acc) ->
case Remaining of
0 ->
{ok, {lists:reverse(Acc), Reader}};
_ ->
gleam@result:'try'(
adjust_length(Reader, Current),
fun(_use0) ->
{Current@1, Reader@1} = _use0,
gleam@bool:guard(
(Current@1 < 1) orelse (Current@1 > 20),
{error,
{codec_invalid_data,
<<"bzip2 huffman length out of range"/utf8>>}},
fun() ->
read_table_lengths(
Reader@1,
Remaining - 1,
Current@1,
[Current@1 | Acc]
)
end
)
end
)
end.
-file("src/packkit/bzip2.gleam", 510).
-spec build_huffman_table(list(integer())) -> {ok, huffman_table()} |
{error, packkit@error:codec_error()}.
build_huffman_table(Lengths) ->
Pairs = enumerate_lengths(Lengths, 0, []),
Min_length = find_min_length(Lengths, 21),
Max_length = find_max_length(Lengths, 0),
gleam@bool:guard(
((Min_length < 1) orelse (Max_length > 20)) orelse (Max_length < 1),
{error,
{codec_invalid_data, <<"bzip2 huffman length out of range"/utf8>>}},
fun() ->
Sorted = sort_pairs_by_length(Pairs, Min_length, Max_length, []),
Acc = {build_acc, maps:new(), maps:new(), maps:new(), 0, 0, 0},
Acc@1 = canonical_loop(Sorted, Acc),
Final_limit = case erlang:element(7, Acc@1) of
0 ->
erlang:element(3, Acc@1);
_ ->
gleam@dict:insert(
erlang:element(3, Acc@1),
erlang:element(7, Acc@1),
erlang:element(6, Acc@1) - 1
)
end,
{ok,
{huffman_table,
Min_length,
Max_length,
erlang:element(2, Acc@1),
Final_limit,
erlang:element(4, Acc@1)}}
end
).
-file("src/packkit/bzip2.gleam", 449).
-spec read_huffman_tables_loop(
reader(),
integer(),
integer(),
list(huffman_table())
) -> {ok, {list(huffman_table()), reader()}} |
{error, packkit@error:codec_error()}.
read_huffman_tables_loop(Reader, Remaining, Alphabet_size, Acc) ->
case Remaining of
0 ->
{ok, {lists:reverse(Acc), Reader}};
_ ->
gleam@result:'try'(
read_bits(Reader, 5),
fun(_use0) ->
{Initial, Reader@1} = _use0,
gleam@result:'try'(
read_table_lengths(Reader@1, Alphabet_size, Initial, []),
fun(_use0@1) ->
{Lengths, Reader@2} = _use0@1,
gleam@result:'try'(
build_huffman_table(Lengths),
fun(Table) ->
read_huffman_tables_loop(
Reader@2,
Remaining - 1,
Alphabet_size,
[Table | Acc]
)
end
)
end
)
end
)
end.
-file("src/packkit/bzip2.gleam", 441).
-spec read_huffman_tables(reader(), integer(), integer()) -> {ok,
{list(huffman_table()), reader()}} |
{error, packkit@error:codec_error()}.
read_huffman_tables(Reader, Count, Alphabet_size) ->
read_huffman_tables_loop(Reader, Count, Alphabet_size, []).
-file("src/packkit/bzip2.gleam", 768).
-spec decode_huffman_step(
reader(),
gleam@dict:dict(integer(), huffman_table()),
gleam@dict:dict(integer(), integer()),
integer(),
list(integer()),
integer(),
integer(),
integer(),
list(integer()),
integer(),
packkit@limit:limits()
) -> {ok, huffman_stream_step()} | {error, packkit@error:codec_error()}.
decode_huffman_step(
Reader,
Tables,
Selectors,
Eob,
Mtf,
Symbol_count,
Pending_run,
Run_weight,
Out_rev,
Out_len,
Limits
) ->
Group_index = case 50 of
0 -> 0;
Gleam@denominator -> Symbol_count div Gleam@denominator
end,
Selector = case gleam_stdlib:map_get(Selectors, Group_index) of
{ok, V} ->
V;
{error, _} ->
0
end,
Table = case gleam_stdlib:map_get(Tables, Selector) of
{ok, V@1} ->
V@1;
{error, _} ->
{huffman_table, 1, 1, maps:new(), maps:new(), maps:new()}
end,
gleam@result:'try'(
decode_one_symbol(Reader, Table),
fun(_use0) ->
{Symbol, Reader@1} = _use0,
case Symbol of
S when S =:= Eob ->
gleam@result:'try'(
flush_run(Out_rev, Out_len, Mtf, Pending_run, Limits),
fun(_use0@1) ->
{Out_rev@1, Out_len@1} = _use0@1,
{ok,
{huffman_stream_done,
Out_rev@1,
Out_len@1,
Reader@1}}
end
);
0 ->
{ok,
{huffman_stream_continue,
Reader@1,
Mtf,
Symbol_count + 1,
Pending_run + Run_weight,
Run_weight * 2,
Out_rev,
Out_len}};
1 ->
{ok,
{huffman_stream_continue,
Reader@1,
Mtf,
Symbol_count + 1,
Pending_run + (2 * Run_weight),
Run_weight * 2,
Out_rev,
Out_len}};
Other ->
gleam@result:'try'(
flush_run(Out_rev, Out_len, Mtf, Pending_run, Limits),
fun(_use0@2) ->
{Out_rev@2, Out_len@2} = _use0@2,
Mtf_index = Other - 1,
gleam@result:'try'(
mtf_pick_byte(Mtf, Mtf_index),
fun(_use0@3) ->
{Byte, New_mtf} = _use0@3,
gleam@result:'try'(
emit_byte(
Out_rev@2,
Out_len@2,
Byte,
Limits
),
fun(_use0@4) ->
{Out_rev@3, Out_len@3} = _use0@4,
{ok,
{huffman_stream_continue,
Reader@1,
New_mtf,
Symbol_count + 1,
0,
1,
Out_rev@3,
Out_len@3}}
end
)
end
)
end
)
end
end
).
-file("src/packkit/bzip2.gleam", 693).
-spec decode_huffman_stream(
reader(),
gleam@dict:dict(integer(), huffman_table()),
gleam@dict:dict(integer(), integer()),
integer(),
list(integer()),
integer(),
integer(),
integer(),
integer(),
list(integer()),
integer(),
packkit@limit:limits()
) -> {ok, {gleam@dict:dict(integer(), integer()), integer(), reader()}} |
{error, packkit@error:codec_error()}.
decode_huffman_stream(
Reader,
Tables,
Selectors,
Eob,
Mtf,
Num_syms,
Symbol_count,
Pending_run,
Run_weight,
Out_rev,
Out_len,
Limits
) ->
case decode_huffman_step(
Reader,
Tables,
Selectors,
Eob,
Mtf,
Symbol_count,
Pending_run,
Run_weight,
Out_rev,
Out_len,
Limits
) of
{error, Err} ->
{error, Err};
{ok, {huffman_stream_done, Final_out_rev, Final_out_len, Final_reader}} ->
_ = Num_syms,
L_string = list_to_indexed_dict(
lists:reverse(Final_out_rev),
0,
maps:new()
),
{ok, {L_string, Final_out_len, Final_reader}};
{ok,
{huffman_stream_continue,
Next_reader,
Next_mtf,
Next_symbol_count,
Next_pending_run,
Next_run_weight,
Next_out_rev,
Next_out_len}} ->
decode_huffman_stream(
Next_reader,
Tables,
Selectors,
Eob,
Next_mtf,
Num_syms,
Next_symbol_count,
Next_pending_run,
Next_run_weight,
Next_out_rev,
Next_out_len,
Limits
)
end.
-file("src/packkit/bzip2.gleam", 238).
-spec decode_block(reader(), packkit@limit:limits()) -> {ok,
{bitstring(), integer(), reader()}} |
{error, packkit@error:codec_error()}.
decode_block(Reader, Limits) ->
gleam@result:'try'(
read_bits(Reader, 32),
fun(_use0) ->
{Crc, Reader@1} = _use0,
gleam@result:'try'(
read_bits(Reader@1, 1),
fun(_use0@1) ->
{Randomized, Reader@2} = _use0@1,
gleam@bool:guard(
Randomized /= 0,
{error,
{codec_invalid_data,
<<"randomized bzip2 blocks are not supported"/utf8>>}},
fun() ->
gleam@result:'try'(
read_bits(Reader@2, 24),
fun(_use0@2) ->
{Orig_ptr, Reader@3} = _use0@2,
gleam@result:'try'(
read_symbol_map(Reader@3),
fun(_use0@3) ->
{Symbols, Reader@4} = _use0@3,
Num_syms = erlang:length(Symbols),
gleam@bool:guard(
Num_syms =:= 0,
{error,
{codec_invalid_data,
<<"empty bzip2 symbol map"/utf8>>}},
fun() ->
Alphabet_size = Num_syms + 2,
gleam@result:'try'(
read_bits(Reader@4, 3),
fun(_use0@4) ->
{Num_tables,
Reader@5} = _use0@4,
gleam@bool:guard(
(Num_tables < 2)
orelse (Num_tables
> 6),
{error,
{codec_invalid_data,
<<"invalid bzip2 huffman table count"/utf8>>}},
fun() ->
gleam@result:'try'(
read_bits(
Reader@5,
15
),
fun(
_use0@5
) ->
{Num_selectors,
Reader@6} = _use0@5,
gleam@bool:guard(
(Num_selectors
=< 0)
orelse (Num_selectors
> 18002),
{error,
{codec_invalid_data,
<<"invalid bzip2 selector count"/utf8>>}},
fun(
) ->
gleam@result:'try'(
read_selectors(
Reader@6,
Num_selectors,
Num_tables
),
fun(
_use0@6
) ->
{Selectors,
Reader@7} = _use0@6,
gleam@result:'try'(
read_huffman_tables(
Reader@7,
Num_tables,
Alphabet_size
),
fun(
_use0@7
) ->
{Tables,
Reader@8} = _use0@7,
Tables_arr = list_to_dict(
Tables,
0,
maps:new(
)
),
Selectors_arr = list_to_dict(
Selectors,
0,
maps:new(
)
),
Eob = Alphabet_size
- 1,
gleam@result:'try'(
decode_huffman_stream(
Reader@8,
Tables_arr,
Selectors_arr,
Eob,
Symbols,
Num_syms,
0,
0,
1,
[],
0,
Limits
),
fun(
_use0@8
) ->
{L_string,
L_len,
Reader@9} = _use0@8,
gleam@bool:guard(
Orig_ptr
>= L_len,
{error,
{codec_invalid_data,
<<"bzip2 BWT origin out of range"/utf8>>}},
fun(
) ->
Bwt_dict = inverse_bwt(
L_string,
L_len,
Orig_ptr
),
Plain = rle1_decode(
Bwt_dict,
L_len
),
{ok,
{Plain,
Crc,
Reader@9}}
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/packkit/bzip2.gleam", 155).
-spec decode_blocks_with_remainder(
reader(),
bitstring(),
integer(),
packkit@limit:limits()
) -> {ok, {bitstring(), bitstring()}} | {error, packkit@error:codec_error()}.
decode_blocks_with_remainder(Reader, Output, Combined_crc, Limits) ->
gleam@result:'try'(
read_bits(Reader, 24),
fun(_use0) ->
{Magic_high, Reader@1} = _use0,
gleam@result:'try'(
read_bits(Reader@1, 24),
fun(_use0@1) ->
{Magic_low, Reader@2} = _use0@1,
case {Magic_high, Magic_low} of
{H, L} when (H =:= 16#314159) andalso (L =:= 16#265359) ->
gleam@result:'try'(
decode_block(Reader@2, Limits),
fun(_use0@2) ->
{Block_bytes, Block_crc, Reader@3} = _use0@2,
gleam@result:'try'(
verify_block_crc(Block_bytes, Block_crc),
fun(_) ->
Combined_crc@1 = combine_block_crc(
Combined_crc,
Block_crc
),
gleam@result:'try'(
append_with_limit(
Output,
Block_bytes,
Limits
),
fun(New_output) ->
decode_blocks_with_remainder(
Reader@3,
New_output,
Combined_crc@1,
Limits
)
end
)
end
)
end
);
{H@1, L@1} when (H@1 =:= 16#177245) andalso (L@1 =:= 16#385090) ->
gleam@result:'try'(
read_bits(Reader@2, 32),
fun(_use0@3) ->
{Stream_crc, Reader@4} = _use0@3,
case Stream_crc =:= Combined_crc of
true ->
{ok,
{Output,
byte_aligned_remainder(
Reader@4
)}};
false ->
{error,
{codec_invalid_data,
<<"bzip2 stream CRC mismatch"/utf8>>}}
end
end
);
{_, _} ->
{error,
{codec_invalid_data,
<<"unexpected bzip2 block marker"/utf8>>}}
end
end
)
end
).
-file("src/packkit/bzip2.gleam", 113).
-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(Rest) ->
gleam@result:'try'(
decode_blocks_with_remainder(new_reader(Rest), <<>>, 0, Limits),
fun(_use0) ->
{Payload, After} = _use0,
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]
),
case erlang:byte_size(After) of
0 ->
{ok, Acc@1};
_ ->
decode_streams_loop(
After,
Acc@1,
Next_size,
Limits
)
end
end
end
)
end
).
-file("src/packkit/bzip2.gleam", 98).
?DOC(
" Decode a bzip2 stream using explicit `Limits`.\n"
"\n"
" Handles multi-stream `.bz2` files (the `bzcat`-style concatenation\n"
" of independent bzip2 streams). When the end-of-stream marker for\n"
" one stream is reached, the decoder aligns to the next byte\n"
" boundary and looks for another `\"BZh\"` magic; if present, the\n"
" next stream's payload is appended to the accumulated output.\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/bzip2.gleam", 87).
?DOC(" Decode a bzip2 stream using the shared default `Limits`.\n").
-spec decode(bitstring()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
decode(Bytes) ->
decode_with_limits(Bytes, packkit@limit:default()).
-file("src/packkit/bzip2.gleam", 1238).
-spec encode_block(writer(), bitstring()) -> writer().
encode_block(Writer, Bytes) ->
Block_crc = packkit@checksum:bzip2_crc32(Bytes),
Rle1 = rle1_encode(Bytes),
N = erlang:length(Rle1),
{L_string, Orig_ptr} = bwt_forward(Rle1, N),
Unique = sorted_unique_bytes(L_string),
Mtf_indices = mtf_forward(L_string, Unique),
Symbols = rle2_encode(Mtf_indices, erlang:length(Unique) + 1),
Alphabet_size = erlang:length(Unique) + 2,
Lengths = build_huffman_lengths(Symbols, Alphabet_size),
Codes = canonical_codes(Lengths),
Num_groups = case erlang:length(Symbols) of
0 ->
1;
Sn ->
case 50 of
0 -> 0;
Gleam@denominator -> ((Sn + 50) - 1) div Gleam@denominator
end
end,
Writer@1 = begin
_pipe = Writer,
_pipe@1 = write_bits_msb(_pipe, 16#314159, 24),
_pipe@2 = write_bits_msb(_pipe@1, 16#265359, 24),
_pipe@3 = write_bits_msb(_pipe@2, Block_crc, 32),
_pipe@4 = write_bits_msb(_pipe@3, 0, 1),
write_bits_msb(_pipe@4, Orig_ptr, 24)
end,
Writer@2 = emit_symbol_map(Writer@1, Unique),
Writer@3 = write_bits_msb(Writer@2, 2, 3),
Writer@4 = write_bits_msb(Writer@3, Num_groups, 15),
Writer@5 = emit_selectors(Writer@4, Num_groups),
Writer@6 = emit_two_tables(Writer@5, Lengths, Alphabet_size),
emit_huffman_data(Writer@6, Symbols, Lengths, Codes).
-file("src/packkit/bzip2.gleam", 55).
?DOC(
" Encode `bytes` as a bzip2 stream with an explicit block-size\n"
" level (1..9). The level only affects the stream header byte; the\n"
" encoder always emits a single block so the level mostly carries\n"
" over for round-trip tooling.\n"
).
-spec encode_with_level(bitstring(), integer()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
encode_with_level(Bytes, Level) ->
gleam@bool:guard(
(Level < 1) orelse (Level > 9),
{error, {codec_invalid_data, <<"bzip2 level must be in 1..9"/utf8>>}},
fun() ->
Level_byte = 16#30 + Level,
Header = <<16#42, 16#5A, 16#68, Level_byte>>,
case erlang:byte_size(Bytes) of
0 ->
Writer = write_eos_marker(new_writer(), 0),
{ok,
gleam_stdlib:bit_array_concat(
[Header, flush_writer_msb(Writer)]
)};
_ ->
Stream_crc = packkit@checksum:bzip2_crc32(Bytes),
Writer@1 = encode_block(new_writer(), Bytes),
Writer@2 = write_eos_marker(Writer@1, Stream_crc),
{ok,
gleam_stdlib:bit_array_concat(
[Header, flush_writer_msb(Writer@2)]
)}
end
end
).
-file("src/packkit/bzip2.gleam", 47).
?DOC(
" Encode `bytes` as a bzip2 stream using the default block size 9\n"
" (900 KiB). The encoder emits a single block plus the stream\n"
" trailer with the combined CRC; large inputs that exceed the\n"
" block size will still be packed into one block, so a naive\n"
" forward BWT may dominate the running time.\n"
).
-spec encode(bitstring()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
encode(Bytes) ->
encode_with_level(Bytes, 9).