1 /* Copyright (C) 2004       Manuel Novoa III    <mjn3@codepoet.org>
2  *
3  * GNU Library General Public License (LGPL) version 2 or later.
4  *
5  * Dedicated to Toni.  See uClibc/DEDICATION.mjn3 for details.
6  */
7 
8 #include "_stdio.h"
9 #include <limits.h>
10 #include <locale.h>
11 #include <bits/uClibc_uintmaxtostr.h>
12 
13 
14 /* Avoid using long long / and % operations to cut down dependencies on
15  * libgcc.a.  Definitely helps on i386 at least. */
16 #if (INTMAX_MAX > INT_MAX) && (((INTMAX_MAX/INT_MAX)/2) - 2 <= INT_MAX)
17 #define INTERNAL_DIV_MOD
18 #endif
19 
_uintmaxtostr(register char * __restrict bufend,uintmax_t uval,int base,__UIM_CASE alphacase)20 char attribute_hidden *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
21 					int base, __UIM_CASE alphacase)
22 {
23 	int negative;
24 	unsigned int digit;
25 #ifdef INTERNAL_DIV_MOD
26 	unsigned int H, L, high, low, rh;
27 #endif
28 #ifndef __LOCALE_C_ONLY
29 	int grouping, outdigit;
30 	const char *g;		   /* This does not need to be initialized. */
31 #endif /* __LOCALE_C_ONLY */
32 
33 	negative = 0;
34 	if (base < 0) {				/* signed value */
35 		base = -base;
36 		if (uval > INTMAX_MAX) {
37 			uval = -uval;
38 			negative = 1;
39 		}
40 	}
41 
42 	/* this is an internal routine -- we shouldn't need to check this */
43 	assert(!((base < 2) || (base > 36)));
44 
45 #ifndef __LOCALE_C_ONLY
46 	grouping = -1;
47 	outdigit = 0x80 & alphacase;
48 	alphacase ^= outdigit;
49 	if (alphacase == __UIM_GROUP) {
50 		assert(base == 10);
51 		if (*(g = __UCLIBC_CURLOCALE->grouping)) {
52 			grouping = *g;
53 		}
54 	}
55 #endif /* __LOCALE_C_ONLY */
56 
57 	*bufend = '\0';
58 
59 #ifndef INTERNAL_DIV_MOD
60 	do {
61 #ifndef __LOCALE_C_ONLY
62 		if (!grouping) {		/* Finished a group. */
63 			bufend -= __UCLIBC_CURLOCALE->thousands_sep_len;
64 			memcpy(bufend, __UCLIBC_CURLOCALE->thousands_sep,
65 				   __UCLIBC_CURLOCALE->thousands_sep_len);
66 			if (g[1] != 0) { 	/* g[1] == 0 means repeat last grouping. */
67 				/* Note: g[1] == -1 means no further grouping.  But since
68 				 * we'll never wrap around, we can set grouping to -1 without
69 				 * fear of */
70 				++g;
71 			}
72 			grouping = *g;
73 		}
74 		--grouping;
75 #endif /* __LOCALE_C_ONLY */
76 		digit = uval % base;
77 		uval /= base;
78 
79 #ifndef __LOCALE_C_ONLY
80 		if (unlikely(outdigit)) {
81 			bufend -= __UCLIBC_CURLOCALE->outdigit_length[digit];
82 			memcpy(bufend,
83 				   (&__UCLIBC_CURLOCALE->outdigit0_mb)[digit],
84 				   __UCLIBC_CURLOCALE->outdigit_length[digit]);
85 		} else
86 #endif
87 		{
88 			*--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase );
89 		}
90 	} while (uval);
91 
92 #else  /* ************************************************** */
93 
94 	H = (UINT_MAX / base);
95 	L = UINT_MAX % base + 1;
96 	if (L == base) {
97 		++H;
98 		L = 0;
99 	}
100 	low = (unsigned int) uval;
101 	high = (unsigned int) (uval >> (sizeof(unsigned int) * CHAR_BIT));
102 
103 	do {
104 #ifndef __LOCALE_C_ONLY
105 		if (!grouping) {		/* Finished a group. */
106 			bufend -= __UCLIBC_CURLOCALE->thousands_sep_len;
107 			memcpy(bufend, __UCLIBC_CURLOCALE->thousands_sep,
108 				   __UCLIBC_CURLOCALE->thousands_sep_len);
109 			if (g[1] != 0) { 	/* g[1] == 0 means repeat last grouping. */
110 				/* Note: g[1] == -1 means no further grouping.  But since
111 				 * we'll never wrap around, we can set grouping to -1 without
112 				 * fear of */
113 				++g;
114 			}
115 			grouping = *g;
116 		}
117 		--grouping;
118 #endif /* __LOCALE_C_ONLY */
119 
120 		if (unlikely(high)) {
121 			rh = high % base;
122 			high /= base;
123 			digit = (low % base) + (L * rh);
124 			low = (low / base) + (H * rh) + (digit / base);
125 			digit %= base;
126 		} else {
127 			digit = low % base;
128 			low /= base;
129 		}
130 
131 #ifndef __LOCALE_C_ONLY
132 		if (unlikely(outdigit)) {
133 			bufend -= __UCLIBC_CURLOCALE->outdigit_length[digit];
134 			memcpy(bufend,
135 				   (&__UCLIBC_CURLOCALE->outdigit0_mb)[digit],
136 				   __UCLIBC_CURLOCALE->outdigit_length[digit]);
137 		} else
138 #endif
139 		{
140 			*--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase );
141 		}
142 	} while (low | high);
143 
144 #endif /******************************************************/
145 
146 	if (negative) {
147 		*--bufend = '-';
148 	}
149 
150 	return bufend;
151 }
152