summaryrefslogtreecommitdiff
path: root/sal
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@suse.cz>2012-12-02 22:29:21 +0100
committerLuboš Luňák <l.lunak@suse.cz>2012-12-03 18:04:23 +0100
commitd87f5d30879aca73fff271c254589fc41a91fdd0 (patch)
treed3d736178035c1d590f0642cd031e1f6d4b3fd17 /sal
parent86a53cea0f9066d347cf802f4470ebaef9a5434a (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.mk2
-rw-r--r--sal/Package_inc.mk1
-rw-r--r--sal/inc/rtl/strbuf.hxx30
-rw-r--r--sal/inc/rtl/string.hxx66
-rw-r--r--sal/inc/rtl/stringconcat.hxx261
-rw-r--r--sal/inc/rtl/stringutils.hxx26
-rw-r--r--sal/inc/rtl/ustrbuf.hxx29
-rw-r--r--sal/inc/rtl/ustring.hxx70
-rw-r--r--sal/qa/rtl/strings/test_ostring_concat.cxx79
-rw-r--r--sal/qa/rtl/strings/test_oustring_concat.cxx68
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: */