Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions libcxx/include/__algorithm/copy.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
unsigned __clz = __bits_per_word - __first.__ctz_;
difference_type __dn = std::min(static_cast<difference_type>(__clz), __n);
__n -= __dn;
__storage_type __m = (~__storage_type(0) << __first.__ctz_) & (~__storage_type(0) >> (__clz - __dn));
__storage_type __m = std::__middle_mask<__storage_type>(__clz - __dn, __first.__ctz_);
__storage_type __b = *__first.__seg_ & __m;
*__result.__seg_ &= ~__m;
*__result.__seg_ |= __b;
Expand All @@ -73,7 +73,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
// do last word
if (__n > 0) {
__first.__seg_ += __nw;
__storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
__storage_type __m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
__storage_type __b = *__first.__seg_ & __m;
*__result.__seg_ &= ~__m;
*__result.__seg_ |= __b;
Expand All @@ -98,11 +98,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
unsigned __clz_f = __bits_per_word - __first.__ctz_;
difference_type __dn = std::min(static_cast<difference_type>(__clz_f), __n);
__n -= __dn;
__storage_type __m = (~__storage_type(0) << __first.__ctz_) & (~__storage_type(0) >> (__clz_f - __dn));
__storage_type __m = std::__middle_mask<__storage_type>(__clz_f - __dn, __first.__ctz_);
__storage_type __b = *__first.__seg_ & __m;
unsigned __clz_r = __bits_per_word - __result.__ctz_;
__storage_type __ddn = std::min<__storage_type>(__dn, __clz_r);
__m = (~__storage_type(0) << __result.__ctz_) & (~__storage_type(0) >> (__clz_r - __ddn));
__m = std::__middle_mask<__storage_type>(__clz_r - __ddn, __result.__ctz_);
*__result.__seg_ &= ~__m;
if (__result.__ctz_ > __first.__ctz_)
*__result.__seg_ |= __b << (__result.__ctz_ - __first.__ctz_);
Expand All @@ -112,7 +112,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
__result.__ctz_ = static_cast<unsigned>((__ddn + __result.__ctz_) % __bits_per_word);
__dn -= __ddn;
if (__dn > 0) {
__m = ~__storage_type(0) >> (__bits_per_word - __dn);
__m = std::__trailing_mask<__storage_type>(__bits_per_word - __dn);
*__result.__seg_ &= ~__m;
*__result.__seg_ |= __b >> (__first.__ctz_ + __ddn);
__result.__ctz_ = static_cast<unsigned>(__dn);
Expand All @@ -123,7 +123,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
// __first.__ctz_ == 0;
// do middle words
unsigned __clz_r = __bits_per_word - __result.__ctz_;
__storage_type __m = ~__storage_type(0) << __result.__ctz_;
__storage_type __m = std::__leading_mask<__storage_type>(__result.__ctz_);
for (; __n >= __bits_per_word; __n -= __bits_per_word, ++__first.__seg_) {
__storage_type __b = *__first.__seg_;
*__result.__seg_ &= ~__m;
Expand All @@ -134,17 +134,17 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
}
// do last word
if (__n > 0) {
__m = ~__storage_type(0) >> (__bits_per_word - __n);
__m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
__storage_type __b = *__first.__seg_ & __m;
__storage_type __dn = std::min(__n, static_cast<difference_type>(__clz_r));
__m = (~__storage_type(0) << __result.__ctz_) & (~__storage_type(0) >> (__clz_r - __dn));
__m = std::__middle_mask<__storage_type>(__clz_r - __dn, __result.__ctz_);
*__result.__seg_ &= ~__m;
*__result.__seg_ |= __b << __result.__ctz_;
__result.__seg_ += (__dn + __result.__ctz_) / __bits_per_word;
__result.__ctz_ = static_cast<unsigned>((__dn + __result.__ctz_) % __bits_per_word);
__n -= __dn;
if (__n > 0) {
__m = ~__storage_type(0) >> (__bits_per_word - __n);
__m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
*__result.__seg_ &= ~__m;
*__result.__seg_ |= __b >> __dn;
__result.__ctz_ = static_cast<unsigned>(__n);
Expand Down
11 changes: 9 additions & 2 deletions libcxx/include/__bit_reference
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,20 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask
return static_cast<_StorageType>(~static_cast<_StorageType>(0)) >> __clz;
}

// Creates a mask of type `_StorageType` with a specified number of trailing zeros (__ctz) and sets all remaining
// bits to one.
template <class _StorageType>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __leading_mask(unsigned __ctz) {
static_assert(is_unsigned<_StorageType>::value, "__leading_mask only works with unsigned types");
return static_cast<_StorageType>(~static_cast<_StorageType>(0)) << __ctz;
}

// Creates a mask of type `_StorageType` with a specified number of leading zeros (__clz), a specified number of
// trailing zeros (__ctz), and sets all bits in between to one.
template <class _StorageType>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __clz, unsigned __ctz) {
static_assert(is_unsigned<_StorageType>::value, "__middle_mask only works with unsigned types");
return (static_cast<_StorageType>(~static_cast<_StorageType>(0)) << __ctz) &
std::__trailing_mask<_StorageType>(__clz);
return std::__leading_mask<_StorageType>(__ctz) & std::__trailing_mask<_StorageType>(__clz);
}

// This function is designed to operate correctly even for smaller integral types like `uint8_t`, `uint16_t`,
Expand Down
5 changes: 4 additions & 1 deletion libcxx/include/__fwd/bit_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ struct __size_difference_type_traits;

template <class _StoragePointer>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
__fill_masked_range(_StoragePointer __word, unsigned __ctz, unsigned __clz, bool __fill_val);
__fill_masked_range(_StoragePointer __word, unsigned __clz, unsigned __ctz, bool __fill_val);

template <class _StorageType>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask(unsigned __clz);

template <class _StorageType>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __leading_mask(unsigned __ctz);

template <class _StorageType>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __clz, unsigned __ctz);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
// constexpr OutIter // constexpr after C++17
// copy(InIter first, InIter last, OutIter result);

// XFAIL: FROZEN-CXX03-HEADERS-FIXME

#include <algorithm>
#include <cassert>
#include <vector>

#include "sized_allocator.h"
#include "test_macros.h"
#include "test_iterators.h"
#include "type_algorithms.h"
Expand Down Expand Up @@ -109,6 +112,153 @@ TEST_CONSTEXPR_CXX20 bool test() {
assert(test_vector_bool(256));
}

// Validate std::copy with std::vector<bool> iterators and custom storage types.
// Ensure that assigned bits hold the intended values, while unassigned bits stay unchanged.
// Related issue: https://github.com/llvm/llvm-project/issues/131692.
{
//// Tests for std::copy with aligned bits

{ // Test the first (partial) word for uint8_t
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
std::vector<bool, Alloc> in(8, false, Alloc(1));
std::vector<bool, Alloc> out(8, true, Alloc(1));
std::copy(in.begin() + 1, in.begin() + 2, out.begin() + 1); // out[1] = false
assert(out[1] == false);
for (std::size_t i = 0; i < out.size(); ++i) // Ensure that unassigned bits remain unchanged
if (i != 1)
assert(out[i] == true);
}
{ // Test the last (partial) word for uint8_t
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
std::vector<bool, Alloc> in(8, false, Alloc(1));
std::vector<bool, Alloc> out(8, true, Alloc(1));
std::copy(in.begin(), in.begin() + 1, out.begin()); // out[0] = false
assert(out[0] == false);
for (std::size_t i = 1; i < out.size(); ++i) // Ensure that unassigned bits remain unchanged
assert(out[i] == true);
}
{ // Test middle (whole) words for uint8_t
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
std::vector<bool, Alloc> in(32, true, Alloc(1));
for (std::size_t i = 0; i < in.size(); i += 2)
in[i] = false;
std::vector<bool, Alloc> out(32, false, Alloc(1));
std::copy(in.begin() + 4, in.end() - 4, out.begin() + 4);
for (std::size_t i = 4; i < static_cast<std::size_t>(in.size() - 4); ++i)
assert(in[i] == out[i]);
for (std::size_t i = 0; i < 4; ++i)
assert(out[i] == false);
for (std::size_t i = 28; i < out.size(); ++i)
assert(out[i] == false);
}

{ // Test the first (partial) word for uint16_t
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
std::vector<bool, Alloc> in(16, false, Alloc(1));
std::vector<bool, Alloc> out(16, true, Alloc(1));
std::copy(in.begin() + 1, in.begin() + 3, out.begin() + 1); // out[1..2] = false
assert(out[1] == false);
assert(out[2] == false);
for (std::size_t i = 0; i < out.size(); ++i) // Ensure that unassigned bits remain unchanged
if (i != 1 && i != 2)
assert(out[i] == true);
}
{ // Test the last (partial) word for uint16_t
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
std::vector<bool, Alloc> in(16, false, Alloc(1));
std::vector<bool, Alloc> out(16, true, Alloc(1));
std::copy(in.begin(), in.begin() + 2, out.begin()); // out[0..1] = false
assert(out[0] == false);
assert(out[1] == false);
for (std::size_t i = 2; i < out.size(); ++i) // Ensure that unassigned bits remain unchanged
assert(out[i] == true);
}
{ // Test middle (whole) words for uint16_t
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
std::vector<bool, Alloc> in(64, true, Alloc(1));
for (std::size_t i = 0; i < in.size(); i += 2)
in[i] = false;
std::vector<bool, Alloc> out(64, false, Alloc(1));
std::copy(in.begin() + 8, in.end() - 8, out.begin() + 8);
for (std::size_t i = 8; i < static_cast<std::size_t>(in.size() - 8); ++i)
assert(in[i] == out[i]);
for (std::size_t i = 0; i < 8; ++i)
assert(out[i] == false);
for (std::size_t i = static_cast<std::size_t>(out.size() - 8); i < out.size(); ++i)
assert(out[i] == false);
}

//// Tests for std::copy with unaligned bits

{ // Test the first (partial) word for uint8_t
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
std::vector<bool, Alloc> in(8, false, Alloc(1));
std::vector<bool, Alloc> out(8, true, Alloc(1));
std::copy(in.begin() + 7, in.end(), out.begin()); // out[0] = false
assert(out[0] == false);
for (std::size_t i = 1; i < out.size(); ++i) // Ensure that unassigned bits remain unchanged
assert(out[i] == true);
}
{ // Test the last (partial) word for uint8_t
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
std::vector<bool, Alloc> in(8, false, Alloc(1));
std::vector<bool, Alloc> out(8, true, Alloc(1));
std::copy(in.begin(), in.begin() + 1, out.begin() + 2); // out[2] = false
assert(out[2] == false);
for (std::size_t i = 1; i < out.size(); ++i) // Ensure that unassigned bits remain unchanged
if (i != 2)
assert(out[i] == true);
}
{ // Test middle (whole) words for uint8_t
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
std::vector<bool, Alloc> in(36, true, Alloc(1));
for (std::size_t i = 0; i < in.size(); i += 2)
in[i] = false;
std::vector<bool, Alloc> out(40, false, Alloc(1));
std::copy(in.begin(), in.end(), out.begin() + 4);
for (std::size_t i = 0; i < in.size(); ++i)
assert(in[i] == out[i + 4]);
for (std::size_t i = 0; i < 4; ++i)
assert(out[i] == false);
}

{ // Test the first (partial) word for uint16_t
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
std::vector<bool, Alloc> in(16, false, Alloc(1));
std::vector<bool, Alloc> out(16, true, Alloc(1));
std::copy(in.begin() + 14, in.end(), out.begin()); // out[0..1] = false
assert(out[0] == false);
assert(out[1] == false);
for (std::size_t i = 2; i < out.size(); ++i) // Ensure that unassigned bits remain unchanged
assert(out[i] == true);
}
{ // Test the last (partial) word for uint16_t
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
std::vector<bool, Alloc> in(16, false, Alloc(1));
std::vector<bool, Alloc> out(16, true, Alloc(1));
std::copy(in.begin(), in.begin() + 2, out.begin() + 1); // out[1..2] = false
assert(out[1] == false);
assert(out[2] == false);
for (std::size_t i = 0; i < out.size(); ++i) // Ensure that unassigned bits remain unchanged
if (i != 1 && i != 2)
assert(out[i] == true);
}
{ // Test middle (whole) words for uint16_t
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
std::vector<bool, Alloc> in(72, true, Alloc(1));
for (std::size_t i = 0; i < in.size(); i += 2)
in[i] = false;
std::vector<bool, Alloc> out(80, false, Alloc(1));
std::copy(in.begin(), in.end(), out.begin() + 4);
for (std::size_t i = 0; i < in.size(); ++i)
assert(in[i] == out[i + 4]);
for (std::size_t i = 0; i < 4; ++i)
assert(out[i] == false);
for (std::size_t i = in.size() + 4; i < out.size(); ++i)
assert(out[i] == false);
}
}

return true;
}

Expand Down
Loading