libavcodec/dirac_vlc.c
0eb0f931
 /*
  * Copyright (C) 2016 Open Broadcast Systems Ltd.
  * Author        2016 Rostislav Pehlivanov <rpehlivanov@obe.tv>
  *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
  * FFmpeg is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 #include "dirac_vlc.h"
 
 #define LUT_SIZE   (1 << LUT_BITS)
 #define RSIZE_BITS (CHAR_BIT*sizeof(residual))
 
 #define CONVERT_TO_RESIDUE(a, b)                                               \
     (((residual)(a)) << (RSIZE_BITS - (b)))
 
c4adcff9
 #define INIT_RESIDUE(N)                                                        \
     residual N = 0;                                                            \
     av_unused int32_t N ## _bits  = 0
 
 #define SET_RESIDUE(N, I, B)                                                   \
     N          = CONVERT_TO_RESIDUE(I, B);                                     \
     N ## _bits = B
0eb0f931
 
9c0aba43
 #define APPEND_RESIDUE(N, M)                                                   \
     N          |= M >> (N ## _bits);                                           \
d98d29a7
     N ## _bits  = (N ## _bits + (M ## _bits)) & 0x3F
9c0aba43
 
0eb0f931
 int ff_dirac_golomb_read_32bit(DiracGolombLUT *lut_ctx, const uint8_t *buf,
                                int bytes, uint8_t *_dst, int coeffs)
 {
     int i, b, c_idx = 0;
     int32_t *dst = (int32_t *)_dst;
     DiracGolombLUT *future[4], *l = &lut_ctx[2*LUT_SIZE + buf[0]];
c4adcff9
     INIT_RESIDUE(res);
0eb0f931
 
     for (b = 1; b <= bytes; b++) {
         future[0] = &lut_ctx[buf[b]];
         future[1] = future[0] + 1*LUT_SIZE;
         future[2] = future[0] + 2*LUT_SIZE;
         future[3] = future[0] + 3*LUT_SIZE;
 
         if ((c_idx + 1) > coeffs)
             return c_idx;
 
         /* res_bits is a hint for better branch prediction */
         if (res_bits && l->sign) {
             int32_t coeff = 1;
             APPEND_RESIDUE(res, l->preamble);
             for (i = 0; i < (res_bits >> 1) - 1; i++) {
                 coeff <<= 1;
                 coeff |= (res >> (RSIZE_BITS - 2*i - 2)) & 1;
             }
             dst[c_idx++] = l->sign * (coeff - 1);
f41e37b8
             res_bits = res = 0;
0eb0f931
         }
 
         memcpy(&dst[c_idx], l->ready, LUT_BITS*sizeof(int32_t));
         c_idx += l->ready_num;
 
         APPEND_RESIDUE(res, l->leftover);
 
         l = future[l->need_s ? 3 : !res_bits ? 2 : res_bits & 1];
     }
 
     return c_idx;
 }
 
 int ff_dirac_golomb_read_16bit(DiracGolombLUT *lut_ctx, const uint8_t *buf,
                                int bytes, uint8_t *_dst, int coeffs)
 {
     int i, b, c_idx = 0;
     int16_t *dst = (int16_t *)_dst;
     DiracGolombLUT *future[4], *l = &lut_ctx[2*LUT_SIZE + buf[0]];
c4adcff9
     INIT_RESIDUE(res);
0eb0f931
 
     for (b = 1; b <= bytes; b++) {
         future[0] = &lut_ctx[buf[b]];
         future[1] = future[0] + 1*LUT_SIZE;
         future[2] = future[0] + 2*LUT_SIZE;
         future[3] = future[0] + 3*LUT_SIZE;
 
         if ((c_idx + 1) > coeffs)
             return c_idx;
 
         if (res_bits && l->sign) {
             int32_t coeff = 1;
             APPEND_RESIDUE(res, l->preamble);
             for (i = 0; i < (res_bits >> 1) - 1; i++) {
                 coeff <<= 1;
                 coeff |= (res >> (RSIZE_BITS - 2*i - 2)) & 1;
             }
             dst[c_idx++] = l->sign * (coeff - 1);
f41e37b8
             res_bits = res = 0;
0eb0f931
         }
 
         for (i = 0; i < LUT_BITS; i++)
             dst[c_idx + i] = l->ready[i];
         c_idx += l->ready_num;
 
         APPEND_RESIDUE(res, l->leftover);
 
         l = future[l->need_s ? 3 : !res_bits ? 2 : res_bits & 1];
     }
 
     return c_idx;
 }
 
 /* Searches for golomb codes in a residue */
 static inline void search_for_golomb(DiracGolombLUT *l, residual r, int bits)
 {
     int r_count = RSIZE_BITS - 1;
     int bits_start, bits_tot = bits, need_sign = 0;
 
 #define READ_BIT(N) (((N) >> (N ## _count--)) & 1)
 
     while (1) {
         int32_t coef = 1;
         bits_start = (RSIZE_BITS - 1) - r_count;
 
         while (1) {
             if (!bits--)
                 goto leftover;
             if (READ_BIT(r))
                 break;
 
             coef <<= 1;
 
             if (!bits--)
                 goto leftover;
             coef |= READ_BIT(r);
         }
 
         l->ready[l->ready_num] = coef - 1;
         if (l->ready[l->ready_num]) {
             if (!bits--) {
                 need_sign = 1;
                 goto leftover;
             }
             l->ready[l->ready_num] *= READ_BIT(r) ? -1 : +1;
         }
         l->ready_num++;
 
         if (!bits)
             return;
     }
 
     leftover:
         l->leftover      = r << bits_start;
         l->leftover_bits = bits_tot - bits_start;
         l->need_s        = need_sign;
 }
 
 /* Parity LUTs - even and odd bit end positions */
 static void generate_parity_lut(DiracGolombLUT *lut, int even)
 {
9157ac2f
     int idx;
     for (idx = 0; idx < LUT_SIZE; idx++) {
0eb0f931
         DiracGolombLUT *l = &lut[idx];
         int symbol_end_loc = -1;
         uint32_t code;
9157ac2f
         int i;
0eb0f931
 
c4adcff9
         INIT_RESIDUE(res);
         SET_RESIDUE(res, idx, LUT_BITS);
0eb0f931
 
9157ac2f
         for (i = 0; i < LUT_BITS; i++) {
0eb0f931
             const int cond = even ? (i & 1) : !(i & 1);
             if (((res >> (RSIZE_BITS - i - 1)) & 1) && cond) {
                 symbol_end_loc = i + 2;
                 break;
             }
         }
 
         if (symbol_end_loc < 0 || symbol_end_loc > LUT_BITS) {
             l->preamble      = 0;
             l->preamble_bits = 0;
             l->leftover_bits = LUT_BITS;
             l->leftover      = CONVERT_TO_RESIDUE(idx, l->leftover_bits);
             if (even)
                 l->need_s    = idx & 1;
             continue;
         }
 
         /* Gets bits 0 through to (symbol_end_loc - 1) inclusive */
         code  = idx >> ((LUT_BITS - 1) - (symbol_end_loc - 1));
         code &= ((1 << LUT_BITS) - 1) >> (LUT_BITS - symbol_end_loc);
         l->preamble_bits = symbol_end_loc;
         l->preamble      = CONVERT_TO_RESIDUE(code, l->preamble_bits);
         l->sign = ((l->preamble >> (RSIZE_BITS - l->preamble_bits)) & 1) ? -1 : +1;
 
         search_for_golomb(l, res << symbol_end_loc, LUT_BITS - symbol_end_loc);
     }
 }
 
 /* Reset (off == 0) and needs-one-more-bit (off == 1) LUTs */
 static void generate_offset_lut(DiracGolombLUT *lut, int off)
 {
9157ac2f
     int idx;
     for (idx = 0; idx < LUT_SIZE; idx++) {
0eb0f931
         DiracGolombLUT *l = &lut[idx];
 
c4adcff9
         INIT_RESIDUE(res);
         SET_RESIDUE(res, idx, LUT_BITS);
0eb0f931
 
         l->preamble_bits = off;
69e7daf6
         if (off) {
             l->preamble  = CONVERT_TO_RESIDUE(res >> (RSIZE_BITS - off), off);
             l->sign      = ((l->preamble >> (RSIZE_BITS - l->preamble_bits)) & 1) ? -1 : +1;
         } else {
             l->preamble  = 0;
             l->sign = 1;
         }
0eb0f931
 
         search_for_golomb(l, res << off, LUT_BITS - off);
     }
 }
 
 av_cold int ff_dirac_golomb_reader_init(DiracGolombLUT **lut_ctx)
 {
     DiracGolombLUT *lut;
 
     if (!(lut = av_calloc(4*LUT_SIZE, sizeof(DiracGolombLUT))))
         return AVERROR(ENOMEM);
 
     generate_parity_lut(&lut[0*LUT_SIZE], 0);
     generate_parity_lut(&lut[1*LUT_SIZE], 1);
     generate_offset_lut(&lut[2*LUT_SIZE], 0);
     generate_offset_lut(&lut[3*LUT_SIZE], 1);
 
     *lut_ctx = lut;
 
     return 0;
 }
 
 av_cold void ff_dirac_golomb_reader_end(DiracGolombLUT **lut_ctx)
 {
     av_freep(lut_ctx);
 }