Commit c27c791290 for ffmpeg

commit c27c7912904880e42f4a12c675527ae55a708a91
Author: Mark Thompson <sw@jkqxz.net>
Date:   Sun Oct 25 11:25:19 2026 +0000

    lavc/apv: Improve multi-symbol entropy decode

    This reduces the number of branches in the fast paths, improving
    throughput of the entropy decoder.

diff --git a/libavcodec/apv_decode.c b/libavcodec/apv_decode.c
index 7fd93ad0c2..a5167cc4e7 100644
--- a/libavcodec/apv_decode.c
+++ b/libavcodec/apv_decode.c
@@ -193,7 +193,10 @@ static int apv_decode_block(AVCodecContext *avctx,
     APVDecodeContext *apv = avctx->priv_data;
     int err;

-    LOCAL_ALIGNED_32(int16_t, coeff, [64]);
+    // Aligned for SIMD use in transquant.
+    LOCAL_ALIGNED_32(int16_t, coeff, [APV_ENTROPY_COEFF_SIZE]);
+    // Zero only the used entries, padding entries may be written but
+    // are never read.
     memset(coeff, 0, 64 * sizeof(int16_t));

     err = ff_apv_entropy_decode_block(coeff, gbc, entropy_state);
diff --git a/libavcodec/apv_decode.h b/libavcodec/apv_decode.h
index dd65f1b948..6a6dc853ff 100644
--- a/libavcodec/apv_decode.h
+++ b/libavcodec/apv_decode.h
@@ -19,6 +19,7 @@
 #ifndef AVCODEC_APV_DECODE_H
 #define AVCODEC_APV_DECODE_H

+#include <assert.h>
 #include <stdint.h>
 #include <stdatomic.h>

@@ -33,10 +34,15 @@
 // Number of bits in the entropy look-up tables.
 // It may be desirable to tune this per-architecture, as a larger LUT
 // trades greater memory use for fewer instructions.
-// (N bits -> 24*2^N bytes of tables; 9 -> 12KB of tables.)
+// (N bits -> 384*2^N bytes of tables; 9 -> 192KB of tables.)
 #define APV_VLC_LUT_BITS 9
 #define APV_VLC_LUT_SIZE (1 << APV_VLC_LUT_BITS)

+// The single-symbol LUT must be able to resolve a symbol with a single
+// leading zero for the largest k (5) without reading more bits, which
+// takes 2 + 1 + 5 = 8 bits.
+static_assert(APV_VLC_LUT_BITS >= 8, "APV VLC LUT too small");
+
 typedef struct APVSingleVLCLUTEntry {
     uint16_t result;  // Return value if not reading more.
     uint8_t  consume; // Number of bits to consume.
@@ -52,26 +58,57 @@ typedef struct APVMultiVLCLUTEntry {
     uint8_t k_level_0 : 3;
     // k_level after decoding all symbols.
     uint8_t k_level_1 : 3;
-    // Run output values.
-    uint8_t run[2];
-    // Level output values.
-    int16_t level[2];
-    // Bit index of the end of each code.
+    // Level output values.  If fewer than two levels are decoded the
+    // remaining ones are zero.
+    int8_t  level[2];
+    // Scan position of each level relative to the scan position before
+    // this entry.  Missing levels are placed at the end of the entry,
+    // where writing the level zero is harmless.
+    uint8_t level_pos[2];
+    // Total scan position advance if all symbols are consumed.  If not
+    // all symbols are consumed this can be recalculated from level_pos.
+    uint8_t scan_advance;
+    // Bits consumed at the end of each of the four possible symbols.
+    // Entry 3 always contains the bits from all symbols, even if fewer
+    // than four symbols are consumed.
     uint8_t offset[4];
+    // Index of the table to use for the next lookup, combining the
+    // type of the next symbol with k_run and k_level after this entry.
+    uint8_t next;
 } APVMultiVLCLUTEntry;

+// Types of entry in the entropy table.
+enum {
+    // Entries are run/level/run/level.
+    APV_ENTROPY_RUN_FIRST   = 0,
+    // Entries are level/run/level/run.
+    APV_ENTROPY_LEVEL_FIRST = 1,
+};
+// Index of the base point in the multi-symbol table for the given
+// type, k_run and k_level.
+#define APV_TABLE_INDEX(type, k_run, k_level) \
+    (((type) * 3 + (k_run)) * 5 + (k_level))
+
 typedef struct APVVLCLUT {
     // Single-symbol LUT for VLCs.
     // Applies to all coefficients, but used only for DC coefficients
     // in the decoder.
     APVSingleVLCLUTEntry single_lut[6][APV_VLC_LUT_SIZE];
     // Multi-symbol LUT for run/level combinations, decoding up to four
-    // symbols per step.  Comes in two versions, which to use depends on
-    // whether the next symbol is a run or a level.
-    APVMultiVLCLUTEntry run_first_lut[3][5][APV_VLC_LUT_SIZE];
-    APVMultiVLCLUTEntry level_first_lut[3][5][APV_VLC_LUT_SIZE];
+    // symbols per step.
+    // Indexed by [run first or level first][k_run][k_level][bits].
+    APVMultiVLCLUTEntry multi_lut[2][3][5][APV_VLC_LUT_SIZE];
+    // Zigzag scan extended past the end of the block, where every
+    // position maps to a dummy coefficient after the block.
+    uint8_t zigzag[128];
 } APVVLCLUT;

+
+// Number of coefficients in the block buffer used by entropy decode:
+// the block itself and a dummy coefficient slot at the end which is
+// used to avoid branching.
+#define APV_ENTROPY_COEFF_SIZE 65
+
 typedef struct APVEntropyState {
     void *log_ctx;

@@ -126,8 +163,10 @@ void ff_apv_entropy_build_decode_lut(APVVLCLUT *decode_lut);
  * Entropy decode a single 8x8 block to coefficients.
  *
  * Outputs nonzero coefficients only to the block row-major order
- * (dezigzag is applied within the function).  The output block
- * must have been filled with zeroes before calling this function.
+ * (dezigzag is applied within the function).  The first 64 elements
+ * of the output block must have been filled with zeroes before
+ * calling this function, and there must be space for
+ * APV_ENTROPY_COEFF_SIZE elements in total.
  */
 int ff_apv_entropy_decode_block(int16_t *restrict coeff,
                                 GetBitContext *restrict gbc,
diff --git a/libavcodec/apv_entropy.c b/libavcodec/apv_entropy.c
index 30f31a14ff..507318cafa 100644
--- a/libavcodec/apv_entropy.c
+++ b/libavcodec/apv_entropy.c
@@ -108,101 +108,214 @@ void ff_apv_entropy_build_decode_lut(APVVLCLUT *decode_lut)
     // Build the multi-symbol VLC table.
     for (int start_run = 0; start_run <= 2; start_run++) {
         for (int start_level = 0; start_level <= 4; start_level++) {
-            for (unsigned int code = 0; code < lut_size; code++) {
-                APVMultiVLCLUTEntry *ent;
-                int k_run, k_level;
-                GetBitContext gbc;
-                PutBitContext pbc;
-                uint8_t buffer[16];
-                uint8_t run_first_buffer[16];
-                uint8_t level_first_buffer[16];
-
-                memset(buffer, 0, sizeof(buffer));
-                init_put_bits(&pbc, buffer, sizeof(buffer));
-                put_bits(&pbc, APV_VLC_LUT_BITS, code);
-                flush_put_bits(&pbc);
-
-                memcpy(run_first_buffer,   buffer, sizeof(buffer));
-                memcpy(level_first_buffer, buffer, sizeof(buffer));
-
-                k_run   = start_run;
-                k_level = start_level;
-
-                ent = &decode_lut->run_first_lut[k_run][k_level][code];
-                memset(ent, 0, sizeof(*ent));
-                init_get_bits8(&gbc, run_first_buffer, sizeof(run_first_buffer));
-
-                ent->count = 0;
-                for (int i = 0; i <= 1; i++) {
-                    int value, sign, pos;
-
-                    value = apv_read_vlc(&gbc, k_run, decode_lut);
-                    pos = get_bits_count(&gbc);
-                    if (pos > APV_VLC_LUT_BITS)
-                        break;
-                    ent->run[i] = value;
-                    ent->offset[ent->count] = pos;
-                    ++ent->count;
-                    k_run = FFMIN(value >> 2, 2);
-
-                    value = apv_read_vlc(&gbc, k_level, decode_lut);
-                    sign = get_bits1(&gbc);
-                    pos = get_bits_count(&gbc);
-                    if (pos > APV_VLC_LUT_BITS)
-                        break;
-                    ++value;
-                    ent->level[i] = sign ? -value : value;
-                    ent->offset[ent->count] = pos;
-                    ++ent->count;
-                    k_level = FFMIN(value >> 2, 4);
-                    if (i == 0)
-                        ent->k_level_0 = k_level;
-                }
-                if (ent->count > 0 && ent->count < 4)
-                    ent->offset[3] = ent->offset[ent->count - 1];
-                ent->k_run     = k_run;
-                ent->k_level_1 = k_level;
-
-                k_run   = start_run;
-                k_level = start_level;
-
-                ent = &decode_lut->level_first_lut[k_run][k_level][code];
-                memset(ent, 0, sizeof(*ent));
-                init_get_bits8(&gbc, level_first_buffer, sizeof(level_first_buffer));
-
-                ent->count = 0;
-                for (int i = 0; i <= 1; i++) {
-                    int value, sign, pos;
-
-                    value = apv_read_vlc(&gbc, k_level, decode_lut);
-                    sign = get_bits1(&gbc);
-                    pos = get_bits_count(&gbc);
-                    if (pos > APV_VLC_LUT_BITS)
-                        break;
-                    ++value;
-                    ent->level[i] = sign ? -value : value;
-                    ent->offset[ent->count] = pos;
-                    ++ent->count;
-                    k_level = FFMIN(value >> 2, 4);
-                    if (i == 0)
-                        ent->k_level_0 = k_level;
-
-                    value = apv_read_vlc(&gbc, k_run, decode_lut);
-                    pos = get_bits_count(&gbc);
-                    if (pos > APV_VLC_LUT_BITS)
-                        break;
-                    ent->run[i] = value;
-                    ent->offset[ent->count] = pos;
-                    ++ent->count;
-                    k_run = FFMIN(value >> 2, 2);
+            for (int type = APV_ENTROPY_RUN_FIRST;
+                 type <= APV_ENTROPY_LEVEL_FIRST; type++) {
+                for (unsigned int code = 0; code < lut_size; code++) {
+                    APVMultiVLCLUTEntry *ent;
+                    int k_run, k_level, adv, nb_levels;
+                    GetBitContext gbc;
+                    PutBitContext pbc;
+                    uint8_t buffer[16];
+
+                    memset(buffer, 0, sizeof(buffer));
+                    init_put_bits(&pbc, buffer, sizeof(buffer));
+                    put_bits(&pbc, APV_VLC_LUT_BITS, code);
+                    flush_put_bits(&pbc);
+
+                    k_run   = start_run;
+                    k_level = start_level;
+
+                    ent = &decode_lut->multi_lut[type][k_run][k_level][code];
+                    memset(ent, 0, sizeof(*ent));
+                    init_get_bits8(&gbc, buffer, sizeof(buffer));
+
+                    // Symbols alternate between run and level, starting
+                    // with whichever the type indicates (so even values
+                    // of sym are runs).  Stop at the first one which
+                    // does not fit in the lookup bits.
+                    adv       = 0;
+                    nb_levels = 0;
+                    for (int sym = type; sym < type + 4; sym++) {
+                        int value, sign, pos;
+
+                        if (sym % 2 == 0) {
+                            // Reading a run.
+                            value = apv_read_vlc(&gbc, k_run, decode_lut);
+                            pos   = get_bits_count(&gbc);
+                            if (pos > APV_VLC_LUT_BITS)
+                                break;
+                            adv  += value;
+                            k_run = FFMIN(value >> 2, 2);
+                        } else {
+                            // Reading a level.
+                            value = apv_read_vlc(&gbc, k_level, decode_lut);
+                            sign  = get_bits1(&gbc);
+                            pos   = get_bits_count(&gbc);
+                            if (pos > APV_VLC_LUT_BITS)
+                                break;
+                            ++value;
+                            av_assert0(value <= INT8_MAX);
+                            ent->level[nb_levels] = sign ? -value : value;
+                            ent->level_pos[nb_levels] = adv;
+                            ++nb_levels;
+                            adv    += 1;
+                            k_level = FFMIN(value >> 2, 4);
+                            if (nb_levels == 1)
+                                ent->k_level_0 = k_level;
+                        }
+                        ent->offset[ent->count] = pos;
+                        ++ent->count;
+                    }
+                    // Missing levels write zeroes later in the array where
+                    // they will either be overwritten or not used.
+                    for (int i = nb_levels; i < 2; i++)
+                        ent->level_pos[i] = adv;
+                    // Entries are applied at scan positions below 64, so
+                    // this keeps every write inside the extended zigzag.
+                    av_assert0(adv < 64);
+                    ent->scan_advance = adv;
+                    if (ent->count > 0 && ent->count < 4)
+                        ent->offset[3] = ent->offset[ent->count - 1];
+                    ent->k_run     = k_run;
+                    ent->k_level_1 = k_level;
+                    ent->next = APV_TABLE_INDEX(type ^ (ent->count & 1),
+                                                      k_run, k_level);
                 }
-                if (ent->count > 0 && ent->count < 4)
-                    ent->offset[3] = ent->offset[ent->count - 1];
-                ent->k_run     = k_run;
-                ent->k_level_1 = k_level;
             }
         }
     }
+
+    // Positions past the end of the block all map to the dummy
+    // coefficient slot at the end.
+    for (int i = 0; i < FF_ARRAY_ELEMS(decode_lut->zigzag); i++)
+        decode_lut->zigzag[i] = i < 64 ? ff_zigzag_direct[i] : 64;
+}
+
+// Decode a run code which is too long for the multi-symbol LUT.
+// All such codes fit in the 18 bits already read.
+av_always_inline
+static int apv_decode_long_run(GetBitContext *restrict gbc,
+                               uint32_t next_bits, int k_run,
+                               APVEntropyState *restrict state)
+{
+    uint32_t bits, low_bits;
+    unsigned int leading_zeroes, low_bit_count, low_bit_shift;
+
+    // Remove the prefix bits.
+    bits = next_bits & 0xffff;
+    // Determine code length.
+    leading_zeroes = 15 - av_log2(bits);
+    if (leading_zeroes >= 6) {
+        // 6 zeroes implies run > 64, which is always invalid.
+        av_log(state->log_ctx, AV_LOG_ERROR,
+               "Out-of-range run value: %d leading zeroes.\n",
+               leading_zeroes);
+        return AVERROR_INVALIDDATA;
+    }
+    // Extract the low bits.
+    low_bit_count = leading_zeroes + k_run;
+    low_bit_shift = 16 - (1 + 2 * leading_zeroes + k_run);
+    low_bits = av_zero_extend(bits >> low_bit_shift, low_bit_count);
+    // Skip over the bits just used.
+    skip_bits(gbc, 2 + leading_zeroes + 1 + low_bit_count);
+    // Construct run code.
+    return (2 << k_run) +
+        ((1 << leading_zeroes) - 1) * (1 << k_run) +
+        low_bits;
+}
+
+// Decode a level code which is too long for the multi-symbol LUT,
+// returning the signed level and setting the absolute value.
+// The 18 bits already read are sufficient for the length, but we may
+// need to read more bits to complete the value.
+av_always_inline
+static int apv_decode_long_level(GetBitContext *restrict gbc,
+                                 uint32_t next_bits, int k_level,
+                                 int *abs_level_out, int scan_pos,
+                                 APVEntropyState *restrict state)
+{
+    uint32_t bits;
+    unsigned int leading_zeroes;
+    int level, abs_level, sign;
+
+    // Remove the prefix bits.
+    bits = next_bits & 0xffff;
+    // Determine code length.
+    leading_zeroes = 15 - av_log2(bits);
+    // Skip the prefix and length bits.
+    skip_bits(gbc, 2 + leading_zeroes + 1);
+    // Read the rest of the code and construct the level.
+    // Include the + 1 offset for nonzero value here.
+    abs_level = (2 << k_level) +
+        ((1 << leading_zeroes) - 1) * (1 << k_level) +
+        get_bits(gbc, leading_zeroes + k_level) + 1;
+
+    sign = get_bits1(gbc);
+    if (sign)
+        level = -abs_level;
+    else
+        level = abs_level;
+
+    // Check range (not checked in any other case, only a long
+    // code can be out of range).
+    if (level < APV_MIN_TRANS_COEFF ||
+        level > APV_MAX_TRANS_COEFF) {
+        av_log(state->log_ctx, AV_LOG_ERROR,
+               "Out-of-range AC coefficient value at %d: %d.\n",
+               scan_pos, level);
+        return AVERROR_INVALIDDATA;
+    }
+    *abs_level_out = abs_level;
+    return level;
+}
+
+// Apply the symbols from a multi-symbol LUT entry.  Both levels are
+// always written: absent ones are zero, and any beyond the end of the
+// block go to the dummy coefficient.  Returns nonzero if the end of
+// the block was reached, in which case only the bits up to the symbol
+// which reached it are consumed.
+av_always_inline
+static int apv_apply_multi_entry(int16_t *restrict coeff,
+                                 GetBitContext *restrict gbc,
+                                 const APVMultiVLCLUTEntry *ent,
+                                 const uint8_t *zigzag,
+                                 int *scan_pos_ptr, int type)
+{
+    int scan_pos = *scan_pos_ptr;
+
+    coeff[zigzag[scan_pos + ent->level_pos[0]]] = ent->level[0];
+    coeff[zigzag[scan_pos + ent->level_pos[1]]] = ent->level[1];
+
+    if (scan_pos + ent->scan_advance >= 64) {
+        // We've gone over the end: find the symbol which reached the
+        // end and stop there.
+        int end[4], i;
+
+        if (type == APV_ENTROPY_RUN_FIRST) {
+            end[0] = ent->level_pos[0];
+            end[1] = ent->level_pos[0] + 1;
+            end[2] = ent->level_pos[1];
+            end[3] = ent->level_pos[1] + 1;
+        } else {
+            end[0] = 1;
+            end[1] = ent->level_pos[1];
+            end[2] = ent->level_pos[1] + 1;
+            end[3] = ent->scan_advance;
+        }
+        for (i = 0; i < ent->count - 1; i++) {
+            if (scan_pos + end[i] >= 64)
+                break;
+        }
+        skip_bits(gbc, ent->offset[i]);
+        *scan_pos_ptr = scan_pos + end[i];
+        return 1;
+    } else {
+        // All codes used: advance the bitstream and scan position by
+        // the full values.
+        skip_bits(gbc, ent->offset[3]);
+        *scan_pos_ptr = scan_pos + ent->scan_advance;
+        return 0;
+    }
 }

 int ff_apv_entropy_decode_block(int16_t *restrict coeff,
@@ -210,7 +323,9 @@ int ff_apv_entropy_decode_block(int16_t *restrict coeff,
                                 APVEntropyState *restrict state)
 {
     const APVVLCLUT *lut = state->decode_lut;
-    int scan_pos;
+    const APVMultiVLCLUTEntry *tables = lut->multi_lut[0][0][0];
+    const uint8_t *zigzag = lut->zigzag;
+    int scan_pos, type, table_index;
     int k_dc = state->prev_k_dc;
     int k_run, k_level;
     uint32_t next_bits, lut_bits;
@@ -256,349 +371,118 @@ int ff_apv_entropy_decode_block(int16_t *restrict coeff,
     // single-symbol LUT is never useful here as the multisymbol lookup
     // has already determined that the code is too long.)

-    // Run a single iteration of the run-first LUT to start, then a
-    // single iteration of the level-first LUT if that only read a
-    // single code.  This avoids dealing with the first-AC logic inside
-    // the normal code lookup sequence.
+    // The first level in the block sets the k_level for the next block,
+    // so run the first lookup (and the second, if the first did not
+    // contain a level) separately.

-    k_level = state->prev_k_level;
+    scan_pos = 1;
+    k_run    = 0;
+    k_level  = state->prev_k_level;
     {
         next_bits = show_bits(gbc, 18);
         lut_bits = next_bits >> (18 - APV_VLC_LUT_BITS);

-        ent = &lut->run_first_lut[0][k_level][lut_bits];
+        ent = &lut->multi_lut[APV_ENTROPY_RUN_FIRST][0][k_level][lut_bits];

         if (ent->count == 0) {
-            // One long code.
-            uint32_t bits, low_bits;
-            unsigned int leading_zeroes, low_bit_count, low_bit_shift;
-            int run;
-
-            // Remove the prefix bits.
-            bits = next_bits & 0xffff;
-            // Determine code length.
-            leading_zeroes = 15 - av_log2(bits);
-            if (leading_zeroes >= 6) {
-                // 6 zeroes implies run > 64, which is always invalid.
-                av_log(state->log_ctx, AV_LOG_ERROR,
-                       "Out-of-range run value: %d leading zeroes.\n",
-                       leading_zeroes);
-                return AVERROR_INVALIDDATA;
-            }
-            // Extract the low bits.
-            low_bit_count = leading_zeroes;
-            low_bit_shift = 16 - (1 + 2 * leading_zeroes);
-            low_bits = av_zero_extend(bits >> low_bit_shift, low_bit_count);
-            // Construct run code.
-            run = 2 + ((1 << leading_zeroes) - 1) + low_bits;
-            // Skip over the bits just used.
-            skip_bits(gbc, 2 + leading_zeroes + 1 + low_bit_count);
-
-            scan_pos = run + 1;
+            int run = apv_decode_long_run(gbc, next_bits, 0, state);
+            if (run < 0)
+                return run;
+            scan_pos += run;
             if (scan_pos >= 64)
                 goto end_of_block;
             k_run = FFMIN(run >> 2, 2);
-            goto first_level;
         } else {
-            // One or more short codes starting with a run; if there is
-            // a level code then the length needs to be saved for the
-            // next block.
-
-            scan_pos = ent->run[0] + 1;
-            if (scan_pos >= 64) {
-                skip_bits(gbc, ent->offset[0]);
-                goto end_of_block;
-            }
-            if (ent->count > 1) {
-                coeff[ff_zigzag_direct[scan_pos]] = ent->level[0];
-                ++scan_pos;
+            // The level is only real if the run before it did not
+            // reach the end of the block.
+            if (ent->count > 1 && scan_pos + ent->level_pos[0] < 64)
                 state->prev_k_level = ent->k_level_0;
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[1]);
-                    goto end_of_block;
-                }
-            }
-            if (ent->count > 2) {
-                scan_pos += ent->run[1];
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[2]);
-                    goto end_of_block;
-                }
-            }
-            if (ent->count > 3) {
-                coeff[ff_zigzag_direct[scan_pos]] = ent->level[1];
-                ++scan_pos;
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[3]);
-                    goto end_of_block;
-                }
-            }
-            skip_bits(gbc, ent->offset[3]);
+            if (apv_apply_multi_entry(coeff, gbc, ent, zigzag,
+                                      &scan_pos, APV_ENTROPY_RUN_FIRST))
+                goto end_of_block;
             k_run   = ent->k_run;
             k_level = ent->k_level_1;
-            if (ent->count == 1)
-                goto first_level;
-            else if (ent->count & 1)
-                goto next_is_level;
-            else
-                goto next_is_run;
+            if (ent->count > 1) {
+                type = APV_ENTROPY_RUN_FIRST ^ (ent->count & 1);
+                goto normal_loop;
+            }
         }
     }

-    first_level: {
+    // Only read a run, still need to read the first level.
+    {
         next_bits = show_bits(gbc, 18);
         lut_bits = next_bits >> (18 - APV_VLC_LUT_BITS);

-        ent = &lut->level_first_lut[k_run][k_level][lut_bits];
+        ent = &lut->multi_lut[APV_ENTROPY_LEVEL_FIRST][k_run][k_level][lut_bits];

         if (ent->count == 0) {
-            // One long code.
-            uint32_t bits;
-            unsigned int leading_zeroes;
-            int level, abs_level, sign;
-
-            // Remove the prefix bits.
-            bits = next_bits & 0xffff;
-            // Determine code length.
-            leading_zeroes = 15 - av_log2(bits);
-            // Skip the prefix and length bits.
-            skip_bits(gbc, 2 + leading_zeroes + 1);
-            // Read the rest of the code and construct the level.
-            // Include the + 1 offset for nonzero value here.
-            abs_level = (2 << k_level) +
-                ((1 << leading_zeroes) - 1) * (1 << k_level) +
-                get_bits(gbc, leading_zeroes + k_level) + 1;
-
-            sign = get_bits(gbc, 1);
-            if (sign)
-                level = -abs_level;
-            else
-                level = abs_level;
-
-            // Check range (not checked in any other case, only a long
-            // code can be out of range).
-            if (level < APV_MIN_TRANS_COEFF ||
-                level > APV_MAX_TRANS_COEFF) {
-                av_log(state->log_ctx, AV_LOG_ERROR,
-                       "Out-of-range AC coefficient value at %d: %d.\n",
-                       scan_pos, level);
-                return AVERROR_INVALIDDATA;
-            }
-            coeff[ff_zigzag_direct[scan_pos]] = level;
+            int level, abs_level;
+            level = apv_decode_long_level(gbc, next_bits, k_level,
+                                          &abs_level, scan_pos, state);
+            if (level < APV_MIN_TRANS_COEFF)
+                return level;
+            coeff[zigzag[scan_pos]] = level;
             ++scan_pos;
             k_level = FFMIN(abs_level >> 2, 4);
             state->prev_k_level = k_level;
             if (scan_pos >= 64)
                 goto end_of_block;
-            goto next_is_run;
-
+            type = APV_ENTROPY_RUN_FIRST;
         } else {
-            // One or more short codes.
-
-            coeff[ff_zigzag_direct[scan_pos]] = ent->level[0];
-            ++scan_pos;
             state->prev_k_level = ent->k_level_0;
-            if (scan_pos >= 64) {
-                skip_bits(gbc, ent->offset[0]);
+            if (apv_apply_multi_entry(coeff, gbc, ent, zigzag,
+                                      &scan_pos, APV_ENTROPY_LEVEL_FIRST))
                 goto end_of_block;
-            }
-            if (ent->count > 1) {
-                scan_pos += ent->run[0];
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[1]);
-                    goto end_of_block;
-                }
-            }
-            if (ent->count > 2) {
-                coeff[ff_zigzag_direct[scan_pos]] = ent->level[1];
-                ++scan_pos;
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[2]);
-                    goto end_of_block;
-                }
-            }
-            if (ent->count > 3) {
-                scan_pos += ent->run[1];
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[3]);
-                    goto end_of_block;
-                }
-            }
-            skip_bits(gbc, ent->offset[3]);
             k_run   = ent->k_run;
             k_level = ent->k_level_1;
-            if (ent->count & 1)
-                goto next_is_run;
-            else
-                goto next_is_level;
+            type = APV_ENTROPY_LEVEL_FIRST ^ (ent->count & 1);
         }
     }

-    next_is_run: {
+normal_loop:
+    // From here the only state carried between lookups is the index of
+    // the table to use, which each entry supplies for the next one.
+    table_index = APV_TABLE_INDEX(type, k_run, k_level);
+    for (;;) {
         next_bits = show_bits(gbc, 18);
         lut_bits = next_bits >> (18 - APV_VLC_LUT_BITS);

-        ent = &lut->run_first_lut[k_run][k_level][lut_bits];
+        ent = &tables[table_index * APV_VLC_LUT_SIZE + lut_bits];

         if (ent->count == 0) {
-            // One long code.
-            uint32_t bits, low_bits;
-            unsigned int leading_zeroes, low_bit_count, low_bit_shift;
-            int run;
-
-            // Remove the prefix bits.
-            bits = next_bits & 0xffff;
-            // Determine code length.
-            leading_zeroes = 15 - av_log2(bits);
-            if (leading_zeroes >= 6) {
-                // 6 zeroes implies run > 64, which is always invalid.
-                av_log(state->log_ctx, AV_LOG_ERROR,
-                       "Out-of-range run value: %d leading zeroes.\n",
-                       leading_zeroes);
-                return AVERROR_INVALIDDATA;
-            }
-            // Extract the low bits.
-            low_bit_count = leading_zeroes + k_run;
-            low_bit_shift = 16 - (1 + 2 * leading_zeroes + k_run);
-            low_bits = av_zero_extend(bits >> low_bit_shift, low_bit_count);
-            // Construct run code.
-            run = (2 << k_run) +
-                ((1 << leading_zeroes) - 1) * (1 << k_run) +
-                low_bits;
-            // Skip over the bits just used.
-            skip_bits(gbc, 2 + leading_zeroes + 1 + low_bit_count);
-
-            scan_pos += run;
-            if (scan_pos >= 64)
-                goto end_of_block;
-            k_run = FFMIN(run >> 2, 2);
-            goto next_is_level;
-
-        } else {
-            // One or more short codes.
-
-            scan_pos += ent->run[0];
-            if (scan_pos >= 64) {
-                skip_bits(gbc, ent->offset[0]);
-                goto end_of_block;
-            }
-            if (ent->count > 1) {
-                coeff[ff_zigzag_direct[scan_pos]] = ent->level[0];
-                ++scan_pos;
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[1]);
-                    goto end_of_block;
-                }
-            }
-            if (ent->count > 2) {
-                scan_pos += ent->run[1];
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[2]);
+            // Missed in the lookup, so need to re-derive the type,
+            // k_run and k_level from the table index.
+            k_run   = table_index / 5 % 3;
+            k_level = table_index % 5;
+            if (table_index < APV_TABLE_INDEX(APV_ENTROPY_LEVEL_FIRST, 0, 0)) {
+                int run = apv_decode_long_run(gbc, next_bits, k_run, state);
+                if (run < 0)
+                    return run;
+                scan_pos += run;
+                if (scan_pos >= 64)
                     goto end_of_block;
-                }
-            }
-            if (ent->count > 3) {
-                coeff[ff_zigzag_direct[scan_pos]] = ent->level[1];
+                k_run = FFMIN(run >> 2, 2);
+                table_index = APV_TABLE_INDEX(APV_ENTROPY_LEVEL_FIRST, k_run, k_level);
+            } else {
+                int level, abs_level;
+                level = apv_decode_long_level(gbc, next_bits, k_level,
+                                              &abs_level, scan_pos, state);
+                if (level < APV_MIN_TRANS_COEFF)
+                    return level;
+                coeff[zigzag[scan_pos]] = level;
                 ++scan_pos;
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[3]);
+                k_level = FFMIN(abs_level >> 2, 4);
+                if (scan_pos >= 64)
                     goto end_of_block;
-                }
+                table_index = APV_TABLE_INDEX(APV_ENTROPY_RUN_FIRST, k_run, k_level);
             }
-            skip_bits(gbc, ent->offset[3]);
-            k_run   = ent->k_run;
-            k_level = ent->k_level_1;
-            if (ent->count & 1)
-                goto next_is_level;
-            else
-                goto next_is_run;
-        }
-    }
-
-    next_is_level: {
-        next_bits = show_bits(gbc, 18);
-        lut_bits = next_bits >> (18 - APV_VLC_LUT_BITS);
-
-        ent = &lut->level_first_lut[k_run][k_level][lut_bits];
-
-        if (ent->count == 0) {
-            // One long code.
-            uint32_t bits;
-            unsigned int leading_zeroes;
-            int level, abs_level, sign;
-
-            // Remove the prefix bits.
-            bits = next_bits & 0xffff;
-            // Determine code length.
-            leading_zeroes = 15 - av_log2(bits);
-            // Skip the prefix and length bits.
-            skip_bits(gbc, 2 + leading_zeroes + 1);
-            // Read the rest of the code and construct the level.
-            // Include the + 1 offset for nonzero value here.
-            abs_level = (2 << k_level) +
-                ((1 << leading_zeroes) - 1) * (1 << k_level) +
-                get_bits(gbc, leading_zeroes + k_level) + 1;
-
-            sign = get_bits(gbc, 1);
-            if (sign)
-                level = -abs_level;
-            else
-                level = abs_level;
-
-            // Check range (not checked in any other case, only a long
-            // code can be out of range).
-            if (level < APV_MIN_TRANS_COEFF ||
-                level > APV_MAX_TRANS_COEFF) {
-                av_log(state->log_ctx, AV_LOG_ERROR,
-                       "Out-of-range AC coefficient value at %d: %d.\n",
-                       scan_pos, level);
-                return AVERROR_INVALIDDATA;
-            }
-            coeff[ff_zigzag_direct[scan_pos]] = level;
-            ++scan_pos;
-            k_level = FFMIN(abs_level >> 2, 4);
-            if (scan_pos >= 64)
-                goto end_of_block;
-            goto next_is_run;
-
         } else {
-            // One or more short codes.
-
-            coeff[ff_zigzag_direct[scan_pos]] = ent->level[0];
-            ++scan_pos;
-            if (scan_pos >= 64) {
-                skip_bits(gbc, ent->offset[0]);
+            type = table_index >= APV_TABLE_INDEX(APV_ENTROPY_LEVEL_FIRST, 0, 0);
+            if (apv_apply_multi_entry(coeff, gbc, ent, zigzag,
+                                      &scan_pos, type))
                 goto end_of_block;
-            }
-            if (ent->count > 1) {
-                scan_pos += ent->run[0];
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[1]);
-                    goto end_of_block;
-                }
-            }
-            if (ent->count > 2) {
-                coeff[ff_zigzag_direct[scan_pos]] = ent->level[1];
-                ++scan_pos;
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[2]);
-                    goto end_of_block;
-                }
-            }
-            if (ent->count > 3) {
-                scan_pos += ent->run[1];
-                if (scan_pos >= 64) {
-                    skip_bits(gbc, ent->offset[3]);
-                    goto end_of_block;
-                }
-            }
-            skip_bits(gbc, ent->offset[3]);
-            k_run   = ent->k_run;
-            k_level = ent->k_level_1;
-            if (ent->count & 1)
-                goto next_is_run;
-            else
-                goto next_is_level;
+            table_index = ent->next;
         }
     }

diff --git a/libavcodec/tests/apv.c b/libavcodec/tests/apv.c
index 365a395af1..2f57539a1d 100644
--- a/libavcodec/tests/apv.c
+++ b/libavcodec/tests/apv.c
@@ -313,7 +313,7 @@ static int test_apv_entropy_decode_block(void)
         APVEntropyState state, save_state;
         int16_t block[64];
         int16_t block_test1[64];
-        int16_t block_test2[64];
+        int16_t block_test2[APV_ENTROPY_COEFF_SIZE];
         uint8_t buffer[1024];
         PutBitContext pbc;
         GetBitContext gbc;