You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
2.3 KiB
112 lines
2.3 KiB
3 months ago
|
/*
|
||
|
* UCW Library -- Conversions of Strings to Numbers
|
||
|
*
|
||
|
* (c) 2010 Daniel Fiala <danfiala@ucw.cz>
|
||
|
*
|
||
|
* This software may be freely distributed and used according to the terms
|
||
|
* of the GNU Lesser General Public License.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* This is not a normal header file, it is a generator of a function for
|
||
|
* converting strings to integers of a certain type. This file should be used
|
||
|
* by ucw/stronum.c only.
|
||
|
*/
|
||
|
|
||
|
#define STN_DECLARE(type, suffix) STN_DECLARE_CONVERTOR(type, suffix)
|
||
|
|
||
|
#define S_HELPER2(name, suffix) name##suffix
|
||
|
#define S_HELPER1(name, suffix) S_HELPER2(name, suffix)
|
||
|
#define S(name) S_HELPER1(name, STN_SUFFIX)
|
||
|
|
||
|
#define STN_MAX ((STN_TYPE)(-1))
|
||
|
static const STN_TYPE S(tops)[STN_DBASES_MASK+1] = { [2] = STN_MAX/2, [8] = STN_MAX/8, [10] = STN_MAX/10, [16] = STN_MAX/16 };
|
||
|
|
||
|
static const char *S(parse_string)(const char **pp, const uint flags, const uint sign, const uint base, STN_TYPE *num)
|
||
|
{
|
||
|
const STN_TYPE max = STN_MAX;
|
||
|
const STN_TYPE top = S(tops)[base];
|
||
|
if (!top)
|
||
|
return "Unknown base";
|
||
|
|
||
|
const STN_TYPE sign_max = ((flags & STN_SIGNED) || sign) ? max/2 + sign : max;
|
||
|
|
||
|
STN_TYPE val = 0;
|
||
|
uint digits = 0;
|
||
|
int overflow = 0;
|
||
|
for (;; (*pp)++)
|
||
|
{
|
||
|
const uint c = (byte)**pp;
|
||
|
|
||
|
if (c == '_')
|
||
|
{
|
||
|
if (flags & STN_UNDERSCORE)
|
||
|
continue;
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
const uint d = get_digit(c);
|
||
|
if (d >= base)
|
||
|
break;
|
||
|
|
||
|
digits++;
|
||
|
|
||
|
if (overflow)
|
||
|
continue;
|
||
|
|
||
|
STN_TYPE v = val;
|
||
|
if ( (overflow = (v > top || (v *= base) > sign_max - d)) )
|
||
|
continue;
|
||
|
val = v + d;
|
||
|
}
|
||
|
|
||
|
if (!overflow)
|
||
|
{
|
||
|
if (!digits)
|
||
|
return "Number contains no digits";
|
||
|
|
||
|
if (sign)
|
||
|
val = -val;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (flags & STN_TRUNC)
|
||
|
val = sign_max;
|
||
|
else
|
||
|
return "Numeric overflow";
|
||
|
}
|
||
|
|
||
|
if ((flags & STN_WHOLE) && **pp)
|
||
|
return "Invalid character";
|
||
|
|
||
|
if (num)
|
||
|
*num = val;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
STN_DECLARE(STN_TYPE, STN_SUFFIX)
|
||
|
{
|
||
|
const char *err = NULL;
|
||
|
|
||
|
uint sign, base;
|
||
|
err = str_to_num_init(&str, flags, &sign, &base);
|
||
|
|
||
|
if (!err)
|
||
|
err = S(parse_string)(&str, flags, sign, base, num);
|
||
|
|
||
|
if (next)
|
||
|
*next = str;
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
#undef STN_MAX
|
||
|
#undef S
|
||
|
#undef S_HELPER1
|
||
|
#undef S_HELPER2
|
||
|
#undef STN_TYPE
|
||
|
#undef STN_SUFFIX
|
||
|
#undef STN_DECLARE
|