1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
12 #include <sal/config.h>
17 #include <string_view>
18 #include <type_traits>
20 #include <o3tl/intcmp.hxx>
21 #include <rtl/character.hxx>
22 #include <rtl/ustring.h>
24 #include <sal/types.h>
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())
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())
47 if (s1
.data() == s2
.data())
49 return rtl_ustr_compareIgnoreAsciiCase_WithLength(s1
.data(), s1
.size(), s2
.data(), s2
.size())
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(),
61 inline bool equalsIgnoreAsciiCase(std::string_view s1
, std::string_view s2
)
63 if (s1
.size() != s2
.size())
65 if (s1
.data() == s2
.data())
67 return rtl_str_compareIgnoreAsciiCase_WithLength(s1
.data(), s1
.size(), s2
.data(), s2
.size())
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())
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())
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());
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())
114 if (b
&& rest
!= nullptr)
116 *rest
= s1
.substr(0, s1
.size() - s2
.size());
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
;
142 t
= sv
.substr(position
, n
- position
);
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
,
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
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
;
185 if (*pCharStr
== cTok
)
189 if (nTokCount
> nToken
)
191 if (nTokCount
== nToken
)
192 pCharStrStart
= pCharStr
+ 1;
198 if (nTokCount
>= nToken
)
201 rnIndex
= pCharStr
- pOrgCharStr
+ 1;
204 return std::basic_string_view
<charT
, traits
>(pCharStrStart
, pCharStr
- pCharStrStart
);
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
,
217 return getToken
<char>(sv
, nToken
, cTok
, rnIndex
);
219 inline std::u16string_view
getToken(std::u16string_view sv
, sal_Int32 nToken
, char16_t cTok
,
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
);
244 return sv
.substr(0, x
.size()) == x
;
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
);
253 return !sv
.empty() && traits::eq(sv
.front(), x
);
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
);
262 return starts_with(sv
, std::basic_string_view
<charT
, traits
>(x
));
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
);
272 return sv
.size() >= x
.size()
273 && sv
.compare(sv
.size() - x
.size(), std::basic_string_view
<charT
, traits
>::npos
, x
) == 0;
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
);
282 return !sv
.empty() && traits::eq(sv
.back(), x
);
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
);
291 return ends_with(sv
, std::basic_string_view
<charT
, traits
>(x
));
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
);
327 *rest
= sv
.substr(x
.length());
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
);
339 *rest
= sv
.substr(1);
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
);
351 *rest
= sv
.substr(traits::length(x
));
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
);
364 *rest
= sv
.substr(0, sv
.length() - x
.length());
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
);
376 *rest
= sv
.substr(0, sv
.length() - 1);
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
);
388 *rest
= sv
.substr(0, sv
.length() - traits::length(x
));
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
);
417 inline bool implIsWhitespace(sal_Unicode c
)
419 /* Space or Control character? */
423 /* Only in the General Punctuation area Space or Control characters are included? */
424 if ((c
< 0x2000) || (c
> 0x2029))
427 if ((c
<= 0x200B) || /* U+2000 - U+200B All Spaces */
428 (c
>= 0x2028)) /* U+2028 LINE SEPARATOR, U+2029 PARAGRAPH SEPARATOR */
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
))
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
)
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
)
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
)
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
)
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)
520 assert(indexUtf16
!= nullptr);
522 assert(n
<= string
.length());
523 while (incrementCodePoints
< 0)
527 if (rtl::isLowSurrogate(cu
) && n
!= 0 && rtl::isHighSurrogate(string
[n
- 1]))
531 ++incrementCodePoints
;
533 assert(n
< string
.length());
535 if (rtl::isHighSurrogate(cu
) && string
.length() - n
>= 2 && rtl::isLowSurrogate(string
[n
+ 1]))
537 cp
= rtl::combineSurrogates(cu
, string
[n
+ 1]);
543 while (incrementCodePoints
> 0)
545 assert(n
< string
.length());
547 if (rtl::isHighSurrogate(cu
) && n
!= string
.length() && rtl::isLowSurrogate(string
[n
]))
551 --incrementCodePoints
;
553 assert(n
<= string
.length());
560 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */