duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

small_string.h (15265B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
      3 
      4 #pragma once
      5 #include "types.h"
      6 
      7 #include "fmt/core.h"
      8 
      9 #include <algorithm>
     10 #include <cstdarg>
     11 #include <cstring>
     12 #include <limits>
     13 #include <string>
     14 #include <string_view>
     15 
     16 //
     17 // SmallString
     18 // Lightweight string class which can be allocated on the stack, instead of with heap allocations.
     19 //
     20 class SmallStringBase
     21 {
     22 public:
     23   using value_type = char;
     24 
     25   SmallStringBase();
     26   SmallStringBase(const char* str);
     27   SmallStringBase(const char* str, u32 length);
     28   SmallStringBase(const SmallStringBase& copy);
     29   SmallStringBase(SmallStringBase&& move);
     30   SmallStringBase(const std::string& str);
     31   SmallStringBase(const std::string_view sv);
     32 
     33   // Destructor. Child classes may not have any destructors, as this is not virtual.
     34   ~SmallStringBase();
     35 
     36   // manual assignment
     37   void assign(const char* str);
     38   void assign(const char* str, u32 length);
     39   void assign(const std::string& copy);
     40   void assign(const std::string_view copy);
     41   void assign(const SmallStringBase& copy);
     42   void assign(SmallStringBase&& move);
     43 
     44   // Ensures that we have space bytes free in the buffer.
     45   void make_room_for(u32 space);
     46 
     47   // clears the contents of the string
     48   void clear();
     49 
     50   // append a single character to this string
     51   void append(char c);
     52 
     53   // append a string to this string
     54   void append(const char* appendText);
     55   void append(const char* str, u32 length);
     56   void append(const std::string& str);
     57   void append(const std::string_view str);
     58   void append(const SmallStringBase& str);
     59 
     60   // append formatted string to this string
     61   void append_sprintf(const char* format, ...) printflike(2, 3);
     62   void append_vsprintf(const char* format, va_list ap);
     63 
     64   template<typename... T>
     65   void append_format(fmt::format_string<T...> fmt, T&&... args);
     66 
     67   // append hex string
     68   void append_hex(const void* data, size_t len);
     69 
     70   // append a single character to this string
     71   void prepend(char c);
     72 
     73   // append a string to this string
     74   void prepend(const char* str);
     75   void prepend(const char* str, u32 length);
     76   void prepend(const std::string& str);
     77   void prepend(const std::string_view str);
     78   void prepend(const SmallStringBase& str);
     79 
     80   // append formatted string to this string
     81   void prepend_sprintf(const char* format, ...) printflike(2, 3);
     82   void prepend_vsprintf(const char* format, va_list ap);
     83 
     84   template<typename... T>
     85   void prepend_format(fmt::format_string<T...> fmt, T&&... args);
     86 
     87   // insert a string at the specified offset
     88   void insert(s32 offset, const char* str);
     89   void insert(s32 offset, const char* str, u32 length);
     90   void insert(s32 offset, const std::string& str);
     91   void insert(s32 offset, const std::string_view str);
     92   void insert(s32 offset, const SmallStringBase& str);
     93 
     94   // set to formatted string
     95   void sprintf(const char* format, ...) printflike(2, 3);
     96   void vsprintf(const char* format, va_list ap);
     97 
     98   template<typename... T>
     99   void format(fmt::format_string<T...> fmt, T&&... args);
    100 
    101   void vformat(fmt::string_view fmt, fmt::format_args args);
    102 
    103   // compare one string to another
    104   bool equals(const char* str) const;
    105   bool equals(const SmallStringBase& str) const;
    106   bool equals(const std::string_view str) const;
    107   bool equals(const std::string& str) const;
    108   bool iequals(const char* str) const;
    109   bool iequals(const SmallStringBase& str) const;
    110   bool iequals(const std::string_view str) const;
    111   bool iequals(const std::string& str) const;
    112 
    113   // numerical compares
    114   int compare(const char* str) const;
    115   int compare(const SmallStringBase& str) const;
    116   int compare(const std::string_view str) const;
    117   int compare(const std::string& str) const;
    118   int icompare(const char* str) const;
    119   int icompare(const SmallStringBase& str) const;
    120   int icompare(const std::string_view str) const;
    121   int icompare(const std::string& str) const;
    122 
    123   // starts with / ends with
    124   bool starts_with(const char* str, bool case_sensitive = true) const;
    125   bool starts_with(const SmallStringBase& str, bool case_sensitive = true) const;
    126   bool starts_with(const std::string_view str, bool case_sensitive = true) const;
    127   bool starts_with(const std::string& str, bool case_sensitive = true) const;
    128   bool ends_with(const char* str, bool case_sensitive = true) const;
    129   bool ends_with(const SmallStringBase& str, bool case_sensitive = true) const;
    130   bool ends_with(const std::string_view str, bool case_sensitive = true) const;
    131   bool ends_with(const std::string& str, bool case_sensitive = true) const;
    132 
    133   // searches for a character inside a string
    134   // rfind is the same except it starts at the end instead of the start
    135   // returns -1 if it is not found, otherwise the offset in the string
    136   s32 find(char c, u32 offset = 0) const;
    137   s32 rfind(char c, u32 offset = 0) const;
    138 
    139   // searches for a string inside a string
    140   // rfind is the same except it starts at the end instead of the start
    141   // returns -1 if it is not found, otherwise the offset in the string
    142   s32 find(const char* str, u32 offset = 0) const;
    143 
    144   // returns the number of instances of the specified character
    145   u32 count(char ch) const;
    146 
    147   // replaces search string with replacement, returns the number of replacements made
    148   u32 replace(const char* search, const char* replacement);
    149 
    150   // removes characters from string
    151   void erase(s32 offset, s32 count = std::numeric_limits<s32>::max());
    152 
    153   // alters the length of the string to be at least len bytes long
    154   void reserve(u32 new_reserve);
    155 
    156   // Cuts characters off the string to reduce it to len bytes long.
    157   void resize(u32 new_size, char fill = ' ', bool shrink_if_smaller = false);
    158 
    159   // updates the internal length counter when the string is externally modified
    160   void update_size();
    161 
    162   // shrink the string to the minimum size possible
    163   void shrink_to_fit();
    164 
    165   // gets the size of the string
    166   ALWAYS_INLINE u32 length() const { return m_length; }
    167   ALWAYS_INLINE bool empty() const { return (m_length == 0); }
    168 
    169   // gets the maximum number of bytes we can write to the string, currently
    170   ALWAYS_INLINE u32 buffer_size() const { return m_buffer_size; }
    171 
    172   // gets a constant pointer to the C string
    173   ALWAYS_INLINE const char* c_str() const { return m_buffer; }
    174 
    175   // gets a writable char array, do not write more than reserve characters to it.
    176   ALWAYS_INLINE char* data() { return m_buffer; }
    177 
    178   // returns the end of the string (pointer is past the last character)
    179   ALWAYS_INLINE const char* end_ptr() const { return m_buffer + m_length; }
    180 
    181   // STL adapters
    182   ALWAYS_INLINE char& front() { return m_buffer[0]; }
    183   ALWAYS_INLINE const char& front() const { return m_buffer[0]; }
    184   ALWAYS_INLINE char& back() { return m_buffer[m_length - 1]; }
    185   ALWAYS_INLINE const char& back() const { return m_buffer[m_length - 1]; }
    186   ALWAYS_INLINE void push_back(value_type&& val) { append(val); }
    187   ALWAYS_INLINE void pop_back() { erase(-1); }
    188 
    189   // returns a string view for this string
    190   std::string_view view() const;
    191 
    192   // returns a substring view for this string
    193   std::string_view substr(s32 offset, s32 count) const;
    194 
    195 #ifdef _WIN32
    196   // wide string adapters, win32 only
    197   void assign(const std::wstring_view wstr);
    198   std::wstring wstring() const;
    199 #endif
    200 
    201   // accessor operators
    202   ALWAYS_INLINE operator const char*() const { return c_str(); }
    203   ALWAYS_INLINE operator char*() { return data(); }
    204   ALWAYS_INLINE operator std::string_view() const { return view(); }
    205 
    206   // comparative operators
    207   ALWAYS_INLINE bool operator==(const char* str) const { return equals(str); }
    208   ALWAYS_INLINE bool operator==(const SmallStringBase& str) const { return equals(str); }
    209   ALWAYS_INLINE bool operator==(const std::string_view str) const { return equals(str); }
    210   ALWAYS_INLINE bool operator==(const std::string& str) const { return equals(str); }
    211   ALWAYS_INLINE bool operator!=(const char* str) const { return !equals(str); }
    212   ALWAYS_INLINE bool operator!=(const SmallStringBase& str) const { return !equals(str); }
    213   ALWAYS_INLINE bool operator!=(const std::string_view str) const { return !equals(str); }
    214   ALWAYS_INLINE bool operator!=(const std::string& str) const { return !equals(str); }
    215   ALWAYS_INLINE bool operator<(const char* str) const { return (compare(str) < 0); }
    216   ALWAYS_INLINE bool operator<(const SmallStringBase& str) const { return (compare(str) < 0); }
    217   ALWAYS_INLINE bool operator<(const std::string_view str) const { return (compare(str) < 0); }
    218   ALWAYS_INLINE bool operator<(const std::string& str) const { return (compare(str) < 0); }
    219   ALWAYS_INLINE bool operator>(const char* str) const { return (compare(str) > 0); }
    220   ALWAYS_INLINE bool operator>(const SmallStringBase& str) const { return (compare(str) > 0); }
    221   ALWAYS_INLINE bool operator>(const std::string_view str) const { return (compare(str) > 0); }
    222   ALWAYS_INLINE bool operator>(const std::string& str) const { return (compare(str) > 0); }
    223 
    224   SmallStringBase& operator=(const SmallStringBase& copy);
    225   SmallStringBase& operator=(const char* str);
    226   SmallStringBase& operator=(const std::string& str);
    227   SmallStringBase& operator=(const std::string_view str);
    228   SmallStringBase& operator=(SmallStringBase&& move);
    229 
    230 protected:
    231   // Pointer to memory where the string is located
    232   char* m_buffer = nullptr;
    233 
    234   // Length of the string located in pBuffer (in characters)
    235   u32 m_length = 0;
    236 
    237   // Size of the buffer pointed to by pBuffer
    238   u32 m_buffer_size = 0;
    239 
    240   // True if the string is dynamically allocated on the heap.
    241   bool m_on_heap = false;
    242 };
    243 
    244 // stack-allocated string
    245 template<u32 L>
    246 class SmallStackString : public SmallStringBase
    247 {
    248 public:
    249   ALWAYS_INLINE SmallStackString() { init(); }
    250 
    251   ALWAYS_INLINE SmallStackString(const char* str)
    252   {
    253     init();
    254     assign(str);
    255   }
    256 
    257   ALWAYS_INLINE SmallStackString(const char* str, u32 length)
    258   {
    259     init();
    260     assign(str, length);
    261   }
    262 
    263   ALWAYS_INLINE SmallStackString(const SmallStringBase& copy)
    264   {
    265     init();
    266     assign(copy);
    267   }
    268 
    269   ALWAYS_INLINE SmallStackString(SmallStringBase&& move)
    270   {
    271     init();
    272     assign(move);
    273   }
    274 
    275   ALWAYS_INLINE SmallStackString(const SmallStackString& copy)
    276   {
    277     init();
    278     assign(copy);
    279   }
    280 
    281   ALWAYS_INLINE SmallStackString(SmallStackString&& move)
    282   {
    283     init();
    284     assign(move);
    285   }
    286 
    287   ALWAYS_INLINE SmallStackString(const std::string_view sv)
    288   {
    289     init();
    290     assign(sv);
    291   }
    292 
    293   ALWAYS_INLINE SmallStackString& operator=(const SmallStringBase& copy)
    294   {
    295     assign(copy);
    296     return *this;
    297   }
    298 
    299   ALWAYS_INLINE SmallStackString& operator=(SmallStringBase&& move)
    300   {
    301     assign(move);
    302     return *this;
    303   }
    304 
    305   ALWAYS_INLINE SmallStackString& operator=(const SmallStackString& copy)
    306   {
    307     assign(copy);
    308     return *this;
    309   }
    310 
    311   ALWAYS_INLINE SmallStackString& operator=(SmallStackString&& move)
    312   {
    313     assign(move);
    314     return *this;
    315   }
    316 
    317   ALWAYS_INLINE SmallStackString& operator=(const std::string_view sv)
    318   {
    319     assign(sv);
    320     return *this;
    321   }
    322 
    323   ALWAYS_INLINE SmallStackString& operator=(const char* str)
    324   {
    325     assign(str);
    326     return *this;
    327   }
    328 
    329   // Override the fromstring method
    330   static SmallStackString from_sprintf(const char* format, ...) printflike(1, 2);
    331 
    332   template<typename... T>
    333   static SmallStackString from_format(fmt::format_string<T...> fmt, T&&... args);
    334 
    335   static SmallStackString from_vformat(fmt::string_view fmt, fmt::format_args args);
    336 
    337 private:
    338   char m_stack_buffer[L + 1];
    339 
    340   ALWAYS_INLINE void init()
    341   {
    342     m_buffer = m_stack_buffer;
    343     m_buffer_size = L + 1;
    344 
    345 #ifdef _DEBUG
    346     std::memset(m_stack_buffer, 0, sizeof(m_stack_buffer));
    347 #else
    348     m_stack_buffer[0] = '\0';
    349 #endif
    350   }
    351 };
    352 
    353 #ifdef _MSC_VER
    354 #pragma warning(push)
    355 #pragma warning(disable : 4459) // warning C4459: declaration of 'uint' hides global declaration
    356 #endif
    357 
    358 template<u32 L>
    359 SmallStackString<L> SmallStackString<L>::from_sprintf(const char* format, ...)
    360 {
    361   std::va_list ap;
    362   va_start(ap, format);
    363 
    364   SmallStackString ret;
    365   ret.vsprintf(format, ap);
    366 
    367   va_end(ap);
    368 
    369   return ret;
    370 }
    371 
    372 template<u32 L>
    373 template<typename... T>
    374 ALWAYS_INLINE SmallStackString<L> SmallStackString<L>::from_format(fmt::format_string<T...> fmt, T&&... args)
    375 {
    376   SmallStackString<L> ret;
    377   fmt::vformat_to(std::back_inserter(ret), fmt, fmt::make_format_args(args...));
    378   return ret;
    379 }
    380 
    381 template<u32 L>
    382 ALWAYS_INLINE SmallStackString<L> SmallStackString<L>::from_vformat(fmt::string_view fmt, fmt::format_args args)
    383 {
    384   SmallStackString<L> ret;
    385   fmt::vformat_to(std::back_inserter(ret), fmt, args);
    386   return ret;
    387 }
    388 
    389 // stack string types
    390 using TinyString = SmallStackString<64>;
    391 using SmallString = SmallStackString<256>;
    392 using LargeString = SmallStackString<512>;
    393 
    394 template<typename... T>
    395 ALWAYS_INLINE void SmallStringBase::append_format(fmt::format_string<T...> fmt, T&&... args)
    396 {
    397   fmt::vformat_to(std::back_inserter(*this), fmt, fmt::make_format_args(args...));
    398 }
    399 
    400 template<typename... T>
    401 ALWAYS_INLINE void SmallStringBase::prepend_format(fmt::format_string<T...> fmt, T&&... args)
    402 {
    403   TinyString str;
    404   fmt::vformat_to(std::back_inserter(str), fmt, fmt::make_format_args(args...));
    405   prepend(str);
    406 }
    407 
    408 template<typename... T>
    409 ALWAYS_INLINE void SmallStringBase::format(fmt::format_string<T...> fmt, T&&... args)
    410 {
    411   clear();
    412   fmt::vformat_to(std::back_inserter(*this), fmt, fmt::make_format_args(args...));
    413 }
    414 
    415 #ifdef _MSC_VER
    416 #pragma warning(pop)
    417 #endif
    418 
    419 #define MAKE_FORMATTER(type)                                                                                           \
    420   template<>                                                                                                           \
    421   struct fmt::formatter<type>                                                                                          \
    422   {                                                                                                                    \
    423     template<typename ParseContext>                                                                                    \
    424     constexpr auto parse(ParseContext& ctx)                                                                            \
    425     {                                                                                                                  \
    426       return ctx.begin();                                                                                              \
    427     }                                                                                                                  \
    428                                                                                                                        \
    429     template<typename FormatContext>                                                                                   \
    430     auto format(const type& str, FormatContext& ctx)                                                                   \
    431     {                                                                                                                  \
    432       return fmt::format_to(ctx.out(), "{}", str.view());                                                              \
    433     }                                                                                                                  \
    434   };
    435 
    436 MAKE_FORMATTER(TinyString);
    437 MAKE_FORMATTER(SmallString);
    438 MAKE_FORMATTER(LargeString);
    439 
    440 #undef MAKE_FORMATTER