feat(stdio): printf family
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -299,4 +300,36 @@ fopen64(const char *restrict path, const char *restrict mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* formatted output (todo 16) */
|
||||
|
||||
int
|
||||
printf(const char *restrict format, ...);
|
||||
int
|
||||
fprintf(FILE *restrict stream, const char *restrict format, ...);
|
||||
int
|
||||
sprintf(char *restrict s, const char *restrict format, ...);
|
||||
int
|
||||
snprintf(char *restrict s, size_t n, const char *restrict format, ...);
|
||||
int
|
||||
vprintf(const char *restrict format, va_list ap);
|
||||
int
|
||||
vfprintf(FILE *restrict stream, const char *restrict format, va_list ap);
|
||||
int
|
||||
vsprintf(char *restrict s, const char *restrict format, va_list ap);
|
||||
int
|
||||
vsnprintf(char *restrict s, size_t n, const char *restrict format, va_list ap);
|
||||
int
|
||||
dprintf(int fd, const char *restrict format, ...);
|
||||
int
|
||||
vdprintf(int fd, const char *restrict format, va_list ap);
|
||||
void
|
||||
perror(const char *s);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
int
|
||||
asprintf(char **restrict strp, const char *restrict format, ...);
|
||||
int
|
||||
vasprintf(char **restrict strp, const char *restrict format, va_list ap);
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_STDIO_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,842 @@
|
||||
/* internal unsigned 2^32-limb bignum core for printf float formatting (todo 16)
|
||||
* clean-room implementation; nothing here is exported */
|
||||
/*
|
||||
* Invariants: limbs are little-endian (value = sum d[i] * 2^(32*i)); n == 0
|
||||
* means value 0; no trailing-zero-limb invariant is kept -- callers normalize
|
||||
* via vfpn_bn_norm. Shifts and rounding operate in place; allocation failure
|
||||
* is reported as -1 and leaves errno to __libc_malloc.
|
||||
*/
|
||||
#include "../internal/malloc.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct vfpn_bn
|
||||
{
|
||||
uint32_t fixed[640];
|
||||
uint32_t *d;
|
||||
size_t n;
|
||||
size_t cap;
|
||||
int heap;
|
||||
};
|
||||
|
||||
static __attribute__((unused)) void
|
||||
vfpn_bn_init(struct vfpn_bn *b)
|
||||
{
|
||||
b->d = b->fixed;
|
||||
b->n = 0;
|
||||
b->cap = 640;
|
||||
b->heap = 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) void
|
||||
vfpn_bn_free(struct vfpn_bn *b)
|
||||
{
|
||||
if (b->heap)
|
||||
{
|
||||
__libc_free(b->d);
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_reserve(struct vfpn_bn *b, size_t need)
|
||||
{
|
||||
if (need <= b->cap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t *new = __libc_malloc(need * sizeof(uint32_t));
|
||||
if (!new)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t copy = (b->cap < need) ? b->cap : need;
|
||||
__builtin_memcpy(new, b->d, copy * sizeof(uint32_t));
|
||||
if (b->heap)
|
||||
{
|
||||
__libc_free(b->d);
|
||||
}
|
||||
|
||||
b->d = new;
|
||||
b->cap = need;
|
||||
b->heap = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) void
|
||||
vfpn_bn_norm(struct vfpn_bn *b)
|
||||
{
|
||||
while (b->n > 0 && b->d[b->n - 1] == 0)
|
||||
{
|
||||
b->n--;
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_set_u64(struct vfpn_bn *b, uint64_t v)
|
||||
{
|
||||
size_t need = 0;
|
||||
for (uint64_t t = v; t != 0; t >>= 32)
|
||||
{
|
||||
need++;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(b, need) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
b->n = 0;
|
||||
while (v != 0)
|
||||
{
|
||||
b->d[b->n++] = (uint32_t)v;
|
||||
v >>= 32;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_mul_small(struct vfpn_bn *b, uint32_t m)
|
||||
{
|
||||
if (m == 0)
|
||||
{
|
||||
b->n = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(b, b->n + 1) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t carry = 0;
|
||||
for (size_t i = 0; i < b->n; i++)
|
||||
{
|
||||
uint64_t cur = (uint64_t)b->d[i] * m + carry;
|
||||
b->d[i] = (uint32_t)cur;
|
||||
carry = cur >> 32;
|
||||
}
|
||||
|
||||
if (carry != 0)
|
||||
{
|
||||
b->d[b->n++] = (uint32_t)carry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_shl(struct vfpn_bn *b, size_t bits)
|
||||
{
|
||||
if (bits == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t wl = bits / 32;
|
||||
size_t s = bits % 32;
|
||||
size_t newn = b->n + wl + ((s != 0) ? 1 : 0);
|
||||
if (vfpn_bn_reserve(b, newn) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s == 0)
|
||||
{
|
||||
for (size_t i = b->n; i-- > 0;)
|
||||
{
|
||||
b->d[i + wl] = b->d[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < wl; i++)
|
||||
{
|
||||
b->d[i] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t j = newn; j-- > 0;)
|
||||
{
|
||||
uint64_t cur = 0;
|
||||
if (j >= wl && j - wl < b->n)
|
||||
{
|
||||
cur |= (uint64_t)b->d[j - wl] << s;
|
||||
}
|
||||
|
||||
if (j > wl && j - wl - 1 < b->n)
|
||||
{
|
||||
cur |= (uint64_t)b->d[j - wl - 1] >> (32 - s);
|
||||
}
|
||||
|
||||
b->d[j] = (uint32_t)cur;
|
||||
}
|
||||
}
|
||||
|
||||
b->n = newn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_add_one(struct vfpn_bn *b)
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < b->n)
|
||||
{
|
||||
b->d[i]++;
|
||||
if (b->d[i] != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(b, b->n + 1) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
b->d[b->n] = 1;
|
||||
b->n++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_is_zero(const struct vfpn_bn *b)
|
||||
{
|
||||
return b->n == 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_cmp(const struct vfpn_bn *a, const struct vfpn_bn *b)
|
||||
{
|
||||
if (a->n < b->n)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a->n > b->n)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_t i = a->n; i-- > 0;)
|
||||
{
|
||||
if (a->d[i] < b->d[i])
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a->d[i] > b->d[i])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_shr_round(struct vfpn_bn *b, size_t bits)
|
||||
{
|
||||
if (bits == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t w = bits / 32;
|
||||
size_t s = bits % 32;
|
||||
|
||||
if (w >= b->n)
|
||||
{
|
||||
/* Whole value discarded: result is 0, rounded up iff the original
|
||||
* value exceeds 2^(bits-1). That can only happen when bits == 32*n
|
||||
* (s == 0 and w == n), so compare the top limb against half. */
|
||||
int round = 0;
|
||||
if (s == 0 && w == b->n && b->n > 0)
|
||||
{
|
||||
uint32_t top = b->d[b->n - 1];
|
||||
if (top > 0x80000000u)
|
||||
{
|
||||
round = 1;
|
||||
}
|
||||
else if (top == 0x80000000u)
|
||||
{
|
||||
for (size_t i = b->n - 1; i-- > 0;)
|
||||
{
|
||||
if (b->d[i] != 0)
|
||||
{
|
||||
round = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b->n = 0;
|
||||
if (round)
|
||||
{
|
||||
return vfpn_bn_add_one(b);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Round bit and sticky are captured from the discarded low `bits` bits
|
||||
* of the original value before the in-place shift. */
|
||||
int round_bit;
|
||||
int sticky;
|
||||
if (s > 0)
|
||||
{
|
||||
round_bit = (int)((b->d[w] >> (s - 1)) & 1u);
|
||||
sticky = (b->d[w] & ((1u << (s - 1)) - 1u)) != 0;
|
||||
for (size_t i = 0; i < w; i++)
|
||||
{
|
||||
if (b->d[i] != 0)
|
||||
{
|
||||
sticky = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
round_bit = (int)((b->d[w - 1] >> 31) & 1u);
|
||||
sticky = (b->d[w - 1] & 0x7FFFFFFFu) != 0;
|
||||
for (size_t i = 0; i + 1 < w; i++)
|
||||
{
|
||||
if (b->d[i] != 0)
|
||||
{
|
||||
sticky = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In-place right shift by `bits`; ascending so every source limb is
|
||||
* still readable when its destination is written. */
|
||||
size_t old_n = b->n;
|
||||
if (s == 0)
|
||||
{
|
||||
for (size_t j = 0; j + w < old_n; j++)
|
||||
{
|
||||
b->d[j] = b->d[j + w];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t j = 0; j + w < old_n; j++)
|
||||
{
|
||||
uint32_t lo = b->d[j + w] >> s;
|
||||
uint32_t hi = (j + w + 1 < old_n) ? (b->d[j + w + 1] << (32 - s)) : 0;
|
||||
b->d[j] = lo | hi;
|
||||
}
|
||||
}
|
||||
|
||||
b->n = old_n - w;
|
||||
vfpn_bn_norm(b);
|
||||
|
||||
int odd = (b->n > 0 && (b->d[0] & 1u)) ? 1 : 0;
|
||||
if (round_bit && (sticky || odd))
|
||||
{
|
||||
return vfpn_bn_add_one(b);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) uint32_t
|
||||
vfpn_bn_divmod_small_1e9(struct vfpn_bn *b)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
for (size_t i = b->n; i-- > 0;)
|
||||
{
|
||||
uint64_t cur = (r << 32) | b->d[i];
|
||||
b->d[i] = (uint32_t)(cur / 1000000000ULL);
|
||||
r = cur % 1000000000ULL;
|
||||
}
|
||||
|
||||
vfpn_bn_norm(b);
|
||||
return (uint32_t)r;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_pow5(struct vfpn_bn *b, size_t k)
|
||||
{
|
||||
int r = vfpn_bn_set_u64(b, 1);
|
||||
if (r != 0)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < k; i++)
|
||||
{
|
||||
r = vfpn_bn_mul_small(b, 5);
|
||||
if (r != 0)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --- exact decimal rounding layer ------------------------------------ */
|
||||
|
||||
/*
|
||||
* Number of bits needed to hold |b| (index of the highest set bit plus one);
|
||||
* 0 for the value zero. The caller may leave a trailing zero limb in place,
|
||||
* so the scan starts from the recorded length and skips down.
|
||||
*/
|
||||
static __attribute__((unused)) size_t
|
||||
vfpn_bn_bitlen(const struct vfpn_bn *b)
|
||||
{
|
||||
if (b->n == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t i = b->n - 1;
|
||||
while (i > 0 && b->d[i] == 0)
|
||||
{
|
||||
i--;
|
||||
}
|
||||
|
||||
if (b->d[i] == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return i * 32 + (size_t)(32 - __builtin_clz(b->d[i]));
|
||||
}
|
||||
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_get_bit(const struct vfpn_bn *b, size_t i)
|
||||
{
|
||||
return (int)((b->d[i / 32] >> (i % 32)) & 1u);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set bit i of b. Zero-extends the limb array when the bit lies beyond the
|
||||
* current length; ORing never clears, so the value only grows.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_set_bit(struct vfpn_bn *b, size_t i, int bit)
|
||||
{
|
||||
if (!bit)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (b->n <= i / 32)
|
||||
{
|
||||
size_t need = i / 32 + 1;
|
||||
if (vfpn_bn_reserve(b, need) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t j = b->n; j < need; j++)
|
||||
{
|
||||
b->d[j] = 0;
|
||||
}
|
||||
|
||||
b->n = need;
|
||||
}
|
||||
|
||||
b->d[i / 32] |= (1u << (i % 32));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shift |b| left by one bit in place (limb ripple). The caller's cap must
|
||||
* already make room for the possible extra top limb; the reserve here is
|
||||
* only a safety net and normally returns immediately.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_shl1(struct vfpn_bn *b)
|
||||
{
|
||||
if (b->n == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(b, b->n + 1) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t carry = 0;
|
||||
for (size_t i = 0; i < b->n; i++)
|
||||
{
|
||||
uint32_t cur = b->d[i];
|
||||
b->d[i] = (cur << 1) | carry;
|
||||
carry = cur >> 31;
|
||||
}
|
||||
|
||||
if (carry != 0)
|
||||
{
|
||||
b->d[b->n++] = carry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* In-place limb-wise subtraction a -= b. The caller guarantees a >= b, so
|
||||
* the borrow always clears before the top of a; a is renormalized.
|
||||
*/
|
||||
static __attribute__((unused)) void
|
||||
vfpn_bn_sub_inplace(struct vfpn_bn *a, const struct vfpn_bn *b)
|
||||
{
|
||||
uint32_t borrow = 0;
|
||||
size_t i = 0;
|
||||
for (; i < b->n; i++)
|
||||
{
|
||||
uint64_t diff = (uint64_t)a->d[i] - (uint64_t)b->d[i] - borrow;
|
||||
a->d[i] = (uint32_t)diff;
|
||||
borrow = (uint32_t)((diff >> 32) & 1u);
|
||||
}
|
||||
|
||||
while (i < a->n && borrow != 0)
|
||||
{
|
||||
a->d[i]--;
|
||||
borrow = (a->d[i] == 0xFFFFFFFFu) ? 1u : 0u;
|
||||
i++;
|
||||
}
|
||||
|
||||
vfpn_bn_norm(a);
|
||||
}
|
||||
|
||||
/*
|
||||
* Q = N / D, R = N mod D by restoring binary long division. N may be zero;
|
||||
* D must be nonzero. Q and R are written over whatever they held. The
|
||||
* quotients bits are produced most-significant first; the loop runs over the
|
||||
* dividend's bit positions from bitlen(N)-1 down to 0, pulling one new
|
||||
* dividend bit into R and subtracting D whenever R >= D.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_divmod_binary(struct vfpn_bn *Q, struct vfpn_bn *R, const struct vfpn_bn *N,
|
||||
const struct vfpn_bn *D)
|
||||
{
|
||||
if (vfpn_bn_is_zero(D))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(Q, N->n + 2) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(R, N->n + 2) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
Q->n = 0;
|
||||
R->n = 0;
|
||||
|
||||
size_t nbits = vfpn_bn_bitlen(N);
|
||||
for (size_t i = nbits; i-- > 0;)
|
||||
{
|
||||
if (vfpn_bn_shl1(R) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vfpn_bn_set_bit(R, 0, vfpn_bn_get_bit(N, i)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vfpn_bn_cmp(R, D) >= 0)
|
||||
{
|
||||
vfpn_bn_sub_inplace(R, D);
|
||||
if (vfpn_bn_set_bit(Q, i, 1) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vfpn_bn_norm(Q);
|
||||
vfpn_bn_norm(R);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare 2*a against b (both canonical base-2^32 digit strings) without
|
||||
* building the doubled value: doubling a limb's carry is the next limb's
|
||||
* (d[i] >> 31), which the loop below folds in, and an extra top digit
|
||||
* appears when the original top limb had its high bit set. All digits of 2*a
|
||||
* stay below 2^32, so a plain digitwise comparison is exact.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_cmp_doubled(const struct vfpn_bn *a, const struct vfpn_bn *b)
|
||||
{
|
||||
if (a->n == 0)
|
||||
{
|
||||
return vfpn_bn_is_zero(b) ? 0 : -1;
|
||||
}
|
||||
|
||||
size_t extra = (a->d[a->n - 1] >> 31) ? 1u : 0u;
|
||||
size_t la = a->n + extra;
|
||||
if (la != b->n)
|
||||
{
|
||||
return (la > b->n) ? 1 : -1;
|
||||
}
|
||||
|
||||
for (size_t i = la; i-- > 0;)
|
||||
{
|
||||
uint32_t da;
|
||||
if (extra != 0 && i == a->n)
|
||||
{
|
||||
da = 1; /* carry out of the doubled top limb */
|
||||
}
|
||||
else
|
||||
{
|
||||
da = (uint32_t)(a->d[i] << 1);
|
||||
if (i > 0)
|
||||
{
|
||||
da += (a->d[i - 1] >> 31);
|
||||
}
|
||||
}
|
||||
|
||||
if (da != b->d[i])
|
||||
{
|
||||
return (da < b->d[i]) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* R = round_half_even(mant * 2^exp2 * 10^s), computed exactly as an integer
|
||||
* bignum (mant > 0; s may be negative). s >= 0 scales R by 5^s and by a
|
||||
* single power of two, rounding once at the end (nearest-even). s < 0 builds
|
||||
* D = 5^(-s) and shifts either N or D so that value = N / D, divides exactly,
|
||||
* and rounds the quotient half-even off the remainder: 2*rem > D rounds up,
|
||||
* 2*rem == D rounds up only when the quotient is odd. Returns 0 or -1 on
|
||||
* allocation failure (R is then unspecified).
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_round_scale10(struct vfpn_bn *R, uint64_t mant, int exp2, long s)
|
||||
{
|
||||
struct vfpn_bn N;
|
||||
struct vfpn_bn D;
|
||||
struct vfpn_bn Q;
|
||||
struct vfpn_bn rem;
|
||||
vfpn_bn_init(&N);
|
||||
vfpn_bn_init(&D);
|
||||
vfpn_bn_init(&Q);
|
||||
vfpn_bn_init(&rem);
|
||||
|
||||
if (s >= 0)
|
||||
{
|
||||
if (vfpn_bn_set_u64(R, mant) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (long i = 0; i < s; i++)
|
||||
{
|
||||
if (vfpn_bn_mul_small(R, 5) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
long long sh = (long long)exp2 + (long long)s;
|
||||
if (sh >= 0)
|
||||
{
|
||||
if (vfpn_bn_shl(R, (size_t)sh) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vfpn_bn_shr_round(R, (size_t)(-sh)) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
vfpn_bn_free(&N);
|
||||
vfpn_bn_free(&D);
|
||||
vfpn_bn_free(&Q);
|
||||
vfpn_bn_free(&rem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* value = mant * 2^exp2 / (2^t * 5^t) with t = -s: make it N / D. */
|
||||
{
|
||||
long long t = -(long long)s;
|
||||
long long sh = (long long)exp2 - t;
|
||||
|
||||
if (vfpn_bn_pow5(&D, (size_t)t) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (vfpn_bn_set_u64(&N, mant) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sh >= 0)
|
||||
{
|
||||
if (vfpn_bn_shl(&N, (size_t)sh) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vfpn_bn_norm(&N);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vfpn_bn_shl(&D, (size_t)(-sh)) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vfpn_bn_norm(&D);
|
||||
}
|
||||
|
||||
if (vfpn_divmod_binary(&Q, &rem, &N, &D) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
int c = vfpn_bn_cmp_doubled(&rem, &D);
|
||||
if (c > 0 || (c == 0 && Q.n > 0 && (Q.d[0] & 1u) != 0))
|
||||
{
|
||||
if (vfpn_bn_add_one(&Q) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (vfpn_bn_reserve(R, Q.n) != 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (Q.n > 0)
|
||||
{
|
||||
__builtin_memcpy(R->d, Q.d, Q.n * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
R->n = Q.n;
|
||||
}
|
||||
|
||||
vfpn_bn_free(&N);
|
||||
vfpn_bn_free(&D);
|
||||
vfpn_bn_free(&Q);
|
||||
vfpn_bn_free(&rem);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
vfpn_bn_free(&N);
|
||||
vfpn_bn_free(&D);
|
||||
vfpn_bn_free(&Q);
|
||||
vfpn_bn_free(&rem);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decimal digits of |v| into out (most significant first, no terminating
|
||||
* NUL); returns the digit count or -1 when outcap is too small. The value
|
||||
* zero yields a single '0'. Groups of nine decimal digits are stripped from
|
||||
* the low end into a fixed stack (a 640-limb value caps at ~6165 digits, far
|
||||
* inside 700 groups), then re-emitted top group unpadded, lower groups
|
||||
* zero-padded to nine.
|
||||
*/
|
||||
static __attribute__((unused)) int
|
||||
vfpn_bn_to_dec(const struct vfpn_bn *v, char *out, size_t outcap)
|
||||
{
|
||||
if (vfpn_bn_is_zero(v))
|
||||
{
|
||||
if (outcap < 1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
out[0] = '0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct vfpn_bn w;
|
||||
vfpn_bn_init(&w);
|
||||
if (vfpn_bn_reserve(&w, v->n) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
w.n = v->n;
|
||||
if (v->n > 0)
|
||||
{
|
||||
__builtin_memcpy(w.d, v->d, v->n * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
uint32_t groups[700];
|
||||
size_t ngroups = 0;
|
||||
while (!vfpn_bn_is_zero(&w))
|
||||
{
|
||||
if (ngroups >= 700)
|
||||
{
|
||||
vfpn_bn_free(&w);
|
||||
return -1;
|
||||
}
|
||||
|
||||
groups[ngroups++] = vfpn_bn_divmod_small_1e9(&w);
|
||||
}
|
||||
|
||||
vfpn_bn_free(&w);
|
||||
|
||||
size_t top = ngroups - 1;
|
||||
uint32_t t = groups[top];
|
||||
size_t tdig = 1;
|
||||
for (uint32_t x = t / 10; x != 0; x /= 10)
|
||||
{
|
||||
tdig++;
|
||||
}
|
||||
|
||||
size_t ndigits = tdig + (ngroups - 1) * 9;
|
||||
if (outcap < ndigits)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t pos = 0;
|
||||
char tmp[10];
|
||||
size_t ntmp = 0;
|
||||
do
|
||||
{
|
||||
tmp[ntmp++] = (char)('0' + t % 10);
|
||||
t /= 10;
|
||||
} while (t != 0);
|
||||
|
||||
while (ntmp > 0)
|
||||
{
|
||||
out[pos++] = tmp[--ntmp];
|
||||
}
|
||||
|
||||
for (size_t gi = ngroups - 1; gi-- > 0;)
|
||||
{
|
||||
uint32_t val = groups[gi];
|
||||
for (int k = 8; k >= 0; k--)
|
||||
{
|
||||
out[pos + (size_t)k] = (char)('0' + val % 10);
|
||||
val /= 10;
|
||||
}
|
||||
|
||||
pos += 9;
|
||||
}
|
||||
|
||||
return (int)ndigits;
|
||||
}
|
||||
@@ -0,0 +1,734 @@
|
||||
/*
|
||||
* vlibc — formatted output test, integer conversions (todo 16, task A).
|
||||
*
|
||||
* Compares the snprintf/sprintf/dprintf/FILE-output of vfprintf.c against
|
||||
* golden string literals, all of which were verified against the host glibc
|
||||
* (a throwaway /tmp/probe.c) EXCEPT the spec'd divergence that %p of NULL is
|
||||
* "0x0" (glibc prints "(nil)").
|
||||
*
|
||||
* Coverage: %d %i %u %o %x %X over 0, +/-1, INT_MIN/MAX, UINT_MAX,
|
||||
* LONG_MIN/MAX (as %ld), LLONG_MIN/MAX (%lld), ULONG_MAX; %hhd/%hd
|
||||
* truncation; %zu %zd %tu %td %ju %jd; the '#' prefix rules (including
|
||||
* %#.0o of 0, %#x of 0, %#o of 0); the - + space 0 flags and their
|
||||
* interactions with width and precision; %c %s with precision/width and the
|
||||
* NULL "(null)" spelling; %p of a local (shape check) and of NULL ("0x0");
|
||||
* %n; width and precision supplied via '*'; %% escaping; snprintf truncation
|
||||
* (including n == 0 with a NULL buffer); sprintf/vsprintf; fprintf/vfprintf
|
||||
* to a file; dprintf/vdprintf to a raw descriptor; printf/vprintf through a
|
||||
* redirected stdout; and (level 2) asprintf/vasprintf.
|
||||
*
|
||||
* All diagnostics go through raw SYS_write; errno is never read and never
|
||||
* deliberately written in the success paths exercised here, so no host TCB
|
||||
* preservation is needed. Only vlibc headers are included (-Iinclude wins
|
||||
* over the host's).
|
||||
*/
|
||||
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Byte-compare two buffers (the produced content need not be NUL-ended). */
|
||||
static int
|
||||
mem_eq(const unsigned char *a, const unsigned char *b, unsigned long n)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (a[i] != b[i])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Read a whole small file through raw syscalls into buf; returns size. */
|
||||
static long
|
||||
raw_read_all(const char *path, char *buf, unsigned long cap)
|
||||
{
|
||||
long fd = __syscall4(SYS_openat, -100, (long)path, 0x0, 0);
|
||||
long got = -1;
|
||||
long total = 0;
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while (total < (long)cap)
|
||||
{
|
||||
got = __syscall3(SYS_read, fd, (long)(buf + total), (long)(cap - (unsigned long)total));
|
||||
if (got <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
total += got;
|
||||
}
|
||||
__syscall1(SYS_close, fd);
|
||||
return got < 0 ? -1 : total;
|
||||
}
|
||||
|
||||
/* Forwarding wrappers so the v* functions get exercised with real va_lists. */
|
||||
static int
|
||||
run_vprintf(const char *format, ...) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, format);
|
||||
rc = vprintf(format, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
run_vfprintf(FILE *f, const char *format, ...) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, format);
|
||||
rc = vfprintf(f, format, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
run_vsprintf(char *s, const char *format, ...) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, format);
|
||||
rc = vsprintf(s, format, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
run_vdprintf(int fd, const char *format, ...) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, format);
|
||||
rc = vdprintf(fd, format, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
static int
|
||||
run_vasprintf(char **p, const char *format, ...) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, format);
|
||||
rc = vasprintf(p, format, ap);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* A single snprintf result checked against a golden literal. */
|
||||
static void
|
||||
golden(const char *want, const char *format, ...) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
char out[64];
|
||||
va_list ap;
|
||||
int rc;
|
||||
|
||||
va_start(ap, format);
|
||||
rc = vsnprintf(out, sizeof out, format, ap);
|
||||
va_end(ap);
|
||||
check(rc >= 0 && strcmp(out, want) == 0, want);
|
||||
}
|
||||
|
||||
/* 1. Plain integers: d/i/u/o/x/X over the extremes and the sign cases. */
|
||||
static void
|
||||
int_scenario(void)
|
||||
{
|
||||
char out[64];
|
||||
int rc;
|
||||
|
||||
golden("0", "%d", 0);
|
||||
golden("1", "%d", 1);
|
||||
golden("-1", "%d", -1);
|
||||
golden("-2147483648", "%d", INT_MIN);
|
||||
golden("2147483647", "%d", INT_MAX);
|
||||
golden("-7", "%i", -7);
|
||||
golden("4294967295", "%u", UINT_MAX);
|
||||
golden("0", "%u", 0);
|
||||
golden("10", "%o", 8);
|
||||
golden("ff", "%x", 255);
|
||||
golden("FF", "%X", 255);
|
||||
golden("-9223372036854775808", "%ld", LONG_MIN);
|
||||
golden("9223372036854775807", "%ld", LONG_MAX);
|
||||
golden("-9223372036854775808", "%lld", LLONG_MIN);
|
||||
golden("9223372036854775807", "%lld", LLONG_MAX);
|
||||
golden("18446744073709551615", "%lu", ULONG_MAX);
|
||||
rc = snprintf(out, sizeof out, "%d%d", 1, 2);
|
||||
check(rc == 2 && strcmp(out, "12") == 0, "adjacent conversions concatenate");
|
||||
rc = snprintf(out, sizeof out, "%s%d", "v", -7);
|
||||
check(rc == 3 && strcmp(out, "v-7") == 0, "literal text between conversions");
|
||||
}
|
||||
|
||||
/* 2. Length modifiers: hh/h truncation and the j/z/t widths. */
|
||||
static void
|
||||
length_scenario(void)
|
||||
{
|
||||
golden("44", "%hhd", 300);
|
||||
golden("4464", "%hd", 70000);
|
||||
golden("44", "%hhu", 300);
|
||||
golden("-44", "%hhd", -300);
|
||||
golden("4000000000", "%zu", (size_t)4000000000UL);
|
||||
golden("-4000000000", "%zd", (ssize_t)-4000000000L);
|
||||
golden("123456789", "%tu", (ptrdiff_t)123456789L);
|
||||
golden("-987654321", "%td", (ptrdiff_t)-987654321L);
|
||||
golden("18446744073709551615", "%ju", (uintmax_t)UINTMAX_MAX);
|
||||
golden("-9223372036854775807", "%jd", (intmax_t)-9223372036854775807LL);
|
||||
golden("ffffffffffffffff", "%lx", (unsigned long)ULONG_MAX);
|
||||
}
|
||||
|
||||
/* 3. The '#' prefixes (octal, hex) and the zero value corner cases. */
|
||||
static void
|
||||
alt_scenario(void)
|
||||
{
|
||||
golden("0x1abc", "%#x", 0x1abc);
|
||||
golden("0X1ABC", "%#X", 0x1abc);
|
||||
golden("010", "%#o", 8);
|
||||
golden("0", "%#.0o", 0);
|
||||
golden("0", "%#x", 0);
|
||||
golden("0", "%#o", 0);
|
||||
golden("00000010", "%#08o", 8);
|
||||
golden("010", "%#.3o", 8);
|
||||
golden("", "%#.0x", 0);
|
||||
golden(" 0", "%#5.0o", 0);
|
||||
}
|
||||
|
||||
/* 4. Flags and their interactions with width and precision. */
|
||||
static void
|
||||
flag_scenario(void)
|
||||
{
|
||||
golden("42 ", "%-8d", 42);
|
||||
golden("+42", "%+d", 42);
|
||||
golden("-42", "%+d", -42);
|
||||
golden(" 42", "% d", 42);
|
||||
golden("-42", "% d", -42);
|
||||
golden("00000042", "%08d", 42);
|
||||
golden("42 ", "%-08d", 42);
|
||||
golden("+0000042", "%+08d", 42);
|
||||
golden(" 042", "%5.3d", 42);
|
||||
golden("042 ", "%-5.3d", 42);
|
||||
golden(" ", "%05.0d", 0);
|
||||
golden(" 42", "%05.0d", 42);
|
||||
golden(" 000", "%5.3d", 0);
|
||||
golden(" 42", "%5d", 42);
|
||||
golden("42 ", "%-5d", 42);
|
||||
golden("+0042", "%+05d", 42);
|
||||
golden(" 42", "%*d", 5, 42);
|
||||
}
|
||||
|
||||
/* 5. Characters, strings, pointers, %n, and the literal percent. */
|
||||
static void
|
||||
charstr_scenario(void)
|
||||
{
|
||||
char out[64];
|
||||
int marker;
|
||||
int n;
|
||||
int rc;
|
||||
|
||||
golden("A", "%c", 'A');
|
||||
golden("hello", "%s", "hello");
|
||||
golden("hel", "%.3s", "hello");
|
||||
golden(" hel", "%5.3s", "hello");
|
||||
golden("hel ", "%-5.3s", "hello");
|
||||
golden("(null)", "%s", (char *)0);
|
||||
golden("%", "%%");
|
||||
golden("0x0", "%p", (void *)0);
|
||||
|
||||
n = -1;
|
||||
rc = snprintf(out, sizeof out, "abc%n", &n);
|
||||
check(rc == 3 && n == 3 && strcmp(out, "abc") == 0, "%n stores the count so far");
|
||||
|
||||
rc = snprintf(out, sizeof out, "%p", (void *)&marker);
|
||||
if (rc > 2 && out[0] == '0' && out[1] == 'x')
|
||||
{
|
||||
int i;
|
||||
int hex = 1;
|
||||
|
||||
for (i = 2; out[i] != '\0'; i++)
|
||||
{
|
||||
if (!((out[i] >= '0' && out[i] <= '9') || (out[i] >= 'a' && out[i] <= 'f')))
|
||||
{
|
||||
hex = 0;
|
||||
}
|
||||
}
|
||||
check(hex && i > 2, "%p of a local prints 0x + lowercase hex");
|
||||
}
|
||||
else
|
||||
{
|
||||
check(0, "%p of a local prints 0x + lowercase hex");
|
||||
}
|
||||
}
|
||||
|
||||
/* 6. Width and precision supplied through '*'. */
|
||||
static void
|
||||
star_scenario(void)
|
||||
{
|
||||
golden("00042", "%.*d", 5, 42);
|
||||
golden("042", "%.*d", 3, 42);
|
||||
golden("42", "%.*d", -1, 42);
|
||||
golden("hel", "%.*s", 3, "hello");
|
||||
}
|
||||
|
||||
/* 7. snprintf truncation semantics. */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-truncation"
|
||||
|
||||
static void
|
||||
trunc_scenario(void)
|
||||
{
|
||||
char b[8];
|
||||
int rc;
|
||||
|
||||
rc = snprintf(b, 5, "%s", "hello world");
|
||||
check(rc == 11 && strcmp(b, "hell") == 0,
|
||||
"snprintf truncates at n-1 and returns the full length");
|
||||
rc = snprintf((char *)0, 0, "%s", "hello world");
|
||||
check(rc == 11, "snprintf with n == 0 writes nothing and returns the length");
|
||||
rc = snprintf(b, 1, "%s", "hello world");
|
||||
check(rc == 11 && b[0] == '\0', "snprintf with n == 1 writes only the NUL");
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
/* 8. sprintf/vsprintf (no bound). */
|
||||
static void
|
||||
sprintf_scenario(void)
|
||||
{
|
||||
char s[32];
|
||||
int rc;
|
||||
|
||||
rc = sprintf(s, "%d-%s", 7, "x");
|
||||
check(rc == 3 && strcmp(s, "7-x") == 0, "sprintf writes past the argument bound");
|
||||
rc = run_vsprintf(s, "%d-%s", 7, "x");
|
||||
check(rc == 3 && strcmp(s, "7-x") == 0, "vsprintf writes past the argument bound");
|
||||
}
|
||||
|
||||
/* 9. dprintf/vdprintf to a raw descriptor. */
|
||||
static void
|
||||
dprintf_scenario(void)
|
||||
{
|
||||
const char path[] = "/tmp/vlibc-test-printf-dprintf.txt";
|
||||
char buf[64];
|
||||
long fd;
|
||||
long got;
|
||||
int r1;
|
||||
int r2;
|
||||
|
||||
fd = __syscall4(SYS_openat, -100, (long)path, (long)(0x1 | 0x40 | 0x200), 0666);
|
||||
check(fd >= 0, "dprintf openat succeeds");
|
||||
if (fd < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
r1 = (dprintf((int)fd, "%d %s", 9, "nine") == 6);
|
||||
r2 = (run_vdprintf((int)fd, " %#x", 0xbeef) == 7);
|
||||
__syscall1(SYS_close, fd);
|
||||
check(r1, "dprintf returns the byte count");
|
||||
check(r2, "vdprintf returns the byte count");
|
||||
got = raw_read_all(path, buf, sizeof buf);
|
||||
check(got == 13 &&
|
||||
mem_eq((const unsigned char *)buf, (const unsigned char *)"9 nine 0xbeef", 13),
|
||||
"dprintf content reached the file unbuffered");
|
||||
check(remove(path) == 0, "remove deletes the dprintf file");
|
||||
}
|
||||
|
||||
/* 10. fprintf/vfprintf through a buffered FILE, read back raw. */
|
||||
static void
|
||||
file_scenario(void)
|
||||
{
|
||||
const char path[] = "/tmp/vlibc-test-printf-file.txt";
|
||||
char buf[64];
|
||||
FILE *f;
|
||||
long got;
|
||||
int r1;
|
||||
int r2;
|
||||
|
||||
f = fopen(path, "w");
|
||||
check(f != NULL, "fopen for fprintf succeeds");
|
||||
if (f == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
r1 = (fprintf(f, "%s %d", "abc", 123) == 7);
|
||||
r2 = (run_vfprintf(f, " %x", 255) == 3);
|
||||
check(r1, "fprintf returns the byte count");
|
||||
check(r2, "vfprintf returns the byte count");
|
||||
check(fclose(f) == 0, "fclose flushes the fprintf file");
|
||||
got = raw_read_all(path, buf, sizeof buf);
|
||||
check(got == 10 && mem_eq((const unsigned char *)buf, (const unsigned char *)"abc 123 ff", 10),
|
||||
"fprintf/vfprintf content reached the file");
|
||||
check(remove(path) == 0, "remove deletes the fprintf file");
|
||||
}
|
||||
|
||||
/* 11. printf/vprintf through a redirected stdout (the flush is explicit). */
|
||||
static void
|
||||
stdout_scenario(void)
|
||||
{
|
||||
const char path[] = "/tmp/vlibc-test-printf-stdout.txt";
|
||||
char buf[64];
|
||||
long saved;
|
||||
long fd;
|
||||
long got;
|
||||
int r1;
|
||||
int r2;
|
||||
int r3;
|
||||
|
||||
saved = __syscall1(SYS_dup, 1);
|
||||
check(saved >= 0, "dup(1) for the printf scenario");
|
||||
if (saved < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
fd = __syscall4(SYS_openat, -100, (long)path, (long)(0x1 | 0x40 | 0x200), 0666);
|
||||
if (fd < 0)
|
||||
{
|
||||
__syscall2(SYS_dup2, saved, 1);
|
||||
__syscall1(SYS_close, saved);
|
||||
check(0, "openat for the printf scenario");
|
||||
return;
|
||||
}
|
||||
__syscall2(SYS_dup2, fd, 1);
|
||||
__syscall1(SYS_close, fd);
|
||||
/* No check() output while fd 1 names the file. */
|
||||
r1 = (printf("%d-%s", 1, "x") == 3);
|
||||
r2 = (run_vprintf("%c%d", 'q', 5) == 2);
|
||||
r3 = (fflush(stdout) == 0);
|
||||
__syscall2(SYS_dup2, saved, 1);
|
||||
__syscall1(SYS_close, saved);
|
||||
check(r1, "printf to stdout returns its byte count");
|
||||
check(r2, "vprintf to stdout returns its byte count");
|
||||
check(r3, "fflush(stdout) after the redirect");
|
||||
got = raw_read_all(path, buf, sizeof buf);
|
||||
check(got == 5 && mem_eq((const unsigned char *)buf, (const unsigned char *)"1-xq5", 5),
|
||||
"printf/vprintf content reached the redirected stdout");
|
||||
check(remove(path) == 0, "remove deletes the stdout file");
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* 12. Level 2: asprintf/vasprintf. */
|
||||
static void
|
||||
asprintf_scenario(void)
|
||||
{
|
||||
char *p = NULL;
|
||||
int rc;
|
||||
|
||||
rc = asprintf(&p, "%d-%s", 5, "x");
|
||||
check(rc == 3 && p != NULL && strcmp(p, "5-x") == 0, "asprintf allocates and fills");
|
||||
free(p);
|
||||
p = NULL;
|
||||
rc = run_vasprintf(&p, "%u", 42U);
|
||||
check(rc == 2 && p != NULL && strcmp(p, "42") == 0, "vasprintf allocates and fills");
|
||||
free(p);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
/* Bit-pattern constructors for the float scenarios (host arithmetic cannot
|
||||
* name every subnormal or sign-of-NaN case portably). */
|
||||
static double
|
||||
mkdbl(unsigned long long bits)
|
||||
{
|
||||
double v;
|
||||
|
||||
memcpy(&v, &bits, sizeof v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static long double
|
||||
mkldbl(unsigned e15, unsigned neg, unsigned long long sig)
|
||||
{
|
||||
unsigned char raw[16];
|
||||
long double v;
|
||||
|
||||
memset(raw, 0, sizeof raw);
|
||||
memcpy(raw, &sig, 8);
|
||||
raw[8] = (unsigned char)(e15 & 0xFF);
|
||||
raw[9] = (unsigned char)((e15 >> 8) | (neg ? 0x80 : 0));
|
||||
memcpy(&v, raw, sizeof raw);
|
||||
return v;
|
||||
}
|
||||
|
||||
/* 13. %a/%A hex floats, the long double L length, and the round-half-even
|
||||
* hex-digit rounding (all goldens verified against host glibc). */
|
||||
static void
|
||||
hexfloat_scenario(void)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Woverlength-strings"
|
||||
static const char *const ldmax_dec =
|
||||
"11897314953572317650212638530309702051690633222946242004403237338917370055229707"
|
||||
"22616410290336528882853545697807495577314427443153670288434198125573853743678673"
|
||||
"59320070697326320191591828296152436552951064679108661431179063216977883889613478"
|
||||
"65606003991487534332114549111600886798451548665128523401497730376000091254793939"
|
||||
"66223151383622417838542743917838138717805889487540575168226347659235576974805113"
|
||||
"72564902088485522249479139937758502601177354918009979622602685950855888360815984"
|
||||
"69002356451323465944763849398592764562845796617729304078066092291027150460853880"
|
||||
"87959327781622986827547830768080040150694942303411728957777100335714010559775242"
|
||||
"12405734700738625166011082837911962300846927720096515350020847447079244384854591"
|
||||
"28867230006190851264721119513614675276335195629275979572502780029807959041931396"
|
||||
"03021470997035276467445530922022679656280991498232083329641241038509239184734786"
|
||||
"12192169721054348428704835340811304257300221642134891734717423480071488075100206"
|
||||
"43905172342476560047217680964861079949434157034763206435586242074435044243805661"
|
||||
"36017608837478165389027809576975977286860071487028287955567141404632615832623602"
|
||||
"76289631617397848425448686060994827086796804807870251185893083854658422304090880"
|
||||
"59962945945862019037660484467909260022254105307759010657606713472001258464069570"
|
||||
"30257138960983757998926954553052368560758683179223113639519468850880771872104705"
|
||||
"20395758748001314313144425494391994017575316933939236688185618912993172910425292"
|
||||
"12368351599223220509980016771027840353601408292963981151228777681357060457893435"
|
||||
"35451696539561254048846447169786893211671087229088082778350518228857646062218739"
|
||||
"70285165508372099234948333443522898475123275372663606621390228126470623407535207"
|
||||
"17240586650795182173034637826313533937067749019501978416904418247380631628285868"
|
||||
"57741432581165364040218402724913393320949219498422442730427019873044536620350262"
|
||||
"38695780468200360144729199712309553005720614186697485284685618651483271597448120"
|
||||
"31219467516863793430961896151073300655524214851952017628585950910518394725028638"
|
||||
"71632494167613804996319791441870254302706758495192008837915169401581740046711477"
|
||||
"87720145964446117520405945350476472180797576111172084627363927960033967047003761"
|
||||
"33745095531841500737964126050479232516613548412918842113408230154733047540670728"
|
||||
"18763503617332908005951896325207071673904547777129682265206225651439919376804400"
|
||||
"29238090311243791261477625596469422198137514696707944687035800439250765945161837"
|
||||
"98118593920495440361149153107822510726914869798092409467721427270124043771874092"
|
||||
"16756613634938900451232351668146089322400697993176017805338191849981933008410985"
|
||||
"99393876029260139091141452600372028487213241195542428210183120421610446740462163"
|
||||
"53369005836646065911562987647455250681450039329414041314954006776029510059622530"
|
||||
"22823003631473824681059648442441324864573137437595096416168048024129351876204668"
|
||||
"13563687753281467553879887177183651289394719533506188500326760735438867336800207"
|
||||
"43878496570145760903498575712430451020387304948542567024793393228091105260415385"
|
||||
"28994849203991091946129912491633289917998094380337879522093131466946149705939664"
|
||||
"15237594928589096048991612194498998638483702248667224914892467841020618336462741"
|
||||
"69695763076324802355879752452537370354338829608627534277400163334340550835370485"
|
||||
"07374544819754722228975281083020898682633020285259923084168054539687911418297629"
|
||||
"98896457648276528750456285492426516521775079951625966922911497778896235667095662"
|
||||
"71384820181913483216879958636526376209782850700993372943967846398790249145142227"
|
||||
"42527006363942327998483976739987154418554201562244154926653014515504685489258620"
|
||||
"27608576183712976335876121538256512963353814166394951655600026415918655485005705"
|
||||
"26114319529199188079545223946496276356301785808966922264062353828985358675959906"
|
||||
"47008385687123810329591926494846250768992258419305480763620215089022149220528069"
|
||||
"84201835084058693849381549890944546197789302911357651677540623227829831403347327"
|
||||
"66039522316034228247175281818188443048809213219335508698733958612760736708666523"
|
||||
"75555675803171490108477320096424318780070008797346032906278943553743564448851907"
|
||||
"19161645514115576193939969076741515640282654366402676009508752394550734155613586"
|
||||
"79330660317447209244465135323666476497354008519670407711036405381500734868917983"
|
||||
"64049570606189535005089840913826869535090066783324472578712196604415284924840041"
|
||||
"85093281190896363417573989716659600075948780061916409485433875852065711654107226"
|
||||
"09962881501231443779440087493019447443307843889957018427100048083050121771235606"
|
||||
"22895076269042856800047718893158089358515593863176652948089031267747029662545110"
|
||||
"86154895839508779675546413794489596052797520987481383976257859210575628440175934"
|
||||
"93241621483395653501891968113890918437957347032694063428900878058469403524534793"
|
||||
"98080674273236297887100867175802531561302356064878709259865288416350972529537091"
|
||||
"11431720488774740553905400942537542411931794417513706468964386151771884986701034"
|
||||
"15325423859110896247108853858086888377772586485641459342621210866475884892600317"
|
||||
"62345960769508849149662444156604419552086811989770240";
|
||||
#pragma GCC diagnostic pop
|
||||
double pos_inf = mkdbl(0x7FF0000000000000ULL);
|
||||
double neg_inf = mkdbl(0xFFF0000000000000ULL);
|
||||
double pos_nan = mkdbl(0x7FF8000000000000ULL);
|
||||
double neg_nan = mkdbl(0xFFF8000000000000ULL);
|
||||
double maxsub = mkdbl(0x000FFFFFFFFFFFFFULL);
|
||||
char out[6000];
|
||||
int rc;
|
||||
|
||||
golden("0x0p+0", "%a", 0.0);
|
||||
golden("0x1p+0", "%a", 1.0);
|
||||
golden("0x1.8p+0", "%a", 1.5);
|
||||
golden("0x1p-1", "%a", 0.5);
|
||||
golden("0x1.91eb851eb851fp+1", "%a", 3.14);
|
||||
golden("0x1.fffffffffffffp+1023", "%a", DBL_MAX);
|
||||
golden("0x2p+1023", "%.0a", DBL_MAX);
|
||||
golden("0x2.0p+1023", "%.1a", DBL_MAX);
|
||||
golden("0x0.0000000000001p-1022", "%a", DBL_TRUE_MIN);
|
||||
golden("0x0.0p-1022", "%.1a", DBL_TRUE_MIN);
|
||||
golden("0x0p-1022", "%.0a", DBL_TRUE_MIN);
|
||||
golden("0x0.fffffffffffffp-1022", "%a", maxsub);
|
||||
golden("0x1.0p-1022", "%.1a", maxsub);
|
||||
golden("0x1p-1022", "%.0a", maxsub);
|
||||
golden("0x0.0000p-1022", "%.4a", DBL_TRUE_MIN);
|
||||
golden("0x1.81cd6e631f8a1p+13", "%a", 12345.6789);
|
||||
golden("0x1.8000000000000p+0", "%.13a", 1.5);
|
||||
golden("0x1.8000000000000000p+0", "%.16a", 1.5);
|
||||
golden("0x2.0p+0", "%.1a", 0x1.fffffffffffffp+0);
|
||||
golden("0x2p+0", "%.0a", 0x1.8p+0);
|
||||
golden("0x1p+0", "%.0a", 0x1.4p+0);
|
||||
golden("0x2p-1", "%.0a", 0x1.fffffffffffffp-1);
|
||||
golden("0x1.000p+0", "%.3a", 1.0);
|
||||
golden("0x1.p+0", "%#a", 1.0);
|
||||
golden("0x2.p+0", "%#.0a", 1.5);
|
||||
golden("0x0.p+0", "%#.0a", 0.0);
|
||||
golden("0x0.p+0", "%#a", 0.0);
|
||||
golden("0x0.000p+0", "%.3a", 0.0);
|
||||
golden("0x0000000000001.8p+0", "%020a", 1.5);
|
||||
golden("+0x000000000001.8p+0", "%+020a", 1.5);
|
||||
golden("+0x1.8p+0", "%+a", 1.5);
|
||||
golden(" 0x1.8p+0", "% a", 1.5);
|
||||
golden("+0x1.8p+0 ", "%-+20a", 1.5);
|
||||
golden("0x1.8p+0 ", "%-20a", 1.5);
|
||||
golden("0x1.800p+0", "%.3a", 1.5);
|
||||
golden("+0x1.800p+0", "%+08.3a", 1.5);
|
||||
golden("0X1.8P+0", "%A", 1.5);
|
||||
golden("0X1.91EB851EB851FP+1", "%A", 3.14);
|
||||
golden("0X1.FFFFFFFFFFFFFP+1023", "%A", DBL_MAX);
|
||||
golden("0X2P+0", "%.0A", 1.5);
|
||||
golden("inf", "%a", pos_inf);
|
||||
golden("-inf", "%a", neg_inf);
|
||||
golden("nan", "%a", pos_nan);
|
||||
golden("-nan", "%a", neg_nan);
|
||||
golden("INF", "%A", pos_inf);
|
||||
golden("NAN", "%A", pos_nan);
|
||||
golden("inf", "%La", mkldbl(0x7FFF, 0, 0x8000000000000000ULL));
|
||||
golden("-inf", "%La", mkldbl(0x7FFF, 1, 0x8000000000000000ULL));
|
||||
golden("-nan", "%La", mkldbl(0x7FFF, 1, 0x0000000000000001ULL));
|
||||
|
||||
golden("0x8p-3", "%La", 1.0L);
|
||||
golden("0xcp-3", "%La", 1.5L);
|
||||
golden("0xcp-3", "%.0La", 0xcp-3L);
|
||||
golden("0xc.0p-3", "%.1La", 0xcp-3L);
|
||||
golden("0x8p-4", "%La", 0.5L);
|
||||
golden("0xc.8f5c28f5c28f5c3p-2", "%La", 3.14L);
|
||||
golden("0xf.fffffffffffffffp+16380", "%La", LDBL_MAX);
|
||||
golden("0x1p+16384", "%.0La", LDBL_MAX);
|
||||
golden("0x1.0p+16384", "%.1La", LDBL_MAX);
|
||||
golden("0x8p-16385", "%La", LDBL_MIN);
|
||||
golden("0x8.0p-16385", "%.1La", LDBL_MIN);
|
||||
golden("0x0.000000000000001p-16385", "%La", LDBL_TRUE_MIN);
|
||||
golden("0x0.0p-16385", "%.1La", LDBL_TRUE_MIN);
|
||||
golden("0x0p-16385", "%.0La", LDBL_TRUE_MIN);
|
||||
golden("0xc.p-3", "%#.0La", 1.5L);
|
||||
golden("0x0.p+0", "%#.0La", 0.0L);
|
||||
golden("-0xcp-3", "%La", -1.5L);
|
||||
golden("-0x0p+0", "%.0La", -0.0L);
|
||||
golden("0x0.0p+0", "%.1La", 0.0L);
|
||||
golden("0xc.000p-3", "%.3La", 1.5L);
|
||||
golden("0xc.000000000000000p-3", "%.15La", 1.5L);
|
||||
golden("0x00000000000000cp-3", "%020La", 1.5L);
|
||||
golden("+0x1p+16384", "%+.0La", LDBL_MAX);
|
||||
golden("0x1p+4", "%.0La", 0xf.fffffffffffffffp+0L);
|
||||
golden("0x1.0p+4", "%.1La", 0xf.fffffffffffffffp+0L);
|
||||
golden("0XCP-3", "%LA", 1.5L);
|
||||
golden("0XC.8F5C28F5C28F5C3P-2", "%LA", 3.14L);
|
||||
golden("0xc.0e6b7318fc50481p+10", "%La", 12345.6789L);
|
||||
golden("0xc.8p-3", "%.1La", 0xc.8p-3L);
|
||||
golden("0xcp-3", "%.0La", 0xc.8p-3L);
|
||||
golden("0xc.000000000000001p-3", "%.15La", 0xc.000000000000001p-3L);
|
||||
|
||||
golden("1.500000e+00", "%Le", 1.5L);
|
||||
golden("1.500000", "%Lf", 1.5L);
|
||||
golden("1.5", "%Lg", 1.5L);
|
||||
golden("-0.000000e+00", "%Le", -0.0L);
|
||||
golden("-0.000000", "%Lf", -0.0L);
|
||||
golden("3.14159", "%Lg", 3.14159L);
|
||||
golden("3.141590", "%Lf", 3.14159L);
|
||||
golden("3.141590e+00", "%Le", 3.14159L);
|
||||
golden("0.500000", "%Lf", 0.5L);
|
||||
golden("1.000000", "%Lf", 1.0L);
|
||||
golden("0.0001", "%Lg", 0.0001L);
|
||||
golden("1.189731e+4932", "%Le", LDBL_MAX);
|
||||
golden("0.000000", "%Lf", 0.0L);
|
||||
golden("3.14159", "%.6Lg", 3.14159L);
|
||||
golden("1.5", "%Lg", 1.5000001L);
|
||||
|
||||
rc = snprintf(out, sizeof out, "%.0Lf", LDBL_MAX);
|
||||
check(rc == 4933 && strcmp(out, ldmax_dec) == 0,
|
||||
"%.0Lf of LDBL_MAX prints all 4933 digits (no truncation)");
|
||||
rc = snprintf(out, sizeof out, "%Lf", LDBL_MAX);
|
||||
check(rc == 4940 && out[0] == '1' && strncmp(out, "11897314953572317650", 20) == 0 &&
|
||||
out[4933] == '.' && out[4939] == '0',
|
||||
"%Lf of LDBL_MAX fills the 4933-digit integer part");
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int_scenario();
|
||||
length_scenario();
|
||||
alt_scenario();
|
||||
flag_scenario();
|
||||
charstr_scenario();
|
||||
star_scenario();
|
||||
trunc_scenario();
|
||||
sprintf_scenario();
|
||||
dprintf_scenario();
|
||||
file_scenario();
|
||||
stdout_scenario();
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
asprintf_scenario();
|
||||
#endif
|
||||
|
||||
hexfloat_scenario();
|
||||
if (failures > 0)
|
||||
{
|
||||
say(2, "FAILED (");
|
||||
say_dec(2, (unsigned long)failures);
|
||||
say(2, " check(s))\n");
|
||||
return 1;
|
||||
}
|
||||
say(1, "all printf tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user