#pragma once #include // std::min /****************************************************************************/ // SCString class SCString { friend class c_Graph; private: //When this is not a null pointer and it is not equal to m_DefaultString, then when m_OwnString is 0 it is pointing to a string allocated elsewhere. When m_OwnString is 1, it is pointing to a string which is allocated by an instance of this object. This class is used only by ACSIL. This design of referencing a string allocated elsewhere, needs to be removed and when we need to access those strings, we need to use a function instead and not this class because this design is inherently unsafe. char* m_String; int m_OwnString; int m_IsModified; const char* m_DefaultString; public: SCString(); SCString(int DummyValue); // this is needed by the c_ArrayWrapper class SCString(const SCString& Str);//Copy constructor SCString(const char* SourceString); SCString(const char* SourceString, int Length); SCString(const char Character); ~SCString(); void Initialize(); void Clear(); bool operator == (const SCString& Rhs) const; bool operator == (const char* Rhs) const; bool operator != (const SCString& Rhs) const; bool operator != (const char* Rhs) const; bool operator < (const SCString& Rhs) const; bool operator < (const char* Rhs) const; SCString& operator = (const SCString& Rhs); SCString& operator = (const char* String); SCString& operator += (const SCString& Rhs); SCString operator + (const SCString& Rhs) const; operator const char* () const; SCString& Format(const char* String, ...); SCString& AppendFormat(const char* String, ...); int Compare(const char* StringToCompare, int NumChars = 0) const; int CompareNoCase(const char* StringToCompare, int NumChars = 0) const; int CompareNoCase(const SCString& StringToCompare, int NumChars = 0) const; int IsModified() const; int IsEmpty() const; const char* GetChars() const; int GetLength() const; int IndexOf(char Delimiter, int StartIndex = 0) const; int LastIndexOf(char SearchCharacter, int StartIndex) const; SCString GetSubString(int SubstringLength, int StartIndex = 0) const; void ParseLines(std::vector &Lines); void ParseLineItemsAsFloats(std::vector &FloatValues); int Tokenize(const char* Delim, std::vector& Tokens); SCString& Append(const SCString& Rhs); SCString Left(int Count) const; SCString Right(int Count) const; private: // The maximum allowed length of a string is the maximum value that // can be stored in an int, -1 so that the buffer size which has to // include the null terminating character can also be stored in an // int. static const int MAX_LENGTH = INT_MAX - 1; private: static int TerminatedStringLength(const char* String); static char* StringNew(const int NumBytes); static void StringDelete(char* String); SCString& Copy(const char* String, int StringLength); void InternalAppendFormat ( const char* String , const va_list& ArgumentList ); // For Sierra Chart internal use only void InternalSetStatic(const char* String); }; /*==========================================================================*/ inline SCString::SCString() { m_DefaultString = ""; m_String = (char*)m_DefaultString; //NULL m_OwnString = 0; m_IsModified = 0; } /*============================================================================ This constructor is needed by the c_ArrayWrapper class. ----------------------------------------------------------------------------*/ inline SCString::SCString(int DummyValue) { m_DefaultString = ""; m_String = (char*)m_DefaultString; //NULL m_OwnString= 0; m_IsModified= 0; } /*==========================================================================*/ inline SCString::SCString(const SCString& Str) { m_DefaultString = ""; m_String = (char*)m_DefaultString; //NULL m_OwnString = 0; m_IsModified = Str.m_IsModified; const int StringLength = TerminatedStringLength(Str.m_String); if (StringLength != 0) { const int BufferSize = StringLength + 1; m_String = StringNew(BufferSize); if (m_String != NULL) { #if __STDC_WANT_SECURE_LIB__ strcpy_s(m_String, BufferSize, Str.m_String); #else strcpy(m_String, Str.m_String); #endif m_OwnString = 1; } else { m_String = (char*)m_DefaultString; //NULL } } else { m_String = (char*)m_DefaultString; //NULL } } /*==========================================================================*/ inline SCString::SCString(const char* SourceString) { m_DefaultString = ""; m_String = (char*)m_DefaultString; //NULL m_OwnString = 0; m_IsModified = 0; const int StringLength = TerminatedStringLength(SourceString); if (StringLength != 0) { const int BufferSize = StringLength + 1; m_String = StringNew(BufferSize); if (m_String != NULL) { #if __STDC_WANT_SECURE_LIB__ strncpy_s(m_String, BufferSize, SourceString, StringLength); #else strncpy(m_String, SourceString, StringLength); #endif m_OwnString = 1; m_IsModified = 1; } else { m_String = (char*)m_DefaultString; //NULL } } else m_String = (char*)m_DefaultString; //NULL } /*==========================================================================*/ inline SCString::SCString(const char* SourceString, int Length) { m_DefaultString = ""; m_String = (char*)m_DefaultString; //NULL m_OwnString= 0; m_IsModified= 0; int StringLength = 0; if (SourceString != NULL) { if (Length >= 0) { // Note: if `min` is defined as a macro, that prevents us from // using `std::min`. But if `min` is not defined, we need to // specify the `std` namespace. #ifdef min StringLength = min(Length, MAX_LENGTH); #else StringLength = std::min(Length, MAX_LENGTH); #endif } else StringLength = TerminatedStringLength(SourceString); } if (StringLength != 0) { const int BufferSize = StringLength + 1; m_String = StringNew(BufferSize); if (m_String != NULL) { #if __STDC_WANT_SECURE_LIB__ strncpy_s(m_String, BufferSize, SourceString, StringLength); #else strncpy(m_String, SourceString, StringLength); #endif m_OwnString = 1; m_IsModified = 1; } else { m_String = (char*)m_DefaultString; //NULL } } else m_String = (char*)m_DefaultString; //NULL } /*==========================================================================*/ inline SCString::SCString(const char Character) { m_DefaultString = ""; m_String = (char*)m_DefaultString; //NULL m_OwnString = 0; m_IsModified = 0; const int StringLength = 1; const int BufferSize = StringLength + 1; m_String = StringNew(BufferSize); if (m_String != NULL) { m_String[0] = Character; m_String[StringLength] = '\0'; m_OwnString = 1; m_IsModified = 1; } else m_String = (char*)m_DefaultString; //NULL } /*==========================================================================*/ inline SCString::~SCString() { if (m_OwnString && m_String != NULL) { StringDelete(m_String); m_String = nullptr; m_OwnString = 0; m_IsModified = 0; } } /*==========================================================================*/ inline void SCString::Initialize() { if (m_OwnString && m_String != NULL) StringDelete(m_String); m_String = (char*)m_DefaultString; //NULL m_OwnString = 0; m_IsModified = 0; } /*==========================================================================*/ inline void SCString::Clear() { Initialize(); } /*==========================================================================*/ inline bool SCString::operator == (const SCString& Rhs) const { return (Compare(Rhs.GetChars()) == 0); } /*==========================================================================*/ inline bool SCString::operator == (const char* Rhs) const { return (Compare(Rhs) == 0); } /*==========================================================================*/ inline bool SCString::operator != (const SCString& Rhs) const { return (Compare(Rhs.GetChars()) != 0); } /*==========================================================================*/ inline bool SCString::operator != (const char* Rhs) const { return (Compare(Rhs) != 0); } /*==========================================================================*/ inline bool SCString::operator < (const SCString& Rhs) const { return Compare(Rhs.GetChars()) < 0; } /*==========================================================================*/ inline bool SCString::operator < (const char* Rhs) const { return Compare(Rhs) < 0; } /*==========================================================================*/ inline SCString& SCString::operator = (const SCString& Rhs) { if (&Rhs == this) return *this; if (Rhs.IsEmpty() && IsEmpty() ) return *this; if (Rhs.m_String != nullptr) Copy(Rhs.m_String, TerminatedStringLength(Rhs.m_String)); else Initialize(); m_IsModified = 1; return *this; } /*==========================================================================*/ inline SCString& SCString::operator = (const char* String) { if ((String == NULL || String[0] == '\0') && IsEmpty()) return *this;//nothing to do if (String != nullptr) Copy(String, TerminatedStringLength(String)); else Initialize(); m_IsModified = 1; return *this; } /*==========================================================================*/ inline SCString& SCString::operator += (const SCString& Rhs) { return Append(Rhs); } /*==========================================================================*/ inline SCString SCString::operator + (const SCString& Rhs) const { SCString Result(*this); Result += Rhs; return Result; } /*==========================================================================*/ inline SCString::operator const char* () const { if (m_String != nullptr) return m_String; else return ""; } /*==========================================================================*/ inline SCString& SCString::Format(const char* String, ...) { Initialize(); va_list ArgumentList; va_start(ArgumentList, String); InternalAppendFormat(String, ArgumentList); va_end(ArgumentList); return *this; } /*==========================================================================*/ inline SCString& SCString::AppendFormat(const char* String, ...) { va_list ArgumentList; va_start(ArgumentList, String); InternalAppendFormat(String, ArgumentList); va_end(ArgumentList); return *this; } /*==========================================================================*/ inline int SCString::Compare(const char* StringToCompare, int NumChars) const { const char* InternalString= ""; if (m_String != nullptr) InternalString=m_String; if (StringToCompare != NULL) { if (NumChars == 0) return strcmp(StringToCompare, InternalString); else return strncmp(StringToCompare, InternalString, NumChars); } else return 0; } /*==========================================================================*/ inline int SCString::CompareNoCase(const char* StringToCompare, int NumChars) const { const char* InternalString= ""; if (m_String != nullptr) InternalString=m_String; #if _MSC_VER >= 1400 if (NumChars == 0) return _stricmp(StringToCompare, InternalString); else return _strnicmp(StringToCompare, InternalString, NumChars); #else if (NumChars == 0) return stricmp(StringToCompare, InternalString); else return strnicmp(StringToCompare, InternalString, NumChars); #endif } /*==========================================================================*/ inline int SCString::CompareNoCase(const SCString& StringToCompare, int NumChars) const { return CompareNoCase(StringToCompare.GetChars(),NumChars); } /*==========================================================================*/ inline int SCString::IsModified() const { return m_IsModified; } /*==========================================================================*/ inline int SCString::IsEmpty() const { if (m_String == NULL) return 1; return (m_String[0] == '\0') ? 1 : 0; } /*==========================================================================*/ inline const char* SCString::GetChars() const { if (m_String != nullptr) return m_String; else return ""; } /*==========================================================================*/ inline int SCString::GetLength() const { return TerminatedStringLength(m_String); } /*==========================================================================*/ inline int SCString::IndexOf(char Delimiter, int StartIndex) const { if (m_String == NULL) return -1; for (int i = StartIndex; i < GetLength(); i++) { if (m_String[i] == Delimiter) return i; } return -1; } /*==========================================================================*/ inline int SCString::LastIndexOf(char SearchCharacter, int StartIndex) const { if (m_String == NULL) return -1; const int Length = GetLength(); if (StartIndex >= Length) StartIndex = Length - 1; for (int CharacterIndex = StartIndex; CharacterIndex >= 0; CharacterIndex--) { if (m_String[CharacterIndex] == SearchCharacter) return CharacterIndex; } return -1; } /*==========================================================================*/ inline SCString SCString::GetSubString(int SubstringLength, int StartIndex) const { if (m_String == NULL) return SCString(); const int Length = GetLength(); if (StartIndex >= Length) return SCString(); if (SubstringLength > Length - StartIndex) SubstringLength = Length - StartIndex; SCString Result; Result.Copy(m_String + StartIndex, SubstringLength); return Result; } /*==========================================================================*/ inline void SCString::ParseLines(std::vector& Lines) { int StartIndex = 0; while(true) { int NewlineIndexPosition = IndexOf('\n', StartIndex); if (NewlineIndexPosition == -1) NewlineIndexPosition = GetLength(); SCString Line; Line = GetSubString(NewlineIndexPosition - StartIndex, StartIndex); Lines.push_back(Line); StartIndex = NewlineIndexPosition + 1; if(StartIndex >= GetLength()) break; } } /*==========================================================================*/ inline void SCString::ParseLineItemsAsFloats(std::vector& FloatValues) { char FieldsDelimitter = ','; int find_delim = IndexOf(FieldsDelimitter); int pos = 0; int ArraySize = 1000; int CurrentItem = 0; while (find_delim > 0 && CurrentItem < ArraySize) { SCString NextStr = GetSubString(find_delim - pos, pos); FloatValues.push_back(static_cast(atof(NextStr))); pos = find_delim + 1; find_delim = IndexOf(FieldsDelimitter,find_delim + 1); CurrentItem++; } } /*==========================================================================*/ inline int SCString::Tokenize(const char* Delim, std::vector& Tokens) { Tokens.erase(Tokens.begin(), Tokens.end()); if (m_String == NULL) return 0; // The object must own the string for this method because this method // modifies the string. if (!m_OwnString) Copy(m_String, TerminatedStringLength(m_String)); Tokens.push_back(m_String); if (Delim == NULL) return 1; int StrLen = GetLength(); int DelimLen = TerminatedStringLength(Delim); if (DelimLen == 0) return 1; // Not worth splitting every character for (int a = 0; a < StrLen; a++) { // Look for the delimiter bool DelimFound = true; for (int b = 0; b < DelimLen; b++) { if (a + b >= StrLen) { DelimFound = false; break; } if (m_String[a + b] != Delim[b]) { DelimFound = false; break; } } if (DelimFound) { m_String[a] = '\0'; Tokens.push_back(m_String + a + DelimLen); a += DelimLen - 1; } } return static_cast(Tokens.size()); } /*==========================================================================*/ inline SCString& SCString::Append(const SCString& Rhs) { int RightStringLength = Rhs.GetLength(); if (RightStringLength == 0) return *this; const int LeftStringLength = GetLength(); // Make sure the new string length does not exceed MAX_LENGTH. if (RightStringLength > MAX_LENGTH - LeftStringLength) RightStringLength = MAX_LENGTH - LeftStringLength; const int NewStringLength = LeftStringLength + RightStringLength; char* NewString = StringNew(NewStringLength + 1); if (NewString != nullptr) { const char* InternalString = ""; if (m_String != nullptr) InternalString = m_String; #if __STDC_WANT_SECURE_LIB__ strncpy_s(NewString, NewStringLength + 1, InternalString, LeftStringLength); strncpy_s(NewString + LeftStringLength, NewStringLength + 1 - LeftStringLength, Rhs.m_String, RightStringLength); #else strncpy(NewString, InternalString, LeftStringLength); strncpy(NewString + LeftStringLength, Rhs.m_String, RightStringLength); #endif NewString[NewStringLength] = '\0'; Initialize(); m_String = NewString; m_OwnString = 1; } m_IsModified = 1; return *this; } /*==========================================================================*/ inline SCString SCString::Left(int Count) const { const int StringLength = GetLength(); if (Count >= StringLength) return *this; else if (Count > 0) return SCString(m_String, Count); else if (Count < 0) { int TargetLength = StringLength + Count; if (TargetLength <= 0) return SCString(); else return SCString(m_String, TargetLength); } else return SCString(); } /*==========================================================================*/ inline SCString SCString::Right(int Count) const { int StringLength = GetLength(); if (Count >= StringLength) return *this; else if (Count > 0) return SCString(m_String + (StringLength - Count), Count); else if (Count < 0) { int TargetLength = StringLength + Count; if (TargetLength <= 0) return SCString(); else return SCString(m_String + (StringLength - TargetLength), TargetLength); } else return SCString(); } /*============================================================================ Static ----------------------------------------------------------------------------*/ inline int SCString::TerminatedStringLength(const char* String) { if (String == NULL) return 0; const size_t StringLength = strlen(String); if (StringLength > MAX_LENGTH) return MAX_LENGTH; return static_cast(StringLength); } /*============================================================================ Static ----------------------------------------------------------------------------*/ inline char* SCString::StringNew(const int NumBytes) { if (NumBytes <= 0) return NULL; //The reason that the heap allocation functions in the operating system API are being used rather than the "new" operator is because a custom study DLL which is using the s_sc structure which contains SCString members can be unloaded during debugging and code modification. These pointers are still remembered within an instance of s_sc which is a member of c_Graph which resides within the SierraChart.exe module. An exception will occur when using delete after the DLL has been freed and reloaded. Whereas using the operating system heap allocation functions, this exception is avoided. char* p_Block = (char*)HeapAlloc(GetProcessHeap(), 0, NumBytes); if (p_Block != NULL) memset(p_Block, 0, NumBytes); return p_Block; } /*============================================================================ Static ----------------------------------------------------------------------------*/ inline void SCString::StringDelete(char* String) { if (String != NULL) HeapFree(GetProcessHeap(), 0, String); } /*==========================================================================*/ inline SCString& SCString::Copy(const char* String, int StringLength) { if (String == NULL) return *this; Initialize(); if (StringLength != 0) { if (StringLength > MAX_LENGTH) StringLength = MAX_LENGTH; const int BufferSize = StringLength + 1; m_String = StringNew(BufferSize); if (m_String == NULL) m_String = (char*)m_DefaultString; else { #if __STDC_WANT_SECURE_LIB__ strncpy_s(m_String, BufferSize, String, StringLength); #else strncpy(m_String, String, StringLength); #endif m_String[StringLength] = '\0'; m_OwnString = 1; } } m_IsModified = 1; return *this; } /*==========================================================================*/ inline void SCString::InternalAppendFormat ( const char* FormatString , const va_list& ArgumentList ) { if (FormatString == NULL) return; const int PriorLength = TerminatedStringLength(m_String); const int TargetLength = _vscprintf(FormatString, ArgumentList); if (TargetLength < 0 || TargetLength > MAX_LENGTH - PriorLength) return; char* NewStringBuffer = StringNew(PriorLength + TargetLength + 1); if (NewStringBuffer == NULL) return; // Copy the current string to the new buffer. if (PriorLength > 0) memcpy(NewStringBuffer, m_String, PriorLength); #if __STDC_WANT_SECURE_LIB__ vsprintf_s ( NewStringBuffer + PriorLength , TargetLength + 1 , FormatString , ArgumentList ); #else vsprintf(NewStringBuffer + PriorLength, FormatString, ArgumentList); #endif Clear(); m_String = NewStringBuffer; m_OwnString = 1; m_IsModified = 1; } /*============================================================================ For Sierra Chart internal use only. ----------------------------------------------------------------------------*/ inline void SCString::InternalSetStatic(const char* String) { Initialize(); m_String = const_cast(String); m_OwnString = 0; m_IsModified = 0; } /*==========================================================================*/