xref: /freebsd-src/contrib/llvm-project/libcxx/include/__string/constexpr_c_functions.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
106c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
206c3fb27SDimitry Andric //
306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606c3fb27SDimitry Andric //
706c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
806c3fb27SDimitry Andric 
906c3fb27SDimitry Andric #ifndef _LIBCPP___STRING_CONSTEXPR_C_FUNCTIONS_H
1006c3fb27SDimitry Andric #define _LIBCPP___STRING_CONSTEXPR_C_FUNCTIONS_H
1106c3fb27SDimitry Andric 
1206c3fb27SDimitry Andric #include <__config>
1306c3fb27SDimitry Andric #include <__memory/addressof.h>
1406c3fb27SDimitry Andric #include <__memory/construct_at.h>
1506c3fb27SDimitry Andric #include <__type_traits/datasizeof.h>
1606c3fb27SDimitry Andric #include <__type_traits/is_always_bitcastable.h>
1706c3fb27SDimitry Andric #include <__type_traits/is_assignable.h>
1806c3fb27SDimitry Andric #include <__type_traits/is_constant_evaluated.h>
1906c3fb27SDimitry Andric #include <__type_traits/is_constructible.h>
2006c3fb27SDimitry Andric #include <__type_traits/is_equality_comparable.h>
2106c3fb27SDimitry Andric #include <__type_traits/is_same.h>
2206c3fb27SDimitry Andric #include <__type_traits/is_trivially_copyable.h>
2306c3fb27SDimitry Andric #include <__type_traits/is_trivially_lexicographically_comparable.h>
2406c3fb27SDimitry Andric #include <__type_traits/remove_cv.h>
2506c3fb27SDimitry Andric #include <__utility/is_pointer_in_range.h>
2606c3fb27SDimitry Andric #include <cstddef>
2706c3fb27SDimitry Andric 
2806c3fb27SDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
2906c3fb27SDimitry Andric #  pragma GCC system_header
3006c3fb27SDimitry Andric #endif
3106c3fb27SDimitry Andric 
3206c3fb27SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
3306c3fb27SDimitry Andric 
3406c3fb27SDimitry Andric // Type used to encode that a function takes an integer that represents a number
3506c3fb27SDimitry Andric // of elements as opposed to a number of bytes.
3606c3fb27SDimitry Andric enum class __element_count : size_t {};
3706c3fb27SDimitry Andric 
38*0fca6ea1SDimitry Andric template <class _Tp>
39*0fca6ea1SDimitry Andric inline const bool __is_char_type = false;
40*0fca6ea1SDimitry Andric 
41*0fca6ea1SDimitry Andric template <>
42*0fca6ea1SDimitry Andric inline const bool __is_char_type<char> = true;
43*0fca6ea1SDimitry Andric 
44*0fca6ea1SDimitry Andric #ifndef _LIBCPP_HAS_NO_CHAR8_T
45*0fca6ea1SDimitry Andric template <>
46*0fca6ea1SDimitry Andric inline const bool __is_char_type<char8_t> = true;
47*0fca6ea1SDimitry Andric #endif
48*0fca6ea1SDimitry Andric 
49*0fca6ea1SDimitry Andric template <class _Tp>
50*0fca6ea1SDimitry Andric inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const _Tp* __str) _NOEXCEPT {
51*0fca6ea1SDimitry Andric   static_assert(__is_char_type<_Tp>, "__constexpr_strlen only works with char and char8_t");
5206c3fb27SDimitry Andric   // GCC currently doesn't support __builtin_strlen for heap-allocated memory during constant evaluation.
5306c3fb27SDimitry Andric   // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
5406c3fb27SDimitry Andric   if (__libcpp_is_constant_evaluated()) {
55*0fca6ea1SDimitry Andric #if _LIBCPP_STD_VER >= 17 && defined(_LIBCPP_COMPILER_CLANG_BASED)
56*0fca6ea1SDimitry Andric     if constexpr (is_same_v<_Tp, char>)
57*0fca6ea1SDimitry Andric       return __builtin_strlen(__str);
58*0fca6ea1SDimitry Andric #endif
5906c3fb27SDimitry Andric     size_t __i = 0;
6006c3fb27SDimitry Andric     for (; __str[__i] != '\0'; ++__i)
6106c3fb27SDimitry Andric       ;
6206c3fb27SDimitry Andric     return __i;
6306c3fb27SDimitry Andric   }
64*0fca6ea1SDimitry Andric   return __builtin_strlen(reinterpret_cast<const char*>(__str));
6506c3fb27SDimitry Andric }
6606c3fb27SDimitry Andric 
6706c3fb27SDimitry Andric // Because of __libcpp_is_trivially_lexicographically_comparable we know that comparing the object representations is
6806c3fb27SDimitry Andric // equivalent to a std::memcmp. Since we have multiple objects contiguously in memory, we can call memcmp once instead
6906c3fb27SDimitry Andric // of invoking it on every object individually.
7006c3fb27SDimitry Andric template <class _Tp, class _Up>
7106c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
7206c3fb27SDimitry Andric __constexpr_memcmp(const _Tp* __lhs, const _Up* __rhs, __element_count __n) {
7306c3fb27SDimitry Andric   static_assert(__libcpp_is_trivially_lexicographically_comparable<_Tp, _Up>::value,
7406c3fb27SDimitry Andric                 "_Tp and _Up have to be trivially lexicographically comparable");
7506c3fb27SDimitry Andric 
7606c3fb27SDimitry Andric   auto __count = static_cast<size_t>(__n);
7706c3fb27SDimitry Andric 
7806c3fb27SDimitry Andric   if (__libcpp_is_constant_evaluated()) {
7906c3fb27SDimitry Andric #ifdef _LIBCPP_COMPILER_CLANG_BASED
8006c3fb27SDimitry Andric     if (sizeof(_Tp) == 1 && !is_same<_Tp, bool>::value)
8106c3fb27SDimitry Andric       return __builtin_memcmp(__lhs, __rhs, __count * sizeof(_Tp));
8206c3fb27SDimitry Andric #endif
8306c3fb27SDimitry Andric 
8406c3fb27SDimitry Andric     while (__count != 0) {
8506c3fb27SDimitry Andric       if (*__lhs < *__rhs)
8606c3fb27SDimitry Andric         return -1;
8706c3fb27SDimitry Andric       if (*__rhs < *__lhs)
8806c3fb27SDimitry Andric         return 1;
8906c3fb27SDimitry Andric 
9006c3fb27SDimitry Andric       --__count;
9106c3fb27SDimitry Andric       ++__lhs;
9206c3fb27SDimitry Andric       ++__rhs;
9306c3fb27SDimitry Andric     }
9406c3fb27SDimitry Andric     return 0;
9506c3fb27SDimitry Andric   } else {
9606c3fb27SDimitry Andric     return __builtin_memcmp(__lhs, __rhs, __count * sizeof(_Tp));
9706c3fb27SDimitry Andric   }
9806c3fb27SDimitry Andric }
9906c3fb27SDimitry Andric 
10006c3fb27SDimitry Andric // Because of __libcpp_is_trivially_equality_comparable we know that comparing the object representations is equivalent
10106c3fb27SDimitry Andric // to a std::memcmp(...) == 0. Since we have multiple objects contiguously in memory, we can call memcmp once instead
10206c3fb27SDimitry Andric // of invoking it on every object individually.
10306c3fb27SDimitry Andric template <class _Tp, class _Up>
10406c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
10506c3fb27SDimitry Andric __constexpr_memcmp_equal(const _Tp* __lhs, const _Up* __rhs, __element_count __n) {
10606c3fb27SDimitry Andric   static_assert(__libcpp_is_trivially_equality_comparable<_Tp, _Up>::value,
10706c3fb27SDimitry Andric                 "_Tp and _Up have to be trivially equality comparable");
10806c3fb27SDimitry Andric 
10906c3fb27SDimitry Andric   auto __count = static_cast<size_t>(__n);
11006c3fb27SDimitry Andric 
11106c3fb27SDimitry Andric   if (__libcpp_is_constant_evaluated()) {
11206c3fb27SDimitry Andric #ifdef _LIBCPP_COMPILER_CLANG_BASED
11306c3fb27SDimitry Andric     if (sizeof(_Tp) == 1 && is_integral<_Tp>::value && !is_same<_Tp, bool>::value)
11406c3fb27SDimitry Andric       return __builtin_memcmp(__lhs, __rhs, __count * sizeof(_Tp)) == 0;
11506c3fb27SDimitry Andric #endif
11606c3fb27SDimitry Andric     while (__count != 0) {
11706c3fb27SDimitry Andric       if (*__lhs != *__rhs)
11806c3fb27SDimitry Andric         return false;
11906c3fb27SDimitry Andric 
12006c3fb27SDimitry Andric       --__count;
12106c3fb27SDimitry Andric       ++__lhs;
12206c3fb27SDimitry Andric       ++__rhs;
12306c3fb27SDimitry Andric     }
12406c3fb27SDimitry Andric     return true;
12506c3fb27SDimitry Andric   } else {
1269bc30046SJohn Baldwin     return ::__builtin_memcmp(__lhs, __rhs, __count * sizeof(_Tp)) == 0;
12706c3fb27SDimitry Andric   }
12806c3fb27SDimitry Andric }
12906c3fb27SDimitry Andric 
13006c3fb27SDimitry Andric template <class _Tp, class _Up>
13106c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __constexpr_memchr(_Tp* __str, _Up __value, size_t __count) {
13206c3fb27SDimitry Andric   static_assert(sizeof(_Tp) == 1 && __libcpp_is_trivially_equality_comparable<_Tp, _Up>::value,
13306c3fb27SDimitry Andric                 "Calling memchr on non-trivially equality comparable types is unsafe.");
13406c3fb27SDimitry Andric 
13506c3fb27SDimitry Andric   if (__libcpp_is_constant_evaluated()) {
13606c3fb27SDimitry Andric // use __builtin_char_memchr to optimize constexpr evaluation if we can
13706c3fb27SDimitry Andric #if _LIBCPP_STD_VER >= 17 && __has_builtin(__builtin_char_memchr)
13806c3fb27SDimitry Andric     if constexpr (is_same_v<remove_cv_t<_Tp>, char> && is_same_v<remove_cv_t<_Up>, char>)
13906c3fb27SDimitry Andric       return __builtin_char_memchr(__str, __value, __count);
14006c3fb27SDimitry Andric #endif
14106c3fb27SDimitry Andric 
14206c3fb27SDimitry Andric     for (; __count; --__count) {
14306c3fb27SDimitry Andric       if (*__str == __value)
14406c3fb27SDimitry Andric         return __str;
14506c3fb27SDimitry Andric       ++__str;
14606c3fb27SDimitry Andric     }
14706c3fb27SDimitry Andric     return nullptr;
14806c3fb27SDimitry Andric   } else {
14906c3fb27SDimitry Andric     char __value_buffer = 0;
15006c3fb27SDimitry Andric     __builtin_memcpy(&__value_buffer, &__value, sizeof(char));
15106c3fb27SDimitry Andric     return static_cast<_Tp*>(__builtin_memchr(__str, __value_buffer, __count));
15206c3fb27SDimitry Andric   }
15306c3fb27SDimitry Andric }
15406c3fb27SDimitry Andric 
15506c3fb27SDimitry Andric // This function performs an assignment to an existing, already alive TriviallyCopyable object
15606c3fb27SDimitry Andric // from another TriviallyCopyable object.
15706c3fb27SDimitry Andric //
15806c3fb27SDimitry Andric // It basically works around the fact that TriviallyCopyable objects are not required to be
15906c3fb27SDimitry Andric // syntactically copy/move constructible or copy/move assignable. Technically, only one of the
16006c3fb27SDimitry Andric // four operations is required to be syntactically valid -- but at least one definitely has to
16106c3fb27SDimitry Andric // be valid.
16206c3fb27SDimitry Andric //
16306c3fb27SDimitry Andric // This is necessary in order to implement __constexpr_memmove below in a way that mirrors as
16406c3fb27SDimitry Andric // closely as possible what the compiler's __builtin_memmove is able to do.
16506c3fb27SDimitry Andric template <class _Tp, class _Up, __enable_if_t<is_assignable<_Tp&, _Up const&>::value, int> = 0>
16606c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp& __assign_trivially_copyable(_Tp& __dest, _Up const& __src) {
16706c3fb27SDimitry Andric   __dest = __src;
16806c3fb27SDimitry Andric   return __dest;
16906c3fb27SDimitry Andric }
17006c3fb27SDimitry Andric 
17106c3fb27SDimitry Andric // clang-format off
17206c3fb27SDimitry Andric template <class _Tp, class _Up, __enable_if_t<!is_assignable<_Tp&, _Up const&>::value &&
17306c3fb27SDimitry Andric                                                is_assignable<_Tp&, _Up&&>::value, int> = 0>
17406c3fb27SDimitry Andric // clang-format on
17506c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp& __assign_trivially_copyable(_Tp& __dest, _Up& __src) {
17606c3fb27SDimitry Andric   __dest =
17706c3fb27SDimitry Andric       static_cast<_Up&&>(__src); // this is safe, we're not actually moving anything since the assignment is trivial
17806c3fb27SDimitry Andric   return __dest;
17906c3fb27SDimitry Andric }
18006c3fb27SDimitry Andric 
18106c3fb27SDimitry Andric // clang-format off
18206c3fb27SDimitry Andric template <class _Tp, class _Up, __enable_if_t<!is_assignable<_Tp&, _Up const&>::value &&
18306c3fb27SDimitry Andric                                               !is_assignable<_Tp&, _Up&&>::value &&
18406c3fb27SDimitry Andric                                                is_constructible<_Tp, _Up const&>::value, int> = 0>
18506c3fb27SDimitry Andric // clang-format on
18606c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __assign_trivially_copyable(_Tp& __dest, _Up const& __src) {
18706c3fb27SDimitry Andric   // _Tp is trivially destructible, so we don't need to call its destructor to end the lifetime of the object
18806c3fb27SDimitry Andric   // that was there previously
18906c3fb27SDimitry Andric   std::__construct_at(std::addressof(__dest), __src);
19006c3fb27SDimitry Andric   return __dest;
19106c3fb27SDimitry Andric }
19206c3fb27SDimitry Andric 
19306c3fb27SDimitry Andric // clang-format off
19406c3fb27SDimitry Andric template <class _Tp, class _Up, __enable_if_t<!is_assignable<_Tp&, _Up const&>::value &&
19506c3fb27SDimitry Andric                                               !is_assignable<_Tp&, _Up&&>::value &&
19606c3fb27SDimitry Andric                                               !is_constructible<_Tp, _Up const&>::value &&
19706c3fb27SDimitry Andric                                                is_constructible<_Tp, _Up&&>::value, int> = 0>
19806c3fb27SDimitry Andric // clang-format on
19906c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __assign_trivially_copyable(_Tp& __dest, _Up& __src) {
20006c3fb27SDimitry Andric   // _Tp is trivially destructible, so we don't need to call its destructor to end the lifetime of the object
20106c3fb27SDimitry Andric   // that was there previously
20206c3fb27SDimitry Andric   std::__construct_at(
20306c3fb27SDimitry Andric       std::addressof(__dest),
20406c3fb27SDimitry Andric       static_cast<_Up&&>(__src)); // this is safe, we're not actually moving anything since the constructor is trivial
20506c3fb27SDimitry Andric   return __dest;
20606c3fb27SDimitry Andric }
20706c3fb27SDimitry Andric 
20806c3fb27SDimitry Andric template <class _Tp, class _Up, __enable_if_t<__is_always_bitcastable<_Up, _Tp>::value, int> = 0>
20906c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp*
21006c3fb27SDimitry Andric __constexpr_memmove(_Tp* __dest, _Up* __src, __element_count __n) {
21106c3fb27SDimitry Andric   size_t __count = static_cast<size_t>(__n);
21206c3fb27SDimitry Andric   if (__libcpp_is_constant_evaluated()) {
21306c3fb27SDimitry Andric #ifdef _LIBCPP_COMPILER_CLANG_BASED
21406c3fb27SDimitry Andric     if (is_same<__remove_cv_t<_Tp>, __remove_cv_t<_Up> >::value) {
21506c3fb27SDimitry Andric       ::__builtin_memmove(__dest, __src, __count * sizeof(_Tp));
21606c3fb27SDimitry Andric       return __dest;
21706c3fb27SDimitry Andric     }
21806c3fb27SDimitry Andric #endif
21906c3fb27SDimitry Andric     if (std::__is_pointer_in_range(__src, __src + __count, __dest)) {
22006c3fb27SDimitry Andric       for (; __count > 0; --__count)
22106c3fb27SDimitry Andric         std::__assign_trivially_copyable(__dest[__count - 1], __src[__count - 1]);
22206c3fb27SDimitry Andric     } else {
22306c3fb27SDimitry Andric       for (size_t __i = 0; __i != __count; ++__i)
22406c3fb27SDimitry Andric         std::__assign_trivially_copyable(__dest[__i], __src[__i]);
22506c3fb27SDimitry Andric     }
22606c3fb27SDimitry Andric   } else if (__count > 0) {
227*0fca6ea1SDimitry Andric     ::__builtin_memmove(__dest, __src, (__count - 1) * sizeof(_Tp) + __datasizeof_v<_Tp>);
22806c3fb27SDimitry Andric   }
22906c3fb27SDimitry Andric   return __dest;
23006c3fb27SDimitry Andric }
23106c3fb27SDimitry Andric 
23206c3fb27SDimitry Andric _LIBCPP_END_NAMESPACE_STD
23306c3fb27SDimitry Andric 
23406c3fb27SDimitry Andric #endif // _LIBCPP___STRING_CONSTEXPR_C_FUNCTIONS_H
235