You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
3.4 KiB
C
188 lines
3.4 KiB
C
/* See the end of this file for copyright and license terms. */
|
|
|
|
#include <errno.h>
|
|
|
|
#include "neo/_error.h"
|
|
#include "neo/_nref.h"
|
|
#include "neo/_nstr.h"
|
|
#include "neo/_stddef.h"
|
|
#include "neo/_types.h"
|
|
|
|
static inline u64 nstr2u_unsafe(const char *s, unsigned int radix, error *err)
|
|
{
|
|
u64 num = 0;
|
|
|
|
while (*s != '\0') {
|
|
u64 tmp = num;
|
|
num *= radix;
|
|
if (num / radix != tmp) { /* overflow */
|
|
yeet(err, ERANGE, "Number out of range");
|
|
return 0xffffffffffffffff;
|
|
}
|
|
|
|
u8 digit = 0;
|
|
if (*s >= '0' && *s <= '9')
|
|
digit = *s++ - '0';
|
|
else if (*s >= 'A' && *s <= 'Z')
|
|
digit = *s++ - 'A' + 10;
|
|
else if (*s >= 'a' && *s <= 'z')
|
|
digit = *s++ - 'a' + 10;
|
|
else
|
|
break;
|
|
|
|
if (digit >= radix) {
|
|
s--;
|
|
break;
|
|
}
|
|
|
|
tmp = num;
|
|
num += digit;
|
|
if (tmp > num) { /* overflow */
|
|
yeet(err, ERANGE, "Number out of range");
|
|
return 0xffffffffffffffff;
|
|
}
|
|
}
|
|
|
|
if (*s != '\0') {
|
|
yeet(err, EINVAL, "'%c' is not a base %u digit", *s, radix);
|
|
return 0;
|
|
} else {
|
|
neat(err);
|
|
return num;
|
|
}
|
|
}
|
|
|
|
static const char *strip_prefix_and_guess_radix(int *radix, const char *s, error *err)
|
|
{
|
|
if (*radix < 0 || *radix > 36 || *radix == 1) {
|
|
yeet(err, EINVAL, "Numerical base out of range");
|
|
return nil;
|
|
}
|
|
|
|
int guessed_radix = 10;
|
|
|
|
if (s[0] == '0') {
|
|
switch (s[1]) {
|
|
case 'x':
|
|
case 'X':
|
|
guessed_radix = 16;
|
|
s += 2;
|
|
break;
|
|
case 'o':
|
|
case 'O':
|
|
guessed_radix = 8;
|
|
s += 2;
|
|
break;
|
|
case 'b':
|
|
case 'B':
|
|
s += 2;
|
|
guessed_radix = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*radix == 0)
|
|
*radix = guessed_radix;
|
|
|
|
if (guessed_radix != 10 && *radix != guessed_radix) {
|
|
yeet(err, EINVAL, "Numerical base does not match number prefix");
|
|
return nil;
|
|
}
|
|
|
|
neat(err);
|
|
return s;
|
|
}
|
|
|
|
u64 nstr2u(const nstr_t *s, int radix, error *err)
|
|
{
|
|
if (s == nil) {
|
|
yeet(err, EFAULT, "String is nil");
|
|
return 0;
|
|
}
|
|
|
|
const char *raw = s->_data;
|
|
if (*raw == '+')
|
|
raw++;
|
|
|
|
raw = strip_prefix_and_guess_radix(&radix, raw, err);
|
|
catch(err) {
|
|
return 0;
|
|
}
|
|
|
|
return nstr2u_unsafe(raw, (unsigned int)radix, err);
|
|
}
|
|
|
|
i64 nstr2i(const nstr_t *s, int radix, error *err)
|
|
{
|
|
if (s == nil) {
|
|
yeet(err, EFAULT, "String is nil");
|
|
return 0;
|
|
}
|
|
|
|
const char *raw = s->_data;
|
|
bool negative = *raw == '-';
|
|
if (*raw == '+' || *raw == '-')
|
|
raw++;
|
|
|
|
raw = strip_prefix_and_guess_radix(&radix, raw, err);
|
|
catch(err) {
|
|
return 0;
|
|
}
|
|
|
|
u64 u = nstr2u_unsafe(raw, (unsigned int)radix, err);
|
|
catch(err) {
|
|
return u;
|
|
}
|
|
|
|
if (u == 0)
|
|
negative = false;
|
|
|
|
if (u - (u64)negative >= ((u64)1 << 63)) {
|
|
yeet(err, ERANGE, "Number out of range");
|
|
|
|
i64 ret = ((u64)1 << 63) - 1;
|
|
ret += (i64)negative; /* overflow to min i64 if negative */
|
|
return ret;
|
|
}
|
|
|
|
if (negative)
|
|
return -(i64)u;
|
|
else
|
|
return (i64)u;
|
|
}
|
|
|
|
u64 nstr2u_put(nstr_t *s, int radix, error *err)
|
|
{
|
|
u64 ret = nstr2u(s, radix, err);
|
|
catch(err) {
|
|
return 0;
|
|
}
|
|
|
|
nput(s);
|
|
return ret;
|
|
}
|
|
|
|
i64 nstr2i_put(nstr_t *s, int radix, error *err)
|
|
{
|
|
i64 ret = nstr2i(s, radix, err);
|
|
catch(err) {
|
|
return 0;
|
|
}
|
|
|
|
nput(s);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* This file is part of libneo.
|
|
* Copyright (c) 2021 Fefie <owo@fef.moe>.
|
|
*
|
|
* libneo is non-violent software: you may only use, redistribute,
|
|
* and/or modify it under the terms of the CNPLv6+ as found in
|
|
* the LICENSE file in the source code root directory or at
|
|
* <https://git.pixie.town/thufie/CNPL>.
|
|
*
|
|
* libneo comes with ABSOLUTELY NO WARRANTY, to the extent
|
|
* permitted by applicable law. See the CNPLv6+ for details.
|
|
*/
|