diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /js/src/zydis/Zycore/ZycoreString.c | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/zydis/Zycore/ZycoreString.c')
-rw-r--r-- | js/src/zydis/Zycore/ZycoreString.c | 1098 |
1 files changed, 1098 insertions, 0 deletions
diff --git a/js/src/zydis/Zycore/ZycoreString.c b/js/src/zydis/Zycore/ZycoreString.c new file mode 100644 index 0000000000..4f11719e78 --- /dev/null +++ b/js/src/zydis/Zycore/ZycoreString.c @@ -0,0 +1,1098 @@ +/*************************************************************************************************** + + Zyan Core Library (Zycore-C) + + Original Author : Florian Bernd + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + +***************************************************************************************************/ + +#include "zydis/Zycore/String.h" +#include "zydis/Zycore/LibC.h" + +/* ============================================================================================== */ +/* Internal macros */ +/* ============================================================================================== */ + +/** + * Writes a terminating '\0' character at the end of the string data. + */ +#define ZYCORE_STRING_NULLTERMINATE(string) \ + *(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) = '\0'; + +/** + * Checks for a terminating '\0' character at the end of the string data. + */ +#define ZYCORE_STRING_ASSERT_NULLTERMINATION(string) \ + ZYAN_ASSERT(*(char*)((ZyanU8*)(string)->vector.data + (string)->vector.size - 1) == '\0'); + +/* ============================================================================================== */ +/* Exported functions */ +/* ============================================================================================== */ + +/* ---------------------------------------------------------------------------------------------- */ +/* Constructor and destructor */ +/* ---------------------------------------------------------------------------------------------- */ + +#ifndef ZYAN_NO_LIBC + +ZyanStatus ZyanStringInit(ZyanString* string, ZyanUSize capacity) +{ + return ZyanStringInitEx(string, capacity, ZyanAllocatorDefault(), + ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD); +} + +#endif // ZYAN_NO_LIBC + +ZyanStatus ZyanStringInitEx(ZyanString* string, ZyanUSize capacity, ZyanAllocator* allocator, + ZyanU8 growth_factor, ZyanU8 shrink_threshold) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + string->flags = 0; + capacity = ZYAN_MAX(ZYAN_STRING_MIN_CAPACITY, capacity) + 1; + ZYAN_CHECK(ZyanVectorInitEx(&string->vector, sizeof(char), capacity, ZYAN_NULL, allocator, + growth_factor, shrink_threshold)); + ZYAN_ASSERT(string->vector.capacity >= capacity); + // Some of the string code relies on `sizeof(char) == 1` + ZYAN_ASSERT(string->vector.element_size == 1); + + *(char*)string->vector.data = '\0'; + ++string->vector.size; + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringInitCustomBuffer(ZyanString* string, char* buffer, ZyanUSize capacity) +{ + if (!string || !capacity) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + string->flags = ZYAN_STRING_HAS_FIXED_CAPACITY; + ZYAN_CHECK(ZyanVectorInitCustomBuffer(&string->vector, sizeof(char), (void*)buffer, capacity, + ZYAN_NULL)); + ZYAN_ASSERT(string->vector.capacity == capacity); + // Some of the string code relies on `sizeof(char) == 1` + ZYAN_ASSERT(string->vector.element_size == 1); + + *(char*)string->vector.data = '\0'; + ++string->vector.size; + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringDestroy(ZyanString* string) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + if (string->flags & ZYAN_STRING_HAS_FIXED_CAPACITY) + { + return ZYAN_STATUS_SUCCESS; + } + + return ZyanVectorDestroy(&string->vector); +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Duplication */ +/* ---------------------------------------------------------------------------------------------- */ + +#ifndef ZYAN_NO_LIBC + +ZyanStatus ZyanStringDuplicate(ZyanString* destination, const ZyanStringView* source, + ZyanUSize capacity) +{ + return ZyanStringDuplicateEx(destination, source, capacity, ZyanAllocatorDefault(), + ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD); +} + +#endif // ZYAN_NO_LIBC + +ZyanStatus ZyanStringDuplicateEx(ZyanString* destination, const ZyanStringView* source, + ZyanUSize capacity, ZyanAllocator* allocator, ZyanU8 growth_factor, ZyanU8 shrink_threshold) +{ + if (!source || !source->string.vector.size) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + const ZyanUSize len = source->string.vector.size; + capacity = ZYAN_MAX(capacity, len - 1); + ZYAN_CHECK(ZyanStringInitEx(destination, capacity, allocator, growth_factor, shrink_threshold)); + ZYAN_ASSERT(destination->vector.capacity >= len); + + ZYAN_MEMCPY(destination->vector.data, source->string.vector.data, + source->string.vector.size - 1); + destination->vector.size = len; + ZYCORE_STRING_NULLTERMINATE(destination); + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringDuplicateCustomBuffer(ZyanString* destination, const ZyanStringView* source, + char* buffer, ZyanUSize capacity) +{ + if (!source || !source->string.vector.size) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + const ZyanUSize len = source->string.vector.size; + if (capacity < len) + { + return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE; + } + + ZYAN_CHECK(ZyanStringInitCustomBuffer(destination, buffer, capacity)); + ZYAN_ASSERT(destination->vector.capacity >= len); + + ZYAN_MEMCPY(destination->vector.data, source->string.vector.data, + source->string.vector.size - 1); + destination->vector.size = len; + ZYCORE_STRING_NULLTERMINATE(destination); + + return ZYAN_STATUS_SUCCESS; +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Concatenation */ +/* ---------------------------------------------------------------------------------------------- */ + +#ifndef ZYAN_NO_LIBC + +ZyanStatus ZyanStringConcat(ZyanString* destination, const ZyanStringView* s1, + const ZyanStringView* s2, ZyanUSize capacity) +{ + return ZyanStringConcatEx(destination, s1, s2, capacity, ZyanAllocatorDefault(), + ZYAN_STRING_DEFAULT_GROWTH_FACTOR, ZYAN_STRING_DEFAULT_SHRINK_THRESHOLD); +} + +#endif // ZYAN_NO_LIBC + +ZyanStatus ZyanStringConcatEx(ZyanString* destination, const ZyanStringView* s1, + const ZyanStringView* s2, ZyanUSize capacity, ZyanAllocator* allocator, ZyanU8 growth_factor, + ZyanU8 shrink_threshold) +{ + if (!s1 || !s2 || !s1->string.vector.size || !s2->string.vector.size) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + const ZyanUSize len = s1->string.vector.size + s2->string.vector.size - 1; + capacity = ZYAN_MAX(capacity, len - 1); + ZYAN_CHECK(ZyanStringInitEx(destination, capacity, allocator, growth_factor, shrink_threshold)); + ZYAN_ASSERT(destination->vector.capacity >= len); + + ZYAN_MEMCPY(destination->vector.data, s1->string.vector.data, s1->string.vector.size - 1); + ZYAN_MEMCPY((char*)destination->vector.data + s1->string.vector.size - 1, + s2->string.vector.data, s2->string.vector.size - 1); + destination->vector.size = len; + ZYCORE_STRING_NULLTERMINATE(destination); + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringConcatCustomBuffer(ZyanString* destination, const ZyanStringView* s1, + const ZyanStringView* s2, char* buffer, ZyanUSize capacity) +{ + if (!s1 || !s2 || !s1->string.vector.size || !s2->string.vector.size) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + const ZyanUSize len = s1->string.vector.size + s2->string.vector.size - 1; + if (capacity < len) + { + return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE; + } + + ZYAN_CHECK(ZyanStringInitCustomBuffer(destination, buffer, capacity)); + ZYAN_ASSERT(destination->vector.capacity >= len); + + ZYAN_MEMCPY(destination->vector.data, s1->string.vector.data, s1->string.vector.size - 1); + ZYAN_MEMCPY((char*)destination->vector.data + s1->string.vector.size - 1, + s2->string.vector.data, s2->string.vector.size - 1); + destination->vector.size = len; + ZYCORE_STRING_NULLTERMINATE(destination); + + return ZYAN_STATUS_SUCCESS; +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Views */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringViewInsideView(ZyanStringView* view, const ZyanStringView* source) +{ + if (!view || !source) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + view->string.vector.data = source->string.vector.data; + view->string.vector.size = source->string.vector.size; + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringViewInsideViewEx(ZyanStringView* view, const ZyanStringView* source, + ZyanUSize index, ZyanUSize count) +{ + if (!view || !source) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + if (index + count >= source->string.vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + view->string.vector.data = (void*)((char*)source->string.vector.data + index); + view->string.vector.size = count; + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringViewInsideBuffer(ZyanStringView* view, const char* string) +{ + if (!view || !string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + view->string.vector.data = (void*)string; + view->string.vector.size = ZYAN_STRLEN(string) + 1; + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringViewInsideBufferEx(ZyanStringView* view, const char* buffer, ZyanUSize length) +{ + if (!view || !buffer || !length) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + view->string.vector.data = (void*)buffer; + view->string.vector.size = length + 1; + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringViewGetSize(const ZyanStringView* view, ZyanUSize* size) +{ + if (!view || !size) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + ZYAN_ASSERT(view->string.vector.size >= 1); + *size = view->string.vector.size - 1; + + return ZYAN_STATUS_SUCCESS; +} + +ZYCORE_EXPORT ZyanStatus ZyanStringViewGetData(const ZyanStringView* view, const char** buffer) +{ + if (!view || !buffer) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + *buffer = view->string.vector.data; + + return ZYAN_STATUS_SUCCESS; +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Character access */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringGetChar(const ZyanStringView* string, ZyanUSize index, char* value) +{ + if (!string || !value) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow direct access to the terminating '\0' character + if (index + 1 >= string->string.vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + const char* chr; + ZYAN_CHECK(ZyanVectorGetPointer(&string->string.vector, index, (const void**)&chr)); + *value = *chr; + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringGetCharMutable(ZyanString* string, ZyanUSize index, char** value) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow direct access to the terminating '\0' character + if (index + 1 >= string->vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + return ZyanVectorGetPointerMutable(&string->vector, index, (void**)value); +} + +ZyanStatus ZyanStringSetChar(ZyanString* string, ZyanUSize index, char value) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow direct access to the terminating '\0' character + if (index + 1 >= string->vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + return ZyanVectorSet(&string->vector, index, (void*)&value); +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Insertion */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringInsert(ZyanString* destination, ZyanUSize index, const ZyanStringView* source) +{ + if (!destination || !source || !source->string.vector.size) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + if (index == destination->vector.size) + { + return ZyanStringAppend(destination, source); + } + + // Don't allow insertion after the terminating '\0' character + if (index >= destination->vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + ZYAN_CHECK(ZyanVectorInsertRange(&destination->vector, index, source->string.vector.data, + source->string.vector.size - 1)); + ZYCORE_STRING_ASSERT_NULLTERMINATION(destination); + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringInsertEx(ZyanString* destination, ZyanUSize destination_index, + const ZyanStringView* source, ZyanUSize source_index, ZyanUSize count) +{ + if (!destination || !source || !source->string.vector.size) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + if (destination_index == destination->vector.size) + { + return ZyanStringAppendEx(destination, source, source_index, count); + } + + // Don't allow insertion after the terminating '\0' character + if (destination_index >= destination->vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + // Don't allow access to the terminating '\0' character + if (source_index + count >= source->string.vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + ZYAN_CHECK(ZyanVectorInsertRange(&destination->vector, destination_index, + (char*)source->string.vector.data + source_index, count)); + ZYCORE_STRING_ASSERT_NULLTERMINATION(destination); + + return ZYAN_STATUS_SUCCESS; +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Appending */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringAppend(ZyanString* destination, const ZyanStringView* source) +{ + if (!destination || !source || !source->string.vector.size) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + const ZyanUSize len = destination->vector.size; + ZYAN_CHECK(ZyanVectorResize(&destination->vector, len + source->string.vector.size - 1)); + ZYAN_MEMCPY((char*)destination->vector.data + len - 1, source->string.vector.data, + source->string.vector.size - 1); + ZYCORE_STRING_NULLTERMINATE(destination); + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringAppendEx(ZyanString* destination, const ZyanStringView* source, + ZyanUSize source_index, ZyanUSize count) +{ + if (!destination || !source || !source->string.vector.size) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow access to the terminating '\0' character + if (source_index + count >= source->string.vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + const ZyanUSize len = destination->vector.size; + ZYAN_CHECK(ZyanVectorResize(&destination->vector, len + count)); + ZYAN_MEMCPY((char*)destination->vector.data + len - 1, + (const char*)source->string.vector.data + source_index, count); + ZYCORE_STRING_NULLTERMINATE(destination); + + return ZYAN_STATUS_SUCCESS; +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Deletion */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringDelete(ZyanString* string, ZyanUSize index, ZyanUSize count) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow removal of the terminating '\0' character + if (index + count >= string->vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + ZYAN_CHECK(ZyanVectorDeleteRange(&string->vector, index, count)); + ZYCORE_STRING_NULLTERMINATE(string); + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringTruncate(ZyanString* string, ZyanUSize index) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow removal of the terminating '\0' character + if (index >= string->vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + ZYAN_CHECK(ZyanVectorDeleteRange(&string->vector, index, string->vector.size - index - 1)); + ZYCORE_STRING_NULLTERMINATE(string); + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringClear(ZyanString* string) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + ZYAN_CHECK(ZyanVectorClear(&string->vector)); + // `ZyanVector` guarantees a minimum capacity of 1 element/character + ZYAN_ASSERT(string->vector.capacity >= 1); + + *(char*)string->vector.data = '\0'; + string->vector.size++; + + return ZYAN_STATUS_SUCCESS; +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Searching */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringLPos(const ZyanStringView* haystack, const ZyanStringView* needle, + ZyanISize* found_index) +{ + if (!haystack) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + return ZyanStringLPosEx(haystack, needle, found_index, 0, haystack->string.vector.size - 1); +} + +ZyanStatus ZyanStringLPosEx(const ZyanStringView* haystack, const ZyanStringView* needle, + ZyanISize* found_index, ZyanUSize index, ZyanUSize count) +{ + if (!haystack || !needle || !found_index) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow access to the terminating '\0' character + if (index + count >= haystack->string.vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + if ((haystack->string.vector.size == 1) || (needle->string.vector.size == 1) || + (haystack->string.vector.size < needle->string.vector.size)) + { + *found_index = -1; + return ZYAN_STATUS_FALSE; + } + + const char* s = (const char*)haystack->string.vector.data + index; + const char* b = (const char*)needle->string.vector.data; + for (; s + 1 < (const char*)haystack->string.vector.data + haystack->string.vector.size; ++s) + { + if (*s != *b) + { + continue; + } + const char* a = s; + for (;;) + { + if ((ZyanUSize)(a - (const char*)haystack->string.vector.data) > index + count) + { + *found_index = -1; + return ZYAN_STATUS_FALSE; + } + if (*b == 0) + { + *found_index = (ZyanISize)(s - (const char*)haystack->string.vector.data); + return ZYAN_STATUS_TRUE; + } + if (*a++ != *b++) + { + break; + } + } + b = (char*)needle->string.vector.data; + } + + *found_index = -1; + return ZYAN_STATUS_FALSE; +} + +ZyanStatus ZyanStringLPosI(const ZyanStringView* haystack, const ZyanStringView* needle, + ZyanISize* found_index) +{ + if (!haystack) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + return ZyanStringLPosIEx(haystack, needle, found_index, 0, haystack->string.vector.size - 1); +} + +ZyanStatus ZyanStringLPosIEx(const ZyanStringView* haystack, const ZyanStringView* needle, + ZyanISize* found_index, ZyanUSize index, ZyanUSize count) +{ + // This solution assumes that characters are represented using ASCII representation, i.e., + // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A', + // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively. + + if (!haystack || !needle || !found_index) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow access to the terminating '\0' character + if (index + count >= haystack->string.vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + if ((haystack->string.vector.size == 1) || (needle->string.vector.size == 1) || + (haystack->string.vector.size < needle->string.vector.size)) + { + *found_index = -1; + return ZYAN_STATUS_FALSE; + } + + const char* s = (const char*)haystack->string.vector.data + index; + const char* b = (const char*)needle->string.vector.data; + for (; s + 1 < (const char*)haystack->string.vector.data + haystack->string.vector.size; ++s) + { + if ((*s != *b) && ((*s ^ 32) != *b)) + { + continue; + } + const char* a = s; + for (;;) + { + if ((ZyanUSize)(a - (const char*)haystack->string.vector.data) > index + count) + { + *found_index = -1; + return ZYAN_STATUS_FALSE; + } + if (*b == 0) + { + *found_index = (ZyanISize)(s - (const char*)haystack->string.vector.data); + return ZYAN_STATUS_TRUE; + } + const char c1 = *a++; + const char c2 = *b++; + if ((c1 != c2) && ((c1 ^ 32) != c2)) + { + break; + } + } + b = (char*)needle->string.vector.data; + } + + *found_index = -1; + return ZYAN_STATUS_FALSE; +} + +ZyanStatus ZyanStringRPos(const ZyanStringView* haystack, const ZyanStringView* needle, + ZyanISize* found_index) +{ + if (!haystack) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + return ZyanStringRPosEx(haystack, needle, found_index, haystack->string.vector.size - 1, + haystack->string.vector.size - 1); +} + +ZyanStatus ZyanStringRPosEx(const ZyanStringView* haystack, const ZyanStringView* needle, + ZyanISize* found_index, ZyanUSize index, ZyanUSize count) +{ + if (!haystack || !needle || !found_index) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow access to the terminating '\0' character + if ((index >= haystack->string.vector.size) || (count > index)) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + if (!index || !count || + (haystack->string.vector.size == 1) || (needle->string.vector.size == 1) || + (haystack->string.vector.size < needle->string.vector.size)) + { + *found_index = -1; + return ZYAN_STATUS_FALSE; + } + + const char* s = (const char*)haystack->string.vector.data + index - 1; + const char* b = (const char*)needle->string.vector.data + needle->string.vector.size - 2; + for (; s >= (const char*)haystack->string.vector.data; --s) + { + if (*s != *b) + { + continue; + } + const char* a = s; + for (;;) + { + if (b < (const char*)needle->string.vector.data) + { + *found_index = (ZyanISize)(a - (const char*)haystack->string.vector.data + 1); + return ZYAN_STATUS_TRUE; + } + if (a < (const char*)haystack->string.vector.data + index - count) + { + *found_index = -1; + return ZYAN_STATUS_FALSE; + } + if (*a-- != *b--) + { + break; + } + } + b = (char*)needle->string.vector.data + needle->string.vector.size - 2; + } + + *found_index = -1; + return ZYAN_STATUS_FALSE; +} + +ZyanStatus ZyanStringRPosI(const ZyanStringView* haystack, const ZyanStringView* needle, + ZyanISize* found_index) +{ + if (!haystack) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + return ZyanStringRPosIEx(haystack, needle, found_index, haystack->string.vector.size - 1, + haystack->string.vector.size - 1); +} + +ZyanStatus ZyanStringRPosIEx(const ZyanStringView* haystack, const ZyanStringView* needle, + ZyanISize* found_index, ZyanUSize index, ZyanUSize count) +{ + // This solution assumes that characters are represented using ASCII representation, i.e., + // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A', + // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively. + + if (!haystack || !needle || !found_index) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow access to the terminating '\0' character + if ((index >= haystack->string.vector.size) || (count > index)) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + if (!index || !count || + (haystack->string.vector.size == 1) || (needle->string.vector.size == 1) || + (haystack->string.vector.size < needle->string.vector.size)) + { + *found_index = -1; + return ZYAN_STATUS_FALSE; + } + + const char* s = (const char*)haystack->string.vector.data + index - 1; + const char* b = (const char*)needle->string.vector.data + needle->string.vector.size - 2; + for (; s >= (const char*)haystack->string.vector.data; --s) + { + if ((*s != *b) && ((*s ^ 32) != *b)) + { + continue; + } + const char* a = s; + for (;;) + { + if (b < (const char*)needle->string.vector.data) + { + *found_index = (ZyanISize)(a - (const char*)haystack->string.vector.data + 1); + return ZYAN_STATUS_TRUE; + } + if (a < (const char*)haystack->string.vector.data + index - count) + { + *found_index = -1; + return ZYAN_STATUS_FALSE; + } + const char c1 = *a--; + const char c2 = *b--; + if ((c1 != c2) && ((c1 ^ 32) != c2)) + { + break; + } + } + b = (char*)needle->string.vector.data + needle->string.vector.size - 2; + } + + *found_index = -1; + return ZYAN_STATUS_FALSE; +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Comparing */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringCompare(const ZyanStringView* s1, const ZyanStringView* s2, ZyanI32* result) +{ + if (!s1 || !s2) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + if (s1->string.vector.size < s2->string.vector.size) + { + *result = -1; + return ZYAN_STATUS_FALSE; + } + if (s1->string.vector.size > s2->string.vector.size) + { + *result = 1; + return ZYAN_STATUS_FALSE; + } + + const char* const a = (char*)s1->string.vector.data; + const char* const b = (char*)s2->string.vector.data; + ZyanUSize i; + for (i = 0; (i + 1 < s1->string.vector.size) && (i + 1 < s2->string.vector.size); ++i) + { + if (a[i] == b[i]) + { + continue; + } + break; + } + + if (a[i] == b[i]) + { + *result = 0; + return ZYAN_STATUS_TRUE; + } + + if ((a[i] | 32) < (b[i] | 32)) + { + *result = -1; + return ZYAN_STATUS_FALSE; + } + + *result = 1; + return ZYAN_STATUS_FALSE; +} + +ZyanStatus ZyanStringCompareI(const ZyanStringView* s1, const ZyanStringView* s2, ZyanI32* result) +{ + // This solution assumes that characters are represented using ASCII representation, i.e., + // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A', + // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively. + + if (!s1 || !s2) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + if (s1->string.vector.size < s2->string.vector.size) + { + *result = -1; + return ZYAN_STATUS_FALSE; + } + if (s1->string.vector.size > s2->string.vector.size) + { + *result = 1; + return ZYAN_STATUS_FALSE; + } + + const char* const a = (char*)s1->string.vector.data; + const char* const b = (char*)s2->string.vector.data; + ZyanUSize i; + for (i = 0; (i + 1 < s1->string.vector.size) && (i + 1 < s2->string.vector.size); ++i) + { + if ((a[i] == b[i]) || ((a[i] ^ 32) == b[i])) + { + continue; + } + break; + } + + if (a[i] == b[i]) + { + *result = 0; + return ZYAN_STATUS_TRUE; + } + + if ((a[i] | 32) < (b[i] | 32)) + { + *result = -1; + return ZYAN_STATUS_FALSE; + } + + *result = 1; + return ZYAN_STATUS_FALSE; +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Case conversion */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringToLowerCase(ZyanString* string) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + return ZyanStringToLowerCaseEx(string, 0, string->vector.size - 1); +} + +ZyanStatus ZyanStringToLowerCaseEx(ZyanString* string, ZyanUSize index, ZyanUSize count) +{ + // This solution assumes that characters are represented using ASCII representation, i.e., + // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A', + // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively. + + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow access to the terminating '\0' character + if (index + count >= string->vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + char* s = (char*)string->vector.data + index; + for (ZyanUSize i = index; i < index + count; ++i) + { + const char c = *s; + if ((c >= 'A') && (c <= 'Z')) + { + *s = c | 32; + } + ++s; + } + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringToUpperCase(ZyanString* string) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + return ZyanStringToUpperCaseEx(string, 0, string->vector.size - 1); +} + +ZyanStatus ZyanStringToUpperCaseEx(ZyanString* string, ZyanUSize index, ZyanUSize count) +{ + // This solution assumes that characters are represented using ASCII representation, i.e., + // codes for 'a', 'b', 'c', .. 'z' are 97, 98, 99, .. 122 respectively. And codes for 'A', + // 'B', 'C', .. 'Z' are 65, 66, .. 95 respectively. + + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Don't allow access to the terminating '\0' character + if (index + count >= string->vector.size) + { + return ZYAN_STATUS_OUT_OF_RANGE; + } + + char* s = (char*)string->vector.data + index; + for (ZyanUSize i = index; i < index + count; ++i) + { + const char c = *s; + if ((c >= 'a') && (c <= 'z')) + { + *s = c & ~32; + } + ++s; + } + + return ZYAN_STATUS_SUCCESS; +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Memory management */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringResize(ZyanString* string, ZyanUSize size) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + ZYAN_CHECK(ZyanVectorResize(&string->vector, size + 1)); + ZYCORE_STRING_NULLTERMINATE(string); + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringReserve(ZyanString* string, ZyanUSize capacity) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + return ZyanVectorReserve(&string->vector, capacity); +} + +ZyanStatus ZyanStringShrinkToFit(ZyanString* string) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + return ZyanVectorShrinkToFit(&string->vector); +} + +/* ---------------------------------------------------------------------------------------------- */ +/* Information */ +/* ---------------------------------------------------------------------------------------------- */ + +ZyanStatus ZyanStringGetCapacity(const ZyanString* string, ZyanUSize* capacity) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + ZYAN_ASSERT(string->vector.capacity >= 1); + *capacity = string->vector.capacity - 1; + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringGetSize(const ZyanString* string, ZyanUSize* size) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + ZYAN_ASSERT(string->vector.size >= 1); + *size = string->vector.size - 1; + + return ZYAN_STATUS_SUCCESS; +} + +ZyanStatus ZyanStringGetData(const ZyanString* string, const char** value) +{ + if (!string) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + *value = string->vector.data; + + return ZYAN_STATUS_SUCCESS; +} + +/* ---------------------------------------------------------------------------------------------- */ + +/* ============================================================================================== */ |