Skip to content

Commit 562af5a

Browse files
committed
Implement segmented_equal without relying in mismatch, as the implementation is slower in some compilers.
1 parent af38102 commit 562af5a

2 files changed

Lines changed: 353 additions & 3 deletions

File tree

experimental/segmented_equal_test.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,90 @@ void test_equal_seg2()
125125
BOOST_TEST(!segmented_equal(sv2.begin(), sv2.end(), ref_bad));
126126
}
127127

128+
void test_equal_seg_to_seg()
129+
{
130+
test_detail::seg_vector<int> sv1;
131+
int a1[] = {1, 2, 3};
132+
int a2[] = {4, 5};
133+
int a3[] = {6, 7, 8, 9};
134+
sv1.add_segment_range(a1, a1 + 3);
135+
sv1.add_segment_range(a2, a2 + 2);
136+
sv1.add_segment_range(a3, a3 + 4);
137+
138+
test_detail::seg_vector<int> sv2;
139+
int b1[] = {1, 2};
140+
int b2[] = {3, 4, 5, 6};
141+
int b3[] = {7, 8, 9};
142+
sv2.add_segment_range(b1, b1 + 2);
143+
sv2.add_segment_range(b2, b2 + 4);
144+
sv2.add_segment_range(b3, b3 + 3);
145+
146+
BOOST_TEST(segmented_equal(sv1.begin(), sv1.end(), sv2.begin()));
147+
}
148+
149+
void test_equal_seg_to_seg_mismatch()
150+
{
151+
test_detail::seg_vector<int> sv1;
152+
int a1[] = {1, 2, 3};
153+
int a2[] = {4, 5};
154+
sv1.add_segment_range(a1, a1 + 3);
155+
sv1.add_segment_range(a2, a2 + 2);
156+
157+
test_detail::seg_vector<int> sv2;
158+
int b1[] = {1, 2};
159+
int b2[] = {3, 4, 99};
160+
sv2.add_segment_range(b1, b1 + 2);
161+
sv2.add_segment_range(b2, b2 + 3);
162+
163+
BOOST_TEST(!segmented_equal(sv1.begin(), sv1.end(), sv2.begin()));
164+
}
165+
166+
void test_equal_seg2_to_seg2()
167+
{
168+
test_detail::seg2_vector<int> sv1;
169+
int a1[] = {1, 2, 3};
170+
int a2[] = {4, 5};
171+
int a3[] = {6, 7, 8, 9};
172+
sv1.add_flat_segment_range(a1, a1 + 3);
173+
sv1.add_flat_segment_range(a2, a2 + 2);
174+
sv1.add_flat_segment_range(a3, a3 + 4);
175+
176+
test_detail::seg2_vector<int> sv2;
177+
int b1[] = {1, 2};
178+
int b2[] = {3, 4, 5, 6};
179+
int b3[] = {7, 8, 9};
180+
sv2.add_flat_segment_range(b1, b1 + 2);
181+
sv2.add_flat_segment_range(b2, b2 + 4);
182+
sv2.add_flat_segment_range(b3, b3 + 3);
183+
184+
BOOST_TEST(segmented_equal(sv1.begin(), sv1.end(), sv2.begin()));
185+
186+
test_detail::seg2_vector<int> sv3;
187+
int c1[] = {1, 2, 3, 4, 5, 6, 7, 8, 0};
188+
sv3.add_flat_segment_range(c1, c1 + 9);
189+
190+
BOOST_TEST(!segmented_equal(sv1.begin(), sv1.end(), sv3.begin()));
191+
}
192+
193+
void test_equal_seg_to_seg_misaligned()
194+
{
195+
test_detail::seg_vector<int> sv1;
196+
int a1[] = {10};
197+
int a2[] = {20, 30};
198+
int a3[] = {40, 50, 60};
199+
sv1.add_segment_range(a1, a1 + 1);
200+
sv1.add_segment_range(a2, a2 + 2);
201+
sv1.add_segment_range(a3, a3 + 3);
202+
203+
test_detail::seg_vector<int> sv2;
204+
int b1[] = {10, 20, 30, 40};
205+
int b2[] = {50, 60};
206+
sv2.add_segment_range(b1, b1 + 4);
207+
sv2.add_segment_range(b2, b2 + 2);
208+
209+
BOOST_TEST(segmented_equal(sv1.begin(), sv1.end(), sv2.begin()));
210+
}
211+
128212
int main()
129213
{
130214
test_equal_matching();
@@ -136,5 +220,9 @@ int main()
136220
test_equal_sentinel_segmented();
137221
test_equal_sentinel_non_segmented();
138222
test_equal_seg2();
223+
test_equal_seg_to_seg();
224+
test_equal_seg_to_seg_mismatch();
225+
test_equal_seg2_to_seg2();
226+
test_equal_seg_to_seg_misaligned();
139227
return boost::report_errors();
140228
}

include/boost/container/experimental/segmented_equal.hpp

Lines changed: 265 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,280 @@
2121
#include <boost/container/detail/config_begin.hpp>
2222
#include <boost/container/detail/workaround.hpp>
2323
#include <boost/container/experimental/segmented_iterator_traits.hpp>
24-
#include <boost/container/experimental/segmented_mismatch.hpp>
24+
#include <boost/container/detail/iterators.hpp>
2525

2626
namespace boost {
2727
namespace container {
2828

29+
template <class InpIter1, class Sent, class InpIter2, class BinaryPred>
30+
bool segmented_equal(InpIter1 first1, Sent last1, InpIter2 first2, BinaryPred pred);
31+
32+
template <class InpIter1, class Sent, class InpIter2>
33+
bool segmented_equal(InpIter1 first1, Sent last1, InpIter2 first2);
34+
35+
namespace detail_algo {
36+
37+
struct equal_pred
38+
{
39+
template <class T, class U>
40+
BOOST_CONTAINER_FORCEINLINE bool operator()(const T& a, const U& b) const { return a == b; }
41+
};
42+
43+
//////////////////////////////////////////////////////////////////////////////
44+
// Bounded iter2 helper: compares source [first1, last1) against
45+
// [iter2_first, iter2_last), stopping when source, iter2, or a mismatch
46+
// is encountered.
47+
// Advances first1 (by reference) so the caller knows how far we got.
48+
// Advances iter2_first (by reference) for the same reason.
49+
// Recursively walks iter2 segments when iter2 is segmented.
50+
//
51+
// When iter2_last is unreachable_sentinel_t the segment-boundary check
52+
// is optimised away, giving the same code as an unbounded loop.
53+
//////////////////////////////////////////////////////////////////////////////
54+
55+
#if defined(BOOST_CONTAINER_SEGMENTED_LOOP_UNROLLING)
56+
57+
template <class RASrcIter, class Iter2, class Iter2Sent, class BinaryPred>
58+
bool segmented_equal_iter2_bounded
59+
(RASrcIter &first1_out, RASrcIter last1, Iter2 &iter2_first, Iter2Sent iter2_last, BinaryPred pred,
60+
const non_segmented_iterator_tag &, const std::random_access_iterator_tag &)
61+
{
62+
typedef typename iterator_traits<RASrcIter>::difference_type difference_type;
63+
RASrcIter first1 = first1_out;
64+
Iter2 first2 = iter2_first;
65+
66+
difference_type n = last1 - first1;
67+
68+
while(n >= difference_type(4)) {
69+
if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2;
70+
if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2;
71+
if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2;
72+
if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2;
73+
n -= 4;
74+
}
75+
76+
switch(n) {
77+
case 3:
78+
if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2;
79+
BOOST_FALLTHROUGH;
80+
case 2:
81+
if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2;
82+
BOOST_FALLTHROUGH;
83+
case 1:
84+
if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2;
85+
BOOST_FALLTHROUGH;
86+
default:
87+
break;
88+
}
89+
out_path:
90+
first1_out = first1;
91+
iter2_first = first2;
92+
return (first1 == last1) || (first2 == iter2_last);
93+
}
94+
95+
#endif //BOOST_CONTAINER_SEGMENTED_LOOP_UNROLLING
96+
97+
template <class SrcIter, class Sent, class Iter2, class Iter2Sent, class BinaryPred, class Iter2Tag, class SrcCat>
98+
typename algo_enable_if_c<!Iter2Tag::value, bool>::type
99+
segmented_equal_iter2_bounded
100+
(SrcIter &first1_out, Sent last1, Iter2 &iter2_first, Iter2Sent iter2_last, BinaryPred pred, Iter2Tag, SrcCat)
101+
{
102+
SrcIter first1 = first1_out;
103+
Iter2 first2 = iter2_first;
104+
105+
for(; first1 != last1; ++first1) {
106+
if(first2 == iter2_last)
107+
goto out_path;
108+
if(!pred(*first1, *first2)) {
109+
first1_out = first1;
110+
iter2_first = first2;
111+
return false;
112+
}
113+
++first2;
114+
}
115+
out_path:
116+
first1_out = first1;
117+
iter2_first = first2;
118+
return true;
119+
}
120+
121+
template <class SrcIter, class Sent, class SegIter2, class BinaryPred, class SrcCat>
122+
bool segmented_equal_iter2_bounded
123+
(SrcIter &first1, Sent last1, SegIter2 &iter2_first_out, SegIter2 iter2_last, BinaryPred pred,
124+
segmented_iterator_tag, SrcCat)
125+
{
126+
typedef segmented_iterator_traits<SegIter2> iter2_traits;
127+
typedef typename iter2_traits::local_iterator iter2_local_iterator;
128+
typedef typename iter2_traits::segment_iterator iter2_segment_iterator;
129+
typedef typename segmented_iterator_traits<iter2_local_iterator>::is_segmented_iterator iter2_is_local_seg_t;
130+
131+
iter2_segment_iterator sfirst = iter2_traits::segment(iter2_first_out);
132+
const iter2_segment_iterator slast = iter2_traits::segment(iter2_last);
133+
134+
if(sfirst == slast) {
135+
iter2_local_iterator loc2 = iter2_traits::local(iter2_first_out);
136+
bool r = (segmented_equal_iter2_bounded)
137+
(first1, last1, loc2, iter2_traits::local(iter2_last), pred, iter2_is_local_seg_t(), SrcCat());
138+
iter2_first_out = iter2_traits::compose(sfirst, loc2);
139+
return r;
140+
}
141+
else {
142+
iter2_local_iterator loc2 = iter2_traits::local(iter2_first_out);
143+
if(!(segmented_equal_iter2_bounded)
144+
(first1, last1, loc2, iter2_traits::end(sfirst), pred, iter2_is_local_seg_t(), SrcCat())) {
145+
iter2_first_out = iter2_traits::compose(sfirst, loc2);
146+
return false;
147+
}
148+
if(first1 == last1) {
149+
iter2_first_out = iter2_traits::compose(sfirst, loc2);
150+
return true;
151+
}
152+
153+
for(++sfirst; sfirst != slast; ++sfirst) {
154+
loc2 = iter2_traits::begin(sfirst);
155+
if(!(segmented_equal_iter2_bounded)
156+
(first1, last1, loc2, iter2_traits::end(sfirst), pred, iter2_is_local_seg_t(), SrcCat())) {
157+
iter2_first_out = iter2_traits::compose(sfirst, loc2);
158+
return false;
159+
}
160+
if(first1 == last1) {
161+
iter2_first_out = iter2_traits::compose(sfirst, loc2);
162+
return true;
163+
}
164+
}
165+
166+
loc2 = iter2_traits::begin(slast);
167+
bool r = (segmented_equal_iter2_bounded)
168+
(first1, last1, loc2, iter2_traits::local(iter2_last), pred, iter2_is_local_seg_t(), SrcCat());
169+
iter2_first_out = iter2_traits::compose(sfirst, loc2);
170+
return r;
171+
}
172+
}
173+
174+
//////////////////////////////////////////////////////////////////////////////
175+
// Iter2 dispatch: routes to bounded helper.
176+
// Non-segmented iter2: single unbounded call (unreachable_sentinel_t).
177+
// Segmented iter2: loop over iter2 segments, bounded per segment.
178+
//////////////////////////////////////////////////////////////////////////////
179+
180+
template <class SrcIter, class Sent, class InpIter2, class BinaryPred, class Cat>
181+
BOOST_CONTAINER_FORCEINLINE bool segmented_equal_iter2_dispatch
182+
(SrcIter first1, Sent last1, InpIter2 &first2, BinaryPred pred,
183+
const non_segmented_iterator_tag &, Cat)
184+
{
185+
bool r = (segmented_equal_iter2_bounded)
186+
(first1, last1, first2, unreachable_sentinel_t(), pred, non_segmented_iterator_tag(), Cat());
187+
return r;
188+
}
189+
190+
template <class SrcIter, class Sent, class SegIter2, class BinaryPred, class Cat>
191+
bool segmented_equal_iter2_dispatch
192+
(SrcIter first1, Sent last1, SegIter2 &first2_out, BinaryPred pred,
193+
const segmented_iterator_tag &, Cat)
194+
{
195+
typedef segmented_iterator_traits<SegIter2> iter2_traits;
196+
typedef typename iter2_traits::local_iterator iter2_local_iterator;
197+
typedef typename iter2_traits::segment_iterator iter2_segment_iterator;
198+
typedef typename segmented_iterator_traits<iter2_local_iterator>::is_segmented_iterator iter2_is_local_seg_t;
199+
200+
if(first1 == last1)
201+
return true;
202+
203+
iter2_segment_iterator seg2 = iter2_traits::segment(first2_out);
204+
iter2_local_iterator loc2 = iter2_traits::local(first2_out);
205+
206+
while(first1 != last1) {
207+
iter2_local_iterator end2 = iter2_traits::end(seg2);
208+
if(!(segmented_equal_iter2_bounded)
209+
(first1, last1, loc2, end2, pred, iter2_is_local_seg_t(), Cat())) {
210+
first2_out = iter2_traits::compose(seg2, loc2);
211+
return false;
212+
}
213+
if(first1 != last1) {
214+
++seg2;
215+
loc2 = iter2_traits::begin(seg2);
216+
}
217+
}
218+
first2_out = iter2_traits::compose(seg2, loc2);
219+
return true;
220+
}
221+
222+
//////////////////////////////////////////////////////////////////////////////
223+
// Source dispatch: walks the source (first1) segments
224+
//////////////////////////////////////////////////////////////////////////////
225+
226+
template <class SrcIter, class Sent, class InpIter2, class BinaryPred, class Tag, class Cat>
227+
BOOST_CONTAINER_FORCEINLINE
228+
typename algo_enable_if_c
229+
< !Tag::value || is_sentinel<Sent, SrcIter>::value
230+
, bool
231+
>::type
232+
segmented_equal_dispatch(SrcIter first1, Sent last1, InpIter2 &first2_out, BinaryPred pred, Tag, Cat)
233+
{
234+
#if !defined(BOOST_CONTAINER_DISABLE_SEGMENTED_OUTPUT)
235+
typedef segmented_iterator_traits<InpIter2> iter2_traits;
236+
return (segmented_equal_iter2_dispatch)
237+
(first1, last1, first2_out, pred, typename iter2_traits::is_segmented_iterator(), Cat());
238+
#else
239+
return (segmented_equal_iter2_dispatch)
240+
(first1, last1, first2_out, pred, non_segmented_iterator_tag(), Cat());
241+
#endif
242+
}
243+
244+
template <class SegIter, class InpIter2, class BinaryPred, class Cat>
245+
bool segmented_equal_dispatch
246+
(SegIter first1, SegIter last1, InpIter2 &first2, BinaryPred pred, segmented_iterator_tag, Cat)
247+
{
248+
typedef segmented_iterator_traits<SegIter> traits;
249+
typedef typename traits::local_iterator local_iterator;
250+
typedef typename traits::segment_iterator segment_iterator;
251+
typedef typename segmented_iterator_traits<local_iterator>::is_segmented_iterator is_local_seg_t;
252+
typedef typename iterator_traits<local_iterator>::iterator_category local_cat_t;
253+
254+
segment_iterator sfirst = traits::segment(first1);
255+
segment_iterator const slast = traits::segment(last1);
256+
257+
if(sfirst == slast) {
258+
return (segmented_equal_dispatch)
259+
(traits::local(first1), traits::local(last1), first2, pred, is_local_seg_t(), local_cat_t());
260+
}
261+
else {
262+
if(!(segmented_equal_dispatch)
263+
(traits::local(first1), traits::end(sfirst), first2, pred, is_local_seg_t(), local_cat_t()))
264+
return false;
265+
266+
for(++sfirst; sfirst != slast; ++sfirst) {
267+
if(!(segmented_equal_dispatch)
268+
(traits::begin(sfirst), traits::end(sfirst), first2, pred, is_local_seg_t(), local_cat_t()))
269+
return false;
270+
}
271+
272+
return (segmented_equal_dispatch)
273+
(traits::begin(slast), traits::local(last1), first2, pred, is_local_seg_t(), local_cat_t());
274+
}
275+
}
276+
277+
} // namespace detail_algo
278+
279+
//! Returns \c true if elements in [first1, last1) are equal to the
280+
//! range starting at \c first2 according to \c pred.
281+
//! Exploits segmentation on both ranges.
282+
template <class InpIter1, class Sent, class InpIter2, class BinaryPred>
283+
BOOST_CONTAINER_FORCEINLINE
284+
bool segmented_equal(InpIter1 first1, Sent last1, InpIter2 first2, BinaryPred pred)
285+
{
286+
typedef segmented_iterator_traits<InpIter1> traits;
287+
return detail_algo::segmented_equal_dispatch
288+
(first1, last1, first2, pred, typename traits::is_segmented_iterator(), typename iterator_traits<InpIter1>::iterator_category());
289+
}
290+
29291
//! Returns \c true if elements in [first1, last1) are equal to the
30-
//! range starting at \c first2. Exploits segmentation on the first range.
292+
//! range starting at \c first2. Exploits segmentation on both ranges.
31293
template <class InpIter1, class Sent, class InpIter2>
32294
BOOST_CONTAINER_FORCEINLINE
33295
bool segmented_equal(InpIter1 first1, Sent last1, InpIter2 first2)
34296
{
35-
return (segmented_mismatch)(first1, last1, first2).first == last1;
297+
return boost::container::segmented_equal(first1, last1, first2, detail_algo::equal_pred());
36298
}
37299

38300
} // namespace container

0 commit comments

Comments
 (0)