diff options
author | Luboš Luňák <l.lunak@suse.cz> | 2012-12-02 22:29:21 +0100 |
---|---|---|
committer | Luboš Luňák <l.lunak@suse.cz> | 2012-12-03 18:04:23 +0100 |
commit | d87f5d30879aca73fff271c254589fc41a91fdd0 (patch) | |
tree | d3d736178035c1d590f0642cd031e1f6d4b3fd17 /sal | |
parent | 86a53cea0f9066d347cf802f4470ebaef9a5434a (diff) |
support for fast O(U)String concatenation using operator+
Operator+ now, instead of requiring O(U)String operands and returning
another O(U)String object, keeps a track of the whole concatenation
operation using temporary O(U)StringConcat objects and performs
the whole operation only at the very end.
Change-Id: I94b3348300a137498514d26e96459c1698328520
Diffstat (limited to 'sal')
-rw-r--r-- | sal/CppunitTest_sal_rtl_strings.mk | 2 | ||||
-rw-r--r-- | sal/Package_inc.mk | 1 | ||||
-rw-r--r-- | sal/inc/rtl/strbuf.hxx | 30 | ||||
-rw-r--r-- | sal/inc/rtl/string.hxx | 66 | ||||
-rw-r--r-- | sal/inc/rtl/stringconcat.hxx | 261 | ||||
-rw-r--r-- | sal/inc/rtl/stringutils.hxx | 26 | ||||
-rw-r--r-- | sal/inc/rtl/ustrbuf.hxx | 29 | ||||
-rw-r--r-- | sal/inc/rtl/ustring.hxx | 70 | ||||
-rw-r--r-- | sal/qa/rtl/strings/test_ostring_concat.cxx | 79 | ||||
-rw-r--r-- | sal/qa/rtl/strings/test_oustring_concat.cxx | 68 |
10 files changed, 632 insertions, 0 deletions
diff --git a/sal/CppunitTest_sal_rtl_strings.mk b/sal/CppunitTest_sal_rtl_strings.mk index 4d4f59828dab..a30bf70d30eb 100644 --- a/sal/CppunitTest_sal_rtl_strings.mk +++ b/sal/CppunitTest_sal_rtl_strings.mk @@ -29,8 +29,10 @@ $(eval $(call gb_CppunitTest_CppunitTest,sal_rtl_strings)) $(eval $(call gb_CppunitTest_add_exception_objects,sal_rtl_strings,\ sal/qa/rtl/strings/test_strings_replace \ + sal/qa/rtl/strings/test_ostring_concat \ sal/qa/rtl/strings/test_ostring_stringliterals \ sal/qa/rtl/strings/test_oustring_compare \ + sal/qa/rtl/strings/test_oustring_concat \ sal/qa/rtl/strings/test_oustring_convert \ sal/qa/rtl/strings/test_oustring_endswith \ sal/qa/rtl/strings/test_oustring_noadditional \ diff --git a/sal/Package_inc.mk b/sal/Package_inc.mk index dff02297abbd..bf17e5fead0c 100644 --- a/sal/Package_inc.mk +++ b/sal/Package_inc.mk @@ -91,6 +91,7 @@ $(eval $(call gb_Package_add_file,sal_inc,inc/rtl/strbuf.h,rtl/strbuf.h)) $(eval $(call gb_Package_add_file,sal_inc,inc/rtl/strbuf.hxx,rtl/strbuf.hxx)) $(eval $(call gb_Package_add_file,sal_inc,inc/rtl/string.h,rtl/string.h)) $(eval $(call gb_Package_add_file,sal_inc,inc/rtl/string.hxx,rtl/string.hxx)) +$(eval $(call gb_Package_add_file,sal_inc,inc/rtl/stringconcat.hxx,rtl/stringconcat.hxx)) $(eval $(call gb_Package_add_file,sal_inc,inc/rtl/stringutils.hxx,rtl/stringutils.hxx)) $(eval $(call gb_Package_add_file,sal_inc,inc/rtl/tencinfo.h,rtl/tencinfo.h)) $(eval $(call gb_Package_add_file,sal_inc,inc/rtl/textcvt.h,rtl/textcvt.h)) diff --git a/sal/inc/rtl/strbuf.hxx b/sal/inc/rtl/strbuf.hxx index 7bde9e79b152..945d1d2b179a 100644 --- a/sal/inc/rtl/strbuf.hxx +++ b/sal/inc/rtl/strbuf.hxx @@ -28,6 +28,10 @@ #include <rtl/string.hxx> #include <rtl/stringutils.hxx> +#ifdef RTL_FAST_STRING +#include <rtl/stringconcat.hxx> +#endif + #ifdef __cplusplus // The unittest uses slightly different code to help check that the proper @@ -218,6 +222,20 @@ public: rtl_stringbuffer_newFromStr_WithLength( &pData, value, length ); } +#ifdef RTL_FAST_STRING + template< typename T1, typename T2 > + OStringBuffer( const OStringConcat< T1, T2 >& c ) + { + const int l = c.length(); + rtl_String* buffer = NULL; + rtl_string_new_WithLength( &buffer, l ); + char* end = c.addData( buffer->buffer ); + buffer->length = end - buffer->buffer; + pData = buffer; + nCapacity = l + 16; + } +#endif + /** Assign to this a copy of value. */ OStringBuffer& operator = ( const OStringBuffer& value ) @@ -830,6 +848,18 @@ private: sal_Int32 nCapacity; }; +#ifdef RTL_FAST_STRING +template<> +struct ToStringHelper< OStringBuffer > + { + static int length( const OStringBuffer& s ) { return s.getLength(); } + static char* addData( char* buffer, const OStringBuffer& s ) { return addDataHelper( buffer, s.getStr(), s.getLength()); } + static const bool allowOStringConcat = true; + static const bool allowOUStringConcat = false; + }; +#endif + + } #ifdef RTL_STRING_UNITTEST diff --git a/sal/inc/rtl/string.hxx b/sal/inc/rtl/string.hxx index 4e550209d22b..a2cee5153d2f 100644 --- a/sal/inc/rtl/string.hxx +++ b/sal/inc/rtl/string.hxx @@ -30,6 +30,10 @@ #include <rtl/string.h> #include <rtl/stringutils.hxx> +#ifdef RTL_FAST_STRING +#include <rtl/stringconcat.hxx> +#endif + #include "sal/log.hxx" #if !defined EXCEPTIONS_OFF @@ -250,6 +254,19 @@ public: } } +#ifdef RTL_FAST_STRING + template< typename T1, typename T2 > + OString( const OStringConcat< T1, T2 >& c ) + { + const int l = c.length(); + rtl_String* buffer = NULL; + rtl_string_new_WithLength( &buffer, l ); + char* end = c.addData( buffer->buffer ); + buffer->length = end - buffer->buffer; + pData = buffer; + } +#endif + /** Release the string data. */ @@ -1063,10 +1080,12 @@ public: return OString( pNew, (DO_NOT_ACQUIRE*)0 ); } +#ifndef RTL_FAST_STRING friend OString operator+( const OString & str1, const OString & str2 ) SAL_THROW(()) { return str1.concat( str2 ); } +#endif /** Returns a new string resulting from replacing n = count characters @@ -1435,6 +1454,52 @@ public: /* ======================================================================= */ +#ifdef RTL_FAST_STRING +/** +A simple wrapper around string literal. It is usually not necessary to use, can +be mostly used to force OString operator+ working with operands that otherwise would +not trigger it. + +This class is not part of public API and is meant to be used only in LibreOffice code. +@since LibreOffice 4.0 +*/ +struct SAL_WARN_UNUSED OStringLiteral +{ + template< int N > + OStringLiteral( const char (&str)[ N ] ) : size( N - 1 ), data( str ) {} + int size; + const char* data; +}; + +template<> +struct ToStringHelper< OString > + { + static int length( const OString& s ) { return s.getLength(); } + static char* addData( char* buffer, const OString& s ) { return addDataHelper( buffer, s.getStr(), s.getLength()); } + static const bool allowOStringConcat = true; + static const bool allowOUStringConcat = false; + }; + +template<> +struct ToStringHelper< OStringLiteral > + { + static int length( const OStringLiteral& str ) { return str.size; } + static char* addData( char* buffer, const OStringLiteral& str ) { return addDataHelper( buffer, str.data, str.size ); } + static const bool allowOStringConcat = true; + static const bool allowOUStringConcat = false; + }; + +template< typename charT, typename traits, typename T1, typename T2 > +inline std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const OStringConcat< T1, T2 >& concat) +{ + return stream << OString( concat ); +} +#else +// non-RTL_FAST_CODE needs this to compile +typedef OString OStringLiteral; +#endif + } /* Namespace */ #ifdef RTL_STRING_UNITTEST @@ -1489,6 +1554,7 @@ operator <<( #ifdef RTL_USING using ::rtl::OString; using ::rtl::OStringHash; +using ::rtl::OStringLiteral; #endif #endif /* _RTL_STRING_HXX_ */ diff --git a/sal/inc/rtl/stringconcat.hxx b/sal/inc/rtl/stringconcat.hxx new file mode 100644 index 000000000000..fddf6ed49b6f --- /dev/null +++ b/sal/inc/rtl/stringconcat.hxx @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef RTL_STRINGCONCAT_HXX +#define RTL_STRINGCONCAT_HXX + +#include <rtl/stringutils.hxx> + +#include <string.h> + +#ifdef RTL_FAST_STRING + +#ifdef RTL_STRING_UNITTEST +#define rtl rtlunittest +#endif +namespace rtl +{ +#ifdef RTL_STRING_UNITTEST +#undef rtl +#endif + +/* +Implementation of efficient string concatenation. + +The whole system is built around two basic template classes: +- ToStringHelper< T > - for each T it can give the length of the resulting string representation and can write + this string representation to a buffer +- O(U)StringConcat< T1, T2 > - operator+ now, instead of creating O(U)String object, returns only this helper object, + that keeps a reference to both operator+ operands; only when converted to O(U)String it will actually create + the resulting string object using ToStringHelper, creating directly the resulting object without any string + intermediate objects +As all the code is inline methods, it allows for extensive optimization and will usually result in very effective code +(even surpassing strlen/strcat and equalling handwritten), while allowing for very easy and intuitive syntax. +*/ + +/** +@internal + +Helper class for converting a given type to a string representation. +*/ +template< typename T > +struct ToStringHelper + { + /// Return length of the string representation of the given object (if not known exactly, it needs to be the maximum). + static int length( const T& ); + /// Add 8-bit representation of the given object to the given buffer and return position right after the added data. + static char* addData( char* buffer, const T& ); + /// Add Unicode representation of the given object to the given buffer and return position right after the added data. + static sal_Unicode* addData( sal_Unicode* buffer, const T& ); + /// If true, T can be used in concatenation resulting in OString. + static const bool allowOStringConcat = false; + /// If true, T can be used in concatenation resulting in OUString. + static const bool allowOUStringConcat = false; + }; + +inline +char* addDataHelper( char* buffer, const char* data, int length ) + { + memcpy( buffer, data, length ); + return buffer + length; + } + +inline +sal_Unicode* addDataHelper( sal_Unicode* buffer, const sal_Unicode* data, int length ) + { + memcpy( buffer, data, length * sizeof( sal_Unicode )); + return buffer + length; + } + +inline +sal_Unicode* addDataLiteral( sal_Unicode* buffer, const char* data, int length ) + { + while( length-- > 0 ) + *buffer++ = *data++; + return buffer; + } + +inline +char* addDataCString( char* buffer, const char* str ) + { + while( *str != '\0' ) + *buffer++ = *str++; + return buffer; + } + +inline +sal_Unicode* addDataUString( sal_Unicode* buffer, const sal_Unicode* str ) + { + while( *str != '\0' ) + *buffer++ = *str++; + return buffer; + } + +template<> +struct ToStringHelper< const char* > + { + static int length( const char* str ) { return strlen( str ); } + static char* addData( char* buffer, const char* str ) { return addDataCString( buffer, str ); } + static const bool allowOStringConcat = true; + static const bool allowOUStringConcat = false; + }; + +template<> +struct ToStringHelper< char* > + { + static int length( const char* str ) { return strlen( str ); } + static char* addData( char* buffer, const char* str ) { return addDataCString( buffer, str ); } + static const bool allowOStringConcat = true; + static const bool allowOUStringConcat = false; + }; + +template< int N > +struct ToStringHelper< char[ N ] > + { + static int length( const char str[ N ] ) { return strlen( str ); } + static char* addData( char* buffer, const char str[ N ] ) { return addDataCString( buffer, str ); } + static sal_Unicode* addData( sal_Unicode* buffer, const char str[ N ] ) { return addDataLiteral( buffer, str, N - 1 ); } + static const bool allowOStringConcat = true; + static const bool allowOUStringConcat = false; + }; + +template< int N > +struct ToStringHelper< const char[ N ] > + { + static int length( const char[ N ] ) { return N - 1; } + static char* addData( char* buffer, const char str[ N ] ) { return addDataHelper( buffer, str, N - 1 ); } + static sal_Unicode* addData( sal_Unicode* buffer, const char str[ N ] ) { return addDataLiteral( buffer, str, N - 1 ); } + static const bool allowOStringConcat = true; + static const bool allowOUStringConcat = true; + }; + +/** +@internal + +Objects returned by operator+, instead of OString. These objects (possibly recursively) keep a representation of the whole +concatenation operation. +*/ +template< typename T1, typename T2 > +struct OStringConcat + { + public: + OStringConcat( const T1& left_, const T2& right_ ) : left( left_ ), right( right_ ) {} + int length() const { return ToStringHelper< T1 >::length( left ) + ToStringHelper< T2 >::length( right ); } + char* addData( char* buffer ) const { return ToStringHelper< T2 >::addData( ToStringHelper< T1 >::addData( buffer, left ), right ); } + // NOTE here could be functions that would forward to the "real" temporary OString. Note however that e.g. getStr() + // is not so simple, as the OString temporary must live long enough (i.e. can't be created here in a function, a wrapper + // temporary object containing it must be returned instead). + private: + const T1& left; + const T2& right; + }; + +/** +@internal + +Objects returned by operator+, instead of OUString. These objects (possibly recursively) keep a representation of the whole +concatenation operation. +*/ +template< typename T1, typename T2 > +struct OUStringConcat + { + public: + OUStringConcat( const T1& left_, const T2& right_ ) : left( left_ ), right( right_ ) {} + int length() const { return ToStringHelper< T1 >::length( left ) + ToStringHelper< T2 >::length( right ); } + sal_Unicode* addData( sal_Unicode* buffer ) const { return ToStringHelper< T2 >::addData( ToStringHelper< T1 >::addData( buffer, left ), right ); } + private: + const T1& left; + const T2& right; + }; + +template< typename T1, typename T2 > +struct ToStringHelper< OStringConcat< T1, T2 > > + { + static int length( const OStringConcat< T1, T2 >& c ) { return c.length(); } + static char* addData( char* buffer, const OStringConcat< T1, T2 >& c ) { return c.addData( buffer ); } + static const bool allowOStringConcat = ToStringHelper< T1 >::allowOStringConcat && ToStringHelper< T2 >::allowOStringConcat; + static const bool allowOUStringConcat = false; + }; + +template< typename T1, typename T2 > +struct ToStringHelper< OUStringConcat< T1, T2 > > + { + static int length( const OUStringConcat< T1, T2 >& c ) { return c.length(); } + static sal_Unicode* addData( sal_Unicode* buffer, const OUStringConcat< T1, T2 >& c ) { return c.addData( buffer ); } + static const bool allowOStringConcat = false; + static const bool allowOUStringConcat = ToStringHelper< T1 >::allowOUStringConcat && ToStringHelper< T2 >::allowOUStringConcat; + }; + +template< typename T1, typename T2 > +inline +SAL_WARN_UNUSED_RESULT +typename internal::Enable< OStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOStringConcat && ToStringHelper< T2 >::allowOStringConcat >::Type operator+( const T1& left, const T2& right ) + { + return OStringConcat< T1, T2 >( left, right ); + } + +// char[N] and const char[N] need to be done explicitly, otherwise the compiler likes to treat them the same way for some reason +template< typename T, int N > +inline +SAL_WARN_UNUSED_RESULT +typename internal::Enable< OStringConcat< T, const char[ N ] >, ToStringHelper< T >::allowOStringConcat >::Type operator+( const T& left, const char (&right)[ N ] ) + { + return OStringConcat< T, const char[ N ] >( left, right ); + } + +template< typename T, int N > +inline +SAL_WARN_UNUSED_RESULT +typename internal::Enable< OStringConcat< const char[ N ], T >, ToStringHelper< T >::allowOStringConcat >::Type operator+( const char (&left)[ N ], const T& right ) + { + return OStringConcat< const char[ N ], T >( left, right ); + } + +template< typename T, int N > +inline +SAL_WARN_UNUSED_RESULT +typename internal::Enable< OStringConcat< T, char[ N ] >, ToStringHelper< T >::allowOStringConcat >::Type operator+( const T& left, char (&right)[ N ] ) + { + return OStringConcat< T, char[ N ] >( left, right ); + } + +template< typename T, int N > +inline +SAL_WARN_UNUSED_RESULT +typename internal::Enable< OStringConcat< char[ N ], T >, ToStringHelper< T >::allowOStringConcat >::Type operator+( char (&left)[ N ], const T& right ) + { + return OStringConcat< char[ N ], T >( left, right ); + } + +template< typename T1, typename T2 > +inline +typename internal::Enable< OUStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOUStringConcat && ToStringHelper< T2 >::allowOUStringConcat >::Type operator+( const T1& left, const T2& right ) + { + return OUStringConcat< T1, T2 >( left, right ); + } + +template< typename T1, typename T2 > +inline +typename internal::Enable< OUStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOUStringConcat && ToStringHelper< T2 >::allowOUStringConcat && internal::ConstCharArrayDetector< T1, void >::ok >::Type operator+( T1& left, const T2& right ) + { + return OUStringConcat< T1, T2 >( left, right ); + } + +template< typename T1, typename T2 > +inline +typename internal::Enable< OUStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOUStringConcat && ToStringHelper< T2 >::allowOUStringConcat && internal::ConstCharArrayDetector< T2, void >::ok >::Type operator+( const T1& left, T2& right ) + { + return OUStringConcat< T1, T2 >( left, right ); + } + +} // namespace + +#endif + +#endif diff --git a/sal/inc/rtl/stringutils.hxx b/sal/inc/rtl/stringutils.hxx index 3ed36a758aaa..f0ad4f8765bb 100644 --- a/sal/inc/rtl/stringutils.hxx +++ b/sal/inc/rtl/stringutils.hxx @@ -31,6 +31,16 @@ #include "sal/config.h" +// Manually defining RTL_DISABLE_FAST_STRING allows to force turning fast string concatenation off +// (e.g. for debugging). +#ifndef RTL_DISABLE_FAST_STRING +#ifndef HAVE_SFINAE_ANONYMOUS_BROKEN +// Enable fast string concatenation. +// This feature is not part of public API and is meant to be used only internally by LibreOffice. +#define RTL_FAST_STRING +#endif +#endif + // The unittest uses slightly different code to help check that the proper // calls are made. The class is put into a different namespace to make // sure the compiler generates a different (if generating also non-inline) @@ -46,6 +56,7 @@ namespace rtl #ifdef RTL_STRING_UNITTEST #undef rtl #endif + namespace internal { /* @@ -114,12 +125,14 @@ struct NonConstCharArrayDetector< const char[], T > template< typename T1, typename T2 > struct ConstCharArrayDetector { + static const bool ok = false; }; template< int N, typename T > struct ConstCharArrayDetector< const char[ N ], T > { typedef T Type; static const int size = N; + static const bool ok = true; }; // this one is used to rule out only const char[N] @@ -150,6 +163,19 @@ struct ExceptCharArrayDetector< const char[ N ] > { }; +// SFINAE helper class +template< typename T, bool > +struct Enable + { + }; + +template< typename T > +struct Enable< T, true > + { + typedef T Type; + }; + + } /* Namespace */ } /* Namespace */ diff --git a/sal/inc/rtl/ustrbuf.hxx b/sal/inc/rtl/ustrbuf.hxx index 6338dd1a81cb..7d36356cd404 100644 --- a/sal/inc/rtl/ustrbuf.hxx +++ b/sal/inc/rtl/ustrbuf.hxx @@ -29,6 +29,10 @@ #include <rtl/ustring.hxx> #include <rtl/stringutils.hxx> +#ifdef RTL_FAST_STRING +#include <rtl/stringconcat.hxx> +#endif + // The unittest uses slightly different code to help check that the proper // calls are made. The class is put into a different namespace to make // sure the compiler generates a different (if generating also non-inline) @@ -209,6 +213,20 @@ public: } #endif +#ifdef RTL_FAST_STRING + template< typename T1, typename T2 > + OUStringBuffer( const OUStringConcat< T1, T2 >& c ) + { + const int l = c.length(); + rtl_uString* buffer = NULL; + rtl_uString_new_WithLength( &buffer, l ); // TODO this clears, not necessary + sal_Unicode* end = c.addData( buffer->buffer ); + buffer->length = end - buffer->buffer; + // TODO realloc in case buffer->length is noticeably smaller than l ? + pData = buffer; + nCapacity = l + 16; + } +#endif /** Assign to this a copy of value. */ OUStringBuffer& operator = ( const OUStringBuffer& value ) @@ -1223,6 +1241,17 @@ private: sal_Int32 nCapacity; }; +#ifdef RTL_FAST_STRING +template<> +struct ToStringHelper< OUStringBuffer > + { + static int length( const OUStringBuffer& s ) { return s.getLength(); } + static sal_Unicode* addData( sal_Unicode* buffer, const OUStringBuffer& s ) { return addDataHelper( buffer, s.getStr(), s.getLength()); } + static const bool allowOStringConcat = false; + static const bool allowOUStringConcat = true; + }; +#endif + } #ifdef RTL_STRING_UNITTEST diff --git a/sal/inc/rtl/ustring.hxx b/sal/inc/rtl/ustring.hxx index c5cfcc3f8e02..ec7c29009d55 100644 --- a/sal/inc/rtl/ustring.hxx +++ b/sal/inc/rtl/ustring.hxx @@ -32,6 +32,10 @@ #include <rtl/textenc.h> #include "sal/log.hxx" +#ifdef RTL_FAST_STRING +#include <rtl/stringconcat.hxx> +#endif + #if defined EXCEPTIONS_OFF #include <stdlib.h> #else @@ -315,6 +319,20 @@ public: } } +#ifdef RTL_FAST_STRING + template< typename T1, typename T2 > + OUString( const OUStringConcat< T1, T2 >& c ) + { + const int l = c.length(); + rtl_uString* buffer = NULL; + rtl_uString_new_WithLength( &buffer, l ); // TODO this clears, not necessary + sal_Unicode* end = c.addData( buffer->buffer ); + buffer->length = end - buffer->buffer; + // TODO realloc in case buffer->length is noticeably smaller than l ? + pData = buffer; + } +#endif + /** Release the string data. */ @@ -1400,10 +1418,12 @@ public: return OUString( pNew, (DO_NOT_ACQUIRE*)0 ); } +#ifndef RTL_FAST_STRING friend OUString operator+( const OUString& rStr1, const OUString& rStr2 ) SAL_THROW(()) { return rStr1.concat( rStr2 ); } +#endif /** Returns a new string resulting from replacing n = count characters @@ -2055,10 +2075,59 @@ public: rtl_uString_newFromAscii( &pNew, value ); return OUString( pNew, (DO_NOT_ACQUIRE*)0 ); } + + template< typename T1, typename T2 > + friend struct OUStringConcat; }; /* ======================================================================= */ +#ifdef RTL_FAST_STRING +/** +A simple wrapper around string literal. It is usually not necessary to use, can +be mostly used to force OUString operator+ working with operands that otherwise would +not trigger it. + +This class is not part of public API and is meant to be used only in LibreOffice code. +@since LibreOffice 4.0 +*/ +struct SAL_WARN_UNUSED OUStringLiteral +{ + template< int N > + OUStringLiteral( const char (&str)[ N ] ) : size( N - 1 ), data( str ) {} + int size; + const char* data; +}; + +template<> +struct ToStringHelper< OUString > + { + static int length( const OUString& s ) { return s.getLength(); } + static sal_Unicode* addData( sal_Unicode* buffer, const OUString& s ) { return addDataHelper( buffer, s.getStr(), s.getLength()); } + static const bool allowOStringConcat = false; + static const bool allowOUStringConcat = true; + }; + +template<> +struct ToStringHelper< OUStringLiteral > + { + static int length( const OUStringLiteral& str ) { return str.size; } + static sal_Unicode* addData( sal_Unicode* buffer, const OUStringLiteral& str ) { return addDataLiteral( buffer, str.data, str.size ); } + static const bool allowOStringConcat = false; + static const bool allowOUStringConcat = true; + }; + +template< typename charT, typename traits, typename T1, typename T2 > +inline std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const OUStringConcat< T1, T2 >& concat) +{ + return stream << OUString( concat ); +} +#else +// non-RTL_FAST_CODE needs this to compile +typedef OUString OUStringLiteral; +#endif + } /* Namespace */ #ifdef RTL_STRING_UNITTEST @@ -2183,6 +2252,7 @@ using ::rtl::OUString; using ::rtl::OUStringHash; using ::rtl::OStringToOUString; using ::rtl::OUStringToOString; +using ::rtl::OUStringLiteral; #endif #endif /* _RTL_USTRING_HXX */ diff --git a/sal/qa/rtl/strings/test_ostring_concat.cxx b/sal/qa/rtl/strings/test_ostring_concat.cxx new file mode 100644 index 000000000000..a79b2dad32fb --- /dev/null +++ b/sal/qa/rtl/strings/test_ostring_concat.cxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/types.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +#include <rtl/string.hxx> + +#include <typeinfo> + +using namespace rtl; + +namespace std +{ +template< typename charT, typename traits > std::basic_ostream<charT, traits> & +operator <<( + std::basic_ostream<charT, traits> & stream, const std::type_info& info ) +{ + return stream << info.name(); +} +} // namespace + +namespace test { namespace ostring { + +class StringConcat : public CppUnit::TestFixture +{ +private: + void check(); + +CPPUNIT_TEST_SUITE(StringConcat); +CPPUNIT_TEST(check); +CPPUNIT_TEST_SUITE_END(); +}; + +#ifdef RTL_FAST_STRING +#define TYPES_ASSERT_EQUAL( a, b ) CPPUNIT_ASSERT_EQUAL( a, b ) +#else +#define TYPES_ASSERT_EQUAL( a, b ) +#endif +void test::ostring::StringConcat::check() +{ +// All the extra () are to protect commas againsts being treated as separators of macro arguments. + CPPUNIT_ASSERT_EQUAL( OString( "foobar" ), OString( OString( "foo" ) + OString( "bar" ))); + TYPES_ASSERT_EQUAL(( typeid( OStringConcat< OString, OString > )), typeid( OString( "foo" ) + OString( "bar" ))); + CPPUNIT_ASSERT_EQUAL( OString( "foobar" ), OString( OString( "foo" ) + "bar" )); + TYPES_ASSERT_EQUAL(( typeid( OStringConcat< OString, const char[ 4 ] > )), typeid( OString( "foo" ) + "bar" )); + CPPUNIT_ASSERT_EQUAL( OString( "foobarbaz" ), OString( OString( "foo" ) + "bar" + "baz" )); + TYPES_ASSERT_EQUAL(( typeid( OStringConcat< OStringConcat< OString, const char[ 4 ] >, const char[ 4 ] > )), typeid( OString( "foo" ) + "bar" + "baz" )); + CPPUNIT_ASSERT_EQUAL( OString( "foobar" ), OString( OStringLiteral( "foo" ) + "bar" )); + TYPES_ASSERT_EQUAL(( typeid( OStringConcat< OStringLiteral, const char[ 4 ] > )), typeid( OStringLiteral( "foo" ) + "bar" )); + CPPUNIT_ASSERT_EQUAL( OString( "foobar" ), OString( OStringLiteral( "foo" ) + (const char*)"bar" )); + TYPES_ASSERT_EQUAL(( typeid( OStringConcat< OStringLiteral, const char* > )), typeid( OStringLiteral( "foo" ) + (const char*)"bar" )); + const char d1[] = "xyz"; + char d2[] = "abc"; + const char* d3 = d1; + char* d4 = d2; + CPPUNIT_ASSERT_EQUAL( OString( "fooxyz" ), OString( OString( "foo" ) + d1 )); + TYPES_ASSERT_EQUAL(( typeid( OStringConcat< OString, const char[ 4 ] > )), typeid( OString( "foo" ) + d1 )); + CPPUNIT_ASSERT_EQUAL( OString( "fooabc" ), OString( OString( "foo" ) + d2 )); + TYPES_ASSERT_EQUAL(( typeid( OStringConcat< OString, char[ 4 ] > )), typeid( OString( "foo" ) + d2 )); + CPPUNIT_ASSERT_EQUAL( OString( "fooxyz" ), OString( OString( "foo" ) + d3 )); + TYPES_ASSERT_EQUAL(( typeid( OStringConcat< OString, const char* > )), typeid( OString( "foo" ) + d3 )); + CPPUNIT_ASSERT_EQUAL( OString( "fooabc" ), OString( OString( "foo" ) + d4 )); + TYPES_ASSERT_EQUAL(( typeid( OStringConcat< OString, char* > )), typeid( OString( "foo" ) + d4 )); +} +#undef typeid + +}} // namespace + +CPPUNIT_TEST_SUITE_REGISTRATION(test::ostring::StringConcat); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/qa/rtl/strings/test_oustring_concat.cxx b/sal/qa/rtl/strings/test_oustring_concat.cxx new file mode 100644 index 000000000000..0cc25128a075 --- /dev/null +++ b/sal/qa/rtl/strings/test_oustring_concat.cxx @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/types.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +#include <rtl/ustring.hxx> + +#include <typeinfo> + +using namespace rtl; + +namespace std +{ +template< typename charT, typename traits > std::basic_ostream<charT, traits> & +operator <<( + std::basic_ostream<charT, traits> & stream, const std::type_info& info ) +{ + return stream << info.name(); +} +} // namespace + +namespace test { namespace oustring { + +class StringConcat : public CppUnit::TestFixture +{ +private: + void check(); + +CPPUNIT_TEST_SUITE(StringConcat); +CPPUNIT_TEST(check); +CPPUNIT_TEST_SUITE_END(); +}; + +#ifdef RTL_FAST_STRING +#define TYPES_ASSERT_EQUAL( a, b ) CPPUNIT_ASSERT_EQUAL( a, b ) +#else +#define TYPES_ASSERT_EQUAL( a, b ) +#endif +void test::oustring::StringConcat::check() +{ +// All the extra () are to protect commas againsts being treated as separators of macro arguments. + CPPUNIT_ASSERT_EQUAL( OUString( "foobar" ), OUString( OUString( "foo" ) + OUString( "bar" ))); + TYPES_ASSERT_EQUAL(( typeid( OUStringConcat< OUString, OUString > )), typeid( OUString( "foo" ) + OUString( "bar" ))); + CPPUNIT_ASSERT_EQUAL( OUString( "foobar" ), OUString( OUString( "foo" ) + "bar" )); + TYPES_ASSERT_EQUAL(( typeid( OUStringConcat< OUString, const char[ 4 ] > )), typeid( OUString( "foo" ) + "bar" )); + CPPUNIT_ASSERT_EQUAL( OUString( "foobarbaz" ), OUString( OUString( "foo" ) + "bar" + "baz" )); + TYPES_ASSERT_EQUAL(( typeid( OUStringConcat< OUStringConcat< OUString, const char[ 4 ] >, const char[ 4 ] > )), typeid( OUString( "foo" ) + "bar" + "baz" )); + CPPUNIT_ASSERT_EQUAL( OUString( "foobar" ), OUString( OUStringLiteral( "foo" ) + "bar" )); + TYPES_ASSERT_EQUAL(( typeid( OUStringConcat< OUStringLiteral, const char[ 4 ] > )), typeid( OUStringLiteral( "foo" ) + "bar" )); + const char d1[] = "xyz"; + CPPUNIT_ASSERT_EQUAL( OUString( "fooxyz" ), OUString( OUString( "foo" ) + d1 )); + TYPES_ASSERT_EQUAL(( typeid( OUStringConcat< OUString, const char[ 4 ] > )), typeid( OUString( "foo" ) + d1 )); +} +#undef typeid + +}} // namespace + +CPPUNIT_TEST_SUITE_REGISTRATION(test::oustring::StringConcat); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |