-module(packkit@deflate).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit/deflate.gleam").
-export([codec/0, decode_with_limits/2, decode/1, decode_with_remainder/2, encode_stored_only/1, encode/1, encode_dynamic/1]).
-export_type([reader/0, tree/0, inflate_step/0, huffman_step/0, writer/0, token/0, huff_tree/0, c_l_op/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Pure Gleam DEFLATE (RFC 1951) encoder and decoder.\n"
"\n"
" The decoder handles all three RFC 1951 block types (stored, fixed\n"
" Huffman, dynamic Huffman) and enforces the `Limits` resource budget\n"
" while decoding. The encoder exposes three entry points:\n"
"\n"
" * `encode_stored_only` emits BTYPE=00 blocks for callers that want\n"
" to bypass the match-finder entirely.\n"
" * `encode` runs the greedy LZ77 match-finder (3-byte hash chain, 32\n"
" KiB sliding window) and emits a single fixed-Huffman block\n"
" (BTYPE=01).\n"
" * `encode_dynamic` reuses the same match-finder but builds\n"
" per-stream Huffman codes for the literal/length and distance\n"
" alphabets, plus the 19-symbol code-length alphabet, and emits a\n"
" single dynamic-Huffman block (BTYPE=10). It falls back to the\n"
" fixed-Huffman path when the natural Huffman tree would exceed the\n"
" RFC 1951 15-bit code-length cap.\n"
).
-type reader() :: {reader, integer(), integer(), bitstring(), boolean()}.
-type tree() :: {tree, list(integer()), list(integer()), integer()}.
-type inflate_step() :: {inflate_done, bitstring(), reader()} |
{inflate_continue, bitstring(), reader()}.
-type huffman_step() :: {huffman_done, bitstring(), reader()} |
{huffman_continue, bitstring(), reader()}.
-type writer() :: {writer, list(integer()), integer(), integer()}.
-type token() :: {tok_lit, integer()} | {tok_match, integer(), integer()}.
-type huff_tree() :: {h_leaf, integer()} | {h_node, huff_tree(), huff_tree()}.
-type c_l_op() :: {c_l_lit, integer()} |
{c_l_copy, integer()} |
{c_l_zero3, integer()} |
{c_l_zero11, integer()}.
-file("src/packkit/deflate.gleam", 45).
?DOC(" Raw deflate codec smart constructor.\n").
-spec codec() -> packkit@codec:codec().
codec() ->
packkit@codec:deflate().
-file("src/packkit/deflate.gleam", 119).
-spec bytes_from_low_int(integer(), integer(), bitstring()) -> bitstring().
bytes_from_low_int(Value, Count, Acc) ->
case Count of
0 ->
Acc;
_ ->
bytes_from_low_int(
erlang:'bsr'(Value, 8),
Count - 1,
<<Acc/bitstring, (erlang:'band'(Value, 16#FF))>>
)
end.
-file("src/packkit/deflate.gleam", 108).
-spec recover_remaining_bytes(reader()) -> bitstring().
recover_remaining_bytes(Reader) ->
Whole_bytes = erlang:element(3, Reader) div 8,
Partial = erlang:element(3, Reader) - (Whole_bytes * 8),
Buffer_after_partial = erlang:'bsr'(erlang:element(2, Reader), Partial),
Prefix = bytes_from_low_int(Buffer_after_partial, Whole_bytes, <<>>),
gleam_stdlib:bit_array_concat([Prefix, erlang:element(4, Reader)]).
-file("src/packkit/deflate.gleam", 175).
-spec refill(reader(), integer()) -> reader().
refill(Reader, Needed) ->
case (erlang:element(3, Reader) >= Needed) orelse erlang:element(5, Reader) of
true ->
Reader;
false ->
case erlang:element(4, Reader) of
<<B, Rest/binary>> ->
refill(
{reader,
erlang:'bor'(
erlang:element(2, Reader),
erlang:'bsl'(B, erlang:element(3, Reader))
),
erlang:element(3, Reader) + 8,
Rest,
false},
Needed
);
_ ->
{reader,
erlang:element(2, Reader),
erlang:element(3, Reader) + 8,
<<>>,
true}
end
end.
-file("src/packkit/deflate.gleam", 204).
-spec read_bits(reader(), integer()) -> {ok, {integer(), reader()}} |
{error, packkit@error:codec_error()}.
read_bits(Reader, Count) ->
R = refill(Reader, Count),
case erlang:element(5, R) andalso (erlang:element(3, R) < Count) of
true ->
{error,
{codec_invalid_data, <<"truncated deflate bit stream"/utf8>>}};
false ->
Mask = erlang:'bsl'(1, Count) - 1,
Value = erlang:'band'(erlang:element(2, R), Mask),
Next = {reader,
erlang:'bsr'(erlang:element(2, R), Count),
erlang:element(3, R) - Count,
erlang:element(4, R),
erlang:element(5, R)},
{ok, {Value, Next}}
end.
-file("src/packkit/deflate.gleam", 227).
-spec skip_to_byte_boundary(reader()) -> reader().
skip_to_byte_boundary(Reader) ->
{reader, 0, 0, erlang:element(4, Reader), erlang:element(5, Reader)}.
-file("src/packkit/deflate.gleam", 260).
-spec single_symbol(list(integer())) -> integer().
single_symbol(Symbols) ->
case Symbols of
[Head | _] ->
Head;
[] ->
0
end.
-file("src/packkit/deflate.gleam", 267).
-spec find_max_symbol(list(integer()), integer(), integer(), integer()) -> integer().
find_max_symbol(Lengths, Index, Best, _) ->
case Lengths of
[] ->
Best;
[0 | Rest] ->
find_max_symbol(Rest, Index + 1, Best, 0);
[_ | Rest@1] ->
find_max_symbol(Rest@1, Index + 1, Index, 0)
end.
-file("src/packkit/deflate.gleam", 284).
-spec empty_counts() -> list(integer()).
empty_counts() ->
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
-file("src/packkit/deflate.gleam", 360).
-spec enumerate_lengths(
list(integer()),
integer(),
list({integer(), integer()})
) -> list({integer(), integer()}).
enumerate_lengths(Lengths, Index, Acc) ->
case Lengths of
[] ->
lists:reverse(Acc);
[Length | Rest] ->
enumerate_lengths(Rest, Index + 1, [{Index, Length} | Acc])
end.
-file("src/packkit/deflate.gleam", 392).
-spec prepend_reversed(list(integer()), list(integer())) -> list(integer()).
prepend_reversed(Values, Acc) ->
case Values of
[] ->
Acc;
[Head | Rest] ->
prepend_reversed(Rest, [Head | Acc])
end.
-file("src/packkit/deflate.gleam", 505).
-spec read_raw_bytes(reader(), integer()) -> {ok, {bitstring(), reader()}} |
{error, packkit@error:codec_error()}.
read_raw_bytes(Reader, Length) ->
case Length of
0 ->
{ok, {<<>>, Reader}};
_ ->
case erlang:element(4, Reader) of
<<Chunk:Length/binary, Rest/binary>> ->
{ok, {Chunk, {reader, 0, 0, Rest, false}}};
_ ->
{error,
{codec_invalid_data, <<"truncated stored block"/utf8>>}}
end
end.
-file("src/packkit/deflate.gleam", 580).
-spec empty_clcl_table() -> list(integer()).
empty_clcl_table() ->
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].
-file("src/packkit/deflate.gleam", 584).
-spec code_length_order() -> list(integer()).
code_length_order() ->
[16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15].
-file("src/packkit/deflate.gleam", 705).
-spec repeat_value(integer(), integer(), list(integer())) -> list(integer()).
repeat_value(Value, Count, Acc) ->
case Count of
0 ->
Acc;
_ ->
repeat_value(Value, Count - 1, [Value | Acc])
end.
-file("src/packkit/deflate.gleam", 712).
-spec pad_to_length(list(integer()), integer()) -> list(integer()).
pad_to_length(Values, Target) ->
Current = erlang:length(Values),
case Current >= Target of
true ->
Values;
false ->
lists:append(Values, repeat_value(0, Target - Current, []))
end.
-file("src/packkit/deflate.gleam", 842).
-spec append_with_limit(bitstring(), bitstring(), packkit@limit:limits()) -> {ok,
bitstring()} |
{error, packkit@error:codec_error()}.
append_with_limit(Output, Chunk, Limits) ->
Projected = erlang:byte_size(Output) + erlang:byte_size(Chunk),
case Projected > packkit@limit:max_output_bytes(Limits) of
true ->
{error,
{codec_limit_exceeded, <<"max_output_bytes"/utf8>>, Projected}};
false ->
{ok, gleam_stdlib:bit_array_concat([Output, Chunk])}
end.
-file("src/packkit/deflate.gleam", 484).
-spec inflate_stored(reader(), bitstring(), packkit@limit:limits()) -> {ok,
{bitstring(), reader()}} |
{error, packkit@error:codec_error()}.
inflate_stored(Reader, Output, Limits) ->
Reader@1 = skip_to_byte_boundary(Reader),
gleam@result:'try'(
read_bits(Reader@1, 16),
fun(_use0) ->
{Length, Reader@2} = _use0,
gleam@result:'try'(
read_bits(Reader@2, 16),
fun(_use0@1) ->
{Invlength, Reader@3} = _use0@1,
gleam@bool:guard(
erlang:'band'(erlang:'bxor'(Length, Invlength), 16#FFFF)
/= 16#FFFF,
{error,
{codec_invalid_data,
<<"stored block length mismatch"/utf8>>}},
fun() ->
gleam@result:'try'(
read_raw_bytes(Reader@3, Length),
fun(_use0@2) ->
{Bytes, Reader@4} = _use0@2,
gleam@result:'try'(
append_with_limit(Output, Bytes, Limits),
fun(New_output) ->
{ok, {New_output, Reader@4}}
end
)
end
)
end
)
end
)
end
).
-file("src/packkit/deflate.gleam", 825).
-spec apply_backref_byte_by_byte(
bitstring(),
integer(),
integer(),
packkit@limit:limits()
) -> {ok, bitstring()} | {error, packkit@error:codec_error()}.
apply_backref_byte_by_byte(Output, Distance, Length, Limits) ->
case Length of
0 ->
{ok, Output};
_ ->
Size = erlang:byte_size(Output),
Byte_slice@1 = case gleam_stdlib:bit_array_slice(
Output,
Size - Distance,
1
) of
{ok, Byte_slice} -> Byte_slice;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/deflate"/utf8>>,
function => <<"apply_backref_byte_by_byte"/utf8>>,
line => 835,
value => _assert_fail,
start => 24846,
'end' => 24917,
pattern_start => 24857,
pattern_end => 24871})
end,
gleam@result:'try'(
append_with_limit(Output, Byte_slice@1, Limits),
fun(New_output) ->
apply_backref_byte_by_byte(
New_output,
Distance,
Length - 1,
Limits
)
end
)
end.
-file("src/packkit/deflate.gleam", 804).
-spec apply_backref(bitstring(), integer(), integer(), packkit@limit:limits()) -> {ok,
bitstring()} |
{error, packkit@error:codec_error()}.
apply_backref(Output, Distance, Length, Limits) ->
Size = erlang:byte_size(Output),
gleam@bool:guard(
(Distance =< 0) orelse (Distance > Size),
{error, {codec_invalid_data, <<"back-reference out of range"/utf8>>}},
fun() -> case Distance >= Length of
true ->
Chunk@1 = case gleam_stdlib:bit_array_slice(
Output,
Size - Distance,
Length
) of
{ok, Chunk} -> Chunk;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/deflate"/utf8>>,
function => <<"apply_backref"/utf8>>,
line => 818,
value => _assert_fail,
start => 24393,
'end' => 24464,
pattern_start => 24404,
pattern_end => 24413})
end,
append_with_limit(Output, Chunk@1, Limits);
false ->
apply_backref_byte_by_byte(Output, Distance, Length, Limits)
end end
).
-file("src/packkit/deflate.gleam", 874).
-spec build_fixed_literal_lengths() -> list(integer()).
build_fixed_literal_lengths() ->
lists:append(
[gleam@list:repeat(8, 144),
gleam@list:repeat(9, 112),
gleam@list:repeat(7, 24),
gleam@list:repeat(8, 8)]
).
-file("src/packkit/deflate.gleam", 889).
-spec length_extra_bits() -> list(integer()).
length_extra_bits() ->
[0,
0,
0,
0,
0,
0,
0,
0,
1,
1,
1,
1,
2,
2,
2,
2,
3,
3,
3,
3,
4,
4,
4,
4,
5,
5,
5,
5,
0].
-file("src/packkit/deflate.gleam", 923).
-spec length_base() -> list(integer()).
length_base() ->
[3,
4,
5,
6,
7,
8,
9,
10,
11,
13,
15,
17,
19,
23,
27,
31,
35,
43,
51,
59,
67,
83,
99,
115,
131,
163,
195,
227,
258].
-file("src/packkit/deflate.gleam", 957).
-spec distance_extra_bits() -> list(integer()).
distance_extra_bits() ->
[0,
0,
0,
0,
1,
1,
2,
2,
3,
3,
4,
4,
5,
5,
6,
6,
7,
7,
8,
8,
9,
9,
10,
10,
11,
11,
12,
12,
13,
13].
-file("src/packkit/deflate.gleam", 992).
-spec distance_base() -> list(integer()).
distance_base() ->
[1,
2,
3,
4,
5,
7,
9,
13,
17,
25,
33,
49,
65,
97,
129,
193,
257,
385,
513,
769,
1025,
1537,
2049,
3073,
4097,
6145,
8193,
12289,
16385,
24577].
-file("src/packkit/deflate.gleam", 1037).
-spec empty_stored_block() -> bitstring().
empty_stored_block() ->
<<1, 0, 0, 16#FF, 16#FF>>.
-file("src/packkit/deflate.gleam", 1100).
-spec build_byte_table(
bitstring(),
integer(),
gleam@dict:dict(integer(), integer())
) -> gleam@dict:dict(integer(), integer()).
build_byte_table(Bytes, Index, Acc) ->
case Bytes of
<<B, Rest/binary>> ->
build_byte_table(Rest, Index + 1, gleam@dict:insert(Acc, Index, B));
_ ->
Acc
end.
-file("src/packkit/deflate.gleam", 1112).
-spec byte_at(gleam@dict:dict(integer(), integer()), integer()) -> integer().
byte_at(Table, Index) ->
case gleam_stdlib:map_get(Table, Index) of
{ok, Value} ->
Value;
{error, _} ->
0
end.
-file("src/packkit/deflate.gleam", 1119).
-spec hash3(integer(), integer(), integer()) -> integer().
hash3(B0, B1, B2) ->
erlang:'band'(
erlang:'bxor'(
erlang:'bxor'(B0 * 2654435761, B1 * 40503),
B2 * 2246822519
),
16#FFFF
).
-file("src/packkit/deflate.gleam", 1209).
-spec match_length(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
integer(),
integer(),
integer()
) -> integer().
match_length(Table, Base, Cursor, Size, Max, Acc) ->
case (Acc >= Max) orelse ((Cursor + Acc) >= Size) of
true ->
Acc;
false ->
case byte_at(Table, Base + Acc) =:= byte_at(Table, Cursor + Acc) of
true ->
match_length(Table, Base, Cursor, Size, Max, Acc + 1);
false ->
Acc
end
end.
-file("src/packkit/deflate.gleam", 1264).
-spec length_code(integer()) -> {integer(), integer(), integer()}.
length_code(Length) ->
case Length of
N when (N >= 3) andalso (N =< 10) ->
{254 + N, 0, 0};
N@1 when (N@1 >= 11) andalso (N@1 =< 18) ->
{265 + ((N@1 - 11) div 2), 1, (N@1 - 11) rem 2};
N@2 when (N@2 >= 19) andalso (N@2 =< 34) ->
{269 + ((N@2 - 19) div 4), 2, (N@2 - 19) rem 4};
N@3 when (N@3 >= 35) andalso (N@3 =< 66) ->
{273 + ((N@3 - 35) div 8), 3, (N@3 - 35) rem 8};
N@4 when (N@4 >= 67) andalso (N@4 =< 130) ->
{277 + ((N@4 - 67) div 16), 4, (N@4 - 67) rem 16};
N@5 when (N@5 >= 131) andalso (N@5 =< 257) ->
{281 + ((N@5 - 131) div 32), 5, (N@5 - 131) rem 32};
_ ->
{285, 0, 0}
end.
-file("src/packkit/deflate.gleam", 1276).
-spec distance_code(integer()) -> {integer(), integer(), integer()}.
distance_code(Distance) ->
case Distance of
N when (N >= 1) andalso (N =< 4) ->
{N - 1, 0, 0};
N@1 when (N@1 >= 5) andalso (N@1 =< 8) ->
{4 + ((N@1 - 5) div 2), 1, (N@1 - 5) rem 2};
N@2 when (N@2 >= 9) andalso (N@2 =< 16) ->
{6 + ((N@2 - 9) div 4), 2, (N@2 - 9) rem 4};
N@3 when (N@3 >= 17) andalso (N@3 =< 32) ->
{8 + ((N@3 - 17) div 8), 3, (N@3 - 17) rem 8};
N@4 when (N@4 >= 33) andalso (N@4 =< 64) ->
{10 + ((N@4 - 33) div 16), 4, (N@4 - 33) rem 16};
N@5 when (N@5 >= 65) andalso (N@5 =< 128) ->
{12 + ((N@5 - 65) div 32), 5, (N@5 - 65) rem 32};
N@6 when (N@6 >= 129) andalso (N@6 =< 256) ->
{14 + ((N@6 - 129) div 64), 6, (N@6 - 129) rem 64};
N@7 when (N@7 >= 257) andalso (N@7 =< 512) ->
{16 + ((N@7 - 257) div 128), 7, (N@7 - 257) rem 128};
N@8 when (N@8 >= 513) andalso (N@8 =< 1024) ->
{18 + ((N@8 - 513) div 256), 8, (N@8 - 513) rem 256};
N@9 when (N@9 >= 1025) andalso (N@9 =< 2048) ->
{20 + ((N@9 - 1025) div 512), 9, (N@9 - 1025) rem 512};
N@10 when (N@10 >= 2049) andalso (N@10 =< 4096) ->
{22 + ((N@10 - 2049) div 1024), 10, (N@10 - 2049) rem 1024};
N@11 when (N@11 >= 4097) andalso (N@11 =< 8192) ->
{24 + ((N@11 - 4097) div 2048), 11, (N@11 - 4097) rem 2048};
N@12 when (N@12 >= 8193) andalso (N@12 =< 16384) ->
{26 + ((N@12 - 8193) div 4096), 12, (N@12 - 8193) rem 4096};
N@13 when (N@13 >= 16385) andalso (N@13 =< 32768) ->
{28 + ((N@13 - 16385) div 8192), 13, (N@13 - 16385) rem 8192};
_ ->
{0, 0, 0}
end.
-file("src/packkit/deflate.gleam", 1320).
-spec fixed_literal_code(integer()) -> {integer(), integer()}.
fixed_literal_code(Symbol) ->
case Symbol of
S when (S >= 0) andalso (S =< 143) ->
{48 + S, 8};
S@1 when (S@1 >= 144) andalso (S@1 =< 255) ->
{400 + (S@1 - 144), 9};
S@2 when (S@2 >= 256) andalso (S@2 =< 279) ->
{S@2 - 256, 7};
S@3 when (S@3 >= 280) andalso (S@3 =< 287) ->
{192 + (S@3 - 280), 8};
_ ->
{0, 8}
end.
-file("src/packkit/deflate.gleam", 1347).
-spec reverse_bits_loop(integer(), integer(), integer()) -> integer().
reverse_bits_loop(Value, Count, Acc) ->
case Count of
0 ->
Acc;
_ ->
reverse_bits_loop(
erlang:'bsr'(Value, 1),
Count - 1,
erlang:'bor'(erlang:'bsl'(Acc, 1), erlang:'band'(Value, 1))
)
end.
-file("src/packkit/deflate.gleam", 1343).
-spec reverse_bits(integer(), integer()) -> integer().
reverse_bits(Value, Count) ->
reverse_bits_loop(Value, Count, 0).
-file("src/packkit/deflate.gleam", 1368).
-spec new_writer() -> writer().
new_writer() ->
{writer, [], 0, 0}.
-file("src/packkit/deflate.gleam", 1393).
-spec mask_for(integer()) -> integer().
mask_for(Count) ->
erlang:'bsl'(1, Count) - 1.
-file("src/packkit/deflate.gleam", 1397).
-spec flush_full_bytes(writer()) -> writer().
flush_full_bytes(Writer) ->
case erlang:element(4, Writer) >= 8 of
false ->
Writer;
true ->
flush_full_bytes(
{writer,
[erlang:'band'(erlang:element(3, Writer), 16#FF) |
erlang:element(2, Writer)],
erlang:'bsr'(erlang:element(3, Writer), 8),
erlang:element(4, Writer) - 8}
)
end.
-file("src/packkit/deflate.gleam", 1372).
-spec write_bits(writer(), integer(), integer()) -> writer().
write_bits(Writer, Value, Count) ->
case Count of
0 ->
Writer;
_ ->
Buffer = erlang:'bor'(
erlang:element(3, Writer),
erlang:'bsl'(
erlang:'band'(Value, mask_for(Count)),
erlang:element(4, Writer)
)
),
flush_full_bytes(
{writer,
erlang:element(2, Writer),
Buffer,
erlang:element(4, Writer) + Count}
)
end.
-file("src/packkit/deflate.gleam", 1339).
-spec write_huffman_code(writer(), integer(), integer()) -> writer().
write_huffman_code(Writer, Code, Length) ->
write_bits(Writer, reverse_bits(Code, Length), Length).
-file("src/packkit/deflate.gleam", 1330).
-spec write_fixed_literal_code(writer(), integer()) -> writer().
write_fixed_literal_code(Writer, Symbol) ->
{Code, Length} = fixed_literal_code(Symbol),
write_huffman_code(Writer, Code, Length).
-file("src/packkit/deflate.gleam", 1335).
-spec write_fixed_distance_code(writer(), integer()) -> writer().
write_fixed_distance_code(Writer, Symbol) ->
write_huffman_code(Writer, Symbol, 5).
-file("src/packkit/deflate.gleam", 1254).
-spec write_match(writer(), integer(), integer()) -> writer().
write_match(Writer, Length, Distance) ->
{Length_sym, Length_extra_count, Length_extra_value} = length_code(Length),
Writer@1 = write_fixed_literal_code(Writer, Length_sym),
Writer@2 = write_bits(Writer@1, Length_extra_value, Length_extra_count),
{Dist_sym, Dist_extra_count, Dist_extra_value} = distance_code(Distance),
Writer@3 = write_fixed_distance_code(Writer@2, Dist_sym),
write_bits(Writer@3, Dist_extra_value, Dist_extra_count).
-file("src/packkit/deflate.gleam", 1422).
-spec list_to_bit_array(list(integer()), bitstring()) -> bitstring().
list_to_bit_array(Values, Acc) ->
case Values of
[] ->
Acc;
[Head | Rest] ->
list_to_bit_array(Rest, <<Acc/bitstring, Head>>)
end.
-file("src/packkit/deflate.gleam", 1409).
-spec flush_writer(writer()) -> bitstring().
flush_writer(Writer) ->
Writer@1 = case erlang:element(4, Writer) of
0 ->
Writer;
_ ->
{writer,
[erlang:'band'(erlang:element(3, Writer), 16#FF) |
erlang:element(2, Writer)],
0,
0}
end,
list_to_bit_array(lists:reverse(erlang:element(2, Writer@1)), <<>>).
-file("src/packkit/deflate.gleam", 1438).
-spec list_get_loop(list(integer()), integer(), integer()) -> integer().
list_get_loop(Values, Index, Default) ->
case {Values, Index} of
{[], _} ->
Default;
{[Head | _], 0} ->
Head;
{[_ | Rest], _} ->
list_get_loop(Rest, Index - 1, Default)
end.
-file("src/packkit/deflate.gleam", 1431).
-spec list_get(list(integer()), integer(), integer()) -> integer().
list_get(Values, Index, Default) ->
case Index < 0 of
true ->
Default;
false ->
list_get_loop(Values, Index, Default)
end.
-file("src/packkit/deflate.gleam", 1450).
-spec set_index_loop(list(integer()), integer(), integer(), list(integer())) -> list(integer()).
set_index_loop(Values, Index, Value, Acc) ->
case {Values, Index} of
{[], 0} ->
lists:reverse([Value | Acc]);
{[], N} ->
set_index_loop([], N - 1, Value, [0 | Acc]);
{[_ | Rest], 0} ->
_pipe = lists:reverse([Value | Acc]),
lists:append(_pipe, Rest);
{[Head | Rest@1], N@1} ->
set_index_loop(Rest@1, N@1 - 1, Value, [Head | Acc])
end.
-file("src/packkit/deflate.gleam", 1446).
-spec set_index(list(integer()), integer(), integer()) -> list(integer()).
set_index(Values, Index, Value) ->
set_index_loop(Values, Index, Value, []).
-file("src/packkit/deflate.gleam", 288).
-spec count_lengths_loop(list(integer()), list(integer())) -> list(integer()).
count_lengths_loop(Lengths, Counts) ->
case Lengths of
[] ->
Counts;
[0 | Rest] ->
count_lengths_loop(Rest, Counts);
[Length | Rest@1] ->
count_lengths_loop(
Rest@1,
set_index(Counts, Length, list_get(Counts, Length, 0) + 1)
)
end.
-file("src/packkit/deflate.gleam", 280).
-spec count_lengths(list(integer())) -> list(integer()).
count_lengths(Lengths) ->
count_lengths_loop(Lengths, empty_counts()).
-file("src/packkit/deflate.gleam", 588).
-spec read_code_lengths_loop(reader(), integer(), integer(), list(integer())) -> {ok,
{list(integer()), reader()}} |
{error, packkit@error:codec_error()}.
read_code_lengths_loop(Reader, Remaining, Index, Acc) ->
case Remaining of
0 ->
{ok, {Acc, Reader}};
_ ->
gleam@result:'try'(
read_bits(Reader, 3),
fun(_use0) ->
{Value, Reader@1} = _use0,
Slot = list_get(code_length_order(), Index, 0),
read_code_lengths_loop(
Reader@1,
Remaining - 1,
Index + 1,
set_index(Acc, Slot, Value)
)
end
)
end.
-file("src/packkit/deflate.gleam", 573).
-spec read_code_length_table(reader(), integer()) -> {ok,
{list(integer()), reader()}} |
{error, packkit@error:codec_error()}.
read_code_length_table(Reader, Count) ->
read_code_lengths_loop(Reader, Count, 0, empty_clcl_table()).
-file("src/packkit/deflate.gleam", 1505).
-spec bytes_from_table_unused(bitstring()) -> bitstring().
bytes_from_table_unused(Bytes) ->
Bytes.
-file("src/packkit/deflate.gleam", 1629).
-spec bump_index(list(integer()), integer()) -> list(integer()).
bump_index(Values, Index) ->
set_index(Values, Index, list_get(Values, Index, 0) + 1).
-file("src/packkit/deflate.gleam", 1608).
-spec token_freq_loop(list(token()), list(integer()), list(integer())) -> {list(integer()),
list(integer())}.
token_freq_loop(Tokens, Lit_freqs, Dist_freqs) ->
case Tokens of
[] ->
{Lit_freqs, Dist_freqs};
[{tok_lit, V} | Rest] ->
token_freq_loop(Rest, bump_index(Lit_freqs, V), Dist_freqs);
[{tok_match, L, D} | Rest@1] ->
{Lit_sym, _, _} = length_code(L),
{Dist_sym, _, _} = distance_code(D),
token_freq_loop(
Rest@1,
bump_index(Lit_freqs, Lit_sym),
bump_index(Dist_freqs, Dist_sym)
)
end.
-file("src/packkit/deflate.gleam", 1684).
-spec collect_nonzero(list(integer()), integer(), list({integer(), integer()})) -> list({integer(),
integer()}).
collect_nonzero(Freqs, Index, Acc) ->
case Freqs of
[] ->
lists:reverse(Acc);
[0 | Rest] ->
collect_nonzero(Rest, Index + 1, Acc);
[N | Rest@1] ->
collect_nonzero(Rest@1, Index + 1, [{Index, N} | Acc])
end.
-file("src/packkit/deflate.gleam", 1696).
-spec sort_nonzero_pairs(list({integer(), integer()})) -> list({integer(),
integer()}).
sort_nonzero_pairs(Pairs) ->
gleam@list:sort(
Pairs,
fun(A, B) ->
{Sym_a, Freq_a} = A,
{Sym_b, Freq_b} = B,
case gleam@int:compare(Freq_a, Freq_b) of
eq ->
gleam@int:compare(Sym_a, Sym_b);
Ord ->
Ord
end
end
).
-file("src/packkit/deflate.gleam", 1716).
-spec insert_sorted_node(
{integer(), huff_tree()},
list({integer(), huff_tree()})
) -> list({integer(), huff_tree()}).
insert_sorted_node(Item, Rest) ->
{W, _} = Item,
case Rest of
[] ->
[Item];
[Head | Tail] ->
{Hw, _} = Head,
case W =< Hw of
true ->
[Item | Rest];
false ->
[Head | insert_sorted_node(Item, Tail)]
end
end.
-file("src/packkit/deflate.gleam", 1707).
-spec combine_huffman(list({integer(), huff_tree()})) -> huff_tree().
combine_huffman(Nodes) ->
case Nodes of
[] ->
{h_leaf, 0};
[{_, Tree}] ->
Tree;
[{W1, T1}, {W2, T2} | Rest] ->
combine_huffman(
insert_sorted_node({W1 + W2, {h_node, T1, T2}}, Rest)
)
end.
-file("src/packkit/deflate.gleam", 1733).
-spec collect_huffman_lengths(
huff_tree(),
integer(),
list({integer(), integer()})
) -> list({integer(), integer()}).
collect_huffman_lengths(Tree, Depth, Acc) ->
case Tree of
{h_leaf, Sym} ->
[{Sym, Depth} | Acc];
{h_node, Left, Right} ->
collect_huffman_lengths(
Right,
Depth + 1,
collect_huffman_lengths(Left, Depth + 1, Acc)
)
end.
-file("src/packkit/deflate.gleam", 1749).
-spec list_max(list({integer(), integer()}), integer()) -> integer().
list_max(Pairs, Best) ->
case Pairs of
[] ->
Best;
[{_, N} | Rest] ->
case N > Best of
true ->
list_max(Rest, N);
false ->
list_max(Rest, Best)
end
end.
-file("src/packkit/deflate.gleam", 1765).
-spec make_length_vector_loop(
integer(),
integer(),
gleam@dict:dict(integer(), integer()),
list(integer())
) -> list(integer()).
make_length_vector_loop(Size, Index, Lookup, Acc) ->
case Index >= Size of
true ->
lists:reverse(Acc);
false ->
Value = case gleam_stdlib:map_get(Lookup, Index) of
{ok, V} ->
V;
{error, _} ->
0
end,
make_length_vector_loop(Size, Index + 1, Lookup, [Value | Acc])
end.
-file("src/packkit/deflate.gleam", 1760).
-spec make_length_vector(integer(), list({integer(), integer()})) -> list(integer()).
make_length_vector(Alphabet_size, Pairs) ->
Lookup = maps:from_list(Pairs),
make_length_vector_loop(Alphabet_size, 0, Lookup, []).
-file("src/packkit/deflate.gleam", 1650).
?DOC(
" Build code lengths for an alphabet of `alphabet_size` symbols given\n"
" their frequencies (zero-frequency symbols get length 0). Returns\n"
" `Error(Nil)` when the natural Huffman tree would exceed `max_len`\n"
" bits so the caller can fall back to a different block strategy.\n"
).
-spec huffman_code_lengths(list(integer()), integer(), integer()) -> {ok,
list(integer())} |
{error, nil}.
huffman_code_lengths(Freqs, Alphabet_size, Max_len) ->
Nonzero = collect_nonzero(Freqs, 0, []),
case Nonzero of
[] ->
{ok, gleam@list:repeat(0, Alphabet_size)};
[{Sym, _}] ->
{ok, make_length_vector(Alphabet_size, [{Sym, 1}])};
_ ->
Sorted_initial = sort_nonzero_pairs(Nonzero),
Leaves = gleam@list:map(
Sorted_initial,
fun(Pair) ->
{Sym@1, Freq} = Pair,
{Freq, {h_leaf, Sym@1}}
end
),
Tree = combine_huffman(Leaves),
Lengths = collect_huffman_lengths(Tree, 0, []),
case list_max(Lengths, 0) > Max_len of
true ->
{error, nil};
false ->
{ok, make_length_vector(Alphabet_size, Lengths)}
end
end.
-file("src/packkit/deflate.gleam", 1809).
-spec assign_canonical(
list({integer(), integer()}),
integer(),
integer(),
gleam@dict:dict(integer(), {integer(), integer()})
) -> gleam@dict:dict(integer(), {integer(), integer()}).
assign_canonical(Remaining, Code, Prev_len, Acc) ->
case Remaining of
[] ->
Acc;
[{Sym, Len} | Rest] ->
Shifted = erlang:'bsl'(Code, Len - Prev_len),
assign_canonical(
Rest,
Shifted + 1,
Len,
gleam@dict:insert(Acc, Sym, {Shifted, Len})
)
end.
-file("src/packkit/deflate.gleam", 1788).
?DOC(
" Map symbol → `(code_value, code_length)` for every symbol with\n"
" non-zero length, using the canonical-Huffman recurrence. Symbols\n"
" with length 0 are omitted from the result.\n"
).
-spec canonical_codes_from_lengths(list(integer())) -> gleam@dict:dict(integer(), {integer(),
integer()}).
canonical_codes_from_lengths(Lengths) ->
Pairs = enumerate_lengths(Lengths, 0, []),
Active = gleam@list:filter(
Pairs,
fun(P) ->
{_, Len} = P,
Len > 0
end
),
Sorted = gleam@list:sort(
Active,
fun(A, B) ->
{Sa, La} = A,
{Sb, Lb} = B,
case gleam@int:compare(La, Lb) of
eq ->
gleam@int:compare(Sa, Sb);
Ord ->
Ord
end
end
),
assign_canonical(Sorted, 0, 0, maps:new()).
-file("src/packkit/deflate.gleam", 1873).
-spec flush_zero_run(integer(), list(c_l_op())) -> list(c_l_op()).
flush_zero_run(Count, Acc) ->
case Count of
0 ->
Acc;
1 ->
[{c_l_lit, 0} | Acc];
2 ->
[{c_l_lit, 0}, {c_l_lit, 0} | Acc];
N when N =< 10 ->
[{c_l_zero3, N - 3} | Acc];
N@1 when N@1 =< 138 ->
[{c_l_zero11, N@1 - 11} | Acc];
N@2 ->
flush_zero_run(N@2 - 138, [{c_l_zero11, 127} | Acc])
end.
-file("src/packkit/deflate.gleam", 1901).
-spec emit_nonzero_run(integer(), integer(), list(c_l_op())) -> list(c_l_op()).
emit_nonzero_run(Value, Remaining, Acc) ->
case Remaining of
N when N =< 0 ->
Acc;
1 ->
[{c_l_lit, Value} | Acc];
2 ->
[{c_l_lit, Value}, {c_l_lit, Value} | Acc];
N@1 when N@1 =< 6 ->
[{c_l_copy, N@1 - 3} | Acc];
N@2 ->
emit_nonzero_run(Value, N@2 - 6, [{c_l_copy, 3} | Acc])
end.
-file("src/packkit/deflate.gleam", 1887).
-spec flush_nonzero_run(integer(), integer(), list(c_l_op())) -> list(c_l_op()).
flush_nonzero_run(Value, Count, Acc) ->
case Count of
0 ->
Acc;
_ ->
emit_nonzero_run(Value, Count - 1, [{c_l_lit, Value} | Acc])
end.
-file("src/packkit/deflate.gleam", 1862).
-spec flush_run(integer(), integer(), list(c_l_op())) -> list(c_l_op()).
flush_run(Value, Count, Acc) ->
case Count of
0 ->
Acc;
_ ->
case Value of
0 ->
flush_zero_run(Count, Acc);
_ ->
flush_nonzero_run(Value, Count, Acc)
end
end.
-file("src/packkit/deflate.gleam", 1843).
-spec rle_loop(list(integer()), integer(), integer(), list(c_l_op())) -> list(c_l_op()).
rle_loop(Remaining, Current, Count, Acc) ->
case Remaining of
[] ->
flush_run(Current, Count, Acc);
[Head | Rest] ->
case Head =:= Current of
true ->
rle_loop(Rest, Current, Count + 1, Acc);
false ->
Acc@1 = flush_run(Current, Count, Acc),
rle_loop(Rest, Head, 1, Acc@1)
end
end.
-file("src/packkit/deflate.gleam", 1838).
-spec rle_encode_lengths(list(integer())) -> list(c_l_op()).
rle_encode_lengths(Lengths) ->
_pipe = rle_loop(Lengths, -1, 0, []),
lists:reverse(_pipe).
-file("src/packkit/deflate.gleam", 1917).
-spec cl_freq_loop(list(c_l_op()), list(integer())) -> list(integer()).
cl_freq_loop(Rle, Freqs) ->
case Rle of
[] ->
Freqs;
[{c_l_lit, V} | Rest] ->
cl_freq_loop(Rest, bump_index(Freqs, V));
[{c_l_copy, _} | Rest@1] ->
cl_freq_loop(Rest@1, bump_index(Freqs, 16));
[{c_l_zero3, _} | Rest@2] ->
cl_freq_loop(Rest@2, bump_index(Freqs, 17));
[{c_l_zero11, _} | Rest@3] ->
cl_freq_loop(Rest@3, bump_index(Freqs, 18))
end.
-file("src/packkit/deflate.gleam", 1957).
-spec any_nonzero(list(integer())) -> boolean().
any_nonzero(Values) ->
case Values of
[] ->
false;
[0 | Rest] ->
any_nonzero(Rest);
_ ->
true
end.
-file("src/packkit/deflate.gleam", 2016).
-spec last_nonzero_index(list(integer()), integer(), integer()) -> integer().
last_nonzero_index(Values, Index, Best) ->
case Values of
[] ->
Best;
[0 | Rest] ->
last_nonzero_index(Rest, Index + 1, Best);
[_ | Rest@1] ->
last_nonzero_index(Rest@1, Index + 1, Index)
end.
-file("src/packkit/deflate.gleam", 2024).
-spec take_first(list(integer()), integer(), list(integer())) -> list(integer()).
take_first(Values, N, Acc) ->
case {N, Values} of
{0, _} ->
lists:reverse(Acc);
{_, []} ->
take_first([], N - 1, [0 | Acc]);
{_, [Head | Rest]} ->
take_first(Rest, N - 1, [Head | Acc])
end.
-file("src/packkit/deflate.gleam", 2032).
-spec highest_present_clcl(
list(integer()),
list(integer()),
integer(),
integer()
) -> integer().
highest_present_clcl(Lengths, Order, Index, Best) ->
case Order of
[] ->
Best;
[Slot | Rest] ->
case list_get(Lengths, Slot, 0) of
0 ->
highest_present_clcl(Lengths, Rest, Index + 1, Best);
_ ->
highest_present_clcl(Lengths, Rest, Index + 1, Index)
end
end.
-file("src/packkit/deflate.gleam", 2048).
-spec max_int(integer(), integer()) -> integer().
max_int(A, B) ->
case A > B of
true ->
A;
false ->
B
end.
-file("src/packkit/deflate.gleam", 2055).
-spec write_clcl_lengths(
writer(),
list(integer()),
list(integer()),
integer(),
integer()
) -> writer().
write_clcl_lengths(Writer, Order, Lengths, Hclen, Emitted) ->
case {Emitted >= Hclen, Order} of
{true, _} ->
Writer;
{_, []} ->
Writer;
{_, [Slot | Rest]} ->
Length = list_get(Lengths, Slot, 0),
write_clcl_lengths(
write_bits(Writer, Length, 3),
Rest,
Lengths,
Hclen,
Emitted + 1
)
end.
-file("src/packkit/deflate.gleam", 2136).
-spec write_canonical_code(
writer(),
gleam@dict:dict(integer(), {integer(), integer()}),
integer()
) -> writer().
write_canonical_code(Writer, Codes, Symbol) ->
case gleam_stdlib:map_get(Codes, Symbol) of
{ok, {Code, Length}} ->
write_huffman_code(Writer, Code, Length);
{error, _} ->
Writer
end.
-file("src/packkit/deflate.gleam", 2078).
-spec write_rle_stream(
writer(),
list(c_l_op()),
gleam@dict:dict(integer(), {integer(), integer()})
) -> writer().
write_rle_stream(Writer, Rle, Cl_codes) ->
case Rle of
[] ->
Writer;
[{c_l_lit, V} | Rest] ->
write_rle_stream(
write_canonical_code(Writer, Cl_codes, V),
Rest,
Cl_codes
);
[{c_l_copy, Extra} | Rest@1] ->
Writer@1 = write_canonical_code(Writer, Cl_codes, 16),
Writer@2 = write_bits(Writer@1, Extra, 2),
write_rle_stream(Writer@2, Rest@1, Cl_codes);
[{c_l_zero3, Extra@1} | Rest@2] ->
Writer@3 = write_canonical_code(Writer, Cl_codes, 17),
Writer@4 = write_bits(Writer@3, Extra@1, 3),
write_rle_stream(Writer@4, Rest@2, Cl_codes);
[{c_l_zero11, Extra@2} | Rest@3] ->
Writer@5 = write_canonical_code(Writer, Cl_codes, 18),
Writer@6 = write_bits(Writer@5, Extra@2, 7),
write_rle_stream(Writer@6, Rest@3, Cl_codes)
end.
-file("src/packkit/deflate.gleam", 2109).
-spec write_token_stream(
writer(),
list(token()),
gleam@dict:dict(integer(), {integer(), integer()}),
gleam@dict:dict(integer(), {integer(), integer()})
) -> writer().
write_token_stream(Writer, Tokens, Lit_codes, Dist_codes) ->
case Tokens of
[] ->
Writer;
[{tok_lit, V} | Rest] ->
write_token_stream(
write_canonical_code(Writer, Lit_codes, V),
Rest,
Lit_codes,
Dist_codes
);
[{tok_match, L, D} | Rest@1] ->
{Lit_sym, Len_extra_bits, Len_extra} = length_code(L),
Writer@1 = write_canonical_code(Writer, Lit_codes, Lit_sym),
Writer@2 = write_bits(Writer@1, Len_extra, Len_extra_bits),
{Dist_sym, Dist_extra_bits, Dist_extra} = distance_code(D),
Writer@3 = write_canonical_code(Writer@2, Dist_codes, Dist_sym),
Writer@4 = write_bits(Writer@3, Dist_extra, Dist_extra_bits),
write_token_stream(Writer@4, Rest@1, Lit_codes, Dist_codes)
end.
-file("src/packkit/deflate.gleam", 300).
-spec validate_counts(list(integer()), integer(), integer(), integer()) -> {ok,
integer()} |
{error, packkit@error:codec_error()}.
validate_counts(Counts, Index, Available, Total) ->
case Index > 15 of
true ->
case (Total > 1) andalso (Available > 0) of
true ->
{error,
{codec_invalid_data,
<<"incomplete Huffman code lengths"/utf8>>}};
false ->
{ok, Total}
end;
false ->
Used = list_get(Counts, Index, 0),
case Used > Available of
true ->
{error,
{codec_invalid_data,
<<"over-subscribed Huffman code lengths"/utf8>>}};
false ->
validate_counts(
Counts,
Index + 1,
2 * (Available - Used),
Total + Used
)
end
end.
-file("src/packkit/deflate.gleam", 345).
-spec build_offsets(list(integer()), integer(), integer(), list(integer())) -> list(integer()).
build_offsets(Counts, Cumulative, Index, Acc) ->
case Index > 15 of
true ->
lists:reverse(Acc);
false ->
Used = list_get(Counts, Index, 0),
build_offsets(
Counts,
Cumulative + Used,
Index + 1,
[Cumulative | Acc]
)
end.
-file("src/packkit/deflate.gleam", 372).
-spec bucket_sort_symbols(
list({integer(), integer()}),
integer(),
list(integer())
) -> list(integer()).
bucket_sort_symbols(Pairs, Length, Acc) ->
case Length > 15 of
true ->
lists:reverse(Acc);
false ->
Bucket = gleam@list:filter_map(
Pairs,
fun(Pair) -> case erlang:element(2, Pair) =:= Length of
true ->
{ok, erlang:element(1, Pair)};
false ->
{error, nil}
end end
),
bucket_sort_symbols(
Pairs,
Length + 1,
prepend_reversed(Bucket, Acc)
)
end.
-file("src/packkit/deflate.gleam", 335).
-spec sort_symbols_by_length(list(integer()), list(integer())) -> list(integer()).
sort_symbols_by_length(Lengths, Counts) ->
Offsets = build_offsets(Counts, 0, 1, []),
Pairs = enumerate_lengths(Lengths, 0, []),
_ = Offsets,
bucket_sort_symbols(Pairs, 1, []).
-file("src/packkit/deflate.gleam", 237).
-spec build_tree(list(integer())) -> {ok, tree()} |
{error, packkit@error:codec_error()}.
build_tree(Lengths) ->
Max_sym = find_max_symbol(Lengths, 0, -1, 0),
Counts = count_lengths(Lengths),
gleam@result:'try'(
validate_counts(Counts, 0, 1, 0),
fun(_) ->
Symbols = sort_symbols_by_length(Lengths, Counts),
Counts_normalized = case {erlang:length(Symbols) =:= 1,
list_get(Counts, 1, 0) =:= 1} of
{true, true} ->
set_index(Counts, 1, 2);
{_, _} ->
Counts
end,
Symbols_normalized = case erlang:length(Symbols) =:= 1 of
true ->
[single_symbol(Symbols), Max_sym + 1 | []];
false ->
Symbols
end,
{ok, {tree, Counts_normalized, Symbols_normalized, Max_sym}}
end
).
-file("src/packkit/deflate.gleam", 406).
-spec decode_symbol_loop(reader(), tree(), integer(), integer(), integer()) -> {ok,
{integer(), reader()}} |
{error, packkit@error:codec_error()}.
decode_symbol_loop(Reader, Tree, Length, Base, Offset) ->
case Length > 15 of
true ->
{error,
{codec_invalid_data, <<"invalid Huffman code (overlong)"/utf8>>}};
false ->
gleam@result:'try'(
read_bits(Reader, 1),
fun(_use0) ->
{Bit, Reader@1} = _use0,
Offset@1 = (2 * Offset) + Bit,
Count = list_get(erlang:element(2, Tree), Length, 0),
case Offset@1 < Count of
true ->
{ok,
{list_get(
erlang:element(3, Tree),
Base + Offset@1,
-1
),
Reader@1}};
false ->
decode_symbol_loop(
Reader@1,
Tree,
Length + 1,
Base + Count,
Offset@1 - Count
)
end
end
)
end.
-file("src/packkit/deflate.gleam", 399).
-spec decode_symbol(reader(), tree()) -> {ok, {integer(), reader()}} |
{error, packkit@error:codec_error()}.
decode_symbol(Reader, Tree) ->
decode_symbol_loop(Reader, Tree, 1, 0, 0).
-file("src/packkit/deflate.gleam", 629).
-spec decode_length_run(
reader(),
tree(),
integer(),
integer(),
list(integer()),
integer()
) -> {ok, {list(integer()), reader()}} | {error, packkit@error:codec_error()}.
decode_length_run(Reader, Tree, Total, Produced, Acc, Previous) ->
case Produced >= Total of
true ->
{ok, {lists:reverse(Acc), Reader}};
false ->
gleam@result:'try'(
decode_symbol(Reader, Tree),
fun(_use0) ->
{Symbol, Reader@1} = _use0,
gleam@bool:guard(
(Symbol > 18) orelse (Symbol < 0),
{error,
{codec_invalid_data,
<<"invalid code-length symbol"/utf8>>}},
fun() -> case Symbol of
N when N < 16 ->
decode_length_run(
Reader@1,
Tree,
Total,
Produced + 1,
[N | Acc],
N
);
16 ->
gleam@result:'try'(
read_bits(Reader@1, 2),
fun(_use0@1) ->
{Extra, Reader@2} = _use0@1,
Count = 3 + Extra,
gleam@bool:guard(
Produced =:= 0,
{error,
{codec_invalid_data,
<<"repeat-previous symbol at start of run"/utf8>>}},
fun() ->
gleam@bool:guard(
(Produced + Count) > Total,
{error,
{codec_invalid_data,
<<"code-length run exceeds declared total"/utf8>>}},
fun() ->
Updated = repeat_value(
Previous,
Count,
Acc
),
decode_length_run(
Reader@2,
Tree,
Total,
Produced + Count,
Updated,
Previous
)
end
)
end
)
end
);
17 ->
gleam@result:'try'(
read_bits(Reader@1, 3),
fun(_use0@2) ->
{Extra@1, Reader@3} = _use0@2,
Count@1 = 3 + Extra@1,
gleam@bool:guard(
(Produced + Count@1) > Total,
{error,
{codec_invalid_data,
<<"zero-run symbol exceeds declared total"/utf8>>}},
fun() ->
Updated@1 = repeat_value(
0,
Count@1,
Acc
),
decode_length_run(
Reader@3,
Tree,
Total,
Produced + Count@1,
Updated@1,
0
)
end
)
end
);
_ ->
gleam@result:'try'(
read_bits(Reader@1, 7),
fun(_use0@3) ->
{Extra@2, Reader@4} = _use0@3,
Count@2 = 11 + Extra@2,
gleam@bool:guard(
(Produced + Count@2) > Total,
{error,
{codec_invalid_data,
<<"long zero-run exceeds declared total"/utf8>>}},
fun() ->
Updated@2 = repeat_value(
0,
Count@2,
Acc
),
decode_length_run(
Reader@4,
Tree,
Total,
Produced + Count@2,
Updated@2,
0
)
end
)
end
)
end end
)
end
)
end.
-file("src/packkit/deflate.gleam", 609).
-spec decode_dynamic_lengths(reader(), tree(), integer(), integer()) -> {ok,
{list(integer()), list(integer()), reader()}} |
{error, packkit@error:codec_error()}.
decode_dynamic_lengths(Reader, Tree, Hlit, Hdist) ->
Total = Hlit + Hdist,
gleam@result:'try'(
decode_length_run(Reader, Tree, Total, 0, [], 0),
fun(_use0) ->
{Combined, Reader@1} = _use0,
Lit_lengths = pad_to_length(gleam@list:take(Combined, Hlit), 286),
Dist_lengths = pad_to_length(gleam@list:drop(Combined, Hlit), 30),
{ok, {Lit_lengths, Dist_lengths, Reader@1}}
end
).
-file("src/packkit/deflate.gleam", 860).
-spec fixed_literal_tree() -> tree().
fixed_literal_tree() ->
Lengths = build_fixed_literal_lengths(),
Tree@1 = case build_tree(Lengths) of
{ok, Tree} -> Tree;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/deflate"/utf8>>,
function => <<"fixed_literal_tree"/utf8>>,
line => 864,
value => _assert_fail,
start => 25783,
'end' => 25824,
pattern_start => 25794,
pattern_end => 25802})
end,
Tree@1.
-file("src/packkit/deflate.gleam", 868).
-spec fixed_distance_tree() -> tree().
fixed_distance_tree() ->
Lengths = gleam@list:repeat(5, 32),
Tree@1 = case build_tree(Lengths) of
{ok, Tree} -> Tree;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/deflate"/utf8>>,
function => <<"fixed_distance_tree"/utf8>>,
line => 870,
value => _assert_fail,
start => 25907,
'end' => 25948,
pattern_start => 25918,
pattern_end => 25926})
end,
Tree@1.
-file("src/packkit/deflate.gleam", 749).
-spec inflate_huffman_step(
reader(),
bitstring(),
tree(),
tree(),
packkit@limit:limits()
) -> {ok, huffman_step()} | {error, packkit@error:codec_error()}.
inflate_huffman_step(Reader, Output, Ltree, Dtree, Limits) ->
gleam@result:'try'(
decode_symbol(Reader, Ltree),
fun(_use0) ->
{Symbol, Reader@1} = _use0,
case Symbol of
S when S < 256 ->
gleam@result:'try'(
append_with_limit(Output, <<S>>, Limits),
fun(New_output) ->
{ok, {huffman_continue, New_output, Reader@1}}
end
);
256 ->
{ok, {huffman_done, Output, Reader@1}};
S@1 ->
case S@1 > 285 of
true ->
{error,
{codec_invalid_data,
<<"literal/length code out of range"/utf8>>}};
false ->
Length_index = S@1 - 257,
Extra_bits = list_get(
length_extra_bits(),
Length_index,
0
),
Base = list_get(length_base(), Length_index, 0),
gleam@result:'try'(
read_bits(Reader@1, Extra_bits),
fun(_use0@1) ->
{Extra, Reader@2} = _use0@1,
Length = Base + Extra,
gleam@result:'try'(
decode_symbol(Reader@2, Dtree),
fun(_use0@2) ->
{Dist_symbol, Reader@3} = _use0@2,
gleam@bool:guard(
Dist_symbol > 29,
{error,
{codec_invalid_data,
<<"distance code out of range"/utf8>>}},
fun() ->
Dist_extra_bits = list_get(
distance_extra_bits(),
Dist_symbol,
0
),
Dist_base = list_get(
distance_base(),
Dist_symbol,
0
),
gleam@result:'try'(
read_bits(
Reader@3,
Dist_extra_bits
),
fun(_use0@3) ->
{Dist_extra,
Reader@4} = _use0@3,
Distance = Dist_base
+ Dist_extra,
gleam@result:'try'(
apply_backref(
Output,
Distance,
Length,
Limits
),
fun(
New_output@1
) ->
{ok,
{huffman_continue,
New_output@1,
Reader@4}}
end
)
end
)
end
)
end
)
end
)
end
end
end
).
-file("src/packkit/deflate.gleam", 729).
-spec inflate_huffman_block(
reader(),
bitstring(),
tree(),
tree(),
packkit@limit:limits()
) -> {ok, {bitstring(), reader()}} | {error, packkit@error:codec_error()}.
inflate_huffman_block(Reader, Output, Ltree, Dtree, Limits) ->
case inflate_huffman_step(Reader, Output, Ltree, Dtree, Limits) of
{error, Err} ->
{error, Err};
{ok, {huffman_done, Out, Rdr}} ->
{ok, {Out, Rdr}};
{ok, {huffman_continue, Out@1, Rdr@1}} ->
inflate_huffman_block(Rdr@1, Out@1, Ltree, Dtree, Limits)
end.
-file("src/packkit/deflate.gleam", 520).
-spec inflate_fixed(reader(), bitstring(), packkit@limit:limits()) -> {ok,
{bitstring(), reader()}} |
{error, packkit@error:codec_error()}.
inflate_fixed(Reader, Output, Limits) ->
inflate_huffman_block(
Reader,
Output,
fixed_literal_tree(),
fixed_distance_tree(),
Limits
).
-file("src/packkit/deflate.gleam", 534).
-spec inflate_dynamic(reader(), bitstring(), packkit@limit:limits()) -> {ok,
{bitstring(), reader()}} |
{error, packkit@error:codec_error()}.
inflate_dynamic(Reader, Output, Limits) ->
gleam@result:'try'(
read_bits(Reader, 5),
fun(_use0) ->
{Hlit_raw, Reader@1} = _use0,
Hlit = Hlit_raw + 257,
gleam@result:'try'(
read_bits(Reader@1, 5),
fun(_use0@1) ->
{Hdist_raw, Reader@2} = _use0@1,
Hdist = Hdist_raw + 1,
gleam@result:'try'(
read_bits(Reader@2, 4),
fun(_use0@2) ->
{Hclen_raw, Reader@3} = _use0@2,
Hclen = Hclen_raw + 4,
gleam@bool:guard(
(Hlit > 286) orelse (Hdist > 30),
{error,
{codec_invalid_data,
<<"dynamic header out of range"/utf8>>}},
fun() ->
gleam@result:'try'(
read_code_length_table(Reader@3, Hclen),
fun(_use0@3) ->
{Clcl_table, Reader@4} = _use0@3,
gleam@result:'try'(
build_tree(Clcl_table),
fun(Clcl_tree) ->
gleam@result:'try'(
decode_dynamic_lengths(
Reader@4,
Clcl_tree,
Hlit,
Hdist
),
fun(_use0@4) ->
{Lit_lengths,
Dist_lengths,
Reader@5} = _use0@4,
gleam@bool:guard(
list_get(
Lit_lengths,
256,
0
)
=:= 0,
{error,
{codec_invalid_data,
<<"dynamic header missing end-of-block symbol"/utf8>>}},
fun() ->
gleam@result:'try'(
build_tree(
Lit_lengths
),
fun(
Ltree
) ->
gleam@result:'try'(
build_tree(
Dist_lengths
),
fun(
Dtree
) ->
inflate_huffman_block(
Reader@5,
Output,
Ltree,
Dtree,
Limits
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/packkit/deflate.gleam", 463).
-spec inflate_one_block(reader(), bitstring(), packkit@limit:limits()) -> {ok,
inflate_step()} |
{error, packkit@error:codec_error()}.
inflate_one_block(Reader, Output, Limits) ->
gleam@result:'try'(
read_bits(Reader, 1),
fun(_use0) ->
{Bfinal, Reader@1} = _use0,
gleam@result:'try'(
read_bits(Reader@1, 2),
fun(_use0@1) ->
{Btype, Reader@2} = _use0@1,
gleam@result:'try'(case Btype of
0 ->
inflate_stored(Reader@2, Output, Limits);
1 ->
inflate_fixed(Reader@2, Output, Limits);
2 ->
inflate_dynamic(Reader@2, Output, Limits);
_ ->
{error,
{codec_invalid_data,
<<"reserved DEFLATE block type"/utf8>>}}
end, fun(_use0@2) ->
{Output@1, Reader@3} = _use0@2,
case Bfinal of
1 ->
{ok, {inflate_done, Output@1, Reader@3}};
_ ->
{ok, {inflate_continue, Output@1, Reader@3}}
end
end)
end
)
end
).
-file("src/packkit/deflate.gleam", 446).
-spec inflate(reader(), bitstring(), packkit@limit:limits()) -> {ok,
{bitstring(), reader()}} |
{error, packkit@error:codec_error()}.
inflate(Reader, Output, Limits) ->
case inflate_one_block(Reader, Output, Limits) of
{error, Err} ->
{error, Err};
{ok, {inflate_done, Out, Rdr}} ->
{ok, {Out, Rdr}};
{ok, {inflate_continue, Out@1, Rdr@1}} ->
inflate(Rdr@1, Out@1, Limits)
end.
-file("src/packkit/deflate.gleam", 55).
?DOC(" Decode a raw DEFLATE byte stream using explicit limits.\n").
-spec decode_with_limits(bitstring(), packkit@limit:limits()) -> {ok,
bitstring()} |
{error, packkit@error:codec_error()}.
decode_with_limits(Bytes, Limits) ->
gleam@bool:guard(
erlang:byte_size(Bytes) > packkit@limit:max_input_bytes(Limits),
{error,
{codec_limit_exceeded,
<<"max_input_bytes"/utf8>>,
erlang:byte_size(Bytes)}},
fun() ->
Reader = {reader, 0, 0, Bytes, false},
case inflate(Reader, <<>>, Limits) of
{ok, {Output, _}} ->
{ok, Output};
{error, Err} ->
{error, Err}
end
end
).
-file("src/packkit/deflate.gleam", 50).
?DOC(" Decode a raw DEFLATE byte stream using default limits.\n").
-spec decode(bitstring()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
decode(Bytes) ->
decode_with_limits(Bytes, packkit@limit:default()).
-file("src/packkit/deflate.gleam", 83).
?DOC(
" Decode a DEFLATE stream AND return the byte slice that follows the\n"
" last block in the input. Useful for wrappers like gzip that need\n"
" to know exactly where the deflate stream ends so they can read a\n"
" trailer immediately after it (and, for multi-member streams, the\n"
" next member that comes after the trailer).\n"
"\n"
" The remainder is byte-aligned: any partial bits left in the\n"
" deflate decoder's buffer after the last block are discarded as\n"
" inter-block padding per RFC 1952 §2.2.\n"
).
-spec decode_with_remainder(bitstring(), packkit@limit:limits()) -> {ok,
{bitstring(), bitstring()}} |
{error, packkit@error:codec_error()}.
decode_with_remainder(Bytes, Limits) ->
gleam@bool:guard(
erlang:byte_size(Bytes) > packkit@limit:max_input_bytes(Limits),
{error,
{codec_limit_exceeded,
<<"max_input_bytes"/utf8>>,
erlang:byte_size(Bytes)}},
fun() ->
Reader = {reader, 0, 0, Bytes, false},
gleam@result:'try'(
inflate(Reader, <<>>, Limits),
fun(_use0) ->
{Output, Final_reader} = _use0,
Remainder = recover_remaining_bytes(Final_reader),
{ok, {Output, Remainder}}
end
)
end
).
-file("src/packkit/deflate.gleam", 1041).
-spec encode_stored_blocks(bitstring(), integer(), integer(), list(bitstring())) -> bitstring().
encode_stored_blocks(Source, Remaining, Written, Acc) ->
case Remaining of
0 ->
gleam_stdlib:bit_array_concat(lists:reverse(Acc));
_ ->
Chunk_size = case Remaining > 65535 of
true ->
65535;
false ->
Remaining
end,
Final_flag = case Chunk_size =:= Remaining of
true ->
1;
false ->
0
end,
Chunk@1 = case gleam_stdlib:bit_array_slice(
Source,
Written,
Chunk_size
) of
{ok, Chunk} -> Chunk;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/deflate"/utf8>>,
function => <<"encode_stored_blocks"/utf8>>,
line => 1058,
value => _assert_fail,
start => 28164,
'end' => 28231,
pattern_start => 28175,
pattern_end => 28184})
end,
Header = <<Final_flag,
Chunk_size:16/little,
(erlang:'bxor'(Chunk_size, 16#FFFF)):16/little>>,
encode_stored_blocks(
Source,
Remaining - Chunk_size,
Written + Chunk_size,
[gleam_stdlib:bit_array_concat([Header, Chunk@1]) | Acc]
)
end.
-file("src/packkit/deflate.gleam", 1029).
-spec encode_stored(bitstring()) -> bitstring().
encode_stored(Bytes) ->
Size = erlang:byte_size(Bytes),
case Size of
0 ->
empty_stored_block();
_ ->
encode_stored_blocks(Bytes, Size, 0, [])
end.
-file("src/packkit/deflate.gleam", 143).
?DOC(
" Encode a byte stream as a sequence of stored (uncompressed)\n"
" DEFLATE blocks. Useful when the caller wants to bypass the\n"
" match-finder, for instance to test the framing in isolation.\n"
).
-spec encode_stored_only(bitstring()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
encode_stored_only(Bytes) ->
{ok, encode_stored(Bytes)}.
-file("src/packkit/deflate.gleam", 1227).
-spec insert_hashes_in_range(
gleam@dict:dict(integer(), integer()),
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
integer()
) -> gleam@dict:dict(integer(), integer()).
insert_hashes_in_range(Table, Hashes, From, To, Size) ->
case (From > To) orelse ((From + 3) > Size) of
true ->
Hashes;
false ->
Key = hash3(
byte_at(Table, From),
byte_at(Table, From + 1),
byte_at(Table, From + 2)
),
insert_hashes_in_range(
Table,
gleam@dict:insert(Hashes, Key, From),
From + 1,
To,
Size
)
end.
-file("src/packkit/deflate.gleam", 1129).
-spec emit_lz77(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
gleam@dict:dict(integer(), integer()),
writer()
) -> writer().
emit_lz77(Table, Size, Pos, Hashes, Writer) ->
case Pos >= Size of
true ->
Writer;
false ->
case (Pos + 3) > Size of
true ->
Writer@1 = write_fixed_literal_code(
Writer,
byte_at(Table, Pos)
),
emit_lz77(Table, Size, Pos + 1, Hashes, Writer@1);
false ->
B0 = byte_at(Table, Pos),
B1 = byte_at(Table, Pos + 1),
B2 = byte_at(Table, Pos + 2),
Key = hash3(B0, B1, B2),
case gleam_stdlib:map_get(Hashes, Key) of
{error, _} ->
Writer@2 = write_fixed_literal_code(Writer, B0),
emit_lz77(
Table,
Size,
Pos + 1,
gleam@dict:insert(Hashes, Key, Pos),
Writer@2
);
{ok, Prev} ->
Distance = Pos - Prev,
case (Distance =< 0) orelse (Distance > 32768) of
true ->
Writer@3 = write_fixed_literal_code(
Writer,
B0
),
emit_lz77(
Table,
Size,
Pos + 1,
gleam@dict:insert(Hashes, Key, Pos),
Writer@3
);
false ->
Match = match_length(
Table,
Prev,
Pos,
Size,
258,
0
),
case Match >= 3 of
true ->
Writer@4 = write_match(
Writer,
Match,
Distance
),
Next_hashes = insert_hashes_in_range(
Table,
gleam@dict:insert(
Hashes,
Key,
Pos
),
Pos + 1,
(Pos + Match) - 1,
Size
),
emit_lz77(
Table,
Size,
Pos + Match,
Next_hashes,
Writer@4
);
false ->
Writer@5 = write_fixed_literal_code(
Writer,
B0
),
emit_lz77(
Table,
Size,
Pos + 1,
gleam@dict:insert(
Hashes,
Key,
Pos
),
Writer@5
)
end
end
end
end
end.
-file("src/packkit/deflate.gleam", 1076).
-spec encode_huffman(bitstring()) -> bitstring().
encode_huffman(Bytes) ->
Size = erlang:byte_size(Bytes),
case Size of
0 ->
Writer = begin
_pipe = new_writer(),
_pipe@1 = write_bits(_pipe, 1, 1),
_pipe@2 = write_bits(_pipe@1, 1, 2),
write_fixed_literal_code(_pipe@2, 256)
end,
flush_writer(Writer);
_ ->
Byte_table = build_byte_table(Bytes, 0, maps:new()),
Writer@1 = begin
_pipe@3 = new_writer(),
_pipe@4 = write_bits(_pipe@3, 1, 1),
write_bits(_pipe@4, 1, 2)
end,
Writer@2 = emit_lz77(Byte_table, Size, 0, maps:new(), Writer@1),
Writer@3 = write_fixed_literal_code(Writer@2, 256),
flush_writer(Writer@3)
end.
-file("src/packkit/deflate.gleam", 136).
?DOC(
" Encode a byte stream as a fixed-Huffman DEFLATE block.\n"
"\n"
" The encoder uses a greedy LZ77 match-finder with a 3-byte hash\n"
" chain and a 32 KiB sliding window, then emits the resulting\n"
" literal/length/distance tokens through the RFC 1951 fixed\n"
" Huffman table.\n"
).
-spec encode(bitstring()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
encode(Bytes) ->
{ok, encode_huffman(Bytes)}.
-file("src/packkit/deflate.gleam", 1509).
-spec empty_dynamic_block() -> bitstring().
empty_dynamic_block() ->
encode_huffman(<<>>).
-file("src/packkit/deflate.gleam", 1517).
-spec collect_lz77_tokens(
gleam@dict:dict(integer(), integer()),
integer(),
integer(),
gleam@dict:dict(integer(), integer()),
list(token())
) -> list(token()).
collect_lz77_tokens(Table, Size, Pos, Hashes, Acc) ->
case Pos >= Size of
true ->
lists:reverse(Acc);
false ->
case (Pos + 3) > Size of
true ->
collect_lz77_tokens(
Table,
Size,
Pos + 1,
Hashes,
[{tok_lit, byte_at(Table, Pos)} | Acc]
);
false ->
B0 = byte_at(Table, Pos),
B1 = byte_at(Table, Pos + 1),
B2 = byte_at(Table, Pos + 2),
Key = hash3(B0, B1, B2),
case gleam_stdlib:map_get(Hashes, Key) of
{error, _} ->
collect_lz77_tokens(
Table,
Size,
Pos + 1,
gleam@dict:insert(Hashes, Key, Pos),
[{tok_lit, B0} | Acc]
);
{ok, Prev} ->
Distance = Pos - Prev,
case (Distance =< 0) orelse (Distance > 32768) of
true ->
collect_lz77_tokens(
Table,
Size,
Pos + 1,
gleam@dict:insert(Hashes, Key, Pos),
[{tok_lit, B0} | Acc]
);
false ->
M = match_length(
Table,
Prev,
Pos,
Size,
258,
0
),
case M >= 3 of
true ->
Next_hashes = insert_hashes_in_range(
Table,
gleam@dict:insert(
Hashes,
Key,
Pos
),
Pos + 1,
(Pos + M) - 1,
Size
),
collect_lz77_tokens(
Table,
Size,
Pos + M,
Next_hashes,
[{tok_match, M, Distance} | Acc]
);
false ->
collect_lz77_tokens(
Table,
Size,
Pos + 1,
gleam@dict:insert(
Hashes,
Key,
Pos
),
[{tok_lit, B0} | Acc]
)
end
end
end
end
end.
-file("src/packkit/deflate.gleam", 1602).
-spec token_frequencies(list(token())) -> {list(integer()), list(integer())}.
token_frequencies(Tokens) ->
Lit_freqs = gleam@list:repeat(0, 286),
Dist_freqs = gleam@list:repeat(0, 30),
token_freq_loop(Tokens, Lit_freqs, Dist_freqs).
-file("src/packkit/deflate.gleam", 1913).
-spec cl_frequencies(list(c_l_op())) -> list(integer()).
cl_frequencies(Rle) ->
cl_freq_loop(Rle, gleam@list:repeat(0, 19)).
-file("src/packkit/deflate.gleam", 1965).
-spec emit_dynamic_block(list(token()), list(integer()), list(integer())) -> {ok,
bitstring()} |
{error, nil}.
emit_dynamic_block(Tokens, Lit_lengths, Dist_lengths) ->
Lit_codes = canonical_codes_from_lengths(Lit_lengths),
Dist_codes = canonical_codes_from_lengths(Dist_lengths),
Hlit = max_int(last_nonzero_index(Lit_lengths, 0, -1) + 1, 257),
Hdist = max_int(last_nonzero_index(Dist_lengths, 0, -1) + 1, 1),
Combined = lists:append(
take_first(Lit_lengths, Hlit, []),
take_first(Dist_lengths, Hdist, [])
),
Rle = rle_encode_lengths(Combined),
Cl_freqs = cl_frequencies(Rle),
gleam@result:'try'(
huffman_code_lengths(Cl_freqs, 19, 7),
fun(Cl_lengths) ->
Cl_codes = canonical_codes_from_lengths(Cl_lengths),
Order = code_length_order(),
Hclen = max_int(
highest_present_clcl(Cl_lengths, Order, 0, -1) + 1,
4
),
Writer = begin
_pipe = new_writer(),
_pipe@1 = write_bits(_pipe, 1, 1),
_pipe@2 = write_bits(_pipe@1, 2, 2),
_pipe@3 = write_bits(_pipe@2, Hlit - 257, 5),
_pipe@4 = write_bits(_pipe@3, Hdist - 1, 5),
write_bits(_pipe@4, Hclen - 4, 4)
end,
Writer@1 = write_clcl_lengths(Writer, Order, Cl_lengths, Hclen, 0),
Writer@2 = write_rle_stream(Writer@1, Rle, Cl_codes),
Writer@3 = write_token_stream(
Writer@2,
Tokens,
Lit_codes,
Dist_codes
),
Writer@4 = write_canonical_code(Writer@3, Lit_codes, 256),
{ok, flush_writer(Writer@4)}
end
).
-file("src/packkit/deflate.gleam", 1929).
-spec build_dynamic_block(list(token())) -> {ok, bitstring()} | {error, nil}.
build_dynamic_block(Tokens) ->
{Lit_freqs, Dist_freqs} = token_frequencies(Tokens),
Lit_freqs@1 = bump_index(Lit_freqs, 256),
Dist_freqs@1 = case any_nonzero(Dist_freqs) of
true ->
Dist_freqs;
false ->
bump_index(Dist_freqs, 0)
end,
gleam@result:'try'(
huffman_code_lengths(Lit_freqs@1, 286, 15),
fun(Lit_lengths) ->
gleam@result:'try'(
huffman_code_lengths(Dist_freqs@1, 30, 15),
fun(Dist_lengths) ->
emit_dynamic_block(Tokens, Lit_lengths, Dist_lengths)
end
)
end
).
-file("src/packkit/deflate.gleam", 1487).
-spec encode_dynamic_or_fixed(bitstring()) -> bitstring().
encode_dynamic_or_fixed(Bytes) ->
Size = erlang:byte_size(Bytes),
case Size of
0 ->
empty_dynamic_block();
_ ->
Table = build_byte_table(Bytes, 0, maps:new()),
Tokens = collect_lz77_tokens(Table, Size, 0, maps:new(), []),
case build_dynamic_block(Tokens) of
{ok, Bytes@1} ->
Bytes@1;
{error, nil} ->
encode_huffman(bytes_from_table_unused(Bytes))
end
end.
-file("src/packkit/deflate.gleam", 163).
?DOC(
" Encode a byte stream as a dynamic-Huffman DEFLATE block (BTYPE=10).\n"
"\n"
" Runs the same greedy LZ77 match-finder as [encode], but builds\n"
" per-stream Huffman codes for the literal/length and distance\n"
" alphabets from the observed symbol frequencies, then emits them as\n"
" an RFC 1951 dynamic-Huffman block. This usually compresses better\n"
" than the fixed-Huffman path on real-world inputs because rare\n"
" symbols get longer codes and common symbols get shorter ones.\n"
"\n"
" If the natural Huffman tree would exceed the RFC 1951 15-bit\n"
" maximum code length for the literal/length or distance alphabet\n"
" (which happens only on pathologically skewed inputs), the encoder\n"
" transparently falls back to the fixed-Huffman path so the call\n"
" still returns a valid stream.\n"
).
-spec encode_dynamic(bitstring()) -> {ok, bitstring()} |
{error, packkit@error:codec_error()}.
encode_dynamic(Bytes) ->
{ok, encode_dynamic_or_fixed(Bytes)}.