blob: e501165a99520b20d6abdbbd935818671f01e179 [file] [log] [blame]
/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org>
*
* GNU Library General Public License (LGPL) version 2 or later.
*
* Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
*/
#define _ISOC99_SOURCE /* for ULLONG primarily... */
#include "_stdio.h"
#include <limits.h>
#include <locale.h>
#include <bits/uClibc_uintmaxtostr.h>
/* Avoid using long long / and % operations to cut down dependencies on
* libgcc.a. Definitely helps on i386 at least. */
#if (INTMAX_MAX > INT_MAX) && (((INTMAX_MAX/INT_MAX)/2) - 2 <= INT_MAX)
#define INTERNAL_DIV_MOD
#endif
char attribute_hidden *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
int base, __UIM_CASE alphacase)
{
int negative;
unsigned int digit;
#ifdef INTERNAL_DIV_MOD
unsigned int H, L, high, low, rh;
#endif
#ifndef __LOCALE_C_ONLY
int grouping, outdigit;
const char *g; /* This does not need to be initialized. */
#endif /* __LOCALE_C_ONLY */
negative = 0;
if (base < 0) { /* signed value */
base = -base;
if (uval > INTMAX_MAX) {
uval = -uval;
negative = 1;
}
}
/* this is an internal routine -- we shouldn't need to check this */
assert(!((base < 2) || (base > 36)));
#ifndef __LOCALE_C_ONLY
grouping = -1;
outdigit = 0x80 & alphacase;
alphacase ^= outdigit;
if (alphacase == __UIM_GROUP) {
assert(base == 10);
if (*(g = __UCLIBC_CURLOCALE->grouping)) {
grouping = *g;
}
}
#endif /* __LOCALE_C_ONLY */
*bufend = '\0';
#ifndef INTERNAL_DIV_MOD
do {
#ifndef __LOCALE_C_ONLY
if (!grouping) { /* Finished a group. */
bufend -= __UCLIBC_CURLOCALE->thousands_sep_len;
memcpy(bufend, __UCLIBC_CURLOCALE->thousands_sep,
__UCLIBC_CURLOCALE->thousands_sep_len);
if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */
/* Note: g[1] == -1 means no further grouping. But since
* we'll never wrap around, we can set grouping to -1 without
* fear of */
++g;
}
grouping = *g;
}
--grouping;
#endif /* __LOCALE_C_ONLY */
digit = uval % base;
uval /= base;
#ifndef __LOCALE_C_ONLY
if (unlikely(outdigit)) {
bufend -= __UCLIBC_CURLOCALE->outdigit_length[digit];
memcpy(bufend,
(&__UCLIBC_CURLOCALE->outdigit0_mb)[digit],
__UCLIBC_CURLOCALE->outdigit_length[digit]);
} else
#endif
{
*--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase );
}
} while (uval);
#else /* ************************************************** */
H = (UINT_MAX / base);
L = UINT_MAX % base + 1;
if (L == base) {
++H;
L = 0;
}
low = (unsigned int) uval;
high = (unsigned int) (uval >> (sizeof(unsigned int) * CHAR_BIT));
do {
#ifndef __LOCALE_C_ONLY
if (!grouping) { /* Finished a group. */
bufend -= __UCLIBC_CURLOCALE->thousands_sep_len;
memcpy(bufend, __UCLIBC_CURLOCALE->thousands_sep,
__UCLIBC_CURLOCALE->thousands_sep_len);
if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */
/* Note: g[1] == -1 means no further grouping. But since
* we'll never wrap around, we can set grouping to -1 without
* fear of */
++g;
}
grouping = *g;
}
--grouping;
#endif /* __LOCALE_C_ONLY */
if (unlikely(high)) {
rh = high % base;
high /= base;
digit = (low % base) + (L * rh);
low = (low / base) + (H * rh) + (digit / base);
digit %= base;
} else {
digit = low % base;
low /= base;
}
#ifndef __LOCALE_C_ONLY
if (unlikely(outdigit)) {
bufend -= __UCLIBC_CURLOCALE->outdigit_length[digit];
memcpy(bufend,
(&__UCLIBC_CURLOCALE->outdigit0_mb)[digit],
__UCLIBC_CURLOCALE->outdigit_length[digit]);
} else
#endif
{
*--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase );
}
} while (low | high);
#endif /******************************************************/
if (negative) {
*--bufend = '-';
}
return bufend;
}