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

String.c (34641B)


      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 /* ============================================================================================== */