feat(inttypes): PRI/SCN macros + strtoimax family
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* inttypes.h conversions (todo 14).
|
||||
*
|
||||
* strtoimax/strtoumax are thin delegations: on x86_64 LP64 intmax_t is
|
||||
* long and uintmax_t is unsigned long (GCC __INTMAX_TYPE__), so strtol and
|
||||
* strtoul are ABI-identical — same input, same endptr, same errno policy
|
||||
* (ERANGE clamps, EINVAL for a base outside {0} union [2, 36]). They share
|
||||
* the todo-11 engine in src/stdlib/strtox.c through those calls.
|
||||
*
|
||||
* wcstoimax/wcstoumax cannot reuse the byte-based engine directly, so this
|
||||
* file carries a self-contained wide scan: a wchar_t mirror of the
|
||||
* __strtox_scan algorithm (C-locale whitespace L' ' and L'\t'..L'\r',
|
||||
* optional sign, base 0/2..36 with the 0x/0 prefix rules, saturating u64
|
||||
* magnitude, same clamping policy). wchar_t is a 32-bit code unit here
|
||||
* (GCC __WCHAR_TYPE__ is int), so digits are matched by ASCII codepoint
|
||||
* exactly as strtox_digit matches bytes; non-ASCII code units are never
|
||||
* digits under the C locale. Todo 50 (wchar.h) may later unify this scan
|
||||
* with the narrow engine.
|
||||
*/
|
||||
|
||||
/* Wide-scan results; mirrors the STRTOX_* contract of the narrow engine. */
|
||||
#define WCSTOX_OK 0
|
||||
#define WCSTOX_EMPTY 1
|
||||
#define WCSTOX_BADBASE 2
|
||||
|
||||
/* C-locale whitespace for wide code units: L' ' and L'\t'..L'\r'. */
|
||||
static int
|
||||
wcstox_isspace(wchar_t c)
|
||||
{
|
||||
return c == L' ' || (unsigned int)c - L'\t' < 5U;
|
||||
}
|
||||
|
||||
/* Value of c as a base-36 digit, or -1 when it is not an ASCII letter or
|
||||
* digit; | 0x20 folds 'A'..'F' to 'a'..'f' (same idiom as strtox_digit). */
|
||||
static int
|
||||
wcstox_digit(wchar_t c)
|
||||
{
|
||||
if ((unsigned int)c - L'0' < 10U)
|
||||
{
|
||||
return (int)c - L'0';
|
||||
}
|
||||
if ((unsigned int)(c | 0x20) - L'a' < 26U)
|
||||
{
|
||||
return (int)(c | 0x20) - L'a' + 10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the subject sequence at nptr (C23 7.8.2.4 steps 1-7, the wide
|
||||
* mirror of __strtox_scan). On WCSTOX_OK: *mag holds the magnitude (exact
|
||||
* unless *over), *neg the sign, *endptr the position past the last digit.
|
||||
* On WCSTOX_EMPTY / WCSTOX_BADBASE: *endptr is set to nptr, *mag and *neg
|
||||
* are zeroed; the caller sets errno = EINVAL for WCSTOX_BADBASE.
|
||||
*/
|
||||
static int
|
||||
wcstox_scan(const wchar_t *nptr, wchar_t **endptr, int base, int *neg, unsigned long long *mag,
|
||||
int *over)
|
||||
{
|
||||
const wchar_t *s = nptr;
|
||||
int sg = 0;
|
||||
int ov = 0;
|
||||
int digits = 0;
|
||||
int consumed_prefix = 0;
|
||||
unsigned long long m = 0;
|
||||
|
||||
while (wcstox_isspace(*s))
|
||||
{
|
||||
s++;
|
||||
}
|
||||
if (*s == L'+' || *s == L'-')
|
||||
{
|
||||
sg = (*s == L'-');
|
||||
s++;
|
||||
}
|
||||
if (base != 0 && (base < 2 || base > 36))
|
||||
{
|
||||
if (endptr)
|
||||
{
|
||||
*endptr = (wchar_t *)nptr;
|
||||
}
|
||||
*neg = 0;
|
||||
*mag = 0;
|
||||
*over = 0;
|
||||
return WCSTOX_BADBASE;
|
||||
}
|
||||
|
||||
if (base == 0 || base == 16)
|
||||
{
|
||||
if (s[0] == L'0' && (s[1] == L'x' || s[1] == L'X'))
|
||||
{
|
||||
int d2 = wcstox_digit(s[2]);
|
||||
|
||||
if (d2 >= 0 && d2 < 16)
|
||||
{
|
||||
base = 16;
|
||||
s += 2;
|
||||
}
|
||||
else if (base == 0)
|
||||
{
|
||||
/* "0x" with no hex digit is no integer-constant form. */
|
||||
if (endptr)
|
||||
{
|
||||
*endptr = (wchar_t *)nptr;
|
||||
}
|
||||
*neg = 0;
|
||||
*mag = 0;
|
||||
*over = 0;
|
||||
return WCSTOX_EMPTY;
|
||||
}
|
||||
/* base 16: the 0 itself is the subject; the x ends it. */
|
||||
}
|
||||
else if (base == 0 && s[0] == L'0')
|
||||
{
|
||||
/* Octal prefix: the 0 opens the subject even when no octal
|
||||
* digit follows ("0" is itself a valid integer constant). */
|
||||
base = 8;
|
||||
consumed_prefix = 1;
|
||||
s++;
|
||||
}
|
||||
else if (base == 0)
|
||||
{
|
||||
base = 10;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int d = wcstox_digit(*s);
|
||||
|
||||
if (d < 0 || d >= base)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (ov == 0)
|
||||
{
|
||||
unsigned long long lim =
|
||||
(ULLONG_MAX - (unsigned long long)d) / (unsigned long long)base;
|
||||
|
||||
if (m > lim)
|
||||
{
|
||||
ov = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = (m * (unsigned long long)base) + (unsigned long long)d;
|
||||
}
|
||||
}
|
||||
digits++;
|
||||
s++;
|
||||
}
|
||||
|
||||
if (digits == 0 && consumed_prefix == 0)
|
||||
{
|
||||
if (endptr)
|
||||
{
|
||||
*endptr = (wchar_t *)nptr;
|
||||
}
|
||||
*neg = 0;
|
||||
*mag = 0;
|
||||
*over = 0;
|
||||
return WCSTOX_EMPTY;
|
||||
}
|
||||
if (endptr)
|
||||
{
|
||||
*endptr = (wchar_t *)s;
|
||||
}
|
||||
*neg = sg;
|
||||
*mag = m;
|
||||
*over = ov;
|
||||
return WCSTOX_OK;
|
||||
}
|
||||
|
||||
intmax_t
|
||||
strtoimax(const char *restrict nptr, char **restrict endptr, int base)
|
||||
{
|
||||
/* ABI identity: intmax_t is long on x86_64 LP64. */
|
||||
return strtol(nptr, endptr, base);
|
||||
}
|
||||
|
||||
uintmax_t
|
||||
strtoumax(const char *restrict nptr, char **restrict endptr, int base)
|
||||
{
|
||||
/* ABI identity: uintmax_t is unsigned long on x86_64 LP64. */
|
||||
return strtoul(nptr, endptr, base);
|
||||
}
|
||||
|
||||
intmax_t
|
||||
wcstoimax(const wchar_t *restrict nptr, wchar_t **restrict endptr, int base)
|
||||
{
|
||||
int neg;
|
||||
int over;
|
||||
unsigned long long mag;
|
||||
int rc = wcstox_scan(nptr, endptr, base, &neg, &mag, &over);
|
||||
|
||||
if (rc != WCSTOX_OK)
|
||||
{
|
||||
if (rc == WCSTOX_BADBASE)
|
||||
{
|
||||
errno = EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (over != 0 || mag > (unsigned long long)INTMAX_MAX + (unsigned long long)(neg != 0))
|
||||
{
|
||||
errno = ERANGE;
|
||||
return neg != 0 ? INTMAX_MIN : INTMAX_MAX;
|
||||
}
|
||||
return neg != 0 ? (intmax_t)(0ULL - mag) : (intmax_t)mag;
|
||||
}
|
||||
|
||||
uintmax_t
|
||||
wcstoumax(const wchar_t *restrict nptr, wchar_t **restrict endptr, int base)
|
||||
{
|
||||
int neg;
|
||||
int over;
|
||||
unsigned long long mag;
|
||||
int rc = wcstox_scan(nptr, endptr, base, &neg, &mag, &over);
|
||||
|
||||
if (rc != WCSTOX_OK)
|
||||
{
|
||||
if (rc == WCSTOX_BADBASE)
|
||||
{
|
||||
errno = EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (over != 0 || mag > UINTMAX_MAX)
|
||||
{
|
||||
errno = ERANGE;
|
||||
return UINTMAX_MAX;
|
||||
}
|
||||
return neg != 0 ? (uintmax_t)(0ULL - mag) : (uintmax_t)mag;
|
||||
}
|
||||
|
||||
/*
|
||||
* imaxabs: INTMAX_MIN is not representable as a positive value; the
|
||||
* unsigned computation wraps it back to INTMAX_MIN (implementation-defined
|
||||
* by the uintmax_t -> intmax_t conversion, no errno), matching GCC's
|
||||
* two's complement semantics. The const attribute lives in <inttypes.h>.
|
||||
*/
|
||||
intmax_t
|
||||
imaxabs(intmax_t n)
|
||||
{
|
||||
return n >= 0 ? n : (intmax_t)((uintmax_t)0 - (uintmax_t)n);
|
||||
}
|
||||
|
||||
imaxdiv_t
|
||||
imaxdiv(intmax_t numer, intmax_t denom)
|
||||
{
|
||||
imaxdiv_t r;
|
||||
|
||||
r.quot = numer / denom;
|
||||
r.rem = numer % denom;
|
||||
return r;
|
||||
}
|
||||
Reference in New Issue
Block a user