summaryrefslogtreecommitdiffstats
path: root/js/src/zydis/Zycore/ZycoreString.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/zydis/Zycore/ZycoreString.c1098
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;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */