/ externals / zycore / src / String.c
String.c
   1  /***************************************************************************************************
   2  
   3    Zyan Core Library (Zycore-C)
   4  
   5    Original Author : Florian Bernd
   6  
   7   * Permission is hereby granted, free of charge, to any person obtaining a copy
   8   * of this software and associated documentation files (the "Software"), to deal
   9   * in the Software without restriction, including without limitation the rights
  10   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11   * copies of the Software, and to permit persons to whom the Software is
  12   * furnished to do so, subject to the following conditions:
  13   *
  14   * The above copyright notice and this permission notice shall be included in all
  15   * copies or substantial portions of the Software.
  16   *
  17   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23   * SOFTWARE.
  24  
  25  ***************************************************************************************************/
  26  
  27  #include <Zycore/String.h>
  28  #include <Zycore/LibC.h>
  29  
  30  /* ============================================================================================== */
  31  /* Internal macros                                                                                */
  32  /* ============================================================================================== */
  33  
  34  /**
  35   * Writes a terminating '\0' character at the end of the string data.
  36   */
  37  #define ZYCORE_STRING_NULLTERMINATE(string) \
  38        *(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0';
  39  
  40  /**
  41   * Checks for a terminating '\0' character at the end of the string data.
  42   */
  43  #define ZYCORE_STRING_ASSERT_NULLTERMINATION(string) \
  44        ZYAN_ASSERT(*(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) == '\0');
  45  
  46  /* ============================================================================================== */
  47  /* Exported functions                                                                             */
  48  /* ============================================================================================== */
  49  
  50  /* ---------------------------------------------------------------------------------------------- */
  51  /* Constructor and destructor                                                                     */
  52  /* ---------------------------------------------------------------------------------------------- */
  53  
  54  #ifndef ZYAN_NO_LIBC
  55  
  56  ZyanStatus ZyanStringInit(ZyanString* string, ZyanUSize capacity)
  57  {
  58      return ZyanStringInitEx(string, capacity, ZyanAllocatorDefault(),
  59          ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD);
  60  }
  61  
  62  #endif // ZYAN_NO_LIBC
  63  
  64  ZyanStatus ZyanStringInitEx(ZyanString* string, ZyanUSize capacity, ZyanAllocator* allocator,
  65      ZyanU8 growth_factor, ZyanU8 shrink_threshold)
  66  {
  67      if (!string)
  68      {
  69          return ZYAN_STATUS_INVALID_ARGUMENT;
  70      }
  71  
  72      string->flags = 0;
  73      capacity = ZYAN_MAX(ZYAN_STRING_MIN_CAPACITY, capacity) + 1;
  74      ZYAN_CHECK(ZyanVectorInitEx(&string->vector, sizeof(char), capacity, ZYAN_NULL, allocator,
  75          growth_factor, shrink_threshold));
  76      ZYAN_ASSERT(string->vector.capacity >= capacity);
  77      // Some of the string code relies on `sizeof(char) == 1`
  78      ZYAN_ASSERT(string->vector.element_size == 1);
  79  
  80      *(char*)string->vector.data = '\0';
  81      ++string->vector.size;
  82  
  83      return ZYAN_STATUS_SUCCESS;
  84  }
  85  
  86  ZyanStatus ZyanStringInitCustomBuffer(ZyanString* string, char* buffer, ZyanUSize capacity)
  87  {
  88      if (!string || !capacity)
  89      {
  90          return ZYAN_STATUS_INVALID_ARGUMENT;
  91      }
  92  
  93      string->flags = ZYAN_STRING_HAS_FIXED_CAPACITY;
  94      ZYAN_CHECK(ZyanVectorInitCustomBuffer(&string->vector, sizeof(char), (void*)buffer, capacity,
  95          ZYAN_NULL));
  96      ZYAN_ASSERT(string->vector.capacity == capacity);
  97      // Some of the string code relies on `sizeof(char) == 1`
  98      ZYAN_ASSERT(string->vector.element_size == 1);
  99  
 100      *(char*)string->vector.data = '\0';
 101      ++string->vector.size;
 102  
 103      return ZYAN_STATUS_SUCCESS;
 104  }
 105  
 106  ZyanStatus ZyanStringDestroy(ZyanString* string)
 107  {
 108      if (!string)
 109      {
 110          return ZYAN_STATUS_INVALID_ARGUMENT;
 111      }
 112      if (string->flags & ZYAN_STRING_HAS_FIXED_CAPACITY)
 113      {
 114          return ZYAN_STATUS_SUCCESS;
 115      }
 116  
 117      return ZyanVectorDestroy(&string->vector);
 118  }
 119  
 120  /* ---------------------------------------------------------------------------------------------- */
 121  /* Duplication                                                                                    */
 122  /* ---------------------------------------------------------------------------------------------- */
 123  
 124  #ifndef ZYAN_NO_LIBC
 125  
 126  ZyanStatus ZyanStringDuplicate(ZyanString* destination, const ZyanStringView* source,
 127      ZyanUSize capacity)
 128  {
 129      return ZyanStringDuplicateEx(destination, source, capacity, ZyanAllocatorDefault(),
 130          ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD);
 131  }
 132  
 133  #endif // ZYAN_NO_LIBC
 134  
 135  ZyanStatus ZyanStringDuplicateEx(ZyanString* destination, const ZyanStringView* source,
 136      ZyanUSize capacity, ZyanAllocator* allocator, ZyanU8 growth_factor, ZyanU8 shrink_threshold)
 137  {
 138      if (!source || !source->string.vector.size)
 139      {
 140          return ZYAN_STATUS_INVALID_ARGUMENT;
 141      }
 142  
 143      const ZyanUSize len = source->string.vector.size;
 144      capacity = ZYAN_MAX(capacity, len - 1);
 145      ZYAN_CHECK(ZyanStringInitEx(destination, capacity, allocator, growth_factor, shrink_threshold));
 146      ZYAN_ASSERT(destination->vector.capacity >= len);
 147  
 148      ZYAN_MEMCPY(destination->vector.data, source->string.vector.data,
 149          source->string.vector.size - 1);
 150      destination->vector.size = len;
 151      ZYCORE_STRING_NULLTERMINATE(destination);
 152  
 153      return ZYAN_STATUS_SUCCESS;
 154  }
 155  
 156  ZyanStatus ZyanStringDuplicateCustomBuffer(ZyanString* destination, const ZyanStringView* source,
 157      char* buffer, ZyanUSize capacity)
 158  {
 159      if (!source || !source->string.vector.size)
 160      {
 161          return ZYAN_STATUS_INVALID_ARGUMENT;
 162      }
 163  
 164      const ZyanUSize len = source->string.vector.size;
 165      if (capacity < len)
 166      {
 167          return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
 168      }
 169  
 170      ZYAN_CHECK(ZyanStringInitCustomBuffer(destination, buffer, capacity));
 171      ZYAN_ASSERT(destination->vector.capacity >= len);
 172  
 173      ZYAN_MEMCPY(destination->vector.data, source->string.vector.data,
 174          source->string.vector.size - 1);
 175      destination->vector.size = len;
 176      ZYCORE_STRING_NULLTERMINATE(destination);
 177  
 178      return ZYAN_STATUS_SUCCESS;
 179  }
 180  
 181  /* ---------------------------------------------------------------------------------------------- */
 182  /* Concatenation                                                                                  */
 183  /* ---------------------------------------------------------------------------------------------- */
 184  
 185  #ifndef ZYAN_NO_LIBC
 186  
 187  ZyanStatus ZyanStringConcat(ZyanString* destination, const ZyanStringView* s1,
 188      const ZyanStringView* s2, ZyanUSize capacity)
 189  {
 190      return ZyanStringConcatEx(destination, s1, s2, capacity, ZyanAllocatorDefault(),
 191          ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD);
 192  }
 193  
 194  #endif // ZYAN_NO_LIBC
 195  
 196  ZyanStatus ZyanStringConcatEx(ZyanString* destination, const ZyanStringView* s1,
 197      const ZyanStringView* s2, ZyanUSize capacity, ZyanAllocator* allocator, ZyanU8 growth_factor,
 198      ZyanU8 shrink_threshold)
 199  {
 200      if (!s1 || !s2 || !s1->string.vector.size || !s2->string.vector.size)
 201      {
 202          return ZYAN_STATUS_INVALID_ARGUMENT;
 203      }
 204  
 205      const ZyanUSize len = s1->string.vector.size + s2->string.vector.size - 1;
 206      capacity = ZYAN_MAX(capacity, len - 1);
 207      ZYAN_CHECK(ZyanStringInitEx(destination, capacity, allocator, growth_factor, shrink_threshold));
 208      ZYAN_ASSERT(destination->vector.capacity >= len);
 209  
 210      ZYAN_MEMCPY(destination->vector.data, s1->string.vector.data, s1->string.vector.size - 1);
 211      ZYAN_MEMCPY((char*)destination->vector.data + s1->string.vector.size - 1,
 212          s2->string.vector.data, s2->string.vector.size - 1);
 213      destination->vector.size = len;
 214      ZYCORE_STRING_NULLTERMINATE(destination);
 215  
 216      return ZYAN_STATUS_SUCCESS;
 217  }
 218  
 219  ZyanStatus ZyanStringConcatCustomBuffer(ZyanString* destination, const ZyanStringView* s1,
 220      const ZyanStringView* s2, char* buffer, ZyanUSize capacity)
 221  {
 222      if (!s1 || !s2 || !s1->string.vector.size || !s2->string.vector.size)
 223      {
 224          return ZYAN_STATUS_INVALID_ARGUMENT;
 225      }
 226  
 227      const ZyanUSize len = s1->string.vector.size + s2->string.vector.size - 1;
 228      if (capacity < len)
 229      {
 230          return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
 231      }
 232  
 233      ZYAN_CHECK(ZyanStringInitCustomBuffer(destination, buffer, capacity));
 234      ZYAN_ASSERT(destination->vector.capacity >= len);
 235  
 236      ZYAN_MEMCPY(destination->vector.data, s1->string.vector.data, s1->string.vector.size - 1);
 237      ZYAN_MEMCPY((char*)destination->vector.data + s1->string.vector.size - 1,
 238          s2->string.vector.data, s2->string.vector.size - 1);
 239      destination->vector.size = len;
 240      ZYCORE_STRING_NULLTERMINATE(destination);
 241  
 242      return ZYAN_STATUS_SUCCESS;
 243  }
 244  
 245  /* ---------------------------------------------------------------------------------------------- */
 246  /* Views                                                                                          */
 247  /* ---------------------------------------------------------------------------------------------- */
 248  
 249  ZyanStatus ZyanStringViewInsideView(ZyanStringView* view, const ZyanStringView* source)
 250  {
 251      if (!view || !source)
 252      {
 253          return ZYAN_STATUS_INVALID_ARGUMENT;
 254      }
 255  
 256      view->string.vector.data = source->string.vector.data;
 257      view->string.vector.size = source->string.vector.size;
 258  
 259      return ZYAN_STATUS_SUCCESS;
 260  }
 261  
 262  ZyanStatus ZyanStringViewInsideViewEx(ZyanStringView* view, const ZyanStringView* source,
 263      ZyanUSize index, ZyanUSize count)
 264  {
 265      if (!view || !source)
 266      {
 267          return ZYAN_STATUS_INVALID_ARGUMENT;
 268      }
 269  
 270      if (index + count >= source->string.vector.size)
 271      {
 272          return ZYAN_STATUS_OUT_OF_RANGE;
 273      }
 274  
 275      view->string.vector.data = (void*)((char*)source->string.vector.data + index);
 276      view->string.vector.size = count;
 277  
 278      return ZYAN_STATUS_SUCCESS;
 279  }
 280  
 281  ZyanStatus ZyanStringViewInsideBuffer(ZyanStringView* view, const char* string)
 282  {
 283      if (!view || !string)
 284      {
 285          return ZYAN_STATUS_INVALID_ARGUMENT;
 286      }
 287  
 288      view->string.vector.data = (void*)string;
 289      view->string.vector.size = ZYAN_STRLEN(string) + 1;
 290  
 291      return ZYAN_STATUS_SUCCESS;
 292  }
 293  
 294  ZyanStatus ZyanStringViewInsideBufferEx(ZyanStringView* view, const char* buffer, ZyanUSize length)
 295  {
 296      if (!view || !buffer || !length)
 297      {
 298          return ZYAN_STATUS_INVALID_ARGUMENT;
 299      }
 300  
 301      view->string.vector.data = (void*)buffer;
 302      view->string.vector.size = length + 1;
 303  
 304      return ZYAN_STATUS_SUCCESS;
 305  }
 306  
 307  ZyanStatus ZyanStringViewGetSize(const ZyanStringView* view, ZyanUSize* size)
 308  {
 309      if (!view || !size)
 310      {
 311          return ZYAN_STATUS_INVALID_ARGUMENT;
 312      }
 313  
 314      ZYAN_ASSERT(view->string.vector.size >= 1);
 315      *size = view->string.vector.size - 1;
 316  
 317      return ZYAN_STATUS_SUCCESS;
 318  }
 319  
 320  ZYCORE_EXPORT ZyanStatus ZyanStringViewGetData(const ZyanStringView* view, const char** buffer)
 321  {
 322      if (!view || !buffer)
 323      {
 324          return ZYAN_STATUS_INVALID_ARGUMENT;
 325      }
 326  
 327      *buffer = view->string.vector.data;
 328  
 329      return ZYAN_STATUS_SUCCESS;
 330  }
 331  
 332  /* ---------------------------------------------------------------------------------------------- */
 333  /* Character access                                                                               */
 334  /* ---------------------------------------------------------------------------------------------- */
 335  
 336  ZyanStatus ZyanStringGetChar(const ZyanStringView* string, ZyanUSize index, char* value)
 337  {
 338      if (!string || !value)
 339      {
 340          return ZYAN_STATUS_INVALID_ARGUMENT;
 341      }
 342  
 343      // Don't allow direct access to the terminating '\0' character
 344      if (index + 1 >= string->string.vector.size)
 345      {
 346          return ZYAN_STATUS_OUT_OF_RANGE;
 347      }
 348  
 349      const char* chr;
 350      ZYAN_CHECK(ZyanVectorGetPointer(&string->string.vector, index, (const void**)&chr));
 351      *value = *chr;
 352  
 353      return ZYAN_STATUS_SUCCESS;
 354  }
 355  
 356  ZyanStatus ZyanStringGetCharMutable(ZyanString* string, ZyanUSize index, char** value)
 357  {
 358      if (!string)
 359      {
 360          return ZYAN_STATUS_INVALID_ARGUMENT;
 361      }
 362  
 363      // Don't allow direct access to the terminating '\0' character
 364      if (index + 1 >= string->vector.size)
 365      {
 366          return ZYAN_STATUS_OUT_OF_RANGE;
 367      }
 368  
 369      return ZyanVectorGetPointerMutable(&string->vector, index, (void**)value);
 370  }
 371  
 372  ZyanStatus ZyanStringSetChar(ZyanString* string, ZyanUSize index, char value)
 373  {
 374      if (!string)
 375      {
 376          return ZYAN_STATUS_INVALID_ARGUMENT;
 377      }
 378  
 379      // Don't allow direct access to the terminating '\0' character
 380      if (index + 1 >= string->vector.size)
 381      {
 382          return ZYAN_STATUS_OUT_OF_RANGE;
 383      }
 384  
 385      return ZyanVectorSet(&string->vector, index, (void*)&value);
 386  }
 387  
 388  /* ---------------------------------------------------------------------------------------------- */
 389  /* Insertion                                                                                      */
 390  /* ---------------------------------------------------------------------------------------------- */
 391  
 392  ZyanStatus ZyanStringInsert(ZyanString* destination, ZyanUSize index, const ZyanStringView* source)
 393  {
 394      if (!destination || !source || !source->string.vector.size)
 395      {
 396          return ZYAN_STATUS_INVALID_ARGUMENT;
 397      }
 398  
 399      if (index == destination->vector.size)
 400      {
 401          return ZyanStringAppend(destination, source);
 402      }
 403  
 404      // Don't allow insertion after the terminating '\0' character
 405      if (index >= destination->vector.size)
 406      {
 407          return ZYAN_STATUS_OUT_OF_RANGE;
 408      }
 409  
 410      ZYAN_CHECK(ZyanVectorInsertRange(&destination->vector, index, source->string.vector.data,
 411          source->string.vector.size - 1));
 412      ZYCORE_STRING_ASSERT_NULLTERMINATION(destination);
 413  
 414      return ZYAN_STATUS_SUCCESS;
 415  }
 416  
 417  ZyanStatus ZyanStringInsertEx(ZyanString* destination, ZyanUSize destination_index,
 418      const ZyanStringView* source, ZyanUSize source_index, ZyanUSize count)
 419  {
 420      if (!destination || !source || !source->string.vector.size)
 421      {
 422          return ZYAN_STATUS_INVALID_ARGUMENT;
 423      }
 424  
 425      if (destination_index == destination->vector.size)
 426      {
 427          return ZyanStringAppendEx(destination, source, source_index, count);
 428      }
 429  
 430      // Don't allow insertion after the terminating '\0' character
 431      if (destination_index >= destination->vector.size)
 432      {
 433          return ZYAN_STATUS_OUT_OF_RANGE;
 434      }
 435  
 436      // Don't allow access to the terminating '\0' character
 437      if (source_index + count >= source->string.vector.size)
 438      {
 439          return ZYAN_STATUS_OUT_OF_RANGE;
 440      }
 441  
 442      ZYAN_CHECK(ZyanVectorInsertRange(&destination->vector, destination_index,
 443          (char*)source->string.vector.data + source_index, count));
 444      ZYCORE_STRING_ASSERT_NULLTERMINATION(destination);
 445  
 446      return ZYAN_STATUS_SUCCESS;
 447  }
 448  
 449  /* ---------------------------------------------------------------------------------------------- */
 450  /* Appending                                                                                      */
 451  /* ---------------------------------------------------------------------------------------------- */
 452  
 453  ZyanStatus ZyanStringAppend(ZyanString* destination, const ZyanStringView* source)
 454  {
 455      if (!destination || !source || !source->string.vector.size)
 456      {
 457          return ZYAN_STATUS_INVALID_ARGUMENT;
 458      }
 459  
 460      const ZyanUSize len = destination->vector.size;
 461      ZYAN_CHECK(ZyanVectorResize(&destination->vector, len + source->string.vector.size - 1));
 462      ZYAN_MEMCPY((char*)destination->vector.data + len - 1, source->string.vector.data,
 463          source->string.vector.size - 1);
 464      ZYCORE_STRING_NULLTERMINATE(destination);
 465  
 466      return ZYAN_STATUS_SUCCESS;
 467  }
 468  
 469  ZyanStatus ZyanStringAppendEx(ZyanString* destination, const ZyanStringView* source,
 470      ZyanUSize source_index, ZyanUSize count)
 471  {
 472      if (!destination || !source || !source->string.vector.size)
 473      {
 474          return ZYAN_STATUS_INVALID_ARGUMENT;
 475      }
 476  
 477      // Don't allow access to the terminating '\0' character
 478      if (source_index + count >= source->string.vector.size)
 479      {
 480          return ZYAN_STATUS_OUT_OF_RANGE;
 481      }
 482  
 483      const ZyanUSize len = destination->vector.size;
 484      ZYAN_CHECK(ZyanVectorResize(&destination->vector, len + count));
 485      ZYAN_MEMCPY((char*)destination->vector.data + len - 1,
 486          (const char*)source->string.vector.data + source_index, count);
 487      ZYCORE_STRING_NULLTERMINATE(destination);
 488  
 489      return ZYAN_STATUS_SUCCESS;
 490  }
 491  
 492  /* ---------------------------------------------------------------------------------------------- */
 493  /* Deletion                                                                                       */
 494  /* ---------------------------------------------------------------------------------------------- */
 495  
 496  ZyanStatus ZyanStringDelete(ZyanString* string, ZyanUSize index, ZyanUSize count)
 497  {
 498      if (!string)
 499      {
 500          return ZYAN_STATUS_INVALID_ARGUMENT;
 501      }
 502  
 503      // Don't allow removal of the terminating '\0' character
 504      if (index + count >= string->vector.size)
 505      {
 506          return ZYAN_STATUS_OUT_OF_RANGE;
 507      }
 508  
 509      ZYAN_CHECK(ZyanVectorDeleteRange(&string->vector, index, count));
 510      ZYCORE_STRING_NULLTERMINATE(string);
 511  
 512      return ZYAN_STATUS_SUCCESS;
 513  }
 514  
 515  ZyanStatus ZyanStringTruncate(ZyanString* string, ZyanUSize index)
 516  {
 517      if (!string)
 518      {
 519          return ZYAN_STATUS_INVALID_ARGUMENT;
 520      }
 521  
 522      // Don't allow removal of the terminating '\0' character
 523      if (index >= string->vector.size)
 524      {
 525          return ZYAN_STATUS_OUT_OF_RANGE;
 526      }
 527  
 528      ZYAN_CHECK(ZyanVectorDeleteRange(&string->vector, index, string->vector.size - index - 1));
 529      ZYCORE_STRING_NULLTERMINATE(string);
 530  
 531      return ZYAN_STATUS_SUCCESS;
 532  }
 533  
 534  ZyanStatus ZyanStringClear(ZyanString* string)
 535  {
 536      if (!string)
 537      {
 538          return ZYAN_STATUS_INVALID_ARGUMENT;
 539      }
 540  
 541      ZYAN_CHECK(ZyanVectorClear(&string->vector));
 542      // `ZyanVector` guarantees a minimum capacity of 1 element/character
 543      ZYAN_ASSERT(string->vector.capacity >= 1);
 544  
 545      *(char*)string->vector.data = '\0';
 546      string->vector.size++;
 547  
 548      return ZYAN_STATUS_SUCCESS;
 549  }
 550  
 551  /* ---------------------------------------------------------------------------------------------- */
 552  /* Searching                                                                                      */
 553  /* ---------------------------------------------------------------------------------------------- */
 554  
 555  ZyanStatus ZyanStringLPos(const ZyanStringView* haystack, const ZyanStringView* needle,
 556      ZyanISize* found_index)
 557  {
 558      if (!haystack)
 559      {
 560          return ZYAN_STATUS_INVALID_ARGUMENT;
 561      }
 562  
 563      return ZyanStringLPosEx(haystack, needle, found_index, 0, haystack->string.vector.size - 1);
 564  }
 565  
 566  ZyanStatus ZyanStringLPosEx(const ZyanStringView* haystack, const ZyanStringView* needle,
 567      ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
 568  {
 569      if (!haystack || !needle || !found_index)
 570      {
 571          return ZYAN_STATUS_INVALID_ARGUMENT;
 572      }
 573  
 574      // Don't allow access to the terminating '\0' character
 575      if (index + count >= haystack->string.vector.size)
 576      {
 577          return ZYAN_STATUS_OUT_OF_RANGE;
 578      }
 579  
 580      if ((haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
 581          (haystack->string.vector.size < needle->string.vector.size))
 582      {
 583          *found_index = -1;
 584          return ZYAN_STATUS_FALSE;
 585      }
 586  
 587      const char* s = (const char*)haystack->string.vector.data + index;
 588      const char* b = (const char*)needle->string.vector.data;
 589      for (; s + 1 < (const char*)haystack->string.vector.data + haystack->string.vector.size; ++s)
 590      {
 591          if (*s != *b)
 592          {
 593              continue;
 594          }
 595          const char* a = s;
 596          for (;;)
 597          {
 598              if ((ZyanUSize)(a - (const char*)haystack->string.vector.data) > index + count)
 599              {
 600                  *found_index = -1;
 601                  return ZYAN_STATUS_FALSE;
 602              }
 603              if (*b == 0)
 604              {
 605                  *found_index = (ZyanISize)(s - (const char*)haystack->string.vector.data);
 606                  return ZYAN_STATUS_TRUE;
 607              }
 608              if (*a++ != *b++)
 609              {
 610                  break;
 611              }
 612          }
 613          b = (char*)needle->string.vector.data;
 614      }
 615  
 616      *found_index = -1;
 617      return ZYAN_STATUS_FALSE;
 618  }
 619  
 620  ZyanStatus ZyanStringLPosI(const ZyanStringView* haystack, const ZyanStringView* needle,
 621      ZyanISize* found_index)
 622  {
 623      if (!haystack)
 624      {
 625          return ZYAN_STATUS_INVALID_ARGUMENT;
 626      }
 627  
 628      return ZyanStringLPosIEx(haystack, needle, found_index, 0, haystack->string.vector.size - 1);
 629  }
 630  
 631  ZyanStatus ZyanStringLPosIEx(const ZyanStringView* haystack, const ZyanStringView* needle,
 632      ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
 633  {
 634      // This solution assumes that characters are represented using ASCII representation, i.e.,
 635      // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
 636      // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
 637  
 638      if (!haystack || !needle || !found_index)
 639      {
 640          return ZYAN_STATUS_INVALID_ARGUMENT;
 641      }
 642  
 643      // Don't allow access to the terminating '\0' character
 644      if (index + count >= haystack->string.vector.size)
 645      {
 646          return ZYAN_STATUS_OUT_OF_RANGE;
 647      }
 648  
 649      if ((haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
 650          (haystack->string.vector.size < needle->string.vector.size))
 651      {
 652          *found_index = -1;
 653          return ZYAN_STATUS_FALSE;
 654      }
 655  
 656      const char* s = (const char*)haystack->string.vector.data + index;
 657      const char* b = (const char*)needle->string.vector.data;
 658      for (; s + 1 < (const char*)haystack->string.vector.data + haystack->string.vector.size; ++s)
 659      {
 660          if ((*s != *b) && ((*s ^ 32) != *b))
 661          {
 662              continue;
 663          }
 664          const char* a = s;
 665          for (;;)
 666          {
 667              if ((ZyanUSize)(a - (const char*)haystack->string.vector.data) > index + count)
 668              {
 669                  *found_index = -1;
 670                  return ZYAN_STATUS_FALSE;
 671              }
 672              if (*b == 0)
 673              {
 674                  *found_index = (ZyanISize)(s - (const char*)haystack->string.vector.data);
 675                  return ZYAN_STATUS_TRUE;
 676              }
 677              const char c1 = *a++;
 678              const char c2 = *b++;
 679              if ((c1 != c2) && ((c1 ^ 32) != c2))
 680              {
 681                  break;
 682              }
 683          }
 684          b = (char*)needle->string.vector.data;
 685      }
 686  
 687      *found_index = -1;
 688      return ZYAN_STATUS_FALSE;
 689  }
 690  
 691  ZyanStatus ZyanStringRPos(const ZyanStringView* haystack, const ZyanStringView* needle,
 692      ZyanISize* found_index)
 693  {
 694      if (!haystack)
 695      {
 696          return ZYAN_STATUS_INVALID_ARGUMENT;
 697      }
 698  
 699      return ZyanStringRPosEx(haystack, needle, found_index, haystack->string.vector.size - 1,
 700          haystack->string.vector.size - 1);
 701  }
 702  
 703  ZyanStatus ZyanStringRPosEx(const ZyanStringView* haystack, const ZyanStringView* needle,
 704      ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
 705  {
 706      if (!haystack || !needle || !found_index)
 707      {
 708          return ZYAN_STATUS_INVALID_ARGUMENT;
 709      }
 710  
 711      // Don't allow access to the terminating '\0' character
 712      if ((index >= haystack->string.vector.size) || (count > index))
 713      {
 714          return ZYAN_STATUS_OUT_OF_RANGE;
 715      }
 716  
 717      if (!index || !count ||
 718          (haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
 719          (haystack->string.vector.size < needle->string.vector.size))
 720      {
 721          *found_index = -1;
 722          return ZYAN_STATUS_FALSE;
 723      }
 724  
 725      const char* s = (const char*)haystack->string.vector.data + index - 1;
 726      const char* b = (const char*)needle->string.vector.data + needle->string.vector.size - 2;
 727      for (; s >= (const char*)haystack->string.vector.data; --s)
 728      {
 729          if (*s != *b)
 730          {
 731              continue;
 732          }
 733          const char* a = s;
 734          for (;;)
 735          {
 736              if (b < (const char*)needle->string.vector.data)
 737              {
 738                  *found_index = (ZyanISize)(a - (const char*)haystack->string.vector.data + 1);
 739                  return ZYAN_STATUS_TRUE;
 740              }
 741              if (a < (const char*)haystack->string.vector.data + index - count)
 742              {
 743                  *found_index = -1;
 744                  return ZYAN_STATUS_FALSE;
 745              }
 746              if (*a-- != *b--)
 747              {
 748                  break;
 749              }
 750          }
 751          b = (char*)needle->string.vector.data + needle->string.vector.size - 2;
 752      }
 753  
 754      *found_index = -1;
 755      return ZYAN_STATUS_FALSE;
 756  }
 757  
 758  ZyanStatus ZyanStringRPosI(const ZyanStringView* haystack, const ZyanStringView* needle,
 759      ZyanISize* found_index)
 760  {
 761      if (!haystack)
 762      {
 763          return ZYAN_STATUS_INVALID_ARGUMENT;
 764      }
 765  
 766      return ZyanStringRPosIEx(haystack, needle, found_index, haystack->string.vector.size - 1,
 767          haystack->string.vector.size - 1);
 768  }
 769  
 770  ZyanStatus ZyanStringRPosIEx(const ZyanStringView* haystack, const ZyanStringView* needle,
 771      ZyanISize* found_index, ZyanUSize index, ZyanUSize count)
 772  {
 773      // This solution assumes that characters are represented using ASCII representation, i.e.,
 774      // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
 775      // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
 776  
 777      if (!haystack || !needle || !found_index)
 778      {
 779          return ZYAN_STATUS_INVALID_ARGUMENT;
 780      }
 781  
 782      // Don't allow access to the terminating '\0' character
 783      if ((index >= haystack->string.vector.size) || (count > index))
 784      {
 785          return ZYAN_STATUS_OUT_OF_RANGE;
 786      }
 787  
 788      if (!index || !count ||
 789          (haystack->string.vector.size == 1) || (needle->string.vector.size == 1) ||
 790          (haystack->string.vector.size < needle->string.vector.size))
 791      {
 792          *found_index = -1;
 793          return ZYAN_STATUS_FALSE;
 794      }
 795  
 796      const char* s = (const char*)haystack->string.vector.data + index - 1;
 797      const char* b = (const char*)needle->string.vector.data + needle->string.vector.size - 2;
 798      for (; s >= (const char*)haystack->string.vector.data; --s)
 799      {
 800          if ((*s != *b) && ((*s ^ 32) != *b))
 801          {
 802              continue;
 803          }
 804          const char* a = s;
 805          for (;;)
 806          {
 807              if (b < (const char*)needle->string.vector.data)
 808              {
 809                  *found_index = (ZyanISize)(a - (const char*)haystack->string.vector.data + 1);
 810                  return ZYAN_STATUS_TRUE;
 811              }
 812              if (a < (const char*)haystack->string.vector.data + index - count)
 813              {
 814                  *found_index = -1;
 815                  return ZYAN_STATUS_FALSE;
 816              }
 817              const char c1 = *a--;
 818              const char c2 = *b--;
 819              if ((c1 != c2) && ((c1 ^ 32) != c2))
 820              {
 821                  break;
 822              }
 823          }
 824          b = (char*)needle->string.vector.data + needle->string.vector.size - 2;
 825      }
 826  
 827      *found_index = -1;
 828      return ZYAN_STATUS_FALSE;
 829  }
 830  
 831  /* ---------------------------------------------------------------------------------------------- */
 832  /* Comparing                                                                                      */
 833  /* ---------------------------------------------------------------------------------------------- */
 834  
 835  ZyanStatus ZyanStringCompare(const ZyanStringView* s1, const ZyanStringView* s2, ZyanI32* result)
 836  {
 837      if (!s1 || !s2)
 838      {
 839          return ZYAN_STATUS_INVALID_ARGUMENT;
 840      }
 841  
 842      if (s1->string.vector.size < s2->string.vector.size)
 843      {
 844          *result = -1;
 845          return ZYAN_STATUS_FALSE;
 846      }
 847      if (s1->string.vector.size > s2->string.vector.size)
 848      {
 849          *result =  1;
 850          return ZYAN_STATUS_FALSE;
 851      }
 852  
 853      const char* const a = (char*)s1->string.vector.data;
 854      const char* const b = (char*)s2->string.vector.data;
 855      ZyanUSize i;
 856      for (i = 0; (i + 1 < s1->string.vector.size) && (i + 1 < s2->string.vector.size); ++i)
 857      {
 858          if (a[i] == b[i])
 859          {
 860              continue;
 861          }
 862          break;
 863      }
 864  
 865      if (a[i] == b[i])
 866      {
 867          *result = 0;
 868          return ZYAN_STATUS_TRUE;
 869      }
 870  
 871      if ((a[i] | 32) < (b[i] | 32))
 872      {
 873          *result = -1;
 874          return ZYAN_STATUS_FALSE;
 875      }
 876  
 877      *result = 1;
 878      return ZYAN_STATUS_FALSE;
 879  }
 880  
 881  ZyanStatus ZyanStringCompareI(const ZyanStringView* s1, const ZyanStringView* s2, ZyanI32* result)
 882  {
 883      // This solution assumes that characters are represented using ASCII representation, i.e.,
 884      // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
 885      // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
 886  
 887      if (!s1 || !s2)
 888      {
 889          return ZYAN_STATUS_INVALID_ARGUMENT;
 890      }
 891  
 892      if (s1->string.vector.size < s2->string.vector.size)
 893      {
 894          *result = -1;
 895          return ZYAN_STATUS_FALSE;
 896      }
 897      if (s1->string.vector.size > s2->string.vector.size)
 898      {
 899          *result =  1;
 900          return ZYAN_STATUS_FALSE;
 901      }
 902  
 903      const char* const a = (char*)s1->string.vector.data;
 904      const char* const b = (char*)s2->string.vector.data;
 905      ZyanUSize i;
 906      for (i = 0; (i + 1 < s1->string.vector.size) && (i + 1 < s2->string.vector.size); ++i)
 907      {
 908          if ((a[i] == b[i]) || ((a[i] ^ 32) == b[i]))
 909          {
 910              continue;
 911          }
 912          break;
 913      }
 914  
 915      if (a[i] == b[i])
 916      {
 917          *result = 0;
 918          return ZYAN_STATUS_TRUE;
 919      }
 920  
 921      if ((a[i] | 32) < (b[i] | 32))
 922      {
 923          *result = -1;
 924          return ZYAN_STATUS_FALSE;
 925      }
 926  
 927      *result = 1;
 928      return ZYAN_STATUS_FALSE;
 929  }
 930  
 931  /* ---------------------------------------------------------------------------------------------- */
 932  /* Case conversion                                                                                */
 933  /* ---------------------------------------------------------------------------------------------- */
 934  
 935  ZyanStatus ZyanStringToLowerCase(ZyanString* string)
 936  {
 937      if (!string)
 938      {
 939          return ZYAN_STATUS_INVALID_ARGUMENT;
 940      }
 941  
 942      return ZyanStringToLowerCaseEx(string, 0, string->vector.size - 1);
 943  }
 944  
 945  ZyanStatus ZyanStringToLowerCaseEx(ZyanString* string, ZyanUSize index, ZyanUSize count)
 946  {
 947      // This solution assumes that characters are represented using ASCII representation, i.e.,
 948      // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
 949      // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
 950  
 951      if (!string)
 952      {
 953          return ZYAN_STATUS_INVALID_ARGUMENT;
 954      }
 955  
 956      // Don't allow access to the terminating '\0' character
 957      if (index + count >= string->vector.size)
 958      {
 959          return ZYAN_STATUS_OUT_OF_RANGE;
 960      }
 961  
 962      char* s = (char*)string->vector.data + index;
 963      for (ZyanUSize i = index; i < index + count; ++i)
 964      {
 965          const char c = *s;
 966          if ((c >= 'A') && (c <= 'Z'))
 967          {
 968              *s = c | 32;
 969          }
 970          ++s;
 971      }
 972  
 973      return ZYAN_STATUS_SUCCESS;
 974  }
 975  
 976  ZyanStatus ZyanStringToUpperCase(ZyanString* string)
 977  {
 978      if (!string)
 979      {
 980          return ZYAN_STATUS_INVALID_ARGUMENT;
 981      }
 982  
 983      return ZyanStringToUpperCaseEx(string, 0, string->vector.size - 1);
 984  }
 985  
 986  ZyanStatus ZyanStringToUpperCaseEx(ZyanString* string, ZyanUSize index, ZyanUSize count)
 987  {
 988      // This solution assumes that characters are represented using ASCII representation, i.e.,
 989      // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A',
 990      // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively.
 991  
 992      if (!string)
 993      {
 994          return ZYAN_STATUS_INVALID_ARGUMENT;
 995      }
 996  
 997      // Don't allow access to the terminating '\0' character
 998      if (index + count >= string->vector.size)
 999      {
1000          return ZYAN_STATUS_OUT_OF_RANGE;
1001      }
1002  
1003      char* s = (char*)string->vector.data + index;
1004      for (ZyanUSize i = index; i < index + count; ++i)
1005      {
1006          const char c = *s;
1007          if ((c >= 'a') && (c <= 'z'))
1008          {
1009              *s = c & ~32;
1010          }
1011          ++s;
1012      }
1013  
1014      return ZYAN_STATUS_SUCCESS;
1015  }
1016  
1017  /* ---------------------------------------------------------------------------------------------- */
1018  /* Memory management                                                                              */
1019  /* ---------------------------------------------------------------------------------------------- */
1020  
1021  ZyanStatus ZyanStringResize(ZyanString* string, ZyanUSize size)
1022  {
1023      if (!string)
1024      {
1025          return ZYAN_STATUS_INVALID_ARGUMENT;
1026      }
1027  
1028      ZYAN_CHECK(ZyanVectorResize(&string->vector, size + 1));
1029      ZYCORE_STRING_NULLTERMINATE(string);
1030  
1031      return ZYAN_STATUS_SUCCESS;
1032  }
1033  
1034  ZyanStatus ZyanStringReserve(ZyanString* string, ZyanUSize capacity)
1035  {
1036      if (!string)
1037      {
1038          return ZYAN_STATUS_INVALID_ARGUMENT;
1039      }
1040  
1041      return ZyanVectorReserve(&string->vector, capacity);
1042  }
1043  
1044  ZyanStatus ZyanStringShrinkToFit(ZyanString* string)
1045  {
1046      if (!string)
1047      {
1048          return ZYAN_STATUS_INVALID_ARGUMENT;
1049      }
1050  
1051      return ZyanVectorShrinkToFit(&string->vector);
1052  }
1053  
1054  /* ---------------------------------------------------------------------------------------------- */
1055  /* Information                                                                                    */
1056  /* ---------------------------------------------------------------------------------------------- */
1057  
1058  ZyanStatus ZyanStringGetCapacity(const ZyanString* string, ZyanUSize* capacity)
1059  {
1060      if (!string)
1061      {
1062          return ZYAN_STATUS_INVALID_ARGUMENT;
1063      }
1064  
1065      ZYAN_ASSERT(string->vector.capacity >= 1);
1066      *capacity = string->vector.capacity - 1;
1067  
1068      return ZYAN_STATUS_SUCCESS;
1069  }
1070  
1071  ZyanStatus ZyanStringGetSize(const ZyanString* string, ZyanUSize* size)
1072  {
1073      if (!string)
1074      {
1075          return ZYAN_STATUS_INVALID_ARGUMENT;
1076      }
1077  
1078      ZYAN_ASSERT(string->vector.size >= 1);
1079      *size = string->vector.size - 1;
1080  
1081      return ZYAN_STATUS_SUCCESS;
1082  }
1083  
1084  ZyanStatus ZyanStringGetData(const ZyanString* string, const char** value)
1085  {
1086      if (!string)
1087      {
1088          return ZYAN_STATUS_INVALID_ARGUMENT;
1089      }
1090  
1091      *value = string->vector.data;
1092  
1093      return ZYAN_STATUS_SUCCESS;
1094  }
1095  
1096  /* ---------------------------------------------------------------------------------------------- */
1097  
1098  /* ============================================================================================== */