1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
#include "stdio_impl.h"
#include "intscan.h"
#include "shgetc.h"
#include <inttypes.h>
#include <limits.h>
#include <ctype.h>
#ifdef __EMSCRIPTEN__
#include <errno.h>
// Loosely based on __intscan but simplified, and optimized for size
// - Doesn't use FILE or getc/ungetc, just operates directly on memory
// - Avoids lookup table
// - Avoids special cases loops for certain bases
// - Skips an early exit with EINVAL when char 0 is greater than base. Its not
// clear this was correct, and glibc seems not do this either.
static unsigned long long strtox(const char *s, char **p, int base, unsigned long long lim) {
int neg=0;
unsigned long long y=0;
const char* orig = s;
if (base > 36) {
errno = EINVAL;
return 0;
}
while (*s && isspace(*s)) { s++; };
// Handle sign
if (*s=='+' || *s=='-') {
neg = -(*s=='-');
s++;
}
int found_digit = 0;
// Handle hex/octal prefix 0x/00
if ((base == 0 || base == 16) && *s=='0') {
found_digit = 1;
s++;
if ((*s|32)=='x') {
s++;
base = 16;
} else if (base == 0) {
base = 8;
}
} else if (base == 0) {
base = 10;
}
int val;
int overflow = 0;
for (y=0; ; s++) {
if ('0' <= *s && *s <= '9') val = *s -'0';
else if ('a' <= *s && *s <= 'z') val = 10 + *s -'a';
else if ('A' <= *s && *s <= 'Z') val = 10 + *s -'A';
else break;
if (val>=base) break;
if (y > ULLONG_MAX/base || (base*y>ULLONG_MAX-val)) {
overflow = 1;
continue;
}
found_digit = 1;
y = y*base + val;
}
if (p) {
if (found_digit) {
*p = (char*)s;
} else {
*p = (char*)orig;
}
}
if (overflow) {
// We exit'd the above loop due to overflow
errno = ERANGE;
y = lim;
if (lim&1) neg = 0;
}
if (y>=lim) {
if (!(lim&1) && !neg) {
errno = ERANGE;
return lim-1;
} else if (y>lim) {
errno = ERANGE;
return lim;
}
}
return (y^neg)-neg;
}
#else
static unsigned long long strtox(const char *s, char **p, int base, unsigned long long lim)
{
FILE f;
sh_fromstring(&f, s);
shlim(&f, 0);
unsigned long long y = __intscan(&f, base, 1, lim);
if (p) {
size_t cnt = shcnt(&f);
*p = (char *)s + cnt;
}
return y;
}
#endif
unsigned long long strtoull(const char *restrict s, char **restrict p, int base)
{
return strtox(s, p, base, ULLONG_MAX);
}
long long strtoll(const char *restrict s, char **restrict p, int base)
{
return strtox(s, p, base, LLONG_MIN);
}
unsigned long strtoul(const char *restrict s, char **restrict p, int base)
{
return strtox(s, p, base, ULONG_MAX);
}
long strtol(const char *restrict s, char **restrict p, int base)
{
return strtox(s, p, base, 0UL+LONG_MIN);
}
intmax_t strtoimax(const char *restrict s, char **restrict p, int base)
{
return strtoll(s, p, base);
}
uintmax_t strtoumax(const char *restrict s, char **restrict p, int base)
{
return strtoull(s, p, base);
}
weak_alias(strtol, __strtol_internal);
weak_alias(strtoul, __strtoul_internal);
weak_alias(strtoll, __strtoll_internal);
weak_alias(strtoull, __strtoull_internal);
weak_alias(strtoimax, __strtoimax_internal);
weak_alias(strtoumax, __strtoumax_internal);
|