tdf#154285 Check upper bound of arguments in SbRtl_Minute function
[LibreOffice.git] / include / o3tl / string_view.hxx
blob0fe51fd98eda6f11fb00642d0978fddca9464a9c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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/.
8 */
10 #pragma once
12 #include <sal/config.h>
14 #include <cassert>
15 #include <cstddef>
16 #include <string>
17 #include <string_view>
18 #include <type_traits>
20 #include <o3tl/intcmp.hxx>
21 #include <rtl/character.hxx>
22 #include <rtl/ustring.h>
23 #include <rtl/math.h>
24 #include <sal/types.h>
26 namespace o3tl
28 // Like OUString::equalsAscii/OUString::equalsAsciiL, but for std::u16string_view:
29 inline bool equalsAscii(std::u16string_view s1, std::string_view s2)
31 return s1.size() == s2.size()
32 && rtl_ustr_ascii_shortenedCompare_WithLength(s1.data(), s1.size(), s2.data(), s2.size())
33 == 0;
36 // Like OUString::compareToAscii, but for std::u16string_view and std::string_view:
37 inline int compareToAscii(std::u16string_view s1, std::string_view s2)
39 return rtl_ustr_asciil_reverseCompare_WithLength(s1.data(), s1.size(), s2.data(), s2.size());
42 // Like OUString::equalsIgnoreAsciiCase, but for two std::u16string_view:
43 inline bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
45 if (s1.size() != s2.size())
46 return false;
47 if (s1.data() == s2.data())
48 return true;
49 return rtl_ustr_compareIgnoreAsciiCase_WithLength(s1.data(), s1.size(), s2.data(), s2.size())
50 == 0;
53 inline bool equalsIgnoreAsciiCase(std::u16string_view s1, std::string_view s2)
55 return s1.size() == s2.size()
56 && (rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength(s1.data(), s1.size(),
57 s2.data(), s2.size())
58 == 0);
61 inline bool equalsIgnoreAsciiCase(std::string_view s1, std::string_view s2)
63 if (s1.size() != s2.size())
64 return false;
65 if (s1.data() == s2.data())
66 return true;
67 return rtl_str_compareIgnoreAsciiCase_WithLength(s1.data(), s1.size(), s2.data(), s2.size())
68 == 0;
71 // Like OUString::compareToIgnoreAsciiCase, but for two std::u16string_view:
72 inline int compareToIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
74 return rtl_ustr_compareIgnoreAsciiCase_WithLength(s1.data(), s1.size(), s2.data(), s2.size());
77 // Like OUString::matchIgnoreAsciiCase, but for two std::u16string_view:
78 inline bool matchIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2,
79 sal_Int32 fromIndex = 0)
81 return rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength(
82 s1.data() + fromIndex, s1.size() - fromIndex, s2.data(), s2.size(), s2.size())
83 == 0;
86 // Like OUString::matchIgnoreAsciiCase, but for std::u16string_view and std::string_view:
87 inline bool matchIgnoreAsciiCase(std::u16string_view s1, std::string_view s2,
88 sal_Int32 fromIndex = 0)
90 return rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength(
91 s1.data() + fromIndex, s1.size() - fromIndex, s2.data(), s2.size())
92 == 0;
95 // Like OUString::endsWithIgnoreAsciiCase, but for std::u16string_view
96 inline bool endsWithIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2,
97 std::u16string_view* rest = nullptr)
99 auto const b = s2.size() <= s1.size() && matchIgnoreAsciiCase(s1, s2, s1.size() - s2.size());
100 if (b && rest != nullptr)
102 *rest = s1.substr(0, s1.size() - s2.size());
104 return b;
107 inline bool endsWithIgnoreAsciiCase(std::u16string_view s1, std::string_view s2,
108 std::u16string_view* rest = nullptr)
110 auto const b = s2.size() <= s1.size()
111 && rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
112 s1.data() + s1.size() - s2.size(), s2.size(), s2.data(), s2.size())
113 == 0;
114 if (b && rest != nullptr)
116 *rest = s1.substr(0, s1.size() - s2.size());
118 return b;
121 // Similar to O[U]String::getToken, returning the first token of a std::[u16]string_view starting
122 // at a given position.
124 // Attention: There are two sets of o3tl::getToken overloads here. This first set has an interface
125 // based on std::size_t length parameters, and its semantics don't match those of
126 // O[U]String::getToken exactly (buf if needed, it can be extended to return the n'th token instead
127 // of just the first, and/or support an initial position of npos, to make the semantics match).
128 template <typename charT, typename traits = std::char_traits<charT>>
129 inline std::basic_string_view<charT, traits> getToken(std::basic_string_view<charT, traits> sv,
130 charT delimiter, std::size_t& position)
132 assert(position <= sv.size());
133 auto const n = sv.find(delimiter, position);
134 std::basic_string_view<charT, traits> t;
135 if (n == std::string_view::npos)
137 t = sv.substr(position);
138 position = std::string_view::npos;
140 else
142 t = sv.substr(position, n - position);
143 position = n + 1;
145 return t;
147 // The following two overloads prevent overload resolution mistakes that would occur with their
148 // template counterpart, when sv is of a type that is implicitly convertible to basic_string_view
149 // (like OString or OUString), in which case overload resolution would erroneously choose the
150 // three-argument overloads (taking sv, nToken, cTok) from the second set of
151 // o3tl::getToken overloads below:
152 inline std::string_view getToken(std::string_view sv, char delimiter, std::size_t& position)
154 return getToken<char>(sv, delimiter, position);
156 inline std::u16string_view getToken(std::u16string_view sv, char16_t delimiter,
157 std::size_t& position)
159 return getToken<char16_t>(sv, delimiter, position);
162 // Similar to O[U]String::getToken.
164 // Attention: There are two sets of o3tl::getToken overloads here. This second set has an
165 // interface based on sal_Int32 length parameters, and is meant to be a drop-in replacement for
166 // O[U]String::getToken.
167 template <typename charT, typename traits = std::char_traits<charT>>
168 inline std::basic_string_view<charT, traits> getToken(std::basic_string_view<charT, traits> pStr,
169 sal_Int32 nToken, charT cTok,
170 sal_Int32& rnIndex)
172 assert(o3tl::IntCmp(rnIndex) <= o3tl::IntCmp(pStr.size()));
174 // Return an empty string and set rnIndex to -1 if either nToken or rnIndex is
175 // negative:
176 if (rnIndex >= 0 && nToken >= 0)
178 const charT* pOrgCharStr = pStr.data();
179 const charT* pCharStr = pOrgCharStr + rnIndex;
180 sal_Int32 nLen = pStr.size() - rnIndex;
181 sal_Int32 nTokCount = 0;
182 const charT* pCharStrStart = pCharStr;
183 while (nLen > 0)
185 if (*pCharStr == cTok)
187 nTokCount++;
189 if (nTokCount > nToken)
190 break;
191 if (nTokCount == nToken)
192 pCharStrStart = pCharStr + 1;
195 pCharStr++;
196 nLen--;
198 if (nTokCount >= nToken)
200 if (nLen > 0)
201 rnIndex = pCharStr - pOrgCharStr + 1;
202 else
203 rnIndex = -1;
204 return std::basic_string_view<charT, traits>(pCharStrStart, pCharStr - pCharStrStart);
208 rnIndex = -1;
209 return std::basic_string_view<charT, traits>();
211 // The following two overloads prevent deduction failures that would occur with their template
212 // counterpart, when sv is of a type that is implicitly convertible to basic_string_view (like
213 // OString or OUString):
214 inline std::string_view getToken(std::string_view sv, sal_Int32 nToken, char cTok,
215 sal_Int32& rnIndex)
217 return getToken<char>(sv, nToken, cTok, rnIndex);
219 inline std::u16string_view getToken(std::u16string_view sv, sal_Int32 nToken, char16_t cTok,
220 sal_Int32& rnIndex)
222 return getToken<char16_t>(sv, nToken, cTok, rnIndex);
224 inline std::string_view getToken(std::string_view sv, sal_Int32 nToken, char cTok)
226 sal_Int32 nIndex = 0;
227 return getToken<char>(sv, nToken, cTok, nIndex);
229 inline std::u16string_view getToken(std::u16string_view sv, sal_Int32 nToken, char16_t cTok)
231 sal_Int32 nIndex = 0;
232 return getToken<char16_t>(sv, nToken, cTok, nIndex);
235 // Implementations of C++20 std::basic_string_view::starts_with and
236 // std::basic_string_view::ends_with, until we can use those directly on all platforms:
237 template <typename charT, typename traits = std::char_traits<charT>>
238 constexpr bool starts_with(std::basic_string_view<charT, traits> sv,
239 std::basic_string_view<charT, traits> x) noexcept
241 #if defined __cpp_lib_starts_ends_with
242 return sv.starts_with(x);
243 #else
244 return sv.substr(0, x.size()) == x;
245 #endif
247 template <typename charT, typename traits = std::char_traits<charT>>
248 constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT x) noexcept
250 #if defined __cpp_lib_starts_ends_with
251 return sv.starts_with(x);
252 #else
253 return !sv.empty() && traits::eq(sv.front(), x);
254 #endif
256 template <typename charT, typename traits = std::char_traits<charT>>
257 constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT const* x)
259 #if defined __cpp_lib_starts_ends_with
260 return sv.starts_with(x);
261 #else
262 return starts_with(sv, std::basic_string_view<charT, traits>(x));
263 #endif
265 template <typename charT, typename traits = std::char_traits<charT>>
266 constexpr bool ends_with(std::basic_string_view<charT, traits> sv,
267 std::basic_string_view<charT, traits> x) noexcept
269 #if defined __cpp_lib_starts_ends_with
270 return sv.ends_with(x);
271 #else
272 return sv.size() >= x.size()
273 && sv.compare(sv.size() - x.size(), std::basic_string_view<charT, traits>::npos, x) == 0;
274 #endif
276 template <typename charT, typename traits = std::char_traits<charT>>
277 constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT x) noexcept
279 #if defined __cpp_lib_starts_ends_with
280 return sv.ends_with(x);
281 #else
282 return !sv.empty() && traits::eq(sv.back(), x);
283 #endif
285 template <typename charT, typename traits = std::char_traits<charT>>
286 constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT const* x)
288 #if defined __cpp_lib_starts_ends_with
289 return sv.ends_with(x);
290 #else
291 return ends_with(sv, std::basic_string_view<charT, traits>(x));
292 #endif
294 // The following overloads prevent deduction failures that would occur with their template
295 // counterparts, when x is of a type that is implicitly convertible to basic_string_view (like
296 // OString or OUString, and we only bother to provide overloads for the char and char16_t cases, not
297 // also for char32_t and wchar_t, nor for C++20 char8_t):
298 constexpr bool starts_with(std::string_view sv, std::string_view x) noexcept
300 return starts_with<char>(sv, x);
302 constexpr bool starts_with(std::u16string_view sv, std::u16string_view x) noexcept
304 return starts_with<char16_t>(sv, x);
306 constexpr bool ends_with(std::string_view sv, std::string_view x) noexcept
308 return ends_with<char>(sv, x);
310 constexpr bool ends_with(std::u16string_view sv, std::u16string_view x) noexcept
312 return ends_with<char16_t>(sv, x);
315 // Variants of C++20 std::basic_string_view::starts_with and
316 // std::basic_string_view::ends_with that have a rest out parameter, similar to our OString and
317 // OUString startsWith and endsWith member functions:
318 template <typename charT, typename traits = std::char_traits<charT>>
319 constexpr bool starts_with(std::basic_string_view<charT, traits> sv,
320 std::basic_string_view<charT, traits> x,
321 std::basic_string_view<charT, traits>* rest) noexcept
323 assert(rest != nullptr);
324 auto const found = starts_with(sv, x);
325 if (found)
327 *rest = sv.substr(x.length());
329 return found;
331 template <typename charT, typename traits = std::char_traits<charT>>
332 constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT x,
333 std::basic_string_view<charT, traits>* rest) noexcept
335 assert(rest != nullptr);
336 auto const found = starts_with(sv, x);
337 if (found)
339 *rest = sv.substr(1);
341 return found;
343 template <typename charT, typename traits = std::char_traits<charT>>
344 constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT const* x,
345 std::basic_string_view<charT, traits>* rest)
347 assert(rest != nullptr);
348 auto const found = starts_with(sv, x);
349 if (found)
351 *rest = sv.substr(traits::length(x));
353 return found;
355 template <typename charT, typename traits = std::char_traits<charT>>
356 constexpr bool ends_with(std::basic_string_view<charT, traits> sv,
357 std::basic_string_view<charT, traits> x,
358 std::basic_string_view<charT, traits>* rest) noexcept
360 assert(rest != nullptr);
361 auto const found = ends_with(sv, x);
362 if (found)
364 *rest = sv.substr(0, sv.length() - x.length());
366 return found;
368 template <typename charT, typename traits = std::char_traits<charT>>
369 constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT x,
370 std::basic_string_view<charT, traits>* rest) noexcept
372 assert(rest != nullptr);
373 auto const found = ends_with(sv, x);
374 if (found)
376 *rest = sv.substr(0, sv.length() - 1);
378 return found;
380 template <typename charT, typename traits = std::char_traits<charT>>
381 constexpr bool ends_with(std::basic_string_view<charT, traits> sv, charT const* x,
382 std::basic_string_view<charT, traits>* rest)
384 assert(rest != nullptr);
385 auto const found = ends_with(sv, x);
386 if (found)
388 *rest = sv.substr(0, sv.length() - traits::length(x));
390 return found;
392 // The following overloads prevent deduction failures that would occur with their template
393 // counterparts, when x is of a type that is implicitly convertible to basic_string_view (like
394 // OString or OUString, and we only bother to provide overloads for the char and char16_t cases, not
395 // also for char32_t and wchar_t, nor for C++20 char8_t):
396 constexpr bool starts_with(std::string_view sv, std::string_view x, std::string_view* rest) noexcept
398 return starts_with<char>(sv, x, rest);
400 constexpr bool starts_with(std::u16string_view sv, std::u16string_view x,
401 std::u16string_view* rest) noexcept
403 return starts_with<char16_t>(sv, x, rest);
405 constexpr bool ends_with(std::string_view sv, std::string_view x, std::string_view* rest) noexcept
407 return ends_with<char>(sv, x, rest);
409 constexpr bool ends_with(std::u16string_view sv, std::u16string_view x,
410 std::u16string_view* rest) noexcept
412 return ends_with<char16_t>(sv, x, rest);
415 namespace internal
417 inline bool implIsWhitespace(sal_Unicode c)
419 /* Space or Control character? */
420 if ((c <= 32) && c)
421 return true;
423 /* Only in the General Punctuation area Space or Control characters are included? */
424 if ((c < 0x2000) || (c > 0x2029))
425 return false;
427 if ((c <= 0x200B) || /* U+2000 - U+200B All Spaces */
428 (c >= 0x2028)) /* U+2028 LINE SEPARATOR, U+2029 PARAGRAPH SEPARATOR */
429 return true;
431 return false;
433 } // namespace internal
435 // Like OUString::trim, but for std::[u16]string_view:
436 template <typename charT, typename traits = std::char_traits<charT>>
437 std::basic_string_view<charT, traits> trim(std::basic_string_view<charT, traits> str)
439 auto pFirst = str.data();
440 auto pLast = pFirst + str.size();
442 while ((pFirst < pLast) && internal::implIsWhitespace(*pFirst))
443 ++pFirst;
445 if (pFirst == pLast)
446 return {};
449 --pLast;
450 while (internal::implIsWhitespace(*pLast));
452 return std::basic_string_view<charT, traits>(pFirst, pLast - pFirst + 1);
455 // "deduction guides"
457 inline auto trim(std::string_view str) { return trim<>(str); }
458 inline auto trim(std::u16string_view str) { return trim<>(str); }
460 // Like OString::toInt32, but for std::string_view:
461 inline sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix = 10)
463 sal_Int64 n = rtl_ustr_toInt64_WithLength(str.data(), radix, str.size());
464 if (n < SAL_MIN_INT32 || n > SAL_MAX_INT32)
465 n = 0;
466 return n;
468 inline sal_Int32 toInt32(std::string_view str, sal_Int16 radix = 10)
470 sal_Int64 n = rtl_str_toInt64_WithLength(str.data(), radix, str.size());
471 if (n < SAL_MIN_INT32 || n > SAL_MAX_INT32)
472 n = 0;
473 return n;
476 // Like OString::toUInt32, but for std::string_view:
477 inline sal_uInt32 toUInt32(std::u16string_view str, sal_Int16 radix = 10)
479 sal_Int64 n = rtl_ustr_toInt64_WithLength(str.data(), radix, str.size());
480 if (n < 0 || n > SAL_MAX_UINT32)
481 n = 0;
482 return n;
484 inline sal_uInt32 toUInt32(std::string_view str, sal_Int16 radix = 10)
486 sal_Int64 n = rtl_str_toInt64_WithLength(str.data(), radix, str.size());
487 if (n < 0 || n > SAL_MAX_UINT32)
488 n = 0;
489 return n;
492 // Like OString::toInt64, but for std::string_view:
493 inline sal_Int64 toInt64(std::u16string_view str, sal_Int16 radix = 10)
495 return rtl_ustr_toInt64_WithLength(str.data(), radix, str.size());
497 inline sal_Int64 toInt64(std::string_view str, sal_Int16 radix = 10)
499 return rtl_str_toInt64_WithLength(str.data(), radix, str.size());
502 // Like OString::toDouble, but for std::string_view:
503 inline double toDouble(std::u16string_view str)
505 return rtl_math_uStringToDouble(str.data(), str.data() + str.size(), '.', 0, nullptr, nullptr);
507 inline double toDouble(std::string_view str)
509 return rtl_math_stringToDouble(str.data(), str.data() + str.size(), '.', 0, nullptr, nullptr);
512 // Like OUString::iterateCodePoints, but for std::string_view:
513 template <typename T>
514 requires(std::is_same_v<T, sal_Int32> || std::is_same_v<T, std::size_t>) sal_uInt32
515 iterateCodePoints(std::u16string_view string, T* indexUtf16, sal_Int32 incrementCodePoints = 1)
517 std::size_t n;
518 char16_t cu;
519 sal_uInt32 cp;
520 assert(indexUtf16 != nullptr);
521 n = *indexUtf16;
522 assert(n <= string.length());
523 while (incrementCodePoints < 0)
525 assert(n > 0);
526 cu = string[--n];
527 if (rtl::isLowSurrogate(cu) && n != 0 && rtl::isHighSurrogate(string[n - 1]))
529 --n;
531 ++incrementCodePoints;
533 assert(n < string.length());
534 cu = string[n];
535 if (rtl::isHighSurrogate(cu) && string.length() - n >= 2 && rtl::isLowSurrogate(string[n + 1]))
537 cp = rtl::combineSurrogates(cu, string[n + 1]);
539 else
541 cp = cu;
543 while (incrementCodePoints > 0)
545 assert(n < string.length());
546 cu = string[n++];
547 if (rtl::isHighSurrogate(cu) && n != string.length() && rtl::isLowSurrogate(string[n]))
549 ++n;
551 --incrementCodePoints;
553 assert(n <= string.length());
554 *indexUtf16 = n;
555 return cp;
558 } // namespace
560 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */