Skip to content

Commit 29312d3

Browse files
authored
[libc++] Optimize char_traits a bit (#72799)
This implements two kinds of optimizations. Specifically - `char_traits<char8_t>` uses `char` code paths; these are heavily optimized and the operations are equivalent - `char16_t` and `char32_t` `find` uses `std::find` to forward to `wmemchr` if they have the same size
1 parent 225ae82 commit 29312d3

File tree

2 files changed

+43
-48
lines changed

2 files changed

+43
-48
lines changed

libcxx/include/__string/char_traits.h

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
#define _LIBCPP___STRING_CHAR_TRAITS_H
1111

1212
#include <__algorithm/fill_n.h>
13+
#include <__algorithm/find.h>
1314
#include <__algorithm/find_end.h>
1415
#include <__algorithm/find_first_of.h>
1516
#include <__algorithm/min.h>
1617
#include <__assert>
1718
#include <__compare/ordering.h>
1819
#include <__config>
1920
#include <__functional/hash.h>
21+
#include <__functional/identity.h>
2022
#include <__iterator/iterator_traits.h>
2123
#include <__string/constexpr_c_functions.h>
2224
#include <__type_traits/is_constant_evaluated.h>
@@ -272,10 +274,14 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t> {
272274
return std::__constexpr_memcmp(__s1, __s2, __element_count(__n));
273275
}
274276

275-
static _LIBCPP_HIDE_FROM_ABI constexpr size_t length(const char_type* __s) _NOEXCEPT;
277+
static _LIBCPP_HIDE_FROM_ABI constexpr size_t length(const char_type* __str) _NOEXCEPT {
278+
return std::__constexpr_strlen(__str);
279+
}
276280

277281
_LIBCPP_HIDE_FROM_ABI static constexpr const char_type*
278-
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
282+
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
283+
return std::__constexpr_memchr(__s, __a, __n);
284+
}
279285

280286
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 char_type*
281287
move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@@ -307,25 +313,6 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t> {
307313
static inline _LIBCPP_HIDE_FROM_ABI constexpr int_type eof() noexcept { return int_type(EOF); }
308314
};
309315

310-
// TODO use '__builtin_strlen' if it ever supports char8_t ??
311-
inline constexpr size_t char_traits<char8_t>::length(const char_type* __s) _NOEXCEPT {
312-
size_t __len = 0;
313-
for (; !eq(*__s, char_type(0)); ++__s)
314-
++__len;
315-
return __len;
316-
}
317-
318-
// TODO use '__builtin_char_memchr' if it ever supports char8_t ??
319-
inline constexpr const char8_t*
320-
char_traits<char8_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
321-
for (; __n; --__n) {
322-
if (eq(*__s, __a))
323-
return __s;
324-
++__s;
325-
}
326-
return nullptr;
327-
}
328-
329316
#endif // _LIBCPP_HAS_NO_CHAR8_T
330317

331318
template <>
@@ -353,9 +340,15 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char16_t> {
353340
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 int
354341
compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
355342
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t length(const char_type* __s) _NOEXCEPT;
356-
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
357-
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
358343

344+
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
345+
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
346+
__identity __proj;
347+
const char_type* __match = std::__find_impl(__s, __s + __n, __a, __proj);
348+
if (__match == __s + __n)
349+
return nullptr;
350+
return __match;
351+
}
359352
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static char_type*
360353
move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
361354
return std::__constexpr_memmove(__s1, __s2, __element_count(__n));
@@ -408,16 +401,6 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t char_traits<char16_t>::length(const
408401
return __len;
409402
}
410403

411-
inline _LIBCPP_CONSTEXPR_SINCE_CXX17 const char16_t*
412-
char_traits<char16_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
413-
for (; __n; --__n) {
414-
if (eq(*__s, __a))
415-
return __s;
416-
++__s;
417-
}
418-
return nullptr;
419-
}
420-
421404
template <>
422405
struct _LIBCPP_TEMPLATE_VIS char_traits<char32_t> {
423406
using char_type = char32_t;
@@ -443,8 +426,15 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char32_t> {
443426
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 int
444427
compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
445428
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t length(const char_type* __s) _NOEXCEPT;
429+
446430
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
447-
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
431+
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
432+
__identity __proj;
433+
const char_type* __match = std::__find_impl(__s, __s + __n, __a, __proj);
434+
if (__match == __s + __n)
435+
return nullptr;
436+
return __match;
437+
}
448438

449439
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static char_type*
450440
move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@@ -496,16 +486,6 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t char_traits<char32_t>::length(const
496486
return __len;
497487
}
498488

499-
inline _LIBCPP_CONSTEXPR_SINCE_CXX17 const char32_t*
500-
char_traits<char32_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
501-
for (; __n; --__n) {
502-
if (eq(*__s, __a))
503-
return __s;
504-
++__s;
505-
}
506-
return nullptr;
507-
}
508-
509489
// helper fns for basic_string and string_view
510490

511491
// __str_find

libcxx/include/__string/constexpr_c_functions.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,33 @@ _LIBCPP_BEGIN_NAMESPACE_STD
3535
// of elements as opposed to a number of bytes.
3636
enum class __element_count : size_t {};
3737

38-
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const char* __str) {
38+
template <class _Tp>
39+
inline const bool __is_char_type = false;
40+
41+
template <>
42+
inline const bool __is_char_type<char> = true;
43+
44+
#ifndef _LIBCPP_HAS_NO_CHAR8_T
45+
template <>
46+
inline const bool __is_char_type<char8_t> = true;
47+
#endif
48+
49+
template <class _Tp>
50+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const _Tp* __str) _NOEXCEPT {
51+
static_assert(__is_char_type<_Tp>, "__constexpr_strlen only works with char and char8_t");
3952
// GCC currently doesn't support __builtin_strlen for heap-allocated memory during constant evaluation.
4053
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
41-
#ifdef _LIBCPP_COMPILER_GCC
4254
if (__libcpp_is_constant_evaluated()) {
55+
#if _LIBCPP_STD_VER >= 17 && defined(_LIBCPP_COMPILER_CLANG_BASED)
56+
if constexpr (is_same_v<_Tp, char>)
57+
return __builtin_strlen(__str);
58+
#endif
4359
size_t __i = 0;
4460
for (; __str[__i] != '\0'; ++__i)
4561
;
4662
return __i;
4763
}
48-
#endif
49-
return __builtin_strlen(__str);
64+
return __builtin_strlen(reinterpret_cast<const char*>(__str));
5065
}
5166

5267
// Because of __libcpp_is_trivially_lexicographically_comparable we know that comparing the object representations is

0 commit comments

Comments
 (0)