43 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
44 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x04
51 #include <sys/ioctl.h>
55#if defined(__APPLE__) && defined(__MACH__)
56 #include <mach/mach_time.h>
71 static LARGE_INTEGER freq;
74 if (!QueryPerformanceFrequency(&freq))
77 if (!QueryPerformanceCounter(&ts))
79 return UINT64_C(1000000000) * ts.QuadPart / freq.QuadPart;
80#elif defined(__APPLE__) && defined(__MACH__)
81 static mach_timebase_info_data_t tb_info;
83 if (mach_timebase_info(&tb_info) != KERN_SUCCESS)
86 return mach_absolute_time() * tb_info.numer / tb_info.denom;
87#elif HAVE_CLOCK_GETTIME
91 #ifdef CLOCK_MONOTONIC_RAW
92 id = CLOCK_MONOTONIC_RAW;
99 if (clock_gettime(
id, &ts) < 0)
101 return UINT64_C(1000000000) * ts.tv_sec + ts.tv_nsec;
124#define CHECKASM_PRNG_NUM 4
135 return (x << k) | (x >> (32 - k));
144 const uint32_t t =
xs->s1[
i] << 9;
157 const size_t block_size =
sizeof(
tmp);
160 while (
size >= block_size) {
162 memcpy(buf,
tmp, block_size);
178 #define PRNG_CACHE_SIZE 64
190 "PRNG_CACHE_SIZE should be a multiple of uint32_t[CHECKASM_PRNG_NUM]");
192#define DEF_CHECKASM_RAND(BITS, TYPE, NAME) \
193 TYPE checkasm_rand_##NAME(void) \
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); \
203 uint##BITS##_t raw; \
205 val.raw = prng_cache.buf##BITS[--prng_cache.num##BITS]; \
222 uint64_t z = (*
state += 0x9e3779b97f4a7c15);
224 z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
225 z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
226 return z ^ (z >> 31);
250 static_assert(
sizeof(int) <=
sizeof(uint32_t),
"int larger than 32 bits");
266 w = u1 * u1 + u2 * u2;
269 w = sqrt((-2.0 * log(
w)) /
w);
278 if ((cached = !cached)) {
311 const double scale =
range / (UINT32_MAX + 1.0);
325 const double scale = (
high - low) / (
double) UINT32_MAX;
332 const float scale = (
high - low) / (
float) UINT32_MAX;
337#define RANDOMIZE_DIST(buf, ftype, width, mean, stddev) \
340 *(buf)++ = (ftype) ((mean) + (stddev) * checkasm_rand_norm()); \
344 for (; width; width -= 2) { \
346 z1 = marsaglia(&z2); \
347 *(buf)++ = (ftype) ((mean) + (stddev) * z1); \
348 *(buf)++ = (ftype) ((mean) + (stddev) * z2); \
374 memset(buf, 0xAA, bytes);
391static inline int clz(
const unsigned int mask)
396#elif defined(_MSC_VER) && !defined(__clang__)
399static inline int clz(
const unsigned int mask)
401 unsigned long leading_zero = 0;
402 _BitScanReverse(&leading_zero,
mask);
403 return (31 - leading_zero);
409 return __builtin_clz(
mask);
416 const int bits = 8 *
sizeof(x) -
clz(x);
436#define DEF_CHECKASM_INIT_MASK(BITS, PIXEL) \
437 void checkasm_init_mask##BITS(PIXEL *buf, const int width, const PIXEL mask_pixel) \
442 int step = 0, mode = 0, mask = mask_pixel; \
443 for (int i = 0; i < width; i++, step--) { \
445 step = imax(shift_rand(width), 1); \
446 mode = checkasm_rand_uint8() & 7; \
447 mask = shift_rand(mask_pixel); \
450 const PIXEL low = checkasm_rand_uint##BITS() & mask; \
451 const PIXEL high = mask_pixel - low; \
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; \
475 size_t fmt_len = strlen(fmt);
478 return vfprintf(
f, fmt,
arg);
481 fprintf(
f,
"\r\033[K");
486 fprintf(
f,
"\x1b[0;%dm",
color);
488 int ret = vfprintf(
f, fmt,
arg);
491 fprintf(
f,
"\x1b[0m");
493 if (
f == stderr &&
statusline[0] && fmt[fmt_len - 1] ==
'\n') {
512 fprintf(stderr,
"\r\033[K");
525 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
526 HANDLE con = GetStdHandle(
f == stderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
528 return con && con != INVALID_HANDLE_VALUE && GetConsoleMode(con, &con_mode)
529 && SetConsoleMode(con, con_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
534 if (isatty(
f == stderr ? 2 : 1)) {
535 const char *
const term = getenv(
"TERM");
536 return term && strcmp(term,
"dumb");
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;
558#elif defined(__OS2__)
562#elif HAVE_IOCTL && defined(TIOCGWINSZ)
564 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &
w) != -1)
572 assert(json->
level > 0);
574 for (
int i = 0;
i < json->
level;
i++)
575 fputc(
' ', json->
file);
580 fprintf(json->
file,
"\"%s\": ",
key);
581 vfprintf(json->
file, fmt, ap);
588 assert(json->
level > 0);
590 for (
int i = 0;
i < json->
level;
i++)
591 fputc(
' ', json->
file);
594 fprintf(json->
file,
"\"%s\": \"",
key);
596 fputc(
'"', json->
file);
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;
607 fputc(
'"', json->
file);
614 for (
int i = 0;
i < json->
level;
i++)
615 fputc(
' ', json->
file);
629 assert(json->
level >= 2);
632 fputc(
'\n', json->
file);
633 for (
int i = 0;
i < json->
level;
i++)
634 fputc(
' ', json->
file);
663 if (llabs((
int64_t) x.
i - y.
i) <= max_ulp)
670 const unsigned max_ulp,
const int len)
672 for (
int i = 0;
i <
len;
i++)
685 const float eps,
const int len)
687 for (
int i = 0;
i <
len;
i++)
695 const unsigned max_ulp)
701 const float eps,
const unsigned max_ulp,
704 for (
int i = 0;
i <
len;
i++)
717 const double eps,
const unsigned len)
719 for (
unsigned i = 0;
i <
len;
i++)
727 const int w,
const int h,
int *
const err)
734 fprintf(stderr,
"%s (%dx%d):\n",
name,
w,
h);
738#define PRINT_LINE(buf1, buf2, xstart, xend, xpad, fmt, fmtw) \
740 for (int x = xstart; x < xend; x++) { \
741 if (buf1[x] != buf2[x]) \
742 checkasm_fprintf(stderr, COLOR_RED, " " fmt, buf1[x]); \
744 fprintf(stderr, " " fmt, buf1[x]); \
746 for (int pad = xend; pad < xstart + xpad; pad++) \
747 fprintf(stderr, &" "[9 - fmtw]); \
750#define PRINT_RECT(type, buf1, buf2, ystart, yend, xstart, xend, fmt, fmtw) \
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) \
760 checkasm_fprintf(stderr, COLOR_BLUE, "%3d: ", y); \
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"); \
771 fprintf(stderr, "."); \
773 fprintf(stderr, "\n"); \
780#define CHECK_RECT(buf1, buf2, ystart, yend, xstart, xend, msg, compare, type, fmt, \
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)) \
787 if (check_err(file, line, name, w, h, &err)) \
790 int yprint = y < 0 ? y : ystart; \
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); \
799#define DEF_CHECKASM_CHECK_BODY(compare, type, fmt, fmtw) \
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); \
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); \
816 CHECK_RECT(buf1, buf2, 0, h, -padding, 0, "overwrite left", compare, type, fmt, \
818 CHECK_RECT(buf1, buf2, 0, h, aligned_w, aligned_w + padding, "overwrite right", \
819 compare, type, fmt, fmtw); \
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) \
830 DEF_CHECKASM_CHECK_BODY(cmp_int, type, fmt, fmtw); \
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)
848#define cmp_float(a, b, len) float_near_ulp_array(a, b, max_ulp, len)
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
static double val(void *priv, double ch)
#define i(width, name, range_min, range_max)
#define xs(width, name, var, subs,...)
static __device__ float fabsf(float a)
static __device__ float fabs(float a)
static struct @346255127015250356166251341105367306144006377143 state
static const uint8_t bits[8]
#define float_near_abs_eps
#define float_near_abs_eps_ulp
#define double_near_abs_eps
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.
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.
int checkasm_float_near_ulp(const float a, const float b, const unsigned max_ulp)
Compare floats using ULP (Units in Last Place) tolerance.
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.
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.
int checkasm_float_near_abs_eps(const float a, const float b, const float eps)
Compare floats using absolute epsilon tolerance.
int checkasm_double_near_abs_eps(const double a, const double b, const double eps)
Compare doubles using absolute epsilon tolerance.
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.
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.
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.
void checkasm_randomize_intervalf(float *buf, int width, float low, float high)
Fill a float buffer with random values chosen uniformly from an 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.
void checkasm_randomize(void *buf, size_t bytes)
Fill a buffer with uniformly chosen random bytes.
void checkasm_clear16(uint16_t *buf, int width, uint16_t val)
Fill a uint16_t buffer with a constant value.
void checkasm_clear(void *buf, size_t bytes)
Clear a buffer to a pre-determined pattern (currently 0xAA)
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.
void checkasm_randomize_rangef(float *buf, int width, float range)
Fill a float buffer with random values chosen uniformly below a limit.
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.
void checkasm_randomize_norm(double *buf, int width)
Fill a double buffer with values from a standard normal distribution.
void checkasm_randomize_dist(double *buf, int width, CheckasmDist dist)
Fill a double buffer with normally distributed random values.
void checkasm_randomize_range(double *buf, int width, double range)
Fill a double buffer with random values chosen uniformly below a limit.
void checkasm_randomize_distf(float *buf, int width, CheckasmDist dist)
Fill a float buffer with normally distributed random values.
void checkasm_init(void *buf, size_t bytes)
Initialize a buffer with pathological test patterns.
void checkasm_clear8(uint8_t *buf, int width, uint8_t val)
Fill a uint8_t buffer with a constant value.
int checkasm_rand(void)
Generate a random non-negative integer.
double checkasm_randf(void)
Generate a random double-precision floating-point number.
CHECKASM_API uint32_t checkasm_rand_uint32(void)
Generate a random 32-bit unsigned integer.
static void scale(int *out, const int *in, const int w, const int h, const int shift)
#define u(width, name, range_min, range_max)
static const uint16_t mask[17]
static unsigned int stdc_leading_zeros_ui(unsigned int value)
Describes a normal (Gaussian) distribution.
double stddev
Standard deviation (spread) of the distribution.
double mean
Mean (center) of the distribution.
uint32_t s2[CHECKASM_PRNG_NUM]
uint32_t s1[CHECKASM_PRNG_NUM]
uint32_t s0[CHECKASM_PRNG_NUM]
uint32_t s3[CHECKASM_PRNG_NUM]
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.
Utility functions for checkasm tests.
static void * checkasm_mallocz(const size_t size)
int checkasm_vfprintf(FILE *const f, int color, const char *fmt, va_list arg) CHECKASM_PRINTF(3
uint64_t checkasm_gettime_nsec_diff(uint64_t t)
static int get_terminal_width(void)
#define DEF_CHECKASM_CHECK_BODY(compare, type, fmt, fmtw)
static double marsaglia(double *z2)
void checkasm_json(CheckasmJson *json, const char *key, const char *const fmt,...)
COLD void checkasm_setup_fprintf(void)
void checkasm_srand(unsigned seed)
uint64_t checkasm_gettime_nsec(void)
#define cmp_float(a, b, len)
static char statusline[256]
void checkasm_json_str(CheckasmJson *json, const char *key, const char *str)
uint64_t buf64[PRNG_CACHE_SIZE > > 3]
static struct @043020231131004364200274111274237375066036336150 prng_cache
static ALWAYS_INLINE uint32_t rotl(const uint32_t x, int k)
uint32_t buf32[PRNG_CACHE_SIZE > > 2]
static void prng(CheckasmRand *restrict xs, uint8_t *restrict buf, size_t size)
double checkasm_rand_norm(void)
Generate a random number from the standard normal distribution.
uint8_t buf8[PRNG_CACHE_SIZE]
#define DEF_CHECKASM_INIT_MASK(BITS, PIXEL)
#define CHECKASM_PRNG_NUM
static int clz(const unsigned int mask)
NOINLINE void checkasm_noop(void *ptr)
static uint64_t splitmix64(uint64_t *state)
void checkasm_json_pop(CheckasmJson *json, char type)
void checkasm_statusline(const char *status)
static COLD int should_use_color(FILE *const f)
static int use_printf_color[2]
static int is_negative(const intfloat u)
static CheckasmRand checkasm_prng
unsigned checkasm_seed(void)
void checkasm_json_push(CheckasmJson *json, const char *const key, const char type)
static ALWAYS_INLINE void xoshiro128pp(CheckasmRand *restrict xs, uint32_t *restrict buf)
#define RANDOMIZE_DIST(buf, ftype, width, mean, stddev)
static int shift_rand(int x)
static ALWAYS_INLINE uint64_t gettime_nsec(int is_seed)
char * checkasm_vasprintf(const char *fmt, va_list arg)
static int check_err(const char *const file, const int line, const char *const name, const int w, const int h, int *const err)
double checkasm_rand_dist(CheckasmDist dist)
Generate a normally distributed random number.
#define DEF_CHECKASM_RAND(BITS, TYPE, NAME)
uint16_t buf16[PRNG_CACHE_SIZE > > 1]
static int statusline_visible
#define DEF_CHECKASM_CHECK_FUNC(type, fmt, fmtw)
#define va_copy(dst, src)