Skip to content

Commit 7b104b2

Browse files
committed
[libc++] Optimize char_traits a bit
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 5a305ce commit 7b104b2

File tree

2 files changed

+51
-80
lines changed

2 files changed

+51
-80
lines changed

libcxx/include/__string/char_traits.h

Lines changed: 42 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111

1212
#include <__algorithm/copy_n.h>
1313
#include <__algorithm/fill_n.h>
14+
#include <__algorithm/find.h>
1415
#include <__algorithm/find_end.h>
1516
#include <__algorithm/find_first_of.h>
1617
#include <__algorithm/min.h>
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>
@@ -355,34 +357,35 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t>
355357
{return __c1 < __c2;}
356358

357359
static _LIBCPP_HIDE_FROM_ABI constexpr int
358-
compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
360+
compare(const char_type* __s1, const char_type* __s2, size_t __n) noexcept {
359361
return std::__constexpr_memcmp(__s1, __s2, __element_count(__n));
360362
}
361363

362-
static _LIBCPP_HIDE_FROM_ABI constexpr
363-
size_t length(const char_type* __s) _NOEXCEPT;
364+
static _LIBCPP_HIDE_FROM_ABI constexpr size_t length(const char_type* __str) noexcept {
365+
return std::__constexpr_strlen(__str);
366+
}
364367

365-
_LIBCPP_INLINE_VISIBILITY static constexpr
366-
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
368+
static _LIBCPP_HIDE_FROM_ABI constexpr const char_type*
369+
find(const char_type* __s, size_t __n, const char_type& __a) noexcept {
370+
return std::__constexpr_memchr(__s, __a, __n);
371+
}
367372

368-
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
369-
char_type* move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
370-
return std::__constexpr_memmove(__s1, __s2, __element_count(__n));
371-
}
373+
static _LIBCPP_HIDE_FROM_ABI constexpr char_type*
374+
move(char_type* __s1, const char_type* __s2, size_t __n) noexcept {
375+
return std::__constexpr_memmove(__s1, __s2, __element_count(__n));
376+
}
372377

373-
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
374-
char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
375-
_LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(!std::__is_pointer_in_range(__s1, __s1 + __n, __s2),
376-
"char_traits::copy: source and destination ranges overlap");
377-
std::copy_n(__s2, __n, __s1);
378-
return __s1;
379-
}
378+
static _LIBCPP_HIDE_FROM_ABI constexpr char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) noexcept {
379+
_LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(!std::__is_pointer_in_range(__s1, __s1 + __n, __s2),
380+
"char_traits::copy: source and destination ranges overlap");
381+
std::copy_n(__s2, __n, __s1);
382+
return __s1;
383+
}
380384

381-
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
382-
char_type* assign(char_type* __s, size_t __n, char_type __a) _NOEXCEPT {
383-
std::fill_n(__s, __n, __a);
384-
return __s;
385-
}
385+
static _LIBCPP_HIDE_FROM_ABI constexpr char_type* assign(char_type* __s, size_t __n, char_type __a) noexcept {
386+
std::fill_n(__s, __n, __a);
387+
return __s;
388+
}
386389

387390
static inline _LIBCPP_HIDE_FROM_ABI constexpr int_type not_eof(int_type __c) noexcept
388391
{return eq_int_type(__c, eof()) ? ~eof() : __c;}
@@ -396,31 +399,6 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t>
396399
{return int_type(EOF);}
397400
};
398401

399-
// TODO use '__builtin_strlen' if it ever supports char8_t ??
400-
inline constexpr
401-
size_t
402-
char_traits<char8_t>::length(const char_type* __s) _NOEXCEPT
403-
{
404-
size_t __len = 0;
405-
for (; !eq(*__s, char_type(0)); ++__s)
406-
++__len;
407-
return __len;
408-
}
409-
410-
// TODO use '__builtin_char_memchr' if it ever supports char8_t ??
411-
inline constexpr
412-
const char8_t*
413-
char_traits<char8_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
414-
{
415-
for (; __n; --__n)
416-
{
417-
if (eq(*__s, __a))
418-
return __s;
419-
++__s;
420-
}
421-
return nullptr;
422-
}
423-
424402
#endif // _LIBCPP_HAS_NO_CHAR8_T
425403

426404
template <>
@@ -446,8 +424,15 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char16_t>
446424
int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
447425
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
448426
size_t length(const char_type* __s) _NOEXCEPT;
449-
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
450-
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
427+
428+
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
429+
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
430+
__identity __proj;
431+
const char_type* __match = std::__find_impl(__s, __s + __n, __a, __proj);
432+
if (__match == __s + __n)
433+
return nullptr;
434+
return __match;
435+
}
451436

452437
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
453438
static char_type* move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@@ -504,19 +489,6 @@ char_traits<char16_t>::length(const char_type* __s) _NOEXCEPT
504489
return __len;
505490
}
506491

507-
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
508-
const char16_t*
509-
char_traits<char16_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
510-
{
511-
for (; __n; --__n)
512-
{
513-
if (eq(*__s, __a))
514-
return __s;
515-
++__s;
516-
}
517-
return nullptr;
518-
}
519-
520492
template <>
521493
struct _LIBCPP_TEMPLATE_VIS char_traits<char32_t>
522494
{
@@ -540,8 +512,15 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char32_t>
540512
int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
541513
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
542514
size_t length(const char_type* __s) _NOEXCEPT;
543-
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
544-
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
515+
516+
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
517+
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
518+
__identity __proj;
519+
const char_type* __match = std::__find_impl(__s, __s + __n, __a, __proj);
520+
if (__match == __s + __n)
521+
return nullptr;
522+
return __match;
523+
}
545524

546525
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
547526
static char_type* move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@@ -596,19 +575,6 @@ char_traits<char32_t>::length(const char_type* __s) _NOEXCEPT
596575
return __len;
597576
}
598577

599-
inline _LIBCPP_CONSTEXPR_SINCE_CXX17
600-
const char32_t*
601-
char_traits<char32_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
602-
{
603-
for (; __n; --__n)
604-
{
605-
if (eq(*__s, __a))
606-
return __s;
607-
++__s;
608-
}
609-
return nullptr;
610-
}
611-
612578
// helper fns for basic_string and string_view
613579

614580
// __str_find

libcxx/include/__string/constexpr_c_functions.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,23 @@ _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 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const _Tp* __str) _NOEXCEPT {
40+
static_assert(
41+
is_integral<_Tp>::value && sizeof(_Tp) == 1, "__constexpr_strlen only works with integral types of size 1");
3942
// GCC currently doesn't support __builtin_strlen for heap-allocated memory during constant evaluation.
4043
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
41-
#ifdef _LIBCPP_COMPILER_GCC
4244
if (__libcpp_is_constant_evaluated()) {
45+
#if _LIBCPP_STD_VER >= 17 && defined(_LIBCPP_COMPILER_CLANG_BASED)
46+
if constexpr (is_same_v<_Tp, char>)
47+
return __builtin_strlen(__str);
48+
#endif
4349
size_t __i = 0;
4450
for (; __str[__i] != '\0'; ++__i)
4551
;
4652
return __i;
4753
}
48-
#endif
49-
return __builtin_strlen(__str);
54+
return __builtin_strlen(reinterpret_cast<const char*>(__str));
5055
}
5156

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

0 commit comments

Comments
 (0)