mirror of
https://github.com/boostorg/container.git
synced 2026-08-04 03:34:11 +02:00
Add recursive output handling and improved leave condition return.
This commit is contained in:
@@ -502,13 +502,11 @@ void test_set_difference_single_segment_with_comp()
|
||||
// empty segments the two source walkers must skip independently of each
|
||||
// other -- are enumerated in full.
|
||||
//
|
||||
// The destination is enumerated at depth 1 only. segmented_set_difference
|
||||
// has no segmented_iterator_tag overload of set_difference_dst_bounded, so a
|
||||
// two-level segmented destination does not compile:
|
||||
// set_difference_until_exhausts would have to hand the leaf kernel a local
|
||||
// iterator that is itself segmented. That gap in the header is known and
|
||||
// queued for a separate fix; once it lands this can become a plain
|
||||
// for_each_shape3_all.
|
||||
// The destination is enumerated by for_each_dest_shape_all, so it is walked at
|
||||
// both depths and over both spec families. A two-level destination is what
|
||||
// reaches the segmented_iterator_tag overload of set_difference_dst_bounded,
|
||||
// where the local iterator handed to the level below is itself still
|
||||
// segmented.
|
||||
//
|
||||
// Two destination sizes are run per combination. The output length is
|
||||
// data-dependent, so the guard at index n3 only catches an overrun of the
|
||||
@@ -517,6 +515,75 @@ void test_set_difference_single_segment_with_comp()
|
||||
// second size leaves three unwritten slots, which must still hold the fill.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! One destination shape: runs the algorithm into it and checks the result,
|
||||
//! the written prefix, the untouched tail and the guard.
|
||||
struct set_difference_dst_check
|
||||
{
|
||||
const boost::container::vector<int>* ref;
|
||||
const char* s1;
|
||||
std::size_t n1;
|
||||
const char* s2;
|
||||
std::size_t n2;
|
||||
|
||||
set_difference_dst_check(const boost::container::vector<int>& r,
|
||||
const char* a1, std::size_t b1,
|
||||
const char* a2, std::size_t b2)
|
||||
: ref(&r), s1(a1), n1(b1), s2(a2), n2(b2)
|
||||
{}
|
||||
|
||||
void report(const char* s3, std::size_t n3) const
|
||||
{
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM
|
||||
<< " shapes \"" << s1 << "\"(" << n1 << ") / \"" << s2 << "\"(" << n2
|
||||
<< ") -> \"" << s3 << "\"(" << n3 << ")" << std::endl;
|
||||
}
|
||||
|
||||
template<class C1, class C2, class Dst>
|
||||
void run(C1& c1, C2& c2, Dst& out, std::size_t n3, const char* s3) const
|
||||
{
|
||||
typedef typename Dst::iterator dst_iter_t;
|
||||
|
||||
const dst_iter_t r = segmented_set_difference
|
||||
( c1.begin(), test_detail::iter_at(c1, n1)
|
||||
, c2.begin(), test_detail::iter_at(c2, n2)
|
||||
, out.begin());
|
||||
|
||||
if(!BOOST_TEST(r == test_detail::iter_at(out, ref->size())))
|
||||
this->report(s3, n3);
|
||||
|
||||
// flatten_n_ints, not flatten_all_ints: the guard past index n3
|
||||
// is not part of the answer, and is checked on its own below.
|
||||
const boost::container::vector<int> got = test_detail::flatten_n_ints(out, n3);
|
||||
for(std::size_t k = 0; k != n3; ++k) {
|
||||
const int want = k < ref->size() ? (*ref)[k] : -1;
|
||||
if(!BOOST_TEST_EQ(got[k], want)) {
|
||||
this->report(s3, n3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!BOOST_TEST(test_detail::filler_intact(out, n3, -999)))
|
||||
this->report(s3, n3);
|
||||
}
|
||||
};
|
||||
|
||||
//! Binds the two inputs so the destination combinator can supply the third.
|
||||
template<class C1, class C2>
|
||||
struct set_difference_dst_bind
|
||||
{
|
||||
C1* c1;
|
||||
C2* c2;
|
||||
set_difference_dst_check chk;
|
||||
|
||||
set_difference_dst_bind(C1& a, C2& b, const set_difference_dst_check& c)
|
||||
: c1(&a), c2(&b), chk(c)
|
||||
{}
|
||||
|
||||
template<class Dst>
|
||||
void operator()(Dst& out, std::size_t n3, const char* s3) const
|
||||
{ chk.run(*c1, *c2, out, n3, s3); }
|
||||
};
|
||||
|
||||
struct set_difference_shape_check
|
||||
{
|
||||
std::size_t extra; // destination slots beyond the expected output length
|
||||
@@ -535,9 +602,6 @@ struct set_difference_shape_check
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
typedef test_detail::seg_vector<int> dst_t;
|
||||
typedef test_detail::seg_vector<int>::iterator dst_iter_t;
|
||||
|
||||
const boost::container::vector<int> f1 = test_detail::flatten_n_ints(c1, n1);
|
||||
const boost::container::vector<int> f2 = test_detail::flatten_n_ints(c2, n2);
|
||||
|
||||
@@ -556,38 +620,9 @@ struct set_difference_shape_check
|
||||
|
||||
const std::size_t n3 = ref.size() + extra;
|
||||
|
||||
for(std::size_t fam = 0; fam != test_detail::shape_all_families(); ++fam) {
|
||||
std::size_t cnt = 0;
|
||||
const char* const* dspecs = test_detail::shape_specs_family(fam, 1u, cnt);
|
||||
for(std::size_t d = 0; d != cnt; ++d) {
|
||||
if(!test_detail::shape_feasible(dspecs[d], n3)) continue;
|
||||
|
||||
dst_t out;
|
||||
test_detail::make_dest_range(out, dspecs[d], n3, -1, -999);
|
||||
|
||||
const dst_iter_t r = segmented_set_difference
|
||||
( c1.begin(), test_detail::iter_at(c1, n1)
|
||||
, c2.begin(), test_detail::iter_at(c2, n2)
|
||||
, out.begin());
|
||||
|
||||
if(!BOOST_TEST(r == test_detail::iter_at(out, ref.size())))
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
|
||||
// flatten_n_ints, not flatten_all_ints: the guard past index n3
|
||||
// is not part of the answer, and is checked on its own below.
|
||||
const boost::container::vector<int> got = test_detail::flatten_n_ints(out, n3);
|
||||
for(std::size_t k = 0; k != n3; ++k) {
|
||||
const int want = k < ref.size() ? ref[k] : -1;
|
||||
if(!BOOST_TEST_EQ(got[k], want)) {
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!BOOST_TEST(test_detail::filler_intact(out, n3, -999)))
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
}
|
||||
}
|
||||
const set_difference_dst_bind<C1, C2> dst
|
||||
(c1, c2, set_difference_dst_check(ref, s1, n1, s2, n2));
|
||||
test_detail::for_each_dest_shape_all<int>(n3, -1, -999, dst);
|
||||
|
||||
// Neither input is an output.
|
||||
if(!BOOST_TEST(test_detail::filler_intact(c1, n1, -999)))
|
||||
|
||||
@@ -496,13 +496,11 @@ void test_set_intersection_single_segment_with_comp()
|
||||
// empty segments the two source walkers must skip independently of each
|
||||
// other -- are enumerated in full.
|
||||
//
|
||||
// The destination is enumerated at depth 1 only. segmented_set_intersection
|
||||
// has no segmented_iterator_tag overload of set_intersection_dst_bounded, so
|
||||
// a two-level segmented destination does not compile:
|
||||
// set_intersection_until_exhausts would have to hand the leaf kernel a local
|
||||
// iterator that is itself segmented. That gap in the header is known and
|
||||
// queued for a separate fix; once it lands this can become a plain
|
||||
// for_each_shape3_all.
|
||||
// The destination is enumerated by for_each_dest_shape_all, so it is walked at
|
||||
// both depths and over both spec families. A two-level destination is what
|
||||
// reaches the segmented_iterator_tag overload of set_intersection_dst_bounded,
|
||||
// where the local iterator handed to the level below is itself still
|
||||
// segmented.
|
||||
//
|
||||
// Two destination sizes are run per combination. The output length is
|
||||
// data-dependent, so the guard at index n3 only catches an overrun of the
|
||||
@@ -511,6 +509,75 @@ void test_set_intersection_single_segment_with_comp()
|
||||
// second size leaves three unwritten slots, which must still hold the fill.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! One destination shape: runs the algorithm into it and checks the result,
|
||||
//! the written prefix, the untouched tail and the guard.
|
||||
struct set_intersection_dst_check
|
||||
{
|
||||
const boost::container::vector<int>* ref;
|
||||
const char* s1;
|
||||
std::size_t n1;
|
||||
const char* s2;
|
||||
std::size_t n2;
|
||||
|
||||
set_intersection_dst_check(const boost::container::vector<int>& r,
|
||||
const char* a1, std::size_t b1,
|
||||
const char* a2, std::size_t b2)
|
||||
: ref(&r), s1(a1), n1(b1), s2(a2), n2(b2)
|
||||
{}
|
||||
|
||||
void report(const char* s3, std::size_t n3) const
|
||||
{
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM
|
||||
<< " shapes \"" << s1 << "\"(" << n1 << ") / \"" << s2 << "\"(" << n2
|
||||
<< ") -> \"" << s3 << "\"(" << n3 << ")" << std::endl;
|
||||
}
|
||||
|
||||
template<class C1, class C2, class Dst>
|
||||
void run(C1& c1, C2& c2, Dst& out, std::size_t n3, const char* s3) const
|
||||
{
|
||||
typedef typename Dst::iterator dst_iter_t;
|
||||
|
||||
const dst_iter_t r = segmented_set_intersection
|
||||
( c1.begin(), test_detail::iter_at(c1, n1)
|
||||
, c2.begin(), test_detail::iter_at(c2, n2)
|
||||
, out.begin());
|
||||
|
||||
if(!BOOST_TEST(r == test_detail::iter_at(out, ref->size())))
|
||||
this->report(s3, n3);
|
||||
|
||||
// flatten_n_ints, not flatten_all_ints: the guard past index n3
|
||||
// is not part of the answer, and is checked on its own below.
|
||||
const boost::container::vector<int> got = test_detail::flatten_n_ints(out, n3);
|
||||
for(std::size_t k = 0; k != n3; ++k) {
|
||||
const int want = k < ref->size() ? (*ref)[k] : -1;
|
||||
if(!BOOST_TEST_EQ(got[k], want)) {
|
||||
this->report(s3, n3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!BOOST_TEST(test_detail::filler_intact(out, n3, -999)))
|
||||
this->report(s3, n3);
|
||||
}
|
||||
};
|
||||
|
||||
//! Binds the two inputs so the destination combinator can supply the third.
|
||||
template<class C1, class C2>
|
||||
struct set_intersection_dst_bind
|
||||
{
|
||||
C1* c1;
|
||||
C2* c2;
|
||||
set_intersection_dst_check chk;
|
||||
|
||||
set_intersection_dst_bind(C1& a, C2& b, const set_intersection_dst_check& c)
|
||||
: c1(&a), c2(&b), chk(c)
|
||||
{}
|
||||
|
||||
template<class Dst>
|
||||
void operator()(Dst& out, std::size_t n3, const char* s3) const
|
||||
{ chk.run(*c1, *c2, out, n3, s3); }
|
||||
};
|
||||
|
||||
struct set_intersection_shape_check
|
||||
{
|
||||
std::size_t extra; // destination slots beyond the expected output length
|
||||
@@ -529,9 +596,6 @@ struct set_intersection_shape_check
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
typedef test_detail::seg_vector<int> dst_t;
|
||||
typedef test_detail::seg_vector<int>::iterator dst_iter_t;
|
||||
|
||||
const boost::container::vector<int> f1 = test_detail::flatten_n_ints(c1, n1);
|
||||
const boost::container::vector<int> f2 = test_detail::flatten_n_ints(c2, n2);
|
||||
|
||||
@@ -549,38 +613,9 @@ struct set_intersection_shape_check
|
||||
|
||||
const std::size_t n3 = ref.size() + extra;
|
||||
|
||||
for(std::size_t fam = 0; fam != test_detail::shape_all_families(); ++fam) {
|
||||
std::size_t cnt = 0;
|
||||
const char* const* dspecs = test_detail::shape_specs_family(fam, 1u, cnt);
|
||||
for(std::size_t d = 0; d != cnt; ++d) {
|
||||
if(!test_detail::shape_feasible(dspecs[d], n3)) continue;
|
||||
|
||||
dst_t out;
|
||||
test_detail::make_dest_range(out, dspecs[d], n3, -1, -999);
|
||||
|
||||
const dst_iter_t r = segmented_set_intersection
|
||||
( c1.begin(), test_detail::iter_at(c1, n1)
|
||||
, c2.begin(), test_detail::iter_at(c2, n2)
|
||||
, out.begin());
|
||||
|
||||
if(!BOOST_TEST(r == test_detail::iter_at(out, ref.size())))
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
|
||||
// flatten_n_ints, not flatten_all_ints: the guard past index n3
|
||||
// is not part of the answer, and is checked on its own below.
|
||||
const boost::container::vector<int> got = test_detail::flatten_n_ints(out, n3);
|
||||
for(std::size_t k = 0; k != n3; ++k) {
|
||||
const int want = k < ref.size() ? ref[k] : -1;
|
||||
if(!BOOST_TEST_EQ(got[k], want)) {
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!BOOST_TEST(test_detail::filler_intact(out, n3, -999)))
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
}
|
||||
}
|
||||
const set_intersection_dst_bind<C1, C2> dst
|
||||
(c1, c2, set_intersection_dst_check(ref, s1, n1, s2, n2));
|
||||
test_detail::for_each_dest_shape_all<int>(n3, -1, -999, dst);
|
||||
|
||||
// Neither input is an output.
|
||||
if(!BOOST_TEST(test_detail::filler_intact(c1, n1, -999)))
|
||||
|
||||
@@ -517,13 +517,11 @@ void test_set_symmetric_difference_single_segment_with_comp()
|
||||
// empty segments the two source walkers must skip independently of each
|
||||
// other -- are enumerated in full.
|
||||
//
|
||||
// The destination is enumerated at depth 1 only.
|
||||
// segmented_set_symmetric_difference has no segmented_iterator_tag overload
|
||||
// of set_symmetric_difference_dst_bounded, so a two-level segmented
|
||||
// destination does not compile: the until_exhausts layer would have to hand
|
||||
// the leaf kernel a local iterator that is itself segmented. That gap in
|
||||
// the header is known and queued for a separate fix; once it lands this can
|
||||
// become a plain for_each_shape3_all.
|
||||
// The destination is enumerated by for_each_dest_shape_all, so it is walked at
|
||||
// both depths and over both spec families. A two-level destination is what
|
||||
// reaches the segmented_iterator_tag overload of
|
||||
// set_symmetric_difference_dst_bounded, where the local iterator handed to the
|
||||
// level below is itself still segmented.
|
||||
//
|
||||
// Two destination sizes are run per combination. The output length is
|
||||
// data-dependent, so the guard at index n3 only catches an overrun of the
|
||||
@@ -532,11 +530,83 @@ void test_set_symmetric_difference_single_segment_with_comp()
|
||||
// second size leaves three unwritten slots, which must still hold the fill.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! One destination shape: runs the algorithm into it and checks the result,
|
||||
//! the written prefix, the untouched tail and the guard.
|
||||
struct set_symmetric_difference_dst_check
|
||||
{
|
||||
const boost::container::vector<int>* ref;
|
||||
const char* s1;
|
||||
std::size_t n1;
|
||||
const char* s2;
|
||||
std::size_t n2;
|
||||
|
||||
set_symmetric_difference_dst_check(const boost::container::vector<int>& r,
|
||||
const char* a1, std::size_t b1,
|
||||
const char* a2, std::size_t b2)
|
||||
: ref(&r), s1(a1), n1(b1), s2(a2), n2(b2)
|
||||
{}
|
||||
|
||||
void report(const char* s3, std::size_t n3) const
|
||||
{
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM
|
||||
<< " shapes \"" << s1 << "\"(" << n1 << ") / \"" << s2 << "\"(" << n2
|
||||
<< ") -> \"" << s3 << "\"(" << n3 << ")" << std::endl;
|
||||
}
|
||||
|
||||
template<class C1, class C2, class Dst>
|
||||
void run(C1& c1, C2& c2, Dst& out, std::size_t n3, const char* s3) const
|
||||
{
|
||||
typedef typename Dst::iterator dst_iter_t;
|
||||
|
||||
const dst_iter_t r = segmented_set_symmetric_difference
|
||||
( c1.begin(), test_detail::iter_at(c1, n1)
|
||||
, c2.begin(), test_detail::iter_at(c2, n2)
|
||||
, out.begin());
|
||||
|
||||
if(!BOOST_TEST(r == test_detail::iter_at(out, ref->size())))
|
||||
this->report(s3, n3);
|
||||
|
||||
// flatten_n_ints, not flatten_all_ints: the guard past index n3
|
||||
// is not part of the answer, and is checked on its own below.
|
||||
const boost::container::vector<int> got = test_detail::flatten_n_ints(out, n3);
|
||||
for(std::size_t k = 0; k != n3; ++k) {
|
||||
const int want = k < ref->size() ? (*ref)[k] : -1;
|
||||
if(!BOOST_TEST_EQ(got[k], want)) {
|
||||
this->report(s3, n3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!BOOST_TEST(test_detail::filler_intact(out, n3, -999)))
|
||||
this->report(s3, n3);
|
||||
}
|
||||
};
|
||||
|
||||
//! Binds the two inputs so the destination combinator can supply the third.
|
||||
template<class C1, class C2>
|
||||
struct set_symmetric_difference_dst_bind
|
||||
{
|
||||
C1* c1;
|
||||
C2* c2;
|
||||
set_symmetric_difference_dst_check chk;
|
||||
|
||||
set_symmetric_difference_dst_bind(C1& a, C2& b,
|
||||
const set_symmetric_difference_dst_check& c)
|
||||
: c1(&a), c2(&b), chk(c)
|
||||
{}
|
||||
|
||||
template<class Dst>
|
||||
void operator()(Dst& out, std::size_t n3, const char* s3) const
|
||||
{ chk.run(*c1, *c2, out, n3, s3); }
|
||||
};
|
||||
|
||||
struct set_symmetric_difference_shape_check
|
||||
{
|
||||
std::size_t extra; // destination slots beyond the expected output length
|
||||
|
||||
explicit set_symmetric_difference_shape_check(std::size_t e) : extra(e) {}
|
||||
explicit set_symmetric_difference_shape_check(std::size_t e)
|
||||
: extra(e)
|
||||
{}
|
||||
|
||||
void report(const char* s1, std::size_t n1, const char* s2, std::size_t n2,
|
||||
const char* s3, std::size_t n3) const
|
||||
@@ -550,9 +620,6 @@ struct set_symmetric_difference_shape_check
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
typedef test_detail::seg_vector<int> dst_t;
|
||||
typedef test_detail::seg_vector<int>::iterator dst_iter_t;
|
||||
|
||||
const boost::container::vector<int> f1 = test_detail::flatten_n_ints(c1, n1);
|
||||
const boost::container::vector<int> f2 = test_detail::flatten_n_ints(c2, n2);
|
||||
|
||||
@@ -572,38 +639,9 @@ struct set_symmetric_difference_shape_check
|
||||
|
||||
const std::size_t n3 = ref.size() + extra;
|
||||
|
||||
for(std::size_t fam = 0; fam != test_detail::shape_all_families(); ++fam) {
|
||||
std::size_t cnt = 0;
|
||||
const char* const* dspecs = test_detail::shape_specs_family(fam, 1u, cnt);
|
||||
for(std::size_t d = 0; d != cnt; ++d) {
|
||||
if(!test_detail::shape_feasible(dspecs[d], n3)) continue;
|
||||
|
||||
dst_t out;
|
||||
test_detail::make_dest_range(out, dspecs[d], n3, -1, -999);
|
||||
|
||||
const dst_iter_t r = segmented_set_symmetric_difference
|
||||
( c1.begin(), test_detail::iter_at(c1, n1)
|
||||
, c2.begin(), test_detail::iter_at(c2, n2)
|
||||
, out.begin());
|
||||
|
||||
if(!BOOST_TEST(r == test_detail::iter_at(out, ref.size())))
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
|
||||
// flatten_n_ints, not flatten_all_ints: the guard past index n3
|
||||
// is not part of the answer, and is checked on its own below.
|
||||
const boost::container::vector<int> got = test_detail::flatten_n_ints(out, n3);
|
||||
for(std::size_t k = 0; k != n3; ++k) {
|
||||
const int want = k < ref.size() ? ref[k] : -1;
|
||||
if(!BOOST_TEST_EQ(got[k], want)) {
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!BOOST_TEST(test_detail::filler_intact(out, n3, -999)))
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
}
|
||||
}
|
||||
const set_symmetric_difference_dst_bind<C1, C2> dst
|
||||
(c1, c2, set_symmetric_difference_dst_check(ref, s1, n1, s2, n2));
|
||||
test_detail::for_each_dest_shape_all<int>(n3, -1, -999, dst);
|
||||
|
||||
// Neither input is an output.
|
||||
if(!BOOST_TEST(test_detail::filler_intact(c1, n1, -999)))
|
||||
|
||||
@@ -531,12 +531,10 @@ void test_set_union_single_segment_with_comp()
|
||||
// empty segments the two source walkers must skip independently of each
|
||||
// other -- are enumerated in full.
|
||||
//
|
||||
// The destination is enumerated at depth 1 only. segmented_set_union has no
|
||||
// segmented_iterator_tag overload of set_union_dst_bounded, so a two-level
|
||||
// segmented destination does not compile: set_union_until_exhausts would
|
||||
// have to hand the leaf kernel a local iterator that is itself segmented.
|
||||
// That gap in the header is known and queued for a separate fix; once it
|
||||
// lands this can become a plain for_each_shape3_all.
|
||||
// The destination is enumerated by for_each_dest_shape_all, so it is walked at
|
||||
// both depths and over both spec families. A two-level destination is what
|
||||
// reaches the segmented_iterator_tag overload of set_union_dst_bounded, where
|
||||
// the local iterator handed to the level below is itself still segmented.
|
||||
//
|
||||
// Two destination sizes are run per combination. set_union's output length
|
||||
// is data-dependent, so the guard at index n3 only catches an overrun of the
|
||||
@@ -545,6 +543,75 @@ void test_set_union_single_segment_with_comp()
|
||||
// second size leaves three unwritten slots, which must still hold the fill.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! One destination shape: runs the algorithm into it and checks the result,
|
||||
//! the written prefix, the untouched tail and the guard.
|
||||
struct set_union_dst_check
|
||||
{
|
||||
const boost::container::vector<int>* ref;
|
||||
const char* s1;
|
||||
std::size_t n1;
|
||||
const char* s2;
|
||||
std::size_t n2;
|
||||
|
||||
set_union_dst_check(const boost::container::vector<int>& r,
|
||||
const char* a1, std::size_t b1,
|
||||
const char* a2, std::size_t b2)
|
||||
: ref(&r), s1(a1), n1(b1), s2(a2), n2(b2)
|
||||
{}
|
||||
|
||||
void report(const char* s3, std::size_t n3) const
|
||||
{
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM
|
||||
<< " shapes \"" << s1 << "\"(" << n1 << ") / \"" << s2 << "\"(" << n2
|
||||
<< ") -> \"" << s3 << "\"(" << n3 << ")" << std::endl;
|
||||
}
|
||||
|
||||
template<class C1, class C2, class Dst>
|
||||
void run(C1& c1, C2& c2, Dst& out, std::size_t n3, const char* s3) const
|
||||
{
|
||||
typedef typename Dst::iterator dst_iter_t;
|
||||
|
||||
const dst_iter_t r = segmented_set_union
|
||||
( c1.begin(), test_detail::iter_at(c1, n1)
|
||||
, c2.begin(), test_detail::iter_at(c2, n2)
|
||||
, out.begin());
|
||||
|
||||
if(!BOOST_TEST(r == test_detail::iter_at(out, ref->size())))
|
||||
this->report(s3, n3);
|
||||
|
||||
// flatten_n_ints, not flatten_all_ints: the guard past index n3
|
||||
// is not part of the answer, and is checked on its own below.
|
||||
const boost::container::vector<int> got = test_detail::flatten_n_ints(out, n3);
|
||||
for(std::size_t k = 0; k != n3; ++k) {
|
||||
const int want = k < ref->size() ? (*ref)[k] : -1;
|
||||
if(!BOOST_TEST_EQ(got[k], want)) {
|
||||
this->report(s3, n3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!BOOST_TEST(test_detail::filler_intact(out, n3, -999)))
|
||||
this->report(s3, n3);
|
||||
}
|
||||
};
|
||||
|
||||
//! Binds the two inputs so the destination combinator can supply the third.
|
||||
template<class C1, class C2>
|
||||
struct set_union_dst_bind
|
||||
{
|
||||
C1* c1;
|
||||
C2* c2;
|
||||
set_union_dst_check chk;
|
||||
|
||||
set_union_dst_bind(C1& a, C2& b, const set_union_dst_check& c)
|
||||
: c1(&a), c2(&b), chk(c)
|
||||
{}
|
||||
|
||||
template<class Dst>
|
||||
void operator()(Dst& out, std::size_t n3, const char* s3) const
|
||||
{ chk.run(*c1, *c2, out, n3, s3); }
|
||||
};
|
||||
|
||||
struct set_union_shape_check
|
||||
{
|
||||
std::size_t extra; // destination slots beyond the expected output length
|
||||
@@ -563,9 +630,6 @@ struct set_union_shape_check
|
||||
void operator()(C1& c1, std::size_t n1, const char* s1,
|
||||
C2& c2, std::size_t n2, const char* s2) const
|
||||
{
|
||||
typedef test_detail::seg_vector<int> dst_t;
|
||||
typedef test_detail::seg_vector<int>::iterator dst_iter_t;
|
||||
|
||||
const boost::container::vector<int> f1 = test_detail::flatten_n_ints(c1, n1);
|
||||
const boost::container::vector<int> f2 = test_detail::flatten_n_ints(c2, n2);
|
||||
|
||||
@@ -585,38 +649,9 @@ struct set_union_shape_check
|
||||
|
||||
const std::size_t n3 = ref.size() + extra;
|
||||
|
||||
for(std::size_t fam = 0; fam != test_detail::shape_all_families(); ++fam) {
|
||||
std::size_t cnt = 0;
|
||||
const char* const* dspecs = test_detail::shape_specs_family(fam, 1u, cnt);
|
||||
for(std::size_t d = 0; d != cnt; ++d) {
|
||||
if(!test_detail::shape_feasible(dspecs[d], n3)) continue;
|
||||
|
||||
dst_t out;
|
||||
test_detail::make_dest_range(out, dspecs[d], n3, -1, -999);
|
||||
|
||||
const dst_iter_t r = segmented_set_union
|
||||
( c1.begin(), test_detail::iter_at(c1, n1)
|
||||
, c2.begin(), test_detail::iter_at(c2, n2)
|
||||
, out.begin());
|
||||
|
||||
if(!BOOST_TEST(r == test_detail::iter_at(out, ref.size())))
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
|
||||
// flatten_n_ints, not flatten_all_ints: the guard past index n3
|
||||
// is not part of the answer, and is checked on its own below.
|
||||
const boost::container::vector<int> got = test_detail::flatten_n_ints(out, n3);
|
||||
for(std::size_t k = 0; k != n3; ++k) {
|
||||
const int want = k < ref.size() ? ref[k] : -1;
|
||||
if(!BOOST_TEST_EQ(got[k], want)) {
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!BOOST_TEST(test_detail::filler_intact(out, n3, -999)))
|
||||
this->report(s1, n1, s2, n2, dspecs[d], n3);
|
||||
}
|
||||
}
|
||||
const set_union_dst_bind<C1, C2> dst
|
||||
(c1, c2, set_union_dst_check(ref, s1, n1, s2, n2));
|
||||
test_detail::for_each_dest_shape_all<int>(n3, -1, -999, dst);
|
||||
|
||||
// Neither input is an output.
|
||||
if(!BOOST_TEST(test_detail::filler_intact(c1, n1, -999)))
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
#define BOOST_CONTAINER_TEST_SEGMENTED_TEST_HELPER_HPP
|
||||
|
||||
#include <boost/container/experimental/segmented_iterator_traits.hpp>
|
||||
#include <boost/container/detail/iterator.hpp>
|
||||
#include <boost/container/vector.hpp>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <ostream>
|
||||
|
||||
namespace test_detail {
|
||||
@@ -230,30 +230,6 @@ public:
|
||||
template<class Iter>
|
||||
sentinel_wrapper<Iter> make_sentinel(Iter it) { return sentinel_wrapper<Iter>(it); }
|
||||
|
||||
template<class Iter>
|
||||
class sized_sentinel_wrapper
|
||||
{
|
||||
Iter it_;
|
||||
public:
|
||||
explicit sized_sentinel_wrapper(Iter it) : it_(it) {}
|
||||
|
||||
operator Iter() const { return it_; }
|
||||
|
||||
friend bool operator==(const Iter& a, const sized_sentinel_wrapper& b) { return a == b.it_; }
|
||||
friend bool operator!=(const Iter& a, const sized_sentinel_wrapper& b) { return !(a == b.it_); }
|
||||
friend bool operator==(const sized_sentinel_wrapper& a, const Iter& b) { return a.it_ == b; }
|
||||
friend bool operator!=(const sized_sentinel_wrapper& a, const Iter& b) { return !(a.it_ == b); }
|
||||
|
||||
friend typename boost::container::iterator_traits<Iter>::difference_type
|
||||
operator-(const sized_sentinel_wrapper& a, const Iter& b) { return a.it_ - b; }
|
||||
|
||||
friend typename boost::container::iterator_traits<Iter>::difference_type
|
||||
operator-(const Iter& a, const sized_sentinel_wrapper& b) { return a - b.it_; }
|
||||
};
|
||||
|
||||
template<class Iter>
|
||||
sized_sentinel_wrapper<Iter> make_sized_sentinel(Iter it) { return sized_sentinel_wrapper<Iter>(it); }
|
||||
|
||||
template<class T, class Cat = std::bidirectional_iterator_tag>
|
||||
class seg2_vector_iterator
|
||||
{
|
||||
@@ -417,9 +393,9 @@ public:
|
||||
// empty segments: one before the data, one between the two halves and one
|
||||
// after. At the outer level of a seg2_vector those are outer segments whose
|
||||
// inner container is logically empty. Empty segments are where carry-across-
|
||||
// boundary bugs live, and 'e' is the only way to produce them. 'e' is not
|
||||
// part of the default enumeration: for_each_shape() and friends walk the m/s
|
||||
// specs exactly as they always did, and the _all variants walk both tables.
|
||||
// boundary bugs live, and 'e' is the only way to produce them. 'e' specs
|
||||
// live in a table of their own, which the combinators walk after the core
|
||||
// m/s one.
|
||||
//
|
||||
// make_range() builds a container of exactly n+1 elements. The first n hold
|
||||
// the requested values and form the range [c.begin(), iter_at(c, n)); the
|
||||
@@ -460,42 +436,40 @@ public:
|
||||
// true while the guard still holds filler, i.e. the
|
||||
// algorithm has not written past the end.
|
||||
//
|
||||
// Combinators. Each one builds every range afresh for every combination, so
|
||||
// a callable is free to mutate the ranges it is handed. The callable is taken
|
||||
// by value and copied, so any state it accumulates must live behind a pointer
|
||||
// or reference.
|
||||
// for_each_shape<T>(vals, n, filler, f)
|
||||
// Combinators. Each one walks the core m/s specs first, in their original
|
||||
// order, and then the empty-segment ones. Each builds every range afresh for
|
||||
// every combination, so a callable is free to mutate the ranges it is handed.
|
||||
// The callable is taken by value and copied, so any state it accumulates must
|
||||
// live behind a pointer or reference.
|
||||
// for_each_shape_all<T>(vals, n, filler, f)
|
||||
// f(c, n, spec) once per feasible spec, depth 1 then depth 2,
|
||||
// bidirectional iterators.
|
||||
// for_each_shape_fwd<T>(vals, n, filler, f)
|
||||
// for_each_shape_all_fwd<T>(vals, n, filler, f)
|
||||
// the same with forward iterators, for algorithms that have a distinct
|
||||
// forward-iterator implementation.
|
||||
// for_each_shape_cat<T, Cat>(vals, n, filler, f)
|
||||
// for_each_shape_all_cat<T, Cat>(vals, n, filler, f)
|
||||
// the same for an explicit iterator category.
|
||||
// for_each_shape2<T1, T2>(v1, n1, v2, n2, filler, f)
|
||||
// for_each_shape2_all<T1, T2>(v1, n1, v2, n2, filler, f)
|
||||
// f(c1, n1, spec1, c2, n2, spec2) over the cross product of the two
|
||||
// ranges' specs. Range 2 doubles as the output range of a copy-style
|
||||
// algorithm; build it with an array of fill values.
|
||||
// for_each_shape3<T1, T2, T3>(v1, n1, v2, n2, v3, n3, filler, f)
|
||||
// for_each_shape3_all<T1, T2, T3>(v1, n1, v2, n2, v3, n3, filler, f)
|
||||
// the same for three ranges, e.g. two inputs and one output.
|
||||
// for_each_shape_all / _all_cat / _all_fwd / for_each_shape2_all /
|
||||
// for_each_shape3_all
|
||||
// the same enumerations extended with the 'e' specs. The core specs
|
||||
// still come first, in their original order, so switching a test from
|
||||
// for_each_shape to for_each_shape_all only ever adds cases.
|
||||
// for_each_dest_shape_all<T>(n, fill, filler, g)
|
||||
// g(c, n, spec) over the destination shapes alone, both depths and both
|
||||
// spec families.
|
||||
//
|
||||
// A feasible spec needs at least one element per 'm' or 'e' level, so a small
|
||||
// n yields fewer shapes than a large one; the combinators skip the rest.
|
||||
// With n large enough each range contributes 6 core shapes and 6 more with
|
||||
// empty segments:
|
||||
//
|
||||
// n for_each_shape for_each_shape_all
|
||||
// 0 2 2
|
||||
// 1 5 8
|
||||
// >= 2 6 12
|
||||
// n for_each_shape_all
|
||||
// 0 2
|
||||
// 1 8
|
||||
// >= 2 12
|
||||
//
|
||||
// so for_each_shape2 runs 36 combinations and for_each_shape2_all 144, while
|
||||
// for_each_shape3 runs 216 and for_each_shape3_all 1728.
|
||||
// so for_each_shape2_all runs 144 combinations and for_each_shape3_all 1728.
|
||||
//
|
||||
// Intended usage for a test:
|
||||
//
|
||||
@@ -517,7 +491,7 @@ public:
|
||||
// BOOST_TEST(spec != 0);
|
||||
// }
|
||||
// };
|
||||
// for_each_shape<int>(vals, n, -999, check(...));
|
||||
// for_each_shape_all<int>(vals, n, -999, check(...));
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -652,9 +626,9 @@ inline const char* const* shape_specs(std::size_t depth, std::size_t& count)
|
||||
}
|
||||
|
||||
//! Branch specs in which at least one level also carries empty segments.
|
||||
//! Kept in a table of its own so that shape_specs(), and therefore
|
||||
//! for_each_shape(), enumerate exactly what they enumerated before 'e'
|
||||
//! existed. The _all combinators walk both tables.
|
||||
//! Kept in a table of its own so that shape_specs() enumerates exactly what it
|
||||
//! enumerated before 'e' existed, and so that the combinators can walk the
|
||||
//! core specs first and the empty-segment ones after.
|
||||
inline const char* const* shape_specs_empty(std::size_t depth, std::size_t& count)
|
||||
{
|
||||
static const char* const d1[] = { "e" };
|
||||
@@ -669,9 +643,8 @@ inline const char* const* shape_specs_empty(std::size_t depth, std::size_t& coun
|
||||
inline const char* const* shape_specs_family(std::size_t family, std::size_t depth, std::size_t& count)
|
||||
{ return family == 0u ? shape_specs(depth, count) : shape_specs_empty(depth, count); }
|
||||
|
||||
//! Number of spec families a plain for_each_shape* walks, and the number the
|
||||
//! _all variants walk.
|
||||
inline std::size_t shape_core_families() { return 1u; }
|
||||
//! Number of spec families the combinators walk: the core m/s table plus the
|
||||
//! empty-segment one.
|
||||
inline std::size_t shape_all_families() { return 2u; }
|
||||
|
||||
//! Builds the container that spec describes at the given depth and hands it to
|
||||
@@ -693,8 +666,8 @@ void with_shape(std::size_t depth, const char* spec, const int* vals, std::size_
|
||||
}
|
||||
|
||||
//! Walks the first nfam spec families over depths 1 and 2, calling
|
||||
//! f(container, n, spec) once per feasible spec. With nfam == 1 the
|
||||
//! enumeration is exactly the core m/s one, in exactly its original order.
|
||||
//! f(container, n, spec) once per feasible spec, the core m/s family first
|
||||
//! and in exactly its original order.
|
||||
template<class T, class Cat, class F>
|
||||
void for_each_shape_fam_cat(std::size_t nfam, const int* vals, std::size_t n, int filler, F f)
|
||||
{
|
||||
@@ -710,34 +683,20 @@ void for_each_shape_fam_cat(std::size_t nfam, const int* vals, std::size_t n, in
|
||||
}
|
||||
}
|
||||
|
||||
//! Calls f(container, n, spec) once per reachable branch spec at depths 1 and 2.
|
||||
//! f must be callable with both container types.
|
||||
template<class T, class Cat, class F>
|
||||
void for_each_shape_cat(const int* vals, std::size_t n, int filler, F f)
|
||||
{ for_each_shape_fam_cat<T, Cat>(shape_core_families(), vals, n, filler, f); }
|
||||
|
||||
//! Bidirectional-iterator shapes, the default for most algorithms.
|
||||
template<class T, class F>
|
||||
void for_each_shape(const int* vals, std::size_t n, int filler, F f)
|
||||
{ for_each_shape_cat<T, std::bidirectional_iterator_tag>(vals, n, filler, f); }
|
||||
|
||||
//! Forward-iterator shapes. Algorithms with a separate forward-iterator
|
||||
//! segmented implementation (segmented_find_last, segmented_find_last_if)
|
||||
//! only reach it through these.
|
||||
template<class T, class F>
|
||||
void for_each_shape_fwd(const int* vals, std::size_t n, int filler, F f)
|
||||
{ for_each_shape_cat<T, std::forward_iterator_tag>(vals, n, filler, f); }
|
||||
|
||||
//! Core shapes followed by the empty-segment ones. Use these wherever the
|
||||
//! algorithm has to cope with a segment that contributes no elements.
|
||||
//! Core shapes followed by the empty-segment ones, once per reachable branch
|
||||
//! spec at depths 1 and 2. f must be callable with both container types.
|
||||
template<class T, class Cat, class F>
|
||||
void for_each_shape_all_cat(const int* vals, std::size_t n, int filler, F f)
|
||||
{ for_each_shape_fam_cat<T, Cat>(shape_all_families(), vals, n, filler, f); }
|
||||
|
||||
//! Bidirectional-iterator shapes, the default for most algorithms.
|
||||
template<class T, class F>
|
||||
void for_each_shape_all(const int* vals, std::size_t n, int filler, F f)
|
||||
{ for_each_shape_all_cat<T, std::bidirectional_iterator_tag>(vals, n, filler, f); }
|
||||
|
||||
//! Forward-iterator shapes. Algorithms with a separate forward-iterator
|
||||
//! segmented implementation (segmented_find_last, segmented_find_last_if)
|
||||
//! only reach it through these.
|
||||
template<class T, class F>
|
||||
void for_each_shape_all_fwd(const int* vals, std::size_t n, int filler, F f)
|
||||
{ for_each_shape_all_cat<T, std::forward_iterator_tag>(vals, n, filler, f); }
|
||||
@@ -812,13 +771,9 @@ void for_each_shape2_fam(std::size_t nfam, const int* v1, std::size_t n1,
|
||||
}
|
||||
}
|
||||
|
||||
//! Cross product of the branch specs of two independently segmented ranges.
|
||||
//! Calls f(c1, n1, spec1, c2, n2, spec2).
|
||||
template<class T1, class T2, class F>
|
||||
void for_each_shape2(const int* v1, std::size_t n1, const int* v2, std::size_t n2, int filler, F f)
|
||||
{ for_each_shape2_fam<T1, T2>(shape_core_families(), v1, n1, v2, n2, filler, f); }
|
||||
|
||||
//! The same, including the empty-segment shapes for both ranges.
|
||||
//! Cross product of the branch specs of two independently segmented ranges,
|
||||
//! including the empty-segment shapes for both. Calls
|
||||
//! f(c1, n1, spec1, c2, n2, spec2).
|
||||
template<class T1, class T2, class F>
|
||||
void for_each_shape2_all(const int* v1, std::size_t n1, const int* v2, std::size_t n2, int filler, F f)
|
||||
{ for_each_shape2_fam<T1, T2>(shape_all_families(), v1, n1, v2, n2, filler, f); }
|
||||
@@ -841,7 +796,8 @@ struct shape3_bind
|
||||
};
|
||||
|
||||
//! Builds range 3 for one fixed (depth, spec) pair; the first two ranges are
|
||||
//! supplied by for_each_shape2, which already rebuilds them per combination.
|
||||
//! supplied by for_each_shape2_fam, which already rebuilds them per
|
||||
//! combination.
|
||||
template<class T3, class F>
|
||||
struct shape3_outer
|
||||
{
|
||||
@@ -884,20 +840,38 @@ void for_each_shape3_fam(std::size_t nfam, const int* v1, std::size_t n1,
|
||||
}
|
||||
}
|
||||
|
||||
//! Cross product of the branch specs of three independently segmented ranges.
|
||||
//! Calls f(c1, n1, spec1, c2, n2, spec2, c3, n3, spec3). Typically two input
|
||||
//! ranges and one output range.
|
||||
template<class T1, class T2, class T3, class F>
|
||||
void for_each_shape3(const int* v1, std::size_t n1, const int* v2, std::size_t n2,
|
||||
const int* v3, std::size_t n3, int filler, F f)
|
||||
{ for_each_shape3_fam<T1, T2, T3>(shape_core_families(), v1, n1, v2, n2, v3, n3, filler, f); }
|
||||
|
||||
//! The same, including the empty-segment shapes for all three ranges.
|
||||
//! Cross product of the branch specs of three independently segmented ranges,
|
||||
//! including the empty-segment shapes for all three. Calls
|
||||
//! f(c1, n1, spec1, c2, n2, spec2, c3, n3, spec3). Typically two input ranges
|
||||
//! and one output range.
|
||||
template<class T1, class T2, class T3, class F>
|
||||
void for_each_shape3_all(const int* v1, std::size_t n1, const int* v2, std::size_t n2,
|
||||
const int* v3, std::size_t n3, int filler, F f)
|
||||
{ for_each_shape3_fam<T1, T2, T3>(shape_all_families(), v1, n1, v2, n2, v3, n3, filler, f); }
|
||||
|
||||
//! Destination shapes alone, at both depths and including the empty-segment
|
||||
//! table. for_each_shape3_all needs every range length up front, which an
|
||||
//! algorithm whose output length depends on its input data cannot supply: the
|
||||
//! length is only known once the inputs have been built and the expected
|
||||
//! answer computed. Such a test enumerates its inputs with
|
||||
//! for_each_shape2_all and then calls this from inside, which keeps the
|
||||
//! destination shapes enumerated in full rather than pinned to one depth.
|
||||
template<class T, class G>
|
||||
void for_each_dest_shape_all(std::size_t n, int fill, int filler, G g)
|
||||
{
|
||||
boost::container::vector<int> vals(n ? n : 1u, fill);
|
||||
for(std::size_t fam = 0u; fam != shape_all_families(); ++fam) {
|
||||
for(std::size_t d = 1u; d <= max_shape_depth(); ++d) {
|
||||
std::size_t cnt = 0;
|
||||
const char* const* sp = shape_specs_family(fam, d, cnt);
|
||||
for(std::size_t i = 0; i != cnt; ++i) {
|
||||
if(!shape_feasible(sp[i], n)) continue;
|
||||
with_shape<T, std::bidirectional_iterator_tag>(d, sp[i], &vals[0], n, filler, g);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! True if the guard element that make_range() placed just past the range end
|
||||
//! still holds the filler value, i.e. the algorithm did not write past the end.
|
||||
template<class Cont>
|
||||
|
||||
@@ -42,52 +42,73 @@ struct equal_pred
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Bounded iter2 helper: compares source [first1, last1) against
|
||||
// [first2, iter2_last), stopping when source, iter2, or a mismatch
|
||||
// [first2, last2), stopping when source, iter2, or a mismatch
|
||||
// is encountered.
|
||||
// Returns segduo<SrcIter, Iter2> with the final positions of both
|
||||
// iterators. Recursively walks iter2 segments when iter2 is segmented.
|
||||
// Returns segtrio<SrcIter, Iter2, bool> with the final positions of both
|
||||
// iterators and the reason the walk stopped.
|
||||
// Recursively walks iter2 segments when iter2 is segmented.
|
||||
//
|
||||
// When iter2_last is unreachable_sentinel_t the segment-boundary check
|
||||
// is optimised away, giving the same code as an unbounded loop.
|
||||
// third is true when the walk ended for a reason the caller cannot resume
|
||||
// from (a mismatch was found, or the source was consumed), and false when
|
||||
// only [first2, last2) ran out. That last case is the only one in which a
|
||||
// segmented caller advances to its next segment and calls again, so one flag
|
||||
// is enough to drive the segment loop. Reporting it here spares the caller
|
||||
// the two comparisons it would otherwise need to re-derive it, which are the
|
||||
// very comparisons this helper has just made.
|
||||
//
|
||||
// When last2 is unreachable_sentinel_t the segment-boundary check is
|
||||
// optimised away, giving the same code as an unbounded loop, and third folds
|
||||
// to a constant true.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class SrcIter, class Sent, class Iter2, class Iter2Sent, class BinaryPred, class Iter2Tag, class SrcCat>
|
||||
BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c<!Iter2Tag::value, segduo<SrcIter, Iter2> >::type
|
||||
typename algo_enable_if_c<!Iter2Tag::value, segtrio<SrcIter, Iter2, bool> >::type
|
||||
segmented_equal_iter2_bounded
|
||||
(SrcIter first1, Sent last1, Iter2 first2, Iter2Sent iter2_last, BinaryPred pred, Iter2Tag, SrcCat)
|
||||
(SrcIter first1, Sent last1, Iter2 first2, Iter2Sent last2, BinaryPred pred, Iter2Tag, SrcCat)
|
||||
{
|
||||
typedef segtrio<SrcIter, Iter2, bool> result_t;
|
||||
|
||||
BOOST_CONTAINER_SEGMENTED_UNROLL(4)
|
||||
for(; first1 != last1; ++first1) {
|
||||
if(first2 == iter2_last)
|
||||
goto out_path;
|
||||
if(first2 == last2)
|
||||
return result_t(first1, first2, false);
|
||||
if(!pred(*first1, *first2))
|
||||
return segduo<SrcIter, Iter2>(first1, first2);
|
||||
return result_t(first1, first2, true);
|
||||
++first2;
|
||||
}
|
||||
out_path:
|
||||
return segduo<SrcIter, Iter2>(first1, first2);
|
||||
return result_t(first1, first2, true);
|
||||
}
|
||||
|
||||
template <class RASrcIter, class RAIter2, class BinaryPred>
|
||||
BOOST_CONTAINER_FORCEINLINE
|
||||
typename iterator_enable_if_tag
|
||||
<RAIter2, std::random_access_iterator_tag, segduo<RASrcIter, RAIter2> >::type
|
||||
<RAIter2, std::random_access_iterator_tag, segtrio<RASrcIter, RAIter2, bool> >::type
|
||||
segmented_equal_iter2_bounded
|
||||
(RASrcIter first1, RASrcIter last1, RAIter2 first2, RAIter2 iter2_last, BinaryPred pred,
|
||||
const non_segmented_iterator_tag &, const std::random_access_iterator_tag &src_tag)
|
||||
(RASrcIter first1, RASrcIter last1, RAIter2 first2, RAIter2 last2, BinaryPred pred,
|
||||
const non_segmented_iterator_tag &, const std::random_access_iterator_tag &)
|
||||
{
|
||||
typedef segtrio<RASrcIter, RAIter2, bool> result_t;
|
||||
typedef typename iterator_traits<RASrcIter>::difference_type difference_type;
|
||||
const difference_type src_n = last1 - first1;
|
||||
const difference_type iter2_n = difference_type(iter2_last - first2);
|
||||
const difference_type n = src_n < iter2_n ? src_n : iter2_n;
|
||||
return (segmented_equal_iter2_bounded)(first1, first1 + n, first2, unreachable_sentinel_t(),
|
||||
pred, non_segmented_iterator_tag(), src_tag);
|
||||
const difference_type iter2_n = difference_type(last2 - first2);
|
||||
difference_type n = src_n < iter2_n ? src_n : iter2_n;
|
||||
|
||||
BOOST_CONTAINER_SEGMENTED_UNROLL(4)
|
||||
while(n) {
|
||||
--n;
|
||||
if(!pred(*first1, *first2))
|
||||
return result_t(first1, first2, true);
|
||||
++first1;
|
||||
++first2;
|
||||
}
|
||||
|
||||
return result_t(first1, first2, first1 == last1);
|
||||
}
|
||||
|
||||
template <class SrcIter, class Sent, class SegIter2, class BinaryPred, class SrcCat>
|
||||
segduo<SrcIter, SegIter2> segmented_equal_iter2_bounded
|
||||
(SrcIter first1, Sent last1, SegIter2 iter2_first, SegIter2 iter2_last, BinaryPred pred,
|
||||
segtrio<SrcIter, SegIter2, bool> segmented_equal_iter2_bounded
|
||||
(SrcIter first1, Sent last1, SegIter2 first2, SegIter2 last2, BinaryPred pred,
|
||||
segmented_iterator_tag, SrcCat)
|
||||
{
|
||||
typedef segmented_iterator_traits<SegIter2> iter2_traits;
|
||||
@@ -95,37 +116,37 @@ segduo<SrcIter, SegIter2> segmented_equal_iter2_bounded
|
||||
typedef typename iter2_traits::segment_iterator iter2_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<iter2_local_iterator>::is_segmented_iterator iter2_is_local_seg_t;
|
||||
|
||||
iter2_segment_iterator sfirst = iter2_traits::segment(iter2_first);
|
||||
const iter2_segment_iterator slast = iter2_traits::segment(iter2_last);
|
||||
typedef segtrio<SrcIter, SegIter2, bool> result_t;
|
||||
typedef segtrio<SrcIter, iter2_local_iterator, bool> local_result_t;
|
||||
|
||||
iter2_local_iterator lb2 = iter2_traits::local(iter2_first);
|
||||
iter2_segment_iterator sfirst = iter2_traits::segment(first2);
|
||||
const iter2_segment_iterator slast = iter2_traits::segment(last2);
|
||||
|
||||
iter2_local_iterator lb2 = iter2_traits::local(first2);
|
||||
|
||||
if(BOOST_CONTAINER_SEG_LIKELY(sfirst != slast)) {
|
||||
iter2_local_iterator end2 = iter2_traits::end(sfirst);
|
||||
{
|
||||
const segduo<SrcIter, iter2_local_iterator> r = (segmented_equal_iter2_bounded)
|
||||
(first1, last1, lb2, end2, pred, iter2_is_local_seg_t(), SrcCat());
|
||||
const local_result_t r = (segmented_equal_iter2_bounded)
|
||||
(first1, last1, lb2, iter2_traits::end(sfirst), pred, iter2_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
const iter2_local_iterator loc2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || loc2 != end2))
|
||||
return segduo<SrcIter, SegIter2>(first1, iter2_traits::compose(sfirst, loc2));
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.third))
|
||||
return result_t(first1, iter2_traits::compose(sfirst, r.second), true);
|
||||
}
|
||||
|
||||
for(++sfirst; sfirst != slast; ++sfirst) {
|
||||
end2 = iter2_traits::end(sfirst);
|
||||
const segduo<SrcIter, iter2_local_iterator> r = (segmented_equal_iter2_bounded)
|
||||
(first1, last1, iter2_traits::begin(sfirst), end2, pred, iter2_is_local_seg_t(), SrcCat());
|
||||
const local_result_t r = (segmented_equal_iter2_bounded)
|
||||
(first1, last1, iter2_traits::begin(sfirst), iter2_traits::end(sfirst), pred
|
||||
, iter2_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
const iter2_local_iterator loc2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || loc2 != end2))
|
||||
return segduo<SrcIter, SegIter2>(first1, iter2_traits::compose(sfirst, loc2));
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.third))
|
||||
return result_t(first1, iter2_traits::compose(sfirst, r.second), true);
|
||||
}
|
||||
|
||||
lb2 = iter2_traits::begin(slast);
|
||||
}
|
||||
const segduo<SrcIter, iter2_local_iterator> r = (segmented_equal_iter2_bounded)
|
||||
(first1, last1, lb2, iter2_traits::local(iter2_last), pred, iter2_is_local_seg_t(), SrcCat());
|
||||
return segduo<SrcIter, SegIter2>(r.first, iter2_traits::compose(sfirst, r.second));
|
||||
const local_result_t r = (segmented_equal_iter2_bounded)
|
||||
(first1, last1, lb2, iter2_traits::local(last2), pred, iter2_is_local_seg_t(), SrcCat());
|
||||
return result_t(r.first, iter2_traits::compose(sfirst, r.second), r.third);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -139,7 +160,9 @@ BOOST_CONTAINER_FORCEINLINE segduo<bool, InpIter2> segmented_equal_iter2_dispatc
|
||||
(SrcIter first1, Sent last1, InpIter2 first2, BinaryPred pred,
|
||||
const non_segmented_iterator_tag &, Cat)
|
||||
{
|
||||
segduo<SrcIter, InpIter2> r = (segmented_equal_iter2_bounded)
|
||||
//r.third is a constant true for an unbounded iter2, so the result still
|
||||
//comes from the source position.
|
||||
const segtrio<SrcIter, InpIter2, bool> r = (segmented_equal_iter2_bounded)
|
||||
(first1, last1, first2, unreachable_sentinel_t(), pred, non_segmented_iterator_tag(), Cat());
|
||||
return segduo<bool, InpIter2>(r.first == last1, r.second);
|
||||
}
|
||||
@@ -160,20 +183,19 @@ segduo<bool, SegIter2> segmented_equal_iter2_dispatch
|
||||
iter2_segment_iterator seg2 = iter2_traits::segment(first2);
|
||||
iter2_local_iterator loc2 = iter2_traits::local(first2);
|
||||
|
||||
while(first1 != last1) {
|
||||
iter2_local_iterator end2 = iter2_traits::end(seg2);
|
||||
segduo<SrcIter, iter2_local_iterator> r = (segmented_equal_iter2_bounded)
|
||||
(first1, last1, loc2, end2, pred, iter2_is_local_seg_t(), Cat());
|
||||
for(;;) {
|
||||
const segtrio<SrcIter, iter2_local_iterator, bool> r = (segmented_equal_iter2_bounded)
|
||||
(first1, last1, loc2, iter2_traits::end(seg2), pred, iter2_is_local_seg_t(), Cat());
|
||||
first1 = r.first;
|
||||
loc2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 != last1 && loc2 != end2))
|
||||
return segduo<bool, SegIter2>(false, iter2_traits::compose(seg2, loc2));
|
||||
if(BOOST_CONTAINER_SEG_LIKELY(first1 != last1)) {
|
||||
++seg2;
|
||||
loc2 = iter2_traits::begin(seg2);
|
||||
}
|
||||
loc2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.third))
|
||||
break;
|
||||
++seg2;
|
||||
loc2 = iter2_traits::begin(seg2);
|
||||
}
|
||||
return segduo<bool, SegIter2>(true, iter2_traits::compose(seg2, loc2));
|
||||
//A mismatch leaves first1 on the offending element, so only an exhausted
|
||||
//source means every element compared equal.
|
||||
return segduo<bool, SegIter2>(first1 == last1, iter2_traits::compose(seg2, loc2));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -57,41 +57,44 @@ namespace detail_algo {
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class DstIter, class DstSent,
|
||||
class Comp, class DstTag, class SrcCat>
|
||||
BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c<!DstTag::value, segtrio<Iter1, Iter2, DstIter> >::type
|
||||
typename algo_enable_if_c<!DstTag::value, segquartet<Iter1, Iter2, DstIter, bool> >::type
|
||||
merge_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
DstIter dst_first, DstSent dst_last, Comp comp, DstTag, SrcCat)
|
||||
{
|
||||
// Per-iteration the loop only checks exhaustion on the side that was
|
||||
// just consumed (the other side and dst position are unchanged by the
|
||||
// current step). This matches the STL textbook merge structure and
|
||||
// produces tight branch-based code on MSVC/GCC: each branch of the
|
||||
// merge step ends with `break`, so the loop-bottom test reflects the
|
||||
// consumed side only and MSVC emits a single cmp+branch per element.
|
||||
// On clang the same shape is preserved, and because each branch ends
|
||||
// in a different exhaustion check, if-conversion to a cmov chain is
|
||||
// prevented (cmov would serialise the iteration and cost ~3x on
|
||||
// predictable data). When dst_last is unreachable_sentinel_t the
|
||||
// dst-full check folds away.
|
||||
if(first1 != last1 && first2 != last2 && dst_first != dst_last) {
|
||||
while(true) {
|
||||
if(comp(*first2, *first1)) {
|
||||
*dst_first = *first2;
|
||||
++first2;
|
||||
++dst_first;
|
||||
if(first2 == last2 || dst_first == dst_last)
|
||||
break;
|
||||
}
|
||||
else {
|
||||
*dst_first = *first1;
|
||||
++first1;
|
||||
++dst_first;
|
||||
if(first1 == last1 || dst_first == dst_last)
|
||||
break;
|
||||
bool src_done = true;
|
||||
if(first1 != last1 && first2 != last2) {
|
||||
if(dst_first != dst_last) {
|
||||
while(true) {
|
||||
if(comp(*first2, *first1)) {
|
||||
*dst_first = *first2;
|
||||
++first2;
|
||||
++dst_first;
|
||||
if(first2 == last2)
|
||||
break;
|
||||
if(dst_first == dst_last) {
|
||||
src_done = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*dst_first = *first1;
|
||||
++first1;
|
||||
++dst_first;
|
||||
if(first1 == last1)
|
||||
break;
|
||||
if(dst_first == dst_last) {
|
||||
src_done = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
src_done = false;
|
||||
}
|
||||
}
|
||||
return segtrio<Iter1, Iter2, DstIter>(first1, first2, dst_first);
|
||||
return segquartet<Iter1, Iter2, DstIter, bool>(first1, first2, dst_first, src_done);
|
||||
}
|
||||
|
||||
template <std::size_t BlockSize, class RAIter1, class RAIter2, class DstIter,
|
||||
@@ -122,7 +125,7 @@ BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c
|
||||
< !DstTag::value && seg_is_ra_iterator<RAIter2>::value
|
||||
&& seg_is_ra_iterator<DstIter>::value
|
||||
, segtrio<RAIter1, RAIter2, DstIter> >::type
|
||||
, segquartet<RAIter1, RAIter2, DstIter, bool> >::type
|
||||
merge_dst_bounded
|
||||
(RAIter1 first1, RAIter1 last1, RAIter2 first2, RAIter2 last2,
|
||||
DstIter dst_first, DstSent dst_last, Comp comp, DstTag dst_tag,
|
||||
@@ -136,7 +139,7 @@ merge_dst_bounded
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
class Comp, class SrcCat>
|
||||
segtrio<Iter1, Iter2, SegDstIter> merge_dst_bounded
|
||||
segquartet<Iter1, Iter2, SegDstIter, bool> merge_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
SegDstIter dst_first, SegDstIter dst_last, Comp comp,
|
||||
segmented_iterator_tag, SrcCat)
|
||||
@@ -145,8 +148,8 @@ segtrio<Iter1, Iter2, SegDstIter> merge_dst_bounded
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segtrio<Iter1, Iter2, dst_local_iterator> local_result_t;
|
||||
typedef segtrio<Iter1, Iter2, SegDstIter> result_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> local_result_t;
|
||||
typedef segquartet<Iter1, Iter2, SegDstIter, bool> result_t;
|
||||
|
||||
dst_segment_iterator sfirst = dst_traits::segment(dst_first);
|
||||
const dst_segment_iterator slast = dst_traits::segment(dst_last);
|
||||
@@ -160,8 +163,8 @@ segtrio<Iter1, Iter2, SegDstIter> merge_dst_bounded
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || first2 == last2))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third));
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
for(++sfirst; sfirst != slast; ++sfirst) {
|
||||
@@ -170,8 +173,8 @@ segtrio<Iter1, Iter2, SegDstIter> merge_dst_bounded
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if (BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || first2 == last2))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third));
|
||||
if (BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
db = dst_traits::begin(slast);
|
||||
@@ -179,7 +182,7 @@ segtrio<Iter1, Iter2, SegDstIter> merge_dst_bounded
|
||||
const local_result_t r = (merge_dst_bounded)
|
||||
( first1, last1, first2, last2, db
|
||||
, dst_traits::local(dst_last), comp, dst_is_local_seg_t(), SrcCat());
|
||||
return result_t(r.first, r.second, dst_traits::compose(sfirst, r.third));
|
||||
return result_t(r.first, r.second, dst_traits::compose(sfirst, r.third), r.fourth);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -204,9 +207,12 @@ segtrio<Iter1, Iter2, DstIter> merge_until_exhausts
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2, DstIter result, Comp comp,
|
||||
const /*non_segmented_iterator_tag*/Tag &, const Cat &src1_cat)
|
||||
{
|
||||
return (merge_dst_bounded)
|
||||
//An unbounded destination can only stop on source exhaustion, so the leaf's
|
||||
//flag is a constant here and drops out.
|
||||
const segquartet<Iter1, Iter2, DstIter, bool> r = (merge_dst_bounded)
|
||||
(first1, last1, first2, last2, result, unreachable_sentinel_t(),
|
||||
comp, non_segmented_iterator_tag(), src1_cat);
|
||||
return segtrio<Iter1, Iter2, DstIter>(r.first, r.second, r.third);
|
||||
}
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
@@ -219,8 +225,8 @@ segtrio<Iter1, Iter2, SegDstIter> merge_until_exhausts
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segtrio<Iter1, Iter2, dst_local_iterator> bounded_t;
|
||||
typedef segtrio<Iter1, Iter2, SegDstIter> result_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> bounded_t;
|
||||
typedef segtrio<Iter1, Iter2, SegDstIter> result_t;
|
||||
|
||||
if(BOOST_UNLIKELY(first1 == last1 || first2 == last2))
|
||||
return result_t(first1, first2, result);
|
||||
@@ -241,8 +247,9 @@ segtrio<Iter1, Iter2, SegDstIter> merge_until_exhausts
|
||||
// the output ends exactly on a segment boundary both hold at once, and
|
||||
// stepping dst_seg then walks off the end of the destination. compose()
|
||||
// normalises a local iterator sitting on the segment end, the same way
|
||||
// segmented_copy_dst_dispatch relies on.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || first2 == last2)) {
|
||||
// segmented_copy_dst_dispatch relies on. fourth already answers that
|
||||
// question, and gives source exhaustion priority on such a tie.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth)) {
|
||||
return result_t(first1, first2, dst_traits::compose(dst_seg, dst_local));
|
||||
}
|
||||
// dst segment full and both sources still live; advance to the next.
|
||||
|
||||
@@ -49,52 +49,61 @@ struct mismatch_equal
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Bounded iter2 helper: compares source [first1, last1) against
|
||||
// [first2, iter2_last), stopping when source, iter2, or a mismatch
|
||||
// [first2, last2), stopping when source, iter2, or a mismatch
|
||||
// is encountered.
|
||||
// Returns segduo<SrcIter, Iter2> with the final positions of both iterators.
|
||||
// Returns segtrio<SrcIter, Iter2, bool> with the final positions of both
|
||||
// iterators and the reason the walk stopped.
|
||||
// Recursively walks iter2 segments when iter2 is segmented.
|
||||
//
|
||||
// The caller can derive whether a mismatch was found:
|
||||
// if first1 != last1 && first2 != iter2_last, a mismatch was found.
|
||||
//
|
||||
// When iter2_last is unreachable_sentinel_t the segment-boundary check
|
||||
// is optimised away, giving the same code as an unbounded loop.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
template <class SrcIter, class Sent, class Iter2, class Iter2Sent, class BinaryPred, class Iter2Tag, class SrcCat>
|
||||
BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c<!Iter2Tag::value, segduo<SrcIter, Iter2> >::type
|
||||
typename algo_enable_if_c<!Iter2Tag::value, segtrio<SrcIter, Iter2, bool> >::type
|
||||
segmented_mismatch_iter2_bounded
|
||||
(SrcIter first1, Sent last1, Iter2 first2, Iter2Sent iter2_last, BinaryPred pred, Iter2Tag, SrcCat)
|
||||
(SrcIter first1, Sent last1, Iter2 first2, Iter2Sent last2, BinaryPred pred, Iter2Tag, SrcCat)
|
||||
{
|
||||
typedef segtrio<SrcIter, Iter2, bool> result_t;
|
||||
|
||||
BOOST_CONTAINER_SEGMENTED_UNROLL(4)
|
||||
for(; first1 != last1; ++first1) {
|
||||
if(first2 == iter2_last || !pred(*first1, *first2))
|
||||
break;
|
||||
if(first2 == last2)
|
||||
return result_t(first1, first2, false);
|
||||
if(!pred(*first1, *first2))
|
||||
return result_t(first1, first2, true);
|
||||
++first2;
|
||||
}
|
||||
return segduo<SrcIter, Iter2>(first1, first2);
|
||||
return result_t(first1, first2, true);
|
||||
}
|
||||
|
||||
template <class RASrcIter, class RAIter2, class BinaryPred>
|
||||
BOOST_CONTAINER_FORCEINLINE
|
||||
typename iterator_enable_if_tag
|
||||
<RAIter2, std::random_access_iterator_tag, segduo<RASrcIter, RAIter2> >::type
|
||||
<RAIter2, std::random_access_iterator_tag, segtrio<RASrcIter, RAIter2, bool> >::type
|
||||
segmented_mismatch_iter2_bounded
|
||||
(RASrcIter first1, RASrcIter last1, RAIter2 first2, RAIter2 iter2_last, BinaryPred pred,
|
||||
const non_segmented_iterator_tag &, const std::random_access_iterator_tag &src_tag)
|
||||
(RASrcIter first1, RASrcIter last1, RAIter2 first2, RAIter2 last2, BinaryPred pred,
|
||||
const non_segmented_iterator_tag &, const std::random_access_iterator_tag &)
|
||||
{
|
||||
typedef segtrio<RASrcIter, RAIter2, bool> result_t;
|
||||
typedef typename iterator_traits<RASrcIter>::difference_type difference_type;
|
||||
const difference_type src_n = last1 - first1;
|
||||
const difference_type iter2_n = difference_type(iter2_last - first2);
|
||||
const difference_type n = src_n < iter2_n ? src_n : iter2_n;
|
||||
return (segmented_mismatch_iter2_bounded)(first1, first1 + n, first2, unreachable_sentinel_t(),
|
||||
pred, non_segmented_iterator_tag(), src_tag);
|
||||
const difference_type iter2_n = difference_type(last2 - first2);
|
||||
difference_type n = src_n < iter2_n ? src_n : iter2_n;
|
||||
|
||||
BOOST_CONTAINER_SEGMENTED_UNROLL(4)
|
||||
while(n) {
|
||||
--n;
|
||||
if(!pred(*first1, *first2))
|
||||
return result_t(first1, first2, true);
|
||||
++first1;
|
||||
++first2;
|
||||
}
|
||||
|
||||
return result_t(first1, first2, first1 == last1);
|
||||
}
|
||||
|
||||
template <class SrcIter, class Sent, class SegIter2, class BinaryPred, class SrcCat>
|
||||
segduo<SrcIter, SegIter2> segmented_mismatch_iter2_bounded
|
||||
(SrcIter first1, Sent last1, SegIter2 iter2_first, SegIter2 iter2_last, BinaryPred pred,
|
||||
segtrio<SrcIter, SegIter2, bool> segmented_mismatch_iter2_bounded
|
||||
(SrcIter first1, Sent last1, SegIter2 first2, SegIter2 last2, BinaryPred pred,
|
||||
segmented_iterator_tag, SrcCat)
|
||||
{
|
||||
typedef segmented_iterator_traits<SegIter2> iter2_traits;
|
||||
@@ -102,35 +111,37 @@ segduo<SrcIter, SegIter2> segmented_mismatch_iter2_bounded
|
||||
typedef typename iter2_traits::segment_iterator iter2_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<iter2_local_iterator>::is_segmented_iterator iter2_is_local_seg_t;
|
||||
|
||||
iter2_segment_iterator sfirst = iter2_traits::segment(iter2_first);
|
||||
const iter2_segment_iterator slast = iter2_traits::segment(iter2_last);
|
||||
typedef segtrio<SrcIter, SegIter2, bool> result_t;
|
||||
typedef segtrio<SrcIter, iter2_local_iterator, bool> local_result_t;
|
||||
|
||||
iter2_local_iterator lb2 = iter2_traits::local(iter2_first);
|
||||
iter2_segment_iterator sfirst = iter2_traits::segment(first2);
|
||||
const iter2_segment_iterator slast = iter2_traits::segment(last2);
|
||||
|
||||
iter2_local_iterator lb2 = iter2_traits::local(first2);
|
||||
|
||||
if(BOOST_CONTAINER_SEG_LIKELY(sfirst != slast)) {
|
||||
{
|
||||
const segduo<SrcIter, iter2_local_iterator> r = (segmented_mismatch_iter2_bounded)
|
||||
const local_result_t r = (segmented_mismatch_iter2_bounded)
|
||||
(first1, last1, lb2, iter2_traits::end(sfirst), pred, iter2_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
const iter2_local_iterator loc2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || loc2 != iter2_traits::end(sfirst)))
|
||||
return segduo<SrcIter, SegIter2>(first1, iter2_traits::compose(sfirst, loc2));
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.third))
|
||||
return result_t(first1, iter2_traits::compose(sfirst, r.second), true);
|
||||
}
|
||||
|
||||
for(++sfirst; sfirst != slast; ++sfirst) {
|
||||
const segduo<SrcIter, iter2_local_iterator> r = (segmented_mismatch_iter2_bounded)
|
||||
(first1, last1, iter2_traits::begin(sfirst), iter2_traits::end(sfirst), pred, iter2_is_local_seg_t(), SrcCat());
|
||||
const local_result_t r = (segmented_mismatch_iter2_bounded)
|
||||
(first1, last1, iter2_traits::begin(sfirst), iter2_traits::end(sfirst), pred
|
||||
, iter2_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
const iter2_local_iterator loc2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || loc2 != iter2_traits::end(sfirst)))
|
||||
return segduo<SrcIter, SegIter2>(first1, iter2_traits::compose(sfirst, loc2));
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.third))
|
||||
return result_t(first1, iter2_traits::compose(sfirst, r.second), true);
|
||||
}
|
||||
|
||||
lb2 = iter2_traits::begin(slast);
|
||||
}
|
||||
const segduo<SrcIter, iter2_local_iterator> r = (segmented_mismatch_iter2_bounded)
|
||||
(first1, last1, lb2, iter2_traits::local(iter2_last), pred, iter2_is_local_seg_t(), SrcCat());
|
||||
return segduo<SrcIter, SegIter2>(r.first, iter2_traits::compose(sfirst, r.second));
|
||||
const local_result_t r = (segmented_mismatch_iter2_bounded)
|
||||
(first1, last1, lb2, iter2_traits::local(last2), pred, iter2_is_local_seg_t(), SrcCat());
|
||||
return result_t(r.first, iter2_traits::compose(sfirst, r.second), r.third);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -155,42 +166,43 @@ segduo<SrcIter, SegIter2> segmented_mismatch_iter2_dispatch
|
||||
iter2_segment_iterator seg2 = iter2_traits::segment(first2);
|
||||
iter2_local_iterator loc2 = iter2_traits::local(first2);
|
||||
|
||||
while(first1 != last1) {
|
||||
iter2_local_iterator end2 = iter2_traits::end(seg2);
|
||||
segduo<SrcIter, iter2_local_iterator> r = (segmented_mismatch_iter2_bounded)
|
||||
(first1, last1, loc2, end2, pred, iter2_is_local_seg_t(), Cat());
|
||||
for(;;) {
|
||||
const segtrio<SrcIter, iter2_local_iterator, bool> r = (segmented_mismatch_iter2_bounded)
|
||||
(first1, last1, loc2, iter2_traits::end(seg2), pred, iter2_is_local_seg_t(), Cat());
|
||||
first1 = r.first;
|
||||
loc2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 != last1 && loc2 != end2))
|
||||
return segduo<SrcIter, SegIter2>(first1, iter2_traits::compose(seg2, loc2));
|
||||
if(BOOST_CONTAINER_SEG_LIKELY(first1 != last1)) {
|
||||
++seg2;
|
||||
loc2 = iter2_traits::begin(seg2);
|
||||
}
|
||||
loc2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.third))
|
||||
break;
|
||||
++seg2;
|
||||
loc2 = iter2_traits::begin(seg2);
|
||||
}
|
||||
return segduo<SrcIter, SegIter2>(first1, iter2_traits::compose(seg2, loc2));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Shim: segmented_mismatch_iter2_bounded overload for the combination
|
||||
// "segmented iter2 + unreachable_sentinel_t as iter2_last". The primary
|
||||
// segmented-iter2 overload (above) requires iter2_first and iter2_last to
|
||||
// "segmented iter2 + unreachable_sentinel_t as last2". The primary
|
||||
// segmented-iter2 overload (above) requires first2 and last2 to
|
||||
// share the same type, which prevents passing unreachable_sentinel_t
|
||||
// directly. This shim forwards to segmented_mismatch_iter2_dispatch, which
|
||||
// walks iter2 segments without needing a global iter2_last.
|
||||
// walks iter2 segments without needing a global last2.
|
||||
//
|
||||
// With this shim in place, segmented_mismatch_bounded_dispatch can be
|
||||
// reused as the single implementation for the unbounded case, simply by
|
||||
// passing unreachable_sentinel_t as last2.
|
||||
//
|
||||
// third is always true: an unbounded iter2 can never be the reason the walk
|
||||
// stopped, so a caller of this overload never has a segment to advance to.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
template <class SrcIter, class Sent, class SegIter2, class BinaryPred, class SrcCat>
|
||||
BOOST_CONTAINER_FORCEINLINE segduo<SrcIter, SegIter2>
|
||||
BOOST_CONTAINER_FORCEINLINE segtrio<SrcIter, SegIter2, bool>
|
||||
segmented_mismatch_iter2_bounded
|
||||
(SrcIter first1, Sent last1, SegIter2 iter2_first, unreachable_sentinel_t,
|
||||
(SrcIter first1, Sent last1, SegIter2 first2, unreachable_sentinel_t,
|
||||
BinaryPred pred, segmented_iterator_tag, SrcCat)
|
||||
{
|
||||
return (segmented_mismatch_iter2_dispatch)
|
||||
(first1, last1, iter2_first, pred, segmented_iterator_tag(), SrcCat());
|
||||
const segduo<SrcIter, SegIter2> r = (segmented_mismatch_iter2_dispatch)
|
||||
(first1, last1, first2, pred, segmented_iterator_tag(), SrcCat());
|
||||
return segtrio<SrcIter, SegIter2, bool>(r.first, r.second, true);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -201,7 +213,9 @@ segmented_mismatch_iter2_bounded
|
||||
//
|
||||
// - The non-segmented-source leaf forwards to segmented_mismatch_iter2_bounded,
|
||||
// which already recurses on iter2 segmentation and has the random-access
|
||||
// unrolled fast path.
|
||||
// unrolled fast path. Its stop flag is dropped here: it groups a mismatch
|
||||
// with source exhaustion, whereas the segmented-source walk below needs a
|
||||
// mismatch grouped with iter2 exhaustion instead.
|
||||
// - The segmented-source overload mirrors the classic segmented walk but
|
||||
// threads last2 through the recursion and exits early when iter2 is
|
||||
// exhausted.
|
||||
@@ -219,9 +233,10 @@ segmented_mismatch_bounded_dispatch
|
||||
(SrcIter first1, Sent last1, InpIter2 first2, Sent2 last2, BinaryPred pred, Tag, Cat)
|
||||
{
|
||||
typedef segmented_iterator_traits<InpIter2> iter2_traits;
|
||||
return (segmented_mismatch_iter2_bounded)
|
||||
const segtrio<SrcIter, InpIter2, bool> r = (segmented_mismatch_iter2_bounded)
|
||||
(first1, last1, first2, last2, pred,
|
||||
typename iter2_traits::is_segmented_iterator(), Cat());
|
||||
return segduo<SrcIter, InpIter2>(r.first, r.second);
|
||||
}
|
||||
|
||||
template <class SegIter, class InpIter2, class Sent2, class BinaryPred, class Cat>
|
||||
|
||||
@@ -124,26 +124,32 @@ partition_copy_leaf
|
||||
(SrcIter first, Sent last, TIter t_first, TSent t_last, FIter f_first, FSent f_last,
|
||||
Pred pred, const SrcCat &)
|
||||
{
|
||||
bool true_output_full = false;
|
||||
// fourth says whether [f_first, f_last) filled, the only stop a caller
|
||||
// walking out_false segments can resume from. Every exit sets it to a
|
||||
// constant, so a caller that would otherwise also re-test source exhaustion
|
||||
// just tests the flag. The source test (the loop condition) precedes the
|
||||
// output tests, so a source and an output running out on the same element
|
||||
// counts as source exhaustion, which is what those callers require.
|
||||
bool false_output_full = false;
|
||||
BOOST_CONTAINER_SEGMENTED_UNROLL(4)
|
||||
for(; first != last; ++first) {
|
||||
if(pred(*first)) {
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(t_first == t_last)) {
|
||||
true_output_full = true;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(t_first == t_last))
|
||||
break;
|
||||
}
|
||||
*t_first = *first;
|
||||
++t_first;
|
||||
}
|
||||
else {
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(f_first == f_last))
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(f_first == f_last)) {
|
||||
false_output_full = true;
|
||||
break;
|
||||
}
|
||||
*f_first = *first;
|
||||
++f_first;
|
||||
}
|
||||
}
|
||||
return segquartet<SrcIter, TIter, FIter, bool>
|
||||
(first, t_first, f_first, true_output_full);
|
||||
(first, t_first, f_first, false_output_full);
|
||||
}
|
||||
|
||||
// Random-access-source fast path (needs random-access outputs too). Process
|
||||
@@ -203,8 +209,9 @@ partition_copy_false_bounded
|
||||
// out_false local segmented (nested): walk the bounded [f_first, f_last) span,
|
||||
// recursing on the local structure. Follows the classic same-segment /
|
||||
// initial-middle-final segmented walk, threading the source and the out_true
|
||||
// partner. The recursive result records whether out_true blocked; otherwise a
|
||||
// non-drained return means this out_false sub-segment filled.
|
||||
// partner. The recursive result says whether the sub-segment filled, the only
|
||||
// stop that can be resumed by moving on to the next one; anything else (source
|
||||
// drained, out_true blocked) has to be handed back to the outer stage.
|
||||
template <class SrcIter, class Sent, class TIter, class TSent, class SegFIter,
|
||||
class Pred, class Cat>
|
||||
segquartet<SrcIter, TIter, SegFIter, bool>
|
||||
@@ -228,9 +235,9 @@ partition_copy_false_bounded
|
||||
(first, last, t_first, t_last, fb, ftr::end(fsfirst), pred, floc_seg_t(), cat);
|
||||
first = r.first;
|
||||
t_first = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first == last || r.fourth))
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(!r.fourth))
|
||||
return segquartet<SrcIter, TIter, SegFIter, bool>
|
||||
(first, t_first, ftr::compose(fsfirst, r.third), r.fourth);
|
||||
(first, t_first, ftr::compose(fsfirst, r.third), false);
|
||||
}
|
||||
|
||||
for(++fsfirst; fsfirst != fslast; ++fsfirst) {
|
||||
@@ -238,9 +245,9 @@ partition_copy_false_bounded
|
||||
(first, last, t_first, t_last, ftr::begin(fsfirst), ftr::end(fsfirst), pred, floc_seg_t(), cat);
|
||||
first = r.first;
|
||||
t_first = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first == last || r.fourth))
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(!r.fourth))
|
||||
return segquartet<SrcIter, TIter, SegFIter, bool>
|
||||
(first, t_first, ftr::compose(fsfirst, r.third), r.fourth);
|
||||
(first, t_first, ftr::compose(fsfirst, r.third), false);
|
||||
}
|
||||
|
||||
fb = ftr::begin(fslast);
|
||||
@@ -268,8 +275,9 @@ partition_copy_false_dispatch
|
||||
|
||||
// out_false segmented (driver): refill over out_false segments without an
|
||||
// overall end, bounding each segment with its real end() and delegating to the
|
||||
// worker. Stops when the source drains or the out_true partner fills;
|
||||
// otherwise the current out_false segment filled, so advance to the next one.
|
||||
// worker. The current out_false segment filling is the only stop that can be
|
||||
// resumed here, so the flag alone decides whether to advance to the next
|
||||
// segment or hand control back to the out_true stage.
|
||||
template <class SrcIter, class Sent, class TIter, class TSent, class SegFIter,
|
||||
class Pred, class Cat>
|
||||
segtrio<SrcIter, TIter, SegFIter>
|
||||
@@ -289,7 +297,7 @@ partition_copy_false_dispatch
|
||||
(first, last, t_first, t_last, f_lo, ftr::end(fs), pred, floc_seg_t(), cat);
|
||||
first = r.first;
|
||||
t_first = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first == last || r.fourth))
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(!r.fourth))
|
||||
return segtrio<SrcIter, TIter, SegFIter>(first, t_first, ftr::compose(fs, r.third));
|
||||
++fs;
|
||||
f_lo = ftr::begin(fs);
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace detail_algo {
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class DstIter, class DstSent,
|
||||
class Comp, class DstTag, class SrcCat>
|
||||
BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c<!DstTag::value, segtrio<Iter1, Iter2, DstIter> >::type
|
||||
typename algo_enable_if_c<!DstTag::value, segquartet<Iter1, Iter2, DstIter, bool> >::type
|
||||
set_difference_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
DstIter dst_first, DstSent dst_last, Comp comp, DstTag, SrcCat)
|
||||
@@ -60,10 +60,13 @@ set_difference_dst_bounded
|
||||
// with room left in the sources but none in the destination would leave
|
||||
// the segmented walker unable to tell a full segment from an exhausted
|
||||
// destination. With unreachable_sentinel_t the test folds away as before.
|
||||
bool src_done = true;
|
||||
while(first1 != last1 && first2 != last2) {
|
||||
if (comp(*first1, *first2)) {
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(dst_first == dst_last))
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(dst_first == dst_last)) {
|
||||
src_done = false;
|
||||
break;
|
||||
}
|
||||
*dst_first = *first1;
|
||||
++first1;
|
||||
++dst_first;
|
||||
@@ -74,7 +77,7 @@ set_difference_dst_bounded
|
||||
++first2;
|
||||
}
|
||||
}
|
||||
return segtrio<Iter1, Iter2, DstIter>(first1, first2, dst_first);
|
||||
return segquartet<Iter1, Iter2, DstIter, bool>(first1, first2, dst_first, src_done);
|
||||
}
|
||||
|
||||
template <std::size_t BlockSize, class RAIter1, class RAIter2, class DstIter,
|
||||
@@ -112,7 +115,7 @@ BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c
|
||||
< !DstTag::value && seg_is_ra_iterator<RAIter2>::value
|
||||
&& seg_is_ra_iterator<DstIter>::value
|
||||
, segtrio<RAIter1, RAIter2, DstIter> >::type
|
||||
, segquartet<RAIter1, RAIter2, DstIter, bool> >::type
|
||||
set_difference_dst_bounded
|
||||
(RAIter1 first1, RAIter1 last1, RAIter2 first2, RAIter2 last2,
|
||||
DstIter dst_first, DstSent dst_last, Comp comp, DstTag dst_tag,
|
||||
@@ -124,6 +127,54 @@ set_difference_dst_bounded
|
||||
(r.first, last1, r.second, last2, r.third, dst_last, comp, dst_tag, int());
|
||||
}
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
class Comp, class SrcCat>
|
||||
segquartet<Iter1, Iter2, SegDstIter, bool> set_difference_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
SegDstIter dst_first, SegDstIter dst_last, Comp comp,
|
||||
segmented_iterator_tag, SrcCat)
|
||||
{
|
||||
typedef segmented_iterator_traits<SegDstIter> dst_traits;
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> local_result_t;
|
||||
typedef segquartet<Iter1, Iter2, SegDstIter, bool> result_t;
|
||||
|
||||
dst_segment_iterator sfirst = dst_traits::segment(dst_first);
|
||||
const dst_segment_iterator slast = dst_traits::segment(dst_last);
|
||||
|
||||
dst_local_iterator db = dst_traits::local(dst_first);
|
||||
|
||||
if(BOOST_CONTAINER_SEG_LIKELY(sfirst != slast)) {
|
||||
{
|
||||
const local_result_t r = (set_difference_dst_bounded)
|
||||
( first1, last1, first2, last2, db
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
for(++sfirst; sfirst != slast; ++sfirst) {
|
||||
const local_result_t r = (set_difference_dst_bounded)
|
||||
( first1, last1, first2, last2, dst_traits::begin(sfirst)
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
db = dst_traits::begin(slast);
|
||||
}
|
||||
const local_result_t r = (set_difference_dst_bounded)
|
||||
( first1, last1, first2, last2, db
|
||||
, dst_traits::local(dst_last), comp, dst_is_local_seg_t(), SrcCat());
|
||||
return result_t(r.first, r.second, dst_traits::compose(sfirst, r.third), r.fourth);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// set_difference_until_exhausts: writes the set difference into result
|
||||
// until src1 or src2 is exhausted. No residue draining.
|
||||
@@ -139,9 +190,12 @@ segtrio<Iter1, Iter2, DstIter> set_difference_until_exhausts
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2, DstIter result, Comp comp,
|
||||
const Tag &, const Cat &src1_cat)
|
||||
{
|
||||
return (set_difference_dst_bounded)
|
||||
//An unbounded destination can only stop on source exhaustion, so the leaf's
|
||||
//flag is a constant here and drops out.
|
||||
const segquartet<Iter1, Iter2, DstIter, bool> r = (set_difference_dst_bounded)
|
||||
(first1, last1, first2, last2, result, unreachable_sentinel_t(),
|
||||
comp, non_segmented_iterator_tag(), src1_cat);
|
||||
return segtrio<Iter1, Iter2, DstIter>(r.first, r.second, r.third);
|
||||
}
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
@@ -154,7 +208,7 @@ segtrio<Iter1, Iter2, SegDstIter> set_difference_until_exhausts
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segtrio<Iter1, Iter2, dst_local_iterator> bounded_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> bounded_t;
|
||||
typedef segtrio<Iter1, Iter2, SegDstIter> result_t;
|
||||
|
||||
if(BOOST_UNLIKELY(first1 == last1 || first2 == last2))
|
||||
@@ -176,8 +230,9 @@ segtrio<Iter1, Iter2, SegDstIter> set_difference_until_exhausts
|
||||
// the output ends exactly on a segment boundary both hold at once, and
|
||||
// stepping dst_seg then walks off the end of the destination. compose()
|
||||
// normalises a local iterator sitting on the segment end, the same way
|
||||
// segmented_copy_dst_dispatch relies on.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || first2 == last2)) {
|
||||
// segmented_copy_dst_dispatch relies on. fourth already answers that
|
||||
// question, and gives source exhaustion priority on such a tie.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth)) {
|
||||
return result_t(first1, first2, dst_traits::compose(dst_seg, dst_local));
|
||||
}
|
||||
// dst segment full and both sources still live; advance to the next.
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace detail_algo {
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class DstIter, class DstSent,
|
||||
class Comp, class DstTag, class SrcCat>
|
||||
BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c<!DstTag::value, segtrio<Iter1, Iter2, DstIter> >::type
|
||||
typename algo_enable_if_c<!DstTag::value, segquartet<Iter1, Iter2, DstIter, bool> >::type
|
||||
set_intersection_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
DstIter dst_first, DstSent dst_last, Comp comp, DstTag, SrcCat)
|
||||
@@ -57,16 +57,19 @@ set_intersection_dst_bounded
|
||||
// with room left in the sources but none in the destination would leave
|
||||
// the segmented walker unable to tell a full segment from an exhausted
|
||||
// destination. With unreachable_sentinel_t the test folds away as before.
|
||||
bool src_done = true;
|
||||
while(first1 != last1 && first2 != last2) {
|
||||
if (comp(*first1, *first2)) { ++first1; }
|
||||
else if (comp(*first2, *first1)) { ++first2; }
|
||||
else {
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(dst_first == dst_last))
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(dst_first == dst_last)) {
|
||||
src_done = false;
|
||||
break;
|
||||
}
|
||||
*dst_first = *first1; ++first1; ++first2; ++dst_first;
|
||||
}
|
||||
}
|
||||
return segtrio<Iter1, Iter2, DstIter>(first1, first2, dst_first);
|
||||
return segquartet<Iter1, Iter2, DstIter, bool>(first1, first2, dst_first, src_done);
|
||||
}
|
||||
|
||||
template < std::size_t BlockSize, class RAIter1, class RAIter2, class DstIter
|
||||
@@ -97,7 +100,7 @@ BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c
|
||||
< !DstTag::value && seg_is_ra_iterator<RAIter2>::value
|
||||
&& seg_is_ra_iterator<DstIter>::value
|
||||
, segtrio<RAIter1, RAIter2, DstIter> >::type
|
||||
, segquartet<RAIter1, RAIter2, DstIter, bool> >::type
|
||||
set_intersection_dst_bounded
|
||||
(RAIter1 first1, RAIter1 last1, RAIter2 first2, RAIter2 last2,
|
||||
DstIter dst_first, DstSent dst_last, Comp comp, DstTag dst_tag,
|
||||
@@ -111,6 +114,54 @@ set_intersection_dst_bounded
|
||||
|
||||
#undef BOOST_CONTAINER_SET_INTERSECTION_BLOCKS
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
class Comp, class SrcCat>
|
||||
segquartet<Iter1, Iter2, SegDstIter, bool> set_intersection_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
SegDstIter dst_first, SegDstIter dst_last, Comp comp,
|
||||
segmented_iterator_tag, SrcCat)
|
||||
{
|
||||
typedef segmented_iterator_traits<SegDstIter> dst_traits;
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> local_result_t;
|
||||
typedef segquartet<Iter1, Iter2, SegDstIter, bool> result_t;
|
||||
|
||||
dst_segment_iterator sfirst = dst_traits::segment(dst_first);
|
||||
const dst_segment_iterator slast = dst_traits::segment(dst_last);
|
||||
|
||||
dst_local_iterator db = dst_traits::local(dst_first);
|
||||
|
||||
if(BOOST_CONTAINER_SEG_LIKELY(sfirst != slast)) {
|
||||
{
|
||||
const local_result_t r = (set_intersection_dst_bounded)
|
||||
( first1, last1, first2, last2, db
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
for(++sfirst; sfirst != slast; ++sfirst) {
|
||||
const local_result_t r = (set_intersection_dst_bounded)
|
||||
( first1, last1, first2, last2, dst_traits::begin(sfirst)
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
db = dst_traits::begin(slast);
|
||||
}
|
||||
const local_result_t r = (set_intersection_dst_bounded)
|
||||
( first1, last1, first2, last2, db
|
||||
, dst_traits::local(dst_last), comp, dst_is_local_seg_t(), SrcCat());
|
||||
return result_t(r.first, r.second, dst_traits::compose(sfirst, r.third), r.fourth);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// set_intersection_until_exhausts: writes the intersection into result until
|
||||
// src1 or src2 is exhausted.
|
||||
@@ -126,9 +177,12 @@ segtrio<Iter1, Iter2, DstIter> set_intersection_until_exhausts
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2, DstIter result, Comp comp,
|
||||
const Tag &, const Cat &src1_cat)
|
||||
{
|
||||
return (set_intersection_dst_bounded)
|
||||
//An unbounded destination can only stop on source exhaustion, so the leaf's
|
||||
//flag is a constant here and drops out.
|
||||
const segquartet<Iter1, Iter2, DstIter, bool> r = (set_intersection_dst_bounded)
|
||||
(first1, last1, first2, last2, result, unreachable_sentinel_t(),
|
||||
comp, non_segmented_iterator_tag(), src1_cat);
|
||||
return segtrio<Iter1, Iter2, DstIter>(r.first, r.second, r.third);
|
||||
}
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
@@ -141,7 +195,7 @@ segtrio<Iter1, Iter2, SegDstIter> set_intersection_until_exhausts
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segtrio<Iter1, Iter2, dst_local_iterator> bounded_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> bounded_t;
|
||||
typedef segtrio<Iter1, Iter2, SegDstIter> result_t;
|
||||
|
||||
if(BOOST_UNLIKELY(first1 == last1 || first2 == last2))
|
||||
@@ -163,8 +217,9 @@ segtrio<Iter1, Iter2, SegDstIter> set_intersection_until_exhausts
|
||||
// the output ends exactly on a segment boundary both hold at once, and
|
||||
// stepping dst_seg then walks off the end of the destination. compose()
|
||||
// normalises a local iterator sitting on the segment end, the same way
|
||||
// segmented_copy_dst_dispatch relies on.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || first2 == last2)) {
|
||||
// segmented_copy_dst_dispatch relies on. fourth already answers that
|
||||
// question, and gives source exhaustion priority on such a tie.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth)) {
|
||||
return result_t(first1, first2, dst_traits::compose(dst_seg, dst_local));
|
||||
}
|
||||
// dst segment full and both sources still live; advance to the next.
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace detail_algo {
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class DstIter, class DstSent,
|
||||
class Comp, class DstTag, class SrcCat>
|
||||
BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c<!DstTag::value, segtrio<Iter1, Iter2, DstIter> >::type
|
||||
typename algo_enable_if_c<!DstTag::value, segquartet<Iter1, Iter2, DstIter, bool> >::type
|
||||
set_symmetric_difference_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
DstIter dst_first, DstSent dst_last, Comp comp, DstTag, SrcCat)
|
||||
@@ -60,18 +60,23 @@ set_symmetric_difference_dst_bounded
|
||||
// with room left in the sources but none in the destination would leave
|
||||
// the segmented walker unable to tell a full segment from an exhausted
|
||||
// destination. With unreachable_sentinel_t the test folds away as before.
|
||||
bool src_done = true;
|
||||
while(first1 != last1 && first2 != last2) {
|
||||
if (comp(*first1, *first2)) {
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(dst_first == dst_last))
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(dst_first == dst_last)) {
|
||||
src_done = false;
|
||||
break;
|
||||
}
|
||||
*dst_first = *first1;
|
||||
++first1;
|
||||
++dst_first;
|
||||
}
|
||||
else {
|
||||
if (comp(*first2, *first1)) {
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(dst_first == dst_last))
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(dst_first == dst_last)) {
|
||||
src_done = false;
|
||||
break;
|
||||
}
|
||||
*dst_first = *first2;
|
||||
++dst_first;
|
||||
}
|
||||
@@ -81,7 +86,7 @@ set_symmetric_difference_dst_bounded
|
||||
++first2;
|
||||
}
|
||||
}
|
||||
return segtrio<Iter1, Iter2, DstIter>(first1, first2, dst_first);
|
||||
return segquartet<Iter1, Iter2, DstIter, bool>(first1, first2, dst_first, src_done);
|
||||
}
|
||||
|
||||
template <std::size_t BlockSize, class RAIter1, class RAIter2, class DstIter, class Comp>
|
||||
@@ -128,7 +133,7 @@ BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c
|
||||
< !DstTag::value && seg_is_ra_iterator<RAIter2>::value
|
||||
&& seg_is_ra_iterator<DstIter>::value
|
||||
, segtrio<RAIter1, RAIter2, DstIter> >::type
|
||||
, segquartet<RAIter1, RAIter2, DstIter, bool> >::type
|
||||
set_symmetric_difference_dst_bounded
|
||||
(RAIter1 first1, RAIter1 last1, RAIter2 first2, RAIter2 last2,
|
||||
DstIter dst_first, DstIter dst_last, Comp comp, DstTag dst_tag,
|
||||
@@ -140,6 +145,54 @@ set_symmetric_difference_dst_bounded
|
||||
(r.first, last1, r.second, last2, r.third, dst_last, comp, dst_tag, int());
|
||||
}
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
class Comp, class SrcCat>
|
||||
segquartet<Iter1, Iter2, SegDstIter, bool> set_symmetric_difference_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
SegDstIter dst_first, SegDstIter dst_last, Comp comp,
|
||||
segmented_iterator_tag, SrcCat)
|
||||
{
|
||||
typedef segmented_iterator_traits<SegDstIter> dst_traits;
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> local_result_t;
|
||||
typedef segquartet<Iter1, Iter2, SegDstIter, bool> result_t;
|
||||
|
||||
dst_segment_iterator sfirst = dst_traits::segment(dst_first);
|
||||
const dst_segment_iterator slast = dst_traits::segment(dst_last);
|
||||
|
||||
dst_local_iterator db = dst_traits::local(dst_first);
|
||||
|
||||
if(BOOST_CONTAINER_SEG_LIKELY(sfirst != slast)) {
|
||||
{
|
||||
const local_result_t r = (set_symmetric_difference_dst_bounded)
|
||||
( first1, last1, first2, last2, db
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
for(++sfirst; sfirst != slast; ++sfirst) {
|
||||
const local_result_t r = (set_symmetric_difference_dst_bounded)
|
||||
( first1, last1, first2, last2, dst_traits::begin(sfirst)
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
db = dst_traits::begin(slast);
|
||||
}
|
||||
const local_result_t r = (set_symmetric_difference_dst_bounded)
|
||||
( first1, last1, first2, last2, db
|
||||
, dst_traits::local(dst_last), comp, dst_is_local_seg_t(), SrcCat());
|
||||
return result_t(r.first, r.second, dst_traits::compose(sfirst, r.third), r.fourth);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// set_symmetric_difference_until_exhausts: writes the symmetric difference
|
||||
// into result until src1 or src2 is exhausted. No residue draining.
|
||||
@@ -155,9 +208,12 @@ segtrio<Iter1, Iter2, DstIter> set_symmetric_difference_until_exhausts
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2, DstIter result, Comp comp,
|
||||
const Tag &, const Cat &src1_cat)
|
||||
{
|
||||
return (set_symmetric_difference_dst_bounded)
|
||||
//An unbounded destination can only stop on source exhaustion, so the leaf's
|
||||
//flag is a constant here and drops out.
|
||||
const segquartet<Iter1, Iter2, DstIter, bool> r = (set_symmetric_difference_dst_bounded)
|
||||
(first1, last1, first2, last2, result, unreachable_sentinel_t(),
|
||||
comp, non_segmented_iterator_tag(), src1_cat);
|
||||
return segtrio<Iter1, Iter2, DstIter>(r.first, r.second, r.third);
|
||||
}
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
@@ -170,7 +226,7 @@ segtrio<Iter1, Iter2, SegDstIter> set_symmetric_difference_until_exhausts
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segtrio<Iter1, Iter2, dst_local_iterator> bounded_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> bounded_t;
|
||||
typedef segtrio<Iter1, Iter2, SegDstIter> result_t;
|
||||
|
||||
if(BOOST_UNLIKELY(first1 == last1 || first2 == last2))
|
||||
@@ -192,8 +248,9 @@ segtrio<Iter1, Iter2, SegDstIter> set_symmetric_difference_until_exhausts
|
||||
// the output ends exactly on a segment boundary both hold at once, and
|
||||
// stepping dst_seg then walks off the end of the destination. compose()
|
||||
// normalises a local iterator sitting on the segment end, the same way
|
||||
// segmented_copy_dst_dispatch relies on.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || first2 == last2)) {
|
||||
// segmented_copy_dst_dispatch relies on. fourth already answers that
|
||||
// question, and gives source exhaustion priority on such a tie.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth)) {
|
||||
return result_t(first1, first2, dst_traits::compose(dst_seg, dst_local));
|
||||
}
|
||||
// dst segment full and both sources still live; advance to the next.
|
||||
|
||||
@@ -49,18 +49,23 @@ namespace detail_algo {
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class DstIter, class DstSent,
|
||||
class Comp, class DstTag, class SrcCat>
|
||||
BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c<!DstTag::value, segtrio<Iter1, Iter2, DstIter> >::type
|
||||
typename algo_enable_if_c<!DstTag::value, segquartet<Iter1, Iter2, DstIter, bool> >::type
|
||||
set_union_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
DstIter dst_first, DstSent dst_last, Comp comp, DstTag, SrcCat)
|
||||
{
|
||||
while(first1 != last1 && first2 != last2 && dst_first != dst_last) {
|
||||
bool src_done = true;
|
||||
while(first1 != last1 && first2 != last2) {
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(dst_first == dst_last)) {
|
||||
src_done = false;
|
||||
break;
|
||||
}
|
||||
if (comp(*first1, *first2)) { *dst_first = *first1; ++first1; }
|
||||
else if (comp(*first2, *first1)) { *dst_first = *first2; ++first2; }
|
||||
else { *dst_first = *first1; ++first1; ++first2; }
|
||||
++dst_first;
|
||||
}
|
||||
return segtrio<Iter1, Iter2, DstIter>(first1, first2, dst_first);
|
||||
return segquartet<Iter1, Iter2, DstIter, bool>(first1, first2, dst_first, src_done);
|
||||
}
|
||||
|
||||
template <std::size_t BlockSize, class RAIter1, class RAIter2, class DstIter,
|
||||
@@ -92,7 +97,7 @@ BOOST_CONTAINER_FORCEINLINE
|
||||
typename algo_enable_if_c
|
||||
< !DstTag::value && seg_is_ra_iterator<RAIter2>::value
|
||||
&& seg_is_ra_iterator<DstIter>::value
|
||||
, segtrio<RAIter1, RAIter2, DstIter> >::type
|
||||
, segquartet<RAIter1, RAIter2, DstIter, bool> >::type
|
||||
set_union_dst_bounded
|
||||
(RAIter1 first1, RAIter1 last1, RAIter2 first2, RAIter2 last2,
|
||||
DstIter dst_first, DstSent dst_last, Comp comp, DstTag dst_tag,
|
||||
@@ -104,6 +109,54 @@ set_union_dst_bounded
|
||||
(r.first, last1, r.second, last2, r.third, dst_last, comp, dst_tag, int());
|
||||
}
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
class Comp, class SrcCat>
|
||||
segquartet<Iter1, Iter2, SegDstIter, bool> set_union_dst_bounded
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2,
|
||||
SegDstIter dst_first, SegDstIter dst_last, Comp comp,
|
||||
segmented_iterator_tag, SrcCat)
|
||||
{
|
||||
typedef segmented_iterator_traits<SegDstIter> dst_traits;
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> local_result_t;
|
||||
typedef segquartet<Iter1, Iter2, SegDstIter, bool> result_t;
|
||||
|
||||
dst_segment_iterator sfirst = dst_traits::segment(dst_first);
|
||||
const dst_segment_iterator slast = dst_traits::segment(dst_last);
|
||||
|
||||
dst_local_iterator db = dst_traits::local(dst_first);
|
||||
|
||||
if(BOOST_CONTAINER_SEG_LIKELY(sfirst != slast)) {
|
||||
{
|
||||
const local_result_t r = (set_union_dst_bounded)
|
||||
( first1, last1, first2, last2, db
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
for(++sfirst; sfirst != slast; ++sfirst) {
|
||||
const local_result_t r = (set_union_dst_bounded)
|
||||
( first1, last1, first2, last2, dst_traits::begin(sfirst)
|
||||
, dst_traits::end(sfirst), comp, dst_is_local_seg_t(), SrcCat());
|
||||
first1 = r.first;
|
||||
first2 = r.second;
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth))
|
||||
return result_t(first1, first2, dst_traits::compose(sfirst, r.third), true);
|
||||
}
|
||||
|
||||
db = dst_traits::begin(slast);
|
||||
}
|
||||
const local_result_t r = (set_union_dst_bounded)
|
||||
( first1, last1, first2, last2, db
|
||||
, dst_traits::local(dst_last), comp, dst_is_local_seg_t(), SrcCat());
|
||||
return result_t(r.first, r.second, dst_traits::compose(sfirst, r.third), r.fourth);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// set_union_until_exhausts: writes the union into result until src1 or src2
|
||||
// is exhausted. No residue draining.
|
||||
@@ -119,9 +172,12 @@ segtrio<Iter1, Iter2, DstIter> set_union_until_exhausts
|
||||
(Iter1 first1, Sent1 last1, Iter2 first2, Sent2 last2, DstIter result, Comp comp,
|
||||
const Tag &, const Cat &src1_cat)
|
||||
{
|
||||
return (set_union_dst_bounded)
|
||||
//An unbounded destination can only stop on source exhaustion, so the leaf's
|
||||
//flag is a constant here and drops out.
|
||||
const segquartet<Iter1, Iter2, DstIter, bool> r = (set_union_dst_bounded)
|
||||
(first1, last1, first2, last2, result, unreachable_sentinel_t(),
|
||||
comp, non_segmented_iterator_tag(), src1_cat);
|
||||
return segtrio<Iter1, Iter2, DstIter>(r.first, r.second, r.third);
|
||||
}
|
||||
|
||||
template <class Iter1, class Sent1, class Iter2, class Sent2, class SegDstIter,
|
||||
@@ -134,7 +190,7 @@ segtrio<Iter1, Iter2, SegDstIter> set_union_until_exhausts
|
||||
typedef typename dst_traits::local_iterator dst_local_iterator;
|
||||
typedef typename dst_traits::segment_iterator dst_segment_iterator;
|
||||
typedef typename segmented_iterator_traits<dst_local_iterator>::is_segmented_iterator dst_is_local_seg_t;
|
||||
typedef segtrio<Iter1, Iter2, dst_local_iterator> bounded_t;
|
||||
typedef segquartet<Iter1, Iter2, dst_local_iterator, bool> bounded_t;
|
||||
typedef segtrio<Iter1, Iter2, SegDstIter> result_t;
|
||||
|
||||
if(BOOST_UNLIKELY(first1 == last1 || first2 == last2))
|
||||
@@ -156,8 +212,9 @@ segtrio<Iter1, Iter2, SegDstIter> set_union_until_exhausts
|
||||
// the output ends exactly on a segment boundary both hold at once, and
|
||||
// stepping dst_seg then walks off the end of the destination. compose()
|
||||
// normalises a local iterator sitting on the segment end, the same way
|
||||
// segmented_copy_dst_dispatch relies on.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(first1 == last1 || first2 == last2)) {
|
||||
// segmented_copy_dst_dispatch relies on. fourth already answers that
|
||||
// question, and gives source exhaustion priority on such a tie.
|
||||
if(BOOST_CONTAINER_SEG_UNLIKELY(r.fourth)) {
|
||||
return result_t(first1, first2, dst_traits::compose(dst_seg, dst_local));
|
||||
}
|
||||
// dst segment full and both sources still live; advance to the next.
|
||||
|
||||
Reference in New Issue
Block a user