diff options
Diffstat (limited to 'tools/include/nolibc/stdlib.h')
| -rw-r--r-- | tools/include/nolibc/stdlib.h | 231 |
1 files changed, 110 insertions, 121 deletions
diff --git a/tools/include/nolibc/stdlib.h b/tools/include/nolibc/stdlib.h index 86ad378ab1ea..1816c2368b68 100644 --- a/tools/include/nolibc/stdlib.h +++ b/tools/include/nolibc/stdlib.h @@ -4,6 +4,9 @@ * Copyright (C) 2017-2021 Willy Tarreau <w@1wt.eu> */ +/* make sure to include all global symbols */ +#include "nolibc.h" + #ifndef _NOLIBC_STDLIB_H #define _NOLIBC_STDLIB_H @@ -29,12 +32,30 @@ static __attribute__((unused)) char itoa_buffer[21]; * As much as possible, please keep functions alphabetically sorted. */ +static __inline__ +int abs(int j) +{ + return j >= 0 ? j : -j; +} + +static __inline__ +long labs(long j) +{ + return j >= 0 ? j : -j; +} + +static __inline__ +long long llabs(long long j) +{ + return j >= 0 ? j : -j; +} + /* must be exported, as it's used by libgcc for various divide functions */ void abort(void); __attribute__((weak,unused,noreturn,section(".text.nolibc_abort"))) void abort(void) { - sys_kill(sys_getpid(), SIGABRT); + _sys_kill(_sys_getpid(), SIGABRT); for (;;); } @@ -79,6 +100,7 @@ void free(void *ptr) munmap(heap, heap->len); } +#ifndef NOLIBC_NO_RUNTIME /* getenv() tries to find the environment variable named <name> in the * environment array pointed to by global variable "environ" which must be * declared as a char **, and must be terminated by a NULL (it is recommended @@ -101,32 +123,7 @@ char *getenv(const char *name) } return NULL; } - -static __attribute__((unused)) -unsigned long getauxval(unsigned long type) -{ - const unsigned long *auxv = _auxv; - unsigned long ret; - - if (!auxv) - return 0; - - while (1) { - if (!auxv[0] && !auxv[1]) { - ret = 0; - break; - } - - if (auxv[0] == type) { - ret = auxv[1]; - break; - } - - auxv += 2; - } - - return ret; -} +#endif /* NOLIBC_NO_RUNTIME */ static __attribute__((unused)) void *malloc(size_t len) @@ -148,9 +145,9 @@ void *malloc(size_t len) static __attribute__((unused)) void *calloc(size_t size, size_t nmemb) { - size_t x = size * nmemb; + size_t x; - if (__builtin_expect(size && ((x / size) != nmemb), 0)) { + if (__builtin_expect(__builtin_mul_overflow(size, nmemb, &x), 0)) { SET_ERRNO(ENOMEM); return NULL; } @@ -191,36 +188,91 @@ void *realloc(void *old_ptr, size_t new_size) return ret; } -/* Converts the unsigned long integer <in> to its hex representation into +/* Converts the unsigned 64bit integer <in> to base <base> ascii into * buffer <buffer>, which must be long enough to store the number and the - * trailing zero (17 bytes for "ffffffffffffffff" or 9 for "ffffffff"). The - * buffer is filled from the first byte, and the number of characters emitted - * (not counting the trailing zero) is returned. The function is constructed - * in a way to optimize the code size and avoid any divide that could add a - * dependency on large external functions. + * trailing zero. The buffer is filled from the first byte, and the number + * of characters emitted (not counting the trailing zero) is returned. + * The function uses 'multiply by reciprocal' for the divisions and + * requires the caller pass the correct reciprocal. + * + * Note that unlike __div64_const32() in asm-generic/div64.h there isn't + * an extra shift done (by ___p), the reciprocal has to be lower resulting + * in a slightly low quotient. + * Keep things simple by correcting for the error. + * This also saves calculating the 'low * low' product (e2 below) which is + * very unlikely to be significant. + * + * Some maths: + * recip = p2 / base - e1; // With e1 < base. + * q = (recip * in - e2) / p2; // With e2 < p2. + * = base / in - (e1 * in + e2) / p2; + * > base / in - (e1 * p2 + p2) / p2; + * = base / in - ((e1 + 1) * p2) / p2; + * > base / in - base; + * So the maximum error is less than 'base'. + * Hence the largest possible digit is '2 * base - 1'. + * For base 10 e1 is 6 and you can get digits of 15 (eg from 2**64-1). + * Error e1 is largest for a base that is a factor of 2**64+1, the smallest is 274177 + * and converting 2**42-1 in base 274177 does generate a digit of 274177+274175. + * This all means only a single correction is needed rather than a loop. + * + * __int128 isn't used for mips because gcc prior to 10.0 will call + * __multi3 for MIPS64r6. The same also happens for SPARC and clang. */ -static __attribute__((unused)) -int utoh_r(unsigned long in, char *buffer) +#define _NOLIBC_U64TOA_RECIP(base) ((base) & 1 ? ~0ull / (base) : (1ull << 63) / ((base) / 2)) +static __attribute__((unused, noinline)) +int _nolibc_u64toa_base(uint64_t in, char *buffer, unsigned int base, uint64_t recip) { - signed char pos = (~0UL > 0xfffffffful) ? 60 : 28; - int digits = 0; - int dig; + unsigned int digits = 0; + unsigned int dig; + uint64_t q; + char *p; + /* Generate least significant digit first */ do { - dig = in >> pos; - in -= (uint64_t)dig << pos; - pos -= 4; - if (dig || digits || pos < 0) { - if (dig > 9) - dig += 'a' - '0' - 10; - buffer[digits++] = '0' + dig; +#if defined(__SIZEOF_INT128__) && !defined(__mips__) && !defined(__sparc__) + q = ((unsigned __int128)in * recip) >> 64; +#else + uint64_t p = (uint32_t)in * (recip >> 32); + q = (in >> 32) * (recip >> 32) + (p >> 32); + p = (uint32_t)p + (in >> 32) * (uint32_t)recip; + q += p >> 32; +#endif + dig = in - q * base; + /* Correct for any rounding errors */ + if (dig >= base) { + dig -= base; + q++; } - } while (pos >= 0); + if (dig > 9) + dig += 'a' - '0' - 10; + buffer[digits++] = '0' + dig; + } while ((in = q)); buffer[digits] = 0; + + /* Order reverse to result */ + for (p = buffer + digits - 1; p > buffer; buffer++, p--) { + dig = *buffer; + *buffer = *p; + *p = dig; + } + return digits; } +/* Converts the unsigned long integer <in> to its hex representation into + * buffer <buffer>, which must be long enough to store the number and the + * trailing zero (17 bytes for "ffffffffffffffff" or 9 for "ffffffff"). The + * buffer is filled from the first byte, and the number of characters emitted + * (not counting the trailing zero) is returned. + */ +static __inline__ __attribute__((unused)) +int utoh_r(unsigned long in, char *buffer) +{ + return _nolibc_u64toa_base(in, buffer, 16, _NOLIBC_U64TOA_RECIP(16)); +} + /* converts unsigned long <in> to an hex string using the static itoa_buffer * and returns the pointer to that string. */ @@ -236,30 +288,11 @@ char *utoh(unsigned long in) * trailing zero (21 bytes for 18446744073709551615 in 64-bit, 11 for * 4294967295 in 32-bit). The buffer is filled from the first byte, and the * number of characters emitted (not counting the trailing zero) is returned. - * The function is constructed in a way to optimize the code size and avoid - * any divide that could add a dependency on large external functions. */ -static __attribute__((unused)) +static __inline__ __attribute__((unused)) int utoa_r(unsigned long in, char *buffer) { - unsigned long lim; - int digits = 0; - int pos = (~0UL > 0xfffffffful) ? 19 : 9; - int dig; - - do { - for (dig = 0, lim = 1; dig < pos; dig++) - lim *= 10; - - if (digits || in >= lim || !pos) { - for (dig = 0; in >= lim; dig++) - in -= lim; - buffer[digits++] = '0' + dig; - } - } while (pos--); - - buffer[digits] = 0; - return digits; + return _nolibc_u64toa_base(in, buffer, 10, _NOLIBC_U64TOA_RECIP(10)); } /* Converts the signed long integer <in> to its string representation into @@ -275,7 +308,7 @@ int itoa_r(long in, char *buffer) int len = 0; if (in < 0) { - in = -in; + in = -(unsigned long)in; *(ptr++) = '-'; len++; } @@ -327,34 +360,12 @@ char *utoa(unsigned long in) * buffer <buffer>, which must be long enough to store the number and the * trailing zero (17 bytes for "ffffffffffffffff"). The buffer is filled from * the first byte, and the number of characters emitted (not counting the - * trailing zero) is returned. The function is constructed in a way to optimize - * the code size and avoid any divide that could add a dependency on large - * external functions. + * trailing zero) is returned. */ -static __attribute__((unused)) +static __inline__ __attribute__((unused)) int u64toh_r(uint64_t in, char *buffer) { - signed char pos = 60; - int digits = 0; - int dig; - - do { - if (sizeof(long) >= 8) { - dig = (in >> pos) & 0xF; - } else { - /* 32-bit platforms: avoid a 64-bit shift */ - uint32_t d = (pos >= 32) ? (in >> 32) : in; - dig = (d >> (pos & 31)) & 0xF; - } - if (dig > 9) - dig += 'a' - '0' - 10; - pos -= 4; - if (dig || digits || pos < 0) - buffer[digits++] = '0' + dig; - } while (pos >= 0); - - buffer[digits] = 0; - return digits; + return _nolibc_u64toa_base(in, buffer, 16, _NOLIBC_U64TOA_RECIP(16)); } /* converts uint64_t <in> to an hex string using the static itoa_buffer and @@ -371,31 +382,12 @@ char *u64toh(uint64_t in) * buffer <buffer>, which must be long enough to store the number and the * trailing zero (21 bytes for 18446744073709551615). The buffer is filled from * the first byte, and the number of characters emitted (not counting the - * trailing zero) is returned. The function is constructed in a way to optimize - * the code size and avoid any divide that could add a dependency on large - * external functions. + * trailing zero) is returned. */ -static __attribute__((unused)) +static __inline__ __attribute__((unused)) int u64toa_r(uint64_t in, char *buffer) { - unsigned long long lim; - int digits = 0; - int pos = 19; /* start with the highest possible digit */ - int dig; - - do { - for (dig = 0, lim = 1; dig < pos; dig++) - lim *= 10; - - if (digits || in >= lim || !pos) { - for (dig = 0; in >= lim; dig++) - in -= lim; - buffer[digits++] = '0' + dig; - } - } while (pos--); - - buffer[digits] = 0; - return digits; + return _nolibc_u64toa_base(in, buffer, 10, _NOLIBC_U64TOA_RECIP(10)); } /* Converts the signed 64-bit integer <in> to its string representation into @@ -411,7 +403,7 @@ int i64toa_r(int64_t in, char *buffer) int len = 0; if (in < 0) { - in = -in; + in = -(uint64_t)in; *(ptr++) = '-'; len++; } @@ -548,7 +540,4 @@ uintmax_t strtoumax(const char *nptr, char **endptr, int base) return __strtox(nptr, endptr, base, 0, UINTMAX_MAX); } -/* make sure to include all global symbols */ -#include "nolibc.h" - #endif /* _NOLIBC_STDLIB_H */ |
