small_string.cpp (23089B)
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 #include "small_string.h" 5 #include "assert.h" 6 #include "string_util.h" 7 8 #include <algorithm> 9 #include <cctype> 10 #include <cstdio> 11 #include <cstring> 12 13 #ifdef _WIN32 14 #include "windows_headers.h" 15 #endif 16 17 SmallStringBase::SmallStringBase() = default; 18 19 SmallStringBase::SmallStringBase(const SmallStringBase& copy) 20 { 21 assign(copy.m_buffer, copy.m_length); 22 } 23 24 SmallStringBase::SmallStringBase(const char* str) 25 { 26 assign(str); 27 } 28 29 SmallStringBase::SmallStringBase(const char* str, u32 count) 30 { 31 assign(str, count); 32 } 33 34 SmallStringBase::SmallStringBase(SmallStringBase&& move) 35 { 36 assign(std::move(move)); 37 } 38 39 SmallStringBase::SmallStringBase(const std::string_view sv) 40 { 41 assign(sv); 42 } 43 44 SmallStringBase::SmallStringBase(const std::string& str) 45 { 46 assign(str); 47 } 48 49 SmallStringBase::~SmallStringBase() 50 { 51 if (m_on_heap) 52 std::free(m_buffer); 53 } 54 55 void SmallStringBase::reserve(u32 new_reserve) 56 { 57 const u32 real_reserve = new_reserve + 1; 58 if (m_buffer_size >= real_reserve) 59 return; 60 61 if (m_on_heap) 62 { 63 char* new_ptr = static_cast<char*>(std::realloc(m_buffer, real_reserve)); 64 if (!new_ptr) 65 Panic("Memory allocation failed."); 66 67 #ifdef _DEBUG 68 std::memset(new_ptr + m_length, 0, real_reserve - m_length); 69 #endif 70 m_buffer = new_ptr; 71 } 72 else 73 { 74 char* new_ptr = static_cast<char*>(std::malloc(real_reserve)); 75 if (!new_ptr) 76 Panic("Memory allocation failed."); 77 78 if (m_length > 0) 79 std::memcpy(new_ptr, m_buffer, m_length); 80 #ifdef _DEBUG 81 std::memset(new_ptr + m_length, 0, real_reserve - m_length); 82 #else 83 new_ptr[m_length] = 0; 84 #endif 85 m_buffer = new_ptr; 86 m_on_heap = true; 87 } 88 89 m_buffer_size = new_reserve; 90 } 91 92 void SmallStringBase::shrink_to_fit() 93 { 94 const u32 buffer_size = (m_length + 1); 95 if (!m_on_heap || buffer_size == m_buffer_size) 96 return; 97 98 if (m_length == 0) 99 { 100 std::free(m_buffer); 101 m_buffer = nullptr; 102 m_buffer_size = 0; 103 return; 104 } 105 106 char* new_ptr = static_cast<char*>(std::realloc(m_buffer, buffer_size)); 107 if (!new_ptr) 108 Panic("Memory allocation failed."); 109 110 m_buffer = new_ptr; 111 m_buffer_size = buffer_size; 112 } 113 114 std::string_view SmallStringBase::view() const 115 { 116 return (m_length == 0) ? std::string_view() : std::string_view(m_buffer, m_length); 117 } 118 119 SmallStringBase& SmallStringBase::operator=(SmallStringBase&& move) 120 { 121 assign(move); 122 return *this; 123 } 124 125 SmallStringBase& SmallStringBase::operator=(const std::string_view str) 126 { 127 assign(str); 128 return *this; 129 } 130 131 SmallStringBase& SmallStringBase::operator=(const std::string& str) 132 { 133 assign(str); 134 return *this; 135 } 136 137 SmallStringBase& SmallStringBase::operator=(const char* str) 138 { 139 assign(str); 140 return *this; 141 } 142 143 SmallStringBase& SmallStringBase::operator=(const SmallStringBase& copy) 144 { 145 assign(copy); 146 return *this; 147 } 148 149 void SmallStringBase::make_room_for(u32 space) 150 { 151 const u32 required_size = m_length + space + 1; 152 if (m_buffer_size >= required_size) 153 return; 154 155 reserve(std::max(required_size, m_buffer_size * 2)); 156 } 157 158 void SmallStringBase::append(const char* str, u32 length) 159 { 160 if (length == 0) 161 return; 162 163 make_room_for(length); 164 165 DebugAssert((length + m_length) < m_buffer_size); 166 167 std::memcpy(m_buffer + m_length, str, length); 168 m_length += length; 169 m_buffer[m_length] = 0; 170 } 171 172 void SmallStringBase::append_hex(const void* data, size_t len) 173 { 174 if (len == 0) 175 return; 176 177 make_room_for(static_cast<u32>(len) * 4); 178 const u8* bytes = static_cast<const u8*>(data); 179 append_format("{:02X}", bytes[0]); 180 for (size_t i = 1; i < len; i++) 181 append_format(", {:02X}", bytes[i]); 182 } 183 184 void SmallStringBase::prepend(const char* str, u32 length) 185 { 186 if (length == 0) 187 return; 188 189 make_room_for(length); 190 191 DebugAssert((length + m_length) < m_buffer_size); 192 193 std::memmove(m_buffer + length, m_buffer, m_length); 194 std::memcpy(m_buffer, str, length); 195 m_length += length; 196 m_buffer[m_length] = 0; 197 } 198 199 void SmallStringBase::append(char c) 200 { 201 append(&c, 1); 202 } 203 204 void SmallStringBase::append(const SmallStringBase& str) 205 { 206 append(str.m_buffer, str.m_length); 207 } 208 209 void SmallStringBase::append(const char* str) 210 { 211 append(str, static_cast<u32>(std::strlen(str))); 212 } 213 214 void SmallStringBase::append(const std::string& str) 215 { 216 append(str.c_str(), static_cast<u32>(str.length())); 217 } 218 219 void SmallStringBase::append(const std::string_view str) 220 { 221 append(str.data(), static_cast<u32>(str.length())); 222 } 223 224 void SmallStringBase::append_sprintf(const char* format, ...) 225 { 226 std::va_list ap; 227 va_start(ap, format); 228 append_vsprintf(format, ap); 229 va_end(ap); 230 } 231 232 void SmallStringBase::append_vsprintf(const char* format, va_list ap) 233 { 234 // We have a 1KB byte buffer on the stack here. If this is too little, we'll grow it via the heap, 235 // but 1KB should be enough for most strings. 236 char stack_buffer[1024]; 237 char* heap_buffer = nullptr; 238 char* buffer = stack_buffer; 239 u32 buffer_size = static_cast<u32>(std::size(stack_buffer)); 240 u32 written; 241 242 for (;;) 243 { 244 std::va_list ap_copy; 245 va_copy(ap_copy, ap); 246 const int ret = std::vsnprintf(buffer, buffer_size, format, ap_copy); 247 va_end(ap_copy); 248 if (ret < 0 || (static_cast<u32>(ret) >= (buffer_size - 1))) 249 { 250 buffer_size *= 2; 251 buffer = heap_buffer = reinterpret_cast<char*>(std::realloc(heap_buffer, buffer_size)); 252 if (!buffer) [[unlikely]] 253 Panic("Memory allocation failed."); 254 continue; 255 } 256 257 written = static_cast<u32>(ret); 258 break; 259 } 260 261 append(buffer, written); 262 263 if (heap_buffer) 264 std::free(heap_buffer); 265 } 266 267 void SmallStringBase::prepend(char c) 268 { 269 prepend(&c, 1); 270 } 271 272 void SmallStringBase::prepend(const SmallStringBase& str) 273 { 274 prepend(str.m_buffer, str.m_length); 275 } 276 277 void SmallStringBase::prepend(const char* str) 278 { 279 prepend(str, static_cast<u32>(std::strlen(str))); 280 } 281 282 void SmallStringBase::prepend(const std::string& str) 283 { 284 prepend(str.c_str(), static_cast<u32>(str.length())); 285 } 286 287 void SmallStringBase::prepend(const std::string_view str) 288 { 289 prepend(str.data(), static_cast<u32>(str.length())); 290 } 291 292 void SmallStringBase::prepend_sprintf(const char* format, ...) 293 { 294 va_list ap; 295 va_start(ap, format); 296 prepend_vsprintf(format, ap); 297 va_end(ap); 298 } 299 300 void SmallStringBase::prepend_vsprintf(const char* format, va_list ArgPtr) 301 { 302 // We have a 1KB byte buffer on the stack here. If this is too little, we'll grow it via the heap, 303 // but 1KB should be enough for most strings. 304 char stack_buffer[1024]; 305 char* heap_buffer = nullptr; 306 char* buffer = stack_buffer; 307 u32 buffer_size = static_cast<u32>(std::size(stack_buffer)); 308 u32 written; 309 310 for (;;) 311 { 312 int ret = std::vsnprintf(buffer, buffer_size, format, ArgPtr); 313 if (ret < 0 || (static_cast<u32>(ret) >= (buffer_size - 1))) 314 { 315 buffer_size *= 2; 316 buffer = heap_buffer = reinterpret_cast<char*>(std::realloc(heap_buffer, buffer_size)); 317 if (!buffer) [[unlikely]] 318 Panic("Memory allocation failed."); 319 continue; 320 } 321 322 written = static_cast<u32>(ret); 323 break; 324 } 325 326 prepend(buffer, written); 327 328 if (heap_buffer) 329 std::free(heap_buffer); 330 } 331 332 void SmallStringBase::insert(s32 offset, const char* str) 333 { 334 insert(offset, str, static_cast<u32>(std::strlen(str))); 335 } 336 337 void SmallStringBase::insert(s32 offset, const SmallStringBase& str) 338 { 339 insert(offset, str, str.m_length); 340 } 341 342 void SmallStringBase::insert(s32 offset, const char* str, u32 length) 343 { 344 if (length == 0) 345 return; 346 347 make_room_for(length); 348 349 // calc real offset 350 u32 real_offset; 351 if (offset < 0) 352 real_offset = static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length) + offset)); 353 else 354 real_offset = std::min(static_cast<u32>(offset), m_length); 355 356 // determine number of characters after offset 357 DebugAssert(real_offset <= m_length); 358 const u32 chars_after_offset = m_length - real_offset; 359 if (chars_after_offset > 0) 360 std::memmove(m_buffer + offset + length, m_buffer + offset, chars_after_offset); 361 362 // insert the string 363 std::memcpy(m_buffer + real_offset, str, length); 364 m_length += length; 365 366 // ensure null termination 367 m_buffer[m_length] = 0; 368 } 369 370 void SmallStringBase::insert(s32 offset, const std::string& str) 371 { 372 insert(offset, str.c_str(), static_cast<u32>(str.size())); 373 } 374 375 void SmallStringBase::insert(s32 offset, const std::string_view str) 376 { 377 insert(offset, str.data(), static_cast<u32>(str.size())); 378 } 379 380 void SmallStringBase::sprintf(const char* format, ...) 381 { 382 va_list ap; 383 va_start(ap, format); 384 vsprintf(format, ap); 385 va_end(ap); 386 } 387 388 void SmallStringBase::vsprintf(const char* format, va_list ap) 389 { 390 clear(); 391 append_vsprintf(format, ap); 392 } 393 394 void SmallStringBase::assign(const SmallStringBase& copy) 395 { 396 assign(copy.c_str(), copy.length()); 397 } 398 399 void SmallStringBase::assign(const char* str) 400 { 401 assign(str, static_cast<u32>(std::strlen(str))); 402 } 403 404 void SmallStringBase::assign(const char* str, u32 length) 405 { 406 clear(); 407 if (length > 0) 408 append(str, length); 409 } 410 411 void SmallStringBase::assign(SmallStringBase&& move) 412 { 413 if (move.m_on_heap) 414 { 415 if (m_on_heap) 416 std::free(m_buffer); 417 m_buffer = move.m_buffer; 418 m_buffer_size = move.m_buffer_size; 419 m_length = move.m_length; 420 m_on_heap = true; 421 move.m_buffer = nullptr; 422 move.m_buffer_size = 0; 423 move.m_length = 0; 424 } 425 else 426 { 427 assign(move.m_buffer, move.m_buffer_size); 428 } 429 } 430 431 void SmallStringBase::assign(const std::string& str) 432 { 433 clear(); 434 append(str.data(), static_cast<u32>(str.size())); 435 } 436 437 void SmallStringBase::assign(const std::string_view str) 438 { 439 clear(); 440 append(str.data(), static_cast<u32>(str.size())); 441 } 442 443 #ifdef _WIN32 444 445 void SmallStringBase::assign(const std::wstring_view wstr) 446 { 447 int mblen = 448 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.length()), nullptr, 0, nullptr, nullptr); 449 if (mblen < 0) 450 { 451 clear(); 452 return; 453 } 454 455 reserve(static_cast<u32>(mblen)); 456 if (mblen > 0 && WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.length()), m_buffer, mblen, 457 nullptr, nullptr) < 0) 458 { 459 clear(); 460 return; 461 } 462 463 m_length = static_cast<u32>(mblen); 464 } 465 466 std::wstring SmallStringBase::wstring() const 467 { 468 return StringUtil::UTF8StringToWideString(view()); 469 } 470 471 #endif 472 473 void SmallStringBase::vformat(fmt::string_view fmt, fmt::format_args args) 474 { 475 clear(); 476 fmt::vformat_to(std::back_inserter(*this), fmt, args); 477 } 478 479 bool SmallStringBase::equals(const char* str) const 480 { 481 if (m_length == 0) 482 return (std::strlen(str) == 0); 483 else 484 return (std::strcmp(m_buffer, str) == 0); 485 } 486 487 bool SmallStringBase::equals(const SmallStringBase& str) const 488 { 489 return (m_length == str.m_length && (m_length == 0 || std::strcmp(m_buffer, str.m_buffer) == 0)); 490 } 491 492 bool SmallStringBase::equals(const std::string_view str) const 493 { 494 return (m_length == static_cast<u32>(str.length()) && 495 (m_length == 0 || std::memcmp(m_buffer, str.data(), m_length) == 0)); 496 } 497 498 bool SmallStringBase::equals(const std::string& str) const 499 { 500 return (m_length == static_cast<u32>(str.length()) && 501 (m_length == 0 || std::memcmp(m_buffer, str.data(), m_length) == 0)); 502 } 503 504 bool SmallStringBase::iequals(const char* otherText) const 505 { 506 if (m_length == 0) 507 return (std::strlen(otherText) == 0); 508 else 509 return StringUtil::EqualNoCase(view(), otherText); 510 } 511 512 bool SmallStringBase::iequals(const SmallStringBase& str) const 513 { 514 return (m_length == str.m_length && (m_length == 0 || std::strcmp(m_buffer, str.m_buffer) == 0)); 515 } 516 517 bool SmallStringBase::iequals(const std::string_view str) const 518 { 519 return (m_length == static_cast<u32>(str.length()) && 520 (m_length == 0 || StringUtil::Strncasecmp(m_buffer, str.data(), m_length) == 0)); 521 } 522 523 bool SmallStringBase::iequals(const std::string& str) const 524 { 525 return (m_length == static_cast<u32>(str.length()) && 526 (m_length == 0 || StringUtil::Strncasecmp(m_buffer, str.data(), m_length) == 0)); 527 } 528 529 int SmallStringBase::compare(const char* otherText) const 530 { 531 return compare(std::string_view(otherText)); 532 } 533 534 int SmallStringBase::compare(const SmallStringBase& str) const 535 { 536 if (m_length == 0) 537 return (str.m_length == 0) ? 0 : -1; 538 else if (str.m_length == 0) 539 return 1; 540 541 const int res = std::strncmp(m_buffer, str.m_buffer, std::min(m_length, str.m_length)); 542 if (m_length == str.m_length || res != 0) 543 return res; 544 else 545 return (m_length > str.m_length) ? 1 : -1; 546 } 547 548 int SmallStringBase::compare(const std::string_view str) const 549 { 550 const u32 slength = static_cast<u32>(str.length()); 551 if (m_length == 0) 552 return (slength == 0) ? 0 : -1; 553 else if (slength == 0) 554 return 1; 555 556 const int res = std::strncmp(m_buffer, str.data(), std::min(m_length, slength)); 557 if (m_length == slength || res != 0) 558 return res; 559 else 560 return (m_length > slength) ? 1 : -1; 561 } 562 563 int SmallStringBase::compare(const std::string& str) const 564 { 565 const u32 slength = static_cast<u32>(str.length()); 566 if (m_length == 0) 567 return (slength == 0) ? 0 : -1; 568 else if (slength == 0) 569 return 1; 570 571 const int res = std::strncmp(m_buffer, str.data(), std::min(m_length, slength)); 572 if (m_length == slength || res != 0) 573 return res; 574 else 575 return (m_length > slength) ? 1 : -1; 576 } 577 578 int SmallStringBase::icompare(const char* otherText) const 579 { 580 return icompare(std::string_view(otherText)); 581 } 582 583 int SmallStringBase::icompare(const SmallStringBase& str) const 584 { 585 if (m_length == 0) 586 return (str.m_length == 0) ? 0 : -1; 587 else if (str.m_length == 0) 588 return 1; 589 590 const int res = StringUtil::Strncasecmp(m_buffer, str.m_buffer, std::min(m_length, str.m_length)); 591 if (m_length == str.m_length || res != 0) 592 return res; 593 else 594 return (m_length > str.m_length) ? 1 : -1; 595 } 596 597 int SmallStringBase::icompare(const std::string_view str) const 598 { 599 const u32 slength = static_cast<u32>(str.length()); 600 if (m_length == 0) 601 return (slength == 0) ? 0 : -1; 602 else if (slength == 0) 603 return 1; 604 605 const int res = StringUtil::Strncasecmp(m_buffer, str.data(), std::min(m_length, slength)); 606 if (m_length == slength || res != 0) 607 return res; 608 else 609 return (m_length > slength) ? 1 : -1; 610 } 611 612 int SmallStringBase::icompare(const std::string& str) const 613 { 614 const u32 slength = static_cast<u32>(str.length()); 615 if (m_length == 0) 616 return (slength == 0) ? 0 : -1; 617 else if (slength == 0) 618 return 1; 619 620 const int res = StringUtil::Strncasecmp(m_buffer, str.data(), std::min(m_length, slength)); 621 if (m_length == slength || res != 0) 622 return res; 623 else 624 return (m_length > slength) ? 1 : -1; 625 } 626 627 bool SmallStringBase::starts_with(const char* str, bool case_sensitive) const 628 { 629 const u32 other_length = static_cast<u32>(std::strlen(str)); 630 if (other_length > m_length) 631 return false; 632 633 return (case_sensitive) ? (std::strncmp(str, m_buffer, other_length) == 0) : 634 (StringUtil::Strncasecmp(str, m_buffer, other_length) == 0); 635 } 636 637 bool SmallStringBase::starts_with(const SmallStringBase& str, bool case_sensitive) const 638 { 639 const u32 other_length = str.m_length; 640 if (other_length > m_length) 641 return false; 642 643 return (case_sensitive) ? (std::strncmp(str.m_buffer, m_buffer, other_length) == 0) : 644 (StringUtil::Strncasecmp(str.m_buffer, m_buffer, other_length) == 0); 645 } 646 647 bool SmallStringBase::starts_with(const std::string_view str, bool case_sensitive) const 648 { 649 const u32 other_length = static_cast<u32>(str.length()); 650 if (other_length > m_length) 651 return false; 652 653 return (case_sensitive) ? (std::strncmp(str.data(), m_buffer, other_length) == 0) : 654 (StringUtil::Strncasecmp(str.data(), m_buffer, other_length) == 0); 655 } 656 657 bool SmallStringBase::starts_with(const std::string& str, bool case_sensitive) const 658 { 659 const u32 other_length = static_cast<u32>(str.length()); 660 if (other_length > m_length) 661 return false; 662 663 return (case_sensitive) ? (std::strncmp(str.data(), m_buffer, other_length) == 0) : 664 (StringUtil::Strncasecmp(str.data(), m_buffer, other_length) == 0); 665 } 666 667 bool SmallStringBase::ends_with(const char* str, bool case_sensitive) const 668 { 669 const u32 other_length = static_cast<u32>(std::strlen(str)); 670 if (other_length > m_length) 671 return false; 672 673 u32 start_offset = m_length - other_length; 674 return (case_sensitive) ? (std::strncmp(str, m_buffer + start_offset, other_length) == 0) : 675 (StringUtil::Strncasecmp(str, m_buffer + start_offset, other_length) == 0); 676 } 677 678 bool SmallStringBase::ends_with(const SmallStringBase& str, bool case_sensitive) const 679 { 680 const u32 other_length = str.m_length; 681 if (other_length > m_length) 682 return false; 683 684 const u32 start_offset = m_length - other_length; 685 return (case_sensitive) ? (std::strncmp(str.m_buffer, m_buffer + start_offset, other_length) == 0) : 686 (StringUtil::Strncasecmp(str.m_buffer, m_buffer + start_offset, other_length) == 0); 687 } 688 689 bool SmallStringBase::ends_with(const std::string_view str, bool case_sensitive) const 690 { 691 const u32 other_length = static_cast<u32>(str.length()); 692 if (other_length > m_length) 693 return false; 694 695 const u32 start_offset = m_length - other_length; 696 return (case_sensitive) ? (std::strncmp(str.data(), m_buffer + start_offset, other_length) == 0) : 697 (StringUtil::Strncasecmp(str.data(), m_buffer + start_offset, other_length) == 0); 698 } 699 700 bool SmallStringBase::ends_with(const std::string& str, bool case_sensitive) const 701 { 702 const u32 other_length = static_cast<u32>(str.length()); 703 if (other_length > m_length) 704 return false; 705 706 const u32 start_offset = m_length - other_length; 707 return (case_sensitive) ? (std::strncmp(str.data(), m_buffer + start_offset, other_length) == 0) : 708 (StringUtil::Strncasecmp(str.data(), m_buffer + start_offset, other_length) == 0); 709 } 710 711 void SmallStringBase::clear() 712 { 713 // in debug, zero whole string, in release, zero only the first character 714 #if _DEBUG 715 std::memset(m_buffer, 0, m_buffer_size); 716 #else 717 m_buffer[0] = '\0'; 718 #endif 719 m_length = 0; 720 } 721 722 s32 SmallStringBase::find(char c, u32 offset) const 723 { 724 if (m_length == 0) 725 return -1; 726 727 DebugAssert(offset <= m_length); 728 const char* at = std::strchr(m_buffer + offset, c); 729 return at ? static_cast<s32>(at - m_buffer) : -1; 730 } 731 732 s32 SmallStringBase::rfind(char c, u32 offset) const 733 { 734 if (m_length == 0) 735 return -1; 736 737 DebugAssert(offset <= m_length); 738 const char* at = std::strrchr(m_buffer + offset, c); 739 return at ? static_cast<s32>(at - m_buffer) : -1; 740 } 741 742 s32 SmallStringBase::find(const char* str, u32 offset) const 743 { 744 if (m_length == 0) 745 return -1; 746 747 DebugAssert(offset <= m_length); 748 const char* at = std::strstr(m_buffer + offset, str); 749 return at ? static_cast<s32>(at - m_buffer) : -1; 750 } 751 752 u32 SmallStringBase::count(char ch) const 753 { 754 const char* ptr = m_buffer; 755 const char* end = ptr + m_length; 756 u32 count = 0; 757 while (ptr != end) 758 count += static_cast<u32>(*(ptr++) == ch); 759 return count; 760 } 761 762 u32 SmallStringBase::replace(const char* search, const char* replacement) 763 { 764 const u32 search_length = static_cast<u32>(std::strlen(search)); 765 const u32 replacement_length = static_cast<u32>(std::strlen(replacement)); 766 767 s32 offset = 0; 768 u32 count = 0; 769 for (;;) 770 { 771 offset = find(search, static_cast<u32>(offset)); 772 if (offset < 0) 773 break; 774 775 const u32 new_length = m_length - search_length + replacement_length; 776 reserve(new_length); 777 m_length = new_length; 778 779 const u32 chars_after_offset = (m_length - static_cast<u32>(offset)); 780 DebugAssert(chars_after_offset >= search_length); 781 if (chars_after_offset > search_length) 782 { 783 std::memmove(&m_buffer[static_cast<u32>(offset) + replacement_length], 784 &m_buffer[static_cast<u32>(offset) + search_length], chars_after_offset - search_length); 785 std::memcpy(&m_buffer[static_cast<u32>(offset)], replacement, replacement_length); 786 } 787 else 788 { 789 // at end of string 790 std::memcpy(&m_buffer[static_cast<u32>(offset)], replacement, replacement_length); 791 m_buffer[static_cast<u32>(offset) + replacement_length] = '\0'; 792 } 793 794 offset += replacement_length; 795 count++; 796 } 797 798 return count; 799 } 800 801 void SmallStringBase::resize(u32 new_size, char fill, bool shrink_if_smaller) 802 { 803 // if going larger, or we don't own the buffer, realloc 804 if (new_size >= m_buffer_size) 805 { 806 reserve(new_size); 807 808 if (m_length < new_size) 809 { 810 std::memset(m_buffer + m_length, fill, m_buffer_size - m_length - 1); 811 } 812 813 m_length = new_size; 814 } 815 else 816 { 817 // update length and terminator 818 #if _DEBUG 819 std::memset(m_buffer + new_size, 0, m_buffer_size - new_size); 820 #else 821 m_buffer[new_size] = 0; 822 #endif 823 m_length = new_size; 824 825 // shrink if requested 826 if (shrink_if_smaller) 827 shrink_to_fit(); 828 } 829 } 830 831 void SmallStringBase::update_size() 832 { 833 m_length = static_cast<u32>(std::strlen(m_buffer)); 834 } 835 836 std::string_view SmallStringBase::substr(s32 offset, s32 count) const 837 { 838 // calc real offset 839 u32 real_offset; 840 if (offset < 0) 841 real_offset = static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length + offset))); 842 else 843 real_offset = std::min((u32)offset, m_length); 844 845 // calc real count 846 u32 real_count; 847 if (count < 0) 848 { 849 real_count = 850 std::min(m_length - real_offset, static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length) + count))); 851 } 852 else 853 { 854 real_count = std::min(m_length - real_offset, static_cast<u32>(count)); 855 } 856 857 return (real_count > 0) ? std::string_view(m_buffer + real_offset, real_count) : std::string_view(); 858 } 859 860 void SmallStringBase::erase(s32 offset, s32 count) 861 { 862 // calc real offset 863 u32 real_offset; 864 if (offset < 0) 865 real_offset = static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length + offset))); 866 else 867 real_offset = std::min((u32)offset, m_length); 868 869 // calc real count 870 u32 real_count; 871 if (count < 0) 872 { 873 real_count = 874 std::min(m_length - real_offset, static_cast<u32>(std::max<s32>(0, static_cast<s32>(m_length) + count))); 875 } 876 else 877 { 878 real_count = std::min(m_length - real_offset, static_cast<u32>(count)); 879 } 880 881 // Fastpath: offset == 0, count < 0, wipe whole string. 882 if (real_offset == 0 && real_count == m_length) 883 { 884 clear(); 885 return; 886 } 887 888 // Fastpath: offset >= 0, count < 0, wipe everything after offset + count 889 if ((real_offset + real_count) == m_length) 890 { 891 m_length -= real_count; 892 #ifdef _DEBUG 893 std::memset(m_buffer + m_length, 0, m_buffer_size - m_length); 894 #else 895 m_buffer[m_length] = 0; 896 #endif 897 } 898 // Slowpath: offset >= 0, count < length 899 else 900 { 901 const u32 after_erase_block = m_length - real_offset - real_count; 902 DebugAssert(after_erase_block > 0); 903 904 std::memmove(m_buffer + offset, m_buffer + real_offset + real_count, after_erase_block); 905 m_length = m_length - real_count; 906 907 #ifdef _DEBUG 908 std::memset(m_buffer + m_length, 0, m_buffer_size - m_length); 909 #else 910 m_buffer[m_length] = 0; 911 #endif 912 } 913 }