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

/* 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.
*/