source: mainline/uspace/lib/c/generic/strtol.c@ 3061bc1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3061bc1 was 17c14273, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Work around SPARC's multiplication insanity.

  • Property mode set to 100644
File size: 6.6 KB
Line 
1/*
2 * Copyright (c) 2005 Martin Decky
3 * Copyright (c) 2008 Jiri Svoboda
4 * Copyright (c) 2011 Martin Sucha
5 * Copyright (c) 2011 Oleg Romanenko
6 * Copyright (c) 2011 Jiri Zarevucky
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * - Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * - The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/** @addtogroup libc
34 * @{
35 */
36/** @file
37 */
38
39#include <inttypes.h>
40#include <stdlib.h>
41#include <limits.h>
42#include <ctype.h>
43#include <assert.h>
44
45// TODO: unit tests
46
47static inline int _digit_value(int c)
48{
49 if (isdigit(c)) {
50 return c - '0';
51 } else if (islower(c)) {
52 return c - 'a' + 10;
53 } else if (isupper(c)) {
54 return c - 'A' + 10;
55 }
56 return INT_MAX;
57}
58
59/* FIXME: workaround for GCC "optimizing" the overflow check
60 * into soft-emulated 128b multiplication using `__multi3`,
61 * which we don't currently implement.
62 */
63__attribute__((noinline)) static uintmax_t _max_value(int base)
64{
65 return UINTMAX_MAX / base;
66}
67
68static inline uintmax_t _strtoumax(
69 const char *restrict nptr, char **restrict endptr, int base,
70 bool *restrict sgn)
71{
72 assert(nptr != NULL);
73 assert(sgn != NULL);
74
75 /* Skip leading whitespace. */
76
77 while (isspace(*nptr)) {
78 nptr++;
79 }
80
81 /* Parse sign, if any. */
82
83 switch (*nptr) {
84 case '-':
85 *sgn = true;
86 nptr++;
87 break;
88 case '+':
89 nptr++;
90 break;
91 }
92
93 /* Figure out the base. */
94
95 if (base == 0) {
96 if (*nptr == '0') {
97 if (tolower(nptr[1]) == 'x') {
98 /* 0x... is hex. */
99 base = 16;
100 nptr += 2;
101 } else {
102 /* 0... is octal. */
103 base = 8;
104 }
105 } else {
106 /* Anything else is decimal by default. */
107 base = 10;
108 }
109 } else if (base == 16) {
110 /* Allow hex number to be prefixed with "0x". */
111 if (nptr[0] == '0' && tolower(nptr[1]) == 'x') {
112 nptr += 2;
113 }
114 } else if (base < 0 || base == 1 || base > 36) {
115 errno = EINVAL;
116 return 0;
117 }
118
119 /* Read the value. */
120
121 uintmax_t result = 0;
122 uintmax_t max = _max_value(base);
123 int digit;
124
125 while (digit = _digit_value(*nptr), digit < base) {
126
127 if (result > max ||
128 __builtin_add_overflow(result * base, digit, &result)) {
129
130 errno = ERANGE;
131 result = UINTMAX_MAX;
132 break;
133 }
134
135 nptr++;
136 }
137
138 /* Set endptr. */
139
140 if (endptr != NULL) {
141 /* Move the pointer to the end of the number,
142 * in case it isn't there already.
143 */
144 while (_digit_value(*nptr) < base) {
145 nptr++;
146 }
147
148 *endptr = (char *) nptr;
149 }
150
151 return result;
152}
153
154static inline intmax_t _strtosigned(const char *nptr, char **endptr, int base,
155 intmax_t min, intmax_t max)
156{
157 bool sgn = false;
158 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn);
159
160 if (number > (uintmax_t) max) {
161 if (sgn && (number - 1 == (uintmax_t) max)) {
162 return min;
163 }
164
165 errno = ERANGE;
166 return (sgn ? min : max);
167 }
168
169 return (sgn ? -number : number);
170}
171
172static inline uintmax_t _strtounsigned(const char *nptr, char **endptr, int base,
173 uintmax_t max)
174{
175 bool sgn = false;
176 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn);
177
178 if (sgn) {
179 if (number == 0) {
180 return 0;
181 } else {
182 errno = ERANGE;
183 return max;
184 }
185 }
186
187 if (number > max) {
188 errno = ERANGE;
189 return max;
190 }
191
192 return number;
193}
194
195/** Convert initial part of string to long int according to given base.
196 * The number may begin with an arbitrary number of whitespaces followed by
197 * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
198 * inserted and the number will be taken as hexadecimal one. If the base is 0
199 * and the number begin with a zero, number will be taken as octal one (as with
200 * base 8). Otherwise the base 0 is taken as decimal.
201 *
202 * @param nptr Pointer to string.
203 * @param[out] endptr If not NULL, function stores here pointer to the first
204 * invalid character.
205 * @param base Zero or number between 2 and 36 inclusive.
206 * @return Result of conversion.
207 */
208long strtol(const char *nptr, char **endptr, int base)
209{
210 return _strtosigned(nptr, endptr, base, LONG_MIN, LONG_MAX);
211}
212
213/** Convert initial part of string to unsigned long according to given base.
214 * The number may begin with an arbitrary number of whitespaces followed by
215 * optional sign (`+' or `-'). If the base is 0 or 16, the prefix `0x' may be
216 * inserted and the number will be taken as hexadecimal one. If the base is 0
217 * and the number begin with a zero, number will be taken as octal one (as with
218 * base 8). Otherwise the base 0 is taken as decimal.
219 *
220 * @param nptr Pointer to string.
221 * @param[out] endptr If not NULL, function stores here pointer to the first
222 * invalid character
223 * @param base Zero or number between 2 and 36 inclusive.
224 * @return Result of conversion.
225 */
226unsigned long strtoul(const char *nptr, char **endptr, int base)
227{
228 return _strtounsigned(nptr, endptr, base, ULONG_MAX);
229}
230
231long long strtoll(const char *nptr, char **endptr, int base)
232{
233 return _strtosigned(nptr, endptr, base, LLONG_MIN, LLONG_MAX);
234}
235
236unsigned long long strtoull(const char *nptr, char **endptr, int base)
237{
238 return _strtounsigned(nptr, endptr, base, ULLONG_MAX);
239}
240
241intmax_t strtoimax(const char *nptr, char **endptr, int base)
242{
243 return _strtosigned(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX);
244}
245
246uintmax_t strtoumax(const char *nptr, char **endptr, int base)
247{
248 return _strtounsigned(nptr, endptr, base, UINTMAX_MAX);
249}
250
251int atoi(const char *nptr)
252{
253 return (int)strtol(nptr, NULL, 10);
254}
255
256long atol(const char *nptr)
257{
258 return strtol(nptr, NULL, 10);
259}
260
261long long atoll(const char *nptr)
262{
263 return strtoll(nptr, NULL, 10);
264}
265
266/** @}
267 */
Note: See TracBrowser for help on using the repository browser.