1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #ifndef INCLUDED_RTL_STRINGCONCAT_HXX
11 #define INCLUDED_RTL_STRINGCONCAT_HXX
13 // This file is only included from LIBO_INTERNAL_ONLY
15 #include "rtl/stringutils.hxx"
16 #include "rtl/string.h"
17 #include "rtl/ustring.h"
21 #include <string_view>
22 #include <type_traits>
27 #if defined RTL_STRING_UNITTEST_CONCAT
28 extern bool rtl_string_unittest_invalid_concat
;
31 #ifdef RTL_STRING_UNITTEST
32 #define rtl rtlunittest
36 #ifdef RTL_STRING_UNITTEST
41 Implementation of efficient string concatenation.
43 The whole system is built around two basic template classes:
44 - ToStringHelper< T > - for each T it can give the length of the resulting string representation and can write
45 this string representation to a buffer
46 - O(U)StringConcat< T1, T2 > - operator+ now, instead of creating O(U)String object, returns only this helper object,
47 that keeps a reference to both operator+ operands; only when converted to O(U)String it will actually create
48 the resulting string object using ToStringHelper, creating directly the resulting object without any string
50 As all the code is inline methods, it allows for extensive optimization and will usually result in very effective code
51 (even surpassing strlen/strcat and equalling handwritten), while allowing for very easy and intuitive syntax.
57 Helper class for converting a given type to a string representation.
59 template< typename T
>
62 /// Return length of the string representation of the given object (if not known exactly, it needs to be the maximum).
63 static std::size_t length( const T
& );
64 /// Add 8-bit representation of the given object to the given buffer and return position right after the added data.
65 static char* addData( char* buffer
, const T
& ) SAL_RETURNS_NONNULL
;
66 /// Add Unicode representation of the given object to the given buffer and return position right after the added data.
67 static sal_Unicode
* addData( sal_Unicode
* buffer
, const T
& ) SAL_RETURNS_NONNULL
;
68 /// If true, T can be used in concatenation resulting in OString.
69 static const bool allowOStringConcat
= false;
70 /// If true, T can be used in concatenation resulting in OUString.
71 static const bool allowOUStringConcat
= false;
75 char* addDataHelper( char* buffer
, const char* data
, std::size_t length
)
77 memcpy( buffer
, data
, length
);
78 return buffer
+ length
;
82 sal_Unicode
* addDataHelper( sal_Unicode
* buffer
, const sal_Unicode
* data
, std::size_t length
)
84 memcpy( buffer
, data
, length
* sizeof( sal_Unicode
));
85 return buffer
+ length
;
89 sal_Unicode
* addDataLiteral( sal_Unicode
* buffer
, const char* data
, std::size_t length
)
91 for( std::size_t i
= 0; i
!= length
; ++i
)
97 char* addDataCString( char* buffer
, const char* str
)
105 sal_Unicode
* addDataUString( sal_Unicode
* buffer
, const sal_Unicode
* str
)
107 while( *str
!= '\0' )
113 struct ToStringHelper
< const char* >
115 static std::size_t length( const char* str
) {
116 return str
? strlen( str
) : 0;
118 static char* addData( char* buffer
, const char* str
) {
119 return str
? addDataCString( buffer
, str
) : buffer
;
121 static const bool allowOStringConcat
= true;
122 static const bool allowOUStringConcat
= false;
126 struct ToStringHelper
< char* > : public ToStringHelper
< const char* > {};
128 template< std::size_t N
>
129 struct ToStringHelper
< char[ N
] >
131 static std::size_t length( const char str
[ N
] ) {
132 return strlen( str
);
134 static char* addData( char* buffer
, const char str
[ N
] ) { return addDataCString( buffer
, str
); }
135 static const bool allowOStringConcat
= true;
136 static const bool allowOUStringConcat
= false;
139 template< std::size_t N
>
140 struct ToStringHelper
< const char[ N
] >
142 static std::size_t length( const char str
[ N
] ) { (void)str
; assert( strlen( str
) == N
- 1 ); return N
- 1; }
143 static char* addData( char* buffer
, const char str
[ N
] ) { return addDataHelper( buffer
, str
, N
- 1 ); }
144 static sal_Unicode
* addData( sal_Unicode
* buffer
, const char str
[ N
] ) { return addDataLiteral( buffer
, str
, N
- 1 ); }
145 static const bool allowOStringConcat
= true;
146 static const bool allowOUStringConcat
= true;
150 struct ToStringHelper
<OStringChar
>
152 static std::size_t length(OStringChar
) { return 1; }
153 static char* addData(char* buffer
, OStringChar data
)
154 { return addDataHelper(buffer
, &data
.c
, 1); }
155 static bool const allowOStringConcat
= true;
156 static bool const allowOUStringConcat
= false;
160 struct ToStringHelper
< const sal_Unicode
* >
162 static std::size_t length( const sal_Unicode
* str
) {
163 return str
? std::char_traits
<char16_t
>::length( str
) : 0;
165 static sal_Unicode
* addData( sal_Unicode
* buffer
, const sal_Unicode
* str
) {
166 return str
? addDataUString( buffer
, str
) : buffer
;
168 static const bool allowOStringConcat
= false;
169 static const bool allowOUStringConcat
= true;
173 struct ToStringHelper
< sal_Unicode
* > : public ToStringHelper
< const sal_Unicode
* > {};
175 template<std::size_t N
>
176 struct ToStringHelper
<sal_Unicode
[ N
]>
178 static std::size_t length( const sal_Unicode str
[ N
] ) {
179 return std::char_traits
<char16_t
>::length( str
);
181 static sal_Unicode
* addData(sal_Unicode
* buffer
, sal_Unicode
const str
[N
])
182 { return addDataHelper(buffer
, str
, N
- 1); }
183 static bool const allowOStringConcat
= false;
184 static bool const allowOUStringConcat
= true;
187 template<std::size_t N
>
188 struct ToStringHelper
<sal_Unicode
const[N
]>
190 static std::size_t length( const sal_Unicode str
[ N
] ) { (void)str
; assert( std::char_traits
<char16_t
>::length( str
) == N
- 1 ); return N
- 1; }
191 static sal_Unicode
* addData(sal_Unicode
* buffer
, sal_Unicode
const str
[N
])
192 { return addDataHelper(buffer
, str
, N
- 1); }
193 static bool const allowOStringConcat
= false;
194 static bool const allowOUStringConcat
= true;
198 struct ToStringHelper
<OUStringChar_
>
200 static std::size_t length(OUStringChar_
) { return 1; }
201 static sal_Unicode
* addData(sal_Unicode
* buffer
, OUStringChar_ literal
)
202 { return addDataHelper(buffer
, &literal
.c
, 1); }
203 static bool const allowOStringConcat
= false;
204 static bool const allowOUStringConcat
= true;
210 Objects returned by operator+, instead of OString. These objects (possibly recursively) keep a representation of the whole
211 concatenation operation.
213 If you get a build error related to this class, you most probably need to explicitly convert the result of a string
214 concatenation to OString.
216 template< typename T1
, typename T2
>
220 OStringConcat( const T1
& left_
, const T2
& right_
) : left( left_
), right( right_
) {}
221 std::size_t length() const { return ToStringHelper
< T1
>::length( left
) + ToStringHelper
< T2
>::length( right
); }
222 char* addData( char* buffer
) const SAL_RETURNS_NONNULL
{ return ToStringHelper
< T2
>::addData( ToStringHelper
< T1
>::addData( buffer
, left
), right
); }
223 // NOTE here could be functions that would forward to the "real" temporary OString. Note however that e.g. getStr()
224 // is not so simple, as the OString temporary must live long enough (i.e. can't be created here in a function, a wrapper
225 // temporary object containing it must be returned instead).
234 Objects returned by operator+, instead of OUString. These objects (possibly recursively) keep a representation of the whole
235 concatenation operation.
237 If you get a build error related to this class, you most probably need to explicitly convert the result of a string
238 concatenation to OUString.
240 template< typename T1
, typename T2
>
241 struct OUStringConcat
244 OUStringConcat( const T1
& left_
, const T2
& right_
) : left( left_
), right( right_
) {}
245 std::size_t length() const { return ToStringHelper
< T1
>::length( left
) + ToStringHelper
< T2
>::length( right
); }
246 sal_Unicode
* addData( sal_Unicode
* buffer
) const SAL_RETURNS_NONNULL
{ return ToStringHelper
< T2
>::addData( ToStringHelper
< T1
>::addData( buffer
, left
), right
); }
252 template< typename T1
, typename T2
>
253 struct ToStringHelper
< OStringConcat
< T1
, T2
> >
255 static std::size_t length( const OStringConcat
< T1
, T2
>& c
) { return c
.length(); }
256 static char* addData( char* buffer
, const OStringConcat
< T1
, T2
>& c
) SAL_RETURNS_NONNULL
{ return c
.addData( buffer
); }
257 static const bool allowOStringConcat
= ToStringHelper
< T1
>::allowOStringConcat
&& ToStringHelper
< T2
>::allowOStringConcat
;
258 static const bool allowOUStringConcat
= false;
261 template< typename T1
, typename T2
>
262 struct ToStringHelper
< OUStringConcat
< T1
, T2
> >
264 static std::size_t length( const OUStringConcat
< T1
, T2
>& c
) { return c
.length(); }
265 static sal_Unicode
* addData( sal_Unicode
* buffer
, const OUStringConcat
< T1
, T2
>& c
) SAL_RETURNS_NONNULL
{ return c
.addData( buffer
); }
266 static const bool allowOStringConcat
= false;
267 static const bool allowOUStringConcat
= ToStringHelper
< T1
>::allowOUStringConcat
&& ToStringHelper
< T2
>::allowOUStringConcat
;
270 template< typename T1
, typename T2
>
273 typename
std::enable_if_t
< ToStringHelper
< T1
>::allowOStringConcat
&& ToStringHelper
< T2
>::allowOStringConcat
, OStringConcat
< T1
, T2
> > operator+( const T1
& left
, const T2
& right
)
275 return OStringConcat
< T1
, T2
>( left
, right
);
278 // char[N] and const char[N] need to be done explicitly, otherwise the compiler likes to treat them the same way for some reason
279 template< typename T
, std::size_t N
>
282 typename
std::enable_if_t
< ToStringHelper
< T
>::allowOStringConcat
, OStringConcat
< T
, const char[ N
] > > operator+( const T
& left
, const char (&right
)[ N
] )
284 return OStringConcat
< T
, const char[ N
] >( left
, right
);
287 template< typename T
, std::size_t N
>
290 typename
std::enable_if_t
< ToStringHelper
< T
>::allowOStringConcat
, OStringConcat
< const char[ N
], T
> > operator+( const char (&left
)[ N
], const T
& right
)
292 return OStringConcat
< const char[ N
], T
>( left
, right
);
295 template< typename T
, std::size_t N
>
298 typename
std::enable_if_t
< ToStringHelper
< T
>::allowOStringConcat
, OStringConcat
< T
, char[ N
] > > operator+( const T
& left
, char (&right
)[ N
] )
300 return OStringConcat
< T
, char[ N
] >( left
, right
);
303 template< typename T
, std::size_t N
>
306 typename
std::enable_if_t
< ToStringHelper
< T
>::allowOStringConcat
, OStringConcat
< char[ N
], T
> > operator+( char (&left
)[ N
], const T
& right
)
308 return OStringConcat
< char[ N
], T
>( left
, right
);
311 template< typename T1
, typename T2
>
314 typename
std::enable_if_t
< ToStringHelper
< T1
>::allowOUStringConcat
&& ToStringHelper
< T2
>::allowOUStringConcat
, OUStringConcat
< T1
, T2
> > operator+( const T1
& left
, const T2
& right
)
316 return OUStringConcat
< T1
, T2
>( left
, right
);
319 template< typename T1
, typename T2
>
322 typename
std::enable_if_t
< ToStringHelper
< T1
>::allowOUStringConcat
&& ToStringHelper
< T2
>::allowOUStringConcat
&& libreoffice_internal::ConstCharArrayDetector
< T1
, void >::ok
, OUStringConcat
< T1
, T2
> > operator+( T1
& left
, const T2
& right
)
324 return OUStringConcat
< T1
, T2
>( left
, right
);
327 template< typename T1
, typename T2
>
330 typename
std::enable_if_t
< ToStringHelper
< T1
>::allowOUStringConcat
&& ToStringHelper
< T2
>::allowOUStringConcat
&& libreoffice_internal::ConstCharArrayDetector
< T2
, void >::ok
, OUStringConcat
< T1
, T2
> > operator+( const T1
& left
, T2
& right
)
332 return OUStringConcat
< T1
, T2
>( left
, right
);
335 #ifdef RTL_STRING_UNITTEST_CONCAT
336 // Special overload to catch the remaining invalid combinations. The helper struct must
337 // be used to make this operator+ overload a worse choice than all the existing overloads above.
338 struct StringConcatInvalid
340 template< typename T
>
341 StringConcatInvalid( const T
& ) {}
343 template< typename T
>
345 int operator+( const StringConcatInvalid
&, const T
& )
347 rtl_string_unittest_invalid_concat
= true;
348 return 0; // doesn't matter
355 Objects returned by OString::number(), instead of OString. These objects keep a representation of the number() operation.
357 If you get a build error related to this class, you most probably need to explicitly convert the result of calling
358 OString::number() to OString.
360 template< typename T
>
361 struct OStringNumber
;
363 template <typename Number
, std::size_t nBufSize
> struct OStringNumberBase
365 using number_t
= Number
;
366 // OString::number(value).getStr() is very common (writing xml code, ...),
367 // so implement that one also here, to avoid having to explicitly to convert
368 // to OString in all such places
369 const char * getStr() const SAL_RETURNS_NONNULL
{ return buf
; }
370 OStringNumber
<number_t
>&& toAsciiUpperCase()
372 rtl_str_toAsciiUpperCase_WithLength(buf
, length
);
373 return std::move(*static_cast<OStringNumber
<number_t
>*>(this));
375 operator std::string_view() const { return std::string_view(buf
, length
); }
381 struct OStringNumber
< int >
382 : public OStringNumberBase
<int, RTL_STR_MAX_VALUEOFINT32
>
384 OStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_str_valueOfInt32(buf
, i
, radix
); }
388 struct OStringNumber
< long long >
389 : public OStringNumberBase
<long long, RTL_STR_MAX_VALUEOFINT64
>
391 OStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_str_valueOfInt64(buf
, i
, radix
); }
395 struct OStringNumber
< unsigned long long >
396 : public OStringNumberBase
<unsigned long long, RTL_STR_MAX_VALUEOFUINT64
>
398 OStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_str_valueOfUInt64(buf
, i
, radix
); }
402 struct OStringNumber
< float >
403 : public OStringNumberBase
<float, RTL_STR_MAX_VALUEOFFLOAT
>
405 OStringNumber(number_t f
) { length
= rtl_str_valueOfFloat(buf
, f
); }
409 struct OStringNumber
< double >
410 : public OStringNumberBase
<double, RTL_STR_MAX_VALUEOFDOUBLE
>
412 OStringNumber(number_t d
) { length
= rtl_str_valueOfDouble(buf
, d
); }
415 template< typename T
>
416 struct ToStringHelper
< OStringNumber
< T
> >
418 static std::size_t length( const OStringNumber
< T
>& n
) { return n
.length
; }
419 static char* addData( char* buffer
, const OStringNumber
< T
>& n
) SAL_RETURNS_NONNULL
{ return addDataHelper( buffer
, n
.buf
, n
.length
); }
420 static const bool allowOStringConcat
= true;
421 static const bool allowOUStringConcat
= false;
428 Objects returned by OUString::number(), instead of OUString. These objects keep a representation of the number() operation.
430 If you get a build error related to this class, you most probably need to explicitly convert the result of calling
431 OUString::number() to OUString.
433 template< typename T
>
434 struct OUStringNumber
;
436 template <typename Number
, std::size_t nBufSize
> struct OUStringNumberBase
438 using number_t
= Number
;
439 OUStringNumber
<number_t
>&& toAsciiUpperCase()
441 rtl_ustr_toAsciiUpperCase_WithLength(buf
, length
);
442 return std::move(*static_cast<OUStringNumber
<number_t
>*>(this));
444 operator std::u16string_view() const { return std::u16string_view(buf
, length
); }
445 sal_Unicode buf
[nBufSize
];
450 struct OUStringNumber
< int >
451 : public OUStringNumberBase
<int, RTL_USTR_MAX_VALUEOFINT32
>
453 OUStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_ustr_valueOfInt32(buf
, i
, radix
); }
457 struct OUStringNumber
< long long >
458 : public OUStringNumberBase
<long long, RTL_USTR_MAX_VALUEOFINT64
>
460 OUStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_ustr_valueOfInt64(buf
, i
, radix
); }
464 struct OUStringNumber
< unsigned long long >
465 : public OUStringNumberBase
<unsigned long long, RTL_USTR_MAX_VALUEOFUINT64
>
467 OUStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_ustr_valueOfUInt64(buf
, i
, radix
); }
471 struct OUStringNumber
< float >
472 : public OUStringNumberBase
<float, RTL_USTR_MAX_VALUEOFFLOAT
>
474 OUStringNumber(number_t f
) { length
= rtl_ustr_valueOfFloat(buf
, f
); }
478 struct OUStringNumber
< double >
479 : public OUStringNumberBase
<double, RTL_USTR_MAX_VALUEOFDOUBLE
>
481 OUStringNumber(number_t d
) { length
= rtl_ustr_valueOfDouble(buf
, d
); }
484 template< typename T
>
485 struct ToStringHelper
< OUStringNumber
< T
> >
487 static std::size_t length( const OUStringNumber
< T
>& n
) { return n
.length
; }
488 static sal_Unicode
* addData( sal_Unicode
* buffer
, const OUStringNumber
< T
>& n
) SAL_RETURNS_NONNULL
{ return addDataHelper( buffer
, n
.buf
, n
.length
); }
489 static const bool allowOStringConcat
= false;
490 static const bool allowOUStringConcat
= true;
493 // Abstractions over null-terminated char and sal_Unicode strings that sometimes are needed in
494 // concatenations of multiple such raw strings, as in
496 // char const * s1, s2;
497 // OString s = OStringView(s1) + s2;
499 // (Providing specializations of ToStringHelper<std::string_view> and
500 // ToStringHelper<std::u16string_view> would look dubious, as it would give meaning to expressions
503 // std::string_view(s1) + s2
505 // that do not involve any user-defined types.)
509 explicit OStringView(char const * s
): view_(s
) {}
510 explicit OStringView(char const * s
, size_t len
): view_(s
, len
) {}
512 std::size_t length() const { return view_
.length(); }
514 char const * data() const { return view_
.data(); }
517 std::string_view view_
;
521 struct ToStringHelper
< OStringView
>
523 static std::size_t length( const OStringView
& v
) { return v
.length(); }
524 static char* addData( char* buffer
, const OStringView
& v
) SAL_RETURNS_NONNULL
{ return addDataHelper( buffer
, v
.data(), v
.length() ); }
525 static const bool allowOStringConcat
= true;
526 static const bool allowOUStringConcat
= false;
531 explicit OUStringView(sal_Unicode
const * s
): view_(s
) {}
532 explicit OUStringView(sal_Unicode
const * s
, size_t len
): view_(s
, len
) {}
534 std::size_t length() const { return view_
.length(); }
536 sal_Unicode
const * data() const { return view_
.data(); }
539 std::u16string_view view_
;
543 struct ToStringHelper
< OUStringView
>
545 static std::size_t length( const OUStringView
& v
) { return v
.length(); }
546 static sal_Unicode
* addData( sal_Unicode
* buffer
, const OUStringView
& v
) SAL_RETURNS_NONNULL
{ return addDataHelper( buffer
, v
.data(), v
.length() ); }
547 static const bool allowOStringConcat
= false;
548 static const bool allowOUStringConcat
= true;