Attempt to catch out-of-range error

This commit is contained in:
Janine Liu 2023-12-08 15:39:48 -05:00
parent 2e67c5925b
commit 587db88565
2 changed files with 85 additions and 13 deletions

View File

@ -11,6 +11,7 @@
#include <optional>
#include <string>
#include <string_view>
#include <system_error>
namespace CesiumGltf {
/**
@ -196,17 +197,19 @@ struct MetadataConversions<
return std::nullopt;
}
errno = 0;
char* pLastUsed;
int64_t parsedValue = std::strtoll(from.c_str(), &pLastUsed, 10);
if (pLastUsed == from.c_str() + from.size()) {
if (errno != ERANGE && pLastUsed == from.c_str() + from.size()) {
// Successfully parsed the entire string as an integer of this type.
return CesiumUtility::losslessNarrow<TTo, int64_t>(parsedValue);
}
errno = 0;
// Failed to parse as an integer. Maybe we can parse as a double and
// truncate it?
double parsedDouble = std::strtod(from.c_str(), &pLastUsed);
if (pLastUsed == from.c_str() + from.size()) {
if (errno != ERANGE && pLastUsed == from.c_str() + from.size()) {
// Successfully parsed the entire string as a double.
// Convert it to an integer if we can.
double truncated = glm::trunc(parsedDouble);
@ -218,6 +221,7 @@ struct MetadataConversions<
}
}
errno = 0;
return std::nullopt;
}
};
@ -248,17 +252,22 @@ struct MetadataConversions<
return std::nullopt;
}
errno = 0;
char* pLastUsed;
uint64_t parsedValue = std::strtoull(from.c_str(), &pLastUsed, 10);
if (pLastUsed == from.c_str() + from.size()) {
if (errno == ERANGE) {
return std::nullopt;
}
if (errno != ERANGE && pLastUsed == from.c_str() + from.size()) {
// Successfully parsed the entire string as an integer of this type.
return CesiumUtility::losslessNarrow<TTo, uint64_t>(parsedValue);
}
errno = 0;
// Failed to parse as an integer. Maybe we can parse as a double and
// truncate it?
double parsedDouble = std::strtod(from.c_str(), &pLastUsed);
if (pLastUsed == from.c_str() + from.size()) {
if (errno != ERANGE && pLastUsed == from.c_str() + from.size()) {
// Successfully parsed the entire string as a double.
// Convert it to an integer if we can.
double truncated = glm::trunc(parsedDouble);
@ -270,6 +279,7 @@ struct MetadataConversions<
}
}
errno = 0;
return std::nullopt;
}
};

View File

@ -17,7 +17,7 @@ TEST_CASE("Test MetadataConversions for boolean") {
REQUIRE(MetadataConversions<bool, int8_t>::convert(0) == false);
}
SECTION("converts from string") {
SECTION("converts from string view") {
std::string_view stringView("true");
REQUIRE(
MetadataConversions<bool, std::string_view>::convert(stringView) ==
@ -49,7 +49,16 @@ TEST_CASE("Test MetadataConversions for boolean") {
false);
}
SECTION("returns std::nullopt for incompatible strings") {
SECTION("converts from string") {
REQUIRE(MetadataConversions<bool, std::string>::convert("true") == true);
REQUIRE(MetadataConversions<bool, std::string>::convert("yes") == true);
REQUIRE(MetadataConversions<bool, std::string>::convert("1") == true);
REQUIRE(MetadataConversions<bool, std::string>::convert("false") == false);
REQUIRE(MetadataConversions<bool, std::string>::convert("no") == false);
REQUIRE(MetadataConversions<bool, std::string>::convert("0") == false);
}
SECTION("returns std::nullopt for incompatible string views") {
std::string_view stringView("11");
// invalid number
REQUIRE(!MetadataConversions<bool, std::string_view>::convert(stringView));
@ -59,6 +68,15 @@ TEST_CASE("Test MetadataConversions for boolean") {
REQUIRE(!MetadataConversions<bool, std::string_view>::convert(stringView));
}
SECTION("returns std::nullopt for incompatible strings") {
// invalid number
REQUIRE(!MetadataConversions<bool, std::string_view>::convert("11"));
// invalid word
REQUIRE(
!MetadataConversions<bool, std::string_view>::convert("this is true"));
}
SECTION("returns std::nullopt for incompatible types") {
// vecN
REQUIRE(!MetadataConversions<bool, glm::vec3>::convert(glm::vec3(1, 2, 3)));
@ -90,7 +108,7 @@ TEST_CASE("Test MetadataConversions for integer") {
REQUIRE(MetadataConversions<int32_t, bool>::convert(false) == 0);
}
SECTION("converts from string") {
SECTION("converts from string view") {
// integer string
std::string_view value("-123");
REQUIRE(
@ -101,6 +119,17 @@ TEST_CASE("Test MetadataConversions for integer") {
MetadataConversions<int32_t, std::string_view>::convert(value) == 123);
}
SECTION("converts from string") {
// integer string
REQUIRE(
MetadataConversions<int32_t, std::string_view>::convert("-123") ==
-123);
// double string
REQUIRE(
MetadataConversions<int32_t, std::string_view>::convert("123.456") ==
123);
}
SECTION("returns std::nullopt for out-of-range numbers") {
// out-of-range unsigned int
REQUIRE(!MetadataConversions<int32_t, uint32_t>::convert(
@ -115,21 +144,33 @@ TEST_CASE("Test MetadataConversions for integer") {
std::numeric_limits<double>::max()));
}
SECTION("returns std::nullopt for invalid strings") {
SECTION("returns std::nullopt for invalid string views") {
// out-of-range number
REQUIRE(!MetadataConversions<int8_t, std::string_view>::convert(
std::string_view("-255")));
REQUIRE(!MetadataConversions<uint64_t, std::string_view>::convert(
std::string_view("-1")));
// mixed number and non-number input
REQUIRE(!MetadataConversions<int8_t, std::string_view>::convert(
std::string_view("10 hello")));
// non-number input
REQUIRE(!MetadataConversions<int8_t, std::string_view>::convert(
REQUIRE(!MetadataConversions<uint8_t, std::string_view>::convert(
std::string_view("not a number")));
// empty input
REQUIRE(!MetadataConversions<int8_t, std::string_view>::convert(
std::string_view()));
}
SECTION("returns std::nullopt for invalid strings") {
// out-of-range number
REQUIRE(!MetadataConversions<uint64_t, std::string>::convert("-1"));
// mixed number and non-number input
REQUIRE(!MetadataConversions<int8_t, std::string>::convert("10 hello"));
// non-number input
REQUIRE(
!MetadataConversions<uint8_t, std::string>::convert("not a number"));
// empty input
REQUIRE(!MetadataConversions<int8_t, std::string>::convert(""));
}
SECTION("returns std::nullopt for incompatible types") {
// vecN
REQUIRE(!MetadataConversions<int32_t, glm::ivec3>::convert(
@ -168,7 +209,7 @@ TEST_CASE("Test MetadataConversions for float") {
REQUIRE(MetadataConversions<float, bool>::convert(false) == 0.0f);
}
SECTION("converts from string") {
SECTION("converts from string view") {
REQUIRE(
MetadataConversions<float, std::string_view>::convert(
std::string_view("123")) == static_cast<float>(123));
@ -177,7 +218,16 @@ TEST_CASE("Test MetadataConversions for float") {
std::string_view("123.456")) == static_cast<float>(123.456));
}
SECTION("returns std::nullopt for invalid strings") {
SECTION("converts from string") {
REQUIRE(
MetadataConversions<float, std::string>::convert("123") ==
static_cast<float>(123));
REQUIRE(
MetadataConversions<float, std::string>::convert("123.456") ==
static_cast<float>(123.456));
}
SECTION("returns std::nullopt for invalid string views") {
// out-of-range number
REQUIRE(!MetadataConversions<float, std::string_view>::convert(
std::string_view(std::to_string(std::numeric_limits<double>::max()))));
@ -192,6 +242,18 @@ TEST_CASE("Test MetadataConversions for float") {
std::string_view()));
}
SECTION("returns std::nullopt for invalid strings") {
// out-of-range number
REQUIRE(!MetadataConversions<float, std::string>::convert(
std::to_string(std::numeric_limits<double>::max())));
// mixed number and non-number input
REQUIRE(!MetadataConversions<float, std::string>::convert("10.00f hello"));
// non-number input
REQUIRE(!MetadataConversions<float, std::string>::convert("not a number"));
// empty input
REQUIRE(!MetadataConversions<float, std::string>::convert(""));
}
SECTION("returns std::nullopt for incompatible types") {
// vecN
REQUIRE(