FFmpeg
utils.c
Go to the documentation of this file.
1 /*
2  * Copyright © 2025, Niklas Haas
3  * Copyright © 2018, VideoLAN and dav1d authors
4  * Copyright © 2018, Two Orioles, LLC
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this
11  * list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <assert.h>
30 #include <inttypes.h>
31 #include <limits.h>
32 #include <math.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 
39 #include "checkasm_config.h"
40 
41 #ifdef _WIN32
42  #include <windows.h>
43  #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
44  #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x04
45  #endif
46 #else
47  #if HAVE_ISATTY
48  #include <unistd.h>
49  #endif
50  #if HAVE_IOCTL
51  #include <sys/ioctl.h>
52  #endif
53 #endif
54 
55 #if defined(__APPLE__) && defined(__MACH__)
56  #include <mach/mach_time.h>
57 #endif
58 
59 #include "checkasm/test.h"
60 #include "checkasm/utils.h"
61 #include "internal.h"
62 
63 NOINLINE void checkasm_noop(void *ptr)
64 {
65  (void) ptr;
66 }
67 
68 static ALWAYS_INLINE uint64_t gettime_nsec(int is_seed)
69 {
70 #ifdef _WIN32
71  static LARGE_INTEGER freq;
72  LARGE_INTEGER ts;
73  if (!freq.QuadPart) {
74  if (!QueryPerformanceFrequency(&freq))
75  return -1;
76  }
77  if (!QueryPerformanceCounter(&ts))
78  return -1;
79  return UINT64_C(1000000000) * ts.QuadPart / freq.QuadPart;
80 #elif defined(__APPLE__) && defined(__MACH__)
81  static mach_timebase_info_data_t tb_info;
82  if (!tb_info.denom) {
83  if (mach_timebase_info(&tb_info) != KERN_SUCCESS)
84  return -1;
85  }
86  return mach_absolute_time() * tb_info.numer / tb_info.denom;
87 #elif HAVE_CLOCK_GETTIME
88  struct timespec ts;
89  clockid_t id;
90  if (!is_seed) {
91  #ifdef CLOCK_MONOTONIC_RAW
92  id = CLOCK_MONOTONIC_RAW;
93  #else
94  id = CLOCK_MONOTONIC;
95  #endif
96  } else {
97  id = CLOCK_REALTIME;
98  }
99  if (clock_gettime(id, &ts) < 0)
100  return -1;
101  return UINT64_C(1000000000) * ts.tv_sec + ts.tv_nsec;
102 #else
103  return -1;
104 #endif
105 }
106 
107 uint64_t checkasm_gettime_nsec(void)
108 {
109  return gettime_nsec(0);
110 }
111 
112 uint64_t checkasm_gettime_nsec_diff(uint64_t t)
113 {
114  return gettime_nsec(0) - t;
115 }
116 
117 unsigned checkasm_seed(void)
118 {
119  return (unsigned) gettime_nsec(1);
120 }
121 
122 // (parallel) xoshiro128++ from https://prng.di.unimi.it/
123 typedef struct CheckasmRand {
124 #define CHECKASM_PRNG_NUM 4
129 } CheckasmRand;
130 
132 
133 static ALWAYS_INLINE uint32_t rotl(const uint32_t x, int k)
134 {
135  return (x << k) | (x >> (32 - k));
136 }
137 
138 /* Single round of a parallel xoshiro128++, generates a full block */
139 static ALWAYS_INLINE void xoshiro128pp(CheckasmRand *restrict xs, uint32_t *restrict buf)
140 {
141  for (int i = 0; i < CHECKASM_PRNG_NUM; i++) {
142  buf[i] = rotl(xs->s0[i] + xs->s3[i], 7) + xs->s0[i];
143 
144  const uint32_t t = xs->s1[i] << 9;
145  xs->s2[i] ^= xs->s0[i];
146  xs->s3[i] ^= xs->s1[i];
147  xs->s1[i] ^= xs->s2[i];
148  xs->s0[i] ^= xs->s3[i];
149  xs->s2[i] ^= t;
150  xs->s3[i] = rotl(xs->s3[i], 11);
151  }
152 }
153 
154 static void prng(CheckasmRand *restrict xs, uint8_t *restrict buf, size_t size)
155 {
156  uint32_t tmp[CHECKASM_PRNG_NUM];
157  const size_t block_size = sizeof(tmp);
158  CheckasmRand xs_copy = *xs;
159 
160  while (size >= block_size) {
161  xoshiro128pp(&xs_copy, tmp);
162  memcpy(buf, tmp, block_size);
163  buf += block_size;
164  size -= block_size;
165  }
166 
167  if (size) {
168  xoshiro128pp(&xs_copy, tmp);
169  memcpy(buf, tmp, size);
170  }
171 
172  *xs = xs_copy;
173 }
174 
175 /* Efficient wrapper for generating individual random integers, by caching
176  * the result of a single call to the underlying generator() */
177 static struct {
178  #define PRNG_CACHE_SIZE 64
180  uint16_t buf16[PRNG_CACHE_SIZE >> 1];
181  uint32_t buf32[PRNG_CACHE_SIZE >> 2];
182  uint64_t buf64[PRNG_CACHE_SIZE >> 3];
183  int num8;
184  int num16;
185  int num32;
186  int num64;
187 } prng_cache;
188 
189 static_assert(PRNG_CACHE_SIZE % sizeof(uint32_t[CHECKASM_PRNG_NUM]) == 0,
190  "PRNG_CACHE_SIZE should be a multiple of uint32_t[CHECKASM_PRNG_NUM]");
191 
192 #define DEF_CHECKASM_RAND(BITS, TYPE, NAME) \
193  TYPE checkasm_rand_##NAME(void) \
194  { \
195  if (!prng_cache.num##BITS) { \
196  prng(&checkasm_prng, (uint8_t *) prng_cache.buf##BITS, \
197  sizeof(prng_cache.buf##BITS)); \
198  prng_cache.num##BITS = ARRAY_SIZE(prng_cache.buf##BITS); \
199  } \
200  \
201  union { \
202  TYPE type; \
203  uint##BITS##_t raw; \
204  } val; \
205  val.raw = prng_cache.buf##BITS[--prng_cache.num##BITS]; \
206  return val.type; \
207  }
208 
209 DEF_CHECKASM_RAND(8, int8_t, int8)
210 DEF_CHECKASM_RAND(8, uint8_t, uint8)
211 DEF_CHECKASM_RAND(16, int16_t, int16)
212 DEF_CHECKASM_RAND(16, uint16_t, uint16)
213 DEF_CHECKASM_RAND(32, int32_t, int32)
214 DEF_CHECKASM_RAND(32, uint32_t, uint32)
215 DEF_CHECKASM_RAND(32, float, float32)
216 DEF_CHECKASM_RAND(64, int64_t, int64)
217 DEF_CHECKASM_RAND(64, uint64_t, uint64)
218 DEF_CHECKASM_RAND(64, double, float64)
219 
220 static inline uint64_t splitmix64(uint64_t *state)
221 {
222  uint64_t z = (*state += 0x9e3779b97f4a7c15);
223 
224  z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
225  z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
226  return z ^ (z >> 31);
227 }
228 
229 void checkasm_srand(unsigned seed)
230 {
231  /* Seed using splitmix64() as recommended by xoroshiro128 authors */
232  uint64_t s = seed;
233 
234  for (int i = 0; i < CHECKASM_PRNG_NUM; i++) {
235  const uint64_t a = splitmix64(&s);
236  const uint64_t b = splitmix64(&s);
237 
238  checkasm_prng.s0[i] = (uint32_t) a;
239  checkasm_prng.s1[i] = (uint32_t) b;
240  checkasm_prng.s2[i] = a >> 32;
241  checkasm_prng.s3[i] = b >> 32;
242  }
243 
244  /* discard cached random bytes */
245  prng_cache.num8 = prng_cache.num16 = prng_cache.num32 = prng_cache.num64 = 0;
246 }
247 
248 int checkasm_rand(void)
249 {
250  static_assert(sizeof(int) <= sizeof(uint32_t), "int larger than 32 bits");
251  return checkasm_rand_uint32() & INT_MAX;
252 }
253 
254 double checkasm_randf(void)
255 {
256  return checkasm_rand_uint32() / (UINT32_MAX + 1.0);
257 }
258 
259 /* Marsaglia polar method */
260 static inline double marsaglia(double *z2)
261 {
262  double u1, u2, w;
263  do {
264  u1 = 2.0 / UINT32_MAX * checkasm_rand_uint32() - 1.0;
265  u2 = 2.0 / UINT32_MAX * checkasm_rand_uint32() - 1.0;
266  w = u1 * u1 + u2 * u2;
267  } while (w >= 1.0);
268 
269  w = sqrt((-2.0 * log(w)) / w);
270  *z2 = u2 * w;
271  return u1 * w;
272 }
273 
274 double checkasm_rand_norm(void)
275 {
276  static int cached;
277  static double cache;
278  if ((cached = !cached)) {
279  return marsaglia(&cache);
280  } else {
281  return cache;
282  }
283 }
284 
286 {
287  return dist.mean + dist.stddev * checkasm_rand_norm();
288 }
289 
290 void checkasm_randomize(void *buf, size_t bytes)
291 {
292  prng(&checkasm_prng, buf, bytes);
293 }
294 
295 void checkasm_randomize_mask8(uint8_t *buf, int width, uint8_t mask)
296 {
297  prng(&checkasm_prng, (uint8_t *) buf, width * sizeof(*buf));
298  for (int i = 0; i < width; i++)
299  buf[i] &= mask;
300 }
301 
302 void checkasm_randomize_mask16(uint16_t *buf, int width, uint16_t mask)
303 {
304  prng(&checkasm_prng, (uint8_t *) buf, width * sizeof(*buf));
305  for (int i = 0; i < width; i++)
306  buf[i] &= mask;
307 }
308 
309 void checkasm_randomize_range(double *buf, int width, double range)
310 {
311  const double scale = range / (UINT32_MAX + 1.0);
312  while (width--)
313  *buf++ = scale * checkasm_rand_uint32();
314 }
315 
316 void checkasm_randomize_rangef(float *buf, int width, float range)
317 {
318  const float scale = (float) (range / (UINT32_MAX + 1.0));
319  while (width--)
320  *buf++ = scale * checkasm_rand_uint32();
321 }
322 
323 void checkasm_randomize_interval(double *buf, int width, double low, double high)
324 {
325  const double scale = (high - low) / (double) UINT32_MAX;
326  while (width--)
327  *buf++ = scale * checkasm_rand_uint32() + low;
328 }
329 
330 void checkasm_randomize_intervalf(float *buf, int width, float low, float high)
331 {
332  const float scale = (high - low) / (float) UINT32_MAX;
333  while (width--)
334  *buf++ = scale * checkasm_rand_uint32() + low;
335 }
336 
337 #define RANDOMIZE_DIST(buf, ftype, width, mean, stddev) \
338  do { \
339  if ((width) & 1) { \
340  *(buf)++ = (ftype) ((mean) + (stddev) * checkasm_rand_norm()); \
341  (width) ^= 1; \
342  } \
343  \
344  for (; width; width -= 2) { \
345  double z1, z2; \
346  z1 = marsaglia(&z2); \
347  *(buf)++ = (ftype) ((mean) + (stddev) * z1); \
348  *(buf)++ = (ftype) ((mean) + (stddev) * z2); \
349  } \
350  } while (0)
351 
352 void checkasm_randomize_dist(double *buf, int width, CheckasmDist dist)
353 {
354  RANDOMIZE_DIST(buf, double, width, dist.mean, dist.stddev);
355 }
356 
357 void checkasm_randomize_distf(float *buf, int width, CheckasmDist dist)
358 {
359  RANDOMIZE_DIST(buf, float, width, dist.mean, dist.stddev);
360 }
361 
362 void checkasm_randomize_norm(double *buf, int width)
363 {
364  RANDOMIZE_DIST(buf, double, width, 0.0, 1.0);
365 }
366 
367 void checkasm_randomize_normf(float *buf, int width)
368 {
369  RANDOMIZE_DIST(buf, float, width, 0.0, 1.0);
370 }
371 
372 void checkasm_clear(void *buf, size_t bytes)
373 {
374  memset(buf, 0xAA, bytes);
375 }
376 
377 void checkasm_clear8(uint8_t *buf, int width, uint8_t val)
378 {
379  memset(buf, val, width);
380 }
381 
382 void checkasm_clear16(uint16_t *buf, int width, uint16_t val)
383 {
384  while (width--)
385  *buf++ = val;
386 }
387 
388 #if HAVE_STDBIT_H
389  #include <stdbit.h>
390 
391 static inline int clz(const unsigned int mask)
392 {
393  return stdc_leading_zeros_ui(mask);
394 }
395 
396 #elif defined(_MSC_VER) && !defined(__clang__)
397  #include <intrin.h>
398 
399 static inline int clz(const unsigned int mask)
400 {
401  unsigned long leading_zero = 0;
402  _BitScanReverse(&leading_zero, mask);
403  return (31 - leading_zero);
404 }
405 
406 #else /* !_MSC_VER */
407 static inline int clz(const unsigned int mask)
408 {
409  return __builtin_clz(mask);
410 }
411 #endif /* !_MSC_VER */
412 
413 /* Randomly downshift an integer */
414 static int shift_rand(int x)
415 {
416  const int bits = 8 * sizeof(x) - clz(x);
417  return x ? (x >> (checkasm_rand() % bits)) : 0;
418 }
419 
420 enum {
421  PAT_ZERO, // all zero
422  PAT_ONE, // all one
423  PAT_RAND, // random data
424  PAT_LOW, // all low
425  PAT_HIGH, // all high
426  PAT_ALTLO, // alternating low and high
427  PAT_ALTHI, // alternating high and low
428  PAT_MIX, // random mix of low and high
429 };
430 
431 void checkasm_init(void *buf, size_t bytes)
432 {
433  checkasm_init_mask8(buf, (int) bytes, 0xFF);
434 }
435 
436 #define DEF_CHECKASM_INIT_MASK(BITS, PIXEL) \
437  void checkasm_init_mask##BITS(PIXEL *buf, const int width, const PIXEL mask_pixel) \
438  { \
439  if (!width) \
440  return; \
441  \
442  int step = 0, mode = 0, mask = mask_pixel; \
443  for (int i = 0; i < width; i++, step--) { \
444  if (!step) { \
445  step = imax(shift_rand(width), 1); \
446  mode = checkasm_rand_uint8() & 7; \
447  mask = shift_rand(mask_pixel); \
448  } \
449  \
450  const PIXEL low = checkasm_rand_uint##BITS() & mask; \
451  const PIXEL high = mask_pixel - low; \
452  switch (mode) { \
453  case PAT_ZERO: buf[i] = 0; break; \
454  case PAT_ONE: buf[i] = mask_pixel; break; \
455  case PAT_RAND: buf[i] = checkasm_rand_uint##BITS() & mask_pixel; break; \
456  case PAT_LOW: buf[i] = low; break; \
457  case PAT_HIGH: buf[i] = high; break; \
458  case PAT_ALTLO: buf[i] = (i & 1) ? high : low; break; \
459  case PAT_ALTHI: buf[i] = (i & 1) ? low : high; break; \
460  case PAT_MIX: buf[i] = (checkasm_rand_uint8() & 1) ? low : high; break; \
461  } \
462  } \
463  }
464 
465 DEF_CHECKASM_INIT_MASK(8, uint8_t)
466 DEF_CHECKASM_INIT_MASK(16, uint16_t)
467 
468 static int use_printf_color[2];
469 static char statusline[256];
471 
472 /* Print colored text to stderr if the terminal supports it */
473 int checkasm_vfprintf(FILE *const f, const int color, const char *const fmt, va_list arg)
474 {
475  size_t fmt_len = strlen(fmt);
476  int use_color = use_printf_color[f == stderr];
477  if (!use_color || !fmt_len)
478  return vfprintf(f, fmt, arg);
479 
480  if (f == stderr && statusline_visible) {
481  fprintf(f, "\r\033[K"); /* clear line */
482  statusline_visible = 0;
483  }
484 
485  if (color >= 0)
486  fprintf(f, "\x1b[0;%dm", color);
487 
488  int ret = vfprintf(f, fmt, arg);
489 
490  if (color >= 0)
491  fprintf(f, "\x1b[0m");
492 
493  if (f == stderr && statusline[0] && fmt[fmt_len - 1] == '\n') {
494  fprintf(f, "%s", statusline);
495  statusline_visible = 1;
496  }
497 
498  return ret;
499 }
500 
501 void checkasm_statusline(const char *status)
502 {
503  if (!status)
504  status = "";
505 
506  if (!use_printf_color[1] || !strcmp(statusline, status))
507  return; /* don't re-paint unchanged status */
508 
509  snprintf(statusline, sizeof(statusline), "%s", status);
510 
511  if (statusline_visible) {
512  fprintf(stderr, "\r\033[K");
513  statusline_visible = 0;
514  }
515 
516  if (statusline[0]) {
517  fprintf(stderr, "%s", statusline);
518  statusline_visible = 1;
519  }
520 }
521 
522 static COLD int should_use_color(FILE *const f)
523 {
524 #ifdef _WIN32
525  #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
526  HANDLE con = GetStdHandle(f == stderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
527  DWORD con_mode = 0;
528  return con && con != INVALID_HANDLE_VALUE && GetConsoleMode(con, &con_mode)
529  && SetConsoleMode(con, con_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
530  #else
531  return 0;
532  #endif
533 #elif HAVE_ISATTY
534  if (isatty(f == stderr ? 2 : 1)) {
535  const char *const term = getenv("TERM");
536  return term && strcmp(term, "dumb");
537  }
538  return 0;
539 #else
540  return 0;
541 #endif
542 }
543 
545 {
546  use_printf_color[0] = should_use_color(stdout);
547  use_printf_color[1] = should_use_color(stderr);
548 }
549 
550 static int get_terminal_width(void)
551 {
552 #ifdef _WIN32
553  #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
554  CONSOLE_SCREEN_BUFFER_INFO csbi;
555  if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
556  return csbi.srWindow.Right - csbi.srWindow.Left + 1;
557  #endif
558 #elif defined(__OS2__)
559  int dst[2];
560  _scrsize(dst);
561  return dst[0];
562 #elif HAVE_IOCTL && defined(TIOCGWINSZ)
563  struct winsize w;
564  if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
565  return w.ws_col;
566 #endif
567  return 80;
568 }
569 
570 void checkasm_json(CheckasmJson *json, const char *key, const char *const fmt, ...)
571 {
572  assert(json->level > 0);
573  fputs(json->nonempty ? ",\n" : "\n", json->file);
574  for (int i = 0; i < json->level; i++)
575  fputc(' ', json->file);
576 
577  va_list ap;
578  va_start(ap, fmt);
579  if (key)
580  fprintf(json->file, "\"%s\": ", key);
581  vfprintf(json->file, fmt, ap);
582  va_end(ap);
583  json->nonempty = 1;
584 }
585 
586 void checkasm_json_str(CheckasmJson *json, const char *key, const char *str)
587 {
588  assert(json->level > 0);
589  fputs(json->nonempty ? ",\n" : "\n", json->file);
590  for (int i = 0; i < json->level; i++)
591  fputc(' ', json->file);
592 
593  if (key)
594  fprintf(json->file, "\"%s\": \"", key);
595  else
596  fputc('"', json->file);
597 
598  while (*str) {
599  switch (*str) {
600  case '\\': fputs("\\\\", json->file); break;
601  case '"': fputs("\\\"", json->file); break;
602  case '\n': fputs("\\n", json->file); break;
603  default: fputc(*str, json->file); break;
604  }
605  str++;
606  }
607  fputc('"', json->file);
608  json->nonempty = 1;
609 }
610 
611 void checkasm_json_push(CheckasmJson *json, const char *const key, const char type)
612 {
613  fputs(json->nonempty ? ",\n" : "\n", json->file);
614  for (int i = 0; i < json->level; i++)
615  fputc(' ', json->file);
616 
617  if (key) {
618  fprintf(json->file, "\"%s\": %c", key, type);
619  } else {
620  fputc(type, json->file);
621  }
622 
623  json->level += 2;
624  json->nonempty = 0;
625 }
626 
628 {
629  assert(json->level >= 2);
630  json->level -= 2;
631  if (json->nonempty) {
632  fputc('\n', json->file);
633  for (int i = 0; i < json->level; i++)
634  fputc(' ', json->file);
635  }
636  fputc(type, json->file);
637  json->nonempty = 1;
638 }
639 
640 /* float compare support code */
641 typedef union {
642  float f;
643  uint32_t i;
644 } intfloat;
645 
646 static int is_negative(const intfloat u)
647 {
648  return u.i >> 31;
649 }
650 
651 int checkasm_float_near_ulp(const float a, const float b, const unsigned max_ulp)
652 {
653  intfloat x, y;
654 
655  x.f = a;
656  y.f = b;
657 
658  if (is_negative(x) != is_negative(y)) {
659  // handle -0.0 == +0.0
660  return a == b;
661  }
662 
663  if (llabs((int64_t) x.i - y.i) <= max_ulp)
664  return 1;
665 
666  return 0;
667 }
668 
669 int checkasm_float_near_ulp_array(const float *const a, const float *const b,
670  const unsigned max_ulp, const int len)
671 {
672  for (int i = 0; i < len; i++)
673  if (!float_near_ulp(a[i], b[i], max_ulp))
674  return 0;
675 
676  return 1;
677 }
678 
679 int checkasm_float_near_abs_eps(const float a, const float b, const float eps)
680 {
681  return fabsf(a - b) < eps;
682 }
683 
684 int checkasm_float_near_abs_eps_array(const float *const a, const float *const b,
685  const float eps, const int len)
686 {
687  for (int i = 0; i < len; i++)
688  if (!float_near_abs_eps(a[i], b[i], eps))
689  return 0;
690 
691  return 1;
692 }
693 
694 int checkasm_float_near_abs_eps_ulp(const float a, const float b, const float eps,
695  const unsigned max_ulp)
696 {
697  return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps);
698 }
699 
700 int checkasm_float_near_abs_eps_array_ulp(const float *const a, const float *const b,
701  const float eps, const unsigned max_ulp,
702  const int len)
703 {
704  for (int i = 0; i < len; i++)
705  if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp))
706  return 0;
707 
708  return 1;
709 }
710 
711 int checkasm_double_near_abs_eps(const double a, const double b, const double eps)
712 {
713  return fabs(a - b) < eps;
714 }
715 
716 int checkasm_double_near_abs_eps_array(const double *const a, const double *const b,
717  const double eps, const unsigned len)
718 {
719  for (unsigned i = 0; i < len; i++)
720  if (!double_near_abs_eps(a[i], b[i], eps))
721  return 0;
722 
723  return 1;
724 }
725 
726 static int check_err(const char *const file, const int line, const char *const name,
727  const int w, const int h, int *const err)
728 {
729  if (*err)
730  return 0;
731  if (!checkasm_fail_func("%s:%d", file, line))
732  return 1;
733  *err = 1;
734  fprintf(stderr, "%s (%dx%d):\n", name, w, h);
735  return 0;
736 }
737 
738 #define PRINT_LINE(buf1, buf2, xstart, xend, xpad, fmt, fmtw) \
739  do { \
740  for (int x = xstart; x < xend; x++) { \
741  if (buf1[x] != buf2[x]) \
742  checkasm_fprintf(stderr, COLOR_RED, " " fmt, buf1[x]); \
743  else \
744  fprintf(stderr, " " fmt, buf1[x]); \
745  } \
746  for (int pad = xend; pad < xstart + xpad; pad++) \
747  fprintf(stderr, &" "[9 - fmtw]); \
748  } while (0)
749 
750 #define PRINT_RECT(type, buf1, buf2, ystart, yend, xstart, xend, fmt, fmtw) \
751  do { \
752  const type *ptr1 = (buf1) + ystart * stride1; \
753  const type *ptr2 = (buf2) + ystart * stride1; \
754  const int elem_size = 2 * (fmtw + 1) + 1; \
755  const int display_elems = imin(term_width / elem_size, xend - xstart); \
756  for (int y = ystart; y < yend; y++) { \
757  for (int xpos = xstart; xpos < xend; xpos += display_elems) { \
758  const int xstep = imin(xpos + display_elems, xend); \
759  if (xpos == xstart) /* line change */ \
760  checkasm_fprintf(stderr, COLOR_BLUE, "%3d: ", y); \
761  else \
762  fprintf(stderr, " "); \
763  PRINT_LINE(ptr1, ptr2, xpos, xstep, display_elems, fmt, fmtw); \
764  fprintf(stderr, " "); \
765  PRINT_LINE(ptr2, ptr1, xpos, xstep, display_elems, fmt, fmtw); \
766  fprintf(stderr, " "); \
767  for (int x = xpos; x < xstep; x++) { \
768  if (ptr1[x] != ptr2[x]) \
769  checkasm_fprintf(stderr, COLOR_RED, "x"); \
770  else \
771  fprintf(stderr, "."); \
772  } \
773  fprintf(stderr, "\n"); \
774  } \
775  ptr1 += stride1; \
776  ptr2 += stride2; \
777  } \
778  } while (0)
779 
780 #define CHECK_RECT(buf1, buf2, ystart, yend, xstart, xend, msg, compare, type, fmt, \
781  fmtw) \
782  do { \
783  const int xw = xend - xstart; \
784  for (int y = ystart; y < yend; y++) { \
785  if (compare(&buf1[y * stride1 + xstart], &buf2[y * stride2 + xstart], xw)) \
786  continue; \
787  if (check_err(file, line, name, w, h, &err)) \
788  return 1; \
789  /* Exclude unneeded lines on overwrite above */ \
790  int yprint = y < 0 ? y : ystart; \
791  if (msg[0]) \
792  fprintf(stderr, " %s (%dx%d, from idx [%d]):\n", msg, xend - xstart, \
793  yend - yprint, xstart); \
794  PRINT_RECT(type, buf1, buf2, yprint, yend, xstart, xend, fmt, fmtw); \
795  break; \
796  } \
797  } while (0)
798 
799 #define DEF_CHECKASM_CHECK_BODY(compare, type, fmt, fmtw) \
800  do { \
801  const int overhead = 5 + 3 + 3; \
802  const int term_width = get_terminal_width() - overhead; \
803  const int aligned_w = (w + align_w - 1) & ~(align_w - 1); \
804  stride1 /= sizeof(type); \
805  stride2 /= sizeof(type); \
806  \
807  int err = 0; \
808  CHECK_RECT(buf1, buf2, 0, h, 0, w, "", compare, type, fmt, fmtw); \
809  if (align_h >= 1) { \
810  const int aligned_h = (h + align_h - 1) & ~(align_h - 1); \
811  CHECK_RECT(buf1, buf2, -padding, 0, -padding, w + padding, "overwrite top", \
812  compare, type, fmt, fmtw); \
813  CHECK_RECT(buf1, buf2, aligned_h, aligned_h + padding, -padding, \
814  w + padding, "overwrite bottom", compare, type, fmt, fmtw); \
815  } \
816  CHECK_RECT(buf1, buf2, 0, h, -padding, 0, "overwrite left", compare, type, fmt, \
817  fmtw); \
818  CHECK_RECT(buf1, buf2, 0, h, aligned_w, aligned_w + padding, "overwrite right", \
819  compare, type, fmt, fmtw); \
820  return err; \
821  } while (0)
822 
823 #define cmp_int(a, b, len) (!memcmp(a, b, (len) * sizeof(*(a))))
824 #define DEF_CHECKASM_CHECK_FUNC(type, fmt, fmtw) \
825  int checkasm_check_impl_##type(const char *file, int line, const type *buf1, \
826  ptrdiff_t stride1, const type *buf2, \
827  ptrdiff_t stride2, int w, int h, const char *name, \
828  int align_w, int align_h, int padding) \
829  { \
830  DEF_CHECKASM_CHECK_BODY(cmp_int, type, fmt, fmtw); \
831  }
832 
833 DEF_CHECKASM_CHECK_FUNC(int, "%9d", 9)
834 DEF_CHECKASM_CHECK_FUNC(int8_t, "%4" PRId8, 4)
835 DEF_CHECKASM_CHECK_FUNC(int16_t, "%6" PRId16, 6)
836 DEF_CHECKASM_CHECK_FUNC(int32_t, "%9" PRId32, 9)
837 
838 DEF_CHECKASM_CHECK_FUNC(unsigned, "%08x", 8)
839 DEF_CHECKASM_CHECK_FUNC(uint8_t, "%02" PRIx8, 2)
840 DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04" PRIx16, 4)
841 DEF_CHECKASM_CHECK_FUNC(uint32_t, "%08" PRIx32, 8)
842 
843 int checkasm_check_impl_float_ulp(const char *file, int line, const float *buf1,
844  ptrdiff_t stride1, const float *buf2, ptrdiff_t stride2,
845  int w, int h, const char *name, unsigned max_ulp,
846  int align_w, int align_h, int padding)
847 {
848 #define cmp_float(a, b, len) float_near_ulp_array(a, b, max_ulp, len)
849  DEF_CHECKASM_CHECK_BODY(cmp_float, float, "%7g", 7);
850 #undef cmp_float
851 }
852 
853 char *checkasm_vasprintf(const char *fmt, va_list arg)
854 {
855  va_list arg2;
856  va_copy(arg2, arg);
857  int len = vsnprintf(NULL, 0, fmt, arg2);
858  va_end(arg2);
859  if (len < 0)
860  return NULL;
861 
862  char *buf = checkasm_mallocz(len + 1);
863  vsnprintf(buf, len + 1, fmt, arg);
864  return buf;
865 }
COLD
#define COLD
Definition: internal.h:45
clz
static int clz(const unsigned int mask)
Definition: utils.c:407
checkasm_randomize_distf
void checkasm_randomize_distf(float *buf, int width, CheckasmDist dist)
Fill a float buffer with normally distributed random values.
Definition: utils.c:357
checkasm_randomize_dist
void checkasm_randomize_dist(double *buf, int width, CheckasmDist dist)
Fill a double buffer with normally distributed random values.
Definition: utils.c:352
CheckasmDist::stddev
double stddev
Standard deviation (spread) of the distribution.
Definition: utils.h:149
name
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
marsaglia
static double marsaglia(double *z2)
Definition: utils.c:260
checkasm_randomize_mask16
void checkasm_randomize_mask16(uint16_t *buf, int width, uint16_t mask)
Fill a uint16_t buffer with random values chosen uniformly within a mask.
Definition: utils.c:302
DEF_CHECKASM_CHECK_FUNC
#define DEF_CHECKASM_CHECK_FUNC(type, fmt, fmtw)
Definition: utils.c:824
checkasm_randomize_intervalf
void checkasm_randomize_intervalf(float *buf, int width, float low, float high)
Fill a float buffer with random values chosen uniformly from an interval.
Definition: utils.c:330
checkasm_config.h
num32
int num32
Definition: utils.c:185
checkasm_srand
void checkasm_srand(unsigned seed)
Definition: utils.c:229
color
Definition: vf_paletteuse.c:513
CheckasmJson::level
int level
Definition: internal.h:135
num8
int num8
Definition: utils.c:183
int64_t
long long int64_t
Definition: coverity.c:34
checkasm_float_near_abs_eps_array_ulp
int checkasm_float_near_abs_eps_array_ulp(const float *const a, const float *const b, const float eps, const unsigned max_ulp, const int len)
Compare float arrays using both epsilon and ULP tolerances.
Definition: utils.c:700
normalize.log
log
Definition: normalize.py:21
mask
int mask
Definition: mediacodecdec_common.c:154
checkasm_vfprintf
int checkasm_vfprintf(FILE *const f, const int color, const char *const fmt, va_list arg)
Definition: utils.c:473
num16
int num16
Definition: utils.c:184
get_terminal_width
static int get_terminal_width(void)
Definition: utils.c:550
u
#define u(width, name, range_min, range_max)
Definition: cbs_apv.c:68
PRNG_CACHE_SIZE
#define PRNG_CACHE_SIZE
Definition: utils.c:178
num64
int num64
Definition: utils.c:186
b
#define b
Definition: input.c:43
checkasm_clear8
void checkasm_clear8(uint8_t *buf, int width, uint8_t val)
Fill a uint8_t buffer with a constant value.
Definition: utils.c:377
splitmix64
static uint64_t splitmix64(uint64_t *state)
Definition: utils.c:220
checkasm_float_near_abs_eps_ulp
int checkasm_float_near_abs_eps_ulp(const float a, const float b, const float eps, const unsigned max_ulp)
Compare floats using both epsilon and ULP tolerances.
Definition: utils.c:694
high
int high
Definition: dovi_rpuenc.c:39
check_err
static int check_err(const char *const file, const int line, const char *const name, const int w, const int h, int *const err)
Definition: utils.c:726
buf8
uint8_t buf8[PRNG_CACHE_SIZE]
Definition: utils.c:179
checkasm_randomize_mask8
void checkasm_randomize_mask8(uint8_t *buf, int width, uint8_t mask)
Fill a uint8_t buffer with random values chosen uniformly within a mask.
Definition: utils.c:295
key
const char * key
Definition: ffmpeg_mux_init.c:2971
CheckasmJson::file
FILE * file
Definition: internal.h:134
statusline_visible
static int statusline_visible
Definition: utils.c:470
checkasm_randf
double checkasm_randf(void)
Generate a random double-precision floating-point number.
Definition: utils.c:254
checkasm_randomize_range
void checkasm_randomize_range(double *buf, int width, double range)
Fill a double buffer with random values chosen uniformly below a limit.
Definition: utils.c:309
should_use_color
static COLD int should_use_color(FILE *const f)
Definition: utils.c:522
checkasm_fail_func
CHECKASM_API CheckasmKey CHECKASM_API void CHECKASM_API int checkasm_fail_func(const char *msg,...) CHECKASM_PRINTF(1
Mark the current function as failed with a custom message.
PAT_ZERO
@ PAT_ZERO
Definition: utils.c:421
PAT_ALTHI
@ PAT_ALTHI
Definition: utils.c:427
val
static double val(void *priv, double ch)
Definition: aeval.c:77
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
checkasm_json
void checkasm_json(CheckasmJson *json, const char *key, const char *const fmt,...)
Definition: utils.c:570
state
static struct @604 state
rotl
static ALWAYS_INLINE uint32_t rotl(const uint32_t x, int k)
Definition: utils.c:133
fabsf
static __device__ float fabsf(float a)
Definition: cuda_runtime.h:181
prng
static void prng(CheckasmRand *restrict xs, uint8_t *restrict buf, size_t size)
Definition: utils.c:154
statusline
static char statusline[256]
Definition: utils.c:469
checkasm_randomize_interval
void checkasm_randomize_interval(double *buf, int width, double low, double high)
Fill a double buffer with random values chosen uniformly from an interval.
Definition: utils.c:323
checkasm_float_near_abs_eps
int checkasm_float_near_abs_eps(const float a, const float b, const float eps)
Compare floats using absolute epsilon tolerance.
Definition: utils.c:679
CheckasmRand::s3
uint32_t s3[CHECKASM_PRNG_NUM]
Definition: utils.c:128
intfloat::f
float f
Definition: utils.c:642
checkasm_mallocz
static void * checkasm_mallocz(const size_t size)
Definition: internal.h:197
checkasm_randomize_norm
void checkasm_randomize_norm(double *buf, int width)
Fill a double buffer with values from a standard normal distribution.
Definition: utils.c:362
float
float
Definition: af_crystalizer.c:122
checkasm_init
void checkasm_init(void *buf, size_t bytes)
Initialize a buffer with pathological test patterns.
Definition: utils.c:431
bits
uint8_t bits
Definition: vp3data.h:128
use_printf_color
static int use_printf_color[2]
Definition: utils.c:468
PAT_ALTLO
@ PAT_ALTLO
Definition: utils.c:426
limits.h
CheckasmJson
Definition: internal.h:133
checkasm_float_near_abs_eps_array
int checkasm_float_near_abs_eps_array(const float *const a, const float *const b, const float eps, const int len)
Compare float arrays using absolute epsilon tolerance.
Definition: utils.c:684
PAT_HIGH
@ PAT_HIGH
Definition: utils.c:425
DEF_CHECKASM_CHECK_BODY
#define DEF_CHECKASM_CHECK_BODY(compare, type, fmt, fmtw)
Definition: utils.c:799
tmp
static uint8_t tmp[40]
Definition: aes_ctr.c:52
PAT_ONE
@ PAT_ONE
Definition: utils.c:422
checkasm_gettime_nsec
uint64_t checkasm_gettime_nsec(void)
Definition: utils.c:107
arg
const char * arg
Definition: jacosubdec.c:65
ALWAYS_INLINE
#define ALWAYS_INLINE
Definition: internal.h:72
float_near_abs_eps
#define float_near_abs_eps
Definition: utils.h:449
xs
#define xs(width, name, var, subs,...)
Definition: cbs_vp9.c:305
checkasm_randomize
void checkasm_randomize(void *buf, size_t bytes)
Fill a buffer with uniformly chosen random bytes.
Definition: utils.c:290
fabs
static __device__ float fabs(float a)
Definition: cuda_runtime.h:182
NULL
#define NULL
Definition: coverity.c:32
CheckasmDist::mean
double mean
Mean (center) of the distribution.
Definition: utils.h:148
cmp_float
#define cmp_float(a, b, len)
checkasm_rand_norm
double checkasm_rand_norm(void)
Generate a random number from the standard normal distribution.
Definition: utils.c:274
CheckasmRand
Definition: utils.c:123
time.h
intfloat
Definition: utils.c:641
seed
static unsigned int seed
Definition: videogen.c:78
checkasm_check_impl_float_ulp
int checkasm_check_impl_float_ulp(const char *file, int line, const float *buf1, ptrdiff_t stride1, const float *buf2, ptrdiff_t stride2, int w, int h, const char *name, unsigned max_ulp, int align_w, int align_h, int padding)
Compare float buffers with ULP tolerance.
Definition: utils.c:843
checkasm_double_near_abs_eps_array
int checkasm_double_near_abs_eps_array(const double *const a, const double *const b, const double eps, const unsigned len)
Compare double arrays using absolute epsilon tolerance.
Definition: utils.c:716
checkasm_seed
unsigned checkasm_seed(void)
Definition: utils.c:117
f
f
Definition: af_crystalizer.c:122
is_negative
static int is_negative(const intfloat u)
Definition: utils.c:646
prng_cache
static struct @605 prng_cache
checkasm_json_push
void checkasm_json_push(CheckasmJson *json, const char *const key, const char type)
Definition: utils.c:611
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
NOINLINE
#define NOINLINE
Definition: internal.h:59
test.h
Test writing API for checkasm.
size
int size
Definition: twinvq_data.h:10344
checkasm_randomize_normf
void checkasm_randomize_normf(float *buf, int width)
Fill a float buffer with values from a standard normal distribution.
Definition: utils.c:367
range
enum AVColorRange range
Definition: mediacodec_wrapper.c:2594
checkasm_noop
NOINLINE void checkasm_noop(void *ptr)
Definition: utils.c:63
checkasm_prng
static CheckasmRand checkasm_prng
Definition: utils.c:131
a
The reader does not expect b to be semantically here and if the code is changed by maybe adding a a division or other the signedness will almost certainly be mistaken To avoid this confusion a new type was SUINT is the C unsigned type but it holds a signed int to use the same example SUINT a
Definition: undefined.txt:41
PAT_MIX
@ PAT_MIX
Definition: utils.c:428
line
Definition: graph2dot.c:48
checkasm_init_mask8
CHECKASM_API void checkasm_init_mask8(uint8_t *buf, int width, uint8_t mask)
Initialize a uint8_t buffer with pathological values within a mask.
va_copy
#define va_copy(dst, src)
Definition: va_copy.h:31
gettime_nsec
static ALWAYS_INLINE uint64_t gettime_nsec(int is_seed)
Definition: utils.c:68
checkasm_gettime_nsec_diff
uint64_t checkasm_gettime_nsec_diff(uint64_t t)
Definition: utils.c:112
CheckasmJson::nonempty
int nonempty
Definition: internal.h:136
stdc_leading_zeros_ui
static unsigned int stdc_leading_zeros_ui(unsigned int value)
Definition: stdbit.h:61
RANDOMIZE_DIST
#define RANDOMIZE_DIST(buf, ftype, width, mean, stddev)
Definition: utils.c:337
width
static int width
Definition: utils.c:158
checkasm_json_pop
void checkasm_json_pop(CheckasmJson *json, char type)
Definition: utils.c:627
CheckasmRand::s1
uint32_t s1[CHECKASM_PRNG_NUM]
Definition: utils.c:126
CheckasmRand::s2
uint32_t s2[CHECKASM_PRNG_NUM]
Definition: utils.c:127
CheckasmDist
Describes a normal (Gaussian) distribution.
Definition: utils.h:147
utils.h
Utility functions for checkasm tests.
vsnprintf
#define vsnprintf
Definition: snprintf.h:36
use_color
static int use_color
Definition: log.c:127
PAT_LOW
@ PAT_LOW
Definition: utils.c:424
s
uint8_t s
Definition: llvidencdsp.c:39
checkasm_statusline
void checkasm_statusline(const char *status)
Definition: utils.c:501
len
int len
Definition: vorbis_enc_data.h:426
checkasm_float_near_ulp_array
int checkasm_float_near_ulp_array(const float *const a, const float *const b, const unsigned max_ulp, const int len)
Compare float arrays using ULP tolerance.
Definition: utils.c:669
double_near_abs_eps
#define double_near_abs_eps
Definition: utils.h:454
checkasm_float_near_ulp
int checkasm_float_near_ulp(const float a, const float b, const unsigned max_ulp)
Compare floats using ULP (Units in Last Place) tolerance.
Definition: utils.c:651
ret
ret
Definition: filter_design.txt:187
checkasm_setup_fprintf
COLD void checkasm_setup_fprintf(void)
Definition: utils.c:544
shift_rand
static int shift_rand(int x)
Definition: utils.c:414
checkasm_randomize_rangef
void checkasm_randomize_rangef(float *buf, int width, float range)
Fill a float buffer with random values chosen uniformly below a limit.
Definition: utils.c:316
DEF_CHECKASM_RAND
#define DEF_CHECKASM_RAND(BITS, TYPE, NAME)
Definition: utils.c:192
id
enum AVCodecID id
Definition: dts2pts.c:607
checkasm_rand
int checkasm_rand(void)
Generate a random non-negative integer.
Definition: utils.c:248
xoshiro128pp
static ALWAYS_INLINE void xoshiro128pp(CheckasmRand *restrict xs, uint32_t *restrict buf)
Definition: utils.c:139
status
ov_status_e status
Definition: dnn_backend_openvino.c:99
checkasm_clear
void checkasm_clear(void *buf, size_t bytes)
Clear a buffer to a pre-determined pattern (currently 0xAA)
Definition: utils.c:372
DEF_CHECKASM_INIT_MASK
#define DEF_CHECKASM_INIT_MASK(BITS, PIXEL)
Definition: utils.c:436
checkasm_clear16
void checkasm_clear16(uint16_t *buf, int width, uint16_t val)
Fill a uint16_t buffer with a constant value.
Definition: utils.c:382
internal.h
CheckasmRand::s0
uint32_t s0[CHECKASM_PRNG_NUM]
Definition: utils.c:125
buf16
uint16_t buf16[PRNG_CACHE_SIZE >> 1]
Definition: utils.c:180
checkasm_double_near_abs_eps
int checkasm_double_near_abs_eps(const double a, const double b, const double eps)
Compare doubles using absolute epsilon tolerance.
Definition: utils.c:711
checkasm_rand_uint32
CHECKASM_API uint32_t checkasm_rand_uint32(void)
Generate a random 32-bit unsigned integer.
w
uint8_t w
Definition: llvidencdsp.c:39
checkasm_json_str
void checkasm_json_str(CheckasmJson *json, const char *key, const char *str)
Definition: utils.c:586
scale
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition: intra.c:278
CHECKASM_PRNG_NUM
#define CHECKASM_PRNG_NUM
Definition: utils.c:124
intfloat::i
uint32_t i
Definition: utils.c:643
buf64
uint64_t buf64[PRNG_CACHE_SIZE >> 3]
Definition: utils.c:182
int32_t
int32_t
Definition: audioconvert.c:56
h
h
Definition: vp9dsp_template.c:2070
float_near_abs_eps_ulp
#define float_near_abs_eps_ulp
Definition: utils.h:450
checkasm_rand_dist
double checkasm_rand_dist(CheckasmDist dist)
Generate a normally distributed random number.
Definition: utils.c:285
buf32
uint32_t buf32[PRNG_CACHE_SIZE >> 2]
Definition: utils.c:181
snprintf
#define snprintf
Definition: snprintf.h:34
checkasm_vasprintf
char * checkasm_vasprintf(const char *fmt, va_list arg)
Definition: utils.c:853
float_near_ulp
#define float_near_ulp
Definition: utils.h:448
stdbit.h
PAT_RAND
@ PAT_RAND
Definition: utils.c:423