// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team // SPDX-License-Identifier: GPL-3.0+ #pragma once #include "Pcsx2Defs.h" #include "fmt/core.h" #include #include #include #include #include #include // // 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_sprintf(const char* format, ...) /*printflike(2, 3)*/; void append_vsprintf(const char* format, va_list ap); template void append_format(fmt::format_string fmt, T&&... args); // append hex string void append_hex(const void* data, size_t len); // 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_sprintf(const char* format, ...) /*printflike(2, 3)*/; void prepend_vsprintf(const char* format, va_list ap); template void prepend_format(fmt::format_string 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 sprintf(const char* format, ...) /*printflike(2, 3)*/; void vsprintf(const char* format, va_list ap); template void format(fmt::format_string fmt, T&&... args); void vformat(fmt::string_view fmt, fmt::format_args 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 equals(const std::string& str) const; bool iequals(const char* str) const; bool iequals(const SmallStringBase& str) const; bool iequals(const std::string_view str) const; bool iequals(const std::string& 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 compare(const std::string& str) const; int icompare(const char* str) const; int icompare(const SmallStringBase& str) const; int icompare(const std::string_view str) const; int icompare(const std::string& str) const; // starts with / ends with bool starts_with(const char* str, bool case_sensitive = true) const; bool starts_with(const SmallStringBase& str, bool case_sensitive = true) const; bool starts_with(const std::string_view str, bool case_sensitive = true) const; bool starts_with(const std::string& str, bool case_sensitive = true) const; bool ends_with(const char* str, bool case_sensitive = true) const; bool ends_with(const SmallStringBase& str, bool case_sensitive = true) const; bool ends_with(const std::string_view str, bool case_sensitive = true) const; bool ends_with(const std::string& 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; // returns the number of instances of the specified character u32 count(char ch) const; // removes characters from string void erase(s32 offset, s32 count = std::numeric_limits::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; } // returns the end of the string (pointer is past the last character) __fi const char* end_ptr() const { return m_buffer + m_length; } // 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 std::string& 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 std::string& 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 std::string& 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); } __fi bool operator>(const std::string& 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 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 SmallStackString& copy) { init(); assign(copy); } __fi SmallStackString(SmallStackString&& move) { init(); assign(move); } __fi SmallStackString(const std::string_view sv) { init(); assign(sv); } __fi SmallStackString& operator=(const SmallStringBase& copy) { assign(copy); return *this; } __fi SmallStackString& operator=(SmallStringBase&& move) { assign(move); return *this; } __fi SmallStackString& operator=(const SmallStackString& copy) { assign(copy); return *this; } __fi SmallStackString& operator=(SmallStackString&& move) { assign(move); return *this; } __fi SmallStackString& operator=(const std::string_view sv) { assign(sv); return *this; } __fi SmallStackString& operator=(const char* str) { assign(str); return *this; } // Override the fromstring method static SmallStackString from_sprintf(const char* format, ...) /*printflike(1, 2)*/; template static SmallStackString from_format(fmt::format_string fmt, T&&... args); static SmallStackString from_vformat(fmt::string_view fmt, fmt::format_args args); 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 } }; #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4459) // warning C4459: declaration of 'uint' hides global declaration #endif template SmallStackString SmallStackString::from_sprintf(const char* format, ...) { std::va_list ap; va_start(ap, format); SmallStackString ret; ret.vsprintf(format, ap); va_end(ap); return ret; } template template __fi SmallStackString SmallStackString::from_format(fmt::format_string fmt, T&&... args) { SmallStackString ret; fmt::vformat_to(std::back_inserter(ret), fmt, fmt::make_format_args(args...)); return ret; } template __fi SmallStackString SmallStackString::from_vformat(fmt::string_view fmt, fmt::format_args args) { SmallStackString ret; fmt::vformat_to(std::back_inserter(ret), fmt, args); return ret; } // stack string types using TinyString = SmallStackString<64>; using SmallString = SmallStackString<256>; template __fi void SmallStringBase::append_format(fmt::format_string fmt, T&&... args) { fmt::vformat_to(std::back_inserter(*this), fmt, fmt::make_format_args(args...)); } template __fi void SmallStringBase::prepend_format(fmt::format_string fmt, T&&... args) { TinyString str; fmt::vformat_to(std::back_inserter(str), fmt, fmt::make_format_args(args...)); prepend(str); } template __fi void SmallStringBase::format(fmt::format_string fmt, T&&... args) { clear(); fmt::vformat_to(std::back_inserter(*this), fmt, fmt::make_format_args(args...)); } #ifdef _MSC_VER #pragma warning(pop) #endif #define MAKE_FORMATTER(type) \ template <> \ struct fmt::formatter \ { \ template \ constexpr auto parse(ParseContext& ctx) \ { \ return ctx.begin(); \ } \ \ template \ auto format(const type& str, FormatContext& ctx) \ { \ return fmt::format_to(ctx.out(), "{}", str.view()); \ } \ }; MAKE_FORMATTER(TinyString); MAKE_FORMATTER(SmallString); #undef MAKE_FORMATTER