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 INCLUDED_RTL_STRINGCONCAT_HXX |
11 | #define INCLUDED_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 | char* addDataHelper( char* buffer, const char* data, int length ) |
64 | { |
65 | memcpy( buffer, data, length ); |
66 | return buffer + length; |
67 | } |
68 | |
69 | inline |
70 | sal_Unicode* addDataHelper( sal_Unicode* buffer, const sal_Unicode* data, int length ) |
71 | { |
72 | memcpy( buffer, data, length * sizeof( sal_Unicode )); |
73 | return buffer + length; |
74 | } |
75 | |
76 | inline |
77 | sal_Unicode* addDataLiteral( sal_Unicode* buffer, const char* data, int length ) |
78 | { |
79 | while( length-- > 0 ) |
80 | *buffer++ = *data++; |
81 | return buffer; |
82 | } |
83 | |
84 | inline |
85 | char* addDataCString( char* buffer, const char* str ) |
86 | { |
87 | while( *str != '\0' ) |
88 | *buffer++ = *str++; |
89 | 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 | static int length( const char* str ) { |
104 | return sal::static_int_cast<int>(strlen( str )); |
105 | } |
106 | 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 | static int length( const char* str ) { |
115 | return sal::static_int_cast<int>(strlen( str )); |
116 | } |
117 | 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 | static int length( const char str[ N ] ) { |
126 | return sal::static_int_cast<int>(strlen( str )); |
127 | } |
128 | 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 | static int length( const char str[ N ] ) { (void)str; assert( strlen( str ) == N - 1 ); return N - 1; } |
138 | static char* addData( char* buffer, const char str[ N ] ) { return addDataHelper( buffer, str, N - 1 ); } |
139 | 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 | OStringConcat( const T1& left_, const T2& right_ ) : left( left_ ), right( right_ ) {} |
155 | int length() const { return ToStringHelper< T1 >::length( left ) + ToStringHelper< T2 >::length( right ); } |
156 | 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 | OUStringConcat( const T1& left_, const T2& right_ ) : left( left_ ), right( right_ ) {} |
176 | int length() const { return ToStringHelper< T1 >::length( left ) + ToStringHelper< T2 >::length( right ); } |
177 | 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 | static int length( const OStringConcat< T1, T2 >& c ) { return c.length(); } |
187 | 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 | static int length( const OUStringConcat< T1, T2 >& c ) { return c.length(); } |
196 | 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 | typename internal::Enable< OStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOStringConcat && ToStringHelper< T2 >::allowOStringConcat >::Type operator+( const T1& left, const T2& right ) |
205 | { |
206 | 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 | typename internal::Enable< OStringConcat< T, const char[ N ] >, ToStringHelper< T >::allowOStringConcat >::Type operator+( const T& left, const char (&right)[ N ] ) |
214 | { |
215 | return OStringConcat< T, const char[ N ] >( left, right ); |
216 | } |
217 | |
218 | template< typename T, int N > |
219 | inline |
220 | SAL_WARN_UNUSED_RESULT |
221 | typename internal::Enable< OStringConcat< const char[ N ], T >, ToStringHelper< T >::allowOStringConcat >::Type operator+( const char (&left)[ N ], const T& right ) |
222 | { |
223 | return OStringConcat< const char[ N ], T >( left, right ); |
224 | } |
225 | |
226 | template< typename T, int N > |
227 | inline |
228 | SAL_WARN_UNUSED_RESULT |
229 | typename internal::Enable< OStringConcat< T, char[ N ] >, ToStringHelper< T >::allowOStringConcat >::Type operator+( const T& left, char (&right)[ N ] ) |
230 | { |
231 | 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 | typename internal::Enable< OUStringConcat< T1, T2 >, ToStringHelper< T1 >::allowOUStringConcat && ToStringHelper< T2 >::allowOUStringConcat >::Type operator+( const T1& left, const T2& right ) |
245 | { |
246 | return OUStringConcat< T1, T2 >( left, right ); |
247 | } |
248 | |
249 | template< typename T1, typename T2 > |
250 | inline |
251 | 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 | return OUStringConcat< T1, T2 >( left, right ); |
254 | } |
255 | |
256 | template< typename T1, typename T2 > |
257 | inline |
258 | 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 | 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 | StringConcatInvalid( const T& ) {} |
270 | }; |
271 | template< typename T > |
272 | inline |
273 | int operator+( const StringConcatInvalid&, const T& ) |
274 | { |
275 | rtl_string_unittest_invalid_concat = true; |
276 | return 0; // doesn't matter |
277 | } |
278 | #endif |
279 | |
280 | } // namespace |
281 | |
282 | #endif |
283 | |
284 | #endif |
285 | |