Add recursive output handling and improved leave condition return.

This commit is contained in:
Ion Gaztañaga
2026-07-30 01:43:48 +02:00
parent 3c8ea73b3c
commit 2c4d8043ae
13 changed files with 855 additions and 462 deletions
+77 -42
View File
@@ -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)))
+76 -41
View File
@@ -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)))
+68 -94
View File
@@ -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.