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