Skip to content
200 changes: 96 additions & 104 deletions libcxx/include/bitset
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ template <size_t N> struct hash<std::bitset<N>>;
# include <__algorithm/fill.h>
# include <__algorithm/fill_n.h>
# include <__algorithm/find.h>
# include <__algorithm/min.h>
# include <__assert>
# include <__bit/countr.h>
# include <__bit/invert_if.h>
Expand All @@ -146,7 +147,11 @@ template <size_t N> struct hash<std::bitset<N>>;
# include <__functional/hash.h>
# include <__functional/identity.h>
# include <__functional/unary_function.h>
# include <__tuple/tuple_indices.h>
# include <__type_traits/enable_if.h>
# include <__type_traits/integral_constant.h>
# include <__type_traits/is_char_like_type.h>
# include <__utility/integer_sequence.h>
# include <climits>
# include <stdexcept>
# include <string_view>
Expand Down Expand Up @@ -220,11 +225,52 @@ protected:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void operator^=(const __bitset& __v) _NOEXCEPT;

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void flip() _NOEXCEPT;

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long __to_ulong() const {
static_assert(sizeof(__storage_type) >= sizeof(unsigned long),
"libc++ only supports platforms where sizeof(size_t) >= sizeof(unsigned long), such as 32-bit and "
"64-bit platforms. If you're interested in supporting a platform where that is not the case, please "
"contact the libc++ developers.");
return static_cast<unsigned long>(__first_[0]);
}

// _Bit_size > sizeof(unsigned long) * CHAR_BIT: overflow check needed
template <size_t _Bit_size = _Size, __enable_if_t<(_Bit_size > sizeof(unsigned long) * CHAR_BIT), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const {
return to_ulong(integral_constant < bool, _Size< sizeof(unsigned long) * CHAR_BIT>());
if (auto __e = __make_iter(_Bit_size); std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true) != __e)
std::__throw_overflow_error("__bitset<_N_words, _Size>::to_ulong overflow error");

return __to_ulong();
}

// _Bit_size <= sizeof(unsigned long) * CHAR_BIT: no overflow check needed
template <size_t _Bit_size = _Size, __enable_if_t<(_Bit_size <= sizeof(unsigned long) * CHAR_BIT), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const {
return __to_ulong();
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const {
return to_ullong(integral_constant < bool, _Size< sizeof(unsigned long long) * CHAR_BIT>());
// Check for overflow if _Size does not fit in unsigned long long
if _LIBCPP_CONSTEXPR (_Size > sizeof(unsigned long long) * CHAR_BIT) {
if (auto __e = __make_iter(_Size);
std::find(__make_iter(sizeof(unsigned long long) * CHAR_BIT), __e, true) != __e)
std::__throw_overflow_error("__bitset<_N_words, _Size>::to_ullong overflow error");
}

// At this point, the effective bitset size (excluding leading zeros) fits in unsigned long long

if _LIBCPP_CONSTEXPR (sizeof(__storage_type) >= sizeof(unsigned long long)) {
// If __storage_type is at least as large as unsigned long long, the result spans only one word
return static_cast<unsigned long long>(__first_[0]);
} else {
// Otherwise, the result spans multiple words which are concatenated
const size_t __ull_words = (sizeof(unsigned long long) - 1) / sizeof(__storage_type) + 1;
const size_t __n_words = _N_words < __ull_words ? _N_words : __ull_words;
unsigned long long __r = static_cast<unsigned long long>(__first_[0]);
for (size_t __i = 1; __i < __n_words; ++__i)
__r |= static_cast<unsigned long long>(__first_[__i]) << (__bits_per_word * __i);
return __r;
}
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool all() const _NOEXCEPT { return !__scan_bits(__bit_not()); }
Expand Down Expand Up @@ -255,16 +301,6 @@ private:
return ~__x;
}
};
# ifdef _LIBCPP_CXX03_LANG
void __init(unsigned long long __v, false_type) _NOEXCEPT;
_LIBCPP_HIDE_FROM_ABI void __init(unsigned long long __v, true_type) _NOEXCEPT;
# endif // _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong(false_type) const;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong(true_type) const;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(false_type) const;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(true_type) const;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(true_type, false_type) const;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong(true_type, true_type) const;

template <typename _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool __scan_bits(_Proj __proj) const _NOEXCEPT {
Expand All @@ -282,6 +318,15 @@ private:
}
return false;
}

# ifdef _LIBCPP_CXX03_LANG
void __init(unsigned long long __v, false_type) _NOEXCEPT;
_LIBCPP_HIDE_FROM_ABI void __init(unsigned long long __v, true_type) _NOEXCEPT;
# else
template <size_t... _Indices>
_LIBCPP_HIDE_FROM_ABI constexpr __bitset(unsigned long long __v, std::__tuple_indices<_Indices...>) _NOEXCEPT
: __first_{static_cast<__storage_type>(__v >> (_Indices * __bits_per_word))...} {}
# endif // _LIBCPP_CXX03_LANG
};

template <size_t _N_words, size_t _Size>
Expand Down Expand Up @@ -316,21 +361,15 @@ inline _LIBCPP_HIDE_FROM_ABI void __bitset<_N_words, _Size>::__init(unsigned lon
template <size_t _N_words, size_t _Size>
inline _LIBCPP_CONSTEXPR __bitset<_N_words, _Size>::__bitset(unsigned long long __v) _NOEXCEPT
# ifndef _LIBCPP_CXX03_LANG
# if __SIZEOF_SIZE_T__ == 8
: __first_{__v}
# elif __SIZEOF_SIZE_T__ == 4
: __first_{static_cast<__storage_type>(__v),
_Size >= 2 * __bits_per_word
? static_cast<__storage_type>(__v >> __bits_per_word)
: static_cast<__storage_type>((__v >> __bits_per_word) &
(__storage_type(1) << (_Size - __bits_per_word)) - 1)}
# else
# error This constructor has not been ported to this platform
# endif
: __bitset(__v,
std::__make_indices_imp< (_N_words < (sizeof(unsigned long long) - 1) / sizeof(__storage_type) + 1)
? _N_words
: (sizeof(unsigned long long) - 1) / sizeof(__storage_type) + 1,
0>{})
# endif
{
# ifdef _LIBCPP_CXX03_LANG
__init(__v, integral_constant<bool, sizeof(unsigned long long) <= sizeof(__storage_type)>());
__init(__v, _BoolConstant<sizeof(unsigned long long) <= sizeof(__storage_type)>());
# endif
}

Expand Down Expand Up @@ -369,58 +408,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void __bitset<_N_words, _Siz
*__p ^= (__storage_type(1) << __n) - 1;
}

template <size_t _N_words, size_t _Size>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long
__bitset<_N_words, _Size>::to_ulong(false_type) const {
__const_iterator __e = __make_iter(_Size);
__const_iterator __i = std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true);
if (__i != __e)
std::__throw_overflow_error("bitset to_ulong overflow error");

return __first_[0];
}

template <size_t _N_words, size_t _Size>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long
__bitset<_N_words, _Size>::to_ulong(true_type) const {
return __first_[0];
}

template <size_t _N_words, size_t _Size>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
__bitset<_N_words, _Size>::to_ullong(false_type) const {
__const_iterator __e = __make_iter(_Size);
__const_iterator __i = std::find(__make_iter(sizeof(unsigned long long) * CHAR_BIT), __e, true);
if (__i != __e)
std::__throw_overflow_error("bitset to_ullong overflow error");

return to_ullong(true_type());
}

template <size_t _N_words, size_t _Size>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
__bitset<_N_words, _Size>::to_ullong(true_type) const {
return to_ullong(true_type(), integral_constant<bool, sizeof(__storage_type) < sizeof(unsigned long long)>());
}

template <size_t _N_words, size_t _Size>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
__bitset<_N_words, _Size>::to_ullong(true_type, false_type) const {
return __first_[0];
}

template <size_t _N_words, size_t _Size>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long
__bitset<_N_words, _Size>::to_ullong(true_type, true_type) const {
unsigned long long __r = __first_[0];
_LIBCPP_DIAGNOSTIC_PUSH
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshift-count-overflow")
for (size_t __i = 1; __i < sizeof(unsigned long long) / sizeof(__storage_type); ++__i)
__r |= static_cast<unsigned long long>(__first_[__i]) << (sizeof(__storage_type) * CHAR_BIT);
_LIBCPP_DIAGNOSTIC_POP
return __r;
}

template <size_t _N_words, size_t _Size>
inline size_t __bitset<_N_words, _Size>::__hash_code() const _NOEXCEPT {
size_t __h = 0;
Expand Down Expand Up @@ -479,8 +466,30 @@ protected:

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void flip() _NOEXCEPT;

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const;
template <size_t _Bit_size = _Size, __enable_if_t<(_Bit_size > sizeof(unsigned long) * CHAR_BIT), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const {
if (auto __e = __make_iter(_Bit_size); std::find(__make_iter(sizeof(unsigned long) * CHAR_BIT), __e, true) != __e)
__throw_overflow_error("__bitset<1, _Size>::to_ulong overflow error");

return static_cast<unsigned long>(__first_);
}

template <size_t _Bit_size = _Size, __enable_if_t<(_Bit_size <= sizeof(unsigned long) * CHAR_BIT), int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const {
return static_cast<unsigned long>(__first_);
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const {
// If _Size exceeds the size of unsigned long long, check for overflow
if _LIBCPP_CONSTEXPR (_Size > sizeof(unsigned long long) * CHAR_BIT) {
if (auto __e = __make_iter(_Size);
std::find(__make_iter(sizeof(unsigned long long) * CHAR_BIT), __e, true) != __e)
__throw_overflow_error("__bitset<1, _Size>::to_ullong overflow error");
}

// If _Size fits or no overflow, directly cast to unsigned long long
return static_cast<unsigned long long>(__first_);
}

template <bool _Sparse, class _CharT, class _Traits, class _Allocator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
Expand All @@ -507,8 +516,11 @@ inline _LIBCPP_CONSTEXPR __bitset<1, _Size>::__bitset() _NOEXCEPT : __first_(0)

template <size_t _Size>
inline _LIBCPP_CONSTEXPR __bitset<1, _Size>::__bitset(unsigned long long __v) _NOEXCEPT
: __first_(_Size == __bits_per_word ? static_cast<__storage_type>(__v)
: static_cast<__storage_type>(__v) & ((__storage_type(1) << _Size) - 1)) {}
// TODO: We must refer to __bits_per_word in order to work around an issue with the GDB pretty-printers.
// Without it, the pretty-printers complain about a missing __bits_per_word member. This needs to
// be investigated further.
// See: https://github.com/llvm/llvm-project/actions/runs/15071518915/job/42368867929?pr=121348#logs
: __first_(_Size == __bits_per_word ? static_cast<__storage_type>(__v) : static_cast<__storage_type>(__v)) {}

template <size_t _Size>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void
Expand All @@ -533,16 +545,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void __bitset<1, _Siz
__first_ ^= ~__storage_type(0) >> (__bits_per_word - _Size);
}

template <size_t _Size>
inline _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long __bitset<1, _Size>::to_ulong() const {
return __first_;
}

template <size_t _Size>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long __bitset<1, _Size>::to_ullong() const {
return __first_;
}

template <size_t _Size>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bool __bitset<1, _Size>::all() const _NOEXCEPT {
__storage_type __m = ~__storage_type(0) >> (__bits_per_word - _Size);
Expand Down Expand Up @@ -633,8 +635,6 @@ class bitset : private __bitset<_Size == 0 ? 0 : (_Size - 1) / (sizeof(size_t) *
public:
static const unsigned __n_words = _Size == 0 ? 0 : (_Size - 1) / (sizeof(size_t) * CHAR_BIT) + 1;
typedef __bitset<__n_words, _Size> __base;

public:
typedef typename __base::reference reference;
typedef typename __base::__const_reference __const_reference;

Expand Down Expand Up @@ -713,8 +713,10 @@ public:
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__p < _Size, "bitset::operator[] index out of bounds");
return __base::__make_ref(__p);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const { return __base::to_ulong(); }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long to_ullong() const {
return __base::to_ullong();
}
template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
to_string(_CharT __zero = _CharT('0'), _CharT __one = _CharT('1')) const;
Expand Down Expand Up @@ -850,16 +852,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 bitset<_Size>& bitset<_Size>
return *this;
}

template <size_t _Size>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long bitset<_Size>::to_ulong() const {
return __base::to_ulong();
}

template <size_t _Size>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long long bitset<_Size>::to_ullong() const {
return __base::to_ullong();
}

template <size_t _Size>
template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 basic_string<_CharT, _Traits, _Allocator>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,40 @@
#include <climits>
#include <cassert>

#ifndef TEST_HAS_NO_EXCEPTIONS
# include <stdexcept>
#endif

#include "test_macros.h"

template <std::size_t N>
TEST_CONSTEXPR_CXX23 void test_to_ullong() {
const std::size_t M = sizeof(unsigned long long) * CHAR_BIT < N ? sizeof(unsigned long long) * CHAR_BIT : N;
const bool is_M_zero = std::integral_constant<bool, M == 0>::value; // avoid compiler warnings
const std::size_t X = is_M_zero ? sizeof(unsigned long long) * CHAR_BIT - 1 : sizeof(unsigned long long) * CHAR_BIT - M;
const unsigned long long max = is_M_zero ? 0 : (unsigned long long)(-1) >> X;
unsigned long long tests[] = {
0,
std::min<unsigned long long>(1, max),
std::min<unsigned long long>(2, max),
std::min<unsigned long long>(3, max),
std::min(max, max-3),
std::min(max, max-2),
std::min(max, max-1),
max
};
for (unsigned long long j : tests) {
std::bitset<N> v(j);
assert(j == v.to_ullong());
}
{ // test values bigger than can fit into the bitset
const unsigned long long val = 0x55AAAAFFFFAAAA55ULL;
const bool canFit = N < sizeof(unsigned long long) * CHAR_BIT;
const unsigned long long mask = canFit ? (1ULL << (canFit ? N : 0)) - 1 : (unsigned long long)(-1); // avoid compiler warnings
std::bitset<N> v(val);
assert(v.to_ullong() == (val & mask)); // we shouldn't return bit patterns from outside the limits of the bitset.
}
const std::size_t M = sizeof(unsigned long long) * CHAR_BIT < N ? sizeof(unsigned long long) * CHAR_BIT : N;
const bool is_M_zero = std::integral_constant < bool, M == 0 > ::value; // avoid compiler warnings
const std::size_t X =
is_M_zero ? sizeof(unsigned long long) * CHAR_BIT - 1 : sizeof(unsigned long long) * CHAR_BIT - M;
const unsigned long long max = is_M_zero ? 0 : (unsigned long long)(-1) >> X;
unsigned long long tests[] = {
0,
std::min<unsigned long long>(1, max),
std::min<unsigned long long>(2, max),
std::min<unsigned long long>(3, max),
std::min(max, max - 3),
std::min(max, max - 2),
std::min(max, max - 1),
max};
for (unsigned long long j : tests) {
std::bitset<N> v(j);
assert(j == v.to_ullong());
}
{ // test values bigger than can fit into the bitset
const unsigned long long val = 0x55AAAAFFFFAAAA55ULL;
const bool canFit = N < sizeof(unsigned long long) * CHAR_BIT;
const unsigned long long mask =
canFit ? (1ULL << (canFit ? N : 0)) - 1 : (unsigned long long)(-1); // avoid compiler warnings
std::bitset<N> v(val);
assert(v.to_ullong() == (val & mask)); // we shouldn't return bit patterns from outside the limits of the bitset.
}
}

TEST_CONSTEXPR_CXX23 bool test() {
Expand All @@ -56,6 +61,22 @@ TEST_CONSTEXPR_CXX23 bool test() {
test_to_ullong<65>();
test_to_ullong<1000>();

#ifndef TEST_HAS_NO_EXCEPTIONS
if (!TEST_IS_CONSTANT_EVALUATED) {
// bitset has true bits beyond the size of unsigned long long
std::bitset<std::numeric_limits<unsigned long long>::digits + 1> q(0);
q.flip();
try {
q.to_ullong(); // throws
assert(false);
} catch (const std::overflow_error&) {
// expected
} catch (...) {
assert(false);
}
}
#endif // TEST_HAS_NO_EXCEPTIONS

return true;
}

Expand Down
Loading
Loading