1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-18 13:32:32 +01:00

Rename parse to tryParse, add throwing version, return views for split

This commit is contained in:
ζeh Matt
2025-09-28 23:07:29 +03:00
parent 725b3782bd
commit 9eeaf9a001
4 changed files with 63 additions and 34 deletions

View File

@@ -390,13 +390,13 @@ namespace OpenRCT2::String
return buffer; return buffer;
} }
std::vector<std::string> split(std::string_view s, std::string_view delimiter) std::vector<std::string_view> split(std::string_view s, std::string_view delimiter)
{ {
if (delimiter.empty()) if (delimiter.empty())
{ {
throw std::invalid_argument("delimiter can notbe empty."); throw std::invalid_argument("delimiter can not be empty.");
} }
std::vector<std::string> results; std::vector<std::string_view> results;
if (s.empty()) if (s.empty())
{ {
return results; return results;

View File

@@ -15,6 +15,7 @@
#include <cstdarg> #include <cstdarg>
#include <cstddef> #include <cstddef>
#include <optional> #include <optional>
#include <stdexcept>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
@@ -85,7 +86,7 @@ namespace OpenRCT2::String
* Splits the given string by a delimiter and returns the values as a new string array. * Splits the given string by a delimiter and returns the values as a new string array.
* @returns the number of values. * @returns the number of values.
*/ */
std::vector<std::string> split(std::string_view s, std::string_view delimiter); std::vector<std::string_view> split(std::string_view s, std::string_view delimiter);
utf8* skipBOM(utf8* buffer); utf8* skipBOM(utf8* buffer);
const utf8* skipBOM(const utf8* buffer); const utf8* skipBOM(const utf8* buffer);
@@ -113,7 +114,7 @@ namespace OpenRCT2::String
std::string toUpper(std::string_view src); std::string toUpper(std::string_view src);
template<typename T> template<typename T>
std::optional<T> parse(std::string_view input) inline std::optional<T> tryParse(std::string_view input)
{ {
if (input.empty()) if (input.empty())
{ {
@@ -128,6 +129,34 @@ namespace OpenRCT2::String
return std::nullopt; return std::nullopt;
} }
template<typename T>
inline T parse(std::string_view input)
{
if (input.empty())
{
throw std::invalid_argument("Input is empty");
}
T result;
auto [ptr, ec] = std::from_chars(input.data(), input.data() + input.size(), result);
if (ec == std::errc::invalid_argument)
{
throw std::invalid_argument("Invalid argument in conversion");
}
if (ec == std::errc::result_out_of_range)
{
throw std::out_of_range("Result out of range");
}
if (ec != std::errc())
{
throw std::runtime_error("Conversion error");
}
if (ptr != input.data() + input.size())
{
throw std::invalid_argument("Trailing characters after number");
}
return result;
}
/** /**
* Returns string representation of a hexadecimal input, such as SHA256 hash * Returns string representation of a hexadecimal input, such as SHA256 hash
*/ */

View File

@@ -353,12 +353,12 @@ namespace OpenRCT2
auto parts = String::split(s, ".."); auto parts = String::split(s, "..");
if (parts.size() == 1) if (parts.size() == 1)
{ {
result.push_back(std::stoi(parts[0])); result.push_back(String::parse<int32_t>(parts[0]));
} }
else else
{ {
auto left = std::stoi(parts[0]); auto left = String::parse<int32_t>(parts[0]);
auto right = std::stoi(parts[1]); auto right = String::parse<int32_t>(parts[1]);
if (left <= right) if (left <= right)
{ {
for (auto i = left; i <= right; i++) for (auto i = left; i <= right; i++)

View File

@@ -66,17 +66,17 @@ TEST_P(StringTest, TrimStart)
TEST_F(StringTest, Split_ByComma) TEST_F(StringTest, Split_ByComma)
{ {
auto actual = String::split("a,bb,ccc,dd", ","); auto actual = String::split("a,bb,ccc,dd", ",");
AssertVector<std::string>(actual, { "a", "bb", "ccc", "dd" }); AssertVector<std::string_view>(actual, { "a", "bb", "ccc", "dd" });
} }
TEST_F(StringTest, Split_ByColonColon) TEST_F(StringTest, Split_ByColonColon)
{ {
auto actual = String::split("a::bb:ccc:::::dd", "::"); auto actual = String::split("a::bb:ccc:::::dd", "::");
AssertVector<std::string>(actual, { "a", "bb:ccc", "", ":dd" }); AssertVector<std::string_view>(actual, { "a", "bb:ccc", "", ":dd" });
} }
TEST_F(StringTest, Split_Empty) TEST_F(StringTest, Split_Empty)
{ {
auto actual = String::split("", "."); auto actual = String::split("", ".");
AssertVector<std::string>(actual, {}); AssertVector<std::string_view>(actual, {});
} }
TEST_F(StringTest, Split_ByEmpty) TEST_F(StringTest, Split_ByEmpty)
{ {
@@ -294,153 +294,153 @@ TEST_F(StringTest, LogicalCompare)
TEST_F(StringTest, Parse_Basic) TEST_F(StringTest, Parse_Basic)
{ {
auto actual = String::parse<std::int32_t>("123"); auto actual = String::tryParse<std::int32_t>("123");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_EQ(*actual, 123); ASSERT_EQ(*actual, 123);
} }
TEST_F(StringTest, Parse_Empty) TEST_F(StringTest, Parse_Empty)
{ {
auto actual = String::parse<std::int32_t>(""); auto actual = String::tryParse<std::int32_t>("");
ASSERT_FALSE(actual.has_value()); ASSERT_FALSE(actual.has_value());
} }
TEST_F(StringTest, Parse_Zero) TEST_F(StringTest, Parse_Zero)
{ {
auto actual = String::parse<std::int32_t>("0"); auto actual = String::tryParse<std::int32_t>("0");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_EQ(*actual, 0); ASSERT_EQ(*actual, 0);
} }
TEST_F(StringTest, Parse_LeadingZero) TEST_F(StringTest, Parse_LeadingZero)
{ {
auto actual = String::parse<std::int32_t>("00123"); auto actual = String::tryParse<std::int32_t>("00123");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_EQ(*actual, 123); ASSERT_EQ(*actual, 123);
} }
TEST_F(StringTest, Parse_InvalidChar) TEST_F(StringTest, Parse_InvalidChar)
{ {
auto actual = String::parse<std::int32_t>("12a3"); auto actual = String::tryParse<std::int32_t>("12a3");
ASSERT_FALSE(actual.has_value()); ASSERT_FALSE(actual.has_value());
} }
TEST_F(StringTest, Parse_LeadingNonDigit) TEST_F(StringTest, Parse_LeadingNonDigit)
{ {
auto actual = String::parse<std::int32_t>("a123"); auto actual = String::tryParse<std::int32_t>("a123");
ASSERT_FALSE(actual.has_value()); ASSERT_FALSE(actual.has_value());
} }
TEST_F(StringTest, Parse_Negative) TEST_F(StringTest, Parse_Negative)
{ {
auto actual = String::parse<std::int32_t>("-123"); auto actual = String::tryParse<std::int32_t>("-123");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_EQ(*actual, -123); ASSERT_EQ(*actual, -123);
} }
TEST_F(StringTest, Parse_Overflow) TEST_F(StringTest, Parse_Overflow)
{ {
auto actual = String::parse<std::int32_t>("2147483648"); auto actual = String::tryParse<std::int32_t>("2147483648");
ASSERT_FALSE(actual.has_value()); ASSERT_FALSE(actual.has_value());
} }
TEST_F(StringTest, Parse_LargeNumber) TEST_F(StringTest, Parse_LargeNumber)
{ {
auto actual = String::parse<std::int64_t>("9223372036854775807"); auto actual = String::tryParse<std::int64_t>("9223372036854775807");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_EQ(*actual, 9223372036854775807LL); ASSERT_EQ(*actual, 9223372036854775807LL);
} }
TEST_F(StringTest, Parse_FloatBasic) TEST_F(StringTest, Parse_FloatBasic)
{ {
auto actual = String::parse<float>("123.45"); auto actual = String::tryParse<float>("123.45");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_FLOAT_EQ(*actual, 123.45f); ASSERT_FLOAT_EQ(*actual, 123.45f);
} }
TEST_F(StringTest, Parse_FloatZero) TEST_F(StringTest, Parse_FloatZero)
{ {
auto actual = String::parse<float>("0.0"); auto actual = String::tryParse<float>("0.0");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_FLOAT_EQ(*actual, 0.0f); ASSERT_FLOAT_EQ(*actual, 0.0f);
} }
TEST_F(StringTest, Parse_FloatScientific) TEST_F(StringTest, Parse_FloatScientific)
{ {
auto actual = String::parse<float>("1.23e4"); auto actual = String::tryParse<float>("1.23e4");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_FLOAT_EQ(*actual, 12300.0f); ASSERT_FLOAT_EQ(*actual, 12300.0f);
} }
TEST_F(StringTest, Parse_FloatInvalid) TEST_F(StringTest, Parse_FloatInvalid)
{ {
auto actual = String::parse<float>("123.4a5"); auto actual = String::tryParse<float>("123.4a5");
ASSERT_FALSE(actual.has_value()); ASSERT_FALSE(actual.has_value());
} }
TEST_F(StringTest, Parse_FloatLeadingDot) TEST_F(StringTest, Parse_FloatLeadingDot)
{ {
auto actual = String::parse<float>(".123"); auto actual = String::tryParse<float>(".123");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_FLOAT_EQ(*actual, 0.123f); ASSERT_FLOAT_EQ(*actual, 0.123f);
} }
TEST_F(StringTest, Parse_FloatNegative) TEST_F(StringTest, Parse_FloatNegative)
{ {
auto actual = String::parse<float>("-123.45"); auto actual = String::tryParse<float>("-123.45");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_FLOAT_EQ(*actual, -123.45f); ASSERT_FLOAT_EQ(*actual, -123.45f);
} }
TEST_F(StringTest, Parse_FloatOverflow) TEST_F(StringTest, Parse_FloatOverflow)
{ {
auto actual = String::parse<float>("3.4028235e39"); auto actual = String::tryParse<float>("3.4028235e39");
ASSERT_FALSE(actual.has_value()); ASSERT_FALSE(actual.has_value());
} }
TEST_F(StringTest, Parse_DoubleBasic) TEST_F(StringTest, Parse_DoubleBasic)
{ {
auto actual = String::parse<double>("123.456789"); auto actual = String::tryParse<double>("123.456789");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_DOUBLE_EQ(*actual, 123.456789); ASSERT_DOUBLE_EQ(*actual, 123.456789);
} }
TEST_F(StringTest, Parse_DoubleZero) TEST_F(StringTest, Parse_DoubleZero)
{ {
auto actual = String::parse<double>("0.0"); auto actual = String::tryParse<double>("0.0");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_DOUBLE_EQ(*actual, 0.0); ASSERT_DOUBLE_EQ(*actual, 0.0);
} }
TEST_F(StringTest, Parse_DoubleScientific) TEST_F(StringTest, Parse_DoubleScientific)
{ {
auto actual = String::parse<double>("1.23e10"); auto actual = String::tryParse<double>("1.23e10");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_DOUBLE_EQ(*actual, 12300000000.0); ASSERT_DOUBLE_EQ(*actual, 12300000000.0);
} }
TEST_F(StringTest, Parse_DoubleInvalid) TEST_F(StringTest, Parse_DoubleInvalid)
{ {
auto actual = String::parse<double>("123.4a5"); auto actual = String::tryParse<double>("123.4a5");
ASSERT_FALSE(actual.has_value()); ASSERT_FALSE(actual.has_value());
} }
TEST_F(StringTest, Parse_DoubleLeadingDot) TEST_F(StringTest, Parse_DoubleLeadingDot)
{ {
auto actual = String::parse<double>(".123"); auto actual = String::tryParse<double>(".123");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_DOUBLE_EQ(*actual, 0.123); ASSERT_DOUBLE_EQ(*actual, 0.123);
} }
TEST_F(StringTest, Parse_DoubleNegative) TEST_F(StringTest, Parse_DoubleNegative)
{ {
auto actual = String::parse<double>("-123.45"); auto actual = String::tryParse<double>("-123.45");
ASSERT_TRUE(actual.has_value()); ASSERT_TRUE(actual.has_value());
ASSERT_DOUBLE_EQ(*actual, -123.45); ASSERT_DOUBLE_EQ(*actual, -123.45);
} }
TEST_F(StringTest, Parse_DoubleOverflow) TEST_F(StringTest, Parse_DoubleOverflow)
{ {
auto actual = String::parse<double>("1.7976931348623158e309"); auto actual = String::tryParse<double>("1.7976931348623158e309");
ASSERT_FALSE(actual.has_value()); ASSERT_FALSE(actual.has_value());
} }