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:
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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++)
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user