Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #ifndef RTL_STRINGCONCAT_HXX
11 : #define RTL_STRINGCONCAT_HXX
12 :
13 : #include <rtl/stringutils.hxx>
14 :
15 : #include <string.h>
16 :
17 : #ifdef RTL_FAST_STRING
18 :
19 : #ifdef RTL_STRING_UNITTEST
20 : #define rtl rtlunittest
21 : #endif
22 : namespace rtl
23 : {
24 : #ifdef RTL_STRING_UNITTEST
25 : #undef rtl
26 : #endif
27 :
28 : /*
29 : Implementation of efficient string concatenation.
30 :
31 : The whole system is built around two basic template classes:
32 : - ToStringHelper< T > - for each T it can give the length of the resulting string representation and can write
33 : this string representation to a buffer
34 : - O(U)StringConcat< T1, T2 > - operator+ now, instead of creating O(U)String object, returns only this helper object,
35 : that keeps a reference to both operator+ operands; only when converted to O(U)String it will actually create
36 : the resulting string object using ToStringHelper, creating directly the resulting object without any string
37 : intermediate objects
38 : As all the code is inline methods, it allows for extensive optimization and will usually result in very effective code
39 : (even surpassing strlen/strcat and equalling handwritten), while allowing for very easy and intuitive syntax.
40 : */
41 :
42 : /**
43 : @internal
44 :
45 : Helper class for converting a given type to a string representation.
46 : */
47 : template< typename T >
48 : struct ToStringHelper
49 : {
50 : /// Return length of the string representation of the given object (if not known exactly, it needs to be the maximum).
51 : static int length( const T& );
52 : /// Add 8-bit representation of the given object to the given buffer and return position right after the added data.
53 : static char* addData( char* buffer, const T& );
54 : /// Add Unicode representation of the given object to the given buffer and return position right after the added data.
55 : static sal_Unicode* addData( sal_Unicode* buffer, const T& );
56 : /// If true, T can be used in concatenation resulting in OString.
57 : static const bool allowOStringConcat = false;
58 : /// If true, T can be used in concatenation resulting in OUString.
59 : static const bool allowOUStringConcat = false;
60 : };
61 :
62 : inline
63 894513 : char* addDataHelper( char* buffer, const char* data, int length )
64 : {
65 894513 : memcpy( buffer, data, length );
66 894513 : return buffer + length;
67 : }
68 :
69 : inline
70 5366365 : sal_Unicode* addDataHelper( sal_Unicode* buffer, const sal_Unicode* data, int length )
71 : {
72 5366365 : memcpy( buffer, data, length * sizeof( sal_Unicode ));
73 5366365 : return buffer + length;
74 : }
75 :
76 : inline
77 1693762 : sal_Unicode* addDataLiteral( sal_Unicode* buffer, const char* data, int length )
78 : {
79 7243085 : while( length-- > 0 )
80 3855561 : *buffer++ = *data++;
81 1693762 : return buffer;
82 : }
83 :
84 : inline
85 5 : char* addDataCString( char* buffer, const char* str )
86 : {
87 28 : while( *str != '\0' )
88 18 : *buffer++ = *str++;
89 5 : return buffer;
90 : }
91 :
92 : inline
93 : sal_Unicode* addDataUString( sal_Unicode* buffer, const sal_Unicode* str )
94 : {
95 : while( *str != '\0' )
96 : *buffer++ = *str++;
97 : return buffer;
98 : }
99 :
100 : template<>
101 : struct ToStringHelper< const char* >
102 : {
103 3 : static int length( const char* str ) {
104 3 : return sal::static_int_cast<int>(strlen( str ));
105 : }
106 3 : static char* addData( char* buffer, const char* str ) { return addDataCString( buffer, str ); }
107 : static const bool allowOStringConcat = true;
108 : static const bool allowOUStringConcat = false;
109 : };
110 :
111 : template<>
112 : struct ToStringHelper< char* >
113 : {
114 1 : static int length( const char* str ) {
115 1 : return sal::static_int_cast<int>(strlen( str ));
116 : }
117 1 : static char* addData( char* buffer, const char* str ) { return addDataCString( buffer, str ); }
118 : static const bool allowOStringConcat = true;
119 : static const bool allowOUStringConcat = false;
120 : };
121 :
122 : template< int N >
123 : struct ToStringHelper< char[ N ] >
124 : {
125 1 : static int length( const char str[ N ] ) {
126 1 : return sal::static_int_cast<int>(strlen( str ));
127 : }
128 1 : static char* addData( char* buffer, const char str[ N ] ) { return addDataCString( buffer, str ); }
129 : static sal_Unicode* addData( sal_Unicode* buffer, const char str[ N ] ) { return addDataLiteral( buffer, str, N - 1 ); }
130 : static const bool allowOStringConcat = true;
131 : static const bool allowOUStringConcat = false;
132 : };
133 :
134 : template< int N >
135 : struct ToStringHelper< const char[ N ] >
136 : {
137 2114927 : static int length( const char str[ N ] ) { (void)str; assert( strlen( str ) == N - 1 ); return N - 1; }
138 421168 : static char* addData( char* buffer, const char str[ N ] ) { return addDataHelper( buffer, str, N - 1 ); }
139 1693759 : static sal_Unicode* addData( sal_Unicode* buffer, const char str[ N ] ) { return addDataLiteral( buffer, str, N - 1 ); }
140 : static const bool allowOStringConcat = true;
141 : static const bool allowOUStringConcat = true;
142 : };
143 :
144 : /**
145 : @internal
146 :
147 : Objects returned by operator+, instead of OString. These objects (possibly recursively) keep a representation of the whole
148 : concatenation operation.
149 : */
150 : template< typename T1, typename T2 >
151 : struct OStringConcat
152 : {
153 : public:
154 459923 : OStringConcat( const T1& left_, const T2& right_ ) : left( left_ ), right( right_ ) {}
155 459923 : int length() const { return ToStringHelper< T1 >::length( left ) + ToStringHelper< T2 >::length( right ); }
156 459921 : char* addData( char* buffer ) const { return ToStringHelper< T2 >::addData( ToStringHelper< T1 >::addData( buffer, left ), right ); }
157 : // NOTE here could be functions that would forward to the "real" temporary OString. Note however that e.g. getStr()
158 : // is not so simple, as the OString temporary must live long enough (i.e. can't be created here in a function, a wrapper
159 : // temporary object containing it must be returned instead).
160 : private:
161 : const T1& left;
162 : const T2& right;
163 : };
164 :
165 : /**
166 : @internal
167 :
168 : Objects returned by operator+, instead of OUString. These objects (possibly recursively) keep a representation of the whole
169 : concatenation operation.
170 : */
171 : template< typename T1, typename T2 >
172 : struct OUStringConcat
173 : {
174 : public:
175 4415690 : OUStringConcat( const T1& left_, const T2& right_ ) : left( left_ ), right( right_ ) {}
176 4415690 : int length() const { return ToStringHelper< T1 >::length( left ) + ToStringHelper< T2 >::length( right ); }
177 4415687 : sal_Unicode* addData( sal_Unicode* buffer ) const { return ToStringHelper< T2 >::addData( ToStringHelper< T1 >::addData( buffer, left ), right ); }
178 : private:
179 : const T1& left;
180 : const T2& right;
181 : };
182 :
183 : template< typename T1, typename T2 >
184 : struct ToStringHelper< OStringConcat< T1, T2 > >
185 : {
186 25324 : static int length( const OStringConcat< T1, T2 >& c ) { return c.length(); }
187 25324 : static char* addData( char* buffer, const OStringConcat< T1, T2 >& c ) { return c.addData( buffer ); }
188 : static const bool allowOStringConcat = ToStringHelper< T1 >::allowOStringConcat && ToStringHelper< T2 >::allowOStringConcat;
189 : static const bool allowOUStringConcat = false;
190 : };
191 :
192 : template< typename T1, typename T2 >
193 : struct ToStringHelper< OUStringConcat< T1, T2 > >
194 : {
195 1771247 : static int length( const OUStringConcat< T1, T2 >& c ) { return c.length(); }
196 1771247 : static sal_Unicode* addData( sal_Unicode* buffer, const OUStringConcat< T1, T2 >& c ) { return c.addData( buffer ); }
197 : static const bool allowOStringConcat = false;
198 : static const bool allowOUStringConcat = ToStringHelper< T1 >::allowOUStringConcat && ToStringHelper< T2 >::allowOUStringConcat;
199 : };
200 :
201 : template< typename T1, typename T2 >
202 : inline
203 : SAL_WARN_UNUSED_RESULT
204 38754 : typename internal::Enable< OStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOStringConcat && ToStringHelper< T2 >::allowOStringConcat >::Type operator+( const T1& left, const T2& right )
205 : {
206 38754 : return OStringConcat< T1, T2 >( left, right );
207 : }
208 :
209 : // char[N] and const char[N] need to be done explicitly, otherwise the compiler likes to treat them the same way for some reason
210 : template< typename T, int N >
211 : inline
212 : SAL_WARN_UNUSED_RESULT
213 26555 : typename internal::Enable< OStringConcat< T, const char[ N ] >, ToStringHelper< T >::allowOStringConcat >::Type operator+( const T& left, const char (&right)[ N ] )
214 : {
215 26555 : return OStringConcat< T, const char[ N ] >( left, right );
216 : }
217 :
218 : template< typename T, int N >
219 : inline
220 : SAL_WARN_UNUSED_RESULT
221 394613 : typename internal::Enable< OStringConcat< const char[ N ], T >, ToStringHelper< T >::allowOStringConcat >::Type operator+( const char (&left)[ N ], const T& right )
222 : {
223 394613 : return OStringConcat< const char[ N ], T >( left, right );
224 : }
225 :
226 : template< typename T, int N >
227 : inline
228 : SAL_WARN_UNUSED_RESULT
229 1 : typename internal::Enable< OStringConcat< T, char[ N ] >, ToStringHelper< T >::allowOStringConcat >::Type operator+( const T& left, char (&right)[ N ] )
230 : {
231 1 : return OStringConcat< T, char[ N ] >( left, right );
232 : }
233 :
234 : template< typename T, int N >
235 : inline
236 : SAL_WARN_UNUSED_RESULT
237 : typename internal::Enable< OStringConcat< char[ N ], T >, ToStringHelper< T >::allowOStringConcat >::Type operator+( char (&left)[ N ], const T& right )
238 : {
239 : return OStringConcat< char[ N ], T >( left, right );
240 : }
241 :
242 : template< typename T1, typename T2 >
243 : inline
244 2721931 : typename internal::Enable< OUStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOUStringConcat && ToStringHelper< T2 >::allowOUStringConcat >::Type operator+( const T1& left, const T2& right )
245 : {
246 2721931 : return OUStringConcat< T1, T2 >( left, right );
247 : }
248 :
249 : template< typename T1, typename T2 >
250 : inline
251 120013 : typename internal::Enable< OUStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOUStringConcat && ToStringHelper< T2 >::allowOUStringConcat && internal::ConstCharArrayDetector< T1, void >::ok >::Type operator+( T1& left, const T2& right )
252 : {
253 120013 : return OUStringConcat< T1, T2 >( left, right );
254 : }
255 :
256 : template< typename T1, typename T2 >
257 : inline
258 1573746 : typename internal::Enable< OUStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOUStringConcat && ToStringHelper< T2 >::allowOUStringConcat && internal::ConstCharArrayDetector< T2, void >::ok >::Type operator+( const T1& left, T2& right )
259 : {
260 1573746 : return OUStringConcat< T1, T2 >( left, right );
261 : }
262 :
263 : #ifdef RTL_STRING_UNITTEST_CONCAT
264 : // Special overload to catch the remaining invalid combinations. The helper struct must
265 : // be used to make this operator+ overload a worse choice than all the existing overloads above.
266 : struct StringConcatInvalid
267 : {
268 : template< typename T >
269 15 : StringConcatInvalid( const T& ) {}
270 : };
271 : template< typename T >
272 : inline
273 15 : int operator+( const StringConcatInvalid&, const T& )
274 : {
275 15 : rtl_string_unittest_invalid_concat = true;
276 15 : return 0; // doesn't matter
277 : }
278 : #endif
279 :
280 : } // namespace
281 :
282 : #endif
283 :
284 : #endif
|