From baa758d42040dfceb045c9f3e901e1a532739bb4 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 1 Sep 2026 19:06:55 +0200 Subject: [PATCH] fix cctype usage According to the documentation, we should cast to unsigned char. Anything negative is undefined behavior. --- Utilities/Config.cpp | 21 ++ Utilities/Config.h | 20 +- Utilities/JITLLVM.cpp | 4 +- Utilities/LUrlParser.cpp | 3 +- Utilities/StrFmt.cpp | 11 +- Utilities/Thread.cpp | 5 +- Utilities/bin_patch.cpp | 3 +- rpcs3/CMakeLists.txt | 12 +- rpcs3/Crypto/unedat.cpp | 6 +- rpcs3/Emu/Cell/Modules/cellGame.cpp | 13 +- rpcs3/Emu/Cell/Modules/cellKb.cpp | 4 +- rpcs3/Emu/Cell/Modules/cellRtc.cpp | 78 +++--- rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp | 3 +- rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp | 3 +- rpcs3/Emu/Cell/PPUThread.cpp | 4 +- rpcs3/Emu/Cell/lv2/lv2.cpp | 3 +- rpcs3/Emu/Cell/lv2/sys_tty.cpp | 3 +- rpcs3/Emu/NP/np_helpers.cpp | 5 +- rpcs3/Emu/RSX/GL/glutils/capabilities.cpp | 3 +- rpcs3/Emu/RSX/GL/glutils/program.cpp | 5 +- rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp | 5 +- rpcs3/Emu/RSX/VK/VKCommonDecompiler.cpp | 3 +- rpcs3/Emu/System.cpp | 5 +- rpcs3/Emu/VFS.cpp | 3 +- rpcs3/emucore.vcxproj | 1 + rpcs3/emucore.vcxproj.filters | 3 + rpcs3/rpcs3qt/cheat_manager.cpp | 5 +- rpcs3/rpcs3qt/emu_settings.cpp | 5 +- rpcs3/rpcs3qt/input_dialog.cpp | 3 +- rpcs3/rpcs3qt/memory_string_searcher.cpp | 9 +- rpcs3/rpcs3qt/memory_viewer_panel.cpp | 3 +- rpcs3/rpcs3qt/register_editor_dialog.cpp | 5 +- rpcs3/rpcs3qt/rpcn_settings_dialog.cpp | 7 +- rpcs3/tests/rpcs3_test.vcxproj | 1 + rpcs3/tests/test_cctype.cpp | 314 ++++++++++++++++++++++ rpcs3/util/cctype.hpp | 54 ++++ 36 files changed, 522 insertions(+), 113 deletions(-) create mode 100644 rpcs3/tests/test_cctype.cpp create mode 100644 rpcs3/util/cctype.hpp diff --git a/Utilities/Config.cpp b/Utilities/Config.cpp index 6b2e3bf8410b..7c16791f40cf 100644 --- a/Utilities/Config.cpp +++ b/Utilities/Config.cpp @@ -2,6 +2,7 @@ #include "Config.h" #include "util/types.hpp" #include "util/yaml.hpp" +#include "util/cctype.hpp" #include @@ -712,6 +713,26 @@ bool cfg::node::validate(std::string_view value) return false; } +bool cfg::_bool::from_string(std::string_view value, bool /*dynamic*/) +{ + if (value.size() != 4 && value.size() != 5) + { + return false; + } + + char copy[5]; + std::transform(value.begin(), value.end(), std::begin(copy), utils::tolower); + + if (value.size() == 5 && std::string_view{copy, 5} == "false") + m_value = false; + else if (value.size() == 4 && std::string_view{copy, 4} == "true") + m_value = true; + else + return false; + + return true; +} + std::string cfg::map_entry::get_value(std::string_view key) { if (auto it = m_map.find(key); it != m_map.end()) diff --git a/Utilities/Config.h b/Utilities/Config.h index 8f454e3ade8c..e56df11d51c6 100644 --- a/Utilities/Config.h +++ b/Utilities/Config.h @@ -212,25 +212,7 @@ namespace cfg return def ? "true" : "false"; } - bool from_string(std::string_view value, bool /*dynamic*/ = false) override - { - if (value.size() != 4 && value.size() != 5) - { - return false; - } - - char copy[5]; - std::transform(value.begin(), value.end(), std::begin(copy), ::tolower); - - if (value.size() == 5 && std::string_view{copy, 5} == "false") - m_value = false; - else if (value.size() == 4 && std::string_view{copy, 4} == "true") - m_value = true; - else - return false; - - return true; - } + bool from_string(std::string_view value, bool dynamic = false) override; void set(const bool& value) { diff --git a/Utilities/JITLLVM.cpp b/Utilities/JITLLVM.cpp index ff3bc340fa56..46a6c3ff6419 100644 --- a/Utilities/JITLLVM.cpp +++ b/Utilities/JITLLVM.cpp @@ -14,6 +14,8 @@ #if defined(__APPLE__) #include +#elif defined(ANDROID) +#include "util/cctype.hpp" #endif LOG_CHANNEL(jit_log, "JIT"); @@ -1039,7 +1041,7 @@ const char * fallback_cpu_detection() return "cortex-a78"; } - std::transform(result.begin(), result.end(), result.begin(), ::tolower); + std::transform(result.begin(), result.end(), result.begin(), utils::tolower); return result; }(); diff --git a/Utilities/LUrlParser.cpp b/Utilities/LUrlParser.cpp index a6be4b5a0f0a..ef8713fbfb65 100644 --- a/Utilities/LUrlParser.cpp +++ b/Utilities/LUrlParser.cpp @@ -26,6 +26,7 @@ */ #include "LUrlParser.h" +#include "util/cctype.hpp" #include #include @@ -36,7 +37,7 @@ static bool IsSchemeValid( const std::string& SchemeName ) { return std::all_of(SchemeName.cbegin(), SchemeName.cend(), [](const auto& c) { - return isalpha(c) || c == '+' || c == '-' || c == '.'; + return utils::isalpha(c) || c == '+' || c == '-' || c == '.'; }); } diff --git a/Utilities/StrFmt.cpp b/Utilities/StrFmt.cpp index 39a2cb9233e4..d2367e825719 100644 --- a/Utilities/StrFmt.cpp +++ b/Utilities/StrFmt.cpp @@ -3,6 +3,7 @@ #include "cfmt.h" #include "util/endian.hpp" #include "util/v128.hpp" +#include "util/cctype.hpp" #include #include @@ -198,12 +199,12 @@ fmt::base57_result fmt::base57_result::from_string(std::string_view str) { auto to_val = [](u8 c) -> u64 { - if (std::isdigit(c)) + if (utils::isdigit(c)) { return c - '0'; } - if (std::isupper(c)) + if (utils::isupper(c)) { // Omitted characters if (c == 'B' || c == 'D' || c == 'I' || c == 'O') @@ -231,7 +232,7 @@ fmt::base57_result fmt::base57_result::from_string(std::string_view str) return c - 'A' + 10; } - if (std::islower(c)) + if (utils::islower(c)) { // Omitted characters if (c == 'l') @@ -952,7 +953,7 @@ std::string fmt::to_upper(std::string_view string) { std::string result; result.resize(string.size()); - std::transform(string.begin(), string.end(), result.begin(), ::toupper); + std::transform(string.begin(), string.end(), result.begin(), utils::toupper); return result; } @@ -960,7 +961,7 @@ std::string fmt::to_lower(std::string_view string) { std::string result; result.resize(string.size()); - std::transform(string.begin(), string.end(), result.begin(), ::tolower); + std::transform(string.begin(), string.end(), result.begin(), utils::tolower); return result; } diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 05e7577e5f9f..cc032b216a05 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -96,6 +96,7 @@ DYNAMIC_IMPORT_RENAME("Kernel32.dll", SetThreadDescriptionImport, "SetThreadDesc #include "util/asm.hpp" #include "util/v128.hpp" #include "util/simd.hpp" +#include "util/cctype.hpp" #include "util/sysinfo.hpp" #include "Emu/Memory/vm_locking.h" @@ -185,9 +186,9 @@ bool IsDebuggerPresent() for (const char* cp = status.data() + found + 10; cp <= status.data() + num_read; ++cp) { - if (!std::isspace(*cp)) + if (!utils::isspace(*cp)) { - return std::isdigit(*cp) != 0 && *cp != '0'; + return utils::isdigit(*cp) != 0 && *cp != '0'; } } diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index fd94b830e02f..4c3ab91e10ee 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -10,6 +10,7 @@ #include "util/types.hpp" #include "util/asm.hpp" +#include "util/cctype.hpp" #include #include @@ -329,7 +330,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st is_valid = false; continue; } - else if (serial.size() != 9 || !std::all_of(serial.begin(), serial.end(), [](char c) { return std::isalnum(static_cast(c)); })) + else if (serial.size() != 9 || !std::all_of(serial.begin(), serial.end(), [](char c) { return utils::isalnum(c); })) { append_log_message(log_messages, fmt::format("Error: Serial '%s' invalid (patch: %s, key: %s, location: %s, file: %s)", serial, description, main_key, get_yaml_node_location(serial_node), path), &patch_log.error); is_valid = false; diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 373017197a28..5f8336fb04fe 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -182,17 +182,19 @@ if(BUILD_RPCS3_TESTS) target_sources(rpcs3_test PRIVATE tests/test.cpp + tests/test_address_range.cpp tests/test_bit_set.cpp + tests/test_cctype.cpp + tests/test_dmux_pamf.cpp tests/test_fmt.cpp tests/test_pair.cpp - tests/test_tuple.cpp - tests/test_simple_array.cpp - tests/test_address_range.cpp - tests/test_sys_fs.cpp tests/test_rsx_cfg.cpp tests/test_rsx_fp_asm.cpp - tests/test_dmux_pamf.cpp + tests/test_rsx_mm_queue.cpp + tests/test_simple_array.cpp tests/test_spu_analyser.cpp + tests/test_sys_fs.cpp + tests/test_tuple.cpp tests/test_types_util.cpp ) diff --git a/rpcs3/Crypto/unedat.cpp b/rpcs3/Crypto/unedat.cpp index cc577392cb0c..95ce43746223 100644 --- a/rpcs3/Crypto/unedat.cpp +++ b/rpcs3/Crypto/unedat.cpp @@ -9,6 +9,8 @@ #include "Emu/system_utils.hpp" #include "util/asm.hpp" +#include "util/cctype.hpp" + #include #include @@ -628,8 +630,8 @@ bool validate_npd_hashes(std::string_view file_name, const u8* klicensee, const for (usz i = std::distance(it, buf_span.rend()) - 1; i < buf_len; ++i) { const u8 c = buf[i]; - buf_upper[i] = std::toupper(c); - buf_lower[i] = std::tolower(c); + buf_upper[i] = utils::toupper(c); + buf_lower[i] = utils::tolower(c); } // Hash with NPDRM_OMAC_KEY_3 and compare with title_hash. diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index d9336fe16c75..87dc8b341b4a 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -18,6 +18,7 @@ #include "Utilities/StrUtil.h" #include "util/init_mutex.hpp" #include "util/asm.hpp" +#include "util/cctype.hpp" #include "Crypto/utils.h" #include @@ -228,13 +229,13 @@ static bool check_system_ver(vm::cptr systemVersion) return ( systemVersion && std::strlen(systemVersion.get_ptr()) == 7 && - std::isdigit(systemVersion[0]) && - std::isdigit(systemVersion[1]) && + utils::isdigit(systemVersion[0]) && + utils::isdigit(systemVersion[1]) && systemVersion[2] == '.' && - std::isdigit(systemVersion[3]) && - std::isdigit(systemVersion[4]) && - std::isdigit(systemVersion[5]) && - std::isdigit(systemVersion[6]) + utils::isdigit(systemVersion[3]) && + utils::isdigit(systemVersion[4]) && + utils::isdigit(systemVersion[5]) && + utils::isdigit(systemVersion[6]) ); } diff --git a/rpcs3/Emu/Cell/Modules/cellKb.cpp b/rpcs3/Emu/Cell/Modules/cellKb.cpp index adfa849bfbba..e468a99b1252 100644 --- a/rpcs3/Emu/Cell/Modules/cellKb.cpp +++ b/rpcs3/Emu/Cell/Modules/cellKb.cpp @@ -2,8 +2,8 @@ #include "Emu/IdManager.h" #include "Emu/System.h" #include "Emu/Cell/PPUModule.h" - #include "Emu/Io/KeyboardHandler.h" +#include "util/cctype.hpp" #include "cellKb.h" error_code sys_config_start(ppu_thread& ppu); @@ -198,7 +198,7 @@ u16 cellKbCnvRawCode(u32 arrange, u32 mkey, u32 led, u16 rawcode) const auto get_ascii = [&](u16 raw, u16 shifted = 0, u16 altered = 0) { // Usually caps lock only applies uppercase to letters, but some layouts treat it as shift lock for all keys. - if ((is_shift || (is_caps_lock && (is_shift_lock || std::isalpha(raw)))) && shifted) + if ((is_shift || (is_caps_lock && (is_shift_lock || utils::isalpha(raw)))) && shifted) { return shifted; } diff --git a/rpcs3/Emu/Cell/Modules/cellRtc.cpp b/rpcs3/Emu/Cell/Modules/cellRtc.cpp index fb92dd6f2c40..740edb3330a5 100644 --- a/rpcs3/Emu/Cell/Modules/cellRtc.cpp +++ b/rpcs3/Emu/Cell/Modules/cellRtc.cpp @@ -6,6 +6,7 @@ #include "Emu/Cell/lv2/sys_time.h" #include "Emu/Cell/lv2/sys_memory.h" #include "Emu/Cell/lv2/sys_ss.h" +#include "util/cctype.hpp" LOG_CHANNEL(cellRtc); @@ -197,7 +198,7 @@ error_code cellRtcFormatRfc2822(ppu_thread& ppu, vm::ptr pszDateTime, vm:: s32 weekdayIdx = cellRtcGetDayOfWeek(date_time->year, date_time->month, date_time->day); // Day name - pszDateTime[0] = std::toupper(WEEKDAY_NAMES[weekdayIdx][0]); + pszDateTime[0] = utils::toupper(WEEKDAY_NAMES[weekdayIdx][0]); pszDateTime[1] = WEEKDAY_NAMES[weekdayIdx][1]; pszDateTime[2] = WEEKDAY_NAMES[weekdayIdx][2]; pszDateTime[3] = ','; @@ -208,7 +209,7 @@ error_code cellRtcFormatRfc2822(ppu_thread& ppu, vm::ptr pszDateTime, vm:: pszDateTime[7] = ' '; // month name - pszDateTime[8] = std::toupper(MONTH_NAMES[date_time->month - 1][0]); + pszDateTime[8] = utils::toupper(MONTH_NAMES[date_time->month - 1][0]); pszDateTime[9] = MONTH_NAMES[date_time->month - 1][1]; pszDateTime[10] = MONTH_NAMES[date_time->month - 1][2]; pszDateTime[0xb] = ' '; @@ -433,7 +434,7 @@ u16 rtcParseComponent(vm::cptr pszDateTime, u32& pos, char delimiter, cons pos++; } - if (!std::isdigit(pszDateTime[pos])) + if (!utils::isdigit(pszDateTime[pos])) { cellRtc.error("rtcParseComponent(): failed to parse %s: ASCII value 0x%x at position %d is not a digit", component_name, pszDateTime[pos + 1], pos); return umax; @@ -443,7 +444,7 @@ u16 rtcParseComponent(vm::cptr pszDateTime, u32& pos, char delimiter, cons pos++; - if (std::isdigit(pszDateTime[pos])) + if (utils::isdigit(pszDateTime[pos])) { ret = ret * 10 + digit(pszDateTime[pos]); @@ -462,7 +463,7 @@ u8 rtcParseName(vm::cptr pszDateTime, u32& pos, const std::array pUtc, vm::cptr< // Year: "XX" or "XXXX" u16 year = 0; - if (!std::isdigit(pszDateTime[pos]) || - !std::isdigit(pszDateTime[pos + 1])) + if (!utils::isdigit(pszDateTime[pos]) || + !utils::isdigit(pszDateTime[pos + 1])) { return { CELL_RTC_ERROR_BAD_PARSE, "rtcParseRfc2822(): failed to parse year: one of the first two ASCII values 0x%x, 0x%x at position %d is not a digit", pszDateTime[pos], pszDateTime[pos + 1], pos }; } - if (!std::isdigit(pszDateTime[pos + 2]) || - !std::isdigit(pszDateTime[pos + 3])) + if (!utils::isdigit(pszDateTime[pos + 2]) || + !utils::isdigit(pszDateTime[pos + 3])) { year = digit(pszDateTime[pos]) * 10 + digit(pszDateTime[pos + 1]); year += (year < 50) ? 2000 : 1900; @@ -591,10 +592,10 @@ error_code rtcParseRfc2822(ppu_thread& ppu, vm::ptr pUtc, vm::cptr< { // "±hhmm" - if (std::isdigit(pszDateTime[pos + 1]) && - std::isdigit(pszDateTime[pos + 2]) && - std::isdigit(pszDateTime[pos + 3]) && - std::isdigit(pszDateTime[pos + 4])) + if (utils::isdigit(pszDateTime[pos + 1]) && + utils::isdigit(pszDateTime[pos + 2]) && + utils::isdigit(pszDateTime[pos + 3]) && + utils::isdigit(pszDateTime[pos + 4])) { const s32 time_zone_hhmm = digit(pszDateTime[pos + 1]) * 1000 + digit(pszDateTime[pos + 2]) * 100 + digit(pszDateTime[pos + 3]) * 10 + digit(pszDateTime[pos + 4]); @@ -627,7 +628,7 @@ error_code rtcParseRfc2822(ppu_thread& ppu, vm::ptr pUtc, vm::cptr< // "A", "B", "C", ..., not case sensitive // These are all off by one ("A" should be UTC+01:00, "B" should be UTC+02:00, etc.) - const char letter = std::toupper(pszDateTime[pos]); + const char letter = utils::toupper(pszDateTime[pos]); if (letter >= 'A' && letter <= 'M' && letter != 'J') { @@ -671,15 +672,15 @@ error_code cellRtcParseDateTime(ppu_thread& ppu, vm::ptr pUtc, vm:: u32 pos = 0; - while (std::isblank(pszDateTime[pos])) + while (utils::isblank(pszDateTime[pos])) { pos++; } - if (std::isdigit(pszDateTime[pos]) && - std::isdigit(pszDateTime[pos + 1]) && - std::isdigit(pszDateTime[pos + 2]) && - std::isdigit(pszDateTime[pos + 3])) + if (utils::isdigit(pszDateTime[pos]) && + utils::isdigit(pszDateTime[pos + 1]) && + utils::isdigit(pszDateTime[pos + 2]) && + utils::isdigit(pszDateTime[pos + 3])) { return cellRtcParseRfc3339(ppu, pUtc, pszDateTime + pos); } @@ -697,7 +698,7 @@ error_code cellRtcParseDateTime(ppu_thread& ppu, vm::ptr pUtc, vm:: } // Skip spaces and tabs - while (std::isblank(pszDateTime[pos])) + while (utils::isblank(pszDateTime[pos])) { pos++; } @@ -742,13 +743,13 @@ error_code cellRtcParseDateTime(ppu_thread& ppu, vm::ptr pUtc, vm:: pos++; } - else if (std::isdigit(pszDateTime[pos])) + else if (utils::isdigit(pszDateTime[pos])) { day = digit(pszDateTime[pos]); pos++; - if (std::isdigit(pszDateTime[pos])) + if (utils::isdigit(pszDateTime[pos])) { day = day * 10 + digit(pszDateTime[pos]); @@ -793,10 +794,10 @@ error_code cellRtcParseDateTime(ppu_thread& ppu, vm::ptr pUtc, vm:: pos++; // Year: XXXX - if (!std::isdigit(pszDateTime[pos]) || - !std::isdigit(pszDateTime[pos + 1]) || - !std::isdigit(pszDateTime[pos + 2]) || - !std::isdigit(pszDateTime[pos + 3])) + if (!utils::isdigit(pszDateTime[pos]) || + !utils::isdigit(pszDateTime[pos + 1]) || + !utils::isdigit(pszDateTime[pos + 2]) || + !utils::isdigit(pszDateTime[pos + 3])) { return { CELL_RTC_ERROR_BAD_PARSE, "cellRtcParseDateTime(): failed to parse year: one of the ASCII values 0x%x, 0x%x, 0x%x, or 0x%x is not a digit", pszDateTime[pos], pszDateTime[pos + 1], pszDateTime[pos + 2], pszDateTime[pos + 3] }; @@ -831,7 +832,10 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr pUtc, vm::c vm::var date_time; // Year: XXXX-12-03T13:23:00.00Z - if (std::isdigit(pszDateTime[0]) && std::isdigit(pszDateTime[1]) && std::isdigit(pszDateTime[2]) && std::isdigit(pszDateTime[3])) + if (utils::isdigit(pszDateTime[0]) && + utils::isdigit(pszDateTime[1]) && + utils::isdigit(pszDateTime[2]) && + utils::isdigit(pszDateTime[3])) { date_time->year = digit(pszDateTime[0]) * 1000 + digit(pszDateTime[1]) * 100 + digit(pszDateTime[2]) * 10 + digit(pszDateTime[3]); } @@ -846,7 +850,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr pUtc, vm::c } // Month: 1995-XX-03T13:23:00.00Z - if (std::isdigit(pszDateTime[5]) && std::isdigit(pszDateTime[6])) + if (utils::isdigit(pszDateTime[5]) && utils::isdigit(pszDateTime[6])) { date_time->month = digit(pszDateTime[5]) * 10 + digit(pszDateTime[6]); } @@ -861,7 +865,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr pUtc, vm::c } // Day: 1995-12-XXT13:23:00.00Z - if (std::isdigit(pszDateTime[8]) && std::isdigit(pszDateTime[9])) + if (utils::isdigit(pszDateTime[8]) && utils::isdigit(pszDateTime[9])) { date_time->day = digit(pszDateTime[8]) * 10 + digit(pszDateTime[9]); } @@ -876,7 +880,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr pUtc, vm::c } // Hour: 1995-12-03TXX:23:00.00Z - if (std::isdigit(pszDateTime[11]) && std::isdigit(pszDateTime[12])) + if (utils::isdigit(pszDateTime[11]) && utils::isdigit(pszDateTime[12])) { date_time->hour = digit(pszDateTime[11]) * 10 + digit(pszDateTime[12]); } @@ -891,7 +895,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr pUtc, vm::c } // Minute: 1995-12-03T13:XX:00.00Z - if (std::isdigit(pszDateTime[14]) && std::isdigit(pszDateTime[15])) + if (utils::isdigit(pszDateTime[14]) && utils::isdigit(pszDateTime[15])) { date_time->minute = digit(pszDateTime[14]) * 10 + digit(pszDateTime[15]); } @@ -906,7 +910,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr pUtc, vm::c } // Second: 1995-12-03T13:23:XX.00Z - if (std::isdigit(pszDateTime[17]) && std::isdigit(pszDateTime[18])) + if (utils::isdigit(pszDateTime[17]) && utils::isdigit(pszDateTime[18])) { date_time->second = digit(pszDateTime[17]) * 10 + digit(pszDateTime[18]); } @@ -923,7 +927,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr pUtc, vm::c { u32 mul = 100000; - for (char c = pszDateTime[++pos]; std::isdigit(c); c = pszDateTime[++pos]) + for (char c = pszDateTime[++pos]; utils::isdigit(c); c = pszDateTime[++pos]) { date_time->microsecond += digit(c) * mul; mul /= 10; @@ -942,11 +946,11 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr pUtc, vm::c // Time offset: 1995-12-03T13:23:00.00+02:30 if (sign == '+' || sign == '-') { - if (!std::isdigit(pszDateTime[pos + 1]) || - !std::isdigit(pszDateTime[pos + 2]) || + if (!utils::isdigit(pszDateTime[pos + 1]) || + !utils::isdigit(pszDateTime[pos + 2]) || pszDateTime[pos + 3] != ':' || - !std::isdigit(pszDateTime[pos + 4]) || - !std::isdigit(pszDateTime[pos + 5])) + !utils::isdigit(pszDateTime[pos + 4]) || + !utils::isdigit(pszDateTime[pos + 5])) { return CELL_RTC_ERROR_BAD_PARSE; } diff --git a/rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp b/rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp index d5f3f94f458f..4a01cafb1c81 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp @@ -10,6 +10,7 @@ #include "Emu/NP/np_handler.h" #include "Emu/NP/np_contexts.h" +#include "util/cctype.hpp" LOG_CHANNEL(sceNpCommerce2); @@ -933,7 +934,7 @@ error_code sceNpCommerce2DoProductCodeStartAsync(u32 ctx_id, u32 container, vm:: for (u32 i = 0; i < SCE_NP_COMMERCE2_PRODUCT_CODE_BLOCK_LEN; i++) { - if (!isalnum(param->code1[i]) || !isalnum(param->code2[i]) || !isalnum(param->code3[i])) + if (!utils::isalnum(param->code1[i]) || !utils::isalnum(param->code2[i]) || !utils::isalnum(param->code3[i])) return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID; } } diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp index 3304d2b983eb..6921008c7ba0 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp @@ -25,6 +25,7 @@ #include #include #include "util/asm.hpp" +#include "util/cctype.hpp" LOG_CHANNEL(sceNpTrophy); @@ -254,7 +255,7 @@ void fmt_class_string::format(std::string& out, u64 arg) const auto& id = get_object(arg); const u8 term = id.data[9]; - fmt::append(out, "{ data='%s', term='%s' (0x%x), num=%d, dummy=%d }", id.data, std::isprint(term) ? fmt::format("%c", term) : "", term, id.num, id.dummy); + fmt::append(out, "{ data='%s', term='%s' (0x%x), num=%d, dummy=%d }", id.data, utils::isprint(term) ? fmt::format("%c", term) : "", term, id.num, id.dummy); } // Helpers diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 853b40c37ddd..595e34a51420 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -54,7 +54,6 @@ #endif #include -#include #include #include #include @@ -65,6 +64,7 @@ #include "util/simd.hpp" #include "util/sysinfo.hpp" #include "util/fnv_hash.hpp" +#include "util/cctype.hpp" #include "Utilities/sema.h" @@ -1481,7 +1481,7 @@ void ppu_thread::dump_regs(std::string& ret, std::any& custom_data) const // NTS: size of 3 and above is required // If ends with a newline, only one character is required else if ((sv.size() == buf_tmp.size() || (sv.size() >= (buf_tmp[sv.size()] == '\n' ? 1 : 3))) && - std::all_of(sv.begin(), sv.end(), [](u8 c){ return std::isprint(c); })) + std::all_of(sv.begin(), sv.end(), [](u8 c){ return utils::isprint(c); })) { fmt::append(ret, " -> \"%s\"", sv); } diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 676df5ab4ed5..136b507d3ad0 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -59,6 +59,7 @@ #include "util/tsc.hpp" #include "util/sysinfo.hpp" #include "util/init_mutex.hpp" +#include "util/cctype.hpp" #if defined(ARCH_X64) #ifdef _MSC_VER @@ -1341,7 +1342,7 @@ std::string lv2_obj::name64(u64 name_u64) // NTS string, ignore invalid/newline characters // Example: "lv2\n\0tx" will be printed as "lv2" std::string str{ptr, std::find(ptr, ptr + 7, '\0')}; - str.erase(std::remove_if(str.begin(), str.end(), [](uchar c){ return !std::isprint(c); }), str.end()); + str.erase(std::remove_if(str.begin(), str.end(), [](uchar c){ return !utils::isprint(c); }), str.end()); return str; } diff --git a/rpcs3/Emu/Cell/lv2/sys_tty.cpp b/rpcs3/Emu/Cell/lv2/sys_tty.cpp index dcc142b81aaa..fbc56c971317 100644 --- a/rpcs3/Emu/Cell/lv2/sys_tty.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_tty.cpp @@ -2,6 +2,7 @@ #include "Emu/system_config.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/timers.hpp" +#include "util/cctype.hpp" #include "sys_tty.h" @@ -115,7 +116,7 @@ error_code sys_tty_write([[maybe_unused]] ppu_thread& ppu, s32 ch, vm::cptr= 1u) { - return std::tolower(static_cast(msg[index - 1])) == word[0]; + return utils::tolower(msg[index - 1]) == word[0]; } return false; diff --git a/rpcs3/Emu/NP/np_helpers.cpp b/rpcs3/Emu/NP/np_helpers.cpp index 9d651693d9fc..18517cff2dc4 100644 --- a/rpcs3/Emu/NP/np_helpers.cpp +++ b/rpcs3/Emu/NP/np_helpers.cpp @@ -1,6 +1,7 @@ #include "Emu/Cell/Modules/sceNp.h" #include "stdafx.h" #include "util/types.hpp" +#include "util/cctype.hpp" #include "Utilities/StrUtil.h" #include "rpcn_client.h" @@ -42,7 +43,7 @@ namespace np const auto split_id = fmt::split_sv(str, {"_"}); - if (split_id.size() != 2 || split_id[0].length() != 9 || split_id[1].length() != 2 || !std::isdigit(split_id[1][0]) || !std::isdigit(split_id[1][1])) + if (split_id.size() != 2 || split_id[0].length() != 9 || split_id[1].length() != 2 || !utils::isdigit(split_id[1][0]) || !utils::isdigit(split_id[1][1])) { rpcn_log.error("Tried to parse an invalid communication_id!"); return std::nullopt; @@ -110,7 +111,7 @@ namespace np bool is_valid_npid(const SceNpId& npid) { - if (!std::all_of(npid.handle.data, npid.handle.data + 16, [](char c) { return std::isalnum(static_cast(c)) || c == '-' || c == '_' || c == 0; } ) + if (!std::all_of(npid.handle.data, npid.handle.data + 16, [](char c) { return utils::isalnum(c) || c == '-' || c == '_' || c == 0; } ) || npid.handle.data[16] != 0 || !std::all_of(npid.handle.dummy, npid.handle.dummy + 3, [](char val) { return val == 0; }) ) { diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp index 4ecb74d010bb..7d996309480f 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp @@ -2,6 +2,7 @@ #include "capabilities.h" #include "Utilities/StrUtil.h" +#include "util/cctype.hpp" #include "Emu/system_config.h" #include @@ -118,7 +119,7 @@ namespace gl // Workaround for intel drivers which have terrible capability reporting if (!vendor_string.empty()) { - std::transform(vendor_string.begin(), vendor_string.end(), vendor_string.begin(), ::tolower); + std::transform(vendor_string.begin(), vendor_string.end(), vendor_string.begin(), utils::tolower); } else { diff --git a/rpcs3/Emu/RSX/GL/glutils/program.cpp b/rpcs3/Emu/RSX/GL/glutils/program.cpp index e68e4b51325f..3fb7bb63d95d 100644 --- a/rpcs3/Emu/RSX/GL/glutils/program.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/program.cpp @@ -3,6 +3,7 @@ #include "state_tracker.hpp" #include "Emu/system_config.h" +#include "util/cctype.hpp" namespace gl { @@ -15,8 +16,8 @@ namespace gl size_t string_begin = std::string::npos, i = start; for (size_t count = 0; i < source.length(); ++i) { - const auto& c = source[i]; - const auto is_space = std::isspace(c); + const char c = source[i]; + const auto is_space = utils::isspace(c); if (string_begin == std::string::npos) { diff --git a/rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp b/rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp index 991651d73978..26f102cf04fc 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp +++ b/rpcs3/Emu/RSX/Program/Assembler/FPASM.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "FPASM.h" #include "Emu/RSX/Program/RSXFragmentProgram.h" +#include "util/cctype.hpp" #include @@ -198,7 +199,7 @@ namespace rsx::assembler result.reserve(s.size()); bool literal = false; - for (const auto& c : s) + for (const char c : s) { if (c == ' ') { @@ -209,7 +210,7 @@ namespace rsx::assembler continue; } - if (std::isspace(c)) + if (utils::isspace(c)) { continue; } diff --git a/rpcs3/Emu/RSX/VK/VKCommonDecompiler.cpp b/rpcs3/Emu/RSX/VK/VKCommonDecompiler.cpp index 82c6d40873ac..842403673913 100644 --- a/rpcs3/Emu/RSX/VK/VKCommonDecompiler.cpp +++ b/rpcs3/Emu/RSX/VK/VKCommonDecompiler.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "VKCommonDecompiler.h" +#include "util/cctype.hpp" namespace vk { @@ -50,7 +51,7 @@ namespace vk for (int char_idx = name_length - max_index_length; char_idx < name_length; ++char_idx) { - if (std::isdigit(name[char_idx])) + if (utils::isdigit(name[char_idx])) { index += name[char_idx]; } diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 1e69601d0ef6..f75ca31d17f2 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -47,6 +47,7 @@ #include "util/logs.hpp" #include "util/init_mutex.hpp" #include "util/sysinfo.hpp" +#include "util/cctype.hpp" #include #include @@ -4044,7 +4045,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s tty_buffer.resize(tty_read_fd.read_at(m_tty_file_init_pos, tty_buffer.data(), tty_buffer.size())); tty_read_fd.close(); - if (!tty_buffer.empty() && std::isspace(tty_buffer.back())) + if (!tty_buffer.empty() && utils::isspace(tty_buffer.back())) { tty_buffer.resize(tty_buffer.find_last_not_of(" \f\n\r\t\v"sv) + 1); } @@ -4137,7 +4138,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s iter = index + 1; } - if (!new_log.empty() && std::isspace(new_log.back())) + if (!new_log.empty() && utils::isspace(new_log.back())) { new_log.resize(new_log.find_last_not_of(" \f\n\r\t\v"sv) + 1); } diff --git a/rpcs3/Emu/VFS.cpp b/rpcs3/Emu/VFS.cpp index 46a417ee6ee3..63d6290ffe1d 100644 --- a/rpcs3/Emu/VFS.cpp +++ b/rpcs3/Emu/VFS.cpp @@ -7,6 +7,7 @@ #include "Utilities/mutex.h" #include "Utilities/StrUtil.h" +#include "util/cctype.hpp" #ifdef _WIN32 #include @@ -527,7 +528,7 @@ std::string vfs::escape(std::string_view name, bool escape_slash) if (name.size() > 2) { // Pack first 3 characters - const u32 triple = std::bit_cast, u32>(toupper(name[0]) | toupper(name[1]) << 8 | toupper(name[2]) << 16); + const u32 triple = std::bit_cast, u32>(utils::toupper(name[0]) | utils::toupper(name[1]) << 8 | utils::toupper(name[2]) << 16); switch (triple) { diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 9dc04b6758bd..eeb270ae34a6 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -802,6 +802,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 1afdf03197e0..f86d026b983e 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -2995,6 +2995,9 @@ Emu\GPU\RSX\Overlays\BigPicture + + Utilities + diff --git a/rpcs3/rpcs3qt/cheat_manager.cpp b/rpcs3/rpcs3qt/cheat_manager.cpp index 90b38f2d49f0..a212b0cd079d 100644 --- a/rpcs3/rpcs3qt/cheat_manager.cpp +++ b/rpcs3/rpcs3qt/cheat_manager.cpp @@ -22,6 +22,7 @@ #include "util/asm.hpp" #include "util/logs.hpp" #include "util/to_endian.hpp" +#include "util/cctype.hpp" #include "Utilities/File.h" #include "Utilities/StrUtil.h" #include "Utilities/bin_patch.h" // get_patches_path() @@ -221,12 +222,12 @@ bool cheat_engine::resolve_script(u32& final_offset, const u32 offset, std::stri while (index < red_script.size()) { - if (std::isdigit(static_cast(red_script[index]))) + if (utils::isdigit(red_script[index])) { std::string num_string; for (; index < red_script.size(); index++) { - if (!std::isdigit(static_cast(red_script[index]))) + if (!utils::isdigit(red_script[index])) break; num_string += red_script[index]; diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index 21a20f965c72..1195dfeab70c 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -13,6 +13,7 @@ #include "Emu/Io/Keyboard.h" #include "util/yaml.hpp" +#include "util/cctype.hpp" #include "Utilities/File.h" #include "Utilities/Config.h" @@ -444,7 +445,7 @@ void emu_settings::EnhanceCheckBox(QCheckBox* checkbox, emu_settings_type type) } std::string def = GetSettingDefault(type); - std::transform(def.begin(), def.end(), def.begin(), ::tolower); + std::transform(def.begin(), def.end(), def.begin(), utils::tolower); if (def != "true" && def != "false") { @@ -453,7 +454,7 @@ void emu_settings::EnhanceCheckBox(QCheckBox* checkbox, emu_settings_type type) } std::string selected = GetSetting(type); - std::transform(selected.begin(), selected.end(), selected.begin(), ::tolower); + std::transform(selected.begin(), selected.end(), selected.begin(), utils::tolower); if (selected == "true") { diff --git a/rpcs3/rpcs3qt/input_dialog.cpp b/rpcs3/rpcs3qt/input_dialog.cpp index 25008bc0fb56..cbf5c941de94 100644 --- a/rpcs3/rpcs3qt/input_dialog.cpp +++ b/rpcs3/rpcs3qt/input_dialog.cpp @@ -1,5 +1,6 @@ #include "input_dialog.h" #include "qt_utils.h" +#include "util/cctype.hpp" #include #include @@ -46,7 +47,7 @@ void input_dialog::set_clear_button_enabled(bool enabled) const void input_dialog::set_input_font(const QFont& font, bool fix_width, char sample) const { - if (const int max = m_input->maxLength(); max > 0 && fix_width && std::isprint(static_cast(sample))) + if (const int max = m_input->maxLength(); max > 0 && fix_width && utils::isprint(sample)) { const QString str = QString(max, sample); m_input->setFixedWidth(gui::utils::get_label_width(str, &font)); diff --git a/rpcs3/rpcs3qt/memory_string_searcher.cpp b/rpcs3/rpcs3qt/memory_string_searcher.cpp index c7f693b9c4be..f9ff2b90f99c 100644 --- a/rpcs3/rpcs3qt/memory_string_searcher.cpp +++ b/rpcs3/rpcs3qt/memory_string_searcher.cpp @@ -16,6 +16,7 @@ #include "util/logs.hpp" #include "util/sysinfo.hpp" #include "util/asm.hpp" +#include "util/cctype.hpp" LOG_CHANNEL(gui_log, "GUI"); @@ -71,7 +72,7 @@ u64 memory_viewer_panel::OnSearch(std::string wstr, u32 mode) bool case_insensitive = false; // First characters for case insensitive search - const char first_chars[2]{ static_cast(::tolower(wstr[0])), static_cast(::toupper(wstr[0])) }; + const char first_chars[2]{ static_cast(utils::tolower(wstr[0])), static_cast(utils::toupper(wstr[0])) }; std::string_view insensitive_search{first_chars, 2}; if (insensitive_search[0] == insensitive_search[1]) @@ -92,7 +93,7 @@ u64 memory_viewer_panel::OnSearch(std::string wstr, u32 mode) if (case_insensitive) { - std::transform(wstr.begin(), wstr.end(), wstr.begin(), ::tolower); + std::transform(wstr.begin(), wstr.end(), wstr.begin(), utils::tolower); } break; @@ -257,7 +258,7 @@ u64 memory_viewer_panel::OnSearch(std::string wstr, u32 mode) if (case_insensitive) { - std::transform(last.begin(), last.end(), last.begin(), ::tolower); + std::transform(last.begin(), last.end(), last.begin(), utils::tolower); } std::smatch sm; @@ -399,7 +400,7 @@ u64 memory_viewer_panel::OnSearch(std::string wstr, u32 mode) std::string_view test_sv{get_ptr(start), addr_max - start}; // Do not use allocating functions such as fmt::to_lower - if (test_sv.size() >= wstr.size() && std::all_of(wstr.begin(), wstr.end(), [&](const char& c) { return c == ::tolower(test_sv[&c - wstr.data()]); })) + if (test_sv.size() >= wstr.size() && std::all_of(wstr.begin(), wstr.end(), [&](const char& c) { return c == utils::tolower(test_sv[&c - wstr.data()]); })) { // Force full logging if any character differs in case log_occurance(test_sv, !test_sv.starts_with(wstr)); diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp index 7026a334edde..0511d011720e 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp +++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp @@ -27,6 +27,7 @@ #include "util/logs.hpp" #include "util/asm.hpp" +#include "util/cctype.hpp" #include "debugger_frame.h" LOG_CHANNEL(gui_log, "GUI"); @@ -876,7 +877,7 @@ void memory_viewer_panel::ShowMemory() for (auto& ch : str) { - if (!std::isprint(static_cast(ch))) ch = '.'; + if (!utils::isprint(ch)) ch = '.'; } t_mem_ascii_str += QString::fromStdString(std::move(str)); diff --git a/rpcs3/rpcs3qt/register_editor_dialog.cpp b/rpcs3/rpcs3qt/register_editor_dialog.cpp index 639fb7db3538..b69751cff423 100644 --- a/rpcs3/rpcs3qt/register_editor_dialog.cpp +++ b/rpcs3/rpcs3qt/register_editor_dialog.cpp @@ -17,6 +17,7 @@ #include "util/v128.hpp" #include "util/asm.hpp" +#include "util/cctype.hpp" enum registers : int { @@ -300,7 +301,7 @@ void register_editor_dialog::OnOkay() } } - value.erase(std::remove_if(value.begin(), value.end(), [](uchar c){ return std::isspace(c); }), value.end()); + value.erase(std::remove_if(value.begin(), value.end(), utils::isspace), value.end()); pad(32); @@ -365,7 +366,7 @@ void register_editor_dialog::OnOkay() } } - value.erase(std::remove_if(value.begin(), value.end(), [](uchar c){ return std::isspace(c); }), value.end()); + value.erase(std::remove_if(value.begin(), value.end(), utils::isspace), value.end()); pad(32); diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index edde32ad06bb..31b1b098917c 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -15,6 +15,7 @@ #include "Emu/System.h" #include "Emu/NP/rpcn_config.h" #include "Emu/NP/ip_address.h" +#include "util/cctype.hpp" #ifdef __clang__ #pragma clang diagnostic push @@ -36,9 +37,9 @@ bool validate_rpcn_username(std::string_view username) return false; return std::all_of(username.cbegin(), username.cend(), [](const char c) - { - return std::isalnum(static_cast(c)) || c == '-' || c == '_'; - }); + { + return utils::isalnum(c) || c == '-' || c == '_'; + }); } bool validate_email(std::string_view email) diff --git a/rpcs3/tests/rpcs3_test.vcxproj b/rpcs3/tests/rpcs3_test.vcxproj index df73d02f0d20..e6702dbf4c7d 100644 --- a/rpcs3/tests/rpcs3_test.vcxproj +++ b/rpcs3/tests/rpcs3_test.vcxproj @@ -93,6 +93,7 @@ + true diff --git a/rpcs3/tests/test_cctype.cpp b/rpcs3/tests/test_cctype.cpp new file mode 100644 index 000000000000..0ada63c223df --- /dev/null +++ b/rpcs3/tests/test_cctype.cpp @@ -0,0 +1,314 @@ +#include + +#include "util/cctype.hpp" + +namespace utils +{ + TEST(CctypeTest, Test_check_arg) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_TRUE(utils::check_cctype_arg(static_cast(i))); + EXPECT_TRUE(utils::check_cctype_arg(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(i < 256, utils::check_cctype_arg(static_cast(i))); + } + } + + TEST(CctypeTest, Test_isalnum) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::isalnum(static_cast(i)) != 0, utils::isalnum(static_cast(i))); + EXPECT_EQ(::isalnum(static_cast(i)) != 0, utils::isalnum(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::isalnum(static_cast(i)) != 0, utils::isalnum(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::isalnum(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_isalpha) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::isalpha(static_cast(i)) != 0, utils::isalpha(static_cast(i))); + EXPECT_EQ(::isalpha(static_cast(i)) != 0, utils::isalpha(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::isalpha(static_cast(i)) != 0, utils::isalpha(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::isalpha(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_iscntrl) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::iscntrl(static_cast(i)) != 0, utils::iscntrl(static_cast(i))); + EXPECT_EQ(::iscntrl(static_cast(i)) != 0, utils::iscntrl(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::iscntrl(static_cast(i)) != 0, utils::iscntrl(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::iscntrl(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_isdigit) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::isdigit(static_cast(i)) != 0, utils::isdigit(static_cast(i))); + EXPECT_EQ(::isdigit(static_cast(i)) != 0, utils::isdigit(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::isdigit(static_cast(i)) != 0, utils::isdigit(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::isdigit(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_isgraph) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::isgraph(static_cast(i)) != 0, utils::isgraph(static_cast(i))); + EXPECT_EQ(::isgraph(static_cast(i)) != 0, utils::isgraph(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::isgraph(static_cast(i)) != 0, utils::isgraph(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::isgraph(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_islower) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::islower(static_cast(i)) != 0, utils::islower(static_cast(i))); + EXPECT_EQ(::islower(static_cast(i)) != 0, utils::islower(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::islower(static_cast(i)) != 0, utils::islower(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::islower(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_isupper) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::isupper(static_cast(i)) != 0, utils::isupper(static_cast(i))); + EXPECT_EQ(::isupper(static_cast(i)) != 0, utils::isupper(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::isupper(static_cast(i)) != 0, utils::isupper(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::isupper(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_isprint) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::isprint(static_cast(i)) != 0, utils::isprint(static_cast(i))); + EXPECT_EQ(::isprint(static_cast(i)) != 0, utils::isprint(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::isprint(static_cast(i)) != 0, utils::isprint(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::isprint(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_ispunct) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::ispunct(static_cast(i)) != 0, utils::ispunct(static_cast(i))); + EXPECT_EQ(::ispunct(static_cast(i)) != 0, utils::ispunct(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::ispunct(static_cast(i)) != 0, utils::ispunct(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::ispunct(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_isspace) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::isspace(static_cast(i)) != 0, utils::isspace(static_cast(i))); + EXPECT_EQ(::isspace(static_cast(i)) != 0, utils::isspace(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::isspace(static_cast(i)) != 0, utils::isspace(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::isspace(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_isblank) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::isblank(static_cast(i)) != 0, utils::isblank(static_cast(i))); + EXPECT_EQ(::isblank(static_cast(i)) != 0, utils::isblank(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::isblank(static_cast(i)) != 0, utils::isblank(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::isblank(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_isxdigit) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::isxdigit(static_cast(i)) != 0, utils::isxdigit(static_cast(i))); + EXPECT_EQ(::isxdigit(static_cast(i)) != 0, utils::isxdigit(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::isxdigit(static_cast(i)) != 0, utils::isxdigit(static_cast(i))); + } + else + { + EXPECT_FALSE(utils::isxdigit(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_tolower) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::tolower(static_cast(i)), utils::tolower(static_cast(i))); + EXPECT_EQ(::tolower(static_cast(i)), utils::tolower(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::tolower(static_cast(i)), utils::tolower(static_cast(i))); + } + else + { + EXPECT_EQ(EOF, utils::tolower(static_cast(i))); + } + } + } + + TEST(CctypeTest, Test_toupper) + { + for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) + { + EXPECT_EQ(::toupper(static_cast(i)), utils::toupper(static_cast(i))); + EXPECT_EQ(::toupper(static_cast(i)), utils::toupper(static_cast(i))); + } + + for (u32 i = 0; i <= std::numeric_limits::max(); i++) + { + if (i < 256) + { + EXPECT_EQ(::toupper(static_cast(i)), utils::toupper(static_cast(i))); + } + else + { + EXPECT_EQ(EOF, utils::toupper(static_cast(i))); + } + } + } +} diff --git a/rpcs3/util/cctype.hpp b/rpcs3/util/cctype.hpp new file mode 100644 index 000000000000..af82e597fb67 --- /dev/null +++ b/rpcs3/util/cctype.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "types.hpp" + +#include +#include + +namespace utils +{ + template + concept cctype_char = std::integral && (std::is_same_v || std::is_same_v || std::is_unsigned_v); + + template + constexpr bool check_cctype_arg(T c) + { + if constexpr (std::is_same_v) + { + return true; + } + else + { + return c <= std::numeric_limits::max(); + } + } + + template constexpr bool isalnum(T c) { return check_cctype_arg(c) && ::isalnum(static_cast(c)); } + template constexpr bool isalpha(T c) { return check_cctype_arg(c) && ::isalpha(static_cast(c)); } + template constexpr bool iscntrl(T c) { return check_cctype_arg(c) && ::iscntrl(static_cast(c)); } + template constexpr bool isdigit(T c) { return check_cctype_arg(c) && ::isdigit(static_cast(c)); } + template constexpr bool isgraph(T c) { return check_cctype_arg(c) && ::isgraph(static_cast(c)); } + template constexpr bool islower(T c) { return check_cctype_arg(c) && ::islower(static_cast(c)); } + template constexpr bool isupper(T c) { return check_cctype_arg(c) && ::isupper(static_cast(c)); } + template constexpr bool isprint(T c) { return check_cctype_arg(c) && ::isprint(static_cast(c)); } + template constexpr bool ispunct(T c) { return check_cctype_arg(c) && ::ispunct(static_cast(c)); } + template constexpr bool isspace(T c) { return check_cctype_arg(c) && ::isspace(static_cast(c)); } + template constexpr bool isblank(T c) { return check_cctype_arg(c) && ::isblank(static_cast(c)); } + template constexpr bool isxdigit(T c) { return check_cctype_arg(c) && ::isxdigit(static_cast(c)); } + + template + constexpr int tolower(T c) + { + if (!check_cctype_arg(c)) return EOF; + + return ::tolower(static_cast(c)); + } + + template + constexpr int toupper(T c) + { + if (!check_cctype_arg(c)) return EOF; + + return ::toupper(static_cast(c)); + } +}