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