Skip to content

Specialize fill and fill_n for vector<bool> #879

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 13 commits into from
Aug 1, 2020
59 changes: 59 additions & 0 deletions stl/inc/vector
Original file line number Diff line number Diff line change
Expand Up @@ -2882,6 +2882,65 @@ namespace pmr {
using vector = _STD vector<_Ty, polymorphic_allocator<_Ty>>;
} // namespace pmr
#endif // _HAS_CXX17

#if _HAS_IF_CONSTEXPR
// VARIABLE TEMPLATE _Is_vb_iterator
template <class _Alloc, bool _RequiresMutable>
_INLINE_VAR constexpr bool _Is_vb_iterator<_Vb_iterator<_Alloc>, _RequiresMutable> = true;

template <class _Alloc>
_INLINE_VAR constexpr bool _Is_vb_iterator<_Vb_const_iterator<_Alloc>, false> = true;

template <class _FwdIt, class _Ty>
_CONSTEXPR20 void _Fill_vbool(_FwdIt _First, _FwdIt _Last, const _Ty& _Val) {
// Set [_First, _Last) to _Val
if (_First == _Last) {
return;
}

_Vbase* _VbFirst = const_cast<_Vbase*>(_First._Myptr);
_Vbase* const _VbLast = const_cast<_Vbase*>(_Last._Myptr);

const auto _FirstSourceMask = static_cast<_Vbase>(-1) << _First._Myoff;
const auto _FirstDestMask = ~_FirstSourceMask;
const auto _FillVal = static_cast<_Vbase>(_Val ? -1 : 0);

if (_VbFirst == _VbLast) {
// We already excluded _First == _Last, so here _Last._Myoff > 0 and the shift is safe
const auto _LastSourceMask = static_cast<_Vbase>(-1) >> (_VBITS - _Last._Myoff);
const auto _LastDestMask = ~_LastSourceMask;
const auto _SourceMask = _FirstSourceMask & _LastSourceMask;
const auto _DestMask = _FirstDestMask | _LastDestMask;
*_VbFirst = (*_VbFirst & _DestMask) | (_FillVal & _SourceMask);
return;
}

*_VbFirst = (*_VbFirst & _FirstDestMask) | (_FillVal & _FirstSourceMask);
++_VbFirst;

#ifdef __cpp_lib_is_constant_evaluated
if (_STD is_constant_evaluated()) {
for (; _VbFirst != _VbLast; ++_VbFirst) {
*_VbFirst = _FillVal;
}
} else
#endif // __cpp_lib_is_constant_evaluated
{
const auto _VbFirst_ch = reinterpret_cast<const char*>(_VbFirst);
const auto _VbLast_ch = reinterpret_cast<const char*>(_VbLast);
const auto _Count_ch = static_cast<size_t>(_VbLast_ch - _VbFirst_ch);
const auto _ValChar = static_cast<unsigned char>(_Val ? -1 : 0);
_CSTD memset(_VbFirst, _ValChar, _Count_ch);
_VbFirst = _VbLast;
}

if (_Last._Myoff != 0) {
const auto _LastSourceMask = static_cast<_Vbase>(-1) >> (_VBITS - _Last._Myoff);
const auto _LastDestMask = ~_LastSourceMask;
*_VbFirst = (*_VbFirst & _LastDestMask) | (_FillVal & _LastSourceMask);
}
}
#endif // _HAS_IF_CONSTEXPR
_STD_END

#pragma pop_macro("new")
Expand Down
61 changes: 37 additions & 24 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -4302,6 +4302,10 @@ _OutIt _Copy_memmove(move_iterator<_InIt> _First, move_iterator<_InIt> _Last, _O
}

#if _HAS_IF_CONSTEXPR
// VARIABLE TEMPLATE _Is_vb_iterator
template <class _It, bool _RequiresMutable = false>
_INLINE_VAR constexpr bool _Is_vb_iterator = false;

template <class _InIt, class _OutIt>
_CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _InIt _Last, _OutIt _Dest) {
// copy [_First, _Last) to [_Dest, ...)
Expand Down Expand Up @@ -4717,20 +4721,24 @@ template <class _FwdIt, class _Ty>
_CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) {
// copy _Val through [_First, _Last)
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);
if constexpr (_Fill_memset_is_safe<decltype(_UFirst), _Ty>) {
if constexpr (_Is_vb_iterator<_FwdIt, true>) {
_Fill_vbool(_First, _Last, _Val);
} else {
auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);
if constexpr (_Fill_memset_is_safe<decltype(_UFirst), _Ty>) {
#ifdef __cpp_lib_is_constant_evaluated
if (!_STD is_constant_evaluated())
if (!_STD is_constant_evaluated())
#endif // __cpp_lib_is_constant_evaluated
{
_CSTD memset(_UFirst, static_cast<unsigned char>(_Val), static_cast<size_t>(_ULast - _UFirst));
return;
{
_CSTD memset(_UFirst, static_cast<unsigned char>(_Val), static_cast<size_t>(_ULast - _UFirst));
return;
}
}
}

for (; _UFirst != _ULast; ++_UFirst) {
*_UFirst = _Val;
for (; _UFirst != _ULast; ++_UFirst) {
*_UFirst = _Val;
}
}
}
#else // ^^^ _HAS_IF_CONSTEXPR // !_HAS_IF_CONSTEXPR vvv
Expand Down Expand Up @@ -4773,26 +4781,31 @@ _CONSTEXPR20 _OutIt fill_n(_OutIt _Dest, const _Diff _Count_raw, const _Ty& _Val
// copy _Val _Count times through [_Dest, ...)
_Algorithm_int_t<_Diff> _Count = _Count_raw;
if (0 < _Count) {
auto _UDest = _Get_unwrapped_n(_Dest, _Count);
if constexpr (_Fill_memset_is_safe<decltype(_UDest), _Ty>) {
if constexpr (_Is_vb_iterator<_OutIt, true>) {
const auto _Last = _Dest + static_cast<typename _OutIt::difference_type>(_Count);
_Fill_vbool(_Dest, _Last, _Val);
return _Last;
} else {
auto _UDest = _Get_unwrapped_n(_Dest, _Count);
if constexpr (_Fill_memset_is_safe<decltype(_UDest), _Ty>) {
#ifdef __cpp_lib_is_constant_evaluated
if (!_STD is_constant_evaluated())
if (!_STD is_constant_evaluated())
#endif // __cpp_lib_is_constant_evaluated
{
_CSTD memset(_UDest, static_cast<unsigned char>(_Val), static_cast<size_t>(_Count));
_UDest += _Count;
_Seek_wrapped(_Dest, _UDest);
return _Dest;
{
_CSTD memset(_UDest, static_cast<unsigned char>(_Val), static_cast<size_t>(_Count));
_UDest += _Count;
_Seek_wrapped(_Dest, _UDest);
return _Dest;
}
}
}

for (; 0 < _Count; --_Count, (void) ++_UDest) {
*_UDest = _Val;
}
for (; 0 < _Count; --_Count, (void) ++_UDest) {
*_UDest = _Val;
}

_Seek_wrapped(_Dest, _UDest);
_Seek_wrapped(_Dest, _UDest);
}
}

return _Dest;
}

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ tests\Dev11_1158803_regex_thread_safety
tests\Dev11_1180290_filesystem_error_code
tests\GH_000457_system_error_message
tests\GH_000545_include_compare
tests\GH_000625_vector_bool_optimization
tests\GH_000685_condition_variable_any
tests\GH_000690_overaligned_function
tests\GH_000890_pow_template
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/GH_000625_vector_bool_optimization/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_matrix.lst
101 changes: 101 additions & 0 deletions tests/std/tests/GH_000625_vector_bool_optimization/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <assert.h>
#include <vector>

using namespace std;

constexpr int blockSize = 32;
static_assert(blockSize == _VBITS, "Invalid block size");

void test_fill_helper(const size_t length) {
// No offset
{
vector<bool> result_true(length, true);
result_true.resize(length + 3, false);
vector<bool> dest_true(length + 3, false);
fill(dest_true.begin(), prev(dest_true.end(), 3), true);
assert(dest_true == result_true);

vector<bool> result_false(length, false);
result_false.resize(length + 3, true);
vector<bool> dest_false(length + 3, true);
fill(dest_false.begin(), prev(dest_false.end(), 3), false);
assert(dest_false == result_false);

vector<bool> result_true_n(length, true);
result_true_n.resize(length + 3, false);
vector<bool> dest_true_n(length + 3, false);
const auto res_fill_n = fill_n(dest_true_n.begin(), length, true);
assert(dest_true_n == result_true_n);
assert(res_fill_n == prev(dest_true_n.end(), 3));

vector<bool> result_false_n(length, false);
result_false_n.resize(length + 3, true);
vector<bool> dest_false_n(length + 3, true);
fill_n(dest_false_n.begin(), length, false);
assert(dest_false_n == result_false_n);
}

// With offset
{
vector<bool> result_true(length, true);
result_true.resize(length + 3, false);
result_true.insert(result_true.begin(), false);
vector<bool> dest_true(length + 4, false);
fill(next(dest_true.begin()), prev(dest_true.end(), 3), true);
assert(dest_true == result_true);

vector<bool> result_false(length, false);
result_false.resize(length + 3, true);
result_false.insert(result_false.begin(), true);
vector<bool> dest_false(length + 4, true);
fill(next(dest_false.begin()), prev(dest_false.end(), 3), false);
assert(dest_false == result_false);

vector<bool> result_true_n(length, true);
result_true_n.resize(length + 3, false);
result_true_n.insert(result_true_n.begin(), false);
vector<bool> dest_true_n(length + 4, false);
const auto res_fill_n = fill_n(next(dest_true_n.begin()), length, true);
assert(dest_true_n == result_true_n);
assert(res_fill_n == prev(dest_true_n.end(), 3));

vector<bool> result_false_n(length, false);
result_false_n.resize(length + 3, true);
result_false_n.insert(result_false_n.begin(), true);
vector<bool> dest_false_n(length + 4, true);
fill_n(next(dest_false_n.begin()), length, false);
assert(dest_false_n == result_false_n);
}
}

bool test_fill() {
// Empty
test_fill_helper(0);

// One block, ends within block
test_fill_helper(15);

// One block, ends at block boundary
test_fill_helper(blockSize);

// Multiple blocks, no memset, within block
test_fill_helper(blockSize + 11);

// Multiple blocks, no memset, ends at block boundary
test_fill_helper(2 * blockSize);

// Multiple blocks, with memset, within block
test_fill_helper(3 * blockSize + 5);

// Multiple blocks, with memset, ends at block boundary
test_fill_helper(4 * blockSize);
return true;
}

int main() {
test_fill();
}