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