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
22namespace rtl
23{
24#ifdef RTL_STRING_UNITTEST
25#undef rtl
26#endif
27
28/*
29Implementation of efficient string concatenation.
30
31The 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
38As 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
45Helper class for converting a given type to a string representation.
46*/
47template< typename T >
48struct 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
62inline
63char* addDataHelper( char* buffer, const char* data, int length )
64 {
65 memcpy( buffer, data, length );
66 return buffer + length;
67 }
68
69inline
70sal_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
76inline
77sal_Unicode* addDataLiteral( sal_Unicode* buffer, const char* data, int length )
78 {
79 while( length-- > 0 )
80 *buffer++ = *data++;
81 return buffer;
82 }
83
84inline
85char* addDataCString( char* buffer, const char* str )
86 {
87 while( *str != '\0' )
88 *buffer++ = *str++;
89 return buffer;
90 }
91
92inline
93sal_Unicode* addDataUString( sal_Unicode* buffer, const sal_Unicode* str )
94 {
95 while( *str != '\0' )
96 *buffer++ = *str++;
97 return buffer;
98 }
99
100template<>
101struct 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
111template<>
112struct 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
122template< int N >
123struct 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
134template< int N >
135struct 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
147Objects returned by operator+, instead of OString. These objects (possibly recursively) keep a representation of the whole
148concatenation operation.
149*/
150template< typename T1, typename T2 >
151struct 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
168Objects returned by operator+, instead of OUString. These objects (possibly recursively) keep a representation of the whole
169concatenation operation.
170*/
171template< typename T1, typename T2 >
172struct 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
183template< typename T1, typename T2 >
184struct 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
192template< typename T1, typename T2 >
193struct 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
201template< typename T1, typename T2 >
202inline
203SAL_WARN_UNUSED_RESULT
204typename 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
210template< typename T, int N >
211inline
212SAL_WARN_UNUSED_RESULT
213typename 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
218template< typename T, int N >
219inline
220SAL_WARN_UNUSED_RESULT
221typename 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
226template< typename T, int N >
227inline
228SAL_WARN_UNUSED_RESULT
229typename 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
234template< typename T, int N >
235inline
236SAL_WARN_UNUSED_RESULT
237typename 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
242template< typename T1, typename T2 >
243inline
244typename 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
249template< typename T1, typename T2 >
250inline
251typename 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
256template< typename T1, typename T2 >
257inline
258typename 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.
266struct StringConcatInvalid
267 {
268 template< typename T >
269 StringConcatInvalid( const T& ) {}
270 };
271template< typename T >
272inline
273int 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