/* String.cpp Written by Matthew Fisher Implementation of String class. */ String::String(const UnicodeString &S) { const UINT SLength = S.Length(); _Length = SLength; _Capacity = _Length + 1; _Data = new char[_Length + 1]; for(UINT Index = 0; Index < SLength; Index++) { if(S[Index] >= 128) { _Data[Index] = '#'; } else { _Data[Index] = char(S[Index]); } } _Data[SLength] = '\0'; } String String::UnsignedIntAsHex(UINT Value) { String S; S.Allocate(20); sprintf(S.CString(), "%x", Value); S.ResizeToCStringLength(); return S; } String String::Hash32ToHexString(UINT32 hash) { String result("xxxxxxxx"); BYTE *stream = (BYTE *)&hash; ByteStreamToHexString(stream, 4, result); return result; } String String::Hash64ToHexString(UINT64 hash) { String result("xxxxxxxxxxxxxxxx"); BYTE *stream = (BYTE *)&hash; ByteStreamToHexString(stream, 8, result); return result; } UINT32 String::HexStringToHash32(const String &s) { Assert(s._Length == 8, "Invalid hash length"); UINT32 result; HexStringToByteStream(s, (BYTE *)&result); return result; } UINT64 String::HexStringToHash64(const String &s) { Assert(s._Length == 16, "Invalid hash length"); UINT64 result; HexStringToByteStream(s, (BYTE *)&result); return result; } void String::ByteStreamToHexString(const BYTE *stream, UINT count, String &result) { static const char table[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; if(result._Length != count * 2) { result.Allocate(count * 2 + 1); result._Length = count * 2; } char *resultData = result._Data; UINT stringIndex = 0; for(UINT byteIndex = 0; byteIndex < count; byteIndex++) { const BYTE curByte = stream[byteIndex]; resultData[stringIndex++] = table[ curByte & 0x0F]; resultData[stringIndex++] = table[(curByte >> 4) & 0x0F]; } resultData[stringIndex++] = '\0'; } String String::ByteStreamToHexString(const BYTE *stream, UINT count) { static const char table[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; String result; result.Allocate(count * 2 + 1); result._Length = count * 2; char *resultData = result._Data; UINT stringIndex = 0; for(UINT byteIndex = 0; byteIndex < count; byteIndex++) { const BYTE curByte = stream[byteIndex]; resultData[stringIndex++] = table[ curByte & 0x0F]; resultData[stringIndex++] = table[(curByte >> 4) & 0x0F]; } resultData[stringIndex++] = '\0'; return result; } __forceinline UINT HexStringToByteStreamHelper(char c) { if(c >= '0' && c <= '9') { return UINT(c) - '0'; } if(c >= 'a' && c <= 'f') { return 10 + UINT(c) - 'a'; } return 0; } void String::HexStringToByteStream(const String &hexString, BYTE *result) { const UINT length = hexString._Length / 2; const char *hexStringData = hexString._Data; UINT stringIndex = 0; for(UINT byteIndex = 0; byteIndex < length; byteIndex++) { result[byteIndex] = HexStringToByteStreamHelper(hexStringData[stringIndex++]); result[byteIndex] |= HexStringToByteStreamHelper(hexStringData[stringIndex++]) << 4; } } String String::ZeroPad(const String &S, UINT ZeroPadding) { String Prefix; for(UINT i = S.Length(); i < ZeroPadding; i++) { Prefix.PushEnd('0'); } return Prefix + S; } bool String::ConvertToBoolean() const { if(*this == "true") { return true; } if(*this == "false") { return false; } SignalError("Invalid string to boolean conversion"); return false; } void String::ResizeToCStringLength() { for(UINT i = 0; i < _Capacity; i++) { if(_Data[i] == '\0') { _Length = i; ReSize(_Length + 1); return; } } SignalError("No null terminator found in String::ResizeToCStringLength"); } String& String::operator = (float Value) { ostringstream S(ostringstream::out); S << Value; *this = S.str().c_str(); return *this; } String& String::operator = (char Character) { Allocate(4); _Length = 1; _Data[0] = Character; _Data[1] = '\0'; return *this; } String& String::operator = (int Value) { ostringstream S(ostringstream::out); S << Value; *this = S.str().c_str(); return *this; } String& String::operator = (UINT Value) { ostringstream S(ostringstream::out); S << Value; *this = S.str().c_str(); return *this; } String& String::operator = (UINT64 Value) { ostringstream S(ostringstream::out); S << Value; *this = S.str().c_str(); return *this; } String& String::operator = (const char *String) { UINT NewLength = UINT(strlen(String)); Allocate(NewLength + 1); memcpy(_Data, String, NewLength + 1); _Length = NewLength; return *this; } String& String::operator = (const String &S) { if(S._Data == NULL) { FreeMemory(); } else if(&S != this) { Allocate(S._Length + 1); _Length = S._Length; memcpy(_Data, S._Data, _Capacity); } return *this; } String& String::operator += (const String &S) { if(S._Length > 0) { const UINT StartLength = _Length; ReSize(StartLength + S._Length + 1); memcpy(_Data + StartLength, S._Data, S._Length + 1); _Length = StartLength + S._Length; } return *this; } String String::MakeLowercase() const { String Result(*this); for(UINT i = 0; i < Result._Length; i++) { Result[i] = tolower(Result[i]); } return Result; } void String::Partition(char Seperator, Vector &Output, bool PushEmptyStrings) const { Output.FreeMemory(); String CurEntry; const UINT Length = _Length; for(UINT Index = 0; Index < Length; Index++) { if(_Data[Index] == Seperator) { if(CurEntry._Length > 0 || PushEmptyStrings) { Output.PushEnd(CurEntry); CurEntry._Length = 0; CurEntry._Data[0] = '\0'; } } else { CurEntry.PushEnd(_Data[Index]); } } if(CurEntry._Length > 0) { Output.PushEnd(CurEntry); } } Vector String::Partition(char Seperator) const { Vector Result; Partition(Seperator, Result); return Result; } int String::FindFirstIndex(char Seperator) const { UINT L = Length(); for(UINT CurIndex = 0; CurIndex < L; CurIndex++) { if(_Data[CurIndex] == Seperator) { return CurIndex; } } return -1; } int String::FindLastIndex(char Seperator) const { UINT L = Length(); for(int CurIndex = L - 1; CurIndex >= 0; CurIndex--) { if(_Data[CurIndex] == Seperator) { return CurIndex; } } return -1; } void String::PartitionAboutIndex(UINT Index, String &Left, String &Right) const { UINT L = Length(); Assert(Index < L, "Index invalid in String::PartitionAboutIndex"); Left.ReSize(Index); for(UINT LeftIndex = 0; LeftIndex < Index; LeftIndex++) { Left._Data[LeftIndex] = _Data[LeftIndex]; } Left._Data[Index] = '\0'; Left._Length = Index; const UINT RLength = L - Index - 1; Right.ReSize(RLength); for(UINT RightIndex = 0; RightIndex < RLength; RightIndex++) { Right._Data[RightIndex] = _Data[Index + 1 + RightIndex]; } Right._Data[RLength] = '\0'; Right._Length = RLength; } void String::Partition(const String &Seperator, Vector &Output) const { Assert(Seperator.Length() >= 1, "empty seperator"); Output.FreeMemory(); String CurEntry; for(UINT CurIndex = 0; CurIndex < Length(); CurIndex++) { bool IsSeperator = true; for(UINT TestIndex = 0; TestIndex < Seperator.Length() && CurIndex + TestIndex < Length() && IsSeperator; TestIndex++) { if(_Data[CurIndex + TestIndex] != Seperator[TestIndex]) { IsSeperator = false; } } if(IsSeperator) { if(CurEntry._Length > 0) { Output.PushEnd(CurEntry); CurEntry._Length = 0; CurEntry._Data[0] = '\0'; } CurIndex += Seperator._Length - 1; } else { CurEntry.PushEnd(_Data[CurIndex]); } } if(CurEntry._Length > 0) { Output.PushEnd(CurEntry); } } bool String::ExactMatchAtOffset(const String &Find, UINT Offset) const { UINT MatchLength = 0; for(UINT Index = 0; Index + Offset < _Length && Index < Find._Length; Index++) { if(_Data[Index + Offset] == Find[Index]) { MatchLength++; if(MatchLength == Find._Length) { return true; } } } return false; } bool String::Contains(const String &Find) const { const UINT Length = _Length; for(UINT Index = 0; Index < Length; Index++) { if(ExactMatchAtOffset(Find, Index)) { return true; } } return false; } String String::FindAndReplace(const String &Find, const String &Replace) const { String Result; const UINT Length = _Length; for(UINT Index = 0; Index < Length; Index++) { if(ExactMatchAtOffset(Find, Index)) { Result += Replace; Index += Find.Length() - 1; } else { Result += _Data[Index]; } } return Result; } bool String::IsNumeric() const { return (_Length > 0 && (ConvertToDouble() != 0.0 || _Data[0] == '0' || _Data[0] == '.' || _Data[0] == '-')); } bool String::EndsWith(const String &EndCanidate) const { if(_Length < EndCanidate._Length) { return false; } for(UINT i = 0; i < EndCanidate._Length; i++) { if(_Data[_Length - EndCanidate._Length + i] != EndCanidate._Data[i]) { return false; } } return true; } String String::RemoveSuffix(const String &EndCandidate) const { Assert(EndsWith(EndCandidate), "Removing invalid suffix"); String Result = *this; for(UINT i = 0; i < EndCandidate._Length; i++) { Result.PopEnd(); } return Result; } String String::RemovePrefix(const String &StartCandidate) const { Assert(StartsWith(StartCandidate), "Removing invalid prefix"); String Result = *this; for(UINT i = 0; i < StartCandidate._Length; i++) { Result.PopFront(); } return Result; } bool String::StartsWith(const char *StartCandidate) const { for(UINT i = 0; i <= _Length; i++) { char c = StartCandidate[i]; if(c == 0) { return true; } else if(c != _Data[i]) { return false; } } return false; } bool String::StartsWith(const String &StartCanidate) const { if(_Length < StartCanidate._Length) { return false; } for(UINT i = 0; i < StartCanidate._Length; i++) { if(_Data[i] != StartCanidate._Data[i]) { return false; } } return true; } UINT32 String::Hash32() const { return Utility::Hash32((const BYTE *)_Data, _Length); } // // Overloaded operators // String operator + (const String &L, const String &R) { String Result; const UINT TotalLength = L._Length + R._Length; Result.Allocate(TotalLength + 1); Result._Length = TotalLength; Result._Data[TotalLength] = '\0'; if(L._Length > 0) { memcpy(Result._Data, L._Data, L._Length); } if(R._Length > 0) { memcpy(Result._Data + L._Length, R._Data, R._Length); } return Result; } /*String operator + (const char *L, const String &R) { String Ret = L; for(UINT i = 0; i < R.Length(); i++) { Ret.PushEnd(R[i]); } return Ret; } String operator + (const String &L, const char *R) { UINT RLen = UINT(strlen(R)); String Ret = L; for(UINT i = 0; i < RLen; i++) { Ret.PushEnd(R[i]); } return Ret; }*/ ostream& operator << (ostream &os, const String &S) { if(S.Length() > 0) { os.write(S.CString(), S.Length()); } return os; }