mirror of
https://github.com/google/nanolibc.git
synced 2025-10-19 01:21:42 +08:00
152 lines
3.9 KiB
C
152 lines
3.9 KiB
C
// Copyright 2022 Google LLC.
|
||
//
|
||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
//
|
||
// https://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
/*
|
||
* Template source file for strtol/ll/ul/ull functions. Requires
|
||
* appropriate definition of macros below.
|
||
*/
|
||
|
||
/* Specs for strouX() functions are weird: "if there was a leading
|
||
* minus sign, the negation of the result of the con‐ version
|
||
* represented as an unsigned value". This means that we accept to
|
||
* convert strings like "-1234"... */
|
||
|
||
#ifndef _STRTOX_FNAME
|
||
#error "missing function name"
|
||
#endif
|
||
|
||
#ifndef _STRTOX_SIGNED_TNAME
|
||
#error "missing signed type name"
|
||
#endif
|
||
|
||
#ifndef _STRTOX_VARIANT_UNSIGNED
|
||
#error "missing indication of type signedness (0 for unsigned)"
|
||
#endif
|
||
|
||
#ifndef _STRTOX_INTMAX
|
||
#error "missing max value for type"
|
||
#endif
|
||
|
||
#if !_STRTOX_VARIANT_UNSIGNED && !defined(_STRTOX_INTMIN)
|
||
#error "missing min value for signed type"
|
||
#endif
|
||
|
||
#define _STRTOX_UNSIGNED_TNAME unsigned _STRTOX_SIGNED_TNAME
|
||
|
||
#if _STRTOX_VARIANT_UNSIGNED
|
||
unsigned
|
||
#endif
|
||
_STRTOX_SIGNED_TNAME
|
||
_STRTOX_FNAME(const char *nptr, char **endptr, int base) {
|
||
_STRTOX_UNSIGNED_TNAME absval = 0;
|
||
_STRTOX_UNSIGNED_TNAME max_unshifted_absval;
|
||
char dtop, ltop, utop;
|
||
char *s = (char *)nptr;
|
||
const char *endc = nptr;
|
||
int positive = 1;
|
||
int overflow = 0;
|
||
unsigned max_last_digit;
|
||
|
||
if (!((base == 0) || ((base >= 2) && (base <= 36)))) return 0;
|
||
|
||
/* spaces allowed before */
|
||
while (*s && isspace(*s)) s++;
|
||
|
||
/* both + and - allowed before value, even for strouX (!) */
|
||
if (*s == '+')
|
||
s++;
|
||
else if (*s == '-') {
|
||
positive = 0;
|
||
s++;
|
||
}
|
||
|
||
/* Handle automatic base == 0, also allow 0x... in hex mode */
|
||
if ((base == 0) && (!strncmp(s, "0x", 2) || !strncmp(s, "0X", 2))) {
|
||
base = 16;
|
||
endc = s + 1;
|
||
s += 2;
|
||
} else if (base == 16) {
|
||
if (!strncmp(s, "0x", 2) || !strncmp(s, "0X", 2)) {
|
||
base = 16;
|
||
endc = s + 1;
|
||
s += 2;
|
||
}
|
||
} else if ((base == 0) && (*s == '0'))
|
||
base = 8;
|
||
else if (base == 0)
|
||
base = 10;
|
||
|
||
/* Determine min/max allowed digits */
|
||
if (base <= 10) {
|
||
dtop = '0' + base - 1;
|
||
ltop = utop = '\0';
|
||
} else {
|
||
dtop = '0' + 9;
|
||
ltop = 'a' + (base - 10) - 1;
|
||
utop = 'A' + (base - 10) - 1;
|
||
}
|
||
|
||
#if _STRTOX_VARIANT_UNSIGNED
|
||
max_last_digit = _STRTOX_INTMAX % (_STRTOX_UNSIGNED_TNAME)base;
|
||
max_unshifted_absval = _STRTOX_INTMAX / (_STRTOX_UNSIGNED_TNAME)base;
|
||
#else
|
||
if (positive)
|
||
max_unshifted_absval = _STRTOX_INTMAX;
|
||
else
|
||
max_unshifted_absval = -(_STRTOX_UNSIGNED_TNAME)_STRTOX_INTMIN;
|
||
max_last_digit = max_unshifted_absval % (_STRTOX_UNSIGNED_TNAME)base;
|
||
max_unshifted_absval /= (_STRTOX_UNSIGNED_TNAME)base;
|
||
#endif
|
||
|
||
while (*s) {
|
||
_STRTOX_UNSIGNED_TNAME digit;
|
||
if ((*s >= '0') && (*s <= dtop))
|
||
digit = *s - '0';
|
||
else if ((*s >= 'a') && (*s <= ltop))
|
||
digit = 10 + (*s - 'a');
|
||
else if ((*s >= 'A') && (*s <= utop))
|
||
digit = 10 + (*s - 'A');
|
||
else
|
||
break; /* unallowed digit */
|
||
|
||
s++;
|
||
endc = s;
|
||
|
||
if (!overflow) {
|
||
if (absval > max_unshifted_absval)
|
||
overflow = 1;
|
||
else if ((absval == max_unshifted_absval) && (digit > max_last_digit))
|
||
overflow = 1;
|
||
else {
|
||
absval *= base;
|
||
absval += digit;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (overflow) {
|
||
#if !_STRTOX_VARIANT_UNSIGNED
|
||
if (!positive)
|
||
absval = _STRTOX_INTMIN;
|
||
else
|
||
#endif
|
||
absval = _STRTOX_INTMAX;
|
||
} else if (!positive)
|
||
absval = -absval;
|
||
|
||
if (endptr) *endptr = (char *)endc;
|
||
|
||
return absval;
|
||
}
|