504 lines
12 KiB
C++
504 lines
12 KiB
C++
/**
|
|
* @file kString.cpp
|
|
*
|
|
* @internal
|
|
* Copyright (C) 2005-2021 by LMI Technologies Inc.
|
|
* Licensed under the MIT License.
|
|
* Redistributed files must retain the above copyright notice.
|
|
*/
|
|
#include <kApi/Data/kString.h>
|
|
#include <kApi/Data/kArrayList.h>
|
|
#include <kApi/Io/kSerializer.h>
|
|
#include <stdio.h>
|
|
|
|
kBeginClassEx(k, kString)
|
|
|
|
//serialization versions
|
|
kAddPrivateVersionEx(kString, "kdat5", "5.0.0.0", "43-1", WriteDat5V1, ReadDat5V1)
|
|
kAddPrivateVersionEx(kString, "kdat6", "5.7.1.0", "kString-0", WriteDat6V0, ReadDat6V0)
|
|
|
|
//special constructors
|
|
kAddPrivateFrameworkConstructor(kString, ConstructFramework)
|
|
|
|
//virtual methods
|
|
kAddPrivateVMethod(kString, kObject, VRelease)
|
|
kAddPrivateVMethod(kString, kObject, VClone)
|
|
kAddPrivateVMethod(kString, kObject, VHashCode)
|
|
kAddPrivateVMethod(kString, kObject, VEquals)
|
|
kAddPrivateVMethod(kString, kObject, VSize)
|
|
|
|
kEndClassEx()
|
|
|
|
kFx(kStatus) kString_Construct(kString* str, const kChar* content, kAlloc allocator)
|
|
{
|
|
kAlloc alloc = kAlloc_Fallback(allocator);
|
|
kStatus status;
|
|
|
|
kCheck(kAlloc_GetObject(alloc, kTypeOf(kString), str));
|
|
|
|
if (!kSuccess(status = xkString_Init(*str, kTypeOf(kString), content, alloc)))
|
|
{
|
|
kAlloc_FreeRef(alloc, str);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
kFx(kStatus) xkString_ConstructFramework(kString* string, kAlloc allocator)
|
|
{
|
|
return kString_Construct(string, kNULL, allocator);
|
|
}
|
|
|
|
kFx(kStatus) xkString_Init(kString str, kType classType, const kChar* content, kAlloc allocator)
|
|
{
|
|
kObjR(kString, str);
|
|
kStatus status = kOK;
|
|
|
|
kCheck(kObject_Init(str, classType, allocator));
|
|
|
|
obj->allocSize = 0;
|
|
obj->chars = &obj->nullStr;
|
|
obj->length = 0;
|
|
obj->capacity = 0;
|
|
obj->nullStr = 0;
|
|
|
|
kTry
|
|
{
|
|
kTest(kString_Import(str, content, kFALSE));
|
|
}
|
|
kCatch(&status)
|
|
{
|
|
xkString_VRelease(str);
|
|
kEndCatch(status);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkString_VClone(kString str, kString source, kAlloc valueAlloc, kObject context)
|
|
{
|
|
return kString_Import(str, kString_Chars(source), kFALSE);
|
|
}
|
|
|
|
kFx(kStatus) xkString_VRelease(kString str)
|
|
{
|
|
kObj(kString, str);
|
|
|
|
if (obj->allocSize > 0)
|
|
{
|
|
kCheck(kObject_FreeMem(str, obj->chars));
|
|
}
|
|
|
|
kCheck(kObject_VRelease(str));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kSize) xkString_VSize(kString str)
|
|
{
|
|
kObj(kString, str);
|
|
kSize size = sizeof(kStringClass) + obj->allocSize;
|
|
|
|
return size;
|
|
}
|
|
|
|
kFx(kSize) xkString_VHashCode(kString str)
|
|
{
|
|
kObj(kString, str);
|
|
const kChar* it = kString_Chars(obj);
|
|
kSize hash = 0;
|
|
|
|
while (!kIsNull(*it))
|
|
{
|
|
hash = hash * 31 + *it++;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
kFx(kBool) xkString_VEquals(kString str, kObject other)
|
|
{
|
|
kAssertType(str, kString);
|
|
|
|
if (kObject_Type(other) == kTypeOf(kString))
|
|
{
|
|
return kStrEquals(kString_Chars(str), kString_Chars(other));
|
|
}
|
|
|
|
return kFALSE;
|
|
}
|
|
|
|
kFx(kStatus) xkString_WriteDat5V1(kString str, kSerializer serializer)
|
|
{
|
|
kObj(kString, str);
|
|
kTypeVersion itemVersion;
|
|
|
|
kCheck(kSerializer_WriteSize(serializer, obj->capacity));
|
|
kCheck(kSerializer_Write32s(serializer, kTRUE)); //isDynamic
|
|
|
|
kCheck(kSerializer_WriteSize(serializer, obj->length));
|
|
kCheck(kSerializer_WriteType(serializer, kTypeOf(kChar), &itemVersion));
|
|
kCheck(kSerializer_WriteItems(serializer, kTypeOf(kChar), itemVersion, obj->chars, obj->length));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkString_ReadDat5V1(kString str, kSerializer serializer)
|
|
{
|
|
kObj(kString, str);
|
|
kSize capacity = 0, length = 0;
|
|
kTypeVersion itemVersion;
|
|
k32s isDynamic;
|
|
kType itemType = kNULL;
|
|
|
|
kCheck(kSerializer_ReadSize(serializer, &capacity));
|
|
kCheck(kSerializer_Read32s(serializer, &isDynamic));
|
|
|
|
kCheck(kSerializer_ReadSize(serializer, &length));
|
|
kCheck(kSerializer_ReadType(serializer, &itemType, &itemVersion));
|
|
|
|
kCheck(kString_Reserve(str, length));
|
|
|
|
kCheck(kSerializer_ReadItems(serializer, itemType, itemVersion, obj->chars, length));
|
|
obj->length = length;
|
|
obj->chars[length] = 0;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkString_WriteDat6V0(kString str, kSerializer serializer)
|
|
{
|
|
kObj(kString, str);
|
|
kTypeVersion itemVersion;
|
|
|
|
kCheck(kSerializer_WriteType(serializer, kTypeOf(kChar), &itemVersion));
|
|
kCheck(kSerializer_WriteSize(serializer, obj->length));
|
|
kCheck(kSerializer_WriteItems(serializer, kTypeOf(kChar), itemVersion, obj->chars, obj->length));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkString_ReadDat6V0(kString str, kSerializer serializer)
|
|
{
|
|
kObj(kString, str);
|
|
kType itemType = kNULL;
|
|
kTypeVersion itemVersion;
|
|
kSize length = 0;
|
|
|
|
kCheck(kSerializer_ReadType(serializer, &itemType, &itemVersion));
|
|
kCheck(kSerializer_ReadSize(serializer, &length));
|
|
|
|
kCheck(kString_Reserve(str, length));
|
|
|
|
kCheck(kSerializer_ReadItems(serializer, itemType, itemVersion, obj->chars, length));
|
|
|
|
obj->length = length;
|
|
obj->chars[length] = 0;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kString_Assign(kString str, kString source)
|
|
{
|
|
return kString_Import(str, kString_Chars(source), kFALSE);
|
|
}
|
|
|
|
kFx(kStatus) kString_Clear(kString str)
|
|
{
|
|
kObj(kString, str);
|
|
|
|
obj->chars[0] = 0;
|
|
obj->length = 0;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kString_Reserve(kString str, kSize minimumCapacity)
|
|
{
|
|
kObj(kString, str);
|
|
kType itemType = kTypeOf(kChar);
|
|
kSize itemSize = kType_Size(itemType);
|
|
|
|
if (obj->capacity < minimumCapacity)
|
|
{
|
|
kSize grownCapacity = kMax_(minimumCapacity, xkSTRING_GROWTH_FACTOR*obj->capacity);
|
|
kSize newCapacity = kMax_(grownCapacity, xkSTRING_MIN_CAPACITY);
|
|
kSize newSize = kMax_(obj->allocSize, (newCapacity+1)*itemSize); //+1 for null terminator
|
|
|
|
if (newSize > obj->allocSize)
|
|
{
|
|
kChar* oldChars = obj->chars;
|
|
kChar* newChars = kNULL;
|
|
|
|
kCheck(kObject_GetMem(str, newSize, &newChars));
|
|
|
|
kMemCopy(newChars, oldChars, (obj->capacity+1)*itemSize); //+1 for null terminator
|
|
|
|
obj->chars = newChars;
|
|
obj->allocSize = newSize;
|
|
|
|
if (oldChars != &obj->nullStr)
|
|
{
|
|
kCheck(kObject_FreeMem(str, oldChars));
|
|
}
|
|
}
|
|
|
|
obj->capacity = newCapacity;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kString_SetLength(kString str, kSize length)
|
|
{
|
|
kObj(kString, str);
|
|
|
|
if (length > obj->capacity)
|
|
{
|
|
kCheck(kString_Reserve(str, length));
|
|
}
|
|
|
|
obj->length = length;
|
|
obj->chars[length] = 0;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(k32s) kString_Compare(kString str, const kChar* content)
|
|
{
|
|
kObj(kString, str);
|
|
|
|
return kStrCompare(kString_Chars(obj), content);
|
|
}
|
|
|
|
kFx(kBool) kString_Equals(kString str, const kChar* content)
|
|
{
|
|
kObj(kString, str);
|
|
|
|
return kStrEquals(kString_Chars(obj), content);
|
|
}
|
|
|
|
kFx(kStatus) kString_Trim(kString str)
|
|
{
|
|
kObj(kString, str);
|
|
kSize it = 0;
|
|
|
|
//remove leading
|
|
while (kChar_IsSpace(obj->chars[it]))
|
|
{
|
|
it++;
|
|
}
|
|
|
|
kCheck(kMemMove(obj->chars, &obj->chars[it], obj->length + 1 - it));
|
|
obj->length -= it;
|
|
|
|
//remove trailing
|
|
if (obj->length > 0)
|
|
{
|
|
it = obj->length - 1;
|
|
|
|
while ((it > 0) && kChar_IsSpace(obj->chars[it]))
|
|
{
|
|
it--;
|
|
}
|
|
|
|
obj->length = it + 1;
|
|
obj->chars[obj->length] = 0;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kString_Setf(kString str, const kChar* format, ...)
|
|
{
|
|
kVarArgList argList;
|
|
kStatus status;
|
|
|
|
kVarArgList_Start(argList, format);
|
|
{
|
|
status = kString_Setvf(str, format, argList);
|
|
}
|
|
kVarArgList_End(argList);
|
|
|
|
return status;
|
|
}
|
|
|
|
kFx(kStatus) kString_Setvf(kString str, const kChar* format, kVarArgList argList)
|
|
{
|
|
kObj(kString, str);
|
|
kVarArgList argListCopy;
|
|
k32s charCount;
|
|
|
|
kVarArgList_Copy(argListCopy, argList);
|
|
{
|
|
charCount = xkStrMeasuref(format, argListCopy);
|
|
}
|
|
kVarArgList_End(argListCopy);
|
|
|
|
kCheckArgs(charCount >= 0);
|
|
|
|
kCheck(kString_Reserve(str, (kSize)charCount));
|
|
|
|
if (vsprintf(obj->chars, format, argList) < 0)
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
obj->length = (kSize) charCount;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kString_Set(kString str, const kChar* content)
|
|
{
|
|
kObj(kString, str);
|
|
|
|
kCheck(kString_Clear(str));
|
|
kCheck(kString_Add(str, content));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kString_Addf(kString str, const kChar* format, ...)
|
|
{
|
|
kVarArgList argList;
|
|
kStatus status;
|
|
|
|
kVarArgList_Start(argList, format);
|
|
{
|
|
status = kString_Addvf(str, format, argList);
|
|
}
|
|
kVarArgList_End(argList);
|
|
|
|
return status;
|
|
}
|
|
|
|
kFx(kStatus) kString_Addvf(kString str, const kChar* format, kVarArgList argList)
|
|
{
|
|
kObj(kString, str);
|
|
kSize length = kString_Length(str);
|
|
kVarArgList argListCopy;
|
|
k32s charCount;
|
|
|
|
kVarArgList_Copy(argListCopy, argList);
|
|
{
|
|
charCount = xkStrMeasuref(format, argListCopy);
|
|
}
|
|
kVarArgList_End(argListCopy);
|
|
|
|
kCheckArgs(charCount >= 0);
|
|
|
|
kCheck(kString_Reserve(str, length + (kSize)charCount));
|
|
|
|
if (vsprintf(obj->chars + length, format, argList) < 0)
|
|
{
|
|
return kERROR;
|
|
}
|
|
|
|
obj->length = length + (kSize)charCount;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kString_Add(kString str, const kChar* content)
|
|
{
|
|
kObj(kString, str);
|
|
|
|
if (!kIsNull(content))
|
|
{
|
|
kSize contentLength = kStrLength(content);
|
|
kSize combinedLength = contentLength + obj->length;
|
|
|
|
kCheck(kString_Reserve(str, combinedLength));
|
|
|
|
kMemCopy(obj->chars + obj->length, content, contentLength*sizeof(kChar));
|
|
|
|
obj->length = combinedLength;
|
|
obj->chars[combinedLength] = 0;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kString_AddSubstring(kString str, const kChar* content, kSize start, kSize count)
|
|
{
|
|
kObj(kString, str);
|
|
kSize newLength = obj->length + count;
|
|
|
|
kCheck(kString_Reserve(str, newLength));
|
|
|
|
kMemCopy(obj->chars + obj->length, content + start, count * sizeof(kChar));
|
|
|
|
obj->chars[newLength] = 0;
|
|
obj->length = newLength;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkString_SplitAdd(const kChar* start, kSize length, kArrayList tokens, kAlloc allocator, kBool discardEmpty)
|
|
{
|
|
kString token = kNULL;
|
|
kStatus exception;
|
|
|
|
kTry
|
|
{
|
|
if (!discardEmpty || (length > 0))
|
|
{
|
|
kTest(kString_Construct(&token, kNULL, allocator));
|
|
kTest(kString_AddSubstring(token, start, 0, length));
|
|
|
|
kTest(kArrayList_AddT(tokens, &token));
|
|
}
|
|
}
|
|
kCatch(&exception)
|
|
{
|
|
kObject_Destroy(token);
|
|
kEndCatch(exception);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kString_SplitEx(kString str, const kChar* delimiters, kArrayList* tokens, kBool discardEmpty, kAlloc allocator)
|
|
{
|
|
kObj(kString, str);
|
|
const kChar* start = kString_Chars(obj);
|
|
const kChar* it = start;
|
|
kArrayList output = kNULL;
|
|
kStatus exception;
|
|
|
|
kTry
|
|
{
|
|
kTest(kArrayList_Construct(&output, kTypeOf(kString), 0, allocator));
|
|
|
|
while (*it != 0)
|
|
{
|
|
const kChar* delimIt = delimiters;
|
|
|
|
while (*delimIt != 0)
|
|
{
|
|
if (*it == *delimIt++)
|
|
{
|
|
kTest(xkString_SplitAdd(start, (kSize)(it-start), output, allocator, discardEmpty));
|
|
start = it+1;
|
|
break;
|
|
}
|
|
}
|
|
++it;
|
|
}
|
|
|
|
kTest(xkString_SplitAdd(start, (kSize)(it-start), output, allocator, discardEmpty));
|
|
|
|
*tokens = output;
|
|
}
|
|
kCatch(&exception)
|
|
{
|
|
kObject_Dispose(output);
|
|
kEndCatch(exception);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
|