From 6199705b9ba40c1dba8b7dccf9a418902add09ef Mon Sep 17 00:00:00 2001 From: olive_tree_branch Date: Sat, 15 Aug 2026 13:16:18 +1000 Subject: [PATCH 1/6] Refactored string checking functions Changed the functions that check if the input string is a number, integer or double to use find_first_not_of to clean the code up and make it easier to maintain and read. Functionality has been move to a seperate function in order to avoid repetition and have a better name Added include --- coresdk/src/coresdk/basics.cpp | 50 ++++++++++++++++------------------ 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/coresdk/src/coresdk/basics.cpp b/coresdk/src/coresdk/basics.cpp index 7fe72bf5..45900968 100644 --- a/coresdk/src/coresdk/basics.cpp +++ b/coresdk/src/coresdk/basics.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -112,18 +113,20 @@ namespace splashkit_lib return result; } - // integer check see: https://stackoverflow.com/questions/2844817/how-do-i-check-if-a-c-string-is-an-int#2845275 + // integer check see: https://stackoverflow.com/a/37864920 + bool is_valid_input(std::string_view input, std::string_view valid_characters) + { + return input.find_first_not_of(valid_characters) == std::string_view::npos; + } bool is_integer(const string &text) { - string s = trim(text); - if (s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) + if (text.empty()) + { return false; + } - char *p; - strtol(s.c_str(), &p, 10); - - return (*p == 0); + return is_valid_input(text ,"0123456789+-"); } bool is_double(const string &text) @@ -133,14 +136,12 @@ namespace splashkit_lib bool is_number(const string &text) { - string s = trim(text); - if (s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) + if (text.empty() || text.starts_with('.')) + { return false; + } - char *p; - strtod(s.c_str(), &p); - - return (*p == 0); + return is_valid_input(text, "0123456789+-."); } int convert_to_integer(const string &text) @@ -155,32 +156,29 @@ namespace splashkit_lib bool is_binary(const string &bin_str) { - for (char c : bin_str) + if (bin_str.empty()) { - if (c != '0' && c != '1') - return false; + return false; } - return !bin_str.empty(); + return is_valid_input(bin_str, "01"); } bool is_hex(const string &hex_str) { - for (char c : hex_str) + if (hex_str.empty()) { - if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) - return false; + return false; } - return !hex_str.empty(); + return is_valid_input(hex_str, "0123456789aAbBcCdDeEfF"); } bool is_octal(const string &octal_str) { - for (char c : octal_str) + if (octal_str.empty()) { - if (c < '0' || c > '7') - return false; + return false; } - return !octal_str.empty(); + return is_valid_input(octal_str, "01234567"); } string dec_to_bin(unsigned int a_dec) @@ -496,4 +494,4 @@ namespace splashkit_lib return abs(number1 * number2) / greatest_common_divisor(number1, number2); } -} \ No newline at end of file +} From 921d308edee40c6e1a25d802f9e8835cb6c8018c Mon Sep 17 00:00:00 2001 From: olive_tree_branch Date: Sat, 15 Aug 2026 13:16:18 +1000 Subject: [PATCH 2/6] Refactored conversion from decimal to string functions Changed the functions that convert from an unsigned int to a binary or octal string to use the `std::format` function to be easier to read and maintain Added include --- coresdk/src/coresdk/basics.cpp | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/coresdk/src/coresdk/basics.cpp b/coresdk/src/coresdk/basics.cpp index 45900968..c82b0a79 100644 --- a/coresdk/src/coresdk/basics.cpp +++ b/coresdk/src/coresdk/basics.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -183,17 +184,7 @@ namespace splashkit_lib string dec_to_bin(unsigned int a_dec) { - // Without this check, dec to bin will not work if dec is 0 - if (a_dec == 0) - return "0"; - - string bin_string; - while (a_dec > 0) - { - bin_string = ((a_dec & 1) ? "1" : "0") + bin_string; - a_dec >>= 1; - } - return bin_string; + return std::format("{:b}", a_dec); } unsigned int bin_to_dec(const string &bin_str) @@ -276,18 +267,7 @@ namespace splashkit_lib string dec_to_oct(unsigned int decimal_value) { - if (decimal_value == 0) - { - return "0"; - } - - string octal_string; - while (decimal_value > 0) - { - octal_string = std::to_string(decimal_value % 8) + octal_string; - decimal_value /= 8; - } - return octal_string; + return std::format("{:o}", decimal_value); } unsigned int oct_to_dec(const string &octal_string) From 3c775c51775408d7fe28ba843ec4b6a4d2b9b66e Mon Sep 17 00:00:00 2001 From: olive_tree_branch Date: Sat, 15 Aug 2026 13:16:18 +1000 Subject: [PATCH 3/6] Refactored split function Changed the `split` function to use the standard ranges library `split` function to return a vector of strings. Currently there is a problem with this as the unit test requires the output to be a vector with one empty string if no split happened. The refactor does change the functionality by making it return an empty vector of strings Added include --- coresdk/src/coresdk/basics.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/coresdk/src/coresdk/basics.cpp b/coresdk/src/coresdk/basics.cpp index c82b0a79..7b87a9e1 100644 --- a/coresdk/src/coresdk/basics.cpp +++ b/coresdk/src/coresdk/basics.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -101,17 +102,23 @@ namespace splashkit_lib vector split(const string &text, char delimiter) { - vector result; - string::size_type start = 0; - string::size_type end = text.find(delimiter); - while (end != string::npos) + // To check to keep the same functionality required on line 523 of unit_test_utilities.cpp + // Will return an empty vector without this check + if (text.empty()) { - result.push_back(text.substr(start, end - start)); - start = end + 1; - end = text.find(delimiter, start); + return vector{ text }; } - result.push_back(text.substr(start)); + + vector result{}; + for(const auto& strings : std::views::split(text, delimiter)) + { + result.emplace_back(strings.begin(), strings.end()); + } + return result; + // To future maintainers: if the codebase gets upgraded to C++23 or newer, + // remove the above code and uncomment the code bellow + // return text | std::views::split(delimiter) | std::ranges::to>(); } // integer check see: https://stackoverflow.com/a/37864920 From ad761a60ba0a2a9d72d1205cb068a5c83b9088ef Mon Sep 17 00:00:00 2001 From: olive_tree_branch Date: Sat, 15 Aug 2026 13:16:18 +1000 Subject: [PATCH 4/6] Refactored conversions between different_bases Changed the following functions: - `oct_to_bin` - `bin_to_oct` - `hex_to_oct` - `oct_to_hex` to use functions that are already working to do the conversion --- coresdk/src/coresdk/basics.cpp | 49 +++++++--------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/coresdk/src/coresdk/basics.cpp b/coresdk/src/coresdk/basics.cpp index 7b87a9e1..9214bda2 100644 --- a/coresdk/src/coresdk/basics.cpp +++ b/coresdk/src/coresdk/basics.cpp @@ -81,7 +81,10 @@ namespace splashkit_lib { size_t pos = text.find(subtext); if (pos == string::npos) + { return -1; + } + return static_cast(pos); } @@ -168,6 +171,7 @@ namespace splashkit_lib { return false; } + return is_valid_input(bin_str, "01"); } @@ -177,6 +181,7 @@ namespace splashkit_lib { return false; } + return is_valid_input(hex_str, "0123456789aAbBcCdDeEfF"); } @@ -186,6 +191,7 @@ namespace splashkit_lib { return false; } + return is_valid_input(octal_str, "01234567"); } @@ -307,20 +313,7 @@ namespace splashkit_lib return ""; } - string bin_string; - for (char oct_char : octal_str) - { - int oct_val = oct_char - '0'; - - // Convert each octal digit to a 3-bit binary representation - for (int i = 2; i >= 0; i--) - { - bin_string += ((oct_val >> i) & 1) ? '1' : '0'; - } - } - - size_t first_one = bin_string.find_first_not_of('0'); - return (first_one == string::npos) ? "0" : bin_string.substr(first_one); + return dec_to_bin(oct_to_dec(octal_str)); } string bin_to_oct(const string &bin_str) @@ -331,27 +324,7 @@ namespace splashkit_lib return ""; } - string octal_string; - - // Pad binary string with leading zeros to make its length a multiple of 3 - int padding = (3 - (bin_str.length() % 3)) % 3; - string padded_bin_str = string(padding, '0') + bin_str; - - for (size_t i = 0; i < padded_bin_str.length(); i += 3) - { - int oct_val = 0; - for (size_t j = 0; j < 3; j++) - { - oct_val <<= 1; - if (padded_bin_str[i + j] == '1') - oct_val |= 1; - } - - octal_string += '0' + oct_val; - } - - size_t first_non_zero = octal_string.find_first_not_of('0'); - return (first_non_zero == string::npos) ? "0" : octal_string.substr(first_non_zero); + return dec_to_oct(bin_to_dec(bin_str)); } string hex_to_oct(const string &hex_str) @@ -362,8 +335,7 @@ namespace splashkit_lib return ""; } - string bin_str = hex_to_bin(hex_str); - return bin_to_oct(bin_str); + return bin_to_oct(hex_to_bin(hex_str)); } string oct_to_hex(const string &octal_str) @@ -374,8 +346,7 @@ namespace splashkit_lib return ""; } - string bin_str = oct_to_bin(octal_str); - return bin_to_hex(bin_str); + return bin_to_hex(oct_to_bin(octal_str)); } string base64_encode(const string &input) From af449d8ed8ebcbf8c426324111f5169d0660cf20 Mon Sep 17 00:00:00 2001 From: olive_tree_branch Date: Wed, 19 Aug 2026 12:18:44 +1000 Subject: [PATCH 5/6] Added edge cases to the unit tests --- .../test/unit_tests/unit_test_utilities.cpp | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/coresdk/src/test/unit_tests/unit_test_utilities.cpp b/coresdk/src/test/unit_tests/unit_test_utilities.cpp index d2b871af..2e3ba08d 100644 --- a/coresdk/src/test/unit_tests/unit_test_utilities.cpp +++ b/coresdk/src/test/unit_tests/unit_test_utilities.cpp @@ -334,6 +334,14 @@ TEST_CASE("verify that string is double", "[is_double]") } TEST_CASE("verify that string is integer", "[is_integer]") { + SECTION("Edge Cases") + { + REQUIRE_FALSE(is_integer("1+2")); + REQUIRE_FALSE(is_integer("+-2")); + REQUIRE_FALSE(is_integer("--5")); + REQUIRE_FALSE(is_integer("5--")); + REQUIRE_FALSE(is_integer("5-")); + } SECTION("string is an integer") { REQUIRE(is_integer("123")); @@ -393,6 +401,12 @@ TEST_CASE("verify that string is integer", "[is_integer]") } TEST_CASE("verify that string is number", "[is_number]") { + SECTION("Edge Cases") + { + REQUIRE_FALSE(is_number("1.2.3")); + REQUIRE_FALSE(is_number("1.+2")); + REQUIRE_FALSE(is_number("1.-2")); + } SECTION("string is a double") { REQUIRE(is_number("123.456")); @@ -717,7 +731,7 @@ TEST_CASE("gets the number of milliseconds that have passed since the program wa TEST_CASE("program is put to sleep for a specified number of milliseconds", "[delay]") { constexpr long long DELAY_THRESHOLD = 80; - + SECTION("milliseconds is 0") { auto start = std::chrono::steady_clock::now(); @@ -747,7 +761,7 @@ TEST_CASE("return a SplashKit resource of resource_kind with name filename as a { const resource_kind RESOURCE = resource_kind::BUNDLE_RESOURCE; const string RESOURCE_PATH = "blah.txt"; - + SECTION("filename is a valid file") { string result = file_as_string(RESOURCE_PATH, RESOURCE); @@ -1017,4 +1031,4 @@ TEST_CASE("calculates the lcm of two numbers", "[least_common_multiple]") { REQUIRE(least_common_multiple(42, 42) == 42); } -} \ No newline at end of file +} From e96a1f8ec0ae8e49f35f1688f658f3fd7838e10d Mon Sep 17 00:00:00 2001 From: olive_tree_branch Date: Wed, 19 Aug 2026 15:58:14 +1000 Subject: [PATCH 6/6] Reverted changes to `is_integer` and `is_number` Changed the code for the following functions back due to expected validation results not being exactly the same. For future reference, `std::from_chars` was looked at but the behavior of the function is slightly different and produces a different result. Potentially something to look at in the future --- coresdk/src/coresdk/basics.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/coresdk/src/coresdk/basics.cpp b/coresdk/src/coresdk/basics.cpp index 9214bda2..479b224c 100644 --- a/coresdk/src/coresdk/basics.cpp +++ b/coresdk/src/coresdk/basics.cpp @@ -124,20 +124,17 @@ namespace splashkit_lib // return text | std::views::split(delimiter) | std::ranges::to>(); } - // integer check see: https://stackoverflow.com/a/37864920 - bool is_valid_input(std::string_view input, std::string_view valid_characters) - { - return input.find_first_not_of(valid_characters) == std::string_view::npos; - } - + // integer check see: https://stackoverflow.com/questions/2844817/how-do-i-check-if-a-c-string-is-an-int#2845275 bool is_integer(const string &text) { - if (text.empty()) - { + string s = trim(text); + if (s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false; - } - return is_valid_input(text ,"0123456789+-"); + char *p; + strtol(s.c_str(), &p, 10); + + return (*p == 0); } bool is_double(const string &text) @@ -147,12 +144,14 @@ namespace splashkit_lib bool is_number(const string &text) { - if (text.empty() || text.starts_with('.')) - { + string s = trim(text); + if (s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false; - } - return is_valid_input(text, "0123456789+-."); + char *p; + strtod(s.c_str(), &p); + + return (*p == 0); } int convert_to_integer(const string &text) @@ -165,6 +164,11 @@ namespace splashkit_lib return std::stod(text); } + bool is_valid_input(std::string_view input, std::string_view valid_characters) + { + return input.find_first_not_of(valid_characters) == std::string_view::npos; + } + bool is_binary(const string &bin_str) { if (bin_str.empty())