Common: Add SmallString class

This commit is contained in:
Stenzek 2023-08-27 01:07:48 +10:00 committed by Connor McLaughlin
parent bbb807d860
commit f8e1b9b11b
5 changed files with 1042 additions and 1 deletions

View File

@ -29,6 +29,7 @@ target_sources(common PRIVATE
ReadbackSpinManager.cpp
Semaphore.cpp
SettingsWrapper.cpp
SmallString.cpp
StringUtil.cpp
TextureDecompress.cpp
Timer.cpp
@ -93,6 +94,7 @@ target_sources(common PRIVATE
ScopedGuard.h
SettingsInterface.h
SettingsWrapper.h
SmallString.h
StringUtil.h
Timer.h
TextureDecompress.h

711
common/SmallString.cpp Normal file
View File

@ -0,0 +1,711 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "SmallString.h"
#include "Assertions.h"
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#ifdef _MSC_VER
#define CASE_COMPARE _stricmp
#define CASE_N_COMPARE _strnicmp
#else
#define CASE_COMPARE strcasecmp
#define CASE_N_COMPARE strncasecmp
#endif
SmallStringBase::SmallStringBase() = default;
SmallStringBase::SmallStringBase(const SmallStringBase& copy)
{
assign(copy.m_buffer, copy.m_length);
}
SmallStringBase::SmallStringBase(const char* str)
{
assign(str);
}
SmallStringBase::SmallStringBase(const char* str, u32 count)
{
assign(str, count);
}
SmallStringBase::SmallStringBase(SmallStringBase&& move)
{
assign(std::move(move));
}
SmallStringBase::SmallStringBase(const std::string_view& sv)
{
assign(sv);
}
SmallStringBase::SmallStringBase(const std::string& str)
{
assign(str);
}
SmallStringBase::~SmallStringBase()
{
if (m_on_heap)
std::free(m_buffer);
}
void SmallStringBase::reserve(u32 new_reserve)
{
if (m_buffer_size >= new_reserve)
return;
if (m_on_heap)
{
char* new_ptr = static_cast<char*>(std::realloc(m_buffer, new_reserve));
if (!new_ptr)
pxFailRel("Memory allocation failed.");
#ifdef _DEBUG
std::memset(new_ptr + m_length, 0, new_reserve - m_length);
#endif
m_buffer = new_ptr;
}
else
{
char* new_ptr = static_cast<char*>(std::malloc(new_reserve));
if (!new_ptr)
pxFailRel("Memory allocation failed.");
if (m_length > 0)
std::memcpy(new_ptr, m_buffer, m_length);
#ifdef _DEBUG
std::memset(new_ptr + m_length, 0, new_reserve - m_length);
#else
new_ptr[m_length] = 0;
#endif
m_buffer = new_ptr;
m_on_heap = true;
}
}
void SmallStringBase::shrink_to_fit()
{
if (!m_on_heap || m_length == m_buffer_size)
return;
if (m_length == 0)
{
std::free(m_buffer);
m_buffer_size = 0;
return;
}
char* new_ptr = static_cast<char*>(std::realloc(m_buffer, m_length));
if (!new_ptr)
pxFailRel("Memory allocation failed.");
m_buffer = new_ptr;
m_buffer_size = m_length;
}
std::string_view SmallStringBase::view() const
{
return (m_length == 0) ? std::string_view() : std::string_view(m_buffer, m_length);
}
SmallStringBase& SmallStringBase::operator=(SmallStringBase&& move)
{
assign(move);
return *this;
}
SmallStringBase& SmallStringBase::operator=(const std::string_view& str)
{
assign(str);
return *this;
}
SmallStringBase& SmallStringBase::operator=(const std::string& str)
{
assign(str);
return *this;
}
SmallStringBase& SmallStringBase::operator=(const char* str)
{
assign(str);
return *this;
}
SmallStringBase& SmallStringBase::operator=(const SmallStringBase& copy)
{
assign(copy);
return *this;
}
void SmallStringBase::make_room_for(u32 space)
{
const u32 required_size = m_length + space + 1;
if (m_buffer_size >= required_size)
return;
reserve(std::max(required_size, m_buffer_size * 2));
}
void SmallStringBase::append(const char* str, u32 length)
{
if (length == 0)
return;
make_room_for(length);
pxAssert((length + m_length) < m_buffer_size);
std::memcpy(m_buffer + m_length, str, length);
m_length += length;
m_buffer[m_length] = 0;
}
void SmallStringBase::prepend(const char* str, u32 length)
{
if (length == 0)
return;
make_room_for(length);
pxAssert((length + m_length) < m_buffer_size);
std::memmove(m_buffer + length, m_buffer, m_length);
std::memcpy(m_buffer, str, length);
m_length += length;
m_buffer[m_length] = 0;
}
void SmallStringBase::append(char c)
{
append(&c, 1);
}
void SmallStringBase::append(const SmallStringBase& str)
{
append(str.m_buffer, str.m_length);
}
void SmallStringBase::append(const char* str)
{
append(str, static_cast<u32>(std::strlen(str)));
}
void SmallStringBase::append(const std::string& str)
{
append(str.c_str(), static_cast<u32>(str.length()));
}
void SmallStringBase::append(const std::string_view& str)
{
append(str.data(), static_cast<u32>(str.length()));
}
void SmallStringBase::append_format(const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
append_format_va(format, ap);
va_end(ap);
}
void SmallStringBase::append_format_va(const char* format, va_list ap)
{
// We have a 1KB byte buffer on the stack here. If this is too little, we'll grow it via the heap,
// but 1KB should be enough for most strings.
char stack_buffer[1024];
char* heap_buffer = nullptr;
char* buffer = stack_buffer;
u32 buffer_size = std::size(stack_buffer);
u32 written;
for (;;)
{
std::va_list ap_copy;
va_copy(ap_copy, ap);
const int ret = std::vsnprintf(buffer, buffer_size, format, ap_copy);
va_end(ap_copy);
if (ret < 0 || ((u32)ret >= (buffer_size - 1)))
{
buffer_size *= 2;
buffer = heap_buffer = reinterpret_cast<char*>(std::realloc(heap_buffer, buffer_size));
continue;
}
written = static_cast<u32>(ret);
break;
}
append(buffer, written);
if (heap_buffer)
std::free(heap_buffer);
}
void SmallStringBase::prepend(char c)
{
prepend(&c, 1);
}
void SmallStringBase::prepend(const SmallStringBase& str)
{
prepend(str.m_buffer, str.m_length);
}
void SmallStringBase::prepend(const char* str)
{
prepend(str, static_cast<u32>(std::strlen(str)));
}
void SmallStringBase::prepend(const std::string& str)
{
prepend(str.c_str(), static_cast<u32>(str.length()));
}
void SmallStringBase::prepend(const std::string_view& str)
{
prepend(str.data(), static_cast<u32>(str.length()));
}
void SmallStringBase::prepend_format(const char* format, ...)
{
va_list ap;
va_start(ap, format);
prepend_format_va(format, ap);
va_end(ap);
}
void SmallStringBase::prepend_format_va(const char* format, va_list ArgPtr)
{
// We have a 1KB byte buffer on the stack here. If this is too little, we'll grow it via the heap,
// but 1KB should be enough for most strings.
char stack_buffer[1024];
char* heap_buffer = NULL;
char* buffer = stack_buffer;
u32 buffer_size = std::size(stack_buffer);
u32 written;
for (;;)
{
int ret = std::vsnprintf(buffer, buffer_size, format, ArgPtr);
if (ret < 0 || (static_cast<u32>(ret) >= (buffer_size - 1)))
{
buffer_size *= 2;
buffer = heap_buffer = reinterpret_cast<char*>(std::realloc(heap_buffer, buffer_size));
continue;
}
written = static_cast<u32>(ret);
break;
}
prepend(buffer, written);
if (heap_buffer)
std::free(heap_buffer);
}
void SmallStringBase::insert(s32 offset, const char* str)
{
insert(offset, str, static_cast<u32>(std::strlen(str)));
}
void SmallStringBase::insert(s32 offset, const SmallStringBase& str)
{
insert(offset, str, str.m_length);
}
void SmallStringBase::insert(s32 offset, const char* str, u32 length)
{
if (length == 0)
return;
make_room_for(length);
// calc real offset
u32 real_offset;
if (offset < 0)
real_offset = static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length) + offset));
else
real_offset = std::min(static_cast<u32>(offset), m_length);
// determine number of characters after offset
pxAssert(real_offset <= m_length);
const u32 chars_after_offset = m_length - real_offset;
if (chars_after_offset > 0)
std::memmove(m_buffer + offset + length, m_buffer + offset, chars_after_offset);
// insert the string
std::memcpy(m_buffer + real_offset, str, length);
m_length += length;
// ensure null termination
m_buffer[m_length] = 0;
}
void SmallStringBase::insert(s32 offset, const std::string& str)
{
insert(offset, str.c_str(), static_cast<u32>(str.size()));
}
void SmallStringBase::insert(s32 offset, const std::string_view& str)
{
insert(offset, str.data(), static_cast<u32>(str.size()));
}
void SmallStringBase::format(const char* format, ...)
{
va_list ap;
va_start(ap, format);
format_va(format, ap);
va_end(ap);
}
void SmallStringBase::format_va(const char* format, va_list ap)
{
clear();
append_format_va(format, ap);
}
void SmallStringBase::assign(const SmallStringBase& copy)
{
assign(copy.c_str(), copy.length());
}
void SmallStringBase::assign(const char* str)
{
assign(str, static_cast<u32>(std::strlen(str)));
}
void SmallStringBase::assign(const char* str, u32 length)
{
clear();
if (length > 0)
append(str, length);
}
void SmallStringBase::assign(SmallStringBase&& move)
{
if (move.m_on_heap)
{
if (m_on_heap)
std::free(m_buffer);
m_buffer = move.m_buffer;
m_buffer_size = move.m_buffer_size;
m_length = move.m_length;
m_on_heap = true;
move.m_buffer = nullptr;
move.m_buffer_size = 0;
move.m_length = 0;
}
else
{
assign(move.m_buffer, move.m_buffer_size);
}
}
void SmallStringBase::assign(const std::string& str)
{
clear();
append(str.data(), static_cast<u32>(str.size()));
}
void SmallStringBase::assign(const std::string_view& str)
{
clear();
append(str.data(), static_cast<u32>(str.size()));
}
bool SmallStringBase::equals(const char* str) const
{
if (m_length == 0)
return (std::strlen(str) == 0);
else
return (std::strcmp(m_buffer, str) == 0);
}
bool SmallStringBase::equals(const SmallStringBase& str) const
{
return (m_length == str.m_length && (m_length == 0 || std::strcmp(m_buffer, str.m_buffer) == 0));
}
bool SmallStringBase::equals(const std::string_view& str) const
{
return (m_length == static_cast<u32>(str.length()) &&
(m_length == 0 || CASE_N_COMPARE(m_buffer, str.data(), m_length) == 0));
}
bool SmallStringBase::iequals(const char* otherText) const
{
if (m_length == 0)
return (std::strlen(otherText) == 0);
else
return (CASE_COMPARE(m_buffer, otherText) == 0);
}
bool SmallStringBase::iequals(const SmallStringBase& str) const
{
return (m_length == str.m_length && (m_length == 0 || std::strcmp(m_buffer, str.m_buffer) == 0));
}
bool SmallStringBase::iequals(const std::string_view& str) const
{
return (m_length == static_cast<u32>(str.length()) &&
(m_length == 0 || CASE_N_COMPARE(m_buffer, str.data(), m_length) == 0));
}
int SmallStringBase::compare(const SmallStringBase& str) const
{
return std::strcmp(m_buffer, str.m_buffer);
}
int SmallStringBase::compare(const char* otherText) const
{
return std::strcmp(m_buffer, otherText);
}
int SmallStringBase::icompare(const SmallStringBase& otherString) const
{
return CASE_COMPARE(m_buffer, otherString.m_buffer);
}
int SmallStringBase::icompare(const char* otherText) const
{
return CASE_COMPARE(m_buffer, otherText);
}
bool SmallStringBase::starts_with(const char* str, bool case_sensitive) const
{
const u32 other_length = static_cast<u32>(std::strlen(str));
if (other_length > m_length)
return false;
return (case_sensitive) ? (std::strncmp(str, m_buffer, other_length) == 0) :
(CASE_N_COMPARE(str, m_buffer, other_length) == 0);
}
bool SmallStringBase::starts_with(const SmallStringBase& str, bool case_sensitive) const
{
const u32 other_length = str.m_length;
if (other_length > m_length)
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);
}
bool SmallStringBase::starts_with(const std::string_view& str, bool case_sensitive) const
{
const u32 other_length = static_cast<u32>(str.length());
if (other_length > m_length)
return false;
return (case_sensitive) ? (std::strncmp(str.data(), m_buffer, other_length) == 0) :
(CASE_N_COMPARE(str.data(), m_buffer, other_length) == 0);
}
bool SmallStringBase::ends_with(const char* str, bool case_sensitive) const
{
const u32 other_length = static_cast<u32>(std::strlen(str));
if (other_length > m_length)
return false;
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);
}
bool SmallStringBase::ends_with(const SmallStringBase& str, bool case_sensitive) const
{
const u32 other_length = str.m_length;
if (other_length > m_length)
return false;
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);
}
bool SmallStringBase::ends_with(const std::string_view& str, bool case_sensitive) const
{
const u32 other_length = static_cast<u32>(str.length());
if (other_length > m_length)
return false;
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);
}
void SmallStringBase::clear()
{
// in debug, zero whole string, in release, zero only the first character
#if _DEBUG
std::memset(m_buffer, 0, m_buffer_size);
#else
m_buffer[0] = '\0';
#endif
m_length = 0;
}
s32 SmallStringBase::find(char c, u32 offset) const
{
if (m_length == 0)
return -1;
pxAssert(offset <= m_length);
const char* at = std::strchr(m_buffer + offset, c);
return at ? static_cast<s32>(at - m_buffer) : -1;
}
s32 SmallStringBase::rfind(char c, u32 offset) const
{
if (m_length == 0)
return -1;
pxAssert(offset <= m_length);
const char* at = std::strrchr(m_buffer + offset, c);
return at ? static_cast<s32>(at - m_buffer) : -1;
}
s32 SmallStringBase::find(const char* str, u32 offset) const
{
if (m_length == 0)
return -1;
pxAssert(offset <= m_length);
const char* at = std::strstr(m_buffer + offset, str);
return at ? static_cast<s32>(at - m_buffer) : -1;
}
void SmallStringBase::resize(u32 new_size, char fill, bool shrink_if_smaller)
{
// if going larger, or we don't own the buffer, realloc
if (new_size >= m_buffer_size)
{
reserve(new_size);
if (m_length < new_size)
{
std::memset(m_buffer + m_length, fill, m_buffer_size - m_length - 1);
}
m_length = new_size;
}
else
{
// update length and terminator
#if _DEBUG
std::memset(m_buffer + new_size, 0, m_buffer_size - new_size);
#else
m_buffer[new_size] = 0;
#endif
m_length = new_size;
// shrink if requested
if (shrink_if_smaller)
shrink_to_fit();
}
}
void SmallStringBase::update_size()
{
m_length = static_cast<u32>(std::strlen(m_buffer));
}
std::string_view SmallStringBase::substr(s32 offset, s32 count) const
{
// calc real offset
u32 real_offset;
if (offset < 0)
real_offset = static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length + offset)));
else
real_offset = std::min((u32)offset, m_length);
// calc real count
u32 real_count;
if (count < 0)
{
real_count =
std::min(m_length - real_offset, static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length) + count)));
}
else
{
real_count = std::min(m_length - real_offset, static_cast<u32>(count));
}
return (real_count > 0) ? std::string_view(m_buffer + real_offset, real_count) : std::string_view();
}
void SmallStringBase::erase(s32 offset, s32 count)
{
// calc real offset
u32 real_offset;
if (offset < 0)
real_offset = static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length + offset)));
else
real_offset = std::min((u32)offset, m_length);
// calc real count
u32 real_count;
if (count < 0)
{
real_count =
std::min(m_length - real_offset, static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length) + count)));
}
else
{
real_count = std::min(m_length - real_offset, static_cast<u32>(count));
}
// Fastpath: offset == 0, count < 0, wipe whole string.
if (real_offset == 0 && real_count == m_length)
{
clear();
return;
}
// Fastpath: offset >= 0, count < 0, wipe everything after offset + count
if ((real_offset + real_count) == m_length)
{
m_length -= real_count;
#ifdef _DEBUG
std::memset(m_buffer + m_length, 0, m_buffer_size - m_length);
#else
m_buffer[m_length] = 0;
#endif
}
// Slowpath: offset >= 0, count < length
else
{
const u32 after_erase_block = m_length - real_offset - real_count;
pxAssert(after_erase_block > 0);
std::memmove(m_buffer + offset, m_buffer + real_offset + real_count, after_erase_block);
m_length = m_length - real_count;
#ifdef _DEBUG
std::memset(m_buffer + m_length, 0, m_buffer_size - m_length);
#else
m_buffer[m_length] = 0;
#endif
}
}

320
common/SmallString.h Normal file
View File

@ -0,0 +1,320 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Pcsx2Defs.h"
#include "fmt/core.h"
#include <algorithm>
#include <cstdarg>
#include <cstring>
#include <limits>
#include <string>
#include <string_view>
//
// SmallString
// Lightweight string class which can be allocated on the stack, instead of with heap allocations.
//
class SmallStringBase
{
public:
using value_type = char;
SmallStringBase();
SmallStringBase(const char* str);
SmallStringBase(const char* str, u32 length);
SmallStringBase(const SmallStringBase& copy);
SmallStringBase(SmallStringBase&& move);
SmallStringBase(const std::string& str);
SmallStringBase(const std::string_view& sv);
// Destructor. Child classes may not have any destructors, as this is not virtual.
~SmallStringBase();
// manual assignment
void assign(const char* str);
void assign(const char* str, u32 length);
void assign(const std::string& copy);
void assign(const std::string_view& copy);
void assign(const SmallStringBase& copy);
void assign(SmallStringBase&& move);
// Ensures that we have space bytes free in the buffer.
void make_room_for(u32 space);
// clears the contents of the string
void clear();
// append a single character to this string
void append(char c);
// append a string to this string
void append(const char* appendText);
void append(const char* str, u32 length);
void append(const std::string& str);
void append(const std::string_view& str);
void append(const SmallStringBase& str);
// append formatted string to this string
void append_format(const char* format, ...) /*printflike(2, 3)*/;
void append_format_va(const char* format, va_list ap);
template <typename... T>
void append_fmt(fmt::format_string<T...> fmt, T&&... args);
// append a single character to this string
void prepend(char c);
// append a string to this string
void prepend(const char* str);
void prepend(const char* str, u32 length);
void prepend(const std::string& str);
void prepend(const std::string_view& str);
void prepend(const SmallStringBase& str);
// append formatted string to this string
void prepend_format(const char* format, ...) /*printflike(2, 3)*/;
void prepend_format_va(const char* format, va_list ap);
template <typename... T>
void prepend_fmt(fmt::format_string<T...> fmt, T&&... args);
// insert a string at the specified offset
void insert(s32 offset, const char* str);
void insert(s32 offset, const char* str, u32 length);
void insert(s32 offset, const std::string& str);
void insert(s32 offset, const std::string_view& str);
void insert(s32 offset, const SmallStringBase& str);
// set to formatted string
void format(const char* format, ...) /*printflike(2, 3)*/;
void format_va(const char* format, va_list ap);
template <typename... T>
void fmt(fmt::format_string<T...> fmt, T&&... args);
// compare one string to another
bool equals(const char* str) const;
bool equals(const SmallStringBase& str) const;
bool equals(const std::string_view& str) const;
bool iequals(const char* str) const;
bool iequals(const SmallStringBase& str) const;
bool iequals(const std::string_view& str) const;
// numerical compares
int compare(const char* str) const;
int compare(const SmallStringBase& str) const;
int compare(const std::string_view& str) const;
int icompare(const char* str) const;
int icompare(const SmallStringBase& str) const;
int icompare(const std::string_view& str) const;
// starts with / ends with
bool starts_with(const char* str, bool case_sensitive = true) const;
bool starts_with(const std::string_view& str, bool case_sensitive = true) const;
bool starts_with(const SmallStringBase& str, bool case_sensitive = true) const;
bool ends_with(const char* str, bool case_sensitive = true) const;
bool ends_with(const std::string_view& str, bool case_sensitive = true) const;
bool ends_with(const SmallStringBase& str, bool case_sensitive = true) const;
// searches for a character inside a string
// rfind is the same except it starts at the end instead of the start
// returns -1 if it is not found, otherwise the offset in the string
s32 find(char c, u32 offset = 0) const;
s32 rfind(char c, u32 offset = 0) const;
// searches for a string inside a string
// rfind is the same except it starts at the end instead of the start
// returns -1 if it is not found, otherwise the offset in the string
s32 find(const char* str, u32 offset = 0) const;
// removes characters from string
void erase(s32 offset, s32 count = std::numeric_limits<s32>::max());
// alters the length of the string to be at least len bytes long
void reserve(u32 new_reserve);
// Cuts characters off the string to reduce it to len bytes long.
void resize(u32 new_size, char fill = ' ', bool shrink_if_smaller = false);
// updates the internal length counter when the string is externally modified
void update_size();
// shrink the string to the minimum size possible
void shrink_to_fit();
// gets the size of the string
__fi u32 length() const { return m_length; }
__fi bool empty() const { return (m_length == 0); }
// gets the maximum number of bytes we can write to the string, currently
__fi u32 buffer_size() const { return m_buffer_size; }
// gets a constant pointer to the C string
__fi const char* c_str() const { return m_buffer; }
// gets a writable char array, do not write more than reserve characters to it.
__fi char* data() { return m_buffer; }
// STL adapters
__fi void push_back(value_type&& val) { append(val); }
// returns a string view for this string
std::string_view view() const;
// returns a substring view for this string
std::string_view substr(s32 offset, s32 count) const;
// accessor operators
__fi operator const char*() const { return c_str(); }
__fi operator char*() { return data(); }
__fi operator std::string_view() const { return view(); }
// comparative operators
__fi bool operator==(const char* str) const { return equals(str); }
__fi bool operator==(const SmallStringBase& str) const { return equals(str); }
__fi bool operator==(const std::string_view& str) const { return equals(str); }
__fi bool operator!=(const char* str) const { return !equals(str); }
__fi bool operator!=(const SmallStringBase& str) const { return !equals(str); }
__fi bool operator!=(const std::string_view& str) const { return !equals(str); }
__fi bool operator<(const char* str) const { return (compare(str) < 0); }
__fi bool operator<(const SmallStringBase& str) const { return (compare(str) < 0); }
__fi bool operator<(const std::string_view& str) const { return (compare(str) < 0); }
__fi bool operator>(const char* str) const { return (compare(str) > 0); }
__fi bool operator>(const SmallStringBase& str) const { return (compare(str) > 0); }
__fi bool operator>(const std::string_view& str) const { return (compare(str) > 0); }
SmallStringBase& operator=(const SmallStringBase& copy);
SmallStringBase& operator=(const char* str);
SmallStringBase& operator=(const std::string& str);
SmallStringBase& operator=(const std::string_view& str);
SmallStringBase& operator=(SmallStringBase&& move);
protected:
// Pointer to memory where the string is located
char* m_buffer = nullptr;
// Length of the string located in pBuffer (in characters)
u32 m_length = 0;
// Size of the buffer pointed to by pBuffer
u32 m_buffer_size = 0;
// True if the string is dynamically allocated on the heap.
bool m_on_heap = false;
};
// stack-allocated string
template <u32 L>
class SmallStackString : public SmallStringBase
{
public:
__fi SmallStackString() { init(); }
__fi SmallStackString(const char* str)
{
init();
assign(str);
}
__fi SmallStackString(const char* str, u32 length)
{
init();
assign(str, length);
}
__fi SmallStackString(const SmallStringBase& copy)
{
init();
assign(copy);
}
__fi SmallStackString(SmallStringBase&& move)
{
init();
assign(move);
}
__fi SmallStackString(const std::string_view& sv)
{
init();
assign(sv);
}
// Override the fromstring method
__fi static SmallStackString from_format(const char* format, ...) /*printflike(1, 2)*/
{
std::va_list ap;
va_start(ap, format);
SmallStackString ret;
ret.format_va(format, ap);
va_end(ap);
return ret;
}
template <typename... T>
__fi static SmallStackString from_fmt(fmt::format_string<T...> fmt, T&&... args)
{
SmallStackString ret;
fmt::vformat_to(std::back_inserter(ret), fmt, fmt::make_format_args(args...));
return ret;
}
private:
char m_stack_buffer[L + 1];
__fi void init()
{
m_buffer = m_stack_buffer;
m_buffer_size = L + 1;
#ifdef _DEBUG
std::memset(m_stack_buffer, 0, sizeof(m_stack_buffer));
#else
m_stack_buffer[0] = '\0';
#endif
}
};
// stack string types
typedef SmallStackString<64> TinyString;
typedef SmallStackString<256> SmallString;
template <typename... T>
__fi void SmallStringBase::append_fmt(fmt::format_string<T...> fmt, T&&... args)
{
fmt::vformat_to(std::back_inserter(*this), fmt, fmt::make_format_args(args...));
}
template <typename... T>
__fi void SmallStringBase::prepend_fmt(fmt::format_string<T...> fmt, T&&... args)
{
TinyString str;
fmt::vformat_to(std::back_inserter(str), fmt, fmt::make_format_args(args...));
prepend(str);
}
template <typename... T>
__fi void SmallStringBase::fmt(fmt::format_string<T...> fmt, T&&... args)
{
clear();
fmt::vformat_to(std::back_inserter(*this), fmt, fmt::make_format_args(args...));
}

View File

@ -65,6 +65,7 @@
<ClCompile Include="MemorySettingsInterface.cpp" />
<ClCompile Include="ProgressCallback.cpp" />
<ClCompile Include="ReadbackSpinManager.cpp" />
<ClCompile Include="SmallString.cpp" />
<ClCompile Include="StackWalker.cpp" />
<ClCompile Include="StringUtil.cpp" />
<ClCompile Include="SettingsWrapper.cpp" />
@ -126,6 +127,7 @@
<ClInclude Include="MemorySettingsInterface.h" />
<ClInclude Include="ProgressCallback.h" />
<ClInclude Include="ScopedGuard.h" />
<ClInclude Include="SmallString.h" />
<ClInclude Include="StackWalker.h" />
<ClInclude Include="StringUtil.h" />
<ClInclude Include="SettingsInterface.h" />

View File

@ -145,6 +145,9 @@
<ClCompile Include="TextureDecompress.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SmallString.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="AlignedMalloc.h">
@ -354,6 +357,9 @@
<ClInclude Include="TextureDecompress.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SmallString.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
@ -368,4 +374,4 @@
<Filter>Source Files</Filter>
</MASM>
</ItemGroup>
</Project>
</Project>