/* * vlibc — rand/srand, abs/div, qsort/bsearch test (todo 12). * * Exercises the stdlib todo-12 additions end to end: * * 1. qsort on 0 and 1 element (the comparator is never called), 2048 * pseudo-random ints, duplicate-heavy, reverse-sorted, and * all-equal arrays — each verified ascending with the element-sum * checksum preserved. * 2. bsearch finds a present key and returns NULL for an absent one. * 3. rand is deterministic per seed and stays inside [0, RAND_MAX]; * div(7,3) == {2,1}, div(-7,3) == {-2,-1}, ldiv/lldiv spot checks; * abs/labs/llabs basics plus the INT_MIN/LONG_MIN/LLONG_MIN wrap * through volatile locals so the compiler cannot fold the negation. * 4. Level 2: rand_r determinism and independence from rand()'s * global state, srand48 reseed determinism, drand48 samples in * [0,1), lrand48 in [0,2^31), erand48 stepping the caller's xsubi. * * The instrumented comparator counts calls and records any call where * both arguments point at the same element; every scenario asserts the * self-compare flag stays clear. * * The -f mode stress-sorts three static-BSS arrays of 100000 ints * (reverse-sorted, already-sorted, all-equal) with the instrumented * comparator, verifies ascending order and an O(n log n) comparison * count (< 5.2M, far below the ~5e9 an O(n^2) sort would need), and * exits plainly — nothing here writes errno, so a normal return is * safe. * * All diagnostics go through raw SYS_write (no stdio): under -Iinclude * the vlibc public headers shadow GCC's internal ones, so a host * would not compile. The stdlib.h below is vlibc's own. * * Not part of the library proper; compiled manually for this todo (the * tests/ + make check wiring is owned by a later todo). */ #include #include #include "../include/stdlib.h" #include "../src/internal/syscall.h" static int failures; /* Write a NUL-terminated string to fd via the raw syscall layer. */ static void say(int fd, const char *s) { long n = 0; while (s[n] != '\0') { n++; } __syscall3(SYS_write, fd, (long)s, n); } /* Write v in decimal to fd. */ static void say_dec(int fd, unsigned long v) // NOLINT(bugprone-easily-swappable-parameters) { char buf[24]; int i = (int)sizeof(buf); buf[--i] = '\0'; do { buf[--i] = (char)('0' + (v % 10)); v /= 10; } while (v != 0); __syscall3(SYS_write, fd, (long)(buf + i), (long)(sizeof(buf) - 1 - i)); } static void check(int cond, const char *what) { if (cond) { say(1, "PASS: "); say(1, what); say(1, "\n"); } else { say(2, "FAIL: "); say(2, what); say(2, "\n"); failures++; } } /* xorshift32; deterministic, allocation-independent. */ static unsigned rng_next(unsigned *state) { unsigned x = *state; x ^= x << 13; x ^= x >> 17; x ^= x << 5; *state = x; return x; } /* Instrumented int comparator: counts calls and records self-compares. */ static long compar_count; static int self_compare_seen; static int int_cmp(const void *a, const void *b) { const int *pa = a; const int *pb = b; compar_count++; if (a == b) { self_compare_seen = 1; } if (*pa < *pb) { return -1; } if (*pa > *pb) { return 1; } return 0; } /* Sort arr (n ints) and verify ascending order and a preserved sum. */ static int sort_and_verify(int *arr, size_t n, const char *what) { size_t i; long long before = 0; long long after = 0; int asc = 1; int ok = 1; for (i = 0; i < n; i++) { before += arr[i]; } compar_count = 0; qsort(arr, n, sizeof(int), int_cmp); for (i = 1; i < n; i++) { if (arr[i - 1] > arr[i]) { asc = 0; } } for (i = 0; i < n; i++) { after += arr[i]; } if (!asc) { say(2, "FAIL: "); say(2, what); say(2, " - not ascending\n"); failures++; ok = 0; } if (before != after) { say(2, "FAIL: "); say(2, what); say(2, " - element sum changed\n"); failures++; ok = 0; } if (ok) { say(1, "PASS: "); say(1, what); say(1, "\n"); } return ok; } static void qsort_scenarios(void) { enum { N = 2048 }; int arr[N]; unsigned i; unsigned rng = 0x2545F491U; int one = 7; compar_count = 0; qsort(arr, 0, sizeof(int), int_cmp); qsort(&one, 1, sizeof(int), int_cmp); check(compar_count == 0, "qsort(0) and qsort(1) never call the comparator"); for (i = 0; i < N; i++) { arr[i] = (int)rng_next(&rng); } sort_and_verify(arr, N, "2048 pseudo-random ints sort"); for (i = 0; i < N; i++) { arr[i] = (int)(i % 7); } sort_and_verify(arr, N, "2048 duplicate-heavy ints sort"); for (i = 0; i < N; i++) { arr[i] = (int)(N - 1 - i); } sort_and_verify(arr, N, "2048 reverse-sorted ints sort"); for (i = 0; i < N; i++) { arr[i] = 5; } sort_and_verify(arr, N, "2048 all-equal ints sort"); check(self_compare_seen == 0, "comparator never saw the same element twice"); } static void bsearch_tests(void) { int arr[16]; unsigned i; int key; int *p; for (i = 0; i < 16; i++) { arr[i] = (int)(i * 2); } key = 14; p = bsearch(&key, arr, 16, sizeof(int), int_cmp); check(p != NULL && *p == key, "bsearch finds a present key"); key = 13; p = bsearch(&key, arr, 16, sizeof(int), int_cmp); check(p == NULL, "bsearch returns NULL for an absent key"); check(self_compare_seen == 0, "bsearch never self-compared"); } static void rand_tests(void) { int a[8]; int b[8]; unsigned i; int same = 1; int in_range = 1; srand(1); for (i = 0; i < 8; i++) { a[i] = rand(); } srand(1); for (i = 0; i < 8; i++) { b[i] = rand(); } for (i = 0; i < 8; i++) { if (a[i] != b[i]) { same = 0; } } check(same, "srand(1) twice gives identical rand sequences"); srand(12345); for (i = 0; i < 1000; i++) { int v = rand(); if (v < 0 || v > RAND_MAX) { in_range = 0; } } check(in_range, "rand values stay within [0, RAND_MAX]"); } static void div_tests(void) { div_t d = div(7, 3); ldiv_t ld = ldiv(17L, 5L); ldiv_t ldn = ldiv(-17L, 5L); lldiv_t lld = lldiv(1000000000000000LL, 3LL); check(d.quot == 2 && d.rem == 1, "div(7,3) == {2,1}"); d = div(-7, 3); check(d.quot == -2 && d.rem == -1, "div(-7,3) == {-2,-1}"); check(ld.quot == 3L && ld.rem == 2L, "ldiv(17,5) == {3,2}"); check(ldn.quot == -3L && ldn.rem == -2L, "ldiv(-17,5) == {-3,-2}"); check(lld.quot == 333333333333333LL && lld.rem == 1LL, "lldiv(10^15,3) == {333333333333333,1}"); } /* * noipa proxies for the abs family: GCC knows abs/labs/llabs as const * builtins whose result lies in [0, type_MAX], so even with a volatile * argument its value-range analysis proves `abs(vi) == INT_MIN` false * and folds the check before the call. noipa blocks interprocedural * propagation through the proxy (the repo's established pattern, see * the SIZE_MAX-10 handling above); the volatile locals then force the * wrap values to exist at runtime. */ static __attribute__((noipa)) int abs_proxy(int n) { return abs(n); } static __attribute__((noipa)) long labs_proxy(long n) { return labs(n); } static __attribute__((noipa)) long long llabs_proxy(long long n) { return llabs(n); } static void abs_tests(void) { volatile int vi = INT_MIN; volatile long vl = LONG_MIN; volatile long long vll = LLONG_MIN; check(abs(-7) == 7 && abs(7) == 7, "abs basics"); check(labs(-7L) == 7L, "labs basics"); check(llabs(-7LL) == 7LL, "llabs basics"); check(abs_proxy(vi) == INT_MIN, "abs(INT_MIN) wraps to INT_MIN"); check(labs_proxy(vl) == LONG_MIN, "labs(LONG_MIN) wraps to LONG_MIN"); check(llabs_proxy(vll) == LLONG_MIN, "llabs(LLONG_MIN) wraps to LLONG_MIN"); } #if VLIBC_LEVEL_GE(2) static void rand_r_tests(void) { unsigned s1 = 123; unsigned s2 = 123; unsigned s3 = 99; int r1 = rand_r(&s1); int r2 = rand_r(&s2); int before; int after; check(r1 == r2 && s1 == s2, "rand_r: equal seeds give equal results and state"); srand(7); before = rand(); rand_r(&s3); rand(); srand(7); after = rand(); check(before == after, "rand_r does not disturb rand's global state"); s1 = 5; s2 = 5; rand(); rand(); check(rand_r(&s1) == rand_r(&s2), "rand_r independent of interleaved rand() calls"); } static void drand48_tests(void) { double d1; double d2; unsigned i; int ok = 1; unsigned short xs[3] = {0x330E, 0xABCD, 0x1234}; double e1 = erand48(xs); srand48(42); d1 = drand48(); srand48(42); d2 = drand48(); check(d1 == d2, "srand48 reseed gives deterministic drand48"); srand48(1); for (i = 0; i < 1000; i++) { double v = drand48(); if (!(v >= 0.0 && v < 1.0)) { ok = 0; } } check(ok, "drand48: 1000 samples in [0,1)"); srand48(2); ok = 1; for (i = 0; i < 1000; i++) { long v = lrand48(); if (v < 0 || v >= 2147483648L) { ok = 0; } } check(ok, "lrand48: 1000 samples in [0, 2^31)"); check(!(xs[0] == 0x330E && xs[1] == 0xABCD && xs[2] == 0x1234), "erand48 steps the caller's xsubi in place"); srand48(0x1234ABCDL); check(e1 == drand48(), "erand48(default xsubi) matches srand48(0x1234ABCD) drand48"); } #endif /* VLIBC_LEVEL_GE(2) */ /* -f mode: three 100k-element stress sorts from static BSS (no malloc). */ #define BIG_N 100000 static int big_array[BIG_N]; static int stress_case(const char *label) { unsigned i; int asc = 1; compar_count = 0; qsort(big_array, BIG_N, sizeof(int), int_cmp); for (i = 1; i < BIG_N; i++) { if (big_array[i - 1] > big_array[i]) { asc = 0; } } say(1, label); say(1, " comparisons: "); say_dec(1, (unsigned long)compar_count); say(1, "\n"); if (!asc) { say(2, "FAIL: "); say(2, label); say(2, " - not ascending\n"); return 1; } if (compar_count >= 5200000L) { say(2, "FAIL: "); say(2, label); say(2, " - comparisons >= 5200000 (not O(n log n))\n"); return 1; } if (self_compare_seen) { say(2, "FAIL: "); say(2, label); say(2, " - self-compare observed\n"); return 1; } say(1, "PASS: "); say(1, label); say(1, "\n"); return 0; } static int stress_mode(void) { unsigned i; int rc = 0; self_compare_seen = 0; for (i = 0; i < BIG_N; i++) { big_array[i] = (int)(BIG_N - 1 - i); } rc |= stress_case("100k reverse-sorted"); for (i = 0; i < BIG_N; i++) { big_array[i] = (int)i; } rc |= stress_case("100k already-sorted"); for (i = 0; i < BIG_N; i++) { big_array[i] = 42; } rc |= stress_case("100k all-equal"); return rc; } int main(int argc, char **argv) { if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f') { return stress_mode(); } qsort_scenarios(); bsearch_tests(); rand_tests(); div_tests(); abs_tests(); #if VLIBC_LEVEL_GE(2) rand_r_tests(); drand48_tests(); #endif if (failures > 0) { say(2, "FAILED ("); say_dec(2, (unsigned long)failures); say(2, " check(s))\n"); return 1; } say(1, "all rand/div/qsort/bsearch tests passed\n"); return 0; }