-module(lzf_gleam). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([decompress/1, compress/1]). -export_type([decompress_error/0]). -type decompress_error() :: decompress_error. -spec process_backreference( integer(), lzf_gleam@internal@back_ref:back_ref(), bitstring() ) -> {ok, bitstring()} | {error, nil}. process_backreference(Current_pos, Ref, Output) -> Cursor = (Current_pos - erlang:element(3, Ref)) * 8, Len_bits = erlang:element(2, Ref) * 8, case Output of <<_:Cursor, Literals:Len_bits, _/bitstring>> -> {ok, <>}; _ -> {error, nil} end. -spec process_literals(bitstring(), integer(), integer(), bitstring()) -> {ok, bitstring()} | {error, nil}. process_literals(Data, Current_pos, Len, Output) -> Cursor = Current_pos * 8, Len_bits = Len * 8, case Data of <<_:Cursor, Literals:Len_bits, _/bitstring>> -> {ok, <>}; _ -> {error, nil} end. -spec process_decompress(bitstring(), integer(), integer(), bitstring()) -> {ok, bitstring()} | {error, nil}. process_decompress(Data, Input_pos, Output_pos, Output) -> Cursor = Input_pos * 8, case Data of <<_:Cursor, Control:1, Len:3, Offset:12, _/bitstring>> when Control =:= 1 -> Ref = {back_ref, Len, Offset}, case process_backreference(Output_pos, Ref, Output) of {ok, Output@1} -> process_decompress( Data, Input_pos + 2, Output_pos + erlang:element(2, Ref), Output@1 ); {error, _} -> {error, nil} end; <<_:Cursor, Control@1:1, Len@1:7, _/bitstring>> when Control@1 =:= 0 -> case process_literals(Data, Input_pos + 1, Len@1, Output) of {ok, Output@2} -> Output_pos@1 = Output_pos + Len@1, process_decompress( Data, (Input_pos + Len@1) + 1, Output_pos@1, Output@2 ); {error, _} -> {error, nil} end; _ -> {ok, Output} end. -spec decompress(bitstring()) -> {ok, binary()} | {error, decompress_error()}. decompress(Data) -> Result = begin _pipe = Data, _pipe@1 = process_decompress(_pipe, 0, 0, <<>>), gleam@result:'try'(_pipe@1, fun gleam@bit_array:to_string/1) end, case Result of {ok, Data@1} -> {ok, Data@1}; {error, _} -> {error, decompress_error} end. -spec create_control_byte(integer()) -> bitstring(). create_control_byte(Count) -> <<0:1, Count:7>>. -spec flush_literals(bitstring(), bitstring(), integer()) -> bitstring(). flush_literals(Output, Buffer, Count) -> case Count > 0 of true -> Control_byte = create_control_byte(Count), <>; false -> Output end. -spec match_len_recursive(bitstring(), bitstring(), integer()) -> integer(). match_len_recursive(Seq1, Seq2, Acc) -> case {Seq1, Seq2} of {<>, <>} when (First1 =:= First2) andalso (Acc < 7) -> match_len_recursive(Rest1, Rest2, Acc + 1); {_, _} -> Acc end. -spec get_match_len(bitstring(), integer(), integer()) -> integer(). get_match_len(Input, Match_pos, Current_pos) -> Len = erlang:byte_size(Input), S1_result = gleam_stdlib:bit_array_slice(Input, Match_pos, Len - Match_pos), S2_result = gleam_stdlib:bit_array_slice( Input, Current_pos, Len - Current_pos ), case {S1_result, S2_result} of {{ok, S1}, {ok, S2}} -> match_len_recursive(S1, S2, 0); {_, _} -> erlang:error(#{gleam_error => panic, message => <<"Something went wrong slicing the arrays, this shouldn't happen"/utf8>>, module => <<"lzf_gleam"/utf8>>, function => <<"get_match_len"/utf8>>, line => 234}) end. -spec process_current( bitstring(), integer(), integer(), gleam@dict:dict(integer(), integer()), bitstring(), bitstring(), integer() ) -> bitstring(). process_current( Input, Current, Current_pos, Hash_map, Output, Literal_buffer, Literal_count ) -> case gleam@dict:get(Hash_map, Current) of {ok, Match_pos} -> process_match( Input, Current, Current_pos, Match_pos, Hash_map, Output, Literal_buffer, Literal_count ); _ -> Hash_map@1 = gleam@dict:insert(Hash_map, Current, Current_pos), First = begin _pipe = Current, erlang:'bsr'(_pipe, 16) end, process_input( Input, Current_pos + 1, Hash_map@1, Output, <>, Literal_count + 1 ) end. -spec process_input( bitstring(), integer(), gleam@dict:dict(integer(), integer()), bitstring(), bitstring(), integer() ) -> bitstring(). process_input( Input, Current_pos, Hash_map, Output, Literal_buffer, Literal_count ) -> Cursor = Current_pos * 8, case Input of <<_:Cursor>> -> Output; <<_:Cursor, Current:24, _/bitstring>> -> process_current( Input, Current, Current_pos, Hash_map, Output, Literal_buffer, Literal_count ); <<_:Cursor, Rest/bitstring>> -> Output@1 = flush_literals(Output, Literal_buffer, Literal_count), Size = erlang:byte_size(Rest), <>; _ -> erlang:error(#{gleam_error => panic, message => <<"shouldn't get here"/utf8>>, module => <<"lzf_gleam"/utf8>>, function => <<"process_input"/utf8>>, line => 126}) end. -spec compress(binary()) -> bitstring(). compress(Input) -> _pipe = Input, _pipe@1 = gleam_stdlib:identity(_pipe), process_input(_pipe@1, 0, gleam@dict:new(), <<>>, <<>>, 0). -spec process_match( bitstring(), integer(), integer(), integer(), gleam@dict:dict(integer(), integer()), bitstring(), bitstring(), integer() ) -> bitstring(). process_match( Input, Current, Current_pos, Match_pos, Hash_map, Output, Literal_buffer, Literal_count ) -> case Current_pos - Match_pos of Diff when Diff < 8191 -> Match_len = get_match_len(Input, Match_pos, Current_pos), Token = {back_ref, Match_len, Diff}, Output@1 = flush_literals(Output, Literal_buffer, Literal_count), Token@1 = lzf_gleam@internal@back_ref:to_bit_array(Token), process_input( Input, Current_pos + Match_len, Hash_map, <>, <<>>, 0 ); _ -> Hash_map@1 = gleam@dict:insert(Hash_map, Current, Current_pos), process_input( Input, Current_pos + 1, Hash_map@1, Output, <>, Literal_count + 1 ) end.