File size: 7,947 Bytes
28c70af | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | /* SPDX-License-Identifier: MIT OR Apache-2.0 */
/*
* The printf oracle for bankml_log (0.3.2): for every case, the message bankml_log delivers to the sink must equal,
* byte for byte (length included, an embedded NUL included), what this machine's libc snprintf writes for the same
* format and arguments. Then the cases bankml_log does not support: each must come out as its explicit marker, read
* no argument it cannot type, and never write through %n.
* cc -O2 -Wall -Wextra -Wno-format -Werror printf_oracle.c -I capi/include -L target/release -lbankml
* (-Wno-format: some cases are deliberately unusual formats.) Exit 0 only if every case passes.
*/
#include <bankml.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
static char got[1 << 16];
static size_t got_len;
static int got_level, calls, total, failed;
static void sink(int level, const char *msg, size_t len, void *user) {
(void)user;
if (len >= sizeof got) len = sizeof got - 1;
memcpy(got, msg, len);
got_len = len;
got_level = level;
calls++;
if (msg[len] != '\0') { printf(" sink: message not NUL-terminated\n"); failed++; }
}
static void show(const char *what, const char *s, size_t n) {
printf(" %s (%zu bytes): \"", what, n);
for (size_t i = 0; i < n; i++) {
unsigned char c = (unsigned char)s[i];
if (c < 32 || c == '"' || c == '\\' || c >= 127) printf("\\x%02x", c); else putchar(c);
}
printf("\"\n");
}
static void check(const char *label, const char *want, size_t want_len) {
total++;
if (got_level != BANKML_LOG_INFO || got_len != want_len || memcmp(got, want, want_len) != 0) {
failed++;
printf(" DIFFERS: %s\n", label);
show("libc snprintf", want, want_len);
show("bankml_log ", got, got_len);
}
}
/* the same format and arguments through snprintf and bankml_log */
#define SAME(...) \
do { \
static char want_[1 << 16]; \
int n_ = snprintf(want_, sizeof want_, __VA_ARGS__); \
int before_ = calls; \
bankml_log(BANKML_LOG_INFO, __VA_ARGS__); \
if (calls != before_ + 1) { failed++; printf(" no message: %s\n", #__VA_ARGS__); } \
check(#__VA_ARGS__, want_, (size_t)n_); \
} while (0)
/* a case bankml_log does not support: the exact marker text */
#define MARKED(want, ...) \
do { \
bankml_log(BANKML_LOG_INFO, __VA_ARGS__); \
check(#__VA_ARGS__, want, strlen(want)); \
} while (0)
int main(void) {
bankml_set_log(sink, NULL);
int local = 0;
const char *s = "bankml";
const char unterminated[2] = {'a', 'b'};
/* integers, at their limits and in every supported length */
SAME("%d|%d|%d|%d|%d", 0, 1, -1, INT_MIN, INT_MAX);
SAME("%i|%i", INT_MIN, INT_MAX);
SAME("%u|%u|%u", 0u, 1u, UINT_MAX);
SAME("%ld|%ld|%ld", LONG_MIN, LONG_MAX, 0L);
SAME("%lu|%lu", ULONG_MAX, 0UL);
SAME("%lld|%lld|%lld", LLONG_MIN, LLONG_MAX, -1LL);
SAME("%llu|%llu", ULLONG_MAX, 0ULL);
SAME("%zu|%zu|%zd|%zd", SIZE_MAX, (size_t)0, (ssize_t)-1, (ssize_t)PTRDIFF_MAX);
SAME("%hhd|%hhd|%hd|%hd|%hhu|%hu", 300, -129, 70000, -32769, 511u, 65537u);
SAME("%x|%X|%x|%x|%X", 0xdeadbeefu, 0xdeadbeefu, 0u, UINT_MAX, 0xabcu);
SAME("%#x|%#X|%#x", 255u, 255u, 0u);
SAME("%lx|%llX|%zx|%hhx|%hx", ULONG_MAX, ULLONG_MAX, SIZE_MAX, 0x1ffu, 0x12345u);
SAME("[%5d|%-5d|%05d|%+d|% d|%+ d|%+d]", 42, 42, -42, 7, 7, 7, -7);
SAME("[%.3d|%.0d|%.0d|%8.3d|%-8.3d|%08.3d|%.10d]", 5, 0, 1, -5, -5, 5, INT_MIN);
SAME("[%08x|%#010x|%-#10x|%#.6x|%.0x|%#.0x]", 0xbeefu, 0xbeefu, 0xbeefu, 0xbeefu, 0u, 0u);
SAME("[%*d|%-*d|%*d|%.*d|%.*d|%*.*d]", 6, 42, 6, 42, -6, 42, 4, 7, -1, 7, 8, 5, -3);
SAME("[%+u|% u|%+x|% X]", 3u, 3u, 255u, 255u);
SAME("[%020lld|%-20llu|%+lld]", LLONG_MIN, ULLONG_MAX, LLONG_MAX);
/* %f: precision, rounding (ties to even on the exact binary value), signs, flags, extremes, inf and nan */
SAME("%f|%f|%f|%f", 0.0, -0.0, 1.0, -1.0);
SAME("%f|%.3f|%.3f|%.1f", 3.14258, -2.0005, 2.0005, 0.25);
SAME("%.0f|%.0f|%.0f|%.0f|%.0f|%.0f", 0.5, 1.5, 2.5, -0.5, 0.49999999999999994, 1e15 + 0.5);
SAME("%.20f|%.17f|%.10f|%.30f", 0.1, 0.1 + 0.2, 1.0 / 3.0, 1e-20);
SAME("%f|%f", 1e300, -1e300);
SAME("%f|%.320f", DBL_MIN, DBL_MIN);
SAME("%.1100f", 4.9406564584124654e-324);
SAME("%f", DBL_MAX);
SAME("[%#.0f|%#f|%#.2f]", 3.0, 3.0, 3.0);
SAME("[%10.2f|%-10.2f|%010.2f|%+.1f|% .1f|%+010.3f|% 010.3f]", -1.005, 1.0, -3.25, 0.0, 0.0, 2.5, 2.5);
SAME("[%*.*f|%-*.*f|%.*f]", 12, 4, 2.718281828, 12, 4, 2.718281828, -1, 2.718281828);
SAME("[%lf|%F|%F]", 2.5, 1.25, -1.25);
SAME("[%f|%f|%f|%f]", INFINITY, -INFINITY, NAN, -NAN);
SAME("[%F|%F|%F|%F]", INFINITY, -INFINITY, NAN, -NAN);
SAME("[%5f|%-6f|%05f|%+f|% f|%010F]", INFINITY, NAN, INFINITY, INFINITY, NAN, -INFINITY);
SAME("%.3f|%.3f|%.3f", 1e-4, 9.9995, 0.0005);
/* strings */
SAME("[%s|%s|%10s|%-10s|%.3s|%.0s|%.10s]", s, "", s, s, s, s, s);
SAME("[%.*s|%*s|%-*s|%*.*s]", 2, s, -9, s, 9, s, 8, 3, s);
SAME("[%s|%.5s|%.6s]", "Sav\xc3\xa9 \xf0\x9f\x98\x80", "Sav\xc3\xa9 \xf0\x9f\x98\x80", "Sav\xc3\xa9 \xf0\x9f\x98\x80");
SAME("[%.2s]", unterminated); /* a precision bounds the read: no NUL needed */
SAME("[%s|%.3s|%.6s|%10s]", (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL);
/* characters, an embedded NUL and a high byte included */
SAME("[%c%c|%3c|%-3c|%c|%c]", 'o', 'k', 'x', 'y', 0, 255);
/* pointers */
SAME("[%p|%p|%p]", (void *)0x1234, (void *)NULL, (void *)&local);
SAME("[%20p|%-20p|%8p|%-8p]", (void *)s, (void *)s, (void *)NULL, (void *)NULL);
SAME("[%p]", (void *)UINTPTR_MAX);
/* %% and a mix */
SAME("100%%");
SAME("%s=%d (%.2f%%) %c %zu %#x %p end", "turn", -3, 99.5, 'Z', (size_t)4096, 48879u, (void *)0xff);
SAME("no conversions at all");
SAME("%s", "");
int supported = total, supported_failed = failed;
/* what bankml_log does not support: an explicit marker, never a guess */
MARKED("%<unsupported:e>", "%e", 2.0);
MARKED("1 %<unsupported:g> %<skipped:d> %<skipped:s> % end", "%d %g %d %s %% end", 1, 2.0, 3, s);
MARKED("%<unsupported:o>", "%o", 8u);
MARKED("%<unsupported:a>|%<skipped:d>", "%a|%d", 1.0, 2);
MARKED("%<unsupported:Lf>", "%Lf", (long double)1.5);
MARKED("%<unsupported:ls>", "%ls", L"wide");
MARKED("%<unsupported:jd>", "%jd", (intmax_t)5);
MARKED("%<unsupported:+s>|5", "%+s|%d", s, 5); /* a known type: its argument is read and dropped, the rest goes on */
MARKED("%<unsupported:05c>|6", "%05c|%d", 'q', 6);
MARKED("%<unsupported:#p>|7", "%#p|%d", (void *)s, 7);
MARKED("%<unsupported:#d>|8", "%#d|%d", 1, 8);
MARKED("%<unsupported:.3c>|9", "%.3c|%d", 'q', 9);
MARKED("%<unsupported:70000d>|10", "%70000d|%d", 1, 10);
MARKED("%<unsupported:5%>|11", "%5%|%d", 11);
MARKED("tail %<incomplete:>", "tail %");
int sentinel = 12345;
MARKED("before %<unsupported:n> %<skipped:d>", "before %n %d", &sentinel, 1);
total++;
if (sentinel != 12345) { failed++; printf(" DIFFERS: %%n wrote through its argument\n"); }
printf("printf oracle: %d of %d formats byte-identical to libc snprintf (ints at their limits, every length, %%f "
"precision and rounding, inf/nan, strings, %%c with NUL, %%p, %%%%, width/precision/*); %d of %d unsupported "
"cases written as their marker, no argument guessed, %%n never written\n",
supported - supported_failed, supported, (total - supported) - (failed - supported_failed), total - supported);
bankml_set_log(NULL, NULL);
return failed == 0 ? 0 : 1;
}
|