15
15
#include < __bit/rotate.h>
16
16
#include < __concepts/arithmetic.h>
17
17
#include < __config>
18
+ #include < __type_traits/enable_if.h>
19
+ #include < __type_traits/is_unsigned.h>
18
20
#include < limits>
19
21
20
22
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -38,20 +40,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
38
40
return __builtin_ctzll (__x);
39
41
}
40
42
41
- template <class _Tp >
42
- [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero (_Tp __t ) _NOEXCEPT {
43
- #if __has_builtin(__builtin_ctzg)
44
- return __builtin_ctzg (__t , numeric_limits<_Tp>::digits);
45
- #else // __has_builtin(__builtin_ctzg)
46
- if (__t == 0 )
47
- return numeric_limits<_Tp>::digits;
48
- if (sizeof (_Tp) <= sizeof (unsigned int ))
43
+ #if _LIBCPP_STD_VER >= 17
44
+ // Implementation using constexpr if for C++ standards >= 17
45
+
46
+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
47
+ template <class _Tp , __enable_if_t <is_unsigned<_Tp>::value, int > = 0 >
48
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
49
+ if constexpr (sizeof (_Tp) <= sizeof (unsigned int )) {
49
50
return std::__libcpp_ctz (static_cast <unsigned int >(__t ));
50
- else if (sizeof (_Tp) <= sizeof (unsigned long ))
51
+ } else if constexpr (sizeof (_Tp) <= sizeof (unsigned long )) {
51
52
return std::__libcpp_ctz (static_cast <unsigned long >(__t ));
52
- else if (sizeof (_Tp) <= sizeof (unsigned long long ))
53
+ } else if constexpr (sizeof (_Tp) <= sizeof (unsigned long long )) {
53
54
return std::__libcpp_ctz (static_cast <unsigned long long >(__t ));
54
- else {
55
+ } else {
55
56
int __ret = 0 ;
56
57
const unsigned int __ulldigits = numeric_limits<unsigned long long >::digits;
57
58
while (static_cast <unsigned long long >(__t ) == 0uLL) {
@@ -60,7 +61,72 @@ template <class _Tp>
60
61
}
61
62
return __ret + std::__libcpp_ctz (static_cast <unsigned long long >(__t ));
62
63
}
63
- #endif // __has_builtin(__builtin_ctzg)
64
+ }
65
+
66
+ #else
67
+ // Equivalent SFINAE-based implementation for older C++ standards < 17
68
+
69
+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
70
+ template < class _Tp , __enable_if_t <is_unsigned<_Tp>::value && sizeof (_Tp) <= sizeof (unsigned int ), int > = 0 >
71
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
72
+ return std::__libcpp_ctz (static_cast <unsigned int >(__t ));
73
+ }
74
+
75
+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
76
+ template < class _Tp ,
77
+ __enable_if_t <is_unsigned<_Tp>::value && (sizeof (_Tp) > sizeof (unsigned int )) &&
78
+ sizeof (_Tp) <= sizeof (unsigned long ),
79
+ int > = 0 >
80
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
81
+ return std::__libcpp_ctz (static_cast <unsigned long >(__t ));
82
+ }
83
+
84
+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
85
+ template < class _Tp ,
86
+ __enable_if_t <is_unsigned<_Tp>::value && (sizeof (_Tp) > sizeof (unsigned long )) &&
87
+ sizeof (_Tp) <= sizeof (unsigned long long ),
88
+ int > = 0 >
89
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
90
+ return std::__libcpp_ctz (static_cast <unsigned long long >(__t ));
91
+ }
92
+
93
+ # if _LIBCPP_STD_VER == 11
94
+
95
+ // Recursive constexpr implementation for C++11 due to limited constexpr support
96
+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
97
+ template < class _Tp , __enable_if_t <is_unsigned<_Tp>::value && (sizeof (_Tp) > sizeof (unsigned long long )), int > = 0 >
98
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
99
+ unsigned long long __ull = static_cast <unsigned long long >(__t );
100
+ const unsigned int __ulldigits = numeric_limits<unsigned long long >::digits;
101
+ return __ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__t >> __ulldigits) : std::__libcpp_ctz (__ull);
102
+ }
103
+
104
+ # else
105
+
106
+ // Loop-based constexpr implementation for C++14 (and non-constexpr for C++03, 98)
107
+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
108
+ template < class _Tp , __enable_if_t <is_unsigned<_Tp>::value && (sizeof (_Tp) > sizeof (unsigned long long )), int > = 0 >
109
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
110
+ int __ret = 0 ;
111
+ const unsigned int __ulldigits = numeric_limits<unsigned long long >::digits;
112
+ while (static_cast <unsigned long long >(__t ) == 0uLL) {
113
+ __ret += __ulldigits;
114
+ __t >>= __ulldigits;
115
+ }
116
+ return __ret + std::__libcpp_ctz (static_cast <unsigned long long >(__t ));
117
+ }
118
+
119
+ # endif // _LIBCPP_STD_VER == 11
120
+
121
+ #endif // _LIBCPP_STD_VER >= 17
122
+
123
+ template <class _Tp >
124
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero (_Tp __t ) _NOEXCEPT {
125
+ #if __has_builtin(__builtin_ctzg)
126
+ return __builtin_ctzg (__t , numeric_limits<_Tp>::digits);
127
+ #else
128
+ return __t != 0 ? __countr_zero_impl (__t ) : numeric_limits<_Tp>::digits;
129
+ #endif
64
130
}
65
131
66
132
#if _LIBCPP_STD_VER >= 20
0 commit comments