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"
23 #include <string_view>
24 #include <type_traits>
29 #if defined RTL_STRING_UNITTEST_CONCAT
30 extern bool rtl_string_unittest_invalid_concat
;
33 #ifdef RTL_STRING_UNITTEST
34 #define rtl rtlunittest
38 #ifdef RTL_STRING_UNITTEST
43 Implementation of efficient string concatenation.
45 The whole system is built around two basic template classes:
46 - ToStringHelper< T > - for each T it can give the length of the resulting string representation and can write
47 this string representation to a buffer
48 - O(U)StringConcat< T1, T2 > - operator+ now, instead of creating O(U)String object, returns only this helper object,
49 that keeps a reference to both operator+ operands; only when converted to O(U)String it will actually create
50 the resulting string object using ToStringHelper, creating directly the resulting object without any string
52 As all the code is inline methods, it allows for extensive optimization and will usually result in very effective code
53 (even surpassing strlen/strcat and equalling handwritten), while allowing for very easy and intuitive syntax.
59 Helper class for converting a given type to a string representation.
61 template< typename T
>
64 /// Return length of the string representation of the given object.
65 static std::size_t length( const T
& );
66 /// Add 8-bit representation of the given object to the given buffer and return position right after the added data.
67 static char* addData( char* buffer
, const T
& ) SAL_RETURNS_NONNULL
;
68 /// Add Unicode representation of the given object to the given buffer and return position right after the added data.
69 static sal_Unicode
* addData( sal_Unicode
* buffer
, const T
& ) SAL_RETURNS_NONNULL
;
70 /// If true, T can be used in concatenation resulting in OString.
71 static const bool allowOStringConcat
= false;
72 /// If true, T can be used in concatenation resulting in OUString.
73 static const bool allowOUStringConcat
= false;
77 char* addDataHelper( char* buffer
, const char* data
, std::size_t length
)
80 memcpy( buffer
, data
, length
);
82 return buffer
+ length
;
86 sal_Unicode
* addDataHelper( sal_Unicode
* buffer
, const sal_Unicode
* data
, std::size_t length
)
89 memcpy( buffer
, data
, length
* sizeof( sal_Unicode
));
91 return buffer
+ length
;
95 sal_Unicode
* addDataLiteral( sal_Unicode
* buffer
, const char* data
, std::size_t length
)
97 for( std::size_t i
= 0; i
!= length
; ++i
)
103 char* addDataCString( char* buffer
, const char* str
)
105 while( *str
!= '\0' )
111 sal_Unicode
* addDataUString( sal_Unicode
* buffer
, const sal_Unicode
* str
)
113 while( *str
!= '\0' )
119 struct ToStringHelper
< const char* >
121 static std::size_t length( const char* str
) {
122 return str
? strlen( str
) : 0;
124 static char* addData( char* buffer
, const char* str
) {
125 return str
? addDataCString( buffer
, str
) : buffer
;
127 static const bool allowOStringConcat
= true;
128 static const bool allowOUStringConcat
= false;
132 struct ToStringHelper
< char* > : public ToStringHelper
< const char* > {};
134 template< std::size_t N
>
135 struct ToStringHelper
< char[ N
] >
137 static std::size_t length( const char str
[ N
] ) {
138 return strlen( str
);
140 static char* addData( char* buffer
, const char str
[ N
] ) { return addDataCString( buffer
, str
); }
141 static const bool allowOStringConcat
= true;
142 static const bool allowOUStringConcat
= false;
145 template< std::size_t N
>
146 struct ToStringHelper
< const char[ N
] >
148 static std::size_t length( const char str
[ N
] ) { (void)str
; assert( strlen( str
) == N
- 1 ); return N
- 1; }
149 static char* addData( char* buffer
, const char str
[ N
] ) { return addDataHelper( buffer
, str
, N
- 1 ); }
150 static sal_Unicode
* addData( sal_Unicode
* buffer
, const char str
[ N
] ) { return addDataLiteral( buffer
, str
, N
- 1 ); }
151 static const bool allowOStringConcat
= true;
152 static const bool allowOUStringConcat
= true;
156 struct ToStringHelper
<OStringChar
>
158 static std::size_t length(OStringChar
) { return 1; }
159 static char* addData(char* buffer
, OStringChar data
)
160 { return addDataHelper(buffer
, &data
.c
, 1); }
161 static bool const allowOStringConcat
= true;
162 static bool const allowOUStringConcat
= false;
166 struct ToStringHelper
< const sal_Unicode
* >
168 static std::size_t length( const sal_Unicode
* str
) {
169 return str
? std::char_traits
<char16_t
>::length( str
) : 0;
171 static sal_Unicode
* addData( sal_Unicode
* buffer
, const sal_Unicode
* str
) {
172 return str
? addDataUString( buffer
, str
) : buffer
;
174 static const bool allowOStringConcat
= false;
175 static const bool allowOUStringConcat
= true;
179 struct ToStringHelper
< sal_Unicode
* > : public ToStringHelper
< const sal_Unicode
* > {};
181 template<std::size_t N
>
182 struct ToStringHelper
<sal_Unicode
[ N
]>
184 static std::size_t length( const sal_Unicode str
[ N
] ) {
185 return std::char_traits
<char16_t
>::length( str
);
187 static sal_Unicode
* addData(sal_Unicode
* buffer
, sal_Unicode
const str
[N
])
188 { return addDataHelper(buffer
, str
, N
- 1); }
189 static bool const allowOStringConcat
= false;
190 static bool const allowOUStringConcat
= true;
193 template<std::size_t N
>
194 struct ToStringHelper
<sal_Unicode
const[N
]>
196 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; }
197 static sal_Unicode
* addData(sal_Unicode
* buffer
, sal_Unicode
const str
[N
])
198 { return addDataHelper(buffer
, str
, N
- 1); }
199 static bool const allowOStringConcat
= false;
200 static bool const allowOUStringConcat
= true;
204 struct ToStringHelper
<OUStringChar_
>
206 static std::size_t length(OUStringChar_
) { return 1; }
207 static sal_Unicode
* addData(sal_Unicode
* buffer
, OUStringChar_ literal
)
208 { return addDataHelper(buffer
, &literal
.c
, 1); }
209 static bool const allowOStringConcat
= false;
210 static bool const allowOUStringConcat
= true;
216 Objects returned by operator+, instead of OString. These objects (possibly recursively) keep a representation of the whole
217 concatenation operation.
219 If you get a build error related to this class, you most probably need to explicitly convert the result of a string
220 concatenation to OString.
222 template< typename T1
, typename T2
>
226 OStringConcat( const T1
& left_
, const T2
& right_
) : left( left_
), right( right_
) {}
227 std::size_t length() const { return ToStringHelper
< T1
>::length( left
) + ToStringHelper
< T2
>::length( right
); }
228 char* addData( char* buffer
) const SAL_RETURNS_NONNULL
{ return ToStringHelper
< T2
>::addData( ToStringHelper
< T1
>::addData( buffer
, left
), right
); }
229 // NOTE here could be functions that would forward to the "real" temporary OString. Note however that e.g. getStr()
230 // is not so simple, as the OString temporary must live long enough (i.e. can't be created here in a function, a wrapper
231 // temporary object containing it must be returned instead).
240 Objects returned by operator+, instead of OUString. These objects (possibly recursively) keep a representation of the whole
241 concatenation operation.
243 If you get a build error related to this class, you most probably need to explicitly convert the result of a string
244 concatenation to OUString.
246 template< typename T1
, typename T2
>
247 struct OUStringConcat
250 OUStringConcat( const T1
& left_
, const T2
& right_
) : left( left_
), right( right_
) {}
251 std::size_t length() const { return ToStringHelper
< T1
>::length( left
) + ToStringHelper
< T2
>::length( right
); }
252 sal_Unicode
* addData( sal_Unicode
* buffer
) const SAL_RETURNS_NONNULL
{ return ToStringHelper
< T2
>::addData( ToStringHelper
< T1
>::addData( buffer
, left
), right
); }
258 template< typename T1
, typename T2
>
259 struct ToStringHelper
< OStringConcat
< T1
, T2
> >
261 static std::size_t length( const OStringConcat
< T1
, T2
>& c
) { return c
.length(); }
262 static char* addData( char* buffer
, const OStringConcat
< T1
, T2
>& c
) SAL_RETURNS_NONNULL
{ return c
.addData( buffer
); }
263 static const bool allowOStringConcat
= ToStringHelper
< T1
>::allowOStringConcat
&& ToStringHelper
< T2
>::allowOStringConcat
;
264 static const bool allowOUStringConcat
= false;
267 template< typename T1
, typename T2
>
268 struct ToStringHelper
< OUStringConcat
< T1
, T2
> >
270 static std::size_t length( const OUStringConcat
< T1
, T2
>& c
) { return c
.length(); }
271 static sal_Unicode
* addData( sal_Unicode
* buffer
, const OUStringConcat
< T1
, T2
>& c
) SAL_RETURNS_NONNULL
{ return c
.addData( buffer
); }
272 static const bool allowOStringConcat
= false;
273 static const bool allowOUStringConcat
= ToStringHelper
< T1
>::allowOUStringConcat
&& ToStringHelper
< T2
>::allowOUStringConcat
;
276 template< typename T1
, typename T2
>
279 typename
std::enable_if_t
< ToStringHelper
< T1
>::allowOStringConcat
&& ToStringHelper
< T2
>::allowOStringConcat
, OStringConcat
< T1
, T2
> > operator+( const T1
& left
, const T2
& right
)
281 return OStringConcat
< T1
, T2
>( left
, right
);
284 // char[N] and const char[N] need to be done explicitly, otherwise the compiler likes to treat them the same way for some reason
285 template< typename T
, std::size_t N
>
288 typename
std::enable_if_t
< ToStringHelper
< T
>::allowOStringConcat
, OStringConcat
< T
, const char[ N
] > > operator+( const T
& left
, const char (&right
)[ N
] )
290 return OStringConcat
< T
, const char[ N
] >( left
, right
);
293 template< typename T
, std::size_t N
>
296 typename
std::enable_if_t
< ToStringHelper
< T
>::allowOStringConcat
, OStringConcat
< const char[ N
], T
> > operator+( const char (&left
)[ N
], const T
& right
)
298 return OStringConcat
< const char[ N
], T
>( left
, right
);
301 template< typename T
, std::size_t N
>
304 typename
std::enable_if_t
< ToStringHelper
< T
>::allowOStringConcat
, OStringConcat
< T
, char[ N
] > > operator+( const T
& left
, char (&right
)[ N
] )
306 return OStringConcat
< T
, char[ N
] >( left
, right
);
309 template< typename T
, std::size_t N
>
312 typename
std::enable_if_t
< ToStringHelper
< T
>::allowOStringConcat
, OStringConcat
< char[ N
], T
> > operator+( char (&left
)[ N
], const T
& right
)
314 return OStringConcat
< char[ N
], T
>( left
, right
);
317 template< typename T1
, typename T2
>
320 typename
std::enable_if_t
< ToStringHelper
< T1
>::allowOUStringConcat
&& ToStringHelper
< T2
>::allowOUStringConcat
, OUStringConcat
< T1
, T2
> > operator+( const T1
& left
, const T2
& right
)
322 return OUStringConcat
< T1
, T2
>( left
, right
);
325 template< typename T1
, typename T2
>
328 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
)
330 return OUStringConcat
< T1
, T2
>( left
, right
);
333 template< typename T1
, typename T2
>
336 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
)
338 return OUStringConcat
< T1
, T2
>( left
, right
);
341 #ifdef RTL_STRING_UNITTEST_CONCAT
342 // Special overload to catch the remaining invalid combinations. The helper struct must
343 // be used to make this operator+ overload a worse choice than all the existing overloads above.
344 struct StringConcatInvalid
346 template< typename T
>
347 StringConcatInvalid( const T
& ) {}
349 template< typename T
>
351 int operator+( const StringConcatInvalid
&, const T
& )
353 rtl_string_unittest_invalid_concat
= true;
354 return 0; // doesn't matter
358 // Lightweight alternative to OString when a (temporary) object is needed to hold an OStringConcat
359 // result that can then be used as a std::string_view:
360 class OStringConcatenation
{
362 template<typename T1
, typename T2
>
363 explicit OStringConcatenation(OStringConcat
<T1
, T2
> const & c
):
365 buffer_(new char[length_
])
367 auto const end
= c
.addData(buffer_
.get());
368 assert(end
== buffer_
.get() + length_
); (void)end
;
371 operator std::string_view() const { return {buffer_
.get(), length_
}; }
375 std::unique_ptr
<char[]> buffer_
;
378 // Lightweight alternative to OUString when a (temporary) object is needed to hold an
379 // OUStringConcat result that can then be used as a std::u16string_view:
380 class OUStringConcatenation
{
382 template<typename T1
, typename T2
>
383 explicit OUStringConcatenation(OUStringConcat
<T1
, T2
> const & c
):
385 buffer_(new char16_t
[length_
])
387 auto const end
= c
.addData(buffer_
.get());
388 assert(end
== buffer_
.get() + length_
); (void)end
;
391 operator std::u16string_view() const { return {buffer_
.get(), length_
}; }
395 std::unique_ptr
<char16_t
[]> buffer_
;
401 Objects returned by OString::number(), instead of OString. These objects keep a representation of the number() operation.
403 If you get a build error related to this class, you most probably need to explicitly convert the result of calling
404 OString::number() to OString.
406 template< typename T
>
407 struct OStringNumber
;
409 template <typename Number
, std::size_t nBufSize
> struct OStringNumberBase
411 using number_t
= Number
;
412 // OString::number(value).getStr() is very common (writing xml code, ...),
413 // so implement that one also here, to avoid having to explicitly to convert
414 // to OString in all such places
415 const char * getStr() const SAL_RETURNS_NONNULL
{ return buf
; }
416 OStringNumber
<number_t
>&& toAsciiUpperCase()
418 rtl_str_toAsciiUpperCase_WithLength(buf
, length
);
419 return std::move(*static_cast<OStringNumber
<number_t
>*>(this));
421 operator std::string_view() const { return std::string_view(buf
, length
); }
427 struct OStringNumber
< int >
428 : public OStringNumberBase
<int, RTL_STR_MAX_VALUEOFINT32
>
430 OStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_str_valueOfInt32(buf
, i
, radix
); }
434 struct OStringNumber
< long long >
435 : public OStringNumberBase
<long long, RTL_STR_MAX_VALUEOFINT64
>
437 OStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_str_valueOfInt64(buf
, i
, radix
); }
441 struct OStringNumber
< unsigned long long >
442 : public OStringNumberBase
<unsigned long long, RTL_STR_MAX_VALUEOFUINT64
>
444 OStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_str_valueOfUInt64(buf
, i
, radix
); }
448 struct OStringNumber
< float >
449 : public OStringNumberBase
<float, RTL_STR_MAX_VALUEOFFLOAT
>
451 OStringNumber(number_t f
) { length
= rtl_str_valueOfFloat(buf
, f
); }
455 struct OStringNumber
< double >
456 : public OStringNumberBase
<double, RTL_STR_MAX_VALUEOFDOUBLE
>
458 OStringNumber(number_t d
) { length
= rtl_str_valueOfDouble(buf
, d
); }
461 template< typename T
>
462 struct ToStringHelper
< OStringNumber
< T
> >
464 static std::size_t length( const OStringNumber
< T
>& n
) { return n
.length
; }
465 static char* addData( char* buffer
, const OStringNumber
< T
>& n
) SAL_RETURNS_NONNULL
{ return addDataHelper( buffer
, n
.buf
, n
.length
); }
466 static const bool allowOStringConcat
= true;
467 static const bool allowOUStringConcat
= false;
474 Objects returned by OUString::number(), instead of OUString. These objects keep a representation of the number() operation.
476 If you get a build error related to this class, you most probably need to explicitly convert the result of calling
477 OUString::number() to OUString.
479 template< typename T
>
480 struct OUStringNumber
;
482 template <typename Number
, std::size_t nBufSize
> struct OUStringNumberBase
484 using number_t
= Number
;
485 OUStringNumber
<number_t
>&& toAsciiUpperCase()
487 rtl_ustr_toAsciiUpperCase_WithLength(buf
, length
);
488 return std::move(*static_cast<OUStringNumber
<number_t
>*>(this));
490 operator std::u16string_view() const { return std::u16string_view(buf
, length
); }
491 sal_Unicode buf
[nBufSize
];
496 struct OUStringNumber
< int >
497 : public OUStringNumberBase
<int, RTL_USTR_MAX_VALUEOFINT32
>
499 OUStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_ustr_valueOfInt32(buf
, i
, radix
); }
503 struct OUStringNumber
< long long >
504 : public OUStringNumberBase
<long long, RTL_USTR_MAX_VALUEOFINT64
>
506 OUStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_ustr_valueOfInt64(buf
, i
, radix
); }
510 struct OUStringNumber
< unsigned long long >
511 : public OUStringNumberBase
<unsigned long long, RTL_USTR_MAX_VALUEOFUINT64
>
513 OUStringNumber(number_t i
, sal_Int16 radix
) { length
= rtl_ustr_valueOfUInt64(buf
, i
, radix
); }
517 struct OUStringNumber
< float >
518 : public OUStringNumberBase
<float, RTL_USTR_MAX_VALUEOFFLOAT
>
520 OUStringNumber(number_t f
) { length
= rtl_ustr_valueOfFloat(buf
, f
); }
524 struct OUStringNumber
< double >
525 : public OUStringNumberBase
<double, RTL_USTR_MAX_VALUEOFDOUBLE
>
527 OUStringNumber(number_t d
) { length
= rtl_ustr_valueOfDouble(buf
, d
); }
530 template< typename T
>
531 struct ToStringHelper
< OUStringNumber
< T
> >
533 static std::size_t length( const OUStringNumber
< T
>& n
) { return n
.length
; }
534 static sal_Unicode
* addData( sal_Unicode
* buffer
, const OUStringNumber
< T
>& n
) SAL_RETURNS_NONNULL
{ return addDataHelper( buffer
, n
.buf
, n
.length
); }
535 static const bool allowOStringConcat
= false;
536 static const bool allowOUStringConcat
= true;
539 template<> struct ToStringHelper
<std::string_view
> {
540 static constexpr std::size_t length(std::string_view s
) { return s
.size(); }
542 static char * addData(char * buffer
, std::string_view s
) SAL_RETURNS_NONNULL
543 { return addDataHelper(buffer
, s
.data(), s
.size()); }
545 static constexpr bool allowOStringConcat
= true;
546 static constexpr bool allowOUStringConcat
= false;
549 template<> struct ToStringHelper
<std::u16string_view
> {
550 static constexpr std::size_t length(std::u16string_view s
) { return s
.size(); }
552 static sal_Unicode
* addData(sal_Unicode
* buffer
, std::u16string_view s
) SAL_RETURNS_NONNULL
553 { return addDataHelper(buffer
, s
.data(), s
.size()); }
555 static constexpr bool allowOStringConcat
= false;
556 static constexpr bool allowOUStringConcat
= true;
559 // An internal marker class used by OString::Concat:
560 struct OStringConcatMarker
{};
562 template<> struct ToStringHelper
<OStringConcatMarker
> {
563 static constexpr std::size_t length(OStringConcatMarker
) { return 0; }
565 static constexpr char * addData(char * buffer
, OStringConcatMarker
) SAL_RETURNS_NONNULL
568 static constexpr bool allowOStringConcat
= true;
569 static constexpr bool allowOUStringConcat
= false;
572 // An internal marker class used by OUString::Concat:
573 struct OUStringConcatMarker
{};
575 template<> struct ToStringHelper
<OUStringConcatMarker
> {
576 static constexpr std::size_t length(OUStringConcatMarker
) { return 0; }
578 static constexpr sal_Unicode
* addData(sal_Unicode
* buffer
, OUStringConcatMarker
)
582 static constexpr bool allowOStringConcat
= false;
583 static constexpr bool allowOUStringConcat
= true;