From 8a3dbbbea583efe695c16d223cd1f6c960268760 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 26 Aug 2024 14:06:18 +1000 Subject: [PATCH] SmallString: Tidy-up and add wide string helpers --- src/common/small_string.cpp | 108 +++++++++++++++++++++++++++++------- src/common/small_string.h | 11 +++- 2 files changed, 97 insertions(+), 22 deletions(-) diff --git a/src/common/small_string.cpp b/src/common/small_string.cpp index 0eb837474..05458fb68 100644 --- a/src/common/small_string.cpp +++ b/src/common/small_string.cpp @@ -1,20 +1,17 @@ // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) +// SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0) #include "small_string.h" #include "assert.h" +#include "string_util.h" #include #include #include #include -#ifdef _MSC_VER -#define CASE_COMPARE _stricmp -#define CASE_N_COMPARE _strnicmp -#else -#define CASE_COMPARE strcasecmp -#define CASE_N_COMPARE strncasecmp +#ifdef _WIN32 +#include "windows_headers.h" #endif SmallStringBase::SmallStringBase() = default; @@ -443,6 +440,36 @@ void SmallStringBase::assign(const std::string_view str) append(str.data(), static_cast(str.size())); } +#ifdef _WIN32 + +void SmallStringBase::assign(const std::wstring_view wstr) +{ + int mblen = + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.length()), nullptr, 0, nullptr, nullptr); + if (mblen < 0) + { + clear(); + return; + } + + reserve(static_cast(mblen)); + if (mblen > 0 && WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.length()), m_buffer, mblen, + nullptr, nullptr) < 0) + { + clear(); + return; + } + + m_length = static_cast(mblen); +} + +std::wstring SmallStringBase::wstring() const +{ + return StringUtil::UTF8StringToWideString(view()); +} + +#endif + void SmallStringBase::vformat(fmt::string_view fmt, fmt::format_args args) { clear(); @@ -479,7 +506,7 @@ bool SmallStringBase::iequals(const char* otherText) const if (m_length == 0) return (std::strlen(otherText) == 0); else - return (CASE_COMPARE(m_buffer, otherText) == 0); + return StringUtil::EqualNoCase(view(), otherText); } bool SmallStringBase::iequals(const SmallStringBase& str) const @@ -490,13 +517,13 @@ bool SmallStringBase::iequals(const SmallStringBase& str) const bool SmallStringBase::iequals(const std::string_view str) const { return (m_length == static_cast(str.length()) && - (m_length == 0 || CASE_N_COMPARE(m_buffer, str.data(), m_length) == 0)); + (m_length == 0 || StringUtil::Strncasecmp(m_buffer, str.data(), m_length) == 0)); } bool SmallStringBase::iequals(const std::string& str) const { return (m_length == static_cast(str.length()) && - (m_length == 0 || CASE_N_COMPARE(m_buffer, str.data(), m_length) == 0)); + (m_length == 0 || StringUtil::Strncasecmp(m_buffer, str.data(), m_length) == 0)); } int SmallStringBase::compare(const char* otherText) const @@ -560,7 +587,7 @@ int SmallStringBase::icompare(const SmallStringBase& str) const else if (str.m_length == 0) return 1; - const int res = CASE_N_COMPARE(m_buffer, str.m_buffer, std::min(m_length, str.m_length)); + const int res = StringUtil::Strncasecmp(m_buffer, str.m_buffer, std::min(m_length, str.m_length)); if (m_length == str.m_length || res != 0) return res; else @@ -575,7 +602,7 @@ int SmallStringBase::icompare(const std::string_view str) const else if (slength == 0) return 1; - const int res = CASE_N_COMPARE(m_buffer, str.data(), std::min(m_length, slength)); + const int res = StringUtil::Strncasecmp(m_buffer, str.data(), std::min(m_length, slength)); if (m_length == slength || res != 0) return res; else @@ -590,7 +617,7 @@ int SmallStringBase::icompare(const std::string& str) const else if (slength == 0) return 1; - const int res = CASE_N_COMPARE(m_buffer, str.data(), std::min(m_length, slength)); + const int res = StringUtil::Strncasecmp(m_buffer, str.data(), std::min(m_length, slength)); if (m_length == slength || res != 0) return res; else @@ -604,7 +631,7 @@ bool SmallStringBase::starts_with(const char* str, bool case_sensitive) const return false; return (case_sensitive) ? (std::strncmp(str, m_buffer, other_length) == 0) : - (CASE_N_COMPARE(str, m_buffer, other_length) == 0); + (StringUtil::Strncasecmp(str, m_buffer, other_length) == 0); } bool SmallStringBase::starts_with(const SmallStringBase& str, bool case_sensitive) const @@ -614,7 +641,7 @@ bool SmallStringBase::starts_with(const SmallStringBase& str, bool case_sensitiv return false; return (case_sensitive) ? (std::strncmp(str.m_buffer, m_buffer, other_length) == 0) : - (CASE_N_COMPARE(str.m_buffer, m_buffer, other_length) == 0); + (StringUtil::Strncasecmp(str.m_buffer, m_buffer, other_length) == 0); } bool SmallStringBase::starts_with(const std::string_view str, bool case_sensitive) const @@ -624,7 +651,7 @@ bool SmallStringBase::starts_with(const std::string_view str, bool case_sensitiv return false; return (case_sensitive) ? (std::strncmp(str.data(), m_buffer, other_length) == 0) : - (CASE_N_COMPARE(str.data(), m_buffer, other_length) == 0); + (StringUtil::Strncasecmp(str.data(), m_buffer, other_length) == 0); } bool SmallStringBase::starts_with(const std::string& str, bool case_sensitive) const @@ -634,7 +661,7 @@ bool SmallStringBase::starts_with(const std::string& str, bool case_sensitive) c return false; return (case_sensitive) ? (std::strncmp(str.data(), m_buffer, other_length) == 0) : - (CASE_N_COMPARE(str.data(), m_buffer, other_length) == 0); + (StringUtil::Strncasecmp(str.data(), m_buffer, other_length) == 0); } bool SmallStringBase::ends_with(const char* str, bool case_sensitive) const @@ -645,7 +672,7 @@ bool SmallStringBase::ends_with(const char* str, bool case_sensitive) const u32 start_offset = m_length - other_length; return (case_sensitive) ? (std::strncmp(str, m_buffer + start_offset, other_length) == 0) : - (CASE_N_COMPARE(str, m_buffer + start_offset, other_length) == 0); + (StringUtil::Strncasecmp(str, m_buffer + start_offset, other_length) == 0); } bool SmallStringBase::ends_with(const SmallStringBase& str, bool case_sensitive) const @@ -656,7 +683,7 @@ bool SmallStringBase::ends_with(const SmallStringBase& str, bool case_sensitive) const u32 start_offset = m_length - other_length; return (case_sensitive) ? (std::strncmp(str.m_buffer, m_buffer + start_offset, other_length) == 0) : - (CASE_N_COMPARE(str.m_buffer, m_buffer + start_offset, other_length) == 0); + (StringUtil::Strncasecmp(str.m_buffer, m_buffer + start_offset, other_length) == 0); } bool SmallStringBase::ends_with(const std::string_view str, bool case_sensitive) const @@ -667,7 +694,7 @@ bool SmallStringBase::ends_with(const std::string_view str, bool case_sensitive) const u32 start_offset = m_length - other_length; return (case_sensitive) ? (std::strncmp(str.data(), m_buffer + start_offset, other_length) == 0) : - (CASE_N_COMPARE(str.data(), m_buffer + start_offset, other_length) == 0); + (StringUtil::Strncasecmp(str.data(), m_buffer + start_offset, other_length) == 0); } bool SmallStringBase::ends_with(const std::string& str, bool case_sensitive) const @@ -678,7 +705,7 @@ bool SmallStringBase::ends_with(const std::string& str, bool case_sensitive) con const u32 start_offset = m_length - other_length; return (case_sensitive) ? (std::strncmp(str.data(), m_buffer + start_offset, other_length) == 0) : - (CASE_N_COMPARE(str.data(), m_buffer + start_offset, other_length) == 0); + (StringUtil::Strncasecmp(str.data(), m_buffer + start_offset, other_length) == 0); } void SmallStringBase::clear() @@ -732,6 +759,45 @@ u32 SmallStringBase::count(char ch) const return count; } +u32 SmallStringBase::replace(const char* search, const char* replacement) +{ + const u32 search_length = static_cast(std::strlen(search)); + const u32 replacement_length = static_cast(std::strlen(replacement)); + + s32 offset = 0; + u32 count = 0; + for (;;) + { + offset = find(search, static_cast(offset)); + if (offset < 0) + break; + + const u32 new_length = m_length - search_length + replacement_length; + reserve(new_length); + m_length = new_length; + + const u32 chars_after_offset = (m_length - static_cast(offset)); + DebugAssert(chars_after_offset >= search_length); + if (chars_after_offset > search_length) + { + std::memmove(&m_buffer[static_cast(offset) + replacement_length], + &m_buffer[static_cast(offset) + search_length], chars_after_offset - search_length); + std::memcpy(&m_buffer[static_cast(offset)], replacement, replacement_length); + } + else + { + // at end of string + std::memcpy(&m_buffer[static_cast(offset)], replacement, replacement_length); + m_buffer[static_cast(offset) + replacement_length] = '\0'; + } + + offset += replacement_length; + count++; + } + + return count; +} + void SmallStringBase::resize(u32 new_size, char fill, bool shrink_if_smaller) { // if going larger, or we don't own the buffer, realloc diff --git a/src/common/small_string.h b/src/common/small_string.h index ead59c9a1..cbac9b2af 100644 --- a/src/common/small_string.h +++ b/src/common/small_string.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) +// SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0) #pragma once #include "types.h" @@ -144,6 +144,9 @@ public: // returns the number of instances of the specified character u32 count(char ch) const; + // replaces search string with replacement, returns the number of replacements made + u32 replace(const char* search, const char* replacement); + // removes characters from string void erase(s32 offset, s32 count = std::numeric_limits::max()); @@ -189,6 +192,12 @@ public: // returns a substring view for this string std::string_view substr(s32 offset, s32 count) const; +#ifdef _WIN32 + // wide string adapters, win32 only + void assign(const std::wstring_view wstr); + std::wstring wstring() const; +#endif + // accessor operators ALWAYS_INLINE operator const char*() const { return c_str(); } ALWAYS_INLINE operator char*() { return data(); }