diff --git a/experimental/segmented_equal_test.cpp b/experimental/segmented_equal_test.cpp index 36d1a3b..02d792a 100644 --- a/experimental/segmented_equal_test.cpp +++ b/experimental/segmented_equal_test.cpp @@ -125,6 +125,90 @@ void test_equal_seg2() BOOST_TEST(!segmented_equal(sv2.begin(), sv2.end(), ref_bad)); } +void test_equal_seg_to_seg() +{ + test_detail::seg_vector sv1; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + int a3[] = {6, 7, 8, 9}; + sv1.add_segment_range(a1, a1 + 3); + sv1.add_segment_range(a2, a2 + 2); + sv1.add_segment_range(a3, a3 + 4); + + test_detail::seg_vector sv2; + int b1[] = {1, 2}; + int b2[] = {3, 4, 5, 6}; + int b3[] = {7, 8, 9}; + sv2.add_segment_range(b1, b1 + 2); + sv2.add_segment_range(b2, b2 + 4); + sv2.add_segment_range(b3, b3 + 3); + + BOOST_TEST(segmented_equal(sv1.begin(), sv1.end(), sv2.begin())); +} + +void test_equal_seg_to_seg_mismatch() +{ + test_detail::seg_vector sv1; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + sv1.add_segment_range(a1, a1 + 3); + sv1.add_segment_range(a2, a2 + 2); + + test_detail::seg_vector sv2; + int b1[] = {1, 2}; + int b2[] = {3, 4, 99}; + sv2.add_segment_range(b1, b1 + 2); + sv2.add_segment_range(b2, b2 + 3); + + BOOST_TEST(!segmented_equal(sv1.begin(), sv1.end(), sv2.begin())); +} + +void test_equal_seg2_to_seg2() +{ + test_detail::seg2_vector sv1; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + int a3[] = {6, 7, 8, 9}; + sv1.add_flat_segment_range(a1, a1 + 3); + sv1.add_flat_segment_range(a2, a2 + 2); + sv1.add_flat_segment_range(a3, a3 + 4); + + test_detail::seg2_vector sv2; + int b1[] = {1, 2}; + int b2[] = {3, 4, 5, 6}; + int b3[] = {7, 8, 9}; + sv2.add_flat_segment_range(b1, b1 + 2); + sv2.add_flat_segment_range(b2, b2 + 4); + sv2.add_flat_segment_range(b3, b3 + 3); + + BOOST_TEST(segmented_equal(sv1.begin(), sv1.end(), sv2.begin())); + + test_detail::seg2_vector sv3; + int c1[] = {1, 2, 3, 4, 5, 6, 7, 8, 0}; + sv3.add_flat_segment_range(c1, c1 + 9); + + BOOST_TEST(!segmented_equal(sv1.begin(), sv1.end(), sv3.begin())); +} + +void test_equal_seg_to_seg_misaligned() +{ + test_detail::seg_vector sv1; + int a1[] = {10}; + int a2[] = {20, 30}; + int a3[] = {40, 50, 60}; + sv1.add_segment_range(a1, a1 + 1); + sv1.add_segment_range(a2, a2 + 2); + sv1.add_segment_range(a3, a3 + 3); + + test_detail::seg_vector sv2; + int b1[] = {10, 20, 30, 40}; + int b2[] = {50, 60}; + sv2.add_segment_range(b1, b1 + 4); + sv2.add_segment_range(b2, b2 + 2); + + BOOST_TEST(segmented_equal(sv1.begin(), sv1.end(), sv2.begin())); +} + int main() { test_equal_matching(); @@ -136,5 +220,9 @@ int main() test_equal_sentinel_segmented(); test_equal_sentinel_non_segmented(); test_equal_seg2(); + test_equal_seg_to_seg(); + test_equal_seg_to_seg_mismatch(); + test_equal_seg2_to_seg2(); + test_equal_seg_to_seg_misaligned(); return boost::report_errors(); } diff --git a/include/boost/container/experimental/segmented_equal.hpp b/include/boost/container/experimental/segmented_equal.hpp index 02dd541..0e3f091 100644 --- a/include/boost/container/experimental/segmented_equal.hpp +++ b/include/boost/container/experimental/segmented_equal.hpp @@ -21,18 +21,280 @@ #include #include #include -#include +#include namespace boost { namespace container { +template +bool segmented_equal(InpIter1 first1, Sent last1, InpIter2 first2, BinaryPred pred); + +template +bool segmented_equal(InpIter1 first1, Sent last1, InpIter2 first2); + +namespace detail_algo { + +struct equal_pred +{ + template + BOOST_CONTAINER_FORCEINLINE bool operator()(const T& a, const U& b) const { return a == b; } +}; + +////////////////////////////////////////////////////////////////////////////// +// Bounded iter2 helper: compares source [first1, last1) against +// [iter2_first, iter2_last), stopping when source, iter2, or a mismatch +// is encountered. +// Advances first1 (by reference) so the caller knows how far we got. +// Advances iter2_first (by reference) for the same reason. +// 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. +////////////////////////////////////////////////////////////////////////////// + +#if defined(BOOST_CONTAINER_SEGMENTED_LOOP_UNROLLING) + +template +bool segmented_equal_iter2_bounded + (RASrcIter &first1_out, RASrcIter last1, Iter2 &iter2_first, Iter2Sent iter2_last, BinaryPred pred, + const non_segmented_iterator_tag &, const std::random_access_iterator_tag &) +{ + typedef typename iterator_traits::difference_type difference_type; + RASrcIter first1 = first1_out; + Iter2 first2 = iter2_first; + + difference_type n = last1 - first1; + + while(n >= difference_type(4)) { + if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2; + if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2; + if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2; + if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2; + n -= 4; + } + + switch(n) { + case 3: + if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2; + BOOST_FALLTHROUGH; + case 2: + if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2; + BOOST_FALLTHROUGH; + case 1: + if(first2 == iter2_last) goto out_path; if(!pred(*first1, *first2)) goto out_path; ++first1; ++first2; + BOOST_FALLTHROUGH; + default: + break; + } + out_path: + first1_out = first1; + iter2_first = first2; + return (first1 == last1) || (first2 == iter2_last); +} + +#endif //BOOST_CONTAINER_SEGMENTED_LOOP_UNROLLING + +template +typename algo_enable_if_c::type +segmented_equal_iter2_bounded + (SrcIter &first1_out, Sent last1, Iter2 &iter2_first, Iter2Sent iter2_last, BinaryPred pred, Iter2Tag, SrcCat) +{ + SrcIter first1 = first1_out; + Iter2 first2 = iter2_first; + + for(; first1 != last1; ++first1) { + if(first2 == iter2_last) + goto out_path; + if(!pred(*first1, *first2)) { + first1_out = first1; + iter2_first = first2; + return false; + } + ++first2; + } + out_path: + first1_out = first1; + iter2_first = first2; + return true; +} + +template +bool segmented_equal_iter2_bounded + (SrcIter &first1, Sent last1, SegIter2 &iter2_first_out, SegIter2 iter2_last, BinaryPred pred, + segmented_iterator_tag, SrcCat) +{ + typedef segmented_iterator_traits iter2_traits; + typedef typename iter2_traits::local_iterator iter2_local_iterator; + typedef typename iter2_traits::segment_iterator iter2_segment_iterator; + typedef typename segmented_iterator_traits::is_segmented_iterator iter2_is_local_seg_t; + + iter2_segment_iterator sfirst = iter2_traits::segment(iter2_first_out); + const iter2_segment_iterator slast = iter2_traits::segment(iter2_last); + + if(sfirst == slast) { + iter2_local_iterator loc2 = iter2_traits::local(iter2_first_out); + bool r = (segmented_equal_iter2_bounded) + (first1, last1, loc2, iter2_traits::local(iter2_last), pred, iter2_is_local_seg_t(), SrcCat()); + iter2_first_out = iter2_traits::compose(sfirst, loc2); + return r; + } + else { + iter2_local_iterator loc2 = iter2_traits::local(iter2_first_out); + if(!(segmented_equal_iter2_bounded) + (first1, last1, loc2, iter2_traits::end(sfirst), pred, iter2_is_local_seg_t(), SrcCat())) { + iter2_first_out = iter2_traits::compose(sfirst, loc2); + return false; + } + if(first1 == last1) { + iter2_first_out = iter2_traits::compose(sfirst, loc2); + return true; + } + + for(++sfirst; sfirst != slast; ++sfirst) { + loc2 = iter2_traits::begin(sfirst); + if(!(segmented_equal_iter2_bounded) + (first1, last1, loc2, iter2_traits::end(sfirst), pred, iter2_is_local_seg_t(), SrcCat())) { + iter2_first_out = iter2_traits::compose(sfirst, loc2); + return false; + } + if(first1 == last1) { + iter2_first_out = iter2_traits::compose(sfirst, loc2); + return true; + } + } + + loc2 = iter2_traits::begin(slast); + bool r = (segmented_equal_iter2_bounded) + (first1, last1, loc2, iter2_traits::local(iter2_last), pred, iter2_is_local_seg_t(), SrcCat()); + iter2_first_out = iter2_traits::compose(sfirst, loc2); + return r; + } +} + +////////////////////////////////////////////////////////////////////////////// +// Iter2 dispatch: routes to bounded helper. +// Non-segmented iter2: single unbounded call (unreachable_sentinel_t). +// Segmented iter2: loop over iter2 segments, bounded per segment. +////////////////////////////////////////////////////////////////////////////// + +template +BOOST_CONTAINER_FORCEINLINE bool segmented_equal_iter2_dispatch + (SrcIter first1, Sent last1, InpIter2 &first2, BinaryPred pred, + const non_segmented_iterator_tag &, Cat) +{ + bool r = (segmented_equal_iter2_bounded) + (first1, last1, first2, unreachable_sentinel_t(), pred, non_segmented_iterator_tag(), Cat()); + return r; +} + +template +bool segmented_equal_iter2_dispatch + (SrcIter first1, Sent last1, SegIter2 &first2_out, BinaryPred pred, + const segmented_iterator_tag &, Cat) +{ + typedef segmented_iterator_traits iter2_traits; + typedef typename iter2_traits::local_iterator iter2_local_iterator; + typedef typename iter2_traits::segment_iterator iter2_segment_iterator; + typedef typename segmented_iterator_traits::is_segmented_iterator iter2_is_local_seg_t; + + if(first1 == last1) + return true; + + iter2_segment_iterator seg2 = iter2_traits::segment(first2_out); + iter2_local_iterator loc2 = iter2_traits::local(first2_out); + + while(first1 != last1) { + iter2_local_iterator end2 = iter2_traits::end(seg2); + if(!(segmented_equal_iter2_bounded) + (first1, last1, loc2, end2, pred, iter2_is_local_seg_t(), Cat())) { + first2_out = iter2_traits::compose(seg2, loc2); + return false; + } + if(first1 != last1) { + ++seg2; + loc2 = iter2_traits::begin(seg2); + } + } + first2_out = iter2_traits::compose(seg2, loc2); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// Source dispatch: walks the source (first1) segments +////////////////////////////////////////////////////////////////////////////// + +template +BOOST_CONTAINER_FORCEINLINE +typename algo_enable_if_c + < !Tag::value || is_sentinel::value + , bool + >::type +segmented_equal_dispatch(SrcIter first1, Sent last1, InpIter2 &first2_out, BinaryPred pred, Tag, Cat) +{ +#if !defined(BOOST_CONTAINER_DISABLE_SEGMENTED_OUTPUT) + typedef segmented_iterator_traits iter2_traits; + return (segmented_equal_iter2_dispatch) + (first1, last1, first2_out, pred, typename iter2_traits::is_segmented_iterator(), Cat()); +#else + return (segmented_equal_iter2_dispatch) + (first1, last1, first2_out, pred, non_segmented_iterator_tag(), Cat()); +#endif +} + +template +bool segmented_equal_dispatch + (SegIter first1, SegIter last1, InpIter2 &first2, BinaryPred pred, segmented_iterator_tag, Cat) +{ + typedef segmented_iterator_traits traits; + typedef typename traits::local_iterator local_iterator; + typedef typename traits::segment_iterator segment_iterator; + typedef typename segmented_iterator_traits::is_segmented_iterator is_local_seg_t; + typedef typename iterator_traits::iterator_category local_cat_t; + + segment_iterator sfirst = traits::segment(first1); + segment_iterator const slast = traits::segment(last1); + + if(sfirst == slast) { + return (segmented_equal_dispatch) + (traits::local(first1), traits::local(last1), first2, pred, is_local_seg_t(), local_cat_t()); + } + else { + if(!(segmented_equal_dispatch) + (traits::local(first1), traits::end(sfirst), first2, pred, is_local_seg_t(), local_cat_t())) + return false; + + for(++sfirst; sfirst != slast; ++sfirst) { + if(!(segmented_equal_dispatch) + (traits::begin(sfirst), traits::end(sfirst), first2, pred, is_local_seg_t(), local_cat_t())) + return false; + } + + return (segmented_equal_dispatch) + (traits::begin(slast), traits::local(last1), first2, pred, is_local_seg_t(), local_cat_t()); + } +} + +} // namespace detail_algo + //! Returns \c true if elements in [first1, last1) are equal to the -//! range starting at \c first2. Exploits segmentation on the first range. +//! range starting at \c first2 according to \c pred. +//! Exploits segmentation on both ranges. +template +BOOST_CONTAINER_FORCEINLINE +bool segmented_equal(InpIter1 first1, Sent last1, InpIter2 first2, BinaryPred pred) +{ + typedef segmented_iterator_traits traits; + return detail_algo::segmented_equal_dispatch + (first1, last1, first2, pred, typename traits::is_segmented_iterator(), typename iterator_traits::iterator_category()); +} + +//! Returns \c true if elements in [first1, last1) are equal to the +//! range starting at \c first2. Exploits segmentation on both ranges. template BOOST_CONTAINER_FORCEINLINE bool segmented_equal(InpIter1 first1, Sent last1, InpIter2 first2) { - return (segmented_mismatch)(first1, last1, first2).first == last1; + return boost::container::segmented_equal(first1, last1, first2, detail_algo::equal_pred()); } } // namespace container