Skip to content

Commit e476d1c

Browse files
committed
Fix copy_backward for vector<bool> with small storage types
1 parent 687c9d3 commit e476d1c

File tree

5 files changed

+279
-11
lines changed

5 files changed

+279
-11
lines changed

libcxx/include/__algorithm/copy_backward.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
5252
difference_type __dn = std::min(static_cast<difference_type>(__last.__ctz_), __n);
5353
__n -= __dn;
5454
unsigned __clz = __bits_per_word - __last.__ctz_;
55-
__storage_type __m = (~__storage_type(0) << (__last.__ctz_ - __dn)) & (~__storage_type(0) >> __clz);
55+
__storage_type __m = std::__middle_mask<__storage_type>(__clz, __last.__ctz_ - __dn);
5656
__storage_type __b = *__last.__seg_ & __m;
5757
*__result.__seg_ &= ~__m;
5858
*__result.__seg_ |= __b;
@@ -69,7 +69,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
6969
__n -= __nw * __bits_per_word;
7070
// do last word
7171
if (__n > 0) {
72-
__storage_type __m = ~__storage_type(0) << (__bits_per_word - __n);
72+
__storage_type __m = std::__leading_mask<__storage_type>(__bits_per_word - __n);
7373
__storage_type __b = *--__last.__seg_ & __m;
7474
*--__result.__seg_ &= ~__m;
7575
*__result.__seg_ |= __b;
@@ -94,12 +94,12 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
9494
difference_type __dn = std::min(static_cast<difference_type>(__last.__ctz_), __n);
9595
__n -= __dn;
9696
unsigned __clz_l = __bits_per_word - __last.__ctz_;
97-
__storage_type __m = (~__storage_type(0) << (__last.__ctz_ - __dn)) & (~__storage_type(0) >> __clz_l);
97+
__storage_type __m = std::__middle_mask<__storage_type>(__clz_l, __last.__ctz_ - __dn);
9898
__storage_type __b = *__last.__seg_ & __m;
9999
unsigned __clz_r = __bits_per_word - __result.__ctz_;
100100
__storage_type __ddn = std::min(__dn, static_cast<difference_type>(__result.__ctz_));
101101
if (__ddn > 0) {
102-
__m = (~__storage_type(0) << (__result.__ctz_ - __ddn)) & (~__storage_type(0) >> __clz_r);
102+
__m = std::__middle_mask<__storage_type>(__clz_r, __result.__ctz_ - __ddn);
103103
*__result.__seg_ &= ~__m;
104104
if (__result.__ctz_ > __last.__ctz_)
105105
*__result.__seg_ |= __b << (__result.__ctz_ - __last.__ctz_);
@@ -112,7 +112,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
112112
// __result.__ctz_ == 0
113113
--__result.__seg_;
114114
__result.__ctz_ = static_cast<unsigned>(-__dn & (__bits_per_word - 1));
115-
__m = ~__storage_type(0) << __result.__ctz_;
115+
__m = std::__leading_mask<__storage_type>(__result.__ctz_);
116116
*__result.__seg_ &= ~__m;
117117
__last.__ctz_ -= __dn + __ddn;
118118
*__result.__seg_ |= __b << (__result.__ctz_ - __last.__ctz_);
@@ -123,7 +123,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
123123
// __result.__ctz_ != 0 || __n == 0
124124
// do middle words
125125
unsigned __clz_r = __bits_per_word - __result.__ctz_;
126-
__storage_type __m = ~__storage_type(0) >> __clz_r;
126+
__storage_type __m = std::__trailing_mask<__storage_type>(__clz_r);
127127
for (; __n >= __bits_per_word; __n -= __bits_per_word) {
128128
__storage_type __b = *--__last.__seg_;
129129
*__result.__seg_ &= ~__m;
@@ -133,11 +133,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
133133
}
134134
// do last word
135135
if (__n > 0) {
136-
__m = ~__storage_type(0) << (__bits_per_word - __n);
136+
__m = std::__leading_mask<__storage_type>(__bits_per_word - __n);
137137
__storage_type __b = *--__last.__seg_ & __m;
138138
__clz_r = __bits_per_word - __result.__ctz_;
139139
__storage_type __dn = std::min(__n, static_cast<difference_type>(__result.__ctz_));
140-
__m = (~__storage_type(0) << (__result.__ctz_ - __dn)) & (~__storage_type(0) >> __clz_r);
140+
__m = std::__middle_mask<__storage_type>(__clz_r, __result.__ctz_ - __dn);
141141
*__result.__seg_ &= ~__m;
142142
*__result.__seg_ |= __b >> (__bits_per_word - __result.__ctz_);
143143
__result.__ctz_ = static_cast<unsigned>(((-__dn & (__bits_per_word - 1)) + __result.__ctz_) % __bits_per_word);
@@ -146,7 +146,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> _
146146
// __result.__ctz_ == 0
147147
--__result.__seg_;
148148
__result.__ctz_ = static_cast<unsigned>(-__n & (__bits_per_word - 1));
149-
__m = ~__storage_type(0) << __result.__ctz_;
149+
__m = std::__leading_mask<__storage_type>(__result.__ctz_);
150150
*__result.__seg_ &= ~__m;
151151
*__result.__seg_ |= __b << (__result.__ctz_ - (__bits_per_word - __n - __dn));
152152
}

libcxx/include/__bit_reference

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,20 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask
8282
return static_cast<_StorageType>(~static_cast<_StorageType>(0)) >> __clz;
8383
}
8484

85+
// Creates a mask of type `_StorageType` with a specified number of trailing zeros (__ctz) and sets all remaining
86+
// bits to one.
87+
template <class _StorageType>
88+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __leading_mask(unsigned __ctz) {
89+
static_assert(is_unsigned<_StorageType>::value, "__leading_mask only works with unsigned types");
90+
return static_cast<_StorageType>(~static_cast<_StorageType>(0)) << __ctz;
91+
}
92+
8593
// Creates a mask of type `_StorageType` with a specified number of leading zeros (__clz), a specified number of
8694
// trailing zeros (__ctz), and sets all bits in between to one.
8795
template <class _StorageType>
8896
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __clz, unsigned __ctz) {
8997
static_assert(is_unsigned<_StorageType>::value, "__middle_mask only works with unsigned types");
90-
return (static_cast<_StorageType>(~static_cast<_StorageType>(0)) << __ctz) &
91-
std::__trailing_mask<_StorageType>(__clz);
98+
return std::__leading_mask<_StorageType>(__ctz) & std::__trailing_mask<_StorageType>(__clz);
9299
}
93100

94101
// This function is designed to operate correctly even for smaller integral types like `uint8_t`, `uint16_t`,

libcxx/include/__fwd/bit_reference.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ __fill_masked_range(_StoragePointer __word, unsigned __ctz, unsigned __clz, bool
3333
template <class _StorageType>
3434
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask(unsigned __clz);
3535

36+
template <class _StorageType>
37+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __leading_mask(unsigned __ctz);
38+
3639
template <class _StorageType>
3740
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __clz, unsigned __ctz);
3841

libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_backward.pass.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
// constexpr OutIter // constexpr after C++17
1414
// copy_backward(InIter first, InIter last, OutIter result);
1515

16+
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
17+
1618
#include <algorithm>
1719
#include <cassert>
1820
#include <vector>
1921

22+
#include "sized_allocator.h"
2023
#include "test_macros.h"
2124
#include "test_iterators.h"
2225
#include "type_algorithms.h"
@@ -111,6 +114,133 @@ TEST_CONSTEXPR_CXX20 bool test() {
111114
assert(test_vector_bool(256));
112115
}
113116

117+
// Make sure std::copy_backward behaves properly with std::vector<bool> iterators with custom size types.
118+
{
119+
//// Tests for std::copy_backward with aligned bits
120+
121+
{ // Test the first (partial) word for uint8_t
122+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
123+
std::vector<bool, Alloc> in(8, true, Alloc(1));
124+
std::vector<bool, Alloc> out(8, false, Alloc(1));
125+
std::copy_backward(in.begin() + 2, in.end() - 2, out.end() - 2);
126+
// assert(std::equal(in.begin() + 2, in.end() - 2, out.begin() + 2));
127+
for (std::size_t i = 2; i < static_cast<std::size_t>(in.size() - 2); ++i)
128+
assert(in[i] == out[i]);
129+
}
130+
{ // Test the last word for uint8_t
131+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
132+
std::vector<bool, Alloc> in(12, true, Alloc(1));
133+
std::vector<bool, Alloc> out(15, false, Alloc(1));
134+
std::copy_backward(in.begin(), in.end(), out.end() - 3);
135+
// assert(std::equal(in.begin(), in.end(), out.begin()));
136+
for (std::size_t i = 0; i < in.size(); ++i)
137+
assert(in[i] == out[i]);
138+
}
139+
{ // Test middle words for uint8_t
140+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
141+
std::vector<bool, Alloc> in(24, true, Alloc(1));
142+
for (std::size_t i = 0; i < in.size(); i += 2)
143+
in[i] = false;
144+
std::vector<bool, Alloc> out(29, false, Alloc(1));
145+
std::copy_backward(in.begin(), in.end(), out.end() - 5);
146+
// assert(std::equal(in.begin(), in.end(), out.begin()));
147+
for (std::size_t i = 0; i < in.size(); ++i)
148+
assert(in[i] == out[i]);
149+
}
150+
151+
{ // Test the first (partial) word for uint16_t
152+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
153+
std::vector<bool, Alloc> in(16, true, Alloc(1));
154+
std::vector<bool, Alloc> out(16, false, Alloc(1));
155+
std::copy_backward(in.begin() + 3, in.end() - 2, out.end() - 2);
156+
// assert(std::equal(in.begin() + 3, in.end() - 2, out.begin() + 3));
157+
for (std::size_t i = 3; i < static_cast<std::size_t>(in.size() - 5); ++i)
158+
assert(in[i] == out[i]);
159+
}
160+
{ // Test the last word for uint16_t
161+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
162+
std::vector<bool, Alloc> in(24, true, Alloc(1));
163+
std::vector<bool, Alloc> out(32, false, Alloc(1));
164+
std::copy_backward(in.begin(), in.end(), out.end() - 8);
165+
// assert(std::equal(in.begin(), in.end(), out.begin()));
166+
for (std::size_t i = 0; i < in.size(); ++i)
167+
assert(in[i] == out[i]);
168+
}
169+
{ // Test middle words for uint16_t
170+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
171+
std::vector<bool, Alloc> in(48, true, Alloc(1));
172+
for (std::size_t i = 0; i < in.size(); i += 2)
173+
in[i] = false;
174+
std::vector<bool, Alloc> out(55, false, Alloc(1));
175+
std::copy_backward(in.begin(), in.end(), out.end() - 7);
176+
// assert(std::equal(in.begin(), in.end(), out.begin()));
177+
for (std::size_t i = 0; i < in.size(); ++i)
178+
assert(in[i] == out[i]);
179+
}
180+
181+
//// Tests for std::copy_backward with unaligned bits
182+
183+
{ // Test the first (partial) word for uint8_t
184+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
185+
std::vector<bool, Alloc> in(6, true, Alloc(1));
186+
std::vector<bool, Alloc> out(8, false, Alloc(1));
187+
std::copy_backward(in.begin(), in.end() - 2, out.end() - 1);
188+
// assert(std::equal(in.begin(), in.end() - 2, out.begin() + 3));
189+
for (std::size_t i = 0; i < static_cast<std::size_t>(in.size() - 2); ++i)
190+
assert(in[i] == out[i + 3]);
191+
}
192+
{ // Test the last word for uint8_t
193+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
194+
std::vector<bool, Alloc> in(5, true, Alloc(1));
195+
std::vector<bool, Alloc> out(8, false, Alloc(1));
196+
std::copy_backward(in.begin(), in.end(), out.end());
197+
// assert(std::equal(in.begin(), in.end(), out.begin() + 3));
198+
for (std::size_t i = 0; i < in.size(); ++i)
199+
assert(in[i] == out[i + 3]);
200+
}
201+
{ // Test middle words for uint8_t
202+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
203+
std::vector<bool, Alloc> in(16, true, Alloc(1));
204+
for (std::size_t i = 0; i < in.size(); i += 2)
205+
in[i] = false;
206+
std::vector<bool, Alloc> out(24, false, Alloc(1));
207+
std::copy_backward(in.begin(), in.end(), out.end() - 3);
208+
// assert(std::equal(in.begin(), in.end(), out.begin() + 5));
209+
for (std::size_t i = 0; i < in.size(); ++i)
210+
assert(in[i] == out[i + 5]);
211+
}
212+
213+
{ // Test the first (partial) word for uint16_t
214+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
215+
std::vector<bool, Alloc> in(12, true, Alloc(1));
216+
std::vector<bool, Alloc> out(16, false, Alloc(1));
217+
std::copy_backward(in.begin(), in.end() - 3, out.end() - 2);
218+
// assert(std::equal(in.begin(), in.end() - 3, out.begin() + 5));
219+
for (std::size_t i = 0; i < static_cast<std::size_t>(in.size() - 3); ++i)
220+
assert(in[i] == out[i + 5]);
221+
}
222+
{ // Test the last word for uint16_t
223+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
224+
std::vector<bool, Alloc> in(16, true, Alloc(1));
225+
std::vector<bool, Alloc> out(16, false, Alloc(1));
226+
std::copy_backward(in.begin() + 3, in.end(), out.end() - 3);
227+
// assert(std::equal(in.begin() + 3, in.end(), out.begin()));
228+
for (std::size_t i = 3; i < in.size(); ++i)
229+
assert(in[i] == out[i - 3]);
230+
}
231+
{ // Test the middle words for uint16_t
232+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
233+
std::vector<bool, Alloc> in(32, true, Alloc(1));
234+
for (std::size_t i = 0; i < in.size(); i += 2)
235+
in[i] = false;
236+
std::vector<bool, Alloc> out(64, false, Alloc(1));
237+
std::copy_backward(in.begin(), in.end(), out.end() - 4);
238+
// assert(std::equal(in.begin(), in.end(), out.begin() + 28));
239+
for (std::size_t i = 0; i < in.size(); ++i)
240+
assert(in[i] == out[i + 28]);
241+
}
242+
}
243+
114244
return true;
115245
}
116246

libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <vector>
2929

3030
#include "almost_satisfies_types.h"
31+
#include "sized_allocator.h"
3132
#include "test_iterators.h"
3233
#include "test_macros.h"
3334

@@ -355,6 +356,133 @@ constexpr bool test() {
355356
assert(test_vector_bool(199));
356357
assert(test_vector_bool(256));
357358
}
359+
360+
// Make sure std::ranges::copy_backward behaves properly with std::vector<bool> iterators with custom size types.
361+
{
362+
//// Tests for std::ranges::copy_backward with aligned bits
363+
364+
{ // Test the first (partial) word for uint8_t
365+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
366+
std::vector<bool, Alloc> in(8, true, Alloc(1));
367+
std::vector<bool, Alloc> out(8, false, Alloc(1));
368+
std::ranges::copy_backward(std::ranges::subrange(in.begin() + 2, in.end() - 2), out.end() - 2);
369+
// assert(std::ranges::equal(in.begin() + 2, in.end() - 2, out.begin() + 2, out.end() - 2));
370+
for (std::size_t i = 2; i < static_cast<std::size_t>(in.size() - 2); ++i)
371+
assert(in[i] == out[i]);
372+
}
373+
{ // Test the last word for uint8_t
374+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
375+
std::vector<bool, Alloc> in(12, true, Alloc(1));
376+
std::vector<bool, Alloc> out(15, false, Alloc(1));
377+
std::ranges::copy_backward(std::ranges::subrange(in.begin(), in.end()), out.end() - 3);
378+
// assert(std::ranges::equal(in.begin(), in.end(), out.begin(), out.end() - 3));
379+
for (std::size_t i = 0; i < in.size(); ++i)
380+
assert(in[i] == out[i]);
381+
}
382+
{ // Test middle words for uint8_t
383+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
384+
std::vector<bool, Alloc> in(24, true, Alloc(1));
385+
for (std::size_t i = 0; i < in.size(); i += 2)
386+
in[i] = false;
387+
std::vector<bool, Alloc> out(29, false, Alloc(1));
388+
std::ranges::copy_backward(std::ranges::subrange(in.begin(), in.end()), out.end() - 5);
389+
// assert(std::ranges::equal(in.begin(), in.end(), out.begin(), out.end() - 5));
390+
for (std::size_t i = 0; i < in.size(); ++i)
391+
assert(in[i] == out[i]);
392+
}
393+
394+
{ // Test the first (partial) word for uint16_t
395+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
396+
std::vector<bool, Alloc> in(16, true, Alloc(1));
397+
std::vector<bool, Alloc> out(16, false, Alloc(1));
398+
std::ranges::copy_backward(std::ranges::subrange(in.begin() + 3, in.end() - 2), out.end() - 2);
399+
// assert(std::ranges::equal(in.begin() + 3, in.end() - 2, out.begin() + 3, out.end() - 2));
400+
for (std::size_t i = 3; i < static_cast<std::size_t>(in.size() - 5); ++i)
401+
assert(in[i] == out[i]);
402+
}
403+
{ // Test the last word for uint16_t
404+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
405+
std::vector<bool, Alloc> in(24, true, Alloc(1));
406+
std::vector<bool, Alloc> out(32, false, Alloc(1));
407+
std::ranges::copy_backward(in.begin(), in.end(), out.end() - 8);
408+
// assert(std::ranges::equal(in.begin(), in.end(), out.begin(), out.end() - 8));
409+
for (std::size_t i = 0; i < in.size(); ++i)
410+
assert(in[i] == out[i]);
411+
}
412+
{ // Test middle words for uint16_t
413+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
414+
std::vector<bool, Alloc> in(48, true, Alloc(1));
415+
for (std::size_t i = 0; i < in.size(); i += 2)
416+
in[i] = false;
417+
std::vector<bool, Alloc> out(55, false, Alloc(1));
418+
std::ranges::copy_backward(std::ranges::subrange(in.begin(), in.end()), out.end() - 7);
419+
// assert(std::ranges::equal(in.begin(), in.end(), out.begin(), out.end() - 7));
420+
for (std::size_t i = 0; i < in.size(); ++i)
421+
assert(in[i] == out[i]);
422+
}
423+
424+
// //// Tests for std::ranges::copy_backward with unaligned bits
425+
426+
{ // Test the first (partial) word for uint8_t
427+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
428+
std::vector<bool, Alloc> in(6, true, Alloc(1));
429+
std::vector<bool, Alloc> out(8, false, Alloc(1));
430+
std::ranges::copy_backward(std::ranges::subrange(in.begin(), in.end() - 2), out.end() - 1);
431+
// assert(std::ranges::equal(in.begin(), in.end() - 2, out.begin() + 3, out.end() - 1));
432+
for (std::size_t i = 0; i < static_cast<std::size_t>(in.size() - 2); ++i)
433+
assert(in[i] == out[i + 3]);
434+
}
435+
{ // Test the last word for uint8_t
436+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
437+
std::vector<bool, Alloc> in(5, true, Alloc(1));
438+
std::vector<bool, Alloc> out(8, false, Alloc(1));
439+
std::ranges::copy_backward(std::ranges::subrange(in.begin(), in.end()), out.end());
440+
// assert(std::ranges::equal(in.begin(), in.end(), out.begin() + 3, out.end()));
441+
for (std::size_t i = 0; i < in.size(); ++i)
442+
assert(in[i] == out[i + 3]);
443+
}
444+
{ // Test middle words for uint8_t
445+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
446+
std::vector<bool, Alloc> in(16, true, Alloc(1));
447+
for (std::size_t i = 0; i < in.size(); i += 2)
448+
in[i] = false;
449+
std::vector<bool, Alloc> out(24, false, Alloc(1));
450+
std::ranges::copy_backward(std::ranges::subrange(in.begin(), in.end()), out.end() - 3);
451+
// assert(std::ranges::equal(in.begin(), in.end(), out.begin() + 5, out.end() - 3));
452+
for (std::size_t i = 0; i < in.size(); ++i)
453+
assert(in[i] == out[i + 5]);
454+
}
455+
456+
{ // Test the first (partial) word for uint16_t
457+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
458+
std::vector<bool, Alloc> in(12, true, Alloc(1));
459+
std::vector<bool, Alloc> out(16, false, Alloc(1));
460+
std::ranges::copy_backward(std::ranges::subrange(in.begin(), in.end() - 3), out.end() - 2);
461+
// assert(std::ranges::equal(in.begin(), in.end() - 3, out.begin() + 5, out.end() - 2));
462+
for (std::size_t i = 0; i < static_cast<std::size_t>(in.size() - 3); ++i)
463+
assert(in[i] == out[i + 5]);
464+
}
465+
{ // Test the last word for uint16_t
466+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
467+
std::vector<bool, Alloc> in(16, true, Alloc(1));
468+
std::vector<bool, Alloc> out(16, false, Alloc(1));
469+
std::ranges::copy_backward(std::ranges::subrange(in.begin() + 3, in.end()), out.end() - 3);
470+
// assert(std::ranges::equal(in.begin() + 3, in.end(), out.begin(), out.end() - 1));
471+
for (std::size_t i = 3; i < in.size(); ++i)
472+
assert(in[i] == out[i - 3]);
473+
}
474+
{ // Test the middle words for uint16_t
475+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
476+
std::vector<bool, Alloc> in(32, true, Alloc(1));
477+
for (std::size_t i = 0; i < in.size(); i += 2)
478+
in[i] = false;
479+
std::vector<bool, Alloc> out(64, false, Alloc(1));
480+
std::ranges::copy_backward(std::ranges::subrange(in.begin(), in.end()), out.end() - 4);
481+
// assert(std::ranges::equal(in.begin(), in.end(), out.begin() + 28, out.end() - 4));
482+
for (std::size_t i = 0; i < in.size(); ++i)
483+
assert(in[i] == out[i + 28]);
484+
}
485+
}
358486
#endif
359487

360488
return true;

0 commit comments

Comments
 (0)