tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ZycoreString.c (34653B)


      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 "zydis/Zycore/String.h"
     28 #include "zydis/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 /* ============================================================================================== */