nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: Filename : OVR_String.cpp nuclear@0: Content : String UTF8 string implementation with copy-on-write semantics nuclear@0: (thread-safe for assignment but not modification). nuclear@0: Created : September 19, 2012 nuclear@0: Notes : nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #include "OVR_String.h" nuclear@0: nuclear@0: #include nuclear@0: #include nuclear@0: nuclear@0: #ifdef OVR_OS_QNX nuclear@0: # include nuclear@0: #endif nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: #define String_LengthIsSize (size_t(1) << String::Flag_LengthIsSizeShift) nuclear@0: nuclear@0: String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} }; nuclear@0: nuclear@0: nuclear@0: String::String() nuclear@0: { nuclear@0: pData = &NullData; nuclear@0: pData->AddRef(); nuclear@0: }; nuclear@0: nuclear@0: String::String(const char* pdata) nuclear@0: { nuclear@0: // Obtain length in bytes; it doesn't matter if _data is UTF8. nuclear@0: size_t size = pdata ? OVR_strlen(pdata) : 0; nuclear@0: pData = AllocDataCopy1(size, 0, pdata, size); nuclear@0: }; nuclear@0: nuclear@0: String::String(const char* pdata1, const char* pdata2, const char* pdata3) nuclear@0: { nuclear@0: // Obtain length in bytes; it doesn't matter if _data is UTF8. nuclear@0: size_t size1 = pdata1 ? OVR_strlen(pdata1) : 0; nuclear@0: size_t size2 = pdata2 ? OVR_strlen(pdata2) : 0; nuclear@0: size_t size3 = pdata3 ? OVR_strlen(pdata3) : 0; nuclear@0: nuclear@0: DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0, nuclear@0: pdata1, size1, pdata2, size2); nuclear@0: memcpy(pdataDesc->Data + size1 + size2, pdata3, size3); nuclear@0: pData = pdataDesc; nuclear@0: } nuclear@0: nuclear@0: String::String(const char* pdata, size_t size) nuclear@0: { nuclear@0: OVR_ASSERT((size == 0) || (pdata != 0)); nuclear@0: pData = AllocDataCopy1(size, 0, pdata, size); nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: String::String(const InitStruct& src, size_t size) nuclear@0: { nuclear@0: pData = AllocData(size, 0); nuclear@0: src.InitString(GetData()->Data, size); nuclear@0: } nuclear@0: nuclear@0: String::String(const String& src) nuclear@0: { nuclear@0: pData = src.GetData(); nuclear@0: pData->AddRef(); nuclear@0: } nuclear@0: nuclear@0: String::String(const StringBuffer& src) nuclear@0: { nuclear@0: pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()); nuclear@0: } nuclear@0: nuclear@0: String::String(const wchar_t* data) nuclear@0: { nuclear@0: pData = &NullData; nuclear@0: pData->AddRef(); nuclear@0: // Simplified logic for wchar_t constructor. nuclear@0: if (data) nuclear@0: *this = data; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: String::DataDesc* String::AllocData(size_t size, size_t lengthIsSize) nuclear@0: { nuclear@0: String::DataDesc* pdesc; nuclear@0: nuclear@0: if (size == 0) nuclear@0: { nuclear@0: pdesc = &NullData; nuclear@0: pdesc->AddRef(); nuclear@0: return pdesc; nuclear@0: } nuclear@0: nuclear@0: pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size); nuclear@0: pdesc->Data[size] = 0; nuclear@0: pdesc->RefCount = 1; nuclear@0: pdesc->Size = size | lengthIsSize; nuclear@0: return pdesc; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: String::DataDesc* String::AllocDataCopy1(size_t size, size_t lengthIsSize, nuclear@0: const char* pdata, size_t copySize) nuclear@0: { nuclear@0: String::DataDesc* pdesc = AllocData(size, lengthIsSize); nuclear@0: memcpy(pdesc->Data, pdata, copySize); nuclear@0: return pdesc; nuclear@0: } nuclear@0: nuclear@0: String::DataDesc* String::AllocDataCopy2(size_t size, size_t lengthIsSize, nuclear@0: const char* pdata1, size_t copySize1, nuclear@0: const char* pdata2, size_t copySize2) nuclear@0: { nuclear@0: String::DataDesc* pdesc = AllocData(size, lengthIsSize); nuclear@0: memcpy(pdesc->Data, pdata1, copySize1); nuclear@0: memcpy(pdesc->Data + copySize1, pdata2, copySize2); nuclear@0: return pdesc; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: size_t String::GetLength() const nuclear@0: { nuclear@0: // Optimize length accesses for non-UTF8 character strings. nuclear@0: DataDesc* pdata = GetData(); nuclear@0: size_t length, size = pdata->GetSize(); nuclear@0: nuclear@0: if (pdata->LengthIsSize()) nuclear@0: return size; nuclear@0: nuclear@0: length = (size_t)UTF8Util::GetLength(pdata->Data, (size_t)size); nuclear@0: nuclear@0: if (length == size) nuclear@0: pdata->Size |= String_LengthIsSize; nuclear@0: nuclear@0: return length; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //static uint32_t String_CharSearch(const char* buf, ) nuclear@0: nuclear@0: nuclear@0: uint32_t String::GetCharAt(size_t index) const nuclear@0: { nuclear@0: intptr_t i = (intptr_t) index; nuclear@0: DataDesc* pdata = GetData(); nuclear@0: const char* buf = pdata->Data; nuclear@0: uint32_t c; nuclear@0: nuclear@0: if (pdata->LengthIsSize()) nuclear@0: { nuclear@0: OVR_ASSERT(index < pdata->GetSize()); nuclear@0: buf += i; nuclear@0: return UTF8Util::DecodeNextChar_Advance0(&buf); nuclear@0: } nuclear@0: nuclear@0: c = UTF8Util::GetCharAt(index, buf, pdata->GetSize()); nuclear@0: return c; nuclear@0: } nuclear@0: nuclear@0: uint32_t String::GetFirstCharAt(size_t index, const char** offset) const nuclear@0: { nuclear@0: DataDesc* pdata = GetData(); nuclear@0: intptr_t i = (intptr_t) index; nuclear@0: const char* buf = pdata->Data; nuclear@0: const char* end = buf + pdata->GetSize(); nuclear@0: uint32_t c; nuclear@0: nuclear@0: do nuclear@0: { nuclear@0: c = UTF8Util::DecodeNextChar_Advance0(&buf); nuclear@0: i--; nuclear@0: nuclear@0: if (buf >= end) nuclear@0: { nuclear@0: // We've hit the end of the string; don't go further. nuclear@0: OVR_ASSERT(i == 0); nuclear@0: return c; nuclear@0: } nuclear@0: } while (i >= 0); nuclear@0: nuclear@0: *offset = buf; nuclear@0: nuclear@0: return c; nuclear@0: } nuclear@0: nuclear@0: uint32_t String::GetNextChar(const char** offset) const nuclear@0: { nuclear@0: return UTF8Util::DecodeNextChar(offset); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: void String::AppendChar(uint32_t ch) nuclear@0: { nuclear@0: DataDesc* pdata = GetData(); nuclear@0: size_t size = pdata->GetSize(); nuclear@0: char buff[8]; nuclear@0: intptr_t encodeSize = 0; nuclear@0: nuclear@0: // Converts ch into UTF8 string and fills it into buff. nuclear@0: UTF8Util::EncodeChar(buff, &encodeSize, ch); nuclear@0: OVR_ASSERT(encodeSize >= 0); nuclear@0: nuclear@0: SetData(AllocDataCopy2(size + (size_t)encodeSize, 0, nuclear@0: pdata->Data, size, buff, (size_t)encodeSize)); nuclear@0: pdata->Release(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void String::AppendString(const wchar_t* pstr, intptr_t len) nuclear@0: { nuclear@0: if (!pstr) nuclear@0: return; nuclear@0: nuclear@0: DataDesc* pdata = GetData(); nuclear@0: size_t oldSize = pdata->GetSize(); nuclear@0: size_t encodeSize = (size_t)UTF8Util::GetEncodeStringSize(pstr, len); nuclear@0: nuclear@0: DataDesc* pnewData = AllocDataCopy1(oldSize + (size_t)encodeSize, 0, nuclear@0: pdata->Data, oldSize); nuclear@0: UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len); nuclear@0: nuclear@0: SetData(pnewData); nuclear@0: pdata->Release(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void String::AppendString(const char* putf8str, intptr_t utf8StrSz) nuclear@0: { nuclear@0: if (!putf8str || !utf8StrSz) nuclear@0: return; nuclear@0: if (utf8StrSz == -1) nuclear@0: utf8StrSz = (intptr_t)OVR_strlen(putf8str); nuclear@0: nuclear@0: DataDesc* pdata = GetData(); nuclear@0: size_t oldSize = pdata->GetSize(); nuclear@0: nuclear@0: SetData(AllocDataCopy2(oldSize + (size_t)utf8StrSz, 0, nuclear@0: pdata->Data, oldSize, putf8str, (size_t)utf8StrSz)); nuclear@0: pdata->Release(); nuclear@0: } nuclear@0: nuclear@0: void String::AssignString(const InitStruct& src, size_t size) nuclear@0: { nuclear@0: DataDesc* poldData = GetData(); nuclear@0: DataDesc* pnewData = AllocData(size, 0); nuclear@0: src.InitString(pnewData->Data, size); nuclear@0: SetData(pnewData); nuclear@0: poldData->Release(); nuclear@0: } nuclear@0: nuclear@0: void String::AssignString(const char* putf8str, size_t size) nuclear@0: { nuclear@0: DataDesc* poldData = GetData(); nuclear@0: SetData(AllocDataCopy1(size, 0, putf8str, size)); nuclear@0: poldData->Release(); nuclear@0: } nuclear@0: nuclear@0: void String::operator = (const char* pstr) nuclear@0: { nuclear@0: AssignString(pstr, pstr ? OVR_strlen(pstr) : 0); nuclear@0: } nuclear@0: nuclear@0: void String::operator = (const wchar_t* pwstr) nuclear@0: { nuclear@0: pwstr = pwstr ? pwstr : L""; nuclear@0: nuclear@0: DataDesc* poldData = GetData(); nuclear@0: size_t size = (size_t)UTF8Util::GetEncodeStringSize(pwstr); nuclear@0: nuclear@0: DataDesc* pnewData = AllocData(size, 0); nuclear@0: UTF8Util::EncodeString(pnewData->Data, pwstr); nuclear@0: SetData(pnewData); nuclear@0: poldData->Release(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void String::operator = (const String& src) nuclear@0: { nuclear@0: DataDesc* psdata = src.GetData(); nuclear@0: DataDesc* pdata = GetData(); nuclear@0: nuclear@0: SetData(psdata); nuclear@0: psdata->AddRef(); nuclear@0: pdata->Release(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void String::operator = (const StringBuffer& src) nuclear@0: { nuclear@0: DataDesc* polddata = GetData(); nuclear@0: SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize())); nuclear@0: polddata->Release(); nuclear@0: } nuclear@0: nuclear@0: void String::operator += (const String& src) nuclear@0: { nuclear@0: DataDesc *pourData = GetData(), nuclear@0: *psrcData = src.GetData(); nuclear@0: size_t ourSize = pourData->GetSize(), nuclear@0: srcSize = psrcData->GetSize(); nuclear@0: size_t lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag(); nuclear@0: nuclear@0: SetData(AllocDataCopy2(ourSize + srcSize, lflag, nuclear@0: pourData->Data, ourSize, psrcData->Data, srcSize)); nuclear@0: pourData->Release(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: String String::operator + (const char* str) const nuclear@0: { nuclear@0: String tmp1(*this); nuclear@0: tmp1 += (str ? str : ""); nuclear@0: return tmp1; nuclear@0: } nuclear@0: nuclear@0: String String::operator + (const String& src) const nuclear@0: { nuclear@0: String tmp1(*this); nuclear@0: tmp1 += src; nuclear@0: return tmp1; nuclear@0: } nuclear@0: nuclear@0: void String::Remove(size_t posAt, intptr_t removeLength) nuclear@0: { nuclear@0: DataDesc* pdata = GetData(); nuclear@0: size_t oldSize = pdata->GetSize(); nuclear@0: // Length indicates the number of characters to remove. nuclear@0: size_t length = GetLength(); nuclear@0: nuclear@0: // If index is past the string, nothing to remove. nuclear@0: if (posAt >= length) nuclear@0: return; nuclear@0: // Otherwise, cap removeLength to the length of the string. nuclear@0: if ((posAt + removeLength) > length) nuclear@0: removeLength = length - posAt; nuclear@0: nuclear@0: // Get the byte position of the UTF8 char at position posAt. nuclear@0: intptr_t bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize); nuclear@0: intptr_t removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos); nuclear@0: nuclear@0: SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(), nuclear@0: pdata->Data, bytePos, nuclear@0: pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize))); nuclear@0: pdata->Release(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: String String::Substring(size_t start, size_t end) const nuclear@0: { nuclear@0: size_t length = GetLength(); nuclear@0: if ((start >= length) || (start >= end)) nuclear@0: return String(); nuclear@0: nuclear@0: DataDesc* pdata = GetData(); nuclear@0: nuclear@0: // If size matches, we know the exact index range. nuclear@0: if (pdata->LengthIsSize()) nuclear@0: return String(pdata->Data + start, end - start); nuclear@0: nuclear@0: // Get position of starting character. nuclear@0: intptr_t byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize()); nuclear@0: intptr_t byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart); nuclear@0: return String(pdata->Data + byteStart, (size_t)byteSize); nuclear@0: } nuclear@0: nuclear@0: void String::Clear() nuclear@0: { nuclear@0: NullData.AddRef(); nuclear@0: GetData()->Release(); nuclear@0: SetData(&NullData); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: String String::ToUpper() const nuclear@0: { nuclear@0: uint32_t c; nuclear@0: const char* psource = GetData()->Data; nuclear@0: const char* pend = psource + GetData()->GetSize(); nuclear@0: String str; nuclear@0: intptr_t bufferOffset = 0; nuclear@0: char buffer[512]; nuclear@0: nuclear@0: while(psource < pend) nuclear@0: { nuclear@0: do { nuclear@0: c = UTF8Util::DecodeNextChar_Advance0(&psource); nuclear@0: UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c))); nuclear@0: } while ((psource < pend) && (bufferOffset < intptr_t(sizeof(buffer)-8))); nuclear@0: nuclear@0: // Append string a piece at a time. nuclear@0: str.AppendString(buffer, bufferOffset); nuclear@0: bufferOffset = 0; nuclear@0: } nuclear@0: nuclear@0: return str; nuclear@0: } nuclear@0: nuclear@0: String String::ToLower() const nuclear@0: { nuclear@0: uint32_t c; nuclear@0: const char* psource = GetData()->Data; nuclear@0: const char* pend = psource + GetData()->GetSize(); nuclear@0: String str; nuclear@0: intptr_t bufferOffset = 0; nuclear@0: char buffer[512]; nuclear@0: nuclear@0: while(psource < pend) nuclear@0: { nuclear@0: do { nuclear@0: c = UTF8Util::DecodeNextChar_Advance0(&psource); nuclear@0: UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c))); nuclear@0: } while ((psource < pend) && (bufferOffset < intptr_t(sizeof(buffer)-8))); nuclear@0: nuclear@0: // Append string a piece at a time. nuclear@0: str.AppendString(buffer, bufferOffset); nuclear@0: bufferOffset = 0; nuclear@0: } nuclear@0: nuclear@0: return str; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: String& String::Insert(const char* substr, size_t posAt, intptr_t strSize) nuclear@0: { nuclear@0: DataDesc* poldData = GetData(); nuclear@0: size_t oldSize = poldData->GetSize(); nuclear@0: size_t insertSize = (strSize < 0) ? OVR_strlen(substr) : (size_t)strSize; nuclear@0: size_t byteIndex = (poldData->LengthIsSize()) ? nuclear@0: posAt : (size_t)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize); nuclear@0: nuclear@0: OVR_ASSERT(byteIndex <= oldSize); nuclear@0: nuclear@0: DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0, nuclear@0: poldData->Data, byteIndex, substr, insertSize); nuclear@0: memcpy(pnewData->Data + byteIndex + insertSize, nuclear@0: poldData->Data + byteIndex, oldSize - byteIndex); nuclear@0: SetData(pnewData); nuclear@0: poldData->Release(); nuclear@0: return *this; nuclear@0: } nuclear@0: nuclear@0: /* nuclear@0: String& String::Insert(const uint32_t* substr, size_t posAt, intptr_t len) nuclear@0: { nuclear@0: for (intptr_t i = 0; i < len; ++i) nuclear@0: { nuclear@0: size_t charw = InsertCharAt(substr[i], posAt); nuclear@0: posAt += charw; nuclear@0: } nuclear@0: return *this; nuclear@0: } nuclear@0: */ nuclear@0: nuclear@0: size_t String::InsertCharAt(uint32_t c, size_t posAt) nuclear@0: { nuclear@0: char buf[8]; nuclear@0: intptr_t index = 0; nuclear@0: UTF8Util::EncodeChar(buf, &index, c); nuclear@0: OVR_ASSERT(index >= 0); nuclear@0: buf[(size_t)index] = 0; nuclear@0: nuclear@0: Insert(buf, posAt, index); nuclear@0: return (size_t)index; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: int String::CompareNoCase(const char* a, const char* b) nuclear@0: { nuclear@0: return OVR_stricmp(a, b); nuclear@0: } nuclear@0: nuclear@0: int String::CompareNoCase(const char* a, const char* b, intptr_t len) nuclear@0: { nuclear@0: if (len) nuclear@0: { nuclear@0: intptr_t f,l; nuclear@0: intptr_t slen = len; nuclear@0: const char *s = b; nuclear@0: do { nuclear@0: f = (intptr_t)OVR_tolower((int)(*(a++))); nuclear@0: l = (intptr_t)OVR_tolower((int)(*(b++))); nuclear@0: } while (--len && f && (f == l) && *b != 0); nuclear@0: nuclear@0: if (f == l && (len != 0 || *b != 0)) nuclear@0: { nuclear@0: f = (intptr_t)slen; nuclear@0: l = (intptr_t)OVR_strlen(s); nuclear@0: return int(f - l); nuclear@0: } nuclear@0: nuclear@0: return int(f - l); nuclear@0: } nuclear@0: else nuclear@0: return (0-(int)OVR_strlen(b)); nuclear@0: } nuclear@0: nuclear@0: // ***** Implement hash static functions nuclear@0: nuclear@0: // Hash function nuclear@0: size_t String::BernsteinHashFunction(const void* pdataIn, size_t size, size_t seed) nuclear@0: { nuclear@0: const uint8_t* pdata = (const uint8_t*) pdataIn; nuclear@0: size_t h = seed; nuclear@0: while (size > 0) nuclear@0: { nuclear@0: size--; nuclear@0: h = ((h << 5) + h) ^ (unsigned) pdata[size]; nuclear@0: } nuclear@0: nuclear@0: return h; nuclear@0: } nuclear@0: nuclear@0: // Hash function, case-insensitive nuclear@0: size_t String::BernsteinHashFunctionCIS(const void* pdataIn, size_t size, size_t seed) nuclear@0: { nuclear@0: const uint8_t* pdata = (const uint8_t*) pdataIn; nuclear@0: size_t h = seed; nuclear@0: while (size > 0) nuclear@0: { nuclear@0: size--; nuclear@0: h = ((h << 5) + h) ^ OVR_tolower(pdata[size]); nuclear@0: } nuclear@0: nuclear@0: // Alternative: "sdbm" hash function, suggested at same web page above. nuclear@0: // h = 0; nuclear@0: // for bytes { h = (h << 16) + (h << 6) - hash + *p; } nuclear@0: return h; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // ***** String Buffer used for Building Strings nuclear@0: nuclear@0: nuclear@0: #define OVR_SBUFF_DEFAULT_GROW_SIZE 512 nuclear@0: // Constructors / Destructor. nuclear@0: StringBuffer::StringBuffer() nuclear@0: : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: StringBuffer::StringBuffer(size_t growSize) nuclear@0: : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) nuclear@0: { nuclear@0: SetGrowSize(growSize); nuclear@0: } nuclear@0: nuclear@0: StringBuffer::StringBuffer(const char* data) nuclear@0: : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) nuclear@0: { nuclear@0: AppendString(data); nuclear@0: } nuclear@0: nuclear@0: StringBuffer::StringBuffer(const char* data, size_t dataSize) nuclear@0: : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) nuclear@0: { nuclear@0: AppendString(data, dataSize); nuclear@0: } nuclear@0: nuclear@0: StringBuffer::StringBuffer(const String& src) nuclear@0: : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) nuclear@0: { nuclear@0: AppendString(src.ToCStr(), src.GetSize()); nuclear@0: } nuclear@0: nuclear@0: StringBuffer::StringBuffer(const StringBuffer& src) nuclear@0: : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) nuclear@0: { nuclear@0: AppendString(src.ToCStr(), src.GetSize()); nuclear@0: } nuclear@0: nuclear@0: StringBuffer::StringBuffer(const wchar_t* data) nuclear@0: : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) nuclear@0: { nuclear@0: *this = data; nuclear@0: } nuclear@0: nuclear@0: StringBuffer::~StringBuffer() nuclear@0: { nuclear@0: if (pData) nuclear@0: OVR_FREE(pData); nuclear@0: } nuclear@0: void StringBuffer::SetGrowSize(size_t growSize) nuclear@0: { nuclear@0: if (growSize <= 16) nuclear@0: GrowSize = 16; nuclear@0: else nuclear@0: { nuclear@0: uint8_t bits = Alg::UpperBit(uint32_t(growSize-1)); nuclear@0: size_t size = (size_t)1 << bits; nuclear@0: GrowSize = size == growSize ? growSize : size; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: size_t StringBuffer::GetLength() const nuclear@0: { nuclear@0: size_t length, size = GetSize(); nuclear@0: if (LengthIsSize) nuclear@0: return size; nuclear@0: nuclear@0: length = (size_t)UTF8Util::GetLength(pData, (size_t)GetSize()); nuclear@0: nuclear@0: if (length == GetSize()) nuclear@0: LengthIsSize = true; nuclear@0: return length; nuclear@0: } nuclear@0: nuclear@0: void StringBuffer::Reserve(size_t _size) nuclear@0: { nuclear@0: if (_size >= BufferSize) // >= because of trailing zero! (!AB) nuclear@0: { nuclear@0: BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1); nuclear@0: if (!pData) nuclear@0: pData = (char*)OVR_ALLOC(BufferSize); nuclear@0: else nuclear@0: pData = (char*)OVR_REALLOC(pData, BufferSize); nuclear@0: } nuclear@0: } nuclear@0: void StringBuffer::Resize(size_t _size) nuclear@0: { nuclear@0: Reserve(_size); nuclear@0: LengthIsSize = false; nuclear@0: Size = _size; nuclear@0: if (pData) nuclear@0: pData[Size] = 0; nuclear@0: } nuclear@0: nuclear@0: void StringBuffer::Clear() nuclear@0: { nuclear@0: Resize(0); nuclear@0: /* nuclear@0: if (pData != pEmptyNullData) nuclear@0: { nuclear@0: OVR_FREE(pHeap, pData); nuclear@0: pData = pEmptyNullData; nuclear@0: Size = BufferSize = 0; nuclear@0: LengthIsSize = false; nuclear@0: } nuclear@0: */ nuclear@0: } nuclear@0: // Appends a character nuclear@0: void StringBuffer::AppendChar(uint32_t ch) nuclear@0: { nuclear@0: char buff[8]; nuclear@0: size_t origSize = GetSize(); nuclear@0: nuclear@0: // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes nuclear@0: // in the UTF8 string. nuclear@0: intptr_t srcSize = 0; nuclear@0: UTF8Util::EncodeChar(buff, &srcSize, ch); nuclear@0: OVR_ASSERT(srcSize >= 0); nuclear@0: nuclear@0: size_t size = origSize + srcSize; nuclear@0: Resize(size); nuclear@0: OVR_ASSERT(pData != NULL); nuclear@0: memcpy(pData + origSize, buff, srcSize); nuclear@0: } nuclear@0: nuclear@0: // Append a string nuclear@0: void StringBuffer::AppendString(const wchar_t* pstr, intptr_t len) nuclear@0: { nuclear@0: if (!pstr || !len) nuclear@0: return; nuclear@0: nuclear@0: intptr_t srcSize = UTF8Util::GetEncodeStringSize(pstr, len); nuclear@0: size_t origSize = GetSize(); nuclear@0: size_t size = srcSize + origSize; nuclear@0: nuclear@0: Resize(size); nuclear@0: OVR_ASSERT(pData != NULL); nuclear@0: UTF8Util::EncodeString(pData + origSize, pstr, len); nuclear@0: } nuclear@0: nuclear@0: void StringBuffer::AppendString(const char* putf8str, intptr_t utf8StrSz) nuclear@0: { nuclear@0: if (!putf8str || !utf8StrSz) nuclear@0: return; nuclear@0: if (utf8StrSz == -1) nuclear@0: utf8StrSz = (intptr_t)OVR_strlen(putf8str); nuclear@0: nuclear@0: size_t origSize = GetSize(); nuclear@0: size_t size = utf8StrSz + origSize; nuclear@0: nuclear@0: Resize(size); nuclear@0: OVR_ASSERT(pData != NULL); nuclear@0: memcpy(pData + origSize, putf8str, utf8StrSz); nuclear@0: } nuclear@0: nuclear@0: // If pstr is NULL then the StringBuffer is cleared. nuclear@0: void StringBuffer::operator = (const char* pstr) nuclear@0: { nuclear@0: pstr = pstr ? pstr : ""; nuclear@0: size_t size = OVR_strlen(pstr); nuclear@0: Resize(size); nuclear@0: OVR_ASSERT((pData != NULL) || (size == 0)); nuclear@0: memcpy(pData, pstr, size); nuclear@0: } nuclear@0: nuclear@0: // If pstr is NULL then the StringBuffer is cleared. nuclear@0: void StringBuffer::operator = (const wchar_t* pstr) nuclear@0: { nuclear@0: pstr = pstr ? pstr : L""; nuclear@0: size_t size = (size_t)UTF8Util::GetEncodeStringSize(pstr); nuclear@0: Resize(size); nuclear@0: OVR_ASSERT((pData != NULL) || (size == 0)); nuclear@0: UTF8Util::EncodeString(pData, pstr); nuclear@0: } nuclear@0: nuclear@0: void StringBuffer::operator = (const String& src) nuclear@0: { nuclear@0: const size_t size = src.GetSize(); nuclear@0: Resize(size); nuclear@0: OVR_ASSERT((pData != NULL) || (size == 0)); nuclear@0: memcpy(pData, src.ToCStr(), size); nuclear@0: } nuclear@0: nuclear@0: void StringBuffer::operator = (const StringBuffer& src) nuclear@0: { nuclear@0: Clear(); nuclear@0: AppendString(src.ToCStr(), src.GetSize()); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Inserts substr at posAt nuclear@0: void StringBuffer::Insert(const char* substr, size_t posAt, intptr_t len) nuclear@0: { nuclear@0: size_t oldSize = Size; nuclear@0: size_t insertSize = (len < 0) ? OVR_strlen(substr) : (size_t)len; nuclear@0: size_t byteIndex = LengthIsSize ? posAt : nuclear@0: (size_t)UTF8Util::GetByteIndex(posAt, pData, (intptr_t)Size); nuclear@0: nuclear@0: OVR_ASSERT(byteIndex <= oldSize); nuclear@0: Reserve(oldSize + insertSize); nuclear@0: nuclear@0: OVR_ASSERT(pData != NULL); // pData is unilaterally written to below. nuclear@0: memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1); nuclear@0: memcpy (pData + byteIndex, substr, insertSize); nuclear@0: LengthIsSize = false; nuclear@0: Size = oldSize + insertSize; nuclear@0: pData[Size] = 0; nuclear@0: } nuclear@0: nuclear@0: // Inserts character at posAt nuclear@0: size_t StringBuffer::InsertCharAt(uint32_t c, size_t posAt) nuclear@0: { nuclear@0: char buf[8]; nuclear@0: intptr_t len = 0; nuclear@0: UTF8Util::EncodeChar(buf, &len, c); nuclear@0: OVR_ASSERT(len >= 0); nuclear@0: buf[(size_t)len] = 0; nuclear@0: nuclear@0: Insert(buf, posAt, len); nuclear@0: return (size_t)len; nuclear@0: } nuclear@0: nuclear@0: } // OVR