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 // char* operator()( char* buffer, const T& ) const SAL_RETURNS_NONNULL;
68 /// Add Unicode representation of the given object to the given buffer and return position right after the added data.
69 // sal_Unicode* operator()( sal_Unicode* buffer, const T& ) const SAL_RETURNS_NONNULL;
72 /// If true, T can be used in concatenation resulting in O(U)String.
73 template<typename C
, typename T
, class Enable
= void> constexpr bool allowStringConcat
= false;
74 template<typename C
, typename T
> constexpr bool allowStringConcat
<C
, T
, std::enable_if_t
<std::is_invocable_v
<ToStringHelper
<T
>, C
*, T
>>> = true;
76 template <typename C
> inline
77 C
* addDataHelper( C
* buffer
, const C
* data
, std::size_t length
)
80 memcpy( buffer
, data
, length
* sizeof( C
));
82 return buffer
+ length
;
86 sal_Unicode
* addDataLiteral( sal_Unicode
* buffer
, const char* data
, std::size_t length
)
88 for( std::size_t i
= 0; i
!= length
; ++i
)
93 template <typename C
> inline
94 C
* addDataString( C
* buffer
, const C
* str
)
102 struct ToStringHelper
< const char* >
104 static std::size_t length( const char* str
) {
105 return str
? strlen( str
) : 0;
107 char* operator()( char* buffer
, const char* str
) const {
108 return str
? addDataString( buffer
, str
) : buffer
;
113 struct ToStringHelper
< char* > : public ToStringHelper
< const char* > {};
115 template< std::size_t N
>
116 struct ToStringHelper
< char[ N
] >
118 static std::size_t length( const char str
[ N
] ) {
119 return strlen( str
);
121 char* operator()( char* buffer
, const char str
[ N
] ) const { return addDataString( buffer
, str
); }
124 template< std::size_t N
>
125 struct ToStringHelper
< const char[ N
] >
127 static std::size_t length( const char str
[ N
] ) { (void)str
; assert( strlen( str
) == N
- 1 ); return N
- 1; }
128 char* operator()( char* buffer
, const char str
[ N
] ) const { return addDataHelper( buffer
, str
, N
- 1 ); }
129 sal_Unicode
* operator()( sal_Unicode
* buffer
, const char str
[ N
] ) const { return addDataLiteral( buffer
, str
, N
- 1 ); }
133 struct ToStringHelper
<OStringChar
>
135 static std::size_t length(OStringChar
) { return 1; }
136 char* operator()(char* buffer
, OStringChar data
) const
137 { return addDataHelper(buffer
, &data
.c
, 1); }
141 struct ToStringHelper
< const sal_Unicode
* >
143 static std::size_t length( const sal_Unicode
* str
) {
144 return str
? std::char_traits
<char16_t
>::length( str
) : 0;
146 sal_Unicode
* operator()( sal_Unicode
* buffer
, const sal_Unicode
* str
) const {
147 return str
? addDataString( buffer
, str
) : buffer
;
152 struct ToStringHelper
< sal_Unicode
* > : public ToStringHelper
< const sal_Unicode
* > {};
154 template<std::size_t N
>
155 struct ToStringHelper
<sal_Unicode
[ N
]>
157 static std::size_t length( const sal_Unicode str
[ N
] ) {
158 return std::char_traits
<char16_t
>::length( str
);
160 sal_Unicode
* operator()(sal_Unicode
* buffer
, sal_Unicode
const str
[N
]) const
161 { return addDataHelper(buffer
, str
, N
- 1); }
164 template<std::size_t N
>
165 struct ToStringHelper
<sal_Unicode
const[N
]>
167 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; }
168 sal_Unicode
* operator()(sal_Unicode
* buffer
, sal_Unicode
const str
[N
]) const
169 { return addDataHelper(buffer
, str
, N
- 1); }
173 struct ToStringHelper
<OUStringChar_
>
175 static std::size_t length(OUStringChar_
) { return 1; }
176 sal_Unicode
* operator()(sal_Unicode
* buffer
, OUStringChar_ literal
) const
177 { return addDataHelper(buffer
, &literal
.c
, 1); }
183 Objects returned by operator+, instead of O(U)String. These objects (possibly recursively) keep a representation of the whole
184 concatenation operation.
186 If you get a build error related to this class, you most probably need to explicitly convert the result of a string
187 concatenation to O(U)String.
189 template <typename C
, typename T1
, typename T2
, std::enable_if_t
<allowStringConcat
<C
, T1
> && allowStringConcat
<C
, T2
>, int> = 0 >
193 StringConcat( const T1
& left_
, const T2
& right_
) : left( left_
), right( right_
) {}
194 std::size_t length() const { return ToStringHelper
< T1
>::length( left
) + ToStringHelper
< T2
>::length( right
); }
195 C
* addData( C
* buffer
) const SAL_RETURNS_NONNULL
{ return ToStringHelper
< T2
>()( ToStringHelper
< T1
>()( buffer
, left
), right
); }
196 // NOTE here could be functions that would forward to the "real" temporary O(U)String. Note however that e.g. getStr()
197 // is not so simple, as the O(U)String temporary must live long enough (i.e. can't be created here in a function, a wrapper
198 // temporary object containing it must be returned instead).
204 template <typename C
, typename T1
, typename T2
> struct ToStringHelper
<StringConcat
<C
, T1
, T2
>>
206 static std::size_t length(const StringConcat
<C
, T1
, T2
>& c
) { return c
.length(); }
207 C
* operator()(C
* buffer
, const StringConcat
<C
, T1
, T2
>& c
) const SAL_RETURNS_NONNULL
{ return c
.addData(buffer
); }
210 template <typename T1
, typename T2
> using OStringConcat
= StringConcat
<char, T1
, T2
>;
211 template <typename T1
, typename T2
> using OUStringConcat
= StringConcat
<sal_Unicode
, T1
, T2
>;
213 template< typename T1
, typename T2
>
216 OStringConcat
< T1
, T2
> operator+( const T1
& left
, const T2
& right
)
218 return OStringConcat
< T1
, T2
>( left
, right
);
221 // char[N] and const char[N] need to be done explicitly, otherwise the compiler likes to treat them the same way for some reason
222 template< typename T
, std::size_t N
>
225 OStringConcat
< T
, const char[ N
] > operator+( const T
& left
, const char (&right
)[ N
] )
227 return OStringConcat
< T
, const char[ N
] >( left
, right
);
230 template< typename T
, std::size_t N
>
233 OStringConcat
< const char[ N
], T
> operator+( const char (&left
)[ N
], const T
& right
)
235 return OStringConcat
< const char[ N
], T
>( left
, right
);
238 template< typename T
, std::size_t N
>
241 OStringConcat
< T
, char[ N
] > operator+( const T
& left
, char (&right
)[ N
] )
243 return OStringConcat
< T
, char[ N
] >( left
, right
);
246 template< typename T
, std::size_t N
>
249 OStringConcat
< char[ N
], T
> operator+( char (&left
)[ N
], const T
& right
)
251 return OStringConcat
< char[ N
], T
>( left
, right
);
254 template< typename T1
, typename T2
>
257 OUStringConcat
< T1
, T2
> operator+( const T1
& left
, const T2
& right
)
259 return OUStringConcat
< T1
, T2
>( left
, right
);
262 template< typename T1
, typename T2
>
265 typename
std::enable_if_t
< libreoffice_internal::ConstCharArrayDetector
< T1
, void >::ok
, OUStringConcat
< T1
, T2
> > operator+( T1
& left
, const T2
& right
)
267 return OUStringConcat
< T1
, T2
>( left
, right
);
270 template< typename T1
, typename T2
>
273 typename
std::enable_if_t
< libreoffice_internal::ConstCharArrayDetector
< T2
, void >::ok
, OUStringConcat
< T1
, T2
> > operator+( const T1
& left
, T2
& right
)
275 return OUStringConcat
< T1
, T2
>( left
, right
);
278 #ifdef RTL_STRING_UNITTEST_CONCAT
279 // Special overload to catch the remaining invalid combinations. The helper struct must
280 // be used to make this operator+ overload a worse choice than all the existing overloads above.
281 struct StringConcatInvalid
283 template< typename T
>
284 StringConcatInvalid( const T
& ) {}
286 template< typename T
>
288 int operator+( const StringConcatInvalid
&, const T
& )
290 rtl_string_unittest_invalid_concat
= true;
291 return 0; // doesn't matter
295 // Lightweight alternative to O(U)String when a (temporary) object is needed to hold
296 // an O(U)StringConcat result that can then be used as a std::(u16)string_view:
297 template <typename C
> class StringConcatenation
{
299 template <class Concat
>
300 explicit StringConcatenation(Concat
const& c
):
302 buffer_(new C
[length_
])
304 auto const end
= c
.addData(buffer_
.get());
305 assert(end
== buffer_
.get() + length_
); (void)end
;
308 operator std::basic_string_view
<C
>() const { return {buffer_
.get(), length_
}; }
312 std::unique_ptr
<C
[]> buffer_
;
315 template <typename C
, typename T1
, typename T2
> auto Concat2View(StringConcat
<C
, T1
, T2
> const& c
)
317 return StringConcatenation
<C
>(c
);
321 * O(U)StringNumber implementation
323 Objects returned by O(U)String::number(), instead of O(U)String. These objects keep a representation of the number() operation.
325 If you get a build error related to this class, you most probably need to explicitly convert the result of calling
326 O(U)String::number() to O(U)String.
329 template <typename C
, std::size_t nBufSize
> struct StringNumber
331 template <typename Func
, typename
... Args
,
332 std::enable_if_t
<std::is_invocable_r_v
<sal_Int32
, Func
, C
*, Args
...>, int> = 0>
333 StringNumber(Func func
, Args
... args
) { length
= func(buf
, args
...); }
334 // O(U)String::number(value).getStr() is very common (writing xml code, ...),
335 // so implement that one also here, to avoid having to explicitly convert
336 // to O(U)String in all such places
337 const C
* getStr() const SAL_RETURNS_NONNULL
{ return buf
; }
338 StringNumber
&& toAsciiUpperCase() &&
340 if constexpr (sizeof(C
) == sizeof(char))
341 rtl_str_toAsciiUpperCase_WithLength(buf
, length
);
343 rtl_ustr_toAsciiUpperCase_WithLength(buf
, length
);
344 return std::move(*this);
346 operator std::basic_string_view
<C
>() const { return std::basic_string_view
<C
>(buf
, length
); }
351 template<std::size_t nBufSize
> using OStringNumber
= StringNumber
<char, nBufSize
>;
352 template<std::size_t nBufSize
> using OUStringNumber
= StringNumber
<sal_Unicode
, nBufSize
>;
354 template< typename C
, std::size_t nBufSize
>
355 struct ToStringHelper
< StringNumber
< C
, nBufSize
> >
357 static std::size_t length( const StringNumber
< C
, nBufSize
>& n
) { return n
.length
; }
358 C
* operator()( C
* buffer
, const StringNumber
< C
, nBufSize
>& n
) const SAL_RETURNS_NONNULL
{ return addDataHelper( buffer
, n
.buf
, n
.length
); }
361 template<typename C
> struct ToStringHelper
<std::basic_string_view
<C
>> {
362 static constexpr std::size_t length(std::basic_string_view
<C
> s
) { return s
.size(); }
364 C
* operator()(C
* buffer
, std::basic_string_view
<C
> s
) const SAL_RETURNS_NONNULL
365 { return addDataHelper(buffer
, s
.data(), s
.size()); }
368 // An internal marker class used by O(U)String::Concat:
369 template<typename C
> struct StringConcatMarker
{};
371 using OStringConcatMarker
= StringConcatMarker
<char>;
372 using OUStringConcatMarker
= StringConcatMarker
<sal_Unicode
>;
374 template<typename C
> constexpr bool allowStringConcat
<C
, StringConcatMarker
<C
>> = true;
376 #if defined __GNUC__ && !defined __clang__
377 template <typename C
, typename T2
>
378 struct StringConcat
<C
, StringConcatMarker
<C
>, T2
>
380 template <typename C
, typename T2
, std::enable_if_t
<allowStringConcat
<C
, T2
>, int> Dummy
>
381 struct StringConcat
<C
, StringConcatMarker
<C
>, T2
, Dummy
>
385 StringConcat( const T2
& right_
) : right( right_
) {}
386 std::size_t length() const { return ToStringHelper
< T2
>::length( right
); }
387 C
* addData( C
* buffer
) const SAL_RETURNS_NONNULL
{ return ToStringHelper
< T2
>()( buffer
, right
); }