tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tools_common.c (20198B)


      1 /*
      2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved.
      3 *
      4 * This source code is subject to the terms of the BSD 2 Clause License and
      5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
      6 * was not distributed with this source code in the LICENSE file, you can
      7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
      8 * Media Patent License 1.0 was not distributed with this source code in the
      9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
     10 */
     11 
     12 #include <assert.h>
     13 #include <math.h>
     14 #include <stdarg.h>
     15 #include <stdio.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 
     19 #include "common/tools_common.h"
     20 
     21 #if CONFIG_AV1_ENCODER
     22 #include "aom/aomcx.h"
     23 #endif
     24 
     25 #if CONFIG_AV1_DECODER
     26 #include "aom/aomdx.h"
     27 #endif
     28 
     29 #if defined(_WIN32)
     30 #include <io.h>
     31 #include <fcntl.h>
     32 #endif
     33 
     34 #define LOG_ERROR(label)               \
     35  do {                                 \
     36    const char *l = label;             \
     37    va_list ap;                        \
     38    va_start(ap, fmt);                 \
     39    if (l) fprintf(stderr, "%s: ", l); \
     40    vfprintf(stderr, fmt, ap);         \
     41    fprintf(stderr, "\n");             \
     42    va_end(ap);                        \
     43  } while (0)
     44 
     45 FILE *set_binary_mode(FILE *stream) {
     46  (void)stream;
     47 #if defined(_WIN32)
     48  _setmode(_fileno(stream), _O_BINARY);
     49 #endif
     50  return stream;
     51 }
     52 
     53 void die(const char *fmt, ...) {
     54  LOG_ERROR(NULL);
     55  usage_exit();
     56 }
     57 
     58 void fatal(const char *fmt, ...) {
     59  LOG_ERROR("Fatal");
     60  exit(EXIT_FAILURE);
     61 }
     62 
     63 void aom_tools_warn(const char *fmt, ...) { LOG_ERROR("Warning"); }
     64 
     65 void die_codec(aom_codec_ctx_t *ctx, const char *s) {
     66  const char *detail = aom_codec_error_detail(ctx);
     67 
     68  fprintf(stderr, "%s: %s\n", s, aom_codec_error(ctx));
     69  if (detail) fprintf(stderr, "    %s\n", detail);
     70  exit(EXIT_FAILURE);
     71 }
     72 
     73 const char *image_format_to_string(aom_img_fmt_t fmt) {
     74  switch (fmt) {
     75    case AOM_IMG_FMT_I420: return "I420";
     76    case AOM_IMG_FMT_I422: return "I422";
     77    case AOM_IMG_FMT_I444: return "I444";
     78    case AOM_IMG_FMT_YV12: return "YV12";
     79    case AOM_IMG_FMT_NV12: return "NV12";
     80    case AOM_IMG_FMT_YV1216: return "YV1216";
     81    case AOM_IMG_FMT_I42016: return "I42016";
     82    case AOM_IMG_FMT_I42216: return "I42216";
     83    case AOM_IMG_FMT_I44416: return "I44416";
     84    default: return "Other";
     85  }
     86 }
     87 
     88 int read_yuv_frame(struct AvxInputContext *input_ctx, aom_image_t *yuv_frame) {
     89  FILE *f = input_ctx->file;
     90  struct FileTypeDetectionBuffer *detect = &input_ctx->detect;
     91  int plane = 0;
     92  int shortread = 0;
     93  const int bytespp = (yuv_frame->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
     94 
     95  for (plane = 0; plane < 3; ++plane) {
     96    uint8_t *ptr;
     97    int w = aom_img_plane_width(yuv_frame, plane);
     98    const int h = aom_img_plane_height(yuv_frame, plane);
     99    int r;
    100    // Assuming that for nv12 we read all chroma data at once
    101    if (yuv_frame->fmt == AOM_IMG_FMT_NV12 && plane > 1) break;
    102    if (yuv_frame->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2;
    103    /* Determine the correct plane based on the image format. The for-loop
    104     * always counts in Y,U,V order, but this may not match the order of
    105     * the data on disk.
    106     */
    107    switch (plane) {
    108      case 1:
    109        ptr =
    110            yuv_frame->planes[yuv_frame->fmt == AOM_IMG_FMT_YV12 ? AOM_PLANE_V
    111                                                                 : AOM_PLANE_U];
    112        break;
    113      case 2:
    114        ptr =
    115            yuv_frame->planes[yuv_frame->fmt == AOM_IMG_FMT_YV12 ? AOM_PLANE_U
    116                                                                 : AOM_PLANE_V];
    117        break;
    118      default: ptr = yuv_frame->planes[plane];
    119    }
    120 
    121    for (r = 0; r < h; ++r) {
    122      size_t needed = w * bytespp;
    123      size_t buf_position = 0;
    124      const size_t left = detect->buf_read - detect->position;
    125      if (left > 0) {
    126        const size_t more = (left < needed) ? left : needed;
    127        memcpy(ptr, detect->buf + detect->position, more);
    128        buf_position = more;
    129        needed -= more;
    130        detect->position += more;
    131      }
    132      if (needed > 0) {
    133        shortread |= (fread(ptr + buf_position, 1, needed, f) < needed);
    134      }
    135 
    136      ptr += yuv_frame->stride[plane];
    137    }
    138  }
    139 
    140  return shortread;
    141 }
    142 
    143 struct CodecInfo {
    144  // Pointer to a function of zero arguments that returns an aom_codec_iface_t.
    145  aom_codec_iface_t *(*interface)(void);
    146  const char *short_name;
    147  uint32_t fourcc;
    148 };
    149 
    150 #if CONFIG_AV1_ENCODER
    151 static const struct CodecInfo aom_encoders[] = {
    152  { &aom_codec_av1_cx, "av1", AV1_FOURCC },
    153 };
    154 
    155 int get_aom_encoder_count(void) {
    156  return sizeof(aom_encoders) / sizeof(aom_encoders[0]);
    157 }
    158 
    159 aom_codec_iface_t *get_aom_encoder_by_index(int i) {
    160  assert(i >= 0 && i < get_aom_encoder_count());
    161  return aom_encoders[i].interface();
    162 }
    163 
    164 aom_codec_iface_t *get_aom_encoder_by_short_name(const char *name) {
    165  for (int i = 0; i < get_aom_encoder_count(); ++i) {
    166    const struct CodecInfo *info = &aom_encoders[i];
    167    if (strcmp(info->short_name, name) == 0) return info->interface();
    168  }
    169  return NULL;
    170 }
    171 
    172 uint32_t get_fourcc_by_aom_encoder(aom_codec_iface_t *iface) {
    173  for (int i = 0; i < get_aom_encoder_count(); ++i) {
    174    const struct CodecInfo *info = &aom_encoders[i];
    175    if (info->interface() == iface) {
    176      return info->fourcc;
    177    }
    178  }
    179  return 0;
    180 }
    181 
    182 const char *get_short_name_by_aom_encoder(aom_codec_iface_t *iface) {
    183  for (int i = 0; i < get_aom_encoder_count(); ++i) {
    184    const struct CodecInfo *info = &aom_encoders[i];
    185    if (info->interface() == iface) {
    186      return info->short_name;
    187    }
    188  }
    189  return NULL;
    190 }
    191 
    192 #endif  // CONFIG_AV1_ENCODER
    193 
    194 #if CONFIG_AV1_DECODER
    195 static const struct CodecInfo aom_decoders[] = {
    196  { &aom_codec_av1_dx, "av1", AV1_FOURCC },
    197 };
    198 
    199 int get_aom_decoder_count(void) {
    200  return sizeof(aom_decoders) / sizeof(aom_decoders[0]);
    201 }
    202 
    203 aom_codec_iface_t *get_aom_decoder_by_index(int i) {
    204  assert(i >= 0 && i < get_aom_decoder_count());
    205  return aom_decoders[i].interface();
    206 }
    207 
    208 aom_codec_iface_t *get_aom_decoder_by_short_name(const char *name) {
    209  for (int i = 0; i < get_aom_decoder_count(); ++i) {
    210    const struct CodecInfo *info = &aom_decoders[i];
    211    if (strcmp(info->short_name, name) == 0) return info->interface();
    212  }
    213  return NULL;
    214 }
    215 
    216 aom_codec_iface_t *get_aom_decoder_by_fourcc(uint32_t fourcc) {
    217  for (int i = 0; i < get_aom_decoder_count(); ++i) {
    218    const struct CodecInfo *info = &aom_decoders[i];
    219    if (info->fourcc == fourcc) return info->interface();
    220  }
    221  return NULL;
    222 }
    223 
    224 const char *get_short_name_by_aom_decoder(aom_codec_iface_t *iface) {
    225  for (int i = 0; i < get_aom_decoder_count(); ++i) {
    226    const struct CodecInfo *info = &aom_decoders[i];
    227    if (info->interface() == iface) {
    228      return info->short_name;
    229    }
    230  }
    231  return NULL;
    232 }
    233 
    234 uint32_t get_fourcc_by_aom_decoder(aom_codec_iface_t *iface) {
    235  for (int i = 0; i < get_aom_decoder_count(); ++i) {
    236    const struct CodecInfo *info = &aom_decoders[i];
    237    if (info->interface() == iface) {
    238      return info->fourcc;
    239    }
    240  }
    241  return 0;
    242 }
    243 
    244 #endif  // CONFIG_AV1_DECODER
    245 
    246 void aom_img_write(const aom_image_t *img, FILE *file) {
    247  int plane;
    248  const int bytespp = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
    249 
    250  for (plane = 0; plane < 3; ++plane) {
    251    const unsigned char *buf = img->planes[plane];
    252    const int stride = img->stride[plane];
    253    int w = aom_img_plane_width(img, plane);
    254    const int h = aom_img_plane_height(img, plane);
    255    int y;
    256 
    257    // Assuming that for nv12 we write all chroma data at once
    258    if (img->fmt == AOM_IMG_FMT_NV12 && plane > 1) break;
    259    if (img->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2;
    260 
    261    for (y = 0; y < h; ++y) {
    262      fwrite(buf, bytespp, w, file);
    263      buf += stride;
    264    }
    265  }
    266 }
    267 
    268 bool aom_img_read(aom_image_t *img, FILE *file) {
    269  int plane;
    270  const int bytespp = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
    271 
    272  for (plane = 0; plane < 3; ++plane) {
    273    unsigned char *buf = img->planes[plane];
    274    const int stride = img->stride[plane];
    275    int w = aom_img_plane_width(img, plane);
    276    const int h = aom_img_plane_height(img, plane);
    277    int y;
    278 
    279    // Assuming that for nv12 we read all chroma data at once
    280    if (img->fmt == AOM_IMG_FMT_NV12 && plane > 1) break;
    281    if (img->fmt == AOM_IMG_FMT_NV12 && plane == 1) w *= 2;
    282 
    283    for (y = 0; y < h; ++y) {
    284      if (fread(buf, bytespp, w, file) != (size_t)w) return false;
    285      buf += stride;
    286    }
    287  }
    288 
    289  return true;
    290 }
    291 
    292 // TODO(dkovalev) change sse_to_psnr signature: double -> int64_t
    293 double sse_to_psnr(double samples, double peak, double sse) {
    294  static const double kMaxPSNR = 100.0;
    295 
    296  if (sse > 0.0) {
    297    const double psnr = 10.0 * log10(samples * peak * peak / sse);
    298    return psnr > kMaxPSNR ? kMaxPSNR : psnr;
    299  } else {
    300    return kMaxPSNR;
    301  }
    302 }
    303 
    304 // TODO(debargha): Consolidate the functions below into a separate file.
    305 static void highbd_img_upshift(aom_image_t *dst, const aom_image_t *src,
    306                               int input_shift) {
    307  // Note the offset is 1 less than half.
    308  const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
    309  int plane;
    310  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
    311      dst->x_chroma_shift != src->x_chroma_shift ||
    312      dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt ||
    313      input_shift < 0) {
    314    fatal("Unsupported image conversion");
    315  }
    316  switch (src->fmt) {
    317    case AOM_IMG_FMT_I42016:
    318    case AOM_IMG_FMT_I42216:
    319    case AOM_IMG_FMT_I44416: break;
    320    default: fatal("Unsupported image conversion");
    321  }
    322  for (plane = 0; plane < 3; plane++) {
    323    int w = src->d_w;
    324    int h = src->d_h;
    325    int x, y;
    326    if (plane) {
    327      w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    328      h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    329    }
    330    for (y = 0; y < h; y++) {
    331      const uint16_t *p_src =
    332          (const uint16_t *)(src->planes[plane] + y * src->stride[plane]);
    333      uint16_t *p_dst =
    334          (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
    335      for (x = 0; x < w; x++) *p_dst++ = (*p_src++ << input_shift) + offset;
    336    }
    337  }
    338 }
    339 
    340 static void lowbd_img_upshift(aom_image_t *dst, const aom_image_t *src,
    341                              int input_shift) {
    342  // Note the offset is 1 less than half.
    343  const int offset = input_shift > 0 ? (1 << (input_shift - 1)) - 1 : 0;
    344  int plane;
    345  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
    346      dst->x_chroma_shift != src->x_chroma_shift ||
    347      dst->y_chroma_shift != src->y_chroma_shift ||
    348      dst->fmt != src->fmt + AOM_IMG_FMT_HIGHBITDEPTH || input_shift < 0) {
    349    fatal("Unsupported image conversion");
    350  }
    351  switch (src->fmt) {
    352    case AOM_IMG_FMT_YV12:
    353    case AOM_IMG_FMT_I420:
    354    case AOM_IMG_FMT_I422:
    355    case AOM_IMG_FMT_I444: break;
    356    default: fatal("Unsupported image conversion");
    357  }
    358  for (plane = 0; plane < 3; plane++) {
    359    int w = src->d_w;
    360    int h = src->d_h;
    361    int x, y;
    362    if (plane) {
    363      w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    364      h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    365    }
    366    for (y = 0; y < h; y++) {
    367      const uint8_t *p_src = src->planes[plane] + y * src->stride[plane];
    368      uint16_t *p_dst =
    369          (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
    370      for (x = 0; x < w; x++) {
    371        *p_dst++ = (*p_src++ << input_shift) + offset;
    372      }
    373    }
    374  }
    375 }
    376 
    377 void aom_img_upshift(aom_image_t *dst, const aom_image_t *src,
    378                     int input_shift) {
    379  if (src->fmt & AOM_IMG_FMT_HIGHBITDEPTH) {
    380    highbd_img_upshift(dst, src, input_shift);
    381  } else {
    382    lowbd_img_upshift(dst, src, input_shift);
    383  }
    384 }
    385 
    386 void aom_img_truncate_16_to_8(aom_image_t *dst, const aom_image_t *src) {
    387  int plane;
    388  if (dst->fmt + AOM_IMG_FMT_HIGHBITDEPTH != src->fmt || dst->d_w != src->d_w ||
    389      dst->d_h != src->d_h || dst->x_chroma_shift != src->x_chroma_shift ||
    390      dst->y_chroma_shift != src->y_chroma_shift) {
    391    fatal("Unsupported image conversion");
    392  }
    393  switch (dst->fmt) {
    394    case AOM_IMG_FMT_I420:
    395    case AOM_IMG_FMT_I422:
    396    case AOM_IMG_FMT_I444: break;
    397    default: fatal("Unsupported image conversion");
    398  }
    399  for (plane = 0; plane < 3; plane++) {
    400    int w = src->d_w;
    401    int h = src->d_h;
    402    int x, y;
    403    if (plane) {
    404      w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    405      h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    406    }
    407    for (y = 0; y < h; y++) {
    408      const uint16_t *p_src =
    409          (const uint16_t *)(src->planes[plane] + y * src->stride[plane]);
    410      uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
    411      for (x = 0; x < w; x++) {
    412        *p_dst++ = (uint8_t)(*p_src++);
    413      }
    414    }
    415  }
    416 }
    417 
    418 static void highbd_img_downshift(aom_image_t *dst, const aom_image_t *src,
    419                                 int down_shift) {
    420  int plane;
    421  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
    422      dst->x_chroma_shift != src->x_chroma_shift ||
    423      dst->y_chroma_shift != src->y_chroma_shift || dst->fmt != src->fmt ||
    424      down_shift < 0) {
    425    fatal("Unsupported image conversion");
    426  }
    427  switch (src->fmt) {
    428    case AOM_IMG_FMT_I42016:
    429    case AOM_IMG_FMT_I42216:
    430    case AOM_IMG_FMT_I44416: break;
    431    default: fatal("Unsupported image conversion");
    432  }
    433  for (plane = 0; plane < 3; plane++) {
    434    int w = src->d_w;
    435    int h = src->d_h;
    436    int x, y;
    437    if (plane) {
    438      w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    439      h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    440    }
    441    for (y = 0; y < h; y++) {
    442      const uint16_t *p_src =
    443          (const uint16_t *)(src->planes[plane] + y * src->stride[plane]);
    444      uint16_t *p_dst =
    445          (uint16_t *)(dst->planes[plane] + y * dst->stride[plane]);
    446      for (x = 0; x < w; x++) *p_dst++ = *p_src++ >> down_shift;
    447    }
    448  }
    449 }
    450 
    451 static void lowbd_img_downshift(aom_image_t *dst, const aom_image_t *src,
    452                                int down_shift) {
    453  int plane;
    454  if (dst->d_w != src->d_w || dst->d_h != src->d_h ||
    455      dst->x_chroma_shift != src->x_chroma_shift ||
    456      dst->y_chroma_shift != src->y_chroma_shift ||
    457      src->fmt != dst->fmt + AOM_IMG_FMT_HIGHBITDEPTH || down_shift < 0) {
    458    fatal("Unsupported image conversion");
    459  }
    460  switch (dst->fmt) {
    461    case AOM_IMG_FMT_I420:
    462    case AOM_IMG_FMT_I422:
    463    case AOM_IMG_FMT_I444: break;
    464    default: fatal("Unsupported image conversion");
    465  }
    466  for (plane = 0; plane < 3; plane++) {
    467    int w = src->d_w;
    468    int h = src->d_h;
    469    int x, y;
    470    if (plane) {
    471      w = (w + src->x_chroma_shift) >> src->x_chroma_shift;
    472      h = (h + src->y_chroma_shift) >> src->y_chroma_shift;
    473    }
    474    for (y = 0; y < h; y++) {
    475      const uint16_t *p_src =
    476          (const uint16_t *)(src->planes[plane] + y * src->stride[plane]);
    477      uint8_t *p_dst = dst->planes[plane] + y * dst->stride[plane];
    478      for (x = 0; x < w; x++) {
    479        *p_dst++ = *p_src++ >> down_shift;
    480      }
    481    }
    482  }
    483 }
    484 
    485 void aom_img_downshift(aom_image_t *dst, const aom_image_t *src,
    486                       int down_shift) {
    487  if (dst->fmt & AOM_IMG_FMT_HIGHBITDEPTH) {
    488    highbd_img_downshift(dst, src, down_shift);
    489  } else {
    490    lowbd_img_downshift(dst, src, down_shift);
    491  }
    492 }
    493 
    494 static int img_shifted_realloc_required(const aom_image_t *img,
    495                                        const aom_image_t *shifted,
    496                                        aom_img_fmt_t required_fmt) {
    497  return img->d_w != shifted->d_w || img->d_h != shifted->d_h ||
    498         required_fmt != shifted->fmt;
    499 }
    500 
    501 bool aom_shift_img(unsigned int output_bit_depth, aom_image_t **img_ptr,
    502                   aom_image_t **img_shifted_ptr) {
    503  aom_image_t *img = *img_ptr;
    504  aom_image_t *img_shifted = *img_shifted_ptr;
    505 
    506  const aom_img_fmt_t shifted_fmt = output_bit_depth == 8
    507                                        ? img->fmt & ~AOM_IMG_FMT_HIGHBITDEPTH
    508                                        : img->fmt | AOM_IMG_FMT_HIGHBITDEPTH;
    509 
    510  if (shifted_fmt != img->fmt || output_bit_depth != img->bit_depth) {
    511    if (img_shifted &&
    512        img_shifted_realloc_required(img, img_shifted, shifted_fmt)) {
    513      aom_img_free(img_shifted);
    514      img_shifted = NULL;
    515    }
    516    if (img_shifted) {
    517      img_shifted->monochrome = img->monochrome;
    518    }
    519    if (!img_shifted) {
    520      img_shifted = aom_img_alloc(NULL, shifted_fmt, img->d_w, img->d_h, 16);
    521      if (!img_shifted) {
    522        *img_shifted_ptr = NULL;
    523        return false;
    524      }
    525      img_shifted->bit_depth = output_bit_depth;
    526      img_shifted->monochrome = img->monochrome;
    527      img_shifted->csp = img->csp;
    528    }
    529    if (output_bit_depth > img->bit_depth) {
    530      aom_img_upshift(img_shifted, img, output_bit_depth - img->bit_depth);
    531    } else {
    532      aom_img_downshift(img_shifted, img, img->bit_depth - output_bit_depth);
    533    }
    534    *img_shifted_ptr = img_shifted;
    535    *img_ptr = img_shifted;
    536  }
    537 
    538  return true;
    539 }
    540 
    541 // Related to I420, NV12 format has one luma "luminance" plane Y and one plane
    542 // with U and V values interleaved.
    543 void aom_img_write_nv12(const aom_image_t *img, FILE *file) {
    544  // Y plane
    545  const unsigned char *buf = img->planes[0];
    546  int stride = img->stride[0];
    547  int w = aom_img_plane_width(img, 0) *
    548          ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
    549  int h = aom_img_plane_height(img, 0);
    550  int x, y;
    551 
    552  for (y = 0; y < h; ++y) {
    553    fwrite(buf, 1, w, file);
    554    buf += stride;
    555  }
    556 
    557  // Interleaved U and V plane
    558  const unsigned char *ubuf = img->planes[1];
    559  const unsigned char *vbuf = img->planes[2];
    560  const size_t size = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
    561  stride = img->stride[1];
    562  w = aom_img_plane_width(img, 1);
    563  h = aom_img_plane_height(img, 1);
    564 
    565  for (y = 0; y < h; ++y) {
    566    for (x = 0; x < w; ++x) {
    567      fwrite(ubuf, size, 1, file);
    568      fwrite(vbuf, size, 1, file);
    569      ubuf += size;
    570      vbuf += size;
    571    }
    572    ubuf += (stride - w * size);
    573    vbuf += (stride - w * size);
    574  }
    575 }
    576 
    577 size_t read_from_input(struct AvxInputContext *input_ctx, size_t n,
    578                       unsigned char *buf) {
    579  const size_t buffered_bytes =
    580      input_ctx->detect.buf_read - input_ctx->detect.position;
    581  size_t read_n;
    582  if (buffered_bytes == 0) {
    583    read_n = fread(buf, 1, n, input_ctx->file);
    584  } else if (n <= buffered_bytes) {
    585    memcpy(buf, input_ctx->detect.buf + input_ctx->detect.position, n);
    586    input_ctx->detect.position += n;
    587    read_n = n;
    588  } else {
    589    memcpy(buf, input_ctx->detect.buf + input_ctx->detect.position,
    590           buffered_bytes);
    591    input_ctx->detect.position += buffered_bytes;
    592    read_n = buffered_bytes;
    593    read_n +=
    594        fread(buf + buffered_bytes, 1, n - buffered_bytes, input_ctx->file);
    595  }
    596  return read_n;
    597 }
    598 
    599 size_t input_to_detect_buf(struct AvxInputContext *input_ctx, size_t n) {
    600  if (n + input_ctx->detect.position > DETECT_BUF_SZ) {
    601    die("Failed to store in the detect buffer, maximum size exceeded.");
    602  }
    603  const size_t buffered_bytes =
    604      input_ctx->detect.buf_read - input_ctx->detect.position;
    605  size_t read_n;
    606  if (buffered_bytes == 0) {
    607    read_n = fread(input_ctx->detect.buf + input_ctx->detect.buf_read, 1, n,
    608                   input_ctx->file);
    609    input_ctx->detect.buf_read += read_n;
    610  } else if (n <= buffered_bytes) {
    611    // In this case, don't need to do anything as the data is already in
    612    // the detect buffer
    613    read_n = n;
    614  } else {
    615    read_n = fread(input_ctx->detect.buf + input_ctx->detect.buf_read, 1,
    616                   n - buffered_bytes, input_ctx->file);
    617    input_ctx->detect.buf_read += read_n;
    618    read_n += buffered_bytes;
    619  }
    620  return read_n;
    621 }
    622 
    623 // Read from detect buffer to a buffer. If not enough, read from input and also
    624 // buffer them first.
    625 size_t buffer_input(struct AvxInputContext *input_ctx, size_t n,
    626                    unsigned char *buf, bool buffered) {
    627  if (!buffered) {
    628    return read_from_input(input_ctx, n, buf);
    629  }
    630  const size_t buf_n = input_to_detect_buf(input_ctx, n);
    631  if (buf_n < n) {
    632    return buf_n;
    633  }
    634  return read_from_input(input_ctx, n, buf);
    635 }
    636 
    637 void rewind_detect(struct AvxInputContext *input_ctx) {
    638  input_ctx->detect.position = 0;
    639 }
    640 
    641 bool input_eof(struct AvxInputContext *input_ctx) {
    642  return feof(input_ctx->file) &&
    643         input_ctx->detect.position == input_ctx->detect.buf_read;
    644 }