xref: /llvm-project/libcxx/include/__string/constexpr_c_functions.h (revision 1fd08edd5808fdb642ecd2e612ae82d370a90163)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef _LIBCPP___STRING_CONSTEXPR_C_FUNCTIONS_H
10 #define _LIBCPP___STRING_CONSTEXPR_C_FUNCTIONS_H
11 
12 #include <__config>
13 #include <__type_traits/is_constant_evaluated.h>
14 #include <__type_traits/is_equality_comparable.h>
15 #include <__type_traits/is_same.h>
16 #include <__type_traits/is_trivially_lexicographically_comparable.h>
17 #include <__type_traits/remove_cv.h>
18 #include <cstddef>
19 
20 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
21 #  pragma GCC system_header
22 #endif
23 
24 _LIBCPP_BEGIN_NAMESPACE_STD
25 
26 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const char* __str) {
27   // GCC currently doesn't support __builtin_strlen for heap-allocated memory during constant evaluation.
28   // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
29 #ifdef _LIBCPP_COMPILER_GCC
30   if (__libcpp_is_constant_evaluated()) {
31     size_t __i = 0;
32     for (; __str[__i] != '\0'; ++__i)
33       ;
34     return __i;
35   }
36 #endif
37   return __builtin_strlen(__str);
38 }
39 
40 // Because of __libcpp_is_trivially_lexicographically_comparable we know that comparing the object representations is
41 // equivalent to a std::memcmp. Since we have multiple objects contiguously in memory, we can call memcmp once instead
42 // of invoking it on every object individually.
43 template <class _Tp, class _Up>
44 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
45 __constexpr_memcmp(const _Tp* __lhs, const _Up* __rhs, size_t __count) {
46   static_assert(__libcpp_is_trivially_lexicographically_comparable<_Tp, _Up>::value,
47                 "_Tp and _Up have to be trivially lexicographically comparable");
48 
49   if (__libcpp_is_constant_evaluated()) {
50 #ifdef _LIBCPP_COMPILER_CLANG_BASED
51     if (sizeof(_Tp) == 1 && !is_same<_Tp, bool>::value)
52       return __builtin_memcmp(__lhs, __rhs, __count);
53 #endif
54 
55     while (__count != 0) {
56       if (*__lhs < *__rhs)
57         return -1;
58       if (*__rhs < *__lhs)
59         return 1;
60 
61       __count -= sizeof(_Tp);
62       ++__lhs;
63       ++__rhs;
64     }
65     return 0;
66   } else {
67     return __builtin_memcmp(__lhs, __rhs, __count);
68   }
69 }
70 
71 // Because of __libcpp_is_trivially_equality_comparable we know that comparing the object representations is equivalent
72 // to a std::memcmp(...) == 0. Since we have multiple objects contiguously in memory, we can call memcmp once instead
73 // of invoking it on every object individually.
74 template <class _Tp, class _Up>
75 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
76 __constexpr_memcmp_equal(const _Tp* __lhs, const _Up* __rhs, size_t __count) {
77   static_assert(__libcpp_is_trivially_equality_comparable<_Tp, _Up>::value,
78                 "_Tp and _Up have to be trivially equality comparable");
79 
80   if (__libcpp_is_constant_evaluated()) {
81 #ifdef _LIBCPP_COMPILER_CLANG_BASED
82     if (sizeof(_Tp) == 1 && is_integral<_Tp>::value && !is_same<_Tp, bool>::value)
83       return __builtin_memcmp(__lhs, __rhs, __count) == 0;
84 #endif
85     while (__count != 0) {
86       if (*__lhs != *__rhs)
87         return false;
88 
89       __count -= sizeof(_Tp);
90       ++__lhs;
91       ++__rhs;
92     }
93     return true;
94   } else {
95     return __builtin_memcmp(__lhs, __rhs, __count) == 0;
96   }
97 }
98 
99 template <class _Tp, class _Up>
100 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __constexpr_memchr(_Tp* __str, _Up __value, size_t __count) {
101   static_assert(sizeof(_Tp) == 1 && __libcpp_is_trivially_equality_comparable<_Tp, _Up>::value,
102                 "Calling memchr on non-trivially equality comparable types is unsafe.");
103 
104   if (__libcpp_is_constant_evaluated()) {
105 // use __builtin_char_memchr to optimize constexpr evaluation if we can
106 #if _LIBCPP_STD_VER >= 17 && __has_builtin(__builtin_char_memchr)
107     if constexpr (is_same_v<remove_cv_t<_Tp>, char> && is_same_v<remove_cv_t<_Up>, char>)
108       return __builtin_char_memchr(__str, __value, __count);
109 #endif
110 
111     for (; __count; --__count) {
112       if (*__str == __value)
113         return __str;
114       ++__str;
115     }
116     return nullptr;
117   } else {
118     char __value_buffer = 0;
119     __builtin_memcpy(&__value_buffer, &__value, sizeof(char));
120     return static_cast<_Tp*>(__builtin_memchr(__str, __value_buffer, __count));
121   }
122 }
123 
124 _LIBCPP_END_NAMESPACE_STD
125 
126 #endif // _LIBCPP___STRING_CONSTEXPR_C_FUNCTIONS_H
127