FFmpeg
Loading...
Searching...
No Matches
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
63NOINLINE void checkasm_noop(void *ptr)
64{
65 (void) ptr;
66}
67
68static 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
108{
109 return gettime_nsec(0);
110}
111
112uint64_t checkasm_gettime_nsec_diff(uint64_t t)
113{
114 return gettime_nsec(0) - t;
115}
116
117unsigned checkasm_seed(void)
118{
119 return (unsigned) gettime_nsec(1);
120}
121
122// (parallel) xoshiro128++ from https://prng.di.unimi.it/
123typedef struct CheckasmRand {
124#define CHECKASM_PRNG_NUM 4
130
132
133static 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 */
139static 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
154static 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() */
177static 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;
188
189static_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
209DEF_CHECKASM_RAND(8, int8_t, int8)
210DEF_CHECKASM_RAND(8, uint8_t, uint8)
211DEF_CHECKASM_RAND(16, int16_t, int16)
212DEF_CHECKASM_RAND(16, uint16_t, uint16)
213DEF_CHECKASM_RAND(32, int32_t, int32)
214DEF_CHECKASM_RAND(32, uint32_t, uint32)
215DEF_CHECKASM_RAND(32, float, float32)
216DEF_CHECKASM_RAND(64, int64_t, int64)
217DEF_CHECKASM_RAND(64, uint64_t, uint64)
218DEF_CHECKASM_RAND(64, double, float64)
219
220static 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
229void 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
249{
250 static_assert(sizeof(int) <= sizeof(uint32_t), "int larger than 32 bits");
251 return checkasm_rand_uint32() & INT_MAX;
252}
253
254double checkasm_randf(void)
255{
256 return checkasm_rand_uint32() / (UINT32_MAX + 1.0);
257}
258
259/* Marsaglia polar method */
260static 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
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
290void checkasm_randomize(void *buf, size_t bytes)
291{
292 prng(&checkasm_prng, buf, bytes);
293}
294
295void 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
302void 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
309void 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
316void 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
323void 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
330void 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
352void checkasm_randomize_dist(double *buf, int width, CheckasmDist dist)
353{
354 RANDOMIZE_DIST(buf, double, width, dist.mean, dist.stddev);
355}
356
357void checkasm_randomize_distf(float *buf, int width, CheckasmDist dist)
358{
359 RANDOMIZE_DIST(buf, float, width, dist.mean, dist.stddev);
360}
361
362void checkasm_randomize_norm(double *buf, int width)
363{
364 RANDOMIZE_DIST(buf, double, width, 0.0, 1.0);
365}
366
367void checkasm_randomize_normf(float *buf, int width)
368{
369 RANDOMIZE_DIST(buf, float, width, 0.0, 1.0);
370}
371
372void checkasm_clear(void *buf, size_t bytes)
373{
374 memset(buf, 0xAA, bytes);
375}
376
377void checkasm_clear8(uint8_t *buf, int width, uint8_t val)
378{
379 memset(buf, val, width);
380}
381
382void 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
391static inline int clz(const unsigned int mask)
392{
394}
395
396#elif defined(_MSC_VER) && !defined(__clang__)
397 #include <intrin.h>
398
399static 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 */
407static inline int clz(const unsigned int mask)
408{
409 return __builtin_clz(mask);
410}
411#endif /* !_MSC_VER */
412
413/* Randomly downshift an integer */
414static 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
420enum {
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
431void 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
465DEF_CHECKASM_INIT_MASK(8, uint8_t)
466DEF_CHECKASM_INIT_MASK(16, uint16_t)
467
468static int use_printf_color[2];
469static char statusline[256];
471
472/* Print colored text to stderr if the terminal supports it */
473int 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 */
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);
496 }
497
498 return ret;
499}
500
501void 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");
514 }
515
516 if (statusline[0]) {
517 fprintf(stderr, "%s", statusline);
519 }
520}
521
522static 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{
548}
549
550static 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
570void 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
586void 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
611void 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 */
641typedef union {
642 float f;
643 uint32_t i;
644} intfloat;
645
646static int is_negative(const intfloat u)
647{
648 return u.i >> 31;
649}
650
651int 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
669int 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
679int checkasm_float_near_abs_eps(const float a, const float b, const float eps)
680{
681 return fabsf(a - b) < eps;
682}
683
684int 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
694int 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
700int 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
711int checkasm_double_near_abs_eps(const double a, const double b, const double eps)
712{
713 return fabs(a - b) < eps;
714}
715
716int 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
726static 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
833DEF_CHECKASM_CHECK_FUNC(int, "%9d", 9)
834DEF_CHECKASM_CHECK_FUNC(int8_t, "%4" PRId8, 4)
835DEF_CHECKASM_CHECK_FUNC(int16_t, "%6" PRId16, 6)
836DEF_CHECKASM_CHECK_FUNC(int32_t, "%9" PRId32, 9)
837
838DEF_CHECKASM_CHECK_FUNC(unsigned, "%08x", 8)
839DEF_CHECKASM_CHECK_FUNC(uint8_t, "%02" PRIx8, 2)
840DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04" PRIx16, 4)
841DEF_CHECKASM_CHECK_FUNC(uint32_t, "%08" PRIx32, 8)
842
843int 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
853char *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}
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
static double val(void *priv, double ch)
Definition aeval.c:77
int32_t
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define f(width, name)
Definition cbs_vp8.c:236
#define s(width, name)
Definition cbs_vp9.c:198
#define xs(width, name, var, subs,...)
Definition cbs_vp9.c:222
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static __device__ float fabsf(float a)
static __device__ float fabs(float a)
int high
Definition dovi_rpuenc.c:39
enum AVCodecID id
Definition dts2pts.c:607
static struct @346255127015250356166251341105367306144006377143 state
static const uint8_t bits[8]
Definition fastaudio.c:100
const char * key
#define float_near_ulp
Definition utils.h:448
#define float_near_abs_eps
Definition utils.h:449
#define float_near_abs_eps_ulp
Definition utils.h:450
#define double_near_abs_eps
Definition utils.h:454
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
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
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
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
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
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
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
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
CHECKASM_API 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_API void checkasm_init_mask8(uint8_t *buf, int width, uint8_t mask)
Initialize a uint8_t buffer with pathological values within a mask.
void checkasm_randomize_normf(float *buf, int width)
Fill a float buffer with values from a standard normal distribution.
Definition utils.c:367
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
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
void checkasm_randomize(void *buf, size_t bytes)
Fill a buffer with uniformly chosen random bytes.
Definition utils.c:290
void checkasm_clear16(uint16_t *buf, int width, uint16_t val)
Fill a uint16_t buffer with a constant value.
Definition utils.c:382
void checkasm_clear(void *buf, size_t bytes)
Clear a buffer to a pre-determined pattern (currently 0xAA)
Definition utils.c:372
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
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
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
void checkasm_randomize_norm(double *buf, int width)
Fill a double buffer with values from a standard normal distribution.
Definition utils.c:362
void checkasm_randomize_dist(double *buf, int width, CheckasmDist dist)
Fill a double buffer with normally distributed random values.
Definition utils.c:352
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
void checkasm_randomize_distf(float *buf, int width, CheckasmDist dist)
Fill a float buffer with normally distributed random values.
Definition utils.c:357
void checkasm_init(void *buf, size_t bytes)
Initialize a buffer with pathological test patterns.
Definition utils.c:431
void checkasm_clear8(uint8_t *buf, int width, uint8_t val)
Fill a uint8_t buffer with a constant value.
Definition utils.c:377
int checkasm_rand(void)
Generate a random non-negative integer.
Definition utils.c:248
double checkasm_randf(void)
Generate a random double-precision floating-point number.
Definition utils.c:254
CHECKASM_API uint32_t checkasm_rand_uint32(void)
Generate a random 32-bit unsigned integer.
int a
cl_device_type type
#define b
Definition input.c:43
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition intra.c:278
#define u(width, name, range_min, range_max)
Definition cbs_apv.c:68
const char * arg
Definition jacosubdec.c:65
uint8_t w
Definition llvidencdsp.c:39
static int use_color
Definition log.c:127
static const uint16_t mask[17]
Definition lzw.c:38
enum AVColorRange range
const char * name
Definition qsvenc.c:142
#define vsnprintf
Definition snprintf.h:36
#define snprintf
Definition snprintf.h:34
static unsigned int stdc_leading_zeros_ui(unsigned int value)
Definition stdbit.h:61
Describes a normal (Gaussian) distribution.
Definition utils.h:147
double stddev
Standard deviation (spread) of the distribution.
Definition utils.h:149
double mean
Mean (center) of the distribution.
Definition utils.h:148
FILE * file
Definition internal.h:134
uint32_t s2[CHECKASM_PRNG_NUM]
Definition utils.c:127
uint32_t s1[CHECKASM_PRNG_NUM]
Definition utils.c:126
uint32_t s0[CHECKASM_PRNG_NUM]
Definition utils.c:125
uint32_t s3[CHECKASM_PRNG_NUM]
Definition utils.c:128
Test writing API for checkasm.
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.
static uint8_t tmp[40]
Definition aes_ctr.c:52
Utility functions for checkasm tests.
static void * checkasm_mallocz(const size_t size)
Definition internal.h:197
#define NOINLINE
Definition internal.h:59
int checkasm_vfprintf(FILE *const f, int color, const char *fmt, va_list arg) CHECKASM_PRINTF(3
#define ALWAYS_INLINE
Definition internal.h:72
#define COLD
Definition internal.h:45
uint64_t checkasm_gettime_nsec_diff(uint64_t t)
Definition utils.c:112
static int get_terminal_width(void)
Definition utils.c:550
@ PAT_HIGH
Definition utils.c:425
@ PAT_RAND
Definition utils.c:423
@ PAT_ALTLO
Definition utils.c:426
@ PAT_MIX
Definition utils.c:428
@ PAT_ONE
Definition utils.c:422
@ PAT_ALTHI
Definition utils.c:427
@ PAT_LOW
Definition utils.c:424
@ PAT_ZERO
Definition utils.c:421
#define DEF_CHECKASM_CHECK_BODY(compare, type, fmt, fmtw)
Definition utils.c:799
static double marsaglia(double *z2)
Definition utils.c:260
void checkasm_json(CheckasmJson *json, const char *key, const char *const fmt,...)
Definition utils.c:570
int num32
Definition utils.c:185
COLD void checkasm_setup_fprintf(void)
Definition utils.c:544
void checkasm_srand(unsigned seed)
Definition utils.c:229
uint64_t checkasm_gettime_nsec(void)
Definition utils.c:107
#define cmp_float(a, b, len)
static char statusline[256]
Definition utils.c:469
void checkasm_json_str(CheckasmJson *json, const char *key, const char *str)
Definition utils.c:586
uint64_t buf64[PRNG_CACHE_SIZE > > 3]
Definition utils.c:182
#define PRNG_CACHE_SIZE
Definition utils.c:178
static struct @043020231131004364200274111274237375066036336150 prng_cache
static ALWAYS_INLINE uint32_t rotl(const uint32_t x, int k)
Definition utils.c:133
uint32_t buf32[PRNG_CACHE_SIZE > > 2]
Definition utils.c:181
static void prng(CheckasmRand *restrict xs, uint8_t *restrict buf, size_t size)
Definition utils.c:154
double checkasm_rand_norm(void)
Generate a random number from the standard normal distribution.
Definition utils.c:274
uint8_t buf8[PRNG_CACHE_SIZE]
Definition utils.c:179
#define DEF_CHECKASM_INIT_MASK(BITS, PIXEL)
Definition utils.c:436
int num8
Definition utils.c:183
#define CHECKASM_PRNG_NUM
Definition utils.c:124
static int clz(const unsigned int mask)
Definition utils.c:407
NOINLINE void checkasm_noop(void *ptr)
Definition utils.c:63
static uint64_t splitmix64(uint64_t *state)
Definition utils.c:220
void checkasm_json_pop(CheckasmJson *json, char type)
Definition utils.c:627
void checkasm_statusline(const char *status)
Definition utils.c:501
static COLD int should_use_color(FILE *const f)
Definition utils.c:522
int num16
Definition utils.c:184
static int use_printf_color[2]
Definition utils.c:468
static int is_negative(const intfloat u)
Definition utils.c:646
static CheckasmRand checkasm_prng
Definition utils.c:131
unsigned checkasm_seed(void)
Definition utils.c:117
void checkasm_json_push(CheckasmJson *json, const char *const key, const char type)
Definition utils.c:611
static ALWAYS_INLINE void xoshiro128pp(CheckasmRand *restrict xs, uint32_t *restrict buf)
Definition utils.c:139
#define RANDOMIZE_DIST(buf, ftype, width, mean, stddev)
Definition utils.c:337
static int shift_rand(int x)
Definition utils.c:414
static ALWAYS_INLINE uint64_t gettime_nsec(int is_seed)
Definition utils.c:68
int num64
Definition utils.c:186
char * checkasm_vasprintf(const char *fmt, va_list arg)
Definition utils.c:853
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
double checkasm_rand_dist(CheckasmDist dist)
Generate a normally distributed random number.
Definition utils.c:285
#define DEF_CHECKASM_RAND(BITS, TYPE, NAME)
Definition utils.c:192
uint16_t buf16[PRNG_CACHE_SIZE > > 1]
Definition utils.c:180
static int statusline_visible
Definition utils.c:470
#define DEF_CHECKASM_CHECK_FUNC(type, fmt, fmtw)
Definition utils.c:824
#define width
Definition dsp.h:89
int size
float f
Definition utils.c:642
uint32_t i
Definition utils.c:643
#define va_copy(dst, src)
Definition va_copy.h:31
static unsigned int seed
Definition videogen.c:78
int len