The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

#include "test.h"
#include <panda/string.h>
#include <panda/from_chars.h>
#define TEST(name) TEST_CASE("from_chars: " name, "[from_chars]")
struct Exc : std::exception {};
template <typename T> struct nstr;
template <> struct nstr<int8_t> {
static string min () { return "-128"; }
static string max () { return "127"; }
static string mmin () { return "-129"; }
static string mmax () { return "128"; }
};
template <> struct nstr<uint8_t> {
static string min () { return "0"; }
static string max () { return "255"; }
static string mmin () { return ""; }
static string mmax () { return "256"; }
};
template <> struct nstr<int16_t> {
static string min () { return "-32768"; }
static string max () { return "32767"; }
static string mmin () { return "-32769"; }
static string mmax () { return "32768"; }
};
template <> struct nstr<uint16_t> {
static string min () { return "0"; }
static string max () { return "65535"; }
static string mmin () { return ""; }
static string mmax () { return "65536"; }
};
template <> struct nstr<int32_t> {
static string min () { return "-2147483648"; }
static string max () { return "2147483647"; }
static string mmin () { return "-2147483649"; }
static string mmax () { return "2147483648"; }
};
template <> struct nstr<uint32_t> {
static string min () { return "0"; }
static string max () { return "4294967295"; }
static string mmin () { return ""; }
static string mmax () { return "4294967296"; }
};
template <> struct nstr<int64_t> {
static string min () { return "-9223372036854775808"; }
static string max () { return "9223372036854775807"; }
static string mmin () { return "-9223372036854775809"; }
static string mmax () { return "9223372036854775808"; }
};
template <> struct nstr<uint64_t> {
static string min () { return "0"; }
static string max () { return "18446744073709551615"; }
static string mmin () { return ""; }
static string mmax () { return "18446744073709551616"; }
};
template<typename Int>
Int fci (string str, unsigned& pos, int base = 10) {
Int val;
auto res = panda::from_chars(str.data() + pos, str.data() + str.size() - pos, val, base);
pos = res.ptr - str.data();
if (res.ec) throw Exc();
return val;
}
template <typename Int, bool is_signed = std::numeric_limits<Int>::is_signed>
struct test_sign_dependent;
template <typename Int>
struct test_sign_dependent<Int, true> {
static void run () {
unsigned pos = 0;
SECTION("negative number") {
REQUIRE(fci<Int>("-65", pos) == -65);
REQUIRE(pos == 3);
}
SECTION("under min") {
REQUIRE_THROWS_AS(fci<Int>(nstr<Int>::mmin(), pos), Exc);
REQUIRE(pos == nstr<Int>::mmin().size());
}
SECTION("-dohuya") {
REQUIRE_THROWS_AS(fci<Int>(" -123456789012345678901234567890123456789012345678901234567890", pos), Exc);
REQUIRE(pos == 65);
}
SECTION("negative 8-base") {
REQUIRE(fci<Int>(" -0012", pos, 8) == -10);
REQUIRE(pos == 6);
}
SECTION("negative 16-base") {
REQUIRE(fci<Int>(" -0Dqwe", pos, 16) == -13);
REQUIRE(pos == 5);
}
}
};
template <typename Int>
struct test_sign_dependent<Int, false> {
static void run () {
unsigned pos = 0;
SECTION("negative number") {
REQUIRE_THROWS_AS(fci<Int>("-65", pos), Exc);
REQUIRE(pos == 0);
}
}
};
template <typename Int>
void from_chars_test() {
unsigned pos = 0;
SECTION("just number") {
REQUIRE(fci<Int>("12", pos) == 12);
REQUIRE(pos == 2);
}
SECTION("+ sign not supported") {
REQUIRE_THROWS_AS(fci<Int>("+55", pos), Exc);
REQUIRE(pos == 0);
}
SECTION("junk after number") {
REQUIRE(fci<Int>("14abc", pos) == 14);
REQUIRE(pos == 2);
}
SECTION("spaces before number") {
REQUIRE(fci<Int>(" 32epta", pos) == 32);
REQUIRE(pos == 7);
}
SECTION("floating point") {
REQUIRE(fci<Int>(" 65.3", pos) == 65);
REQUIRE(pos == 3);
}
SECTION("junk only") {
REQUIRE_THROWS_AS(fci<Int>("asdff", pos), Exc);
REQUIRE(pos == 0);
}
SECTION("empty") {
REQUIRE_THROWS_AS(fci<Int>("", pos), Exc);
REQUIRE(pos == 0);
}
SECTION("non-digits only") {
REQUIRE_THROWS_AS(fci<Int>(" -", pos), Exc);
REQUIRE(pos == 0);
}
SECTION("max") {
REQUIRE(fci<Int>(nstr<Int>::max(), pos) == std::numeric_limits<Int>::max());
REQUIRE(pos == nstr<Int>::max().size());
}
SECTION("above max") {
REQUIRE_THROWS_AS(fci<Int>(nstr<Int>::mmax(), pos), Exc);
REQUIRE(pos == nstr<Int>::mmax().size());
}
SECTION("dohuya") {
REQUIRE_THROWS_AS(fci<Int>(" 123456789012345678901234567890123456789012345678901234567890", pos), Exc);
REQUIRE(pos == 65);
}
SECTION("min") {
REQUIRE(fci<Int>(nstr<Int>::min(), pos) == std::numeric_limits<Int>::min());
REQUIRE(pos == nstr<Int>::min().size());
}
SECTION("8-base") {
SECTION("simple") {
REQUIRE(fci<Int>("11", pos, 8) == 9);
REQUIRE(pos == 2);
}
SECTION("with leading zero") {
REQUIRE(fci<Int>("011", pos, 8) == 9);
REQUIRE(pos == 3);
}
}
SECTION("16-base") {
SECTION("simple") {
REQUIRE(fci<Int>("11", pos, 16) == 17);
REQUIRE(pos == 2);
}
SECTION("0x not supported for 16-base") {
REQUIRE(fci<Int>("0x10", pos, 16) == 0);
REQUIRE(pos == 1);
}
SECTION("letters") {
REQUIRE(fci<Int>("0F", pos, 16) == 15);
REQUIRE(pos == 2);
}
}
SECTION("invalid base") {
SECTION("0-base = 10 base") {
REQUIRE(fci<Int>("12", pos, 0) == 12);
REQUIRE(pos == 2);
}
SECTION("1-base = 10 base") {
REQUIRE(fci<Int>("13", pos, 0) == 13);
REQUIRE(pos == 2);
}
SECTION("36-base") {
REQUIRE(fci<Int>("13", pos, 36) == 39);
REQUIRE(pos == 2);
}
SECTION(">36-base = 10 base") {
REQUIRE(fci<Int>("13", pos, 37) == 13);
REQUIRE(pos == 2);
}
}
test_sign_dependent<Int>::run();
}
TEST("int8_t") { from_chars_test<int8_t>(); }
TEST("int16_t") { from_chars_test<int16_t>(); }
TEST("int32_t") { from_chars_test<int32_t>(); }
TEST("int64_t") { from_chars_test<int64_t>(); }
TEST("uint8_t") { from_chars_test<uint8_t>(); }
TEST("uint16_t") { from_chars_test<uint16_t>(); }
TEST("uint32_t") { from_chars_test<uint32_t>(); }
TEST("uint64_t") { from_chars_test<uint64_t>(); }