dynarmic/externals/zycore/src/String.c

1099 lines
34 KiB
C
Raw Normal View History

/***************************************************************************************************
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 <Zycore/String.h>
#include <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,
float growth_factor, float 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, float growth_factor, float 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, float growth_factor,
float 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;
}
/* ---------------------------------------------------------------------------------------------- */
/* ============================================================================================== */