source: mainline/kernel/generic/src/lib/strtol.c@ 42e91ae

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

Replace kernel's copy of str_uint64_t with a piece of strtol.c

  • Property mode set to 100644
File size: 6.9 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 kernel_generic
34 * @{
35 */
36/** @file
37 */
38
39#include <assert.h>
40#include <errno.h>
41#include <inttypes.h>
42#include <limits.h>
43#include <stdbool.h>
44#include <stdlib.h>
45#include <str.h>
46
47// FIXME: The original HelenOS functions return EOVERFLOW instead
48// of ERANGE. It's a pointless distinction from standard functions,
49// so we should change that. Beware the callers though.
50
51// TODO: more unit tests
52
53static inline int isdigit(int c)
54{
55 return c >= '0' && c <= '9';
56}
57
58static inline int islower(int c)
59{
60 return c >= 'a' && c <= 'z';
61}
62
63static inline int isupper(int c)
64{
65 return c >= 'A' && c <= 'Z';
66}
67
68static inline int isspace(int c)
69{
70 switch (c) {
71 case ' ':
72 case '\n':
73 case '\t':
74 case '\f':
75 case '\r':
76 case '\v':
77 return 1;
78 default:
79 return 0;
80 }
81}
82
83static inline int _digit_value(int c)
84{
85 if (isdigit(c)) {
86 return c - '0';
87 } else if (islower(c)) {
88 return c - 'a' + 10;
89 } else if (isupper(c)) {
90 return c - 'A' + 10;
91 }
92 return INT_MAX;
93}
94
95/*
96 * FIXME: workaround for GCC "optimizing" the overflow check
97 * into soft-emulated 128b multiplication using `__multi3`,
98 * which we don't currently implement.
99 */
100__attribute__((noinline)) static uintmax_t _max_value(int base)
101{
102 return UINTMAX_MAX / base;
103}
104
105static inline int _prefixbase(const char *restrict *nptrptr, bool nonstd)
106{
107 const char *nptr = *nptrptr;
108
109 if (nptr[0] != '0')
110 return 10;
111
112 if (nptr[1] == 'x' || nptr[1] == 'X') {
113 if (_digit_value(nptr[2]) < 16) {
114 *nptrptr += 2;
115 return 16;
116 }
117 }
118
119 if (nonstd) {
120 switch (nptr[1]) {
121 case 'b':
122 case 'B':
123 if (_digit_value(nptr[2]) < 2) {
124 *nptrptr += 2;
125 return 2;
126 }
127 break;
128 case 'o':
129 case 'O':
130 if (_digit_value(nptr[2]) < 8) {
131 *nptrptr += 2;
132 return 8;
133 }
134 break;
135 case 'd':
136 case 'D':
137 case 't':
138 case 'T':
139 if (_digit_value(nptr[2]) < 10) {
140 *nptrptr += 2;
141 return 10;
142 }
143 break;
144 }
145 }
146
147 return 8;
148}
149
150static inline uintmax_t _strtoumax(
151 const char *restrict nptr, char **restrict endptr, int base,
152 bool *restrict sgn, errno_t *err, bool nonstd)
153{
154 assert(nptr != NULL);
155 assert(sgn != NULL);
156
157 const char *first = nptr;
158
159 /* Skip leading whitespace. */
160
161 while (isspace(*nptr)) {
162 nptr++;
163 }
164
165 /* Parse sign, if any. */
166
167 switch (*nptr) {
168 case '-':
169 *sgn = true;
170 nptr++;
171 break;
172 case '+':
173 nptr++;
174 break;
175 }
176
177 /* Figure out the base. */
178
179 if (base == 0)
180 base = _prefixbase(&nptr, nonstd);
181
182 if (base == 16 && !nonstd) {
183 /*
184 * Standard strto* functions allow hexadecimal prefix to be
185 * present when base is explicitly set to 16.
186 * Our nonstandard str_* functions don't allow it.
187 * I don't know if that is intended, just matching the original
188 * functionality here.
189 */
190
191 if (nptr[0] == '0' && (nptr[1] == 'x' || nptr[1] == 'X') &&
192 _digit_value(nptr[2]) < base)
193 nptr += 2;
194 }
195
196 if (base < 2 || base > 36) {
197 *err = EINVAL;
198 return 0;
199 }
200
201 /* Must be at least one digit. */
202
203 if (_digit_value(*nptr) >= base) {
204 /* No digits on input. */
205 if (endptr != NULL)
206 *endptr = (char *) first;
207 return 0;
208 }
209
210 /* Read the value. */
211
212 uintmax_t result = 0;
213 uintmax_t max = _max_value(base);
214 int digit;
215
216 while (digit = _digit_value(*nptr), digit < base) {
217 if (result > max ||
218 __builtin_add_overflow(result * base, digit, &result)) {
219
220 *err = nonstd ? EOVERFLOW : ERANGE;
221 result = UINTMAX_MAX;
222 break;
223 }
224
225 nptr++;
226 }
227
228 /* Set endptr. */
229
230 if (endptr != NULL) {
231 /*
232 * Move the pointer to the end of the number,
233 * in case it isn't there already.
234 * This can happen when the number has legal formatting,
235 * but is out of range of the target type.
236 */
237 while (_digit_value(*nptr) < base) {
238 nptr++;
239 }
240
241 *endptr = (char *) nptr;
242 }
243
244 return result;
245}
246
247static inline intmax_t _strtosigned(const char *nptr, char **endptr, int base,
248 intmax_t min, intmax_t max, errno_t *err, bool nonstd)
249{
250 bool sgn = false;
251 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err, nonstd);
252
253 if (number > (uintmax_t) max) {
254 if (sgn && (number - 1 == (uintmax_t) max)) {
255 return min;
256 }
257
258 *err = nonstd ? EOVERFLOW : ERANGE;
259 return (sgn ? min : max);
260 }
261
262 return (sgn ? -number : number);
263}
264
265static inline uintmax_t _strtounsigned(const char *nptr, char **endptr, int base,
266 uintmax_t max, errno_t *err, bool nonstd)
267{
268 bool sgn = false;
269 uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err, nonstd);
270
271 if (nonstd && sgn) {
272 /* Do not allow negative values */
273 *err = EINVAL;
274 return 0;
275 }
276
277 if (number > max) {
278 *err = nonstd ? EOVERFLOW : ERANGE;
279 return max;
280 }
281
282 return (sgn ? -number : number);
283}
284
285/** Convert string to uint64_t.
286 *
287 * @param nptr Pointer to string.
288 * @param endptr If not NULL, pointer to the first invalid character
289 * is stored here.
290 * @param base Zero or number between 2 and 36 inclusive.
291 * @param strict Do not allow any trailing characters.
292 * @param result Result of the conversion.
293 *
294 * @return EOK if conversion was successful.
295 *
296 */
297errno_t str_uint64_t(const char *nptr, char **endptr, unsigned int base,
298 bool strict, uint64_t *result)
299{
300 assert(result != NULL);
301
302 errno_t rc = EOK;
303 char *lendptr = (char *) nptr;
304
305 uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT64_MAX, &rc, true);
306
307 if (endptr)
308 *endptr = lendptr;
309
310 if (rc != EOK)
311 return rc;
312
313 if (strict && *lendptr != '\0')
314 return EINVAL;
315
316 *result = r;
317 return EOK;
318}
319
320/** @}
321 */
Note: See TracBrowser for help on using the repository browser.