Skip to content

[libc++] Fix bitset conversion functions and refactor constructor #121348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 24, 2025
184 changes: 80 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,42 @@ 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 {
return to_ulong(integral_constant < bool, _Size< sizeof(unsigned long) * CHAR_BIT>());
if _LIBCPP_CONSTEXPR (_Size > sizeof(unsigned long) * CHAR_BIT) {
if (auto __e = __make_iter(_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");
}

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]);
}

_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 +291,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 +308,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 +351,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 +398,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 +456,25 @@ 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;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unsigned long to_ulong() const {
if _LIBCPP_CONSTEXPR (_Size > sizeof(unsigned long) * CHAR_BIT) {
if (auto __e = __make_iter(_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_);
}

_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 +501,10 @@ 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.
: __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 +529,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 +619,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 +697,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 +836,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 @@ -13,36 +13,38 @@
#include <type_traits>
#include <climits>
#include <cassert>
#include <stdexcept>

#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 +58,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