-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, <> ) 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 <> -> 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 <> -> {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 => <>, 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 => <>, 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 <> -> 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, <>) 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 => <>, 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 => <>, 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, <>, 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 => <>, 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 = <>, 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)}.