From 848bf7f697c1fa57bcbd6d2694d98908e48daeac Mon Sep 17 00:00:00 2001 From: Pasukhin Dmitry Date: Sun, 18 Jan 2026 21:07:30 +0000 Subject: [PATCH] Foundation Classes - Enhance TCollection_ExtendedString with std::u16string_view support (#1009) - Added constructor and assignment operator for std::u16string_view. - Implemented conversion operator to std::u16string_view. - Introduced methods to append std::u16string_view. - Added Copy and assignment operator overloads for char16_t pointers. - Fixed variable name inconsistencies (myLength vs mylength). - Added new string manipulation methods: LeftAdjust, RightAdjust, LeftJustify, RightJustify, Center, Capitalize, Prepend, FirstLocationInSet, FirstLocationNotInSet, IntegerValue, IsIntegerValue, RealValue, IsRealValue, IsSameString. --- .../XmlMDataStd_ExtStringArrayDriver.cxx | 2 +- .../TCollection_ExtendedString_Test.cxx | 584 ++++++++ .../TCollection_ExtendedString.cxx | 1330 ++++++++++++----- .../TCollection_ExtendedString.hxx | 911 +++++++++-- 4 files changed, 2262 insertions(+), 565 deletions(-) diff --git a/src/ApplicationFramework/TKXmlL/XmlMDataStd/XmlMDataStd_ExtStringArrayDriver.cxx b/src/ApplicationFramework/TKXmlL/XmlMDataStd/XmlMDataStd_ExtStringArrayDriver.cxx index 94a83ae535..a0b0c25b85 100644 --- a/src/ApplicationFramework/TKXmlL/XmlMDataStd/XmlMDataStd_ExtStringArrayDriver.cxx +++ b/src/ApplicationFramework/TKXmlL/XmlMDataStd/XmlMDataStd_ExtStringArrayDriver.cxx @@ -319,7 +319,7 @@ void XmlMDataStd_ExtStringArrayDriver::Paste(const occ::handle& t } if (xstr.SearchFromEnd(c) == isym - 1) isym--; // replace the last separator by '\0' - xstr.SetValue(isym, '\0'); + xstr.SetValue(isym, u'\0'); #ifdef _DEBUG TCollection_AsciiString cstr(xstr, '?'); // deb #endif diff --git a/src/FoundationClasses/TKernel/GTests/TCollection_ExtendedString_Test.cxx b/src/FoundationClasses/TKernel/GTests/TCollection_ExtendedString_Test.cxx index f356bf1b3e..bb49fe0803 100644 --- a/src/FoundationClasses/TKernel/GTests/TCollection_ExtendedString_Test.cxx +++ b/src/FoundationClasses/TKernel/GTests/TCollection_ExtendedString_Test.cxx @@ -13,6 +13,7 @@ #include #include +#include #include @@ -444,3 +445,586 @@ TEST(TCollection_ExtendedStringTest, OCC3277_CatOperation) // Verify the content matches EXPECT_EQ(anInputString, aResult) << "Concatenated string should match input string"; } + +// ======================================== +// Tests for LeftAdjust method +// ======================================== + +TEST(TCollection_ExtendedStringTest, LeftAdjust_RemovesLeadingSpaces) +{ + TCollection_ExtendedString aString(" Hello World"); + aString.LeftAdjust(); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello World", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, LeftAdjust_NoLeadingSpaces) +{ + TCollection_ExtendedString aString("Hello World"); + aString.LeftAdjust(); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello World", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, LeftAdjust_AllSpaces) +{ + TCollection_ExtendedString aString(" "); + aString.LeftAdjust(); + EXPECT_TRUE(aString.IsEmpty()); +} + +TEST(TCollection_ExtendedStringTest, LeftAdjust_EmptyString) +{ + TCollection_ExtendedString aString; + aString.LeftAdjust(); + EXPECT_TRUE(aString.IsEmpty()); +} + +// ======================================== +// Tests for RightAdjust method +// ======================================== + +TEST(TCollection_ExtendedStringTest, RightAdjust_RemovesTrailingSpaces) +{ + TCollection_ExtendedString aString("Hello World "); + aString.RightAdjust(); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello World", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, RightAdjust_NoTrailingSpaces) +{ + TCollection_ExtendedString aString("Hello World"); + aString.RightAdjust(); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello World", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, RightAdjust_AllSpaces) +{ + TCollection_ExtendedString aString(" "); + aString.RightAdjust(); + EXPECT_TRUE(aString.IsEmpty()); +} + +// ======================================== +// Tests for LeftJustify method +// ======================================== + +TEST(TCollection_ExtendedStringTest, LeftJustify_ExtendsWithFiller) +{ + TCollection_ExtendedString aString("Hello"); + aString.LeftJustify(10, '-'); + EXPECT_EQ(10, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello-----", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, LeftJustify_NoChangeIfShorter) +{ + TCollection_ExtendedString aString("Hello"); + aString.LeftJustify(3, '-'); + EXPECT_EQ(5, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello", anAscii.ToCString()); +} + +// ======================================== +// Tests for RightJustify method +// ======================================== + +TEST(TCollection_ExtendedStringTest, RightJustify_ExtendsWithFiller) +{ + TCollection_ExtendedString aString("Hello"); + aString.RightJustify(10, '-'); + EXPECT_EQ(10, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("-----Hello", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, RightJustify_NoChangeIfShorter) +{ + TCollection_ExtendedString aString("Hello"); + aString.RightJustify(3, '-'); + EXPECT_EQ(5, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello", anAscii.ToCString()); +} + +// ======================================== +// Tests for Center method +// ======================================== + +TEST(TCollection_ExtendedStringTest, Center_CentersWithFiller) +{ + TCollection_ExtendedString aString("Hi"); + aString.Center(6, '-'); + EXPECT_EQ(6, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("--Hi--", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, Center_OddWidth) +{ + TCollection_ExtendedString aString("Hi"); + aString.Center(7, '-'); + EXPECT_EQ(7, aString.Length()); + TCollection_AsciiString anAscii(aString); + // 7 - 2 = 5, left = 2, right = 3 + EXPECT_STREQ("--Hi---", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, Center_NoChangeIfShorter) +{ + TCollection_ExtendedString aString("Hello"); + aString.Center(3, '-'); + EXPECT_EQ(5, aString.Length()); +} + +// ======================================== +// Tests for Capitalize method +// ======================================== + +TEST(TCollection_ExtendedStringTest, Capitalize_UppercasesFirstLetter) +{ + TCollection_ExtendedString aString("hello WORLD"); + aString.Capitalize(); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello world", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, Capitalize_AlreadyCapitalized) +{ + TCollection_ExtendedString aString("Hello"); + aString.Capitalize(); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, Capitalize_EmptyString) +{ + TCollection_ExtendedString aString; + aString.Capitalize(); + EXPECT_TRUE(aString.IsEmpty()); +} + +// ======================================== +// Tests for Prepend method +// ======================================== + +TEST(TCollection_ExtendedStringTest, Prepend_InsertsAtBeginning) +{ + TCollection_ExtendedString aString("World"); + TCollection_ExtendedString aPrefix("Hello "); + aString.Prepend(aPrefix); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello World", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, Prepend_EmptyPrefix) +{ + TCollection_ExtendedString aString("Hello"); + TCollection_ExtendedString aPrefix; + aString.Prepend(aPrefix); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, Prepend_ToEmptyString) +{ + TCollection_ExtendedString aString; + TCollection_ExtendedString aPrefix("Hello"); + aString.Prepend(aPrefix); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello", anAscii.ToCString()); +} + +// ======================================== +// Tests for FirstLocationInSet method +// ======================================== + +TEST(TCollection_ExtendedStringTest, FirstLocationInSet_FindsCharacter) +{ + TCollection_ExtendedString aString("Hello World"); + TCollection_ExtendedString aSet("aeiou"); + int aLoc = aString.FirstLocationInSet(aSet, 1, aString.Length()); + EXPECT_EQ(2, aLoc); // 'e' at position 2 +} + +TEST(TCollection_ExtendedStringTest, FirstLocationInSet_NotFound) +{ + TCollection_ExtendedString aString("BCDFG"); + TCollection_ExtendedString aSet("aeiou"); + int aLoc = aString.FirstLocationInSet(aSet, 1, aString.Length()); + EXPECT_EQ(0, aLoc); +} + +TEST(TCollection_ExtendedStringTest, FirstLocationInSet_EmptySet) +{ + TCollection_ExtendedString aString("Hello"); + TCollection_ExtendedString aSet; + int aLoc = aString.FirstLocationInSet(aSet, 1, aString.Length()); + EXPECT_EQ(0, aLoc); +} + +// ======================================== +// Tests for FirstLocationNotInSet method +// ======================================== + +TEST(TCollection_ExtendedStringTest, FirstLocationNotInSet_FindsCharacter) +{ + TCollection_ExtendedString aString("aaaHello"); + TCollection_ExtendedString aSet("a"); + int aLoc = aString.FirstLocationNotInSet(aSet, 1, aString.Length()); + EXPECT_EQ(4, aLoc); // 'H' at position 4 +} + +TEST(TCollection_ExtendedStringTest, FirstLocationNotInSet_AllInSet) +{ + TCollection_ExtendedString aString("aaa"); + TCollection_ExtendedString aSet("a"); + int aLoc = aString.FirstLocationNotInSet(aSet, 1, aString.Length()); + EXPECT_EQ(0, aLoc); +} + +TEST(TCollection_ExtendedStringTest, FirstLocationNotInSet_EmptySet) +{ + TCollection_ExtendedString aString("Hello"); + TCollection_ExtendedString aSet; + int aLoc = aString.FirstLocationNotInSet(aSet, 1, aString.Length()); + EXPECT_EQ(1, aLoc); // First character is not in empty set +} + +// ======================================== +// Tests for IntegerValue and IsIntegerValue methods +// ======================================== + +TEST(TCollection_ExtendedStringTest, IntegerValue_ValidInteger) +{ + TCollection_ExtendedString aString("12345"); + EXPECT_TRUE(aString.IsIntegerValue()); + EXPECT_EQ(12345, aString.IntegerValue()); +} + +TEST(TCollection_ExtendedStringTest, IntegerValue_NegativeInteger) +{ + TCollection_ExtendedString aString("-42"); + EXPECT_TRUE(aString.IsIntegerValue()); + EXPECT_EQ(-42, aString.IntegerValue()); +} + +TEST(TCollection_ExtendedStringTest, IntegerValue_NotAnInteger) +{ + TCollection_ExtendedString aString("Hello"); + EXPECT_FALSE(aString.IsIntegerValue()); +} + +TEST(TCollection_ExtendedStringTest, IntegerValue_EmptyString) +{ + TCollection_ExtendedString aString; + EXPECT_FALSE(aString.IsIntegerValue()); + EXPECT_EQ(0, aString.IntegerValue()); +} + +// ======================================== +// Tests for RealValue and IsRealValue methods +// ======================================== + +TEST(TCollection_ExtendedStringTest, RealValue_ValidReal) +{ + TCollection_ExtendedString aString("3.14159"); + EXPECT_TRUE(aString.IsRealValue(true)); + EXPECT_NEAR(3.14159, aString.RealValue(), 0.00001); +} + +TEST(TCollection_ExtendedStringTest, RealValue_Integer) +{ + TCollection_ExtendedString aString("42"); + EXPECT_TRUE(aString.IsRealValue(true)); + EXPECT_NEAR(42.0, aString.RealValue(), 0.00001); +} + +TEST(TCollection_ExtendedStringTest, RealValue_NotAReal) +{ + TCollection_ExtendedString aString("Hello"); + EXPECT_FALSE(aString.IsRealValue(true)); +} + +TEST(TCollection_ExtendedStringTest, RealValue_EmptyString) +{ + TCollection_ExtendedString aString; + EXPECT_FALSE(aString.IsRealValue()); + EXPECT_NEAR(0.0, aString.RealValue(), 0.00001); +} + +// ======================================== +// Tests for IsSameString method +// ======================================== + +TEST(TCollection_ExtendedStringTest, IsSameString_CaseSensitiveMatch) +{ + TCollection_ExtendedString aString1("Hello"); + TCollection_ExtendedString aString2("Hello"); + EXPECT_TRUE(aString1.IsSameString(aString2, true)); +} + +TEST(TCollection_ExtendedStringTest, IsSameString_CaseSensitiveMismatch) +{ + TCollection_ExtendedString aString1("Hello"); + TCollection_ExtendedString aString2("hello"); + EXPECT_FALSE(aString1.IsSameString(aString2, true)); +} + +TEST(TCollection_ExtendedStringTest, IsSameString_CaseInsensitiveMatch) +{ + TCollection_ExtendedString aString1("Hello"); + TCollection_ExtendedString aString2("HELLO"); + EXPECT_TRUE(aString1.IsSameString(aString2, false)); +} + +TEST(TCollection_ExtendedStringTest, IsSameString_DifferentLengths) +{ + TCollection_ExtendedString aString1("Hello"); + TCollection_ExtendedString aString2("Hello World"); + EXPECT_FALSE(aString1.IsSameString(aString2, false)); +} + +// ======================================== +// Tests for C++17 std::u16string_view support +// ======================================== + +#if __cplusplus >= 201703L +TEST(TCollection_ExtendedStringTest, StringView_Constructor) +{ + std::u16string_view aView(u"Hello World"); + TCollection_ExtendedString aString(aView); + EXPECT_EQ(11, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello World", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, StringView_Assignment) +{ + TCollection_ExtendedString aString("Original"); + std::u16string_view aView(u"New Value"); + aString = aView; + EXPECT_EQ(9, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("New Value", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, StringView_AssignCat) +{ + TCollection_ExtendedString aString("Hello"); + std::u16string_view aView(u" World"); + aString.AssignCat(aView); + EXPECT_EQ(11, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("Hello World", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, StringView_Conversion) +{ + TCollection_ExtendedString aString("Test"); + std::u16string_view aView = aString; + EXPECT_EQ(4, aView.size()); + EXPECT_EQ(u'T', aView[0]); + EXPECT_EQ(u'e', aView[1]); +} + +TEST(TCollection_ExtendedStringTest, StringView_EmptyConstructor) +{ + std::u16string_view aView; + TCollection_ExtendedString aString(aView); + EXPECT_TRUE(aString.IsEmpty()); +} +#endif + +// ======================================== +// Tests for edge cases and error handling +// ======================================== + +TEST(TCollection_ExtendedStringTest, FillerConstructor_NegativeLength_Throws) +{ + EXPECT_THROW(TCollection_ExtendedString(-5, u'X'), Standard_OutOfRange); +} + +TEST(TCollection_ExtendedStringTest, FillerConstructor_ZeroLength) +{ + const int aZeroLength = 0; + TCollection_ExtendedString aString(aZeroLength, u'X'); + EXPECT_TRUE(aString.IsEmpty()); + EXPECT_EQ(0, aString.Length()); +} + +// ======================================== +// Tests for AssignCat self-append (aliasing) +// ======================================== + +TEST(TCollection_ExtendedStringTest, AssignCat_SelfAppend) +{ + TCollection_ExtendedString aString("Hello"); + aString.AssignCat(aString); + EXPECT_EQ(10, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("HelloHello", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, AssignCat_SelfAppendMultiple) +{ + TCollection_ExtendedString aString("AB"); + aString.AssignCat(aString); + aString.AssignCat(aString); + EXPECT_EQ(8, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("ABABABAB", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, AssignCat_PointerIntoSelf) +{ + TCollection_ExtendedString aString("HelloWorld"); + // Append substring of self starting at position 5 (0-based), length 5 + aString.AssignCat(aString.ToExtString() + 5, 5); + EXPECT_EQ(15, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("HelloWorldWorld", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, AssignCat_PointerIntoSelfBeginning) +{ + TCollection_ExtendedString aString("Hello"); + // Append first 3 characters of self + aString.AssignCat(aString.ToExtString(), 3); + EXPECT_EQ(8, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("HelloHel", anAscii.ToCString()); +} + +// ======================================== +// Tests for Prepend aliasing +// ======================================== + +TEST(TCollection_ExtendedStringTest, Prepend_PointerIntoSelf) +{ + TCollection_ExtendedString aString("HelloWorld"); + // Prepend substring of self starting at position 5 (0-based), length 5 + aString.Prepend(aString.ToExtString() + 5, 5); + EXPECT_EQ(15, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("WorldHelloWorld", anAscii.ToCString()); +} + +TEST(TCollection_ExtendedStringTest, Prepend_PointerIntoSelfBeginning) +{ + TCollection_ExtendedString aString("Hello"); + // Prepend first 3 characters of self + aString.Prepend(aString.ToExtString(), 3); + EXPECT_EQ(8, aString.Length()); + TCollection_AsciiString anAscii(aString); + EXPECT_STREQ("HelHello", anAscii.ToCString()); +} + +// ======================================== +// Tests for comparison methods with null/negative length +// ======================================== + +TEST(TCollection_ExtendedStringTest, IsEqual_NullPointer) +{ + TCollection_ExtendedString aString("Hello"); + EXPECT_FALSE(aString.IsEqual(nullptr, 5)); +} + +TEST(TCollection_ExtendedStringTest, IsEqual_NegativeLength) +{ + TCollection_ExtendedString aString("Hello"); + const char16_t* aOther = u"Hello"; + EXPECT_FALSE(aString.IsEqual(aOther, -1)); +} + +TEST(TCollection_ExtendedStringTest, IsEqual_ZeroLengthNullPointer) +{ + TCollection_ExtendedString aString; + // Empty string with null pointer and zero length should be equal + EXPECT_TRUE(aString.IsEqual(nullptr, 0)); +} + +TEST(TCollection_ExtendedStringTest, IsDifferent_NullPointer) +{ + TCollection_ExtendedString aString("Hello"); + EXPECT_TRUE(aString.IsDifferent(nullptr, 5)); +} + +TEST(TCollection_ExtendedStringTest, IsDifferent_NegativeLength) +{ + TCollection_ExtendedString aString("Hello"); + const char16_t* aOther = u"Hello"; + EXPECT_TRUE(aString.IsDifferent(aOther, -1)); +} + +TEST(TCollection_ExtendedStringTest, IsLess_NullPointer) +{ + TCollection_ExtendedString aString("Hello"); + // Non-empty string is not less than null/invalid + EXPECT_FALSE(aString.IsLess(nullptr, 5)); +} + +TEST(TCollection_ExtendedStringTest, IsLess_NegativeLength) +{ + TCollection_ExtendedString aString("Hello"); + const char16_t* aOther = u"Hello"; + EXPECT_FALSE(aString.IsLess(aOther, -1)); +} + +TEST(TCollection_ExtendedStringTest, IsGreater_NullPointer) +{ + TCollection_ExtendedString aString("Hello"); + // Non-empty string is greater than null/invalid + EXPECT_TRUE(aString.IsGreater(nullptr, 5)); +} + +TEST(TCollection_ExtendedStringTest, IsGreater_NegativeLength) +{ + TCollection_ExtendedString aString("Hello"); + const char16_t* aOther = u"Hello"; + EXPECT_TRUE(aString.IsGreater(aOther, -1)); +} + +TEST(TCollection_ExtendedStringTest, StartsWith_NullPointer) +{ + TCollection_ExtendedString aString("Hello"); + EXPECT_FALSE(aString.StartsWith(nullptr, 3)); +} + +TEST(TCollection_ExtendedStringTest, StartsWith_NegativeLength) +{ + TCollection_ExtendedString aString("Hello"); + const char16_t* aPrefix = u"Hel"; + EXPECT_FALSE(aString.StartsWith(aPrefix, -1)); +} + +TEST(TCollection_ExtendedStringTest, StartsWith_ZeroLength) +{ + TCollection_ExtendedString aString("Hello"); + // Any string starts with empty string + EXPECT_TRUE(aString.StartsWith(nullptr, 0)); +} + +TEST(TCollection_ExtendedStringTest, EndsWith_NullPointer) +{ + TCollection_ExtendedString aString("Hello"); + EXPECT_FALSE(aString.EndsWith(nullptr, 3)); +} + +TEST(TCollection_ExtendedStringTest, EndsWith_NegativeLength) +{ + TCollection_ExtendedString aString("Hello"); + const char16_t* aSuffix = u"llo"; + EXPECT_FALSE(aString.EndsWith(aSuffix, -1)); +} + +TEST(TCollection_ExtendedStringTest, EndsWith_ZeroLength) +{ + TCollection_ExtendedString aString("Hello"); + // Any string ends with empty string + EXPECT_TRUE(aString.EndsWith(nullptr, 0)); +} diff --git a/src/FoundationClasses/TKernel/TCollection/TCollection_ExtendedString.cxx b/src/FoundationClasses/TKernel/TCollection/TCollection_ExtendedString.cxx index 76d68ba697..cd523f8acb 100644 --- a/src/FoundationClasses/TKernel/TCollection/TCollection_ExtendedString.cxx +++ b/src/FoundationClasses/TKernel/TCollection/TCollection_ExtendedString.cxx @@ -21,10 +21,11 @@ #include #include +#include namespace { -static char16_t THE_DEFAULT_EXT_CHAR_STRING[1] = {0}; +static char16_t THE_DEFAULT_EXT_CHAR_STRING[1] = {u'\0'}; //! Calculate padded allocation size for ExtendedString (2-byte characters) //! Guarantees at least +1 character space for null terminator, aligned to 4-byte boundary @@ -56,7 +57,7 @@ inline char16_t* Standard_UNUSED fromWideString(const wchar_t* theUtfString, int return THE_DEFAULT_EXT_CHAR_STRING; } const size_t aRoundSize = calculatePaddedSize(theLength); - char16_t* aString = static_cast(Standard::AllocateOptimal(aRoundSize)); + char16_t* aString = static_cast(Standard::AllocateOptimal(aRoundSize)); NCollection_UtfIterator anIterRead(theUtfString); for (char16_t* anIterWrite = aString; *anIterRead != 0; ++anIterRead) { @@ -79,36 +80,54 @@ inline char16_t* Standard_UNUSED fromWideString(const wchar_t* return THE_DEFAULT_EXT_CHAR_STRING; } const size_t aRoundSize = calculatePaddedSize(theLength); - char16_t* aString = static_cast(Standard::AllocateOptimal(aRoundSize)); - const int aSize = theLength * sizeof(char16_t); + char16_t* aString = static_cast(Standard::AllocateOptimal(aRoundSize)); + const int aSize = theLength * sizeof(char16_t); memcpy(aString, theUtfString, aSize); aString[theLength] = 0; return aString; } +//! Helper structure to hold formatted integer string with its length +struct FormattedInteger +{ + char Buffer[16]; // Enough for 32-bit int + sign + null terminator + int Length; + + FormattedInteger(const int theValue) { Length = Sprintf(Buffer, "%d", theValue); } +}; + +//! Helper structure to hold formatted real number string with its length +struct FormattedReal +{ + char Buffer[64]; // Enough for double in %g format + null terminator + int Length; + + FormattedReal(const double theValue) { Length = Sprintf(Buffer, "%g", theValue); } +}; + } // namespace -//================================================================================================= +//================================================================================================== TCollection_ExtendedString::TCollection_ExtendedString() noexcept { allocate(0); } -//================================================================================================= +//================================================================================================== TCollection_ExtendedString::TCollection_ExtendedString(const char* theString, - const bool isMultiByte) + const bool theIsMultiByte) { if (theString == nullptr) { throw Standard_NullObject("TCollection_ExtendedString : null parameter "); } - if (isMultiByte) + if (theIsMultiByte) { allocate(nbSymbols(theString)); - mystring[mylength] = 0; + myString[myLength] = 0; if (ConvertToUnicode(theString)) { return; @@ -119,13 +138,13 @@ TCollection_ExtendedString::TCollection_ExtendedString(const char* theString, { allocate((int)strlen(theString)); } - for (int aCharIter = 0; aCharIter < mylength; ++aCharIter) + for (int aCharIter = 0; aCharIter < myLength; ++aCharIter) { - mystring[aCharIter] = ToExtCharacter(theString[aCharIter]); + myString[aCharIter] = ToExtCharacter(theString[aCharIter]); } } -//================================================================================================= +//================================================================================================== TCollection_ExtendedString::TCollection_ExtendedString(const char16_t* theString) { @@ -134,14 +153,35 @@ TCollection_ExtendedString::TCollection_ExtendedString(const char16_t* theString throw Standard_NullObject("TCollection_ExtendedString : null parameter "); } - for (mylength = 0; theString[mylength] != 0; ++mylength) + for (myLength = 0; theString[myLength] != 0; ++myLength) { } - allocate(mylength); - memcpy(mystring, theString, mylength * sizeof(char16_t)); + allocate(myLength); + memcpy(myString, theString, myLength * sizeof(char16_t)); } -//================================================================================================= +//================================================================================================== + +TCollection_ExtendedString::TCollection_ExtendedString(const char16_t* theString, + const int theLength) +{ + if (theString == nullptr) + { + throw Standard_NullObject("TCollection_ExtendedString : null parameter "); + } + if (theLength < 0) + { + throw Standard_OutOfRange("TCollection_ExtendedString : negative length"); + } + + allocate(theLength); + if (theLength > 0) + { + memcpy(myString, theString, theLength * sizeof(char16_t)); + } +} + +//================================================================================================== TCollection_ExtendedString::TCollection_ExtendedString(const wchar_t* theStringUtf) { @@ -150,17 +190,17 @@ TCollection_ExtendedString::TCollection_ExtendedString(const wchar_t* theStringU throw Standard_NullObject("TCollection_ExtendedString : null parameter "); } - mystring = fromWideString(theStringUtf, mylength); + myString = fromWideString(theStringUtf, myLength); } -//================================================================================================= +//================================================================================================== -TCollection_ExtendedString::TCollection_ExtendedString(const char aChar) +TCollection_ExtendedString::TCollection_ExtendedString(const char theChar) { - if (aChar != '\0') + if (theChar != '\0') { allocate(1); - mystring[0] = ToExtCharacter(aChar); + myString[0] = ToExtCharacter(theChar); } else { @@ -168,173 +208,221 @@ TCollection_ExtendedString::TCollection_ExtendedString(const char aChar) } } -//================================================================================================= +//================================================================================================== -TCollection_ExtendedString::TCollection_ExtendedString(const char16_t aChar) +TCollection_ExtendedString::TCollection_ExtendedString(const char16_t theChar) { - allocate(1); - mystring[0] = aChar; + if (theChar != u'\0') + { + allocate(1); + myString[0] = theChar; + } + else + { + allocate(0); + } } -//================================================================================================= +//================================================================================================== -TCollection_ExtendedString::TCollection_ExtendedString(const int length, const char16_t filler) +TCollection_ExtendedString::TCollection_ExtendedString(const int theLength, + const char16_t theFiller) { - allocate(length); - for (int i = 0; i < length; i++) - mystring[i] = filler; + if (theLength < 0) + { + throw Standard_OutOfRange("TCollection_ExtendedString : negative length"); + } + allocate(theLength); + for (int i = 0; i < theLength; i++) + { + myString[i] = theFiller; + } } -//================================================================================================= +//================================================================================================== -TCollection_ExtendedString::TCollection_ExtendedString(const int aValue) +TCollection_ExtendedString::TCollection_ExtendedString(const int theValue) { - union { - int bid; - char t[13]; - } CHN{}; - - Sprintf(&CHN.t[0], "%d", aValue); - allocate((int)strlen(CHN.t)); - for (int i = 0; i < mylength; i++) - mystring[i] = ToExtCharacter(CHN.t[i]); + const FormattedInteger aFormatted(theValue); + allocate(aFormatted.Length); + for (int i = 0; i < myLength; i++) + { + myString[i] = ToExtCharacter(aFormatted.Buffer[i]); + } } -//================================================================================================= +//================================================================================================== -TCollection_ExtendedString::TCollection_ExtendedString(const double aValue) +TCollection_ExtendedString::TCollection_ExtendedString(const double theValue) { - union { - int bid; - char t[50]; - } CHN{}; - - Sprintf(&CHN.t[0], "%g", aValue); - allocate((int)strlen(CHN.t)); - for (int i = 0; i < mylength; i++) - mystring[i] = ToExtCharacter(CHN.t[i]); + const FormattedReal aFormatted(theValue); + allocate(aFormatted.Length); + for (int i = 0; i < myLength; i++) + { + myString[i] = ToExtCharacter(aFormatted.Buffer[i]); + } } -//================================================================================================= +//================================================================================================== -TCollection_ExtendedString::TCollection_ExtendedString(const TCollection_ExtendedString& theOther) +TCollection_ExtendedString::TCollection_ExtendedString(const TCollection_ExtendedString& theString) { - allocate(theOther.mylength); - memcpy(mystring, theOther.mystring, mylength * sizeof(char16_t)); + allocate(theString.myLength); + memcpy(myString, theString.myString, myLength * sizeof(char16_t)); } -//================================================================================================= +//================================================================================================== TCollection_ExtendedString::TCollection_ExtendedString( TCollection_ExtendedString&& theOther) noexcept { - if (theOther.mystring == THE_DEFAULT_EXT_CHAR_STRING) + if (theOther.myString == THE_DEFAULT_EXT_CHAR_STRING) { allocate(0); } else { - mystring = theOther.mystring; - mylength = theOther.mylength; + myString = theOther.myString; + myLength = theOther.myLength; } - theOther.mystring = THE_DEFAULT_EXT_CHAR_STRING; - theOther.mylength = 0; + theOther.myString = THE_DEFAULT_EXT_CHAR_STRING; + theOther.myLength = 0; } -//================================================================================================= +//================================================================================================== TCollection_ExtendedString::TCollection_ExtendedString(const TCollection_AsciiString& theString, - const bool isMultiByte) + const bool theIsMultiByte) { allocate(nbSymbols(theString.ToCString())); - if (isMultiByte && ConvertToUnicode(theString.ToCString())) + if (theIsMultiByte && ConvertToUnicode(theString.ToCString())) { return; } reallocate(theString.Length()); const char* aCString = theString.ToCString(); - for (int aCharIter = 0; aCharIter <= mylength; ++aCharIter) + for (int aCharIter = 0; aCharIter <= myLength; ++aCharIter) { - mystring[aCharIter] = ToExtCharacter(aCString[aCharIter]); + myString[aCharIter] = ToExtCharacter(aCString[aCharIter]); } } -//================================================================================================= +//================================================================================================== void TCollection_ExtendedString::AssignCat(const TCollection_ExtendedString& theOther) { - if (theOther.mylength == 0) + if (theOther.myLength == 0) { return; } - const int anOtherLength = theOther.mylength; - const int anOldLength = mylength; - reallocate(mylength + anOtherLength); - memcpy(mystring + anOldLength, theOther.mystring, anOtherLength * sizeof(char16_t)); + // Handle self-append case: theOther.myString will be invalidated by reallocate + if (&theOther == this) + { + const int anOldLength = myLength; + reallocate(myLength + myLength); + memcpy(myString + anOldLength, myString, anOldLength * sizeof(char16_t)); + return; + } + + const int anOtherLength = theOther.myLength; + const int anOldLength = myLength; + reallocate(myLength + anOtherLength); + memcpy(myString + anOldLength, theOther.myString, anOtherLength * sizeof(char16_t)); } -//================================================================================================= +//================================================================================================== void TCollection_ExtendedString::AssignCat(const char16_t theChar) { - if (theChar != '\0') + if (theChar != u'\0') { - reallocate(mylength + 1); - mystring[mylength - 1] = theChar; + reallocate(myLength + 1); + myString[myLength - 1] = theChar; } } -//================================================================================================= +//================================================================================================== -TCollection_ExtendedString TCollection_ExtendedString::Cat( - const TCollection_ExtendedString& other) const +void TCollection_ExtendedString::AssignCat(const char16_t* theString, const int theLength) { - TCollection_ExtendedString res(mylength + other.mylength, 0); - if (mylength > 0) - memcpy(res.mystring, mystring, mylength * sizeof(char16_t)); - if (other.mylength > 0) - memcpy(res.mystring + mylength, other.mystring, other.mylength * sizeof(char16_t)); - return res; + if (theString == nullptr || theLength <= 0) + { + return; + } + + // Check if theString points into current buffer (aliasing case) + const bool isAliased = (theString >= myString && theString < myString + myLength); + + if (isAliased) + { + // Save offset before reallocate invalidates the pointer + const std::ptrdiff_t anOffset = theString - myString; + const int anOldLength = myLength; + reallocate(myLength + theLength); + // Use memmove since source and destination may overlap + memmove(myString + anOldLength, myString + anOffset, theLength * sizeof(char16_t)); + } + else + { + const int anOldLength = myLength; + reallocate(myLength + theLength); + memcpy(myString + anOldLength, theString, theLength * sizeof(char16_t)); + } } -//================================================================================================= +//================================================================================================== -void TCollection_ExtendedString::ChangeAll(const char16_t aChar, const char16_t NewChar) +TCollection_ExtendedString TCollection_ExtendedString::Cat(const char16_t* theOther, + const int theLength) const { - for (int i = 0; i < mylength; i++) - if (mystring[i] == aChar) - mystring[i] = NewChar; + TCollection_ExtendedString aResult(myLength + theLength, 0); + if (myLength > 0) + { + memcpy(aResult.myString, myString, myLength * sizeof(char16_t)); + } + if (theLength > 0) + { + memcpy(aResult.myString + myLength, theOther, theLength * sizeof(char16_t)); + } + return aResult; } -//================================================================================================= +//================================================================================================== + +void TCollection_ExtendedString::ChangeAll(const char16_t theChar, const char16_t theNewChar) +{ + for (int i = 0; i < myLength; i++) + if (myString[i] == theChar) + myString[i] = theNewChar; +} + +//================================================================================================== void TCollection_ExtendedString::Clear() { deallocate(); } -//================================================================================================= +//================================================================================================== -void TCollection_ExtendedString::Copy(const TCollection_ExtendedString& fromwhere) +void TCollection_ExtendedString::Copy(const char16_t* theString, const int theLength) { - if (&fromwhere == this) + if (theString == nullptr || theLength <= 0) { + if (myString != THE_DEFAULT_EXT_CHAR_STRING) + { + myLength = 0; + myString[0] = 0; + } return; } - if (fromwhere.mystring && fromwhere.mylength > 0) - { - reallocate(fromwhere.mylength); - memcpy(mystring, fromwhere.mystring, mylength * sizeof(char16_t)); - } - else if (mystring != THE_DEFAULT_EXT_CHAR_STRING) - { - mylength = 0; - mystring[mylength] = 0; - } + + reallocate(theLength); + memcpy(myString, theString, theLength * sizeof(char16_t)); } -//================================================================================================= +//================================================================================================== void TCollection_ExtendedString::Move(TCollection_ExtendedString&& theOther) { @@ -342,24 +430,24 @@ void TCollection_ExtendedString::Move(TCollection_ExtendedString&& theOther) { return; } - if (theOther.mystring == THE_DEFAULT_EXT_CHAR_STRING) + if (theOther.myString == THE_DEFAULT_EXT_CHAR_STRING) { reallocate(0); } else { - if (mystring != THE_DEFAULT_EXT_CHAR_STRING) + if (myString != THE_DEFAULT_EXT_CHAR_STRING) { - Standard::Free(mystring); + Standard::Free(myString); } - mystring = theOther.mystring; - mylength = theOther.mylength; + myString = theOther.myString; + myLength = theOther.myLength; } - theOther.mystring = THE_DEFAULT_EXT_CHAR_STRING; - theOther.mylength = 0; + theOther.myString = THE_DEFAULT_EXT_CHAR_STRING; + theOther.myLength = 0; } -//================================================================================================= +//================================================================================================== void TCollection_ExtendedString::Swap(TCollection_ExtendedString& theOther) { @@ -367,357 +455,462 @@ void TCollection_ExtendedString::Swap(TCollection_ExtendedString& theOther) { return; } - std::swap(mystring, theOther.mystring); - std::swap(mylength, theOther.mylength); + std::swap(myString, theOther.myString); + std::swap(myLength, theOther.myLength); } -//================================================================================================= +//================================================================================================== TCollection_ExtendedString::~TCollection_ExtendedString() { deallocate(); } -//================================================================================================= +//================================================================================================== -void TCollection_ExtendedString::Insert(const int where, const char16_t what) +void TCollection_ExtendedString::Insert(const int theWhere, const char16_t theWhat) { - if (where > mylength + 1) + if (theWhere > myLength + 1) throw Standard_OutOfRange("TCollection_ExtendedString::Insert : " - "Parameter where is too big"); - if (where < 0) + "Parameter theWhere is too big"); + if (theWhere < 1) throw Standard_OutOfRange("TCollection_ExtendedString::Insert : " - "Parameter where is negative"); - reallocate(mylength + 1); - if (where != mylength) + "Parameter theWhere is out of range"); + reallocate(myLength + 1); + if (theWhere != myLength) { - for (int i = mylength - 2; i >= where - 1; i--) - mystring[i + 1] = mystring[i]; + for (int i = myLength - 2; i >= theWhere - 1; i--) + myString[i + 1] = myString[i]; } - mystring[where - 1] = what; + myString[theWhere - 1] = theWhat; } -//================================================================================================= +//================================================================================================== -void TCollection_ExtendedString::Insert(const int where, const TCollection_ExtendedString& what) +void TCollection_ExtendedString::Insert(const int theWhere, + const char16_t* theWhat, + const int theLength) { - const char16_t* swhat = what.mystring; - if (where <= mylength + 1) + if (theWhere > myLength + 1) { - int whatlength = what.mylength; - if (whatlength) + throw Standard_OutOfRange("TCollection_ExtendedString::Insert : " + "Parameter theWhere is too big"); + } + if (theWhere < 1) + { + throw Standard_OutOfRange("TCollection_ExtendedString::Insert : " + "Parameter theWhere is out of range"); + } + if (theWhat == nullptr || theLength <= 0) + { + return; + } + + const int anOldLength = myLength; + reallocate(myLength + theLength); + if (theWhere != anOldLength + 1) + { + for (int i = anOldLength - 1; i >= theWhere - 1; i--) { - const int anOldLength = mylength; - reallocate(mylength + whatlength); - if (where != anOldLength + 1) - { - for (int i = anOldLength - 1; i >= where - 1; i--) - mystring[i + whatlength] = mystring[i]; - } - for (int i = 0; i < whatlength; i++) - mystring[where - 1 + i] = swhat[i]; + myString[i + theLength] = myString[i]; } } - else + for (int i = 0; i < theLength; i++) { - throw Standard_OutOfRange("TCollection_ExtendedString::Insert : " - "Parameter where is too big"); + myString[theWhere - 1 + i] = theWhat[i]; } } -//================================================================================================= +//================================================================================================== -static int ExtStrCmp(const char16_t* theStr1, const char16_t* theStr2) +bool TCollection_ExtendedString::IsEqual(const char16_t* theOther, const int theLength) const { - const char16_t* aStr1 = theStr1; - const char16_t* aStr2 = theStr2; - while (*aStr1 != 0 && *aStr1 == *aStr2) + if (theLength < 0) { - aStr1++; - aStr2++; + return false; } - // char16_t is char16_t which is 2-byte unsigned int, it will be promoted - // to plain 4-byte int for calculation - return *aStr1 - *aStr2; -} - -//================================================================================================= - -bool TCollection_ExtendedString::IsEqual(const char16_t* other) const -{ - return ExtStrCmp(mystring, other) == 0; -} - -//================================================================================================= - -bool TCollection_ExtendedString::IsEqual(const TCollection_ExtendedString& other) const -{ - return mylength == other.mylength - && memcmp(mystring, other.mystring, (mylength + 1) * sizeof(char16_t)) == 0; -} - -//================================================================================================= - -bool TCollection_ExtendedString::IsDifferent(const char16_t* other) const -{ - return ExtStrCmp(mystring, other) != 0; -} - -//================================================================================================= - -bool TCollection_ExtendedString::IsDifferent(const TCollection_ExtendedString& other) const -{ - return mylength != other.mylength - || memcmp(mystring, other.mystring, (mylength + 1) * sizeof(char16_t)) != 0; -} - -//================================================================================================= - -bool TCollection_ExtendedString::IsLess(const char16_t* other) const -{ - return ExtStrCmp(mystring, other) < 0; -} - -//================================================================================================= - -bool TCollection_ExtendedString::IsLess(const TCollection_ExtendedString& other) const -{ - return ExtStrCmp(mystring, other.mystring) < 0; -} - -//================================================================================================= - -bool TCollection_ExtendedString::IsGreater(const char16_t* other) const -{ - return ExtStrCmp(mystring, other) > 0; -} - -//================================================================================================= - -bool TCollection_ExtendedString::IsGreater(const TCollection_ExtendedString& other) const -{ - return ExtStrCmp(mystring, other.mystring) > 0; -} - -//================================================================================================= - -bool TCollection_ExtendedString::StartsWith(const TCollection_ExtendedString& theStartString) const -{ - if (this == &theStartString) + if (myLength != theLength) + { + return false; + } + if (theLength == 0) { return true; } - - return mylength >= theStartString.mylength - && memcmp(theStartString.mystring, mystring, theStartString.mylength * sizeof(char16_t)) - == 0; + if (theOther == nullptr) + { + return false; + } + return memcmp(myString, theOther, theLength * sizeof(char16_t)) == 0; } -//================================================================================================= +//================================================================================================== -bool TCollection_ExtendedString::EndsWith(const TCollection_ExtendedString& theEndString) const +bool TCollection_ExtendedString::IsDifferent(const char16_t* theOther, const int theLength) const { - if (this == &theEndString) + if (theLength < 0) { return true; } - - return mylength >= theEndString.mylength - && memcmp(theEndString.mystring, - mystring + mylength - theEndString.mylength, - theEndString.mylength * sizeof(char16_t)) - == 0; + if (myLength != theLength) + { + return true; + } + if (theLength == 0) + { + return false; + } + if (theOther == nullptr) + { + return true; + } + return memcmp(myString, theOther, theLength * sizeof(char16_t)) != 0; } -//================================================================================================= +//================================================================================================== + +bool TCollection_ExtendedString::IsLess(const char16_t* theOther, const int theLength) const +{ + if (theLength < 0) + { + return false; // this >= invalid + } + if (theOther == nullptr) + { + return theLength > 0 ? false : myLength < 0; // this >= empty/invalid + } + const int aMinLen = (myLength < theLength) ? myLength : theLength; + for (int i = 0; i < aMinLen; ++i) + { + if (myString[i] < theOther[i]) + { + return true; + } + if (myString[i] > theOther[i]) + { + return false; + } + } + return myLength < theLength; +} + +//================================================================================================== + +bool TCollection_ExtendedString::IsGreater(const char16_t* theOther, const int theLength) const +{ + if (theLength < 0) + { + return true; // this > invalid + } + if (theOther == nullptr) + { + return theLength == 0 ? myLength > 0 : true; // this > empty/invalid + } + const int aMinLen = (myLength < theLength) ? myLength : theLength; + for (int i = 0; i < aMinLen; ++i) + { + if (myString[i] > theOther[i]) + { + return true; + } + if (myString[i] < theOther[i]) + { + return false; + } + } + return myLength > theLength; +} + +//================================================================================================== + +bool TCollection_ExtendedString::StartsWith(const char16_t* theStartString, + const int theLength) const +{ + if (theLength < 0) + { + return false; + } + if (theLength == 0) + { + return true; + } + if (theStartString == nullptr) + { + return false; + } + if (myLength < theLength) + { + return false; + } + return memcmp(myString, theStartString, theLength * sizeof(char16_t)) == 0; +} + +//================================================================================================== + +bool TCollection_ExtendedString::EndsWith(const char16_t* theEndString, const int theLength) const +{ + if (theLength < 0) + { + return false; + } + if (theLength == 0) + { + return true; + } + if (theEndString == nullptr) + { + return false; + } + if (myLength < theLength) + { + return false; + } + return memcmp(myString + myLength - theLength, theEndString, theLength * sizeof(char16_t)) == 0; +} + +//================================================================================================== bool TCollection_ExtendedString::IsAscii() const { - for (int i = 0; i < mylength; i++) - if (!IsAnAscii(mystring[i])) + for (int i = 0; i < myLength; i++) + if (!IsAnAscii(myString[i])) return false; return true; } -//================================================================================================= +//================================================================================================== int TCollection_ExtendedString::Length() const { - return mylength; + return myLength; } -//================================================================================================= +//================================================================================================== void TCollection_ExtendedString::Print(Standard_OStream& theStream) const { - if (mylength > 0) + if (myLength > 0) { - const TCollection_AsciiString aUtf8(mystring); + const TCollection_AsciiString aUtf8(myString); theStream << aUtf8; } } -// ---------------------------------------------------------------------------- -Standard_OStream& operator<<(Standard_OStream& astream, const TCollection_ExtendedString& astring) +//================================================================================================== + +Standard_OStream& operator<<(Standard_OStream& theStream, + const TCollection_ExtendedString& theString) { - astring.Print(astream); - return astream; + theString.Print(theStream); + return theStream; } -//================================================================================================= +//================================================================================================== -void TCollection_ExtendedString::RemoveAll(const char16_t what) +void TCollection_ExtendedString::RemoveAll(const char16_t theWhat) { - if (mylength == 0) + if (myLength == 0) return; int c = 0; - for (int i = 0; i < mylength; i++) - if (mystring[i] != what) - mystring[c++] = mystring[i]; - mylength = c; - mystring[mylength] = '\0'; + for (int i = 0; i < myLength; i++) + if (myString[i] != theWhat) + myString[c++] = myString[i]; + myLength = c; + myString[myLength] = u'\0'; } -//================================================================================================= +//================================================================================================== -void TCollection_ExtendedString::Remove(const int where, const int ahowmany) +void TCollection_ExtendedString::Remove(const int theWhere, const int theHowMany) { - if (where + ahowmany <= mylength + 1) + if (theWhere < 1 || theWhere > myLength || theHowMany < 0 || theWhere + theHowMany > myLength + 1) { - int i, j; - for (i = where + ahowmany - 1, j = where - 1; i < mylength; i++, j++) - mystring[j] = mystring[i]; - mylength -= ahowmany; - mystring[mylength] = '\0'; - } - else throw Standard_OutOfRange("TCollection_ExtendedString::Remove: " - "Too many characters to erase or " - "invalid starting value."); + "Invalid parameters"); + } + + if (theHowMany == 0) + { + return; + } + + for (int i = theWhere + theHowMany - 1, j = theWhere - 1; i < myLength; i++, j++) + { + myString[j] = myString[i]; + } + myLength -= theHowMany; + myString[myLength] = u'\0'; } -//================================================================================================= +//================================================================================================== -int TCollection_ExtendedString::Search(const TCollection_ExtendedString& what) const +int TCollection_ExtendedString::Search(const char16_t* theWhat, const int theLength) const { - int size = what.mylength; - const char16_t* swhat = what.mystring; - if (size) + if (theWhat == nullptr || theLength <= 0) { - int k, j; - int i = 0; - bool find = false; - while (i < mylength - size + 1 && !find) + return -1; + } + if (theLength > myLength) + { + return -1; + } + + int k, j; + int i = 0; + bool find = false; + while (i < myLength - theLength + 1 && !find) + { + k = i++; + j = 0; + while (j < theLength && myString[k++] == theWhat[j++]) { - k = i++; - j = 0; - while (j < size && mystring[k++] == swhat[j++]) - if (j == size) - find = true; + if (j == theLength) + { + find = true; + } } - if (find) - return i; + } + if (find) + { + return i; } return -1; } -//================================================================================================= +//================================================================================================== -int TCollection_ExtendedString::SearchFromEnd(const TCollection_ExtendedString& what) const +int TCollection_ExtendedString::SearchFromEnd(const char16_t* theWhat, const int theLength) const { - int size = what.mylength; - if (size) + if (theWhat == nullptr || theLength <= 0) { - const char16_t* swhat = what.mystring; - int k, j; - int i = mylength - 1; - bool find = false; - while (i >= size - 1 && !find) + return -1; + } + if (theLength > myLength) + { + return -1; + } + + int k, j; + int i = myLength - 1; + bool find = false; + while (i >= theLength - 1 && !find) + { + k = i--; + j = theLength - 1; + while (j >= 0 && myString[k--] == theWhat[j--]) { - k = i--; - j = size - 1; - while (j >= 0 && mystring[k--] == swhat[j--]) - if (j == -1) - find = true; + if (j == -1) + { + find = true; + } } - if (find) - return i - size + 3; + } + if (find) + { + return i - theLength + 3; } return -1; } -//================================================================================================= +//================================================================================================== -void TCollection_ExtendedString::SetValue(const int where, const char16_t what) +void TCollection_ExtendedString::SetValue(const int theWhere, const char16_t theWhat) { - if (where > 0 && where <= mylength) + if (theWhere > 0 && theWhere <= myLength) { - mystring[where - 1] = what; + myString[theWhere - 1] = theWhat; } else { - throw Standard_OutOfRange("TCollection_ExtendedString::SetValue : parameter where"); + throw Standard_OutOfRange("TCollection_ExtendedString::SetValue : parameter theWhere"); } } -//================================================================================================= +//================================================================================================== -void TCollection_ExtendedString::SetValue(const int where, const TCollection_ExtendedString& what) +void TCollection_ExtendedString::SetValue(const int theWhere, + const char16_t* theWhat, + const int theLength) { - if (where > 0 && where <= mylength + 1) + if (theWhere < 1 || theWhere > myLength + 1) { - int size = what.mylength; - const char16_t* swhat = what.mystring; - size += (where - 1); - if (size >= mylength) - { - reallocate(size); - } - for (int i = where - 1; i < size; i++) - mystring[i] = swhat[i - (where - 1)]; + throw Standard_OutOfRange("TCollection_ExtendedString::SetValue : parameter theWhere"); + } + if (theWhat == nullptr || theLength <= 0) + { + return; + } + + const int aNewEnd = theWhere - 1 + theLength; + if (aNewEnd > myLength) + { + reallocate(aNewEnd); + } + for (int i = 0; i < theLength; i++) + { + myString[theWhere - 1 + i] = theWhat[i]; } - else - throw Standard_OutOfRange("TCollection_ExtendedString::SetValue : " - "parameter where"); } -//================================================================================================= +//================================================================================================== -TCollection_ExtendedString TCollection_ExtendedString::Split(const int where) +TCollection_ExtendedString TCollection_ExtendedString::SubString(const int theFromIndex, + const int theToIndex) const { - if (where >= 0 && where < mylength) + if (theFromIndex < 1 || theFromIndex > myLength) { - TCollection_ExtendedString res(&mystring[where]); - Trunc(where); + throw Standard_OutOfRange("TCollection_ExtendedString::SubString : theFromIndex"); + } + if (theToIndex < 1 || theToIndex > myLength) + { + throw Standard_OutOfRange("TCollection_ExtendedString::SubString : theToIndex"); + } + if (theFromIndex > theToIndex) + { + throw Standard_OutOfRange("TCollection_ExtendedString::SubString : theFromIndex > theToIndex"); + } + + const int aSubLen = theToIndex - theFromIndex + 1; + return TCollection_ExtendedString(myString + theFromIndex - 1, aSubLen); +} + +//================================================================================================== + +TCollection_ExtendedString TCollection_ExtendedString::Split(const int theWhere) +{ + if (theWhere >= 0 && theWhere < myLength) + { + TCollection_ExtendedString res(&myString[theWhere]); + Trunc(theWhere); return res; } throw Standard_OutOfRange("TCollection_ExtendedString::Split index"); } -//================================================================================================= +//================================================================================================== -TCollection_ExtendedString TCollection_ExtendedString::Token(const char16_t* separators, - const int whichone) const +TCollection_ExtendedString TCollection_ExtendedString::Token(const char16_t* theSeparators, + const int theWhichOne) const { - if (mylength == 0) + if (myLength == 0) { return TCollection_ExtendedString(); } - TCollection_ExtendedString res(mylength, 0); - if (!separators) + TCollection_ExtendedString res(myLength, 0); + if (!theSeparators) throw Standard_NullObject("TCollection_ExtendedString::Token : " - "parameter 'separators'"); + "parameter 'theSeparators'"); - int i, j, k, l; - Standard_PExtCharacter buftmp = res.mystring; - char16_t aSep; + int i, j, k, l; + char16_t* buftmp = res.myString; + char16_t aSep; bool isSepFound = false, otherSepFound; j = 0; - for (i = 0; i < whichone && j < mylength; i++) + for (i = 0; i < theWhichOne && j < myLength; i++) { isSepFound = false; k = 0; @@ -726,109 +919,109 @@ TCollection_ExtendedString TCollection_ExtendedString::Token(const char16_t* sep // Avant de commencer il faut virer les saloperies devant // otherSepFound = true; - while (j < mylength && otherSepFound) + while (j < myLength && otherSepFound) { l = 0; otherSepFound = false; - aSep = separators[l]; + aSep = theSeparators[l]; while (aSep != 0) { - if (aSep == mystring[j]) + if (aSep == myString[j]) { aSep = 0; otherSepFound = true; } else { - aSep = separators[l++]; + aSep = theSeparators[l++]; } } if (otherSepFound) j++; } - while (!isSepFound && k < mylength && j < mylength) + while (!isSepFound && k < myLength && j < myLength) { l = 0; - aSep = separators[l]; + aSep = theSeparators[l]; while (aSep != 0 && !isSepFound) { - if (aSep == mystring[j]) + if (aSep == myString[j]) { buftmp[k] = 0; isSepFound = true; } else { - buftmp[k] = mystring[j]; + buftmp[k] = myString[j]; } l++; - aSep = separators[l]; + aSep = theSeparators[l]; } j++; k++; - if (j == mylength) + if (j == myLength) buftmp[k] = 0; } } - if (i < whichone) + if (i < theWhichOne) { - res.mylength = 0; - res.mystring[0] = 0; + res.myLength = 0; + res.myString[0] = 0; } else { - for (res.mylength = 0; buftmp[res.mylength]; ++res.mylength) + for (res.myLength = 0; buftmp[res.myLength]; ++res.myLength) ; - res.mystring[res.mylength] = '\0'; + res.myString[res.myLength] = u'\0'; } return res; } -//================================================================================================= +//================================================================================================== const char16_t* TCollection_ExtendedString::ToExtString() const { - return mystring; + return myString; } -//================================================================================================= +//================================================================================================== -void TCollection_ExtendedString::Trunc(const int ahowmany) +void TCollection_ExtendedString::Trunc(const int theHowMany) { - if (ahowmany < 0 || ahowmany > mylength) + if (theHowMany < 0 || theHowMany > myLength) throw Standard_OutOfRange("TCollection_ExtendedString::Trunc : " - "parameter 'ahowmany'"); - mylength = ahowmany; - mystring[mylength] = '\0'; + "parameter 'theHowMany'"); + myLength = theHowMany; + myString[myLength] = u'\0'; } -//================================================================================================= +//================================================================================================== -char16_t TCollection_ExtendedString::Value(const int where) const +char16_t TCollection_ExtendedString::Value(const int theWhere) const { - if (where > 0 && where <= mylength) + if (theWhere > 0 && theWhere <= myLength) { - if (mystring) - return mystring[where - 1]; + if (myString) + return myString[theWhere - 1]; else return 0; } throw Standard_OutOfRange("TCollection_ExtendedString::Value : " - "parameter where"); + "parameter theWhere"); } -//================================================================================================= +//================================================================================================== -bool TCollection_ExtendedString::ConvertToUnicode(const char* theStringUtf) +bool TCollection_ExtendedString::ConvertToUnicode(const char* theString) { - NCollection_UtfIterator anIterRead(theStringUtf); - char16_t* anIterWrite = mystring; + NCollection_UtfIterator anIterRead(theString); + char16_t* anIterWrite = myString; if (*anIterRead == 0) { - *anIterWrite = '\0'; + *anIterWrite = u'\0'; return true; } @@ -844,25 +1037,23 @@ bool TCollection_ExtendedString::ConvertToUnicode(const char* theStringUtf) return true; } -//================================================================================================= +//================================================================================================== int TCollection_ExtendedString::LengthOfCString() const { int aSizeBytes = 0; - for (NCollection_UtfIterator anIter(mystring); *anIter != 0; ++anIter) + for (NCollection_UtfIterator anIter(myString); *anIter != 0; ++anIter) { aSizeBytes += anIter.AdvanceBytesUtf8(); } return aSizeBytes; } -//---------------------------------------------------------------------------- -// Converts the internal to UTF8 coding and returns length of the -// out CString. -//---------------------------------------------------------------------------- +//================================================================================================== + int TCollection_ExtendedString::ToUTF8CString(Standard_PCharacter& theCString) const { - NCollection_UtfIterator anIterRead(mystring); + NCollection_UtfIterator anIterRead(myString); char* anIterWrite = theCString; if (*anIterRead == 0) { @@ -878,65 +1069,402 @@ int TCollection_ExtendedString::ToUTF8CString(Standard_PCharacter& theCString) c return int(anIterWrite - theCString); } -//================================================================================================= +//================================================================================================== void TCollection_ExtendedString::allocate(const int theLength) { - mylength = theLength; + myLength = theLength; if (theLength == 0) { - mystring = THE_DEFAULT_EXT_CHAR_STRING; + myString = THE_DEFAULT_EXT_CHAR_STRING; } else { const size_t aRoundSize = calculatePaddedSize(theLength); - mystring = static_cast(Standard::AllocateOptimal(aRoundSize)); - mystring[mylength] = '\0'; + myString = static_cast(Standard::AllocateOptimal(aRoundSize)); + myString[myLength] = u'\0'; } } -//================================================================================================= +//================================================================================================== void TCollection_ExtendedString::reallocate(const int theLength) { - if (theLength != 0) + if (theLength == 0) { - if (mystring == THE_DEFAULT_EXT_CHAR_STRING) + // When shrinking to empty, just set length to 0 and null-terminate + if (myString != THE_DEFAULT_EXT_CHAR_STRING) { - const size_t aRoundSize = calculatePaddedSize(theLength); - mystring = static_cast(Standard::AllocateOptimal(aRoundSize)); + myString[0] = 0; } - else - { - // For reallocate, use padded size for consistency - const size_t aRoundSize = calculatePaddedSize(theLength); - mystring = static_cast(Standard::Reallocate(mystring, aRoundSize)); - } - mystring[theLength] = 0; + myLength = 0; + return; } - if (mystring != THE_DEFAULT_EXT_CHAR_STRING) + + if (myString == THE_DEFAULT_EXT_CHAR_STRING) { - mystring[theLength] = 0; + const size_t aRoundSize = calculatePaddedSize(theLength); + myString = static_cast(Standard::AllocateOptimal(aRoundSize)); } - mylength = theLength; + else + { + const size_t aRoundSize = calculatePaddedSize(theLength); + myString = static_cast(Standard::Reallocate(myString, aRoundSize)); + } + myString[theLength] = 0; + myLength = theLength; } -//================================================================================================= +//================================================================================================== void TCollection_ExtendedString::deallocate() { - if (mystring != THE_DEFAULT_EXT_CHAR_STRING) + if (myString != THE_DEFAULT_EXT_CHAR_STRING) { - Standard::Free(mystring); + Standard::Free(myString); } - mylength = 0; - mystring = THE_DEFAULT_EXT_CHAR_STRING; + myLength = 0; + myString = THE_DEFAULT_EXT_CHAR_STRING; } -//================================================================================================= +//================================================================================================== const TCollection_ExtendedString& TCollection_ExtendedString::EmptyString() noexcept { static const TCollection_ExtendedString THE_EMPTY_STRING; return THE_EMPTY_STRING; } + +//================================================================================================== + +void TCollection_ExtendedString::LeftAdjust() +{ + if (myLength == 0) + { + return; + } + + int anIndex = 0; + while (anIndex < myLength && myString[anIndex] == u' ') + { + ++anIndex; + } + + if (anIndex > 0) + { + myLength -= anIndex; + memmove(myString, myString + anIndex, myLength * sizeof(char16_t)); + myString[myLength] = u'\0'; + } +} + +//================================================================================================== + +void TCollection_ExtendedString::RightAdjust() +{ + if (myLength == 0) + { + return; + } + + while (myLength > 0 && myString[myLength - 1] == u' ') + { + --myLength; + } + myString[myLength] = u'\0'; +} + +//================================================================================================== + +void TCollection_ExtendedString::LeftJustify(const int theWidth, const char16_t theFiller) +{ + if (theWidth < 0) + { + throw Standard_OutOfRange("TCollection_ExtendedString::LeftJustify : theWidth is negative"); + } + + if (theWidth > myLength) + { + const int anOldLength = myLength; + reallocate(theWidth); + for (int i = anOldLength; i < theWidth; ++i) + { + myString[i] = theFiller; + } + } +} + +//================================================================================================== + +void TCollection_ExtendedString::RightJustify(const int theWidth, const char16_t theFiller) +{ + if (theWidth < 0) + { + throw Standard_OutOfRange("TCollection_ExtendedString::RightJustify : theWidth is negative"); + } + + if (theWidth > myLength) + { + const int aShift = theWidth - myLength; + const int anOldLen = myLength; + reallocate(theWidth); + // Move existing characters to the end + memmove(myString + aShift, myString, anOldLen * sizeof(char16_t)); + // Fill the beginning with filler + for (int i = 0; i < aShift; ++i) + { + myString[i] = theFiller; + } + } +} + +//================================================================================================== + +void TCollection_ExtendedString::Center(const int theWidth, const char16_t theFiller) +{ + if (theWidth < 0) + { + throw Standard_OutOfRange("TCollection_ExtendedString::Center : theWidth is negative"); + } + + if (theWidth > myLength) + { + const int aLeftShift = (theWidth - myLength) / 2; + const int aRightSpace = theWidth - myLength - aLeftShift; + const int anOldLen = myLength; + reallocate(theWidth); + + // Move existing characters to the center + memmove(myString + aLeftShift, myString, anOldLen * sizeof(char16_t)); + // Fill the beginning + for (int i = 0; i < aLeftShift; ++i) + { + myString[i] = theFiller; + } + // Fill the end + for (int i = 0; i < aRightSpace; ++i) + { + myString[anOldLen + aLeftShift + i] = theFiller; + } + } +} + +//================================================================================================== + +void TCollection_ExtendedString::Capitalize() +{ + if (myLength == 0) + { + return; + } + + // Convert first character to uppercase (ASCII only) + if (myString[0] >= u'a' && myString[0] <= u'z') + { + myString[0] = static_cast(myString[0] - u'a' + u'A'); + } + + // Convert remaining characters to lowercase (ASCII only) + for (int i = 1; i < myLength; ++i) + { + if (myString[i] >= u'A' && myString[i] <= u'Z') + { + myString[i] = static_cast(myString[i] - u'A' + u'a'); + } + } +} + +//================================================================================================== + +void TCollection_ExtendedString::Prepend(const char16_t* theOther, const int theLength) +{ + if (theOther == nullptr || theLength <= 0) + { + return; + } + + // Check if theOther points into current buffer (aliasing case) + const bool isAliased = (theOther >= myString && theOther < myString + myLength); + + if (isAliased) + { + // Save offset before reallocate invalidates the pointer + const std::ptrdiff_t anOffset = theOther - myString; + const int anOldLength = myLength; + reallocate(myLength + theLength); + // Move existing characters to the end first + memmove(myString + theLength, myString, anOldLength * sizeof(char16_t)); + // Copy from the shifted position (offset + theLength because content was moved) + memmove(myString, myString + theLength + anOffset, theLength * sizeof(char16_t)); + } + else + { + const int anOldLength = myLength; + reallocate(myLength + theLength); + // Move existing characters to the end + memmove(myString + theLength, myString, anOldLength * sizeof(char16_t)); + // Copy theOther to the beginning + memcpy(myString, theOther, theLength * sizeof(char16_t)); + } +} + +//================================================================================================== + +int TCollection_ExtendedString::FirstLocationInSet(const TCollection_ExtendedString& theSet, + const int theFromIndex, + const int theToIndex) const +{ + if (theFromIndex < 1 || theFromIndex > myLength) + { + throw Standard_OutOfRange("TCollection_ExtendedString::FirstLocationInSet : theFromIndex"); + } + if (theToIndex < 1 || theToIndex > myLength) + { + throw Standard_OutOfRange("TCollection_ExtendedString::FirstLocationInSet : theToIndex"); + } + if (theSet.myLength == 0) + { + return 0; + } + + for (int i = theFromIndex - 1; i < theToIndex; ++i) + { + for (int j = 0; j < theSet.myLength; ++j) + { + if (myString[i] == theSet.myString[j]) + { + return i + 1; + } + } + } + return 0; +} + +//================================================================================================== + +int TCollection_ExtendedString::FirstLocationNotInSet(const TCollection_ExtendedString& theSet, + const int theFromIndex, + const int theToIndex) const +{ + if (theFromIndex < 1 || theFromIndex > myLength) + { + throw Standard_OutOfRange("TCollection_ExtendedString::FirstLocationNotInSet : theFromIndex"); + } + if (theToIndex < 1 || theToIndex > myLength) + { + throw Standard_OutOfRange("TCollection_ExtendedString::FirstLocationNotInSet : theToIndex"); + } + if (theSet.myLength == 0) + { + return (theFromIndex <= theToIndex) ? theFromIndex : 0; + } + + for (int i = theFromIndex - 1; i < theToIndex; ++i) + { + bool isFound = false; + for (int j = 0; j < theSet.myLength; ++j) + { + if (myString[i] == theSet.myString[j]) + { + isFound = true; + break; + } + } + if (!isFound) + { + return i + 1; + } + } + return 0; +} + +//================================================================================================== + +int TCollection_ExtendedString::IntegerValue() const +{ + if (myLength == 0) + { + return 0; + } + // Convert to ASCII and parse + TCollection_AsciiString anAscii(*this); + return anAscii.IntegerValue(); +} + +//================================================================================================== + +bool TCollection_ExtendedString::IsIntegerValue() const +{ + if (myLength == 0) + { + return false; + } + // Convert to ASCII and check + TCollection_AsciiString anAscii(*this); + return anAscii.IsIntegerValue(); +} + +//================================================================================================== + +double TCollection_ExtendedString::RealValue() const +{ + if (myLength == 0) + { + return 0.0; + } + // Convert to ASCII and parse + TCollection_AsciiString anAscii(*this); + return anAscii.RealValue(); +} + +//================================================================================================== + +bool TCollection_ExtendedString::IsRealValue(bool theToCheckFull) const +{ + if (myLength == 0) + { + return false; + } + // Convert to ASCII and check + TCollection_AsciiString anAscii(*this); + return anAscii.IsRealValue(theToCheckFull); +} + +//================================================================================================== + +bool TCollection_ExtendedString::IsSameString(const TCollection_ExtendedString& theOther, + const bool theIsCaseSensitive) const +{ + if (myLength != theOther.myLength) + { + return false; + } + + if (theIsCaseSensitive) + { + return memcmp(myString, theOther.myString, myLength * sizeof(char16_t)) == 0; + } + + // Case-insensitive comparison (ASCII only) + for (int i = 0; i < myLength; ++i) + { + char16_t c1 = myString[i]; + char16_t c2 = theOther.myString[i]; + + // Convert to lowercase for comparison + if (c1 >= u'A' && c1 <= u'Z') + { + c1 = static_cast(c1 - u'A' + u'a'); + } + if (c2 >= u'A' && c2 <= u'Z') + { + c2 = static_cast(c2 - u'A' + u'a'); + } + + if (c1 != c2) + { + return false; + } + } + return true; +} diff --git a/src/FoundationClasses/TKernel/TCollection/TCollection_ExtendedString.hxx b/src/FoundationClasses/TKernel/TCollection/TCollection_ExtendedString.hxx index 050606a492..52d5fa4863 100644 --- a/src/FoundationClasses/TKernel/TCollection/TCollection_ExtendedString.hxx +++ b/src/FoundationClasses/TKernel/TCollection/TCollection_ExtendedString.hxx @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -31,6 +30,11 @@ #include #include #include + +#if Standard_CPP17_OR_HIGHER + #include +#endif + class TCollection_AsciiString; //! A variable-length sequence of "extended" (UNICODE) characters (16-bit character type). @@ -53,18 +57,21 @@ class TCollection_ExtendedString public: DEFINE_STANDARD_ALLOC - //! Initializes a ExtendedString to an empty ExtendedString. + //! Initializes an ExtendedString to an empty ExtendedString. Standard_EXPORT TCollection_ExtendedString() noexcept; - //! Creation by converting a CString to an extended - //! string. If is true then the string is - //! treated as having UTF-8 coding. If it is not a UTF-8 - //! then is ignored and each character is + //! Creation by converting a CString to an extended string. + //! If theIsMultiByte is true then the string is treated as having UTF-8 coding. + //! If it is not a UTF-8 then theIsMultiByte is ignored and each character is //! copied to ExtCharacter. - Standard_EXPORT TCollection_ExtendedString(const char* astring, const bool isMultiByte = false); + //! @param[in] theString the C string to convert + //! @param[in] theIsMultiByte flag indicating UTF-8 coding + Standard_EXPORT TCollection_ExtendedString(const char* theString, + const bool theIsMultiByte = false); - //! Creation by converting an ExtString to an extended string. - Standard_EXPORT TCollection_ExtendedString(const char16_t* astring); + //! Creation by converting an ExtString (char16_t*) to an extended string. + //! @param[in] theString the char16_t string to copy + Standard_EXPORT TCollection_ExtendedString(const char16_t* theString); #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) //! Initialize from wide-char string considering it as Unicode string @@ -72,81 +79,292 @@ public: //! //! This constructor is unavailable if application is built with deprecated msvc option //! "-Zc:wchar_t-", since OCCT itself is never built with this option. + //! @param[in] theStringUtf the wide character string to convert Standard_EXPORT TCollection_ExtendedString(const wchar_t* theStringUtf); #endif - //! Initializes a AsciiString with a single character. - Standard_EXPORT TCollection_ExtendedString(const char aChar); + //! Initializes an ExtendedString with a single ASCII character. + //! @param[in] theChar the ASCII character to initialize from + Standard_EXPORT TCollection_ExtendedString(const char theChar); - //! Initializes a ExtendedString with a single character. - Standard_EXPORT TCollection_ExtendedString(const char16_t aChar); + //! Initializes an ExtendedString with a single extended character. + //! @param[in] theChar the extended character to initialize from + Standard_EXPORT TCollection_ExtendedString(const char16_t theChar); - //! Initializes a ExtendedString with space allocated. - //! and filled with .This is useful for buffers. - Standard_EXPORT TCollection_ExtendedString(const int length, const char16_t filler); + //! Initializes an ExtendedString with specified length space allocated + //! and filled with filler character. This is useful for buffers. + //! @param[in] theLength the length to allocate + //! @param[in] theFiller the character to fill with + Standard_EXPORT TCollection_ExtendedString(const int theLength, const char16_t theFiller); - //! Initializes an ExtendedString with an integer value - Standard_EXPORT TCollection_ExtendedString(const int value); + //! Initializes an ExtendedString with an integer value. + //! @param[in] theValue the integer value to convert to string + Standard_EXPORT TCollection_ExtendedString(const int theValue); - //! Initializes an ExtendedString with a real value - Standard_EXPORT TCollection_ExtendedString(const double value); + //! Initializes an ExtendedString with a real value. + //! @param[in] theValue the real value to convert to string + Standard_EXPORT TCollection_ExtendedString(const double theValue); - //! Initializes a ExtendedString with another ExtendedString. - Standard_EXPORT TCollection_ExtendedString(const TCollection_ExtendedString& astring); + //! Initializes an ExtendedString with another ExtendedString. + //! @param[in] theString the string to copy from + Standard_EXPORT TCollection_ExtendedString(const TCollection_ExtendedString& theString); - //! Move constructor + //! Move constructor. + //! @param[in] theOther the string to move from Standard_EXPORT TCollection_ExtendedString(TCollection_ExtendedString&& theOther) noexcept; - //! Creation by converting an Ascii string to an extended - //! string. The string is treated as having UTF-8 coding. - //! If it is not a UTF-8 or multi byte then - //! each character is copied to ExtCharacter. - Standard_EXPORT TCollection_ExtendedString(const TCollection_AsciiString& astring, - const bool isMultiByte = true); + //! Creation by converting an AsciiString to an extended string. + //! The string is treated as having UTF-8 coding. + //! If it is not a UTF-8 or multi byte then each character is copied to ExtCharacter. + //! @param[in] theString the ASCII string to convert + //! @param[in] theIsMultiByte flag indicating UTF-8 coding + Standard_EXPORT TCollection_ExtendedString(const TCollection_AsciiString& theString, + const bool theIsMultiByte = true); + + //! Initializes an ExtendedString with a char16_t string and explicit length. + //! @param[in] theString the char16_t string to initialize from + //! @param[in] theLength the length of the string + Standard_EXPORT TCollection_ExtendedString(const char16_t* theString, const int theLength); + + //! Template constructor for char16_t string literals or arrays. + //! For true string literals (const char16_t[N] in code), the size is computed at runtime + //! by scanning for null terminator to handle both literals and buffers correctly. + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"Hello World"); // Works with string literals + //! char16_t buffer[100]; + //! // ... fill buffer ... + //! TCollection_ExtendedString aBufferString(buffer); // Works with buffers too + //! ``` + //! @param[in] theLiteral the string literal or char16_t array + template + TCollection_ExtendedString(const char16_t (&theLiteral)[N]) + : TCollection_ExtendedString(theLiteral, extStringLen(theLiteral)) + { + } + +#if Standard_CPP17_OR_HIGHER + //! Initializes an ExtendedString from a std::u16string_view. + //! @param[in] theStringView the string view to copy + explicit TCollection_ExtendedString(const std::u16string_view& theStringView) + { + myLength = static_cast(theStringView.size()); + if (myLength == 0) + { + allocate(0); + } + else + { + allocate(myLength); + memcpy(myString, theStringView.data(), myLength * sizeof(char16_t)); + } + } + + //! Assignment from a std::u16string_view. + //! @param[in] theStringView the string view to copy + TCollection_ExtendedString& operator=(const std::u16string_view& theStringView) + { + const int aNewLen = static_cast(theStringView.size()); + reallocate(aNewLen); + if (aNewLen > 0) + { + memcpy(myString, theStringView.data(), aNewLen * sizeof(char16_t)); + } + return *this; + } + + //! Conversion to std::u16string_view. + //! @return a non-owning view of the string data + operator std::u16string_view() const noexcept { return std::u16string_view(myString, myLength); } +#endif //! Appends the other extended string to this extended string. //! Note that this method is an alias of operator +=. - //! Example: aString += anotherString - Standard_EXPORT void AssignCat(const TCollection_ExtendedString& other); + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"Hello"); + //! TCollection_ExtendedString anotherString(u" World"); + //! aString += anotherString; + //! // Result: aString == u"Hello World" + //! ``` + //! @param[in] theOther the string to append + Standard_EXPORT void AssignCat(const TCollection_ExtendedString& theOther); - void operator+=(const TCollection_ExtendedString& other) { AssignCat(other); } + void operator+=(const TCollection_ExtendedString& theOther) { AssignCat(theOther); } //! Appends the utf16 char to this extended string. + //! @param[in] theChar the character to append Standard_EXPORT void AssignCat(const char16_t theChar); - //! Appends to me. - Standard_EXPORT TCollection_ExtendedString Cat(const TCollection_ExtendedString& other) const; + //! Core implementation: Appends char16_t string (pointer and length) to this extended string. + //! This is the primary implementation that all other AssignCat overloads redirect to. + //! @param[in] theString pointer to the string to append + //! @param[in] theLength length of the string to append + Standard_EXPORT void AssignCat(const char16_t* theString, const int theLength); - TCollection_ExtendedString operator+(const TCollection_ExtendedString& other) const + //! Appends the char16_t string to this extended string. + //! @param[in] theString the string to append + inline void AssignCat(const char16_t* theString) { - return Cat(other); + if (theString != nullptr) + { + AssignCat(theString, extStringLen(theString)); + } } - //! Substitutes all the characters equal to aChar by NewChar - //! in the ExtendedString . - //! The substitution can be case sensitive. - //! If you don't use default case sensitive, no matter whether aChar is uppercase or not. - Standard_EXPORT void ChangeAll(const char16_t aChar, const char16_t NewChar); + //! Appends the char16_t string to this extended string (alias of AssignCat()). + void operator+=(const char16_t* theString) { AssignCat(theString); } - //! Removes all characters contained in . + //! Template method for appending char16_t string literals or arrays. + //! For char arrays (like buffers), scans for null terminator to get actual length. + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"Hello"); + //! aString += u" World"; // Works with string literals + //! char16_t buffer[100]; + //! // ... fill buffer ... + //! aString += buffer; // Works with buffers too + //! ``` + //! @param[in] theLiteral the string literal or char16_t array to append + template + void AssignCat(const char16_t (&theLiteral)[N]) + { + AssignCat(theLiteral, extStringLen(theLiteral)); + } + + template + void operator+=(const char16_t (&theLiteral)[N]) + { + AssignCat(theLiteral, extStringLen(theLiteral)); + } + +#if Standard_CPP17_OR_HIGHER + //! Appends the std::u16string_view to this extended string. + //! @param[in] theStringView the string view to append + void AssignCat(const std::u16string_view& theStringView) + { + if (theStringView.empty()) + { + return; + } + const int anOtherLen = static_cast(theStringView.size()); + const int anOldLen = myLength; + reallocate(myLength + anOtherLen); + memcpy(myString + anOldLen, theStringView.data(), anOtherLen * sizeof(char16_t)); + } + + //! Appends the std::u16string_view to this extended string (alias of AssignCat()). + void operator+=(const std::u16string_view& theStringView) { AssignCat(theStringView); } +#endif + + //! Core implementation: Concatenates char16_t string (pointer and length) + //! and returns a new string. + //! @param[in] theOther pointer to the string to append + //! @param[in] theLength length of the string to append + //! @return new string with theOther appended + Standard_EXPORT TCollection_ExtendedString Cat(const char16_t* theOther, + const int theLength) const; + + //! Concatenates char16_t string and returns a new string. + //! @param[in] theOther the null-terminated string to append + //! @return new string with theOther appended + TCollection_ExtendedString Cat(const char16_t* theOther) const + { + return Cat(theOther, extStringLen(theOther)); + } + + TCollection_ExtendedString operator+(const char16_t* theOther) const { return Cat(theOther); } + + //! Appends the other extended string to this string and returns a new string. + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"Hello"); + //! TCollection_ExtendedString anotherString(u" World"); + //! TCollection_ExtendedString aResult = aString + anotherString; + //! // Result: aResult == u"Hello World" + //! ``` + //! @param[in] theOther the string to append + //! @return new string with theOther appended + TCollection_ExtendedString Cat(const TCollection_ExtendedString& theOther) const + { + return Cat(theOther.myString, theOther.myLength); + } + + TCollection_ExtendedString operator+(const TCollection_ExtendedString& theOther) const + { + return Cat(theOther); + } + + //! Substitutes all the characters equal to theChar by theNewChar + //! in this ExtendedString. + //! The substitution can be case sensitive. + //! If you don't use default case sensitive, no matter whether theChar is uppercase or not. + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"Histake"); + //! aString.ChangeAll(u'H', u'M'); + //! // Result: aString == u"Mistake" + //! ``` + //! @param[in] theChar the character to replace + //! @param[in] theNewChar the replacement character + Standard_EXPORT void ChangeAll(const char16_t theChar, const char16_t theNewChar); + + //! Removes all characters contained in this string. //! This produces an empty ExtendedString. Standard_EXPORT void Clear(); - //! Copy to . - //! Used as operator = - Standard_EXPORT void Copy(const TCollection_ExtendedString& fromwhere); + //! Core implementation: Copy from a char16_t pointer with explicit length. + //! @param[in] theString pointer to the string to copy + //! @param[in] theLength length of the string to copy + Standard_EXPORT void Copy(const char16_t* theString, const int theLength); - //! Copy assignment operator + //! Copy from a char16_t pointer. + //! @param[in] theString the null-terminated string to copy + void Copy(const char16_t* theString) { Copy(theString, extStringLen(theString)); } + + //! Copy theFromWhere to this string. + //! Used as operator = + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString; + //! TCollection_ExtendedString anotherString(u"Hello World"); + //! aString = anotherString; // operator= + //! // Result: aString == u"Hello World" + //! ``` + //! @param[in] theFromWhere the string to copy from + void Copy(const TCollection_ExtendedString& theFromWhere) + { + if (&theFromWhere != this) + { + Copy(theFromWhere.myString, theFromWhere.myLength); + } + } + + //! Copy assignment operator. TCollection_ExtendedString& operator=(const TCollection_ExtendedString& theOther) { Copy(theOther); return *this; } - //! Moves string without reallocations + //! Assignment from char16_t pointer. + TCollection_ExtendedString& operator=(const char16_t* theString) + { + Copy(theString); + return *this; + } + + //! Moves string without reallocations. + //! @param[in] theOther the string to move from Standard_EXPORT void Move(TCollection_ExtendedString&& theOther); - //! Move assignment operator + //! Move assignment operator. TCollection_ExtendedString& operator=(TCollection_ExtendedString&& theOther) noexcept { Move(std::forward(theOther)); @@ -154,168 +372,411 @@ public: } //! Exchange the data of two strings (without reallocating memory). + //! @param[in,out] theOther the string to exchange data with Standard_EXPORT void Swap(TCollection_ExtendedString& theOther); //! Frees memory allocated by ExtendedString. Standard_EXPORT ~TCollection_ExtendedString(); - //! Insert a Character at position . - Standard_EXPORT void Insert(const int where, const char16_t what); + //! Insert a Character at position theWhere. + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"hy not ?"); + //! aString.Insert(1, u'W'); + //! // Result: aString == u"Why not ?" + //! ``` + //! @param[in] theWhere the position to insert at (1-based) + //! @param[in] theWhat the character to insert + Standard_EXPORT void Insert(const int theWhere, const char16_t theWhat); - //! Insert a ExtendedString at position . - Standard_EXPORT void Insert(const int where, const TCollection_ExtendedString& what); + //! Core implementation: Insert a char16_t string (pointer and length) at position theWhere. + //! @param[in] theWhere the position to insert at (1-based) + //! @param[in] theWhat pointer to the string to insert + //! @param[in] theLength length of the string to insert + Standard_EXPORT void Insert(const int theWhere, const char16_t* theWhat, const int theLength); + + //! Insert a char16_t string at position theWhere. + //! @param[in] theWhere the position to insert at (1-based) + //! @param[in] theWhat the null-terminated string to insert + void Insert(const int theWhere, const char16_t* theWhat) + { + Insert(theWhere, theWhat, extStringLen(theWhat)); + } + + //! Insert an ExtendedString at position theWhere. + //! @param[in] theWhere the position to insert at (1-based) + //! @param[in] theWhat the string to insert + void Insert(const int theWhere, const TCollection_ExtendedString& theWhat) + { + Insert(theWhere, theWhat.myString, theWhat.myLength); + } //! Returns True if this string contains no characters. - bool IsEmpty() const { return mylength == 0; } + bool IsEmpty() const { return myLength == 0; } + + //! Core implementation: Returns true if this string equals theOther (pointer and length). + //! @param[in] theOther pointer to the string to compare with + //! @param[in] theLength length of the string to compare with + //! @return true if strings are equal, false otherwise + Standard_EXPORT bool IsEqual(const char16_t* theOther, const int theLength) const; + + //! Returns true if this string equals theOther null-terminated string. + //! Note that this method is an alias of operator ==. + //! @param[in] theOther the char16_t string to compare with + //! @return true if strings are equal, false otherwise + bool IsEqual(const char16_t* theOther) const { return IsEqual(theOther, extStringLen(theOther)); } + + bool operator==(const char16_t* theOther) const { return IsEqual(theOther); } //! Returns true if the characters in this extended - //! string are identical to the characters in the other extended string. - //! Note that this method is an alias of operator == - Standard_EXPORT bool IsEqual(const char16_t* other) const; + //! string are identical to the characters in theOther extended string. + //! Note that this method is an alias of operator ==. + //! @param[in] theOther the extended string to compare with + //! @return true if strings are equal, false otherwise + bool IsEqual(const TCollection_ExtendedString& theOther) const + { + return myLength == theOther.myLength && IsEqual(theOther.myString, theOther.myLength); + } - bool operator==(const char16_t* other) const { return IsEqual(other); } + bool operator==(const TCollection_ExtendedString& theOther) const { return IsEqual(theOther); } - //! Returns true if the characters in this extended - //! string are identical to the characters in the other extended string. - //! Note that this method is an alias of operator == - Standard_EXPORT bool IsEqual(const TCollection_ExtendedString& other) const; + //! Core implementation: Returns true if this string differs from theOther (pointer and length). + //! @param[in] theOther pointer to the string to compare with + //! @param[in] theLength length of the string to compare with + //! @return true if strings are different, false otherwise + Standard_EXPORT bool IsDifferent(const char16_t* theOther, const int theLength) const; - bool operator==(const TCollection_ExtendedString& other) const { return IsEqual(other); } + //! Returns true if this string differs from theOther null-terminated string. + //! Note that this method is an alias of operator !=. + //! @param[in] theOther the char16_t string to compare with + //! @return true if strings are different, false otherwise + bool IsDifferent(const char16_t* theOther) const + { + return IsDifferent(theOther, extStringLen(theOther)); + } + + bool operator!=(const char16_t* theOther) const { return IsDifferent(theOther); } //! Returns true if there are differences between the - //! characters in this extended string and the other extended string. + //! characters in this extended string and theOther extended string. //! Note that this method is an alias of operator !=. - Standard_EXPORT bool IsDifferent(const char16_t* other) const; + //! @param[in] theOther the extended string to compare with + //! @return true if strings are different, false otherwise + bool IsDifferent(const TCollection_ExtendedString& theOther) const + { + return IsDifferent(theOther.myString, theOther.myLength); + } - bool operator!=(const char16_t* other) const { return IsDifferent(other); } + bool operator!=(const TCollection_ExtendedString& theOther) const + { + return IsDifferent(theOther); + } - //! Returns true if there are differences between the - //! characters in this extended string and the other extended string. - //! Note that this method is an alias of operator !=. - Standard_EXPORT bool IsDifferent(const TCollection_ExtendedString& other) const; + //! Core implementation: Returns TRUE if this string is lexicographically less than theOther. + //! @param[in] theOther pointer to the string to compare with + //! @param[in] theLength length of the string to compare with + //! @return true if this string is less than theOther + Standard_EXPORT bool IsLess(const char16_t* theOther, const int theLength) const; - bool operator!=(const TCollection_ExtendedString& other) const { return IsDifferent(other); } + //! Returns TRUE if this string is lexicographically less than theOther. + //! @param[in] theOther the char16_t string to compare with + //! @return true if this string is less than theOther + bool IsLess(const char16_t* theOther) const { return IsLess(theOther, extStringLen(theOther)); } - //! Returns TRUE if is less than . - Standard_EXPORT bool IsLess(const char16_t* other) const; + bool operator<(const char16_t* theOther) const { return IsLess(theOther); } - bool operator<(const char16_t* other) const { return IsLess(other); } + //! Returns TRUE if this string is lexicographically less than theOther. + //! @param[in] theOther the extended string to compare with + //! @return true if this string is less than theOther + bool IsLess(const TCollection_ExtendedString& theOther) const + { + return IsLess(theOther.myString, theOther.myLength); + } - //! Returns TRUE if is less than . - Standard_EXPORT bool IsLess(const TCollection_ExtendedString& other) const; + bool operator<(const TCollection_ExtendedString& theOther) const { return IsLess(theOther); } - bool operator<(const TCollection_ExtendedString& other) const { return IsLess(other); } + //! Core implementation: Returns TRUE if this string is lexicographically greater than theOther. + //! @param[in] theOther pointer to the string to compare with + //! @param[in] theLength length of the string to compare with + //! @return true if this string is greater than theOther + Standard_EXPORT bool IsGreater(const char16_t* theOther, const int theLength) const; - //! Returns TRUE if is greater than . - Standard_EXPORT bool IsGreater(const char16_t* other) const; + //! Returns TRUE if this string is lexicographically greater than theOther. + //! @param[in] theOther the char16_t string to compare with + //! @return true if this string is greater than theOther + bool IsGreater(const char16_t* theOther) const + { + return IsGreater(theOther, extStringLen(theOther)); + } - bool operator>(const char16_t* other) const { return IsGreater(other); } + bool operator>(const char16_t* theOther) const { return IsGreater(theOther); } - //! Returns TRUE if is greater than . - Standard_EXPORT bool IsGreater(const TCollection_ExtendedString& other) const; + //! Returns TRUE if this string is lexicographically greater than theOther. + //! @param[in] theOther the extended string to compare with + //! @return true if this string is greater than theOther + bool IsGreater(const TCollection_ExtendedString& theOther) const + { + return IsGreater(theOther.myString, theOther.myLength); + } - bool operator>(const TCollection_ExtendedString& other) const { return IsGreater(other); } + bool operator>(const TCollection_ExtendedString& theOther) const { return IsGreater(theOther); } + + //! Core implementation: Determines whether this string starts with theStartString. + //! @param[in] theStartString pointer to the string to check for + //! @param[in] theLength length of the string to check for + //! @return true if this string starts with theStartString + Standard_EXPORT bool StartsWith(const char16_t* theStartString, const int theLength) const; + + //! Determines whether this string starts with theStartString. + //! @param[in] theStartString the null-terminated string to check for + //! @return true if this string starts with theStartString + bool StartsWith(const char16_t* theStartString) const + { + return StartsWith(theStartString, extStringLen(theStartString)); + } //! Determines whether the beginning of this string instance matches the specified string. - Standard_EXPORT bool StartsWith(const TCollection_ExtendedString& theStartString) const; + //! @param[in] theStartString the string to check for at the beginning + //! @return true if this string starts with theStartString + bool StartsWith(const TCollection_ExtendedString& theStartString) const + { + return StartsWith(theStartString.myString, theStartString.myLength); + } + + //! Core implementation: Determines whether this string ends with theEndString. + //! @param[in] theEndString pointer to the string to check for + //! @param[in] theLength length of the string to check for + //! @return true if this string ends with theEndString + Standard_EXPORT bool EndsWith(const char16_t* theEndString, const int theLength) const; + + //! Determines whether this string ends with theEndString. + //! @param[in] theEndString the null-terminated string to check for + //! @return true if this string ends with theEndString + bool EndsWith(const char16_t* theEndString) const + { + return EndsWith(theEndString, extStringLen(theEndString)); + } //! Determines whether the end of this string instance matches the specified string. - Standard_EXPORT bool EndsWith(const TCollection_ExtendedString& theEndString) const; + //! @param[in] theEndString the string to check for at the end + //! @return true if this string ends with theEndString + bool EndsWith(const TCollection_ExtendedString& theEndString) const + { + return EndsWith(theEndString.myString, theEndString.myLength); + } - //! Returns True if the ExtendedString contains only - //! "Ascii Range" characters . + //! Returns True if the ExtendedString contains only "Ascii Range" characters. + //! @return true if string contains only ASCII characters Standard_EXPORT bool IsAscii() const; //! Returns the number of 16-bit code units //! (might be greater than number of Unicode symbols if string contains surrogate pairs). + //! @return the number of 16-bit code units Standard_EXPORT int Length() const; - //! Displays . - Standard_EXPORT void Print(Standard_OStream& astream) const; - friend Standard_EXPORT Standard_OStream& operator<<(Standard_OStream& astream, - const TCollection_ExtendedString& astring); + //! Displays this string on a stream. + //! @param[in] theStream the output stream + Standard_EXPORT void Print(Standard_OStream& theStream) const; + friend Standard_EXPORT Standard_OStream& operator<<(Standard_OStream& theStream, + const TCollection_ExtendedString& theString); - //! Removes every characters from . - Standard_EXPORT void RemoveAll(const char16_t what); + //! Removes every theWhat characters from this string. + //! @param[in] theWhat the character to remove + Standard_EXPORT void RemoveAll(const char16_t theWhat); - //! Erases characters from position , included. - Standard_EXPORT void Remove(const int where, const int ahowmany = 1); + //! Erases theHowMany characters from position theWhere, theWhere included. + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"Hello"); + //! aString.Remove(2, 2); // erases 2 characters from position 2 + //! // Result: aString == u"Hlo" + //! ``` + //! @param[in] theWhere the position to start erasing from (1-based) + //! @param[in] theHowMany the number of characters to erase + Standard_EXPORT void Remove(const int theWhere, const int theHowMany = 1); - //! Searches a ExtendedString in from the beginning - //! and returns position of first item matching. - //! it returns -1 if not found. - Standard_EXPORT int Search(const TCollection_ExtendedString& what) const; + //! Core implementation: Searches for theWhat (pointer and length) from the beginning. + //! @param[in] theWhat pointer to the string to search for + //! @param[in] theLength length of the string to search for + //! @return the position of first match (1-based), or -1 if not found + Standard_EXPORT int Search(const char16_t* theWhat, const int theLength) const; - //! Searches a ExtendedString in another ExtendedString from the - //! end and returns position of first item matching. - //! it returns -1 if not found. - Standard_EXPORT int SearchFromEnd(const TCollection_ExtendedString& what) const; + //! Searches for theWhat null-terminated string from the beginning. + //! @param[in] theWhat the null-terminated string to search for + //! @return the position of first match (1-based), or -1 if not found + int Search(const char16_t* theWhat) const { return Search(theWhat, extStringLen(theWhat)); } - //! Replaces one character in the ExtendedString at position . - //! If is less than zero or greater than the length of + //! Searches an ExtendedString in this string from the beginning + //! and returns position of first item theWhat matching. + //! It returns -1 if not found. + //! @param[in] theWhat the string to search for + //! @return the position of first match (1-based), or -1 if not found + int Search(const TCollection_ExtendedString& theWhat) const + { + return Search(theWhat.myString, theWhat.myLength); + } + + //! Core implementation: Searches for theWhat (pointer and length) from the end. + //! @param[in] theWhat pointer to the string to search for + //! @param[in] theLength length of the string to search for + //! @return the position of first match from end (1-based), or -1 if not found + Standard_EXPORT int SearchFromEnd(const char16_t* theWhat, const int theLength) const; + + //! Searches for theWhat null-terminated string from the end. + //! @param[in] theWhat the null-terminated string to search for + //! @return the position of first match from end (1-based), or -1 if not found + int SearchFromEnd(const char16_t* theWhat) const + { + return SearchFromEnd(theWhat, extStringLen(theWhat)); + } + + //! Searches an ExtendedString in this string from the end + //! and returns position of first item theWhat matching. + //! It returns -1 if not found. + //! @param[in] theWhat the string to search for + //! @return the position of first match from end (1-based), or -1 if not found + int SearchFromEnd(const TCollection_ExtendedString& theWhat) const + { + return SearchFromEnd(theWhat.myString, theWhat.myLength); + } + + //! Replaces one character in the ExtendedString at position theWhere. + //! If theWhere is less than zero or greater than the length of this string //! an exception is raised. - Standard_EXPORT void SetValue(const int where, const char16_t what); - - //! Replaces a part of by another ExtendedString see above. - Standard_EXPORT void SetValue(const int where, const TCollection_ExtendedString& what); - - //! Splits this extended string into two sub-strings at position where. - //! - The second sub-string (from position - //! where + 1 of this string to the end) is - //! returned in a new extended string. - //! - this extended string is modified: its last - //! characters are removed, it becomes equal to - //! the first sub-string (from the first character to position where). + //! //! Example: - //! aString contains "abcdefg" - //! aString.Split(3) gives = "abc" and returns "defg" - Standard_EXPORT TCollection_ExtendedString Split(const int where); + //! ```cpp + //! TCollection_ExtendedString aString(u"Garbake"); + //! aString.SetValue(6, u'g'); + //! // Result: aString == u"Garbage" + //! ``` + //! @param[in] theWhere the position to replace at (1-based) + //! @param[in] theWhat the character to replace with + Standard_EXPORT void SetValue(const int theWhere, const char16_t theWhat); - //! Extracts token from . - //! By default, the is set to space and tabulation. - //! By default, the token extracted is the first one (whichone = 1). - //! contains all separators you need. - //! If no token indexed by is found, it returns an empty AsciiString. + //! Core implementation: Replaces a part of this string by char16_t string (pointer and length). + //! @param[in] theWhere the position to start replacement (1-based) + //! @param[in] theWhat pointer to the string to replace with + //! @param[in] theLength length of the string to replace with + Standard_EXPORT void SetValue(const int theWhere, const char16_t* theWhat, const int theLength); + + //! Replaces a part of this string by a null-terminated char16_t string. + //! @param[in] theWhere the position to start replacement (1-based) + //! @param[in] theWhat the null-terminated string to replace with + void SetValue(const int theWhere, const char16_t* theWhat) + { + SetValue(theWhere, theWhat, extStringLen(theWhat)); + } + + //! Replaces a part of this string by another ExtendedString. + //! @param[in] theWhere the position to start replacement (1-based) + //! @param[in] theWhat the string to replace with + void SetValue(const int theWhere, const TCollection_ExtendedString& theWhat) + { + SetValue(theWhere, theWhat.myString, theWhat.myLength); + } + + //! Copies characters from this string starting from index theFromIndex + //! to the index theToIndex (inclusive). + //! Raises an exception if theToIndex or theFromIndex is out of bounds. + //! //! Example: - //! aString contains "This is a message" - //! aString.Token() returns "This" - //! aString.Token(" ",4) returns "message" - //! aString.Token(" ",2) returns "is" - //! aString.Token(" ",9) returns "" - //! Other separators than space character and tabulation are allowed : - //! aString contains "1234; test:message , value" - //! aString.Token("; :,",4) returns "value" - //! aString.Token("; :,",2) returns "test" - Standard_EXPORT TCollection_ExtendedString Token(const char16_t* separators, - const int whichone = 1) const; + //! ```cpp + //! TCollection_ExtendedString aString(u"abcdefg"); + //! TCollection_ExtendedString aSubString = aString.SubString(3, 6); + //! // Result: aSubString == u"cdef" + //! ``` + //! @param[in] theFromIndex the starting index (1-based) + //! @param[in] theToIndex the ending index (1-based, inclusive) + //! @return the substring from theFromIndex to theToIndex + Standard_EXPORT TCollection_ExtendedString SubString(const int theFromIndex, + const int theToIndex) const; - //! Returns pointer to ExtString + //! Splits this extended string into two sub-strings at position theWhere. + //! - The second sub-string (from position theWhere + 1 of this string to the end) is + //! returned in a new extended string. + //! - This extended string is modified: its last characters are removed, it becomes equal to + //! the first sub-string (from the first character to position theWhere). + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"abcdefg"); + //! TCollection_ExtendedString aSecondPart = aString.Split(3); + //! // Result: aString == u"abc" and aSecondPart == u"defg" + //! ``` + //! @param[in] theWhere the position to split at (0-based) + //! @return the second part of the split string + Standard_EXPORT TCollection_ExtendedString Split(const int theWhere); + + //! Extracts theWhichOne token from this string. + //! By default, the theSeparators is set to space and tabulation. + //! By default, the token extracted is the first one (theWhichOne = 1). + //! theSeparators contains all separators you need. + //! If no token indexed by theWhichOne is found, it returns an empty ExtendedString. + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"This is a message"); + //! TCollection_ExtendedString aToken1 = aString.Token(); + //! // Result: aToken1 == u"This" + //! + //! TCollection_ExtendedString aToken2 = aString.Token(u" ", 4); + //! // Result: aToken2 == u"message" + //! + //! TCollection_ExtendedString aToken3 = aString.Token(u" ", 2); + //! // Result: aToken3 == u"is" + //! + //! TCollection_ExtendedString aToken4 = aString.Token(u" ", 9); + //! // Result: aToken4 == u"" + //! + //! TCollection_ExtendedString bString(u"1234; test:message , value"); + //! TCollection_ExtendedString bToken1 = bString.Token(u"; :,", 4); + //! // Result: bToken1 == u"value" + //! ``` + //! @param[in] theSeparators the separator characters + //! @param[in] theWhichOne the token number to extract (1-based) + //! @return the extracted token + Standard_EXPORT TCollection_ExtendedString Token(const char16_t* theSeparators, + const int theWhichOne = 1) const; + + //! Returns pointer to ExtString (char16_t*). + //! @return the char16_t string representation Standard_EXPORT const char16_t* ToExtString() const; #ifdef _WIN32 //! Returns pointer to string as wchar_t* on Windows platform where wchar_t* is considered as //! UTF-16 string. This method is useful to pass string into wide-char system APIs, and makes //! sense only on Windows (other systems use UTF-8 and can miss wide-char functions at all). + //! @return the wchar_t string representation const wchar_t* ToWideString() const { return (const wchar_t*)ToExtString(); } #endif - //! Truncates to characters. - //! Example: me = "Hello Dolly" -> Trunc(3) -> me = "Hel" - //! Exceptions - //! Standard_OutOfRange if ahowmany is greater - //! than the length of this string. - Standard_EXPORT void Trunc(const int ahowmany); - - //! Returns character at position in . - //! If is less than zero or greater than the length of - //! , an exception is raised. + //! Truncates this string to theHowMany characters. + //! //! Example: - //! aString contains "Hello" - //! aString.Value(2) returns 'e' - //! Exceptions - //! Standard_OutOfRange if where lies outside - //! the bounds of this extended string. - Standard_EXPORT char16_t Value(const int where) const; + //! ```cpp + //! TCollection_ExtendedString aString(u"Hello Dolly"); + //! aString.Trunc(3); + //! // Result: aString == u"Hel" + //! ``` + //! @param[in] theHowMany the number of characters to keep + Standard_EXPORT void Trunc(const int theHowMany); + + //! Returns character at position theWhere in this string. + //! If theWhere is less than zero or greater than the length of + //! this string, an exception is raised. + //! + //! Example: + //! ```cpp + //! TCollection_ExtendedString aString(u"Hello"); + //! char16_t aChar = aString.Value(2); + //! // Result: aChar == u'e' + //! ``` + //! @param[in] theWhere the position to get character from (1-based) + //! @return the character at the specified position + Standard_EXPORT char16_t Value(const int theWhere) const; //! Returns a hashed value for the extended string. //! Note: if string is ASCII, the computed value is the same as the value computed with the @@ -324,12 +785,12 @@ public: //! @return a computed hash code size_t HashCode() const { - const int aSize = mylength * sizeof(char16_t); - if (mylength < 2) + const int aSize = myLength * sizeof(char16_t); + if (myLength < 2) { - return opencascade::FNVHash::hash_combine(*mystring, aSize); + return opencascade::FNVHash::hash_combine(*myString, aSize); } - return opencascade::hashBytes(mystring, aSize); + return opencascade::hashBytes(myString, aSize); } //! Returns a const reference to a single shared empty string instance. @@ -348,25 +809,133 @@ public: //! Returns true if the characters in this extended //! string are identical to the characters in the other extended string. //! Note that this method is an alias of operator ==. + //! @param[in] theString1 first string to compare + //! @param[in] theString2 second string to compare + //! @return true if strings are equal static bool IsEqual(const TCollection_ExtendedString& theString1, const TCollection_ExtendedString& theString2) { return theString1.IsEqual(theString2); } - //! Converts the internal to UTF8 coding and + //! Converts the internal myString to UTF8 coding and //! returns length of the out CString. A memory for the - //! should be allocated before call! + //! theCString should be allocated before call! + //! @param[in,out] theCString pointer to the output buffer + //! @return length of the UTF-8 string Standard_EXPORT int ToUTF8CString(Standard_PCharacter& theCString) const; //! Returns expected CString length in UTF8 coding (like strlen, without null terminator). //! It can be used for memory calculation before converting to CString containing symbols in UTF8 //! coding. For external allocation, use: char* buf = new char[str.LengthOfCString() + 1]; + //! @return expected UTF-8 string length Standard_EXPORT int LengthOfCString() const; + //! Removes all space characters in the beginning of the string. + Standard_EXPORT void LeftAdjust(); + + //! Removes all space characters at the end of the string. + Standard_EXPORT void RightAdjust(); + + //! Left justify. + //! Length becomes equal to theWidth and the new characters are + //! equal to theFiller. + //! If theWidth < Length nothing happens. + //! @param[in] theWidth the desired width of the string + //! @param[in] theFiller the character to fill with + Standard_EXPORT void LeftJustify(const int theWidth, const char16_t theFiller); + + //! Right justify. + //! Length becomes equal to theWidth and the new characters are + //! equal to theFiller. + //! If theWidth < Length nothing happens. + //! @param[in] theWidth the desired width of the string + //! @param[in] theFiller the character to fill with + Standard_EXPORT void RightJustify(const int theWidth, const char16_t theFiller); + + //! Modifies this string so that its length becomes equal to theWidth + //! and the new characters are equal to theFiller. + //! New characters are added both at the beginning and at the end of this string. + //! If theWidth is less than the length of this string, nothing happens. + //! @param[in] theWidth the desired width of the string + //! @param[in] theFiller the character to fill with + Standard_EXPORT void Center(const int theWidth, const char16_t theFiller); + + //! Converts the first character into its corresponding + //! upper-case character and the other characters into lowercase. + //! @note Only ASCII characters (a-z, A-Z) are affected by case conversion. + Standard_EXPORT void Capitalize(); + + //! Core implementation: Inserts char16_t string (pointer and length) at the beginning. + //! @param[in] theOther pointer to the string to prepend + //! @param[in] theLength length of the string to prepend + Standard_EXPORT void Prepend(const char16_t* theOther, const int theLength); + + //! Inserts a null-terminated char16_t string at the beginning. + //! @param[in] theOther the null-terminated string to prepend + void Prepend(const char16_t* theOther) { Prepend(theOther, extStringLen(theOther)); } + + //! Inserts the other extended string at the beginning of this string. + //! @param[in] theOther the string to prepend + void Prepend(const TCollection_ExtendedString& theOther) + { + Prepend(theOther.myString, theOther.myLength); + } + + //! Returns the index of the first character of this string that is + //! present in theSet. + //! The search begins at index theFromIndex and ends at index theToIndex. + //! Returns zero if failure. + //! @param[in] theSet the set of characters to search for + //! @param[in] theFromIndex the starting index for search (1-based) + //! @param[in] theToIndex the ending index for search (1-based) + //! @return the index of first character found in set, or 0 if not found + Standard_EXPORT int FirstLocationInSet(const TCollection_ExtendedString& theSet, + const int theFromIndex, + const int theToIndex) const; + + //! Returns the index of the first character of this string that is + //! NOT present in theSet. + //! The search begins at index theFromIndex and ends at index theToIndex. + //! Returns zero if failure. + //! @param[in] theSet the set of characters to check against + //! @param[in] theFromIndex the starting index for search (1-based) + //! @param[in] theToIndex the ending index for search (1-based) + //! @return the index of first character not in set, or 0 if not found + Standard_EXPORT int FirstLocationNotInSet(const TCollection_ExtendedString& theSet, + const int theFromIndex, + const int theToIndex) const; + + //! Converts this extended string containing a numeric expression to an Integer. + //! @return the integer value + Standard_EXPORT int IntegerValue() const; + + //! Returns True if this extended string contains an integer value. + //! @return true if string represents an integer value + Standard_EXPORT bool IsIntegerValue() const; + + //! Converts this extended string containing a numeric expression to a Real. + //! @return the real value + Standard_EXPORT double RealValue() const; + + //! Returns True if this extended string starts with characters that can be + //! interpreted as a real value. + //! @param[in] theToCheckFull when TRUE, checks if entire string defines a real value; + //! otherwise checks if string starts with a real value + //! @return true if string represents a real value + Standard_EXPORT bool IsRealValue(bool theToCheckFull = false) const; + + //! Returns True if the strings contain same characters. + //! @param[in] theOther the string to compare with + //! @param[in] theIsCaseSensitive flag indicating case sensitivity + //! @note When case-insensitive, only ASCII characters (a-z, A-Z) are affected. + //! @return true if strings contain same characters + Standard_EXPORT bool IsSameString(const TCollection_ExtendedString& theOther, + const bool theIsCaseSensitive) const; + private: //! Returns true if the input CString was successfully converted to UTF8 coding. - bool ConvertToUnicode(const char* astring); + bool ConvertToUnicode(const char* theString); //! Internal wrapper to allocate on stack or heap void allocate(const int theLength); @@ -377,11 +946,27 @@ private: //! Internal wrapper to deallocate on stack void deallocate(); + //! Helper function to compute length of char16_t string (like strlen for char). + //! Scans for null terminator to find actual string length. + //! @param[in] theString the char16_t string + //! @return length of string (excluding null terminator) + static int extStringLen(const char16_t* theString) + { + if (theString == nullptr) + { + return 0; + } + int aLen = 0; + while (theString[aLen] != u'\0') + { + ++aLen; + } + return aLen; + } + private: - Standard_PExtCharacter mystring{}; //!< NULL-terminated string - // clang-format off - int mylength{}; //!< length in 16-bit code units (excluding terminating NULL symbol) - // clang-format on + char16_t* myString{}; //!< NULL-terminated string + int myLength{}; //!< length in 16-bit code units (excluding terminating NULL symbol) }; namespace std