diff --git a/sal/inc/rtl/string.h b/sal/inc/rtl/string.h index 2a7a2d9..0051fc3 100644 --- a/sal/inc/rtl/string.h +++ b/sal/inc/rtl/string.h @@ -73,6 +73,29 @@ sal_Int32 SAL_CALL rtl_str_getLength( const sal_Char * str ) SAL_THROW_EXTERN_C( */ sal_Int32 SAL_CALL rtl_str_compare( const sal_Char * first, const sal_Char * second ) SAL_THROW_EXTERN_C(); +/** Compare two strings using natural order. + + For non digit characters, the comparison use the same algorithm as + rtl_str_compare. When a number is encountered during the comparison, + natural order is used. Thus, Heading 10 will be considered as greater + than Heading 2. Numerical comparison is done using decimal representation. + + Beware that "MyString 001" and "MyString 1" will be considered as equal + since leading 0 are meaningless. + + @param first + the first null-terminated string to be compared. + + @param second + the second null-terminated string which is compared with the first one. + + @return + 0 if both strings are equal, a value less than 0 if the first string is + less than the second string, and a value greater than 0 if the first + string is greater than the second string. + */ +sal_Int32 SAL_CALL rtl_str_compare_Numeric( const sal_Char * first, const sal_Char * second ) SAL_THROW_EXTERN_C(); + /** Compare two strings. The comparison is based on the numeric value of each character in the diff --git a/sal/inc/rtl/string.hxx b/sal/inc/rtl/string.hxx index 186b515..bd0fa03 100644 --- a/sal/inc/rtl/string.hxx +++ b/sal/inc/rtl/string.hxx @@ -283,6 +283,28 @@ public: } /** + Compares two strings using natural order. + + For non digit characters, the comparison use the same algorithm as + rtl_str_compare. When a number is encountered during the comparison, + natural order is used. Thus, Heading 10 will be considered as greater + than Heading 2. Numerical comparison is done using decimal representation. + + Beware that "MyString 001" and "MyString 1" will be considered as equal + since leading 0 are meaningless. + + @param str the object to be compared. + @return 0 - if both strings are equal + < 0 - if this string is less than the string argument + > 0 - if this string is greater than the string argument + */ + sal_Int32 compareToNumeric( const OString & str ) const SAL_THROW(()) + { + return rtl_str_compare_Numeric( pData->buffer, + str.pData->buffer ); + } + + /** Compares two strings with an maximum count of characters. The comparison is based on the numeric value of each character in diff --git a/sal/inc/rtl/ustring.h b/sal/inc/rtl/ustring.h index 723117c..0ed0bbf 100644 --- a/sal/inc/rtl/ustring.h +++ b/sal/inc/rtl/ustring.h @@ -74,6 +74,29 @@ sal_Int32 SAL_CALL rtl_ustr_getLength( const sal_Unicode * str ) SAL_THROW_EXTER */ sal_Int32 SAL_CALL rtl_ustr_compare( const sal_Unicode * first, const sal_Unicode * second ) SAL_THROW_EXTERN_C(); +/** Compare two strings using natural order. + + For non digit characters, the comparison use the same algorithm as + rtl_str_compare. When a number is encountered during the comparison, + natural order is used. Thus, Heading 10 will be considered as greater + than Heading 2. Numerical comparison is done using decimal representation. + + Beware that "MyString 001" and "MyString 1" will be considered as equal + since leading 0 are meaningless. + + @param first + the first null-terminated string to be compared. + + @param second + the second null-terminated string which is compared with the first one. + + @return + 0 if both strings are equal, a value less than 0 if the first string is + less than the second string, and a value greater than 0 if the first + string is greater than the second string. + */ +sal_Int32 SAL_CALL rtl_ustr_compare_Numeric( const sal_Unicode * first, const sal_Unicode * second ) SAL_THROW_EXTERN_C(); + /** Compare two strings. The comparison is based on the numeric value of each character in the diff --git a/sal/inc/rtl/ustring.hxx b/sal/inc/rtl/ustring.hxx index f948a0c..c44603e 100644 --- a/sal/inc/rtl/ustring.hxx +++ b/sal/inc/rtl/ustring.hxx @@ -330,6 +330,28 @@ public: } /** + Compares two strings using natural order. + + For non digit characters, the comparison use the same algorithm as + rtl_str_compare. When a number is encountered during the comparison, + natural order is used. Thus, Heading 10 will be considered as greater + than Heading 2. Numerical comparison is done using decimal representation. + + Beware that "MyString 001" and "MyString 1" will be considered as equal + since leading 0 are meaningless. + + @param str the object to be compared. + @return 0 - if both strings are equal + < 0 - if this string is less than the string argument + > 0 - if this string is greater than the string argument + */ + sal_Int32 compareToNumeric( const OUString & str ) const SAL_THROW(()) + { + return rtl_ustr_compare_Numeric( pData->buffer, + str.pData->buffer ); + } + + /** Compares two strings with an maximum count of characters. The comparison is based on the numeric value of each character in diff --git a/sal/qa/rtl/strings/test_oustring_compare.cxx b/sal/qa/rtl/strings/test_oustring_compare.cxx index 60423ce..0c46308 100644 --- a/sal/qa/rtl/strings/test_oustring_compare.cxx +++ b/sal/qa/rtl/strings/test_oustring_compare.cxx @@ -42,9 +42,11 @@ class Compare: public CppUnit::TestFixture { private: void equalsIgnoreAsciiCaseAscii(); + void compareNumeric(); CPPUNIT_TEST_SUITE(Compare); CPPUNIT_TEST(equalsIgnoreAsciiCaseAscii); + CPPUNIT_TEST(compareNumeric); CPPUNIT_TEST_SUITE_END(); }; @@ -70,4 +72,60 @@ void test::oustring::Compare::equalsIgnoreAsciiCaseAscii() equalsIgnoreAsciiCaseAscii("abcd")); } +void test::oustring::Compare::compareNumeric() +{ +// --- Some generic tests to ensure we do not alter original behavior +// outside what we want + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("ABC"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("ABC")))) == 0 + ); + // Case sensitivity + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("ABC"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("abc")))) < 0 + ); + // Reverse + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("abc"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("ABC")))) > 0 + ); + // First shorter + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("alongstring"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("alongerstring")))) > 0 + ); + // Second shorter + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("alongerstring"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("alongstring")))) < 0 + ); +// -- Here we go on natural order, each one is followed by classic compare and the reverse comparison + // That's why we originally made the patch + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("Heading 9"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("Heading 10")))) < 0 + ); + // Original behavior + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("Heading 9"))).compareTo(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("Heading 10")))) > 0 + ); + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("Heading 10"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("Heading 9")))) > 0 + ); + // Harder + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("July, the 4th"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("July, the 10th")))) < 0 + ); + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("July, the 4th"))).compareTo(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("July, the 10th")))) > 0 + ); + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("July, the 10th"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("July, the 4th")))) > 0 + ); + // Hardest + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("abc08"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("abc010")))) < 0 + ); + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("abc08"))).compareTo(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("abc010")))) > 0 + ); + CPPUNIT_ASSERT( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("abc010"))).compareToNumeric(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(("abc08")))) > 0 + ); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/source/strtmpl.c b/sal/rtl/source/strtmpl.c index 98c09f1..18bc959 100644 --- a/sal/rtl/source/strtmpl.c +++ b/sal/rtl/source/strtmpl.c @@ -60,6 +60,8 @@ inline void rtl_str_ImplCopy( IMPL_RTL_STRCODE* pDest, } \ } +#define IS_DIGIT(CHAR) (((CHAR) >= 48) && ((CHAR <= 57))) + /* ======================================================================= */ /* C-String functions which could be used without the String-Class */ /* ======================================================================= */ @@ -91,6 +93,51 @@ sal_Int32 SAL_CALL IMPL_RTL_STRNAME( compare )( const IMPL_RTL_STRCODE* pStr1, /* ----------------------------------------------------------------------- */ +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( compare_Numeric )( const IMPL_RTL_STRCODE* pStr1, + const IMPL_RTL_STRCODE* pStr2 ) +{ + sal_Int32 nRet; + do { + while ( ((nRet = ((sal_Int32)(IMPL_RTL_USTRCODE(*pStr1)))- + ((sal_Int32)(IMPL_RTL_USTRCODE(*pStr2)))) == 0) && + *pStr2 ) + { + pStr1++; + pStr2++; + } + + if(*pStr1 && *pStr2) + { + IMPL_RTL_STRCODE c1 = (sal_Int32)IMPL_RTL_USTRCODE( *pStr1 ); + IMPL_RTL_STRCODE c2 = (sal_Int32)IMPL_RTL_USTRCODE( *pStr2 ); + sal_Int64 number1 = 0; + sal_Int64 number2 = 0; + if(IS_DIGIT(c1) && IS_DIGIT(c2)) + { + do + { + number1 = number1 * 10 + (c1 - '0'); + pStr1++; + c1 = (sal_Int32)IMPL_RTL_USTRCODE( *pStr1 ); + } while(IS_DIGIT(c1)); + + do + { + number2 = number2 * 10 + (c2 - '0'); + pStr2++; + c2 = (sal_Int32)IMPL_RTL_USTRCODE( *pStr2 ); + } while(IS_DIGIT(c2)); + + nRet = number1 - number2; + } + } + } while(nRet == 0 && *pStr1 && *pStr2); + + return nRet; +} + +/* ----------------------------------------------------------------------- */ + sal_Int32 SAL_CALL IMPL_RTL_STRNAME( compare_WithLength )( const IMPL_RTL_STRCODE* pStr1, sal_Int32 nStr1Len, const IMPL_RTL_STRCODE* pStr2, diff --git a/sal/util/sal.map b/sal/util/sal.map index 8b1c079..a7e3eb6 100755 --- a/sal/util/sal.map +++ b/sal/util/sal.map @@ -194,6 +194,7 @@ UDK_3_0_0 { rtl_locale_setDefault; rtl_str_getLength; rtl_str_compare; + rtl_str_compare_Numeric; rtl_str_compare_WithLength; rtl_str_shortenedCompare_WithLength; rtl_str_reverseCompare_WithLength; @@ -251,6 +252,7 @@ UDK_3_0_0 { rtl_stringbuffer_newFromStr_WithLength; rtl_stringbuffer_newFromStringBuffer; rtl_ustr_compare; + rtl_ustr_compare_Numeric; rtl_ustr_compare_WithLength; rtl_ustr_shortenedCompare_WithLength; rtl_ustr_reverseCompare_WithLength;