string: add nstr2i and nstr2u
This commit is contained in:
parent
57b24014dc
commit
026bfd0b39
6 changed files with 715 additions and 1 deletions
|
@ -131,6 +131,76 @@ nstr_t *i2nstr(i64 i, int radix, error *err);
|
|||
*/
|
||||
nstr_t *u2nstr(u64 u, int radix, error *err);
|
||||
|
||||
/**
|
||||
* @brief Parse a numerical string to a signed integer.
|
||||
*
|
||||
* The string is parsed case-insensitive and may be prefixed with a single
|
||||
* `+` or `-` sign. If `radix` is 0, the first two characters (after the
|
||||
* sign, if present) determine the base:
|
||||
*
|
||||
* - If the string begins with `0b` or `0B`, `radix` is 2.
|
||||
* - If the string begins with `0o` or `0O`, `radix` is 8.
|
||||
* - If the string begins with `0x` or `0X`, `radix` is 16.
|
||||
* - In all other cases, `radix` is 10, even if there are leading zeroes.
|
||||
*
|
||||
* If the number contains illegal characters or cannot be stored in an `i64`,
|
||||
* `s` is nil, or `radix` is outside the allowed range, an error is yeeted.
|
||||
*
|
||||
* @param s String to parse
|
||||
* @param radix Base of the numerical system to interpret the string as.
|
||||
* Must be either within 2~36, or 0 to determine automatically (see above).
|
||||
* @param err Error pointer
|
||||
* @returns The parsed integer, unless an error occurred
|
||||
*/
|
||||
i64 nstr2i(const nstr_t *s, int radix, error *err);
|
||||
|
||||
/**
|
||||
* @brief The same as `nstr2i()`, but `nput()` is called on `s` afterwards.
|
||||
*
|
||||
* @param s String to parse
|
||||
* @param radix Base of the numerical system to interpret the string as.
|
||||
* Must be either within 2~36, or 0 to determine automatically.
|
||||
* @param err Error pointer
|
||||
* @returns The parsed integer, unless an error occurred
|
||||
* @see nstr2i
|
||||
*/
|
||||
i64 nstr2i_put(nstr_t *s, int radix, error *err);
|
||||
|
||||
/**
|
||||
* @brief Parse a numerical string to an unsigned integer.
|
||||
*
|
||||
* The string is parsed case-insensitive and may be prefixed with a single
|
||||
* `+` (no `-`) sign. If `radix` is 0, the first two characters (after the
|
||||
* sign, if present) determine the base:
|
||||
*
|
||||
* - If the string begins with `0b` or `0B`, `radix` is 2.
|
||||
* - If the string begins with `0o` or `0O`, `radix` is 8.
|
||||
* - If the string begins with `0x` or `0X`, `radix` is 16.
|
||||
* - In all other cases, `radix` is 10, even if there are leading zeroes.
|
||||
*
|
||||
* If the number contains illegal characters or cannot be stored in an `u64`,
|
||||
* `s` is nil, or `radix` is outside the allowed range, an error is yeeted.
|
||||
*
|
||||
* @param s String to parse
|
||||
* @param radix Base of the numerical system to interpret the string as.
|
||||
* Must be either within 2~36, or 0 to determine automatically (see above).
|
||||
* @param err Error pointer
|
||||
* @returns The parsed integer, unless an error occurred
|
||||
*/
|
||||
u64 nstr2u(const nstr_t *s, int radix, error *err);
|
||||
|
||||
/**
|
||||
* @brief The same as `nstr2u()`, but `nput()` is called on `s` afterwards.
|
||||
*
|
||||
* @param s String to parse
|
||||
* @param radix Base of the numerical system to interpret the string as.
|
||||
* Must be either within 2~36, or 0 to determine automatically (see above).
|
||||
* @param err Error pointer
|
||||
* @returns The parsed integer, unless an error occurred
|
||||
* @see nstr2u
|
||||
*/
|
||||
u64 nstr2u_put(nstr_t *s, int radix, error *err);
|
||||
|
||||
/**
|
||||
* @brief Duplicate a string.
|
||||
*
|
||||
|
|
187
src/string/nstr2x.c
Normal file
187
src/string/nstr2x.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
/* 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.
|
||||
*/
|
|
@ -1,11 +1,12 @@
|
|||
target_sources(neo PRIVATE
|
||||
./string/leftpad.c
|
||||
./string/nstr.c
|
||||
./string/nstr2x.c
|
||||
./string/nstrcat.c
|
||||
./string/nstrcmp.c
|
||||
./string/nstrdup.c
|
||||
./string/nstrmul.c
|
||||
./string/nstrtrim.c
|
||||
./string/leftpad.c
|
||||
./string/utf.c
|
||||
./string/x2nstr.c
|
||||
)
|
||||
|
|
239
test/string/nstr2i.cpp
Normal file
239
test/string/nstr2i.cpp
Normal file
|
@ -0,0 +1,239 @@
|
|||
/* See the end of this file for copyright and license terms. */
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <errno.h>
|
||||
|
||||
#include <neo.h>
|
||||
|
||||
extern "C" struct nstr2i_positive_case {
|
||||
const char *s;
|
||||
i64 i;
|
||||
int radix;
|
||||
};
|
||||
|
||||
TEST_CASE( "nstr2i: Parse numbers", "[string/nstr2x.c]" )
|
||||
{
|
||||
struct nstr2i_positive_case positives[] = {
|
||||
{"0", 0, 2 },
|
||||
{"0", 0, 36 },
|
||||
{"1y2p0ij32e8e7", 0x7fffffffffffffff, 36 },
|
||||
{"-1y2p0ij32e8e8", (i64)0x8000000000000000, 36 },
|
||||
{"1Y2P0IJ32E8E7", 0x7fffffffffffffff, 36 },
|
||||
{"-1Y2P0IJ32E8E8", (i64)0x8000000000000000, 36 },
|
||||
{"0x7fffffffffffffff", 0x7fffffffffffffff, 0 },
|
||||
{"-0x8000000000000000", (i64)0x8000000000000000, 0 },
|
||||
{"69420", 69420, 0 },
|
||||
{"+69420", 69420, 0 },
|
||||
{"-69420", -69420, 0 },
|
||||
{"0xff", 0xff, 0 },
|
||||
{"0XFF", 0xff, 0 },
|
||||
{"+0xff", 0xff, 0 },
|
||||
{"+0XFF", 0xff, 0 },
|
||||
{"-0xff", -0xff, 0 },
|
||||
{"-0XFF", -0xff, 0 },
|
||||
{"0b11111111", 0xff, 0 },
|
||||
{"0B11111111", 0xff, 0 },
|
||||
{"0o77777777", 077777777, 0 },
|
||||
{"0O77777777", 077777777, 0 },
|
||||
{"069420", 69420, 0 }
|
||||
};
|
||||
|
||||
struct nstr2i_positive_case *current_case = &positives[0];
|
||||
do {
|
||||
error err;
|
||||
nstr_t *s = nstr(current_case->s, nil);
|
||||
i64 i = nstr2i(s, current_case->radix, &err);
|
||||
|
||||
REQUIRE( i == current_case->i );
|
||||
REQUIRE( errnum(&err) == 0 );
|
||||
} while (++current_case < &positives[0] + sizeof(positives) / sizeof(positives[0]));
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i: Error if number too low", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("-8000000000000001", nil);
|
||||
i64 i = nstr2i(s, 16, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("Number out of range", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( i == -0x8000000000000000 );
|
||||
REQUIRE( errnum(&err) == ERANGE );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i: Error if number too high", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("8000000000000000", nil);
|
||||
i64 i = nstr2i(s, 16, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("Number out of range", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( i == 0x7fffffffffffffff );
|
||||
REQUIRE( errnum(&err) == ERANGE );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i: Error if base too low", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("420", nil);
|
||||
i64 i = nstr2i(s, -1, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("Numerical base out of range", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( i == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i: Error if base is 1", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("420", nil);
|
||||
i64 i = nstr2i(s, 1, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("Numerical base out of range", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( i == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i: Error if base too high", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("420", nil);
|
||||
i64 i = nstr2i(s, 37, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("Numerical base out of range", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( i == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i: Error if character not part of numerical system", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("012", nil);
|
||||
i64 i = nstr2i(s, 2, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("'2' is not a base 2 digit", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( i == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i: Error if non-digit character", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("0,12", nil);
|
||||
i64 i = nstr2i(s, 2, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("',' is not a base 2 digit", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( i == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i: Error if string is nil", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
i64 i = nstr2i(nil, 2, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("String is nil", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( i == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EFAULT );
|
||||
|
||||
nput(expected_msg);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i_put: Decrement refcount", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("420", nil);
|
||||
nget(s);
|
||||
i64 i = nstr2i_put(s, 10, &err);
|
||||
|
||||
REQUIRE( i == 420 );
|
||||
REQUIRE( nref_count(s) == 1 );
|
||||
|
||||
nput(s);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2i_put: Error if string is nil", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
i64 i = nstr2i_put(nil, 2, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("String is nil", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( i == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EFAULT );
|
||||
|
||||
nput(expected_msg);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
215
test/string/nstr2u.cpp
Normal file
215
test/string/nstr2u.cpp
Normal file
|
@ -0,0 +1,215 @@
|
|||
/* See the end of this file for copyright and license terms. */
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <errno.h>
|
||||
|
||||
#include <neo.h>
|
||||
|
||||
extern "C" struct nstr2u_positive_case {
|
||||
const char *s;
|
||||
u64 u;
|
||||
int radix;
|
||||
};
|
||||
|
||||
TEST_CASE( "nstr2u: Parse numbers", "[string/nstr2x.c]" )
|
||||
{
|
||||
struct nstr2u_positive_case positives[] = {
|
||||
{"0", 0, 2 },
|
||||
{"0", 0, 36 },
|
||||
{"3w5e11264sgsf", 0xffffffffffffffff, 36 },
|
||||
{"3W5E11264SGSF", 0xffffffffffffffff, 36 },
|
||||
{"0xffffffffffffffff", 0xffffffffffffffff, 0 },
|
||||
{"69420", 69420, 0 },
|
||||
{"+69420", 69420, 0 },
|
||||
{"0xff", 0xff, 0 },
|
||||
{"0XFF", 0xff, 0 },
|
||||
{"+0xff", 0xff, 0 },
|
||||
{"+0XFF", 0xff, 0 },
|
||||
{"0b11111111", 0xff, 0 },
|
||||
{"0B11111111", 0xff, 0 },
|
||||
{"0o77777777", 077777777, 0 },
|
||||
{"0O77777777", 077777777, 0 },
|
||||
{"069420", 69420, 0 }
|
||||
};
|
||||
|
||||
struct nstr2u_positive_case *current_case = &positives[0];
|
||||
do {
|
||||
error err;
|
||||
nstr_t *s = nstr(current_case->s, nil);
|
||||
u64 u = nstr2u(s, current_case->radix, &err);
|
||||
|
||||
REQUIRE( u == current_case->u );
|
||||
REQUIRE( errnum(&err) == 0 );
|
||||
} while (++current_case < &positives[0] + sizeof(positives) / sizeof(positives[0]));
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2u: Error if number too high", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("10000000000000000", nil);
|
||||
u64 u = nstr2u(s, 16, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("Number out of range", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( u == 0xffffffffffffffff );
|
||||
REQUIRE( errnum(&err) == ERANGE );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2u: Error if base too low", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("420", nil);
|
||||
u64 u = nstr2u(s, -1, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("Numerical base out of range", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( u == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2u: Error if base is 1", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("420", nil);
|
||||
u64 u = nstr2u(s, 1, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("Numerical base out of range", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( u == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2u: Error if base too high", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("420", nil);
|
||||
u64 u = nstr2u(s, 37, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("Numerical base out of range", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( u == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2u: Error if character not part of numerical system", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("012", nil);
|
||||
u64 u = nstr2u(s, 2, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("'2' is not a base 2 digit", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( u == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2u: Error if non-digit character", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("0,12", nil);
|
||||
u64 u = nstr2u(s, 2, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("',' is not a base 2 digit", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( u == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EINVAL );
|
||||
|
||||
nput(expected_msg);
|
||||
nput(actual_msg);
|
||||
nput(s);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2u: Error if string is nil", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
u64 u = nstr2u(nil, 2, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("String is nil", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( u == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EFAULT );
|
||||
|
||||
nput(expected_msg);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2u_put: Decrement refcount", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
nstr_t *s = nstr("420", nil);
|
||||
nget(s);
|
||||
u64 u = nstr2u_put(s, 10, &err);
|
||||
|
||||
REQUIRE( u == 420 );
|
||||
REQUIRE( nref_count(s) == 1 );
|
||||
|
||||
nput(s);
|
||||
}
|
||||
|
||||
TEST_CASE( "nstr2u_put: Error if string is nil", "[string/nstr2x.c]" )
|
||||
{
|
||||
error err;
|
||||
u64 u = nstr2u_put(nil, 2, &err);
|
||||
|
||||
nstr_t *expected_msg = nstr("String is nil", nil);
|
||||
nstr_t *actual_msg = errmsg(&err);
|
||||
|
||||
REQUIRE( u == 0 );
|
||||
REQUIRE( nstreq(expected_msg, actual_msg, nil) );
|
||||
REQUIRE( errnum(&err) == EFAULT );
|
||||
|
||||
nput(expected_msg);
|
||||
errput(&err);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
|
@ -6,6 +6,8 @@ target_sources(neo_test PRIVATE
|
|||
string/i2nstr.cpp
|
||||
string/leftpad.cpp
|
||||
string/nstr.cpp
|
||||
string/nstr2i.cpp
|
||||
string/nstr2u.cpp
|
||||
string/nstrcat.cpp
|
||||
string/nstrcmp.cpp
|
||||
string/nstrdup.cpp
|
||||
|
|
Loading…
Reference in a new issue