nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: PublicHeader: OVR_Kernel.h nuclear@0: Filename : OVR_String.h 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: #ifndef OVR_String_h nuclear@0: #define OVR_String_h nuclear@0: nuclear@0: #include "OVR_Types.h" nuclear@0: #include "OVR_Allocator.h" nuclear@0: #include "OVR_UTF8Util.h" nuclear@0: #include "OVR_Atomic.h" nuclear@0: #include "OVR_Std.h" nuclear@0: #include "OVR_Alg.h" nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: // ***** Classes nuclear@0: nuclear@0: class String; nuclear@0: class StringBuffer; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** String Class nuclear@0: nuclear@0: // String is UTF8 based string class with copy-on-write implementation nuclear@0: // for assignment. nuclear@0: nuclear@0: class String nuclear@0: { nuclear@0: protected: nuclear@0: nuclear@0: enum FlagConstants nuclear@0: { nuclear@0: //Flag_GetLength = 0x7FFFFFFF, nuclear@0: // This flag is set if GetLength() == GetSize() for a string. nuclear@0: // Avoid extra scanning is Substring and indexing logic. nuclear@0: Flag_LengthIsSizeShift = (sizeof(size_t)*8 - 1) nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // Internal structure to hold string data nuclear@0: struct DataDesc nuclear@0: { nuclear@0: // Number of bytes. Will be the same as the number of chars if the characters nuclear@0: // are ascii, may not be equal to number of chars in case string data is UTF8. nuclear@0: size_t Size; nuclear@0: volatile int32_t RefCount; nuclear@0: char Data[1]; nuclear@0: nuclear@0: void AddRef() nuclear@0: { nuclear@0: AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); nuclear@0: } nuclear@0: // Decrement ref count. This needs to be thread-safe, since nuclear@0: // a different thread could have also decremented the ref count. nuclear@0: // For example, if u start off with a ref count = 2. Now if u nuclear@0: // decrement the ref count and check against 0 in different nuclear@0: // statements, a different thread can also decrement the ref count nuclear@0: // in between our decrement and checking against 0 and will find nuclear@0: // the ref count = 0 and delete the object. This will lead to a crash nuclear@0: // when context switches to our thread and we'll be trying to delete nuclear@0: // an already deleted object. Hence decrementing the ref count and nuclear@0: // checking against 0 needs to made an atomic operation. nuclear@0: void Release() nuclear@0: { nuclear@0: if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) nuclear@0: OVR_FREE(this); nuclear@0: } nuclear@0: nuclear@0: static size_t GetLengthFlagBit() { return size_t(1) << Flag_LengthIsSizeShift; } nuclear@0: size_t GetSize() const { return Size & ~GetLengthFlagBit() ; } nuclear@0: size_t GetLengthFlag() const { return Size & GetLengthFlagBit(); } nuclear@0: bool LengthIsSize() const { return GetLengthFlag() != 0; } nuclear@0: }; nuclear@0: nuclear@0: // Heap type of the string is encoded in the lower bits. nuclear@0: enum HeapType nuclear@0: { nuclear@0: HT_Global = 0, // Heap is global. nuclear@0: HT_Local = 1, // SF::String_loc: Heap is determined based on string's address. nuclear@0: HT_Dynamic = 2, // SF::String_temp: Heap is stored as a part of the class. nuclear@0: HT_Mask = 3 nuclear@0: }; nuclear@0: nuclear@0: union { nuclear@0: DataDesc* pData; nuclear@0: size_t HeapTypeBits; nuclear@0: }; nuclear@0: typedef union { nuclear@0: DataDesc* pData; nuclear@0: size_t HeapTypeBits; nuclear@0: } DataDescUnion; nuclear@0: nuclear@0: inline HeapType GetHeapType() const { return (HeapType) (HeapTypeBits & HT_Mask); } nuclear@0: nuclear@0: inline DataDesc* GetData() const nuclear@0: { nuclear@0: DataDescUnion u; nuclear@0: u.pData = pData; nuclear@0: u.HeapTypeBits = (u.HeapTypeBits & ~(size_t)HT_Mask); nuclear@0: return u.pData; nuclear@0: } nuclear@0: nuclear@0: inline void SetData(DataDesc* pdesc) nuclear@0: { nuclear@0: HeapType ht = GetHeapType(); nuclear@0: pData = pdesc; nuclear@0: OVR_ASSERT((HeapTypeBits & HT_Mask) == 0); nuclear@0: HeapTypeBits |= ht; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: DataDesc* AllocData(size_t size, size_t lengthIsSize); nuclear@0: DataDesc* AllocDataCopy1(size_t size, size_t lengthIsSize, nuclear@0: const char* pdata, size_t copySize); nuclear@0: DataDesc* 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: // Special constructor to avoid data initalization when used in derived class. nuclear@0: struct NoConstructor { }; nuclear@0: String(const NoConstructor&) { } nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: // For initializing string with dynamic buffer nuclear@0: struct InitStruct nuclear@0: { nuclear@0: virtual ~InitStruct() { } nuclear@0: virtual void InitString(char* pbuffer, size_t size) const = 0; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // Constructors / Destructors. nuclear@0: String(); nuclear@0: String(const char* data); nuclear@0: String(const char* data1, const char* pdata2, const char* pdata3 = 0); nuclear@0: String(const char* data, size_t buflen); nuclear@0: String(const String& src); nuclear@0: String(const StringBuffer& src); nuclear@0: String(const InitStruct& src, size_t size); nuclear@0: explicit String(const wchar_t* data); nuclear@0: nuclear@0: // Destructor (Captain Obvious guarantees!) nuclear@0: ~String() nuclear@0: { nuclear@0: GetData()->Release(); nuclear@0: } nuclear@0: nuclear@0: // Declaration of NullString nuclear@0: static DataDesc NullData; nuclear@0: nuclear@0: nuclear@0: // *** General Functions nuclear@0: nuclear@0: void Clear(); nuclear@0: nuclear@0: // For casting to a pointer to char. nuclear@0: operator const char*() const { return GetData()->Data; } nuclear@0: // Pointer to raw buffer. nuclear@0: const char* ToCStr() const { return GetData()->Data; } nuclear@0: nuclear@0: // Returns number of bytes nuclear@0: size_t GetSize() const { return GetData()->GetSize() ; } nuclear@0: // Tells whether or not the string is empty nuclear@0: bool IsEmpty() const { return GetSize() == 0; } nuclear@0: nuclear@0: // Returns number of characters nuclear@0: size_t GetLength() const; nuclear@0: int GetLengthI() const { return (int)GetLength(); } nuclear@0: nuclear@0: // Returns character at the specified index nuclear@0: uint32_t GetCharAt(size_t index) const; nuclear@0: uint32_t GetFirstCharAt(size_t index, const char** offset) const; nuclear@0: uint32_t GetNextChar(const char** offset) const; nuclear@0: nuclear@0: // Appends a character nuclear@0: void AppendChar(uint32_t ch); nuclear@0: nuclear@0: // Append a string nuclear@0: void AppendString(const wchar_t* pstr, intptr_t len = -1); nuclear@0: void AppendString(const char* putf8str, intptr_t utf8StrSz = -1); nuclear@0: nuclear@0: // Assigned a string with dynamic data (copied through initializer). nuclear@0: void AssignString(const InitStruct& src, size_t size); nuclear@0: // Assigns string with known size. nuclear@0: void AssignString(const char* putf8str, size_t size); nuclear@0: nuclear@0: // Resize the string to the new size nuclear@0: // void Resize(size_t _size); nuclear@0: nuclear@0: // Removes the character at posAt nuclear@0: void Remove(size_t posAt, intptr_t len = 1); nuclear@0: nuclear@0: // Returns a String that's a substring of this. nuclear@0: // -start is the index of the first UTF8 character you want to include. nuclear@0: // -end is the index one past the last UTF8 character you want to include. nuclear@0: String Substring(size_t start, size_t end) const; nuclear@0: nuclear@0: // Case-conversion nuclear@0: String ToUpper() const; nuclear@0: String ToLower() const; nuclear@0: nuclear@0: // Inserts substr at posAt nuclear@0: String& Insert (const char* substr, size_t posAt, intptr_t len = -1); nuclear@0: nuclear@0: // Inserts character at posAt nuclear@0: size_t InsertCharAt(uint32_t c, size_t posAt); nuclear@0: nuclear@0: // Inserts substr at posAt, which is an index of a character (not byte). nuclear@0: // Of size is specified, it is in bytes. nuclear@0: // String& Insert(const uint32_t* substr, size_t posAt, intptr_t size = -1); nuclear@0: nuclear@0: // Get Byte index of the character at position = index nuclear@0: size_t GetByteIndex(size_t index) const { return (size_t)UTF8Util::GetByteIndex(index, GetData()->Data); } nuclear@0: nuclear@0: // Utility: case-insensitive string compare. stricmp() & strnicmp() are not nuclear@0: // ANSI or POSIX, do not seem to appear in Linux. nuclear@0: static int OVR_STDCALL CompareNoCase(const char* a, const char* b); nuclear@0: static int OVR_STDCALL CompareNoCase(const char* a, const char* b, intptr_t len); nuclear@0: nuclear@0: // Hash function, case-insensitive nuclear@0: static size_t OVR_STDCALL BernsteinHashFunctionCIS(const void* pdataIn, size_t size, size_t seed = 5381); nuclear@0: nuclear@0: // Hash function, case-sensitive nuclear@0: static size_t OVR_STDCALL BernsteinHashFunction(const void* pdataIn, size_t size, size_t seed = 5381); nuclear@0: nuclear@0: nuclear@0: // ***** File path parsing helper functions. nuclear@0: // Implemented in OVR_String_FilePath.cpp. nuclear@0: nuclear@0: // Absolute paths can star with: nuclear@0: // - protocols: 'file://', 'http://' nuclear@0: // - windows drive: 'c:\' nuclear@0: // - UNC share name: '\\share' nuclear@0: // - unix root '/' nuclear@0: static bool HasAbsolutePath(const char* path); nuclear@0: static bool HasExtension(const char* path); nuclear@0: static bool HasProtocol(const char* path); nuclear@0: nuclear@0: bool HasAbsolutePath() const { return HasAbsolutePath(ToCStr()); } nuclear@0: bool HasExtension() const { return HasExtension(ToCStr()); } nuclear@0: bool HasProtocol() const { return HasProtocol(ToCStr()); } nuclear@0: nuclear@0: String GetProtocol() const; // Returns protocol, if any, with trailing '://'. nuclear@0: String GetPath() const; // Returns path with trailing '/'. nuclear@0: String GetFilename() const; // Returns filename, including extension. nuclear@0: String GetExtension() const; // Returns extension with a dot. nuclear@0: nuclear@0: void StripProtocol(); // Strips front protocol, if any, from the string. nuclear@0: void StripExtension(); // Strips off trailing extension. nuclear@0: nuclear@0: nuclear@0: // Operators nuclear@0: // Assignment nuclear@0: void operator = (const char* str); nuclear@0: void operator = (const wchar_t* str); nuclear@0: void operator = (const String& src); nuclear@0: void operator = (const StringBuffer& src); nuclear@0: nuclear@0: // Addition nuclear@0: void operator += (const String& src); nuclear@0: void operator += (const char* psrc) { AppendString(psrc); } nuclear@0: void operator += (const wchar_t* psrc) { AppendString(psrc); } nuclear@0: void operator += (char ch) { AppendChar(ch); } nuclear@0: String operator + (const char* str) const; nuclear@0: String operator + (const String& src) const; nuclear@0: nuclear@0: // Comparison nuclear@0: bool operator == (const String& str) const nuclear@0: { nuclear@0: return (OVR_strcmp(GetData()->Data, str.GetData()->Data)== 0); nuclear@0: } nuclear@0: nuclear@0: bool operator != (const String& str) const nuclear@0: { nuclear@0: return !operator == (str); nuclear@0: } nuclear@0: nuclear@0: bool operator == (const char* str) const nuclear@0: { nuclear@0: return OVR_strcmp(GetData()->Data, str) == 0; nuclear@0: } nuclear@0: nuclear@0: bool operator != (const char* str) const nuclear@0: { nuclear@0: return !operator == (str); nuclear@0: } nuclear@0: nuclear@0: bool operator < (const char* pstr) const nuclear@0: { nuclear@0: return OVR_strcmp(GetData()->Data, pstr) < 0; nuclear@0: } nuclear@0: nuclear@0: bool operator < (const String& str) const nuclear@0: { nuclear@0: return *this < str.GetData()->Data; nuclear@0: } nuclear@0: nuclear@0: bool operator > (const char* pstr) const nuclear@0: { nuclear@0: return OVR_strcmp(GetData()->Data, pstr) > 0; nuclear@0: } nuclear@0: nuclear@0: bool operator > (const String& str) const nuclear@0: { nuclear@0: return *this > str.GetData()->Data; nuclear@0: } nuclear@0: nuclear@0: int CompareNoCase(const char* pstr) const nuclear@0: { nuclear@0: return CompareNoCase(GetData()->Data, pstr); nuclear@0: } nuclear@0: int CompareNoCase(const String& str) const nuclear@0: { nuclear@0: return CompareNoCase(GetData()->Data, str.ToCStr()); nuclear@0: } nuclear@0: nuclear@0: // Accesses raw bytes nuclear@0: const char& operator [] (int index) const nuclear@0: { nuclear@0: OVR_ASSERT(index >= 0 && (size_t)index < GetSize()); nuclear@0: return GetData()->Data[index]; nuclear@0: } nuclear@0: const char& operator [] (size_t index) const nuclear@0: { nuclear@0: OVR_ASSERT(index < GetSize()); nuclear@0: return GetData()->Data[index]; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Case insensitive keys are used to look up insensitive string in hash tables nuclear@0: // for SWF files with version before SWF 7. nuclear@0: struct NoCaseKey nuclear@0: { nuclear@0: const String* pStr; nuclear@0: NoCaseKey(const String &str) : pStr(&str){}; nuclear@0: }; nuclear@0: nuclear@0: bool operator == (const NoCaseKey& strKey) const nuclear@0: { nuclear@0: return (CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); nuclear@0: } nuclear@0: bool operator != (const NoCaseKey& strKey) const nuclear@0: { nuclear@0: return !(CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); nuclear@0: } nuclear@0: nuclear@0: // Hash functor used for strings. nuclear@0: struct HashFunctor nuclear@0: { nuclear@0: size_t operator()(const String& data) const nuclear@0: { nuclear@0: size_t size = data.GetSize(); nuclear@0: return String::BernsteinHashFunction((const char*)data, size); nuclear@0: } nuclear@0: }; nuclear@0: // Case-insensitive hash functor used for strings. Supports additional nuclear@0: // lookup based on NoCaseKey. nuclear@0: struct NoCaseHashFunctor nuclear@0: { nuclear@0: size_t operator()(const String& data) const nuclear@0: { nuclear@0: size_t size = data.GetSize(); nuclear@0: return String::BernsteinHashFunctionCIS((const char*)data, size); nuclear@0: } nuclear@0: size_t operator()(const NoCaseKey& data) const nuclear@0: { nuclear@0: size_t size = data.pStr->GetSize(); nuclear@0: return String::BernsteinHashFunctionCIS((const char*)data.pStr->ToCStr(), size); nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** String Buffer used for Building Strings nuclear@0: nuclear@0: class StringBuffer nuclear@0: { nuclear@0: char* pData; nuclear@0: size_t Size; nuclear@0: size_t BufferSize; nuclear@0: size_t GrowSize; nuclear@0: mutable bool LengthIsSize; nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: // Constructors / Destructor. nuclear@0: StringBuffer(); nuclear@0: explicit StringBuffer(size_t growSize); nuclear@0: StringBuffer(const char* data); nuclear@0: StringBuffer(const char* data, size_t buflen); nuclear@0: StringBuffer(const String& src); nuclear@0: StringBuffer(const StringBuffer& src); nuclear@0: explicit StringBuffer(const wchar_t* data); nuclear@0: ~StringBuffer(); nuclear@0: nuclear@0: nuclear@0: // Modify grow size used for growing/shrinking the buffer. nuclear@0: size_t GetGrowSize() const { return GrowSize; } nuclear@0: void SetGrowSize(size_t growSize); nuclear@0: nuclear@0: nuclear@0: // *** General Functions nuclear@0: // Does not release memory, just sets Size to 0 nuclear@0: void Clear(); nuclear@0: nuclear@0: // For casting to a pointer to char. nuclear@0: operator const char*() const { return (pData) ? pData : ""; } nuclear@0: // Pointer to raw buffer. nuclear@0: const char* ToCStr() const { return (pData) ? pData : ""; } nuclear@0: nuclear@0: // Returns number of bytes. nuclear@0: size_t GetSize() const { return Size ; } nuclear@0: // Tells whether or not the string is empty. nuclear@0: bool IsEmpty() const { return GetSize() == 0; } nuclear@0: nuclear@0: // Returns number of characters nuclear@0: size_t GetLength() const; nuclear@0: nuclear@0: // Returns character at the specified index nuclear@0: uint32_t GetCharAt(size_t index) const; nuclear@0: uint32_t GetFirstCharAt(size_t index, const char** offset) const; nuclear@0: uint32_t GetNextChar(const char** offset) const; nuclear@0: nuclear@0: nuclear@0: // Resize the string to the new size nuclear@0: void Resize(size_t _size); nuclear@0: void Reserve(size_t _size); nuclear@0: nuclear@0: // Appends a character nuclear@0: void AppendChar(uint32_t ch); nuclear@0: nuclear@0: // Append a string nuclear@0: void AppendString(const wchar_t* pstr, intptr_t len = -1); nuclear@0: void AppendString(const char* putf8str, intptr_t utf8StrSz = -1); nuclear@0: void AppendFormat(const char* format, ...); nuclear@0: nuclear@0: // Assigned a string with dynamic data (copied through initializer). nuclear@0: //void AssignString(const InitStruct& src, size_t size); nuclear@0: nuclear@0: // Inserts substr at posAt nuclear@0: void Insert (const char* substr, size_t posAt, intptr_t len = -1); nuclear@0: // Inserts character at posAt nuclear@0: size_t InsertCharAt(uint32_t c, size_t posAt); nuclear@0: nuclear@0: // Assignment nuclear@0: void operator = (const char* str); nuclear@0: void operator = (const wchar_t* str); nuclear@0: void operator = (const String& src); nuclear@0: void operator = (const StringBuffer& src); nuclear@0: nuclear@0: // Addition nuclear@0: void operator += (const String& src) { AppendString(src.ToCStr(),src.GetSize()); } nuclear@0: void operator += (const char* psrc) { AppendString(psrc); } nuclear@0: void operator += (const wchar_t* psrc) { AppendString(psrc); } nuclear@0: void operator += (char ch) { AppendChar(ch); } nuclear@0: //String operator + (const char* str) const ; nuclear@0: //String operator + (const String& src) const ; nuclear@0: nuclear@0: // Accesses raw bytes nuclear@0: char& operator [] (int index) nuclear@0: { nuclear@0: OVR_ASSERT(((size_t)index) < GetSize()); nuclear@0: return pData[index]; nuclear@0: } nuclear@0: char& operator [] (size_t index) nuclear@0: { nuclear@0: OVR_ASSERT(index < GetSize()); nuclear@0: return pData[index]; nuclear@0: } nuclear@0: nuclear@0: const char& operator [] (int index) const nuclear@0: { nuclear@0: OVR_ASSERT(((size_t)index) < GetSize()); nuclear@0: return pData[index]; nuclear@0: } nuclear@0: const char& operator [] (size_t index) const nuclear@0: { nuclear@0: OVR_ASSERT(index < GetSize()); nuclear@0: return pData[index]; nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: // nuclear@0: // Wrapper for string data. The data must have a guaranteed nuclear@0: // lifespan throughout the usage of the wrapper. Not intended for nuclear@0: // cached usage. Not thread safe. nuclear@0: // nuclear@0: class StringDataPtr nuclear@0: { nuclear@0: public: nuclear@0: StringDataPtr() : pStr(NULL), Size(0) {} nuclear@0: StringDataPtr(const StringDataPtr& p) nuclear@0: : pStr(p.pStr), Size(p.Size) {} nuclear@0: StringDataPtr(const char* pstr, size_t sz) nuclear@0: : pStr(pstr), Size(sz) {} nuclear@0: StringDataPtr(const char* pstr) nuclear@0: : pStr(pstr), Size((pstr != NULL) ? OVR_strlen(pstr) : 0) {} nuclear@0: explicit StringDataPtr(const String& str) nuclear@0: : pStr(str.ToCStr()), Size(str.GetSize()) {} nuclear@0: template nuclear@0: StringDataPtr(const T (&v)[N]) nuclear@0: : pStr(v), Size(N) {} nuclear@0: nuclear@0: public: nuclear@0: const char* ToCStr() const { return pStr; } nuclear@0: size_t GetSize() const { return Size; } nuclear@0: bool IsEmpty() const { return GetSize() == 0; } nuclear@0: nuclear@0: // value is a prefix of this string nuclear@0: // Character's values are not compared. nuclear@0: bool IsPrefix(const StringDataPtr& value) const nuclear@0: { nuclear@0: return ToCStr() == value.ToCStr() && GetSize() >= value.GetSize(); nuclear@0: } nuclear@0: // value is a suffix of this string nuclear@0: // Character's values are not compared. nuclear@0: bool IsSuffix(const StringDataPtr& value) const nuclear@0: { nuclear@0: return ToCStr() <= value.ToCStr() && (End()) == (value.End()); nuclear@0: } nuclear@0: nuclear@0: // Find first character. nuclear@0: // init_ind - initial index. nuclear@0: intptr_t FindChar(char c, size_t init_ind = 0) const nuclear@0: { nuclear@0: for (size_t i = init_ind; i < GetSize(); ++i) nuclear@0: if (pStr[i] == c) nuclear@0: return static_cast(i); nuclear@0: nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: // Find last character. nuclear@0: // init_ind - initial index. nuclear@0: intptr_t FindLastChar(char c, size_t init_ind = ~0) const nuclear@0: { nuclear@0: if (init_ind == (size_t)~0 || init_ind > GetSize()) nuclear@0: init_ind = GetSize(); nuclear@0: else nuclear@0: ++init_ind; nuclear@0: nuclear@0: for (size_t i = init_ind; i > 0; --i) nuclear@0: if (pStr[i - 1] == c) nuclear@0: return static_cast(i - 1); nuclear@0: nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: // Create new object and trim size bytes from the left. nuclear@0: StringDataPtr GetTrimLeft(size_t size) const nuclear@0: { nuclear@0: // Limit trim size to the size of the string. nuclear@0: size = Alg::PMin(GetSize(), size); nuclear@0: nuclear@0: return StringDataPtr(ToCStr() + size, GetSize() - size); nuclear@0: } nuclear@0: // Create new object and trim size bytes from the right. nuclear@0: StringDataPtr GetTrimRight(size_t size) const nuclear@0: { nuclear@0: // Limit trim to the size of the string. nuclear@0: size = Alg::PMin(GetSize(), size); nuclear@0: nuclear@0: return StringDataPtr(ToCStr(), GetSize() - size); nuclear@0: } nuclear@0: nuclear@0: // Create new object, which contains next token. nuclear@0: // Useful for parsing. nuclear@0: StringDataPtr GetNextToken(char separator = ':') const nuclear@0: { nuclear@0: size_t cur_pos = 0; nuclear@0: const char* cur_str = ToCStr(); nuclear@0: nuclear@0: for (; cur_pos < GetSize() && cur_str[cur_pos]; ++cur_pos) nuclear@0: { nuclear@0: if (cur_str[cur_pos] == separator) nuclear@0: { nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return StringDataPtr(ToCStr(), cur_pos); nuclear@0: } nuclear@0: nuclear@0: // Trim size bytes from the left. nuclear@0: StringDataPtr& TrimLeft(size_t size) nuclear@0: { nuclear@0: // Limit trim size to the size of the string. nuclear@0: size = Alg::PMin(GetSize(), size); nuclear@0: pStr += size; nuclear@0: Size -= size; nuclear@0: nuclear@0: return *this; nuclear@0: } nuclear@0: // Trim size bytes from the right. nuclear@0: StringDataPtr& TrimRight(size_t size) nuclear@0: { nuclear@0: // Limit trim to the size of the string. nuclear@0: size = Alg::PMin(GetSize(), size); nuclear@0: Size -= size; nuclear@0: nuclear@0: return *this; nuclear@0: } nuclear@0: nuclear@0: const char* Begin() const { return ToCStr(); } nuclear@0: const char* End() const { return ToCStr() + GetSize(); } nuclear@0: nuclear@0: // Hash functor used string data pointers nuclear@0: struct HashFunctor nuclear@0: { nuclear@0: size_t operator()(const StringDataPtr& data) const nuclear@0: { nuclear@0: return String::BernsteinHashFunction(data.ToCStr(), data.GetSize()); nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: bool operator== (const StringDataPtr& data) const nuclear@0: { nuclear@0: return (OVR_strncmp(pStr, data.pStr, data.Size) == 0); nuclear@0: } nuclear@0: nuclear@0: protected: nuclear@0: const char* pStr; nuclear@0: size_t Size; nuclear@0: }; nuclear@0: nuclear@0: } // OVR nuclear@0: nuclear@0: #endif