SmallString: Tidy-up and add wide string helpers

This commit is contained in:
Stenzek 2024-08-26 14:06:18 +10:00
parent b02d3592dd
commit 8a3dbbbea5
No known key found for this signature in database
2 changed files with 97 additions and 22 deletions

View file

@ -1,20 +1,17 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// 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 "small_string.h"
#include "assert.h" #include "assert.h"
#include "string_util.h"
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#ifdef _MSC_VER #ifdef _WIN32
#define CASE_COMPARE _stricmp #include "windows_headers.h"
#define CASE_N_COMPARE _strnicmp
#else
#define CASE_COMPARE strcasecmp
#define CASE_N_COMPARE strncasecmp
#endif #endif
SmallStringBase::SmallStringBase() = default; SmallStringBase::SmallStringBase() = default;
@ -443,6 +440,36 @@ void SmallStringBase::assign(const std::string_view str)
append(str.data(), static_cast<u32>(str.size())); append(str.data(), static_cast<u32>(str.size()));
} }
#ifdef _WIN32
void SmallStringBase::assign(const std::wstring_view wstr)
{
int mblen =
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.length()), nullptr, 0, nullptr, nullptr);
if (mblen < 0)
{
clear();
return;
}
reserve(static_cast<u32>(mblen));
if (mblen > 0 && WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.length()), m_buffer, mblen,
nullptr, nullptr) < 0)
{
clear();
return;
}
m_length = static_cast<u32>(mblen);
}
std::wstring SmallStringBase::wstring() const
{
return StringUtil::UTF8StringToWideString(view());
}
#endif
void SmallStringBase::vformat(fmt::string_view fmt, fmt::format_args args) void SmallStringBase::vformat(fmt::string_view fmt, fmt::format_args args)
{ {
clear(); clear();
@ -479,7 +506,7 @@ bool SmallStringBase::iequals(const char* otherText) const
if (m_length == 0) if (m_length == 0)
return (std::strlen(otherText) == 0); return (std::strlen(otherText) == 0);
else else
return (CASE_COMPARE(m_buffer, otherText) == 0); return StringUtil::EqualNoCase(view(), otherText);
} }
bool SmallStringBase::iequals(const SmallStringBase& str) const 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 bool SmallStringBase::iequals(const std::string_view str) const
{ {
return (m_length == static_cast<u32>(str.length()) && return (m_length == static_cast<u32>(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 bool SmallStringBase::iequals(const std::string& str) const
{ {
return (m_length == static_cast<u32>(str.length()) && return (m_length == static_cast<u32>(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 int SmallStringBase::compare(const char* otherText) const
@ -560,7 +587,7 @@ int SmallStringBase::icompare(const SmallStringBase& str) const
else if (str.m_length == 0) else if (str.m_length == 0)
return 1; 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) if (m_length == str.m_length || res != 0)
return res; return res;
else else
@ -575,7 +602,7 @@ int SmallStringBase::icompare(const std::string_view str) const
else if (slength == 0) else if (slength == 0)
return 1; 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) if (m_length == slength || res != 0)
return res; return res;
else else
@ -590,7 +617,7 @@ int SmallStringBase::icompare(const std::string& str) const
else if (slength == 0) else if (slength == 0)
return 1; 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) if (m_length == slength || res != 0)
return res; return res;
else else
@ -604,7 +631,7 @@ bool SmallStringBase::starts_with(const char* str, bool case_sensitive) const
return false; return false;
return (case_sensitive) ? (std::strncmp(str, m_buffer, other_length) == 0) : 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 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 false;
return (case_sensitive) ? (std::strncmp(str.m_buffer, m_buffer, other_length) == 0) : 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 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 false;
return (case_sensitive) ? (std::strncmp(str.data(), m_buffer, other_length) == 0) : 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 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 false;
return (case_sensitive) ? (std::strncmp(str.data(), m_buffer, other_length) == 0) : 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 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; u32 start_offset = m_length - other_length;
return (case_sensitive) ? (std::strncmp(str, m_buffer + start_offset, other_length) == 0) : 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 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; const u32 start_offset = m_length - other_length;
return (case_sensitive) ? (std::strncmp(str.m_buffer, m_buffer + start_offset, other_length) == 0) : 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 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; const u32 start_offset = m_length - other_length;
return (case_sensitive) ? (std::strncmp(str.data(), m_buffer + start_offset, other_length) == 0) : 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 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; const u32 start_offset = m_length - other_length;
return (case_sensitive) ? (std::strncmp(str.data(), m_buffer + start_offset, other_length) == 0) : 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() void SmallStringBase::clear()
@ -732,6 +759,45 @@ u32 SmallStringBase::count(char ch) const
return count; return count;
} }
u32 SmallStringBase::replace(const char* search, const char* replacement)
{
const u32 search_length = static_cast<u32>(std::strlen(search));
const u32 replacement_length = static_cast<u32>(std::strlen(replacement));
s32 offset = 0;
u32 count = 0;
for (;;)
{
offset = find(search, static_cast<u32>(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<u32>(offset));
DebugAssert(chars_after_offset >= search_length);
if (chars_after_offset > search_length)
{
std::memmove(&m_buffer[static_cast<u32>(offset) + replacement_length],
&m_buffer[static_cast<u32>(offset) + search_length], chars_after_offset - search_length);
std::memcpy(&m_buffer[static_cast<u32>(offset)], replacement, replacement_length);
}
else
{
// at end of string
std::memcpy(&m_buffer[static_cast<u32>(offset)], replacement, replacement_length);
m_buffer[static_cast<u32>(offset) + replacement_length] = '\0';
}
offset += replacement_length;
count++;
}
return count;
}
void SmallStringBase::resize(u32 new_size, char fill, bool shrink_if_smaller) void SmallStringBase::resize(u32 new_size, char fill, bool shrink_if_smaller)
{ {
// if going larger, or we don't own the buffer, realloc // if going larger, or we don't own the buffer, realloc

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// 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 #pragma once
#include "types.h" #include "types.h"
@ -144,6 +144,9 @@ public:
// returns the number of instances of the specified character // returns the number of instances of the specified character
u32 count(char ch) const; 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 // removes characters from string
void erase(s32 offset, s32 count = std::numeric_limits<s32>::max()); void erase(s32 offset, s32 count = std::numeric_limits<s32>::max());
@ -189,6 +192,12 @@ public:
// returns a substring view for this string // returns a substring view for this string
std::string_view substr(s32 offset, s32 count) const; 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 // accessor operators
ALWAYS_INLINE operator const char*() const { return c_str(); } ALWAYS_INLINE operator const char*() const { return c_str(); }
ALWAYS_INLINE operator char*() { return data(); } ALWAYS_INLINE operator char*() { return data(); }