LibreOffice
LibreOffice 25.2 SDK C/C++ API Reference
stringutils.hxx
Go to the documentation of this file.
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 /*
11  * This file is part of LibreOffice published API.
12  */
13 
14 #ifndef INCLUDED_RTL_STRINGUTILS_HXX
15 #define INCLUDED_RTL_STRINGUTILS_HXX
16 
17 #include "sal/config.h"
18 
19 #include <cassert>
20 #include <cstddef>
21 
22 #if defined LIBO_INTERNAL_ONLY
23 #include <limits>
24 #include <new>
25 #include <type_traits>
26 #endif
27 
28 #include "sal/types.h"
29 
30 // The unittest uses slightly different code to help check that the proper
31 // calls are made. The class is put into a different namespace to make
32 // sure the compiler generates a different (if generating also non-inline)
33 // copy of the function and does not merge them together. The class
34 // is "brought" into the proper rtl namespace by a typedef below.
35 #ifdef RTL_STRING_UNITTEST
36 #define rtl rtlunittest
37 #endif
38 
39 namespace rtl
40 {
41 
42 #ifdef RTL_STRING_UNITTEST
43 #undef rtl
44 #endif
45 
46 #if defined LIBO_INTERNAL_ONLY
47 
49 // A simple wrapper around a single char. Can be useful in string concatenation contexts, like in
50 //
51 // OString s = ...;
52 // char c = ...;
53 // s += OStringChar(c);
54 //
55 struct SAL_WARN_UNUSED OStringChar {
56  constexpr OStringChar(char theC): c(theC) {}
57  template<typename T> OStringChar(
58  T, std::enable_if_t<std::is_arithmetic_v<T> || std::is_enum_v<T>, int> = 0) = delete;
59  constexpr operator std::string_view() const { return {&c, 1}; }
60  char const c;
61 };
62 
105 struct SAL_WARN_UNUSED OUStringChar_ {
106  constexpr OUStringChar_(sal_Unicode theC): c(theC) {}
107  constexpr OUStringChar_(char theC): c(theC) { assert(c <= 0x7F); }
108  template<typename T> OUStringChar_(
109  T, std::enable_if_t<std::is_arithmetic_v<T> || std::is_enum_v<T>, int> = 0) = delete;
110  constexpr operator std::u16string_view() const { return {&c, 1}; }
111  sal_Unicode const c;
112 };
113 using OUStringChar = OUStringChar_ const;
114 
116 #endif
117 
118 namespace libreoffice_internal
119 {
120 #if defined LIBO_INTERNAL_ONLY
121 template <typename I, std::enable_if_t<
122  std::is_integral_v<I> && std::is_signed_v<I>,
123  int> = 0>
124 constexpr bool IsValidStrLen(I i, sal_Int32 margin = 0)
125 {
126  assert(margin >= 0);
127  constexpr sal_uInt32 maxLen = std::numeric_limits<sal_Int32>::max();
128  return i >= 0 && static_cast<std::make_unsigned_t<I>>(i) <= maxLen - margin;
129 }
130 
131 template <typename I, std::enable_if_t<
132  std::is_integral_v<I> && std::is_unsigned_v<I>,
133  int> = 0>
134 constexpr bool IsValidStrLen(I i, sal_Int32 margin = 0)
135 {
136  assert(margin >= 0);
137  constexpr sal_uInt32 maxLen = std::numeric_limits<sal_Int32>::max();
138  return i <= maxLen - margin;
139 }
140 
141 template <typename I, std::enable_if_t<std::is_integral_v<I>, int> = 0>
142 sal_Int32 ThrowIfInvalidStrLen(I i, sal_Int32 margin = 0)
143 {
144  if (!IsValidStrLen(i, margin))
145  throw std::bad_alloc();
146  return i;
147 }
148 #endif
149 
150 /*
151 These templates use SFINAE (Substitution failure is not an error) to help distinguish the various
152 plain C string types: char*, const char*, char[N], const char[N], char[] and const char[].
153 There are 2 cases:
154 1) Only string literal (i.e. const char[N]) is wanted, not any of the others.
155  In this case it is necessary to distinguish between const char[N] and char[N], as the latter
156  would be automatically converted to the const variant, which is not wanted (not a string literal
157  with known size of the content). In this case ConstCharArrayDetector is used to ensure the function
158  is called only with const char[N] arguments. There's no other plain C string type overload.
159  (Note that OUStringChar is also covered by ConstCharArrayDetector's TypeUtf16 check, but
160  provides a pointer to a string that is not NUL-terminated, unlike the char16_t const[N] arrays
161  normally covered by that check, and which are assumed to represent NUL-terminated string
162  literals.)
163 2) All plain C string types are wanted, and const char[N] needs to be handled differently.
164  In this case const char[N] would match const char* argument type (not exactly sure why, but it's
165  consistent in all of gcc, clang and msvc). Using a template with a reference to const of the type
166  avoids this problem, and CharPtrDetector ensures that the function is called only with char pointer
167  arguments. The const in the argument is necessary to handle the case when something is explicitly
168  cast to const char*. Additionally (non-const) char[N] needs to be handled, but with the reference
169  being const, it would also match const char[N], so another overload with a reference to non-const
170  and NonConstCharArrayDetector are used to ensure the function is called only with (non-const) char[N].
171 Additionally, char[] and const char[] (i.e. size unknown) are rather tricky. Their usage with 'T&' would
172 mean it would be 'char(&)[]', which seems to be invalid. But gcc and clang somehow manage when it is
173 a template. while msvc complains about no conversion from char[] to char[1]. And the reference cannot
174 be avoided, because 'const char[]' as argument type would match also 'const char[N]'
175 So char[] and const char[] should always be used with their contents specified (which automatically
176 turns them into char[N] or const char[N]), or char* and const char* should be used.
177 */
178 struct Dummy {};
179 template< typename T1, typename T2 = void >
181 {
182  static const bool ok = false;
183 };
184 template< typename T >
185 struct CharPtrDetector< const char*, T >
186 {
187  typedef T Type;
188  static const bool ok = true;
189 };
190 template< typename T >
191 struct CharPtrDetector< char*, T >
192 {
193  typedef T Type;
194  static const bool ok = true;
195 };
196 #if defined LIBO_INTERNAL_ONLY
197 template<typename T> struct CharPtrDetector<sal_Unicode *, T> { using TypeUtf16 = T; };
198 template<typename T> struct CharPtrDetector<sal_Unicode const *, T> { using TypeUtf16 = T; };
199 template<typename T> struct CharPtrDetector<sal_Unicode[], T> { using TypeUtf16 = T; };
200 template<typename T> struct CharPtrDetector<sal_Unicode const[], T> { using TypeUtf16 = T; };
201 #endif
202 
203 template< typename T1, typename T2 >
205 {
206 };
207 template< typename T, int N >
208 struct NonConstCharArrayDetector< char[ N ], T >
209 {
210  typedef T Type;
211 };
212 #ifdef RTL_STRING_UNITTEST
213 // never use, until all compilers handle this
214 template< typename T >
215 struct NonConstCharArrayDetector< char[], T >
216 {
217  typedef T Type;
218 };
219 template< typename T >
220 struct NonConstCharArrayDetector< const char[], T >
221 {
222  typedef T Type;
223 };
224 #endif
225 #if defined LIBO_INTERNAL_ONLY
226 template<typename T, std::size_t N> struct NonConstCharArrayDetector<sal_Unicode[N], T> {
227  using TypeUtf16 = T;
228 };
229 #endif
230 
231 template< typename T1, typename T2 = void >
233 {
234  static const bool ok = false;
235 };
236 template< std::size_t N, typename T >
237 struct ConstCharArrayDetector< const char[ N ], T >
238 {
239  typedef T Type;
240  static const std::size_t length = N - 1;
241  static const bool ok = true;
242 #if defined LIBO_INTERNAL_ONLY
243  constexpr
244 #endif
245  static bool isValid(char const (& literal)[N]) {
246  for (std::size_t i = 0; i != N - 1; ++i) {
247  if (literal[i] == '\0') {
248  return false;
249  }
250  }
251  return literal[N - 1] == '\0';
252  }
253 #if defined LIBO_INTERNAL_ONLY
254  constexpr
255 #endif
256  static char const * toPointer(char const (& literal)[N]) { return literal; }
257 };
258 
259 #if defined(__COVERITY__) && __COVERITY_MAJOR__ <= 2023
260 //to silence over zealous warnings that the loop is logically dead
261 //for the single char case
262 template< typename T >
263 struct ConstCharArrayDetector< const char[ 1 ], T >
264 {
265  typedef T Type;
266  static const std::size_t length = 0;
267  static const bool ok = true;
268 #if defined LIBO_INTERNAL_ONLY
269  constexpr
270 #endif
271  static bool isValid(char const (& literal)[1]) {
272  return literal[0] == '\0';
273  }
274 #if defined LIBO_INTERNAL_ONLY
275  constexpr
276 #endif
277  static char const * toPointer(char const (& literal)[1]) { return literal; }
278 };
279 #endif
280 
281 #if defined LIBO_INTERNAL_ONLY \
282  && !(defined _MSC_VER && _MSC_VER >= 1930 && _MSC_VER <= 1939 && defined _MANAGED)
283 template<std::size_t N, typename T>
284 struct ConstCharArrayDetector<char8_t const [N], T> {
285  using Type = T;
286  static constexpr bool const ok = true;
287  static constexpr std::size_t const length = N - 1;
288  static constexpr bool isValid(char8_t const (& literal)[N]) {
289  for (std::size_t i = 0; i != N - 1; ++i) {
290  if (literal[i] == u8'\0') {
291  return false;
292  }
293  }
294  return literal[N - 1] == u8'\0';
295  }
296  static constexpr char const * toPointer(char8_t const (& literal)[N])
297  { return reinterpret_cast<char const *>(literal); }
298 };
299 #endif
300 
301 #if defined LIBO_INTERNAL_ONLY
302 template<std::size_t N, typename T>
303 struct ConstCharArrayDetector<sal_Unicode const [N], T> {
304  using TypeUtf16 = T;
305  static constexpr bool const ok = true;
306  static constexpr std::size_t const length = N - 1;
307  static constexpr bool isValid(sal_Unicode const (& literal)[N]) {
308  for (std::size_t i = 0; i != N - 1; ++i) {
309  if (literal[i] == '\0') {
310  return false;
311  }
312  }
313  return literal[N - 1] == '\0';
314  }
315  static constexpr sal_Unicode const * toPointer(
316  sal_Unicode const (& literal)[N])
317  { return literal; }
318 };
319 
320 #if defined(__COVERITY__) && __COVERITY_MAJOR__ <= 2023
321 //to silence over zealous warnings that the loop is logically dead
322 //for the single char case
323 template<typename T>
324 struct ConstCharArrayDetector<sal_Unicode const [1], T> {
325  using TypeUtf16 = T;
326  static constexpr bool const ok = true;
327  static constexpr std::size_t const length = 0;
328  static constexpr bool isValid(sal_Unicode const (& literal)[1]) {
329  return literal[0] == '\0';
330  }
331  static constexpr sal_Unicode const * toPointer(
332  sal_Unicode const (& literal)[1])
333  { return literal; }
334 };
335 #endif
336 
337 template<typename T> struct ConstCharArrayDetector<
338  OUStringChar,
339  T>
340 {
341  using TypeUtf16 = T;
342  static constexpr bool const ok = true;
343  static constexpr std::size_t const length = 1;
344  static constexpr bool isValid(OUStringChar) { return true; }
345  static constexpr sal_Unicode const * toPointer(
346  OUStringChar_ const & literal)
347  { return &literal.c; }
348 };
349 #endif
350 
351 #if defined LIBO_INTERNAL_ONLY && defined RTL_STRING_UNITTEST
352 
353 // this one is used to rule out only const char[N]
354 template< typename T >
355 struct ExceptConstCharArrayDetector
356 {
357  typedef Dummy Type;
358 };
359 template< int N >
360 struct ExceptConstCharArrayDetector< const char[ N ] >
361 {
362 };
363 template<std::size_t N>
364 struct ExceptConstCharArrayDetector<sal_Unicode const[N]> {};
365 template<> struct ExceptConstCharArrayDetector<
366  OUStringChar
367  >
368 {};
369 
370 // this one is used to rule out only const char[N]
371 // (const will be brought in by 'const T&' in the function call)
372 // msvc needs const char[N] here (not sure whether gcc or msvc
373 // are right, it doesn't matter).
374 template< typename T >
375 struct ExceptCharArrayDetector
376 {
377  typedef Dummy Type;
378 };
379 template< int N >
380 struct ExceptCharArrayDetector< char[ N ] >
381 {
382 };
383 template< int N >
384 struct ExceptCharArrayDetector< const char[ N ] >
385 {
386 };
387 template<std::size_t N> struct ExceptCharArrayDetector<sal_Unicode[N]> {};
388 template<std::size_t N> struct ExceptCharArrayDetector<sal_Unicode const[N]> {};
389 template<> struct ExceptCharArrayDetector<OUStringChar_> {};
390 
391 #endif
392 
393 template< typename T1, typename T2 = void >
395 {
396  static const bool ok = false;
397 };
398 template< typename T >
400 {
401  typedef T Type;
402  static const bool ok = true;
403 };
404 template< typename T >
406 {
407  typedef T Type;
408  static const bool ok = true;
409 };
410 
411 // SFINAE helper class
412 template< typename T, bool >
413 struct Enable
414  {
415  };
416 
417 template< typename T >
418 struct Enable< T, true >
419  {
420  typedef T Type;
421  };
422 
423 
424 } /* Namespace */
425 
426 } /* Namespace */
427 
428 #endif // INCLUDED_RTL_STRINGUTILS_HXX
429 
430 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Definition: stringutils.hxx:178
#define SAL_WARN_UNUSED
Annotate classes where a compiler should warn if an instance is unused.
Definition: types.h:611
static const bool ok
Definition: stringutils.hxx:396
Definition: stringutils.hxx:413
sal_uInt16 sal_Unicode
Definition: types.h:123
static const bool ok
Definition: stringutils.hxx:234
Definition: bootstrap.hxx:33
T Type
Definition: stringutils.hxx:420
static bool isValid(char const (&literal)[N])
Definition: stringutils.hxx:245
Definition: stringutils.hxx:180
static const bool ok
Definition: stringutils.hxx:182
static char const * toPointer(char const (&literal)[N])
Definition: stringutils.hxx:256