Skip to main content

src/packkit@internal@bcj2.erl

-module(packkit@internal@bcj2).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit/internal/bcj2.gleam").
-export([decode/5]).
-export_type([bcj2_error/0, state/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(false).

-type bcj2_error() :: range_coder_header_invalid |
    {stream_exhausted, binary()} |
    {output_size_mismatch, integer(), integer()}.

-type state() :: {state,
        bitstring(),
        bitstring(),
        bitstring(),
        bitstring(),
        gleam@dict:dict(integer(), integer()),
        integer(),
        integer(),
        integer(),
        integer(),
        bitstring(),
        integer()}.

-file("src/packkit/internal/bcj2.gleam", 143).
?DOC(false).
-spec init_range_coder(bitstring()) -> {ok, {integer(), bitstring()}} |
    {error, bcj2_error()}.
init_range_coder(Rc) ->
    case Rc of
        <<First, C1, C2, C3, C4, Rest/binary>> ->
            case First =:= 0 of
                true ->
                    Code = erlang:'bor'(
                        erlang:'bor'(erlang:'bsl'(C1, 24), erlang:'bsl'(C2, 16)),
                        erlang:'bor'(erlang:'bsl'(C3, 8), C4)
                    ),
                    {ok, {Code, Rest}};

                false ->
                    {error, range_coder_header_invalid}
            end;

        _ ->
            {error, range_coder_header_invalid}
    end.

-file("src/packkit/internal/bcj2.gleam", 209).
?DOC(false).
-spec consume_main_byte(state(), integer(), bitstring()) -> state().
consume_main_byte(State, Byte, Main_rest) ->
    {state,
        Main_rest,
        erlang:element(3, State),
        erlang:element(4, State),
        erlang:element(5, State),
        erlang:element(6, State),
        erlang:element(7, State),
        erlang:element(8, State),
        erlang:element(9, State) + 1,
        Byte,
        <<(erlang:element(11, State))/bitstring, Byte>>,
        erlang:element(12, State)}.

-file("src/packkit/internal/bcj2.gleam", 223).
?DOC(false).
-spec is_branch_candidate(integer(), integer()) -> boolean().
is_branch_candidate(Byte, Prev_byte) ->
    case Byte of
        16#E8 ->
            true;

        16#E9 ->
            true;

        _ ->
            case (Prev_byte =:= 16#0F) andalso (erlang:'band'(Byte, 16#F0) =:= 16#80) of
                true ->
                    true;

                false ->
                    false
            end
    end.

-file("src/packkit/internal/bcj2.gleam", 269).
?DOC(false).
-spec prev_byte_for_call(state()) -> integer().
prev_byte_for_call(State) ->
    Output_size = erlang:byte_size(erlang:element(11, State)),
    case Output_size < 2 of
        true ->
            0;

        false ->
            case gleam_stdlib:bit_array_slice(
                erlang:element(11, State),
                Output_size - 2,
                1
            ) of
                {ok, <<Single>>} ->
                    Single;

                _ ->
                    0
            end
    end.

-file("src/packkit/internal/bcj2.gleam", 283).
?DOC(false).
-spec range_decode_bit(integer(), integer(), integer()) -> {integer(),
    integer(),
    integer(),
    integer()}.
range_decode_bit(Range, Code, Prob) ->
    Bound = begin
        _pipe = erlang:'bsr'(Range, 11),
        _pipe@1 = gleam@int:multiply(_pipe, Prob),
        erlang:'band'(_pipe@1, 16#FFFFFFFF)
    end,
    case Code < Bound of
        true ->
            New_prob = Prob + erlang:'bsr'(16#800 - Prob, 5),
            {0, Bound, Code, New_prob};

        false ->
            New_range = erlang:'band'(Range - Bound, 16#FFFFFFFF),
            New_code = erlang:'band'(Code - Bound, 16#FFFFFFFF),
            New_prob@1 = Prob - erlang:'bsr'(Prob, 5),
            {1, New_range, New_code, New_prob@1}
    end.

-file("src/packkit/internal/bcj2.gleam", 336).
?DOC(false).
-spec emit_branch_target(state(), integer()) -> {ok, state()} |
    {error, bcj2_error()}.
emit_branch_target(State, Opcode) ->
    {Dest_stream, Dest_label} = case Opcode =:= 16#E8 of
        true ->
            {erlang:element(3, State), <<"call"/utf8>>};

        false ->
            {erlang:element(4, State), <<"jump"/utf8>>}
    end,
    case Dest_stream of
        <<B0, B1, B2, B3, Dest_rest/binary>> ->
            Target = erlang:'bor'(
                erlang:'bor'(erlang:'bsl'(B0, 24), erlang:'bsl'(B1, 16)),
                erlang:'bor'(erlang:'bsl'(B2, 8), B3)
            ),
            New_ip = erlang:element(9, State) + 4,
            Relative = erlang:'band'(Target - New_ip, 16#FFFFFFFF),
            R0 = erlang:'band'(Relative, 16#FF),
            R1 = erlang:'band'(erlang:'bsr'(Relative, 8), 16#FF),
            R2 = erlang:'band'(erlang:'bsr'(Relative, 16), 16#FF),
            R3 = erlang:'band'(erlang:'bsr'(Relative, 24), 16#FF),
            New_output = <<(erlang:element(11, State))/bitstring,
                R0,
                R1,
                R2,
                R3>>,
            Next_state = {state,
                erlang:element(2, State),
                erlang:element(3, State),
                erlang:element(4, State),
                erlang:element(5, State),
                erlang:element(6, State),
                erlang:element(7, State),
                erlang:element(8, State),
                New_ip,
                R3,
                New_output,
                erlang:element(12, State)},
            case Opcode =:= 16#E8 of
                true ->
                    {ok,
                        {state,
                            erlang:element(2, Next_state),
                            Dest_rest,
                            erlang:element(4, Next_state),
                            erlang:element(5, Next_state),
                            erlang:element(6, Next_state),
                            erlang:element(7, Next_state),
                            erlang:element(8, Next_state),
                            erlang:element(9, Next_state),
                            erlang:element(10, Next_state),
                            erlang:element(11, Next_state),
                            erlang:element(12, Next_state)}};

                false ->
                    {ok,
                        {state,
                            erlang:element(2, Next_state),
                            erlang:element(3, Next_state),
                            Dest_rest,
                            erlang:element(5, Next_state),
                            erlang:element(6, Next_state),
                            erlang:element(7, Next_state),
                            erlang:element(8, Next_state),
                            erlang:element(9, Next_state),
                            erlang:element(10, Next_state),
                            erlang:element(11, Next_state),
                            erlang:element(12, Next_state)}}
            end;

        _ ->
            {error, {stream_exhausted, Dest_label}}
    end.

-file("src/packkit/internal/bcj2.gleam", 129).
?DOC(false).
-spec init_probs(integer(), gleam@dict:dict(integer(), integer())) -> gleam@dict:dict(integer(), integer()).
init_probs(Index, Acc) ->
    case gleam@int:compare(Index, 258) of
        lt ->
            init_probs(Index + 1, gleam@dict:insert(Acc, Index, 16#800 div 2));

        _ ->
            Acc
    end.

-file("src/packkit/internal/bcj2.gleam", 178).
?DOC(false).
-spec decode_loop_step(state()) -> {ok, state()} | {error, bcj2_error()}.
decode_loop_step(State) ->
    case erlang:element(2, State) of
        <<>> ->
            {ok, State};

        <<Byte, Main_rest/binary>> ->
            After_consume = consume_main_byte(State, Byte, Main_rest),
            gleam@result:'try'(
                decode_main_byte(After_consume, Byte, erlang:element(10, State)),
                fun(Stepped) -> decode_loop(Stepped) end
            );

        _ ->
            {error, {stream_exhausted, <<"main"/utf8>>}}
    end.

-file("src/packkit/internal/bcj2.gleam", 166).
?DOC(false).
-spec decode_loop(state()) -> {ok, state()} | {error, bcj2_error()}.
decode_loop(State) ->
    case gleam@int:compare(
        erlang:byte_size(erlang:element(11, State)),
        erlang:element(12, State)
    ) of
        lt ->
            decode_loop_step(State);

        _ ->
            {ok, State}
    end.

-file("src/packkit/internal/bcj2.gleam", 80).
?DOC(false).
-spec decode(bitstring(), bitstring(), bitstring(), bitstring(), integer()) -> {ok,
        bitstring()} |
    {error, bcj2_error()}.
decode(Main, Call, Jump, Rc, Output_size) ->
    gleam@result:'try'(
        init_range_coder(Rc),
        fun(_use0) ->
            {Code, Rc_rest} = _use0,
            Probs = init_probs(0, maps:new()),
            Initial = {state,
                Main,
                Call,
                Jump,
                Rc_rest,
                Probs,
                16#FFFFFFFF,
                Code,
                0,
                0,
                <<>>,
                Output_size},
            gleam@result:'try'(
                decode_loop(Initial),
                fun(Final_state) ->
                    Actual = erlang:byte_size(erlang:element(11, Final_state)),
                    case Actual =:= Output_size of
                        true ->
                            {ok, erlang:element(11, Final_state)};

                        false ->
                            {error, {output_size_mismatch, Output_size, Actual}}
                    end
                end
            )
        end
    ).

-file("src/packkit/internal/bcj2.gleam", 317).
?DOC(false).
-spec refill_rc_step(state()) -> {ok, state()} | {error, bcj2_error()}.
refill_rc_step(State) ->
    case erlang:element(5, State) of
        <<Next, Rc_rest/binary>> ->
            New_range = begin
                _pipe = erlang:'bsl'(erlang:element(7, State), 8),
                erlang:'band'(_pipe, 16#FFFFFFFF)
            end,
            New_code = begin
                _pipe@1 = erlang:'bor'(
                    erlang:'bsl'(erlang:element(8, State), 8),
                    Next
                ),
                erlang:'band'(_pipe@1, 16#FFFFFFFF)
            end,
            maybe_refill_rc(
                {state,
                    erlang:element(2, State),
                    erlang:element(3, State),
                    erlang:element(4, State),
                    Rc_rest,
                    erlang:element(6, State),
                    New_range,
                    New_code,
                    erlang:element(9, State),
                    erlang:element(10, State),
                    erlang:element(11, State),
                    erlang:element(12, State)}
            );

        _ ->
            {error, {stream_exhausted, <<"range_coder"/utf8>>}}
    end.

-file("src/packkit/internal/bcj2.gleam", 308).
?DOC(false).
-spec maybe_refill_rc(state()) -> {ok, state()} | {error, bcj2_error()}.
maybe_refill_rc(State) ->
    case gleam@int:compare(erlang:element(7, State), 16#1000000) of
        lt ->
            refill_rc_step(State);

        _ ->
            {ok, State}
    end.

-file("src/packkit/internal/bcj2.gleam", 239).
?DOC(false).
-spec decode_branch(state(), integer()) -> {ok, state()} | {error, bcj2_error()}.
decode_branch(State, Opcode) ->
    Prob_index = case Opcode of
        16#E8 ->
            2 + prev_byte_for_call(State);

        16#E9 ->
            1;

        _ ->
            0
    end,
    Prob = begin
        _pipe = gleam_stdlib:map_get(erlang:element(6, State), Prob_index),
        gleam@result:unwrap(_pipe, 16#800 div 2)
    end,
    {Bit, Range_after, Code_after, Prob_after} = range_decode_bit(
        erlang:element(7, State),
        erlang:element(8, State),
        Prob
    ),
    Probs_after = gleam@dict:insert(
        erlang:element(6, State),
        Prob_index,
        Prob_after
    ),
    Intermediate = {state,
        erlang:element(2, State),
        erlang:element(3, State),
        erlang:element(4, State),
        erlang:element(5, State),
        Probs_after,
        Range_after,
        Code_after,
        erlang:element(9, State),
        erlang:element(10, State),
        erlang:element(11, State),
        erlang:element(12, State)},
    gleam@result:'try'(
        maybe_refill_rc(Intermediate),
        fun(Refilled) -> case Bit of
                0 ->
                    {ok, Refilled};

                _ ->
                    emit_branch_target(Refilled, Opcode)
            end end
    ).

-file("src/packkit/internal/bcj2.gleam", 194).
?DOC(false).
-spec decode_main_byte(state(), integer(), integer()) -> {ok, state()} |
    {error, bcj2_error()}.
decode_main_byte(State, Byte, Prev_byte_before_consume) ->
    gleam@bool:guard(
        not is_branch_candidate(Byte, Prev_byte_before_consume),
        {ok, State},
        fun() -> decode_branch(State, Byte) end
    ).