diff --git a/experimental/segmented_copy_test.cpp b/experimental/segmented_copy_test.cpp index 4790482..a30d2cb 100644 --- a/experimental/segmented_copy_test.cpp +++ b/experimental/segmented_copy_test.cpp @@ -122,6 +122,79 @@ void test_copy_seg2() BOOST_TEST_EQ(out[static_cast(i)], i + 1); } +void test_copy_segmented_output() +{ + test_detail::seg_vector sv; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + int a3[] = {6, 7, 8, 9}; + sv.add_segment_range(a1, a1 + 3); + sv.add_segment_range(a2, a2 + 2); + sv.add_segment_range(a3, a3 + 4); + + test_detail::seg_vector out; + out.add_segment(4, 0); + out.add_segment(3, 0); + out.add_segment(2, 0); + + typedef test_detail::seg_vector::iterator iter_t; + iter_t result = segmented_copy(sv.begin(), sv.end(), out.begin()); + + BOOST_TEST(result == out.end()); + iter_t it = out.begin(); + for(int i = 0; i < 9; ++i, ++it) + BOOST_TEST_EQ(*it, i + 1); +} + +void test_copy_seg2_to_seg2() +{ + test_detail::seg2_vector sv2; + int a1[] = {1, 2, 3}; + int a2[] = {4, 5}; + int a3[] = {6, 7, 8, 9}; + sv2.add_flat_segment_range(a1, a1 + 3); + sv2.add_flat_segment_range(a2, a2 + 2); + sv2.add_flat_segment_range(a3, a3 + 4); + + test_detail::seg2_vector out; + { + test_detail::seg_vector s1; s1.add_segment(5, 0); + test_detail::seg_vector s2; s2.add_segment(4, 0); + out.add_segment(s1); + out.add_segment(s2); + } + + typedef test_detail::seg2_vector::iterator iter_t; + iter_t result = segmented_copy(sv2.begin(), sv2.end(), out.begin()); + + BOOST_TEST(result == out.end()); + iter_t it = out.begin(); + for(int i = 0; i < 9; ++i, ++it) + BOOST_TEST_EQ(*it, i + 1); +} + +void test_copy_seg_to_seg_misaligned() +{ + test_detail::seg_vector sv; + int a1[] = {1, 2, 3, 4, 5}; + int a2[] = {6, 7, 8}; + sv.add_segment_range(a1, a1 + 5); + sv.add_segment_range(a2, a2 + 3); + + test_detail::seg_vector out; + out.add_segment(2, 0); + out.add_segment(3, 0); + out.add_segment(3, 0); + + typedef test_detail::seg_vector::iterator iter_t; + iter_t result = segmented_copy(sv.begin(), sv.end(), out.begin()); + + BOOST_TEST(result == out.end()); + iter_t it = out.begin(); + for(int i = 0; i < 8; ++i, ++it) + BOOST_TEST_EQ(*it, i + 1); +} + int main() { test_copy_full_range(); @@ -131,5 +204,8 @@ int main() test_copy_sentinel_segmented(); test_copy_sentinel_non_segmented(); test_copy_seg2(); + test_copy_segmented_output(); + test_copy_seg2_to_seg2(); + test_copy_seg_to_seg_misaligned(); return boost::report_errors(); } diff --git a/include/boost/container/experimental/segmented_copy.hpp b/include/boost/container/experimental/segmented_copy.hpp index 4d029b4..9664114 100644 --- a/include/boost/container/experimental/segmented_copy.hpp +++ b/include/boost/container/experimental/segmented_copy.hpp @@ -31,83 +31,201 @@ OutIter segmented_copy(InIter first, Sent last, OutIter result); namespace detail_algo { +////////////////////////////////////////////////////////////////////////////// +// Bounded destination helper: copies from source into [dst_first, dst_last), +// stopping when source or destination is exhausted. +// Advances first_out (by reference) so the caller knows how far we got. +// Recursively walks destination segments when dst is segmented. +// +// When dst_last is unreachable_sentinel_t the destination-full check +// is optimised away, giving the same code as an unbounded loop. +////////////////////////////////////////////////////////////////////////////// + #if defined(BOOST_CONTAINER_SEGMENTED_LOOP_UNROLLING) -template -OutIter segmented_copy_dispatch - (RAIter first, RAIter last, OutIter result, const non_segmented_iterator_tag &, const std::random_access_iterator_tag &) +template +DstIter segmented_copy_dst_bounded + (RASrcIter& first_out, RASrcIter last, DstIter dst_first, DstSent dst_last, + const non_segmented_iterator_tag &, const std::random_access_iterator_tag &) { - typedef typename iterator_traits::difference_type difference_type; + typedef typename iterator_traits::difference_type difference_type; + RASrcIter first = first_out; + difference_type n = last - first; while(n >= difference_type(4)) { - *result = *first; ++first; ++result; - *result = *first; ++first; ++result; - *result = *first; ++first; ++result; - *result = *first; ++first; ++result; + if(dst_first == dst_last) goto out_path; *dst_first = *first; ++first; ++dst_first; + if(dst_first == dst_last) goto out_path; *dst_first = *first; ++first; ++dst_first; + if(dst_first == dst_last) goto out_path; *dst_first = *first; ++first; ++dst_first; + if(dst_first == dst_last) goto out_path; *dst_first = *first; ++first; ++dst_first; n -= 4; } switch(n) { case 3: - *result = *first; ++first; ++result; - *result = *first; ++first; ++result; - *result = *first; ++first; ++result; - break; + if(dst_first == dst_last) goto out_path; *dst_first = *first; ++first; ++dst_first; + BOOST_FALLTHROUGH; case 2: - *result = *first; ++first; ++result; - *result = *first; ++first; ++result; - break; + if(dst_first == dst_last) goto out_path; *dst_first = *first; ++first; ++dst_first; + BOOST_FALLTHROUGH; case 1: - *result = *first; ++first; ++result; - break; + if(dst_first == dst_last) goto out_path; *dst_first = *first; ++first; ++dst_first; + BOOST_FALLTHROUGH; default: break; } - return result; + out_path: + first_out = first; + return dst_first; } #endif //BOOST_CONTAINER_SEGMENTED_LOOP_UNROLLING -template -typename algo_enable_if_c< - !Tag::value || is_sentinel::value, OutIter>::type - segmented_copy_dispatch(InIter first, Sent last, OutIter result, Tag, Cat) +template +typename algo_enable_if_c::type +segmented_copy_dst_bounded + (SrcIter& first_out, Sent last, DstIter dst_first, DstSent dst_last, DstTag, SrcCat) { - for(; first != last; ++first, ++result) - *result = *first; - return result; + SrcIter first = first_out; + + for(; first != last; ++first) { + if(dst_first == dst_last) + goto out_path; + *dst_first = *first; + ++dst_first; + } + out_path: + first_out = first; + return dst_first; +} + +template +SegDstIter segmented_copy_dst_bounded + (SrcIter& first, Sent last, SegDstIter dst_first, SegDstIter dst_last, + segmented_iterator_tag, SrcCat) +{ + typedef segmented_iterator_traits 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::is_segmented_iterator dst_is_local_seg_t; + + dst_segment_iterator sfirst = dst_traits::segment(dst_first); + const dst_segment_iterator slast = dst_traits::segment(dst_last); + + if(sfirst == slast) { + dst_local_iterator r = (segmented_copy_dst_bounded) + (first, last, dst_traits::local(dst_first), dst_traits::local(dst_last), dst_is_local_seg_t(), SrcCat()); + return dst_traits::compose(sfirst, r); + } + else { + dst_local_iterator dst_local = (segmented_copy_dst_bounded) + (first, last, dst_traits::local(dst_first), dst_traits::end(sfirst), dst_is_local_seg_t(), SrcCat()); + if(first == last) + return dst_traits::compose(sfirst, dst_local); + + for(++sfirst; sfirst != slast; ++sfirst) { + dst_local = (segmented_copy_dst_bounded) + (first, last, dst_traits::begin(sfirst), dst_traits::end(sfirst), dst_is_local_seg_t(), SrcCat()); + if(first == last) + return dst_traits::compose(sfirst, dst_local); + } + + dst_local = (segmented_copy_dst_bounded) + (first, last, dst_traits::begin(slast), dst_traits::local(dst_last), dst_is_local_seg_t(), SrcCat()); + return dst_traits::compose(sfirst, dst_local); + } +} + +////////////////////////////////////////////////////////////////////////////// +// Destination dispatch: routes to bounded helper. +// Non-segmented destination: single unbounded call (unreachable_sentinel_t). +// Segmented destination: loop over destination segments, bounded per segment. +////////////////////////////////////////////////////////////////////////////// + +template +BOOST_CONTAINER_FORCEINLINE DstIter segmented_copy_dst_dispatch + (SrcIter first, Sent last, DstIter result, + const non_segmented_iterator_tag &, Cat) +{ + return (segmented_copy_dst_bounded) + (first, last, result, unreachable_sentinel_t(), non_segmented_iterator_tag(), Cat()); +} + +template +SegDstIter segmented_copy_dst_dispatch + (SrcIter first, Sent last, SegDstIter result, + const segmented_iterator_tag &, Cat) +{ + typedef segmented_iterator_traits 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::is_segmented_iterator dst_is_local_seg_t; + + if(first == last) + return result; + + dst_segment_iterator dst_seg = dst_traits::segment(result); + dst_local_iterator dst_local = dst_traits::local(result); + + while(first != last) { + dst_local_iterator dst_end = dst_traits::end(dst_seg); + dst_local = (segmented_copy_dst_bounded) + (first, last, dst_local, dst_end, dst_is_local_seg_t(), Cat()); + if(first != last) { + ++dst_seg; + dst_local = dst_traits::begin(dst_seg); + } + } + return dst_traits::compose(dst_seg, dst_local); +} + +////////////////////////////////////////////////////////////////////////////// +// Source dispatch: walks the source (read pointer) segments +////////////////////////////////////////////////////////////////////////////// + +template +typename algo_enable_if_c< + !Tag::value || is_sentinel::value, OutIter>::type +segmented_copy_dispatch(SrcIter first, Sent last, OutIter result, Tag, Cat) +{ + typedef segmented_iterator_traits dst_traits; + return (segmented_copy_dst_dispatch) + (first, last, result, typename dst_traits::is_segmented_iterator(), Cat()); } template OutIter segmented_copy_dispatch(SegIter first, SegIter last, OutIter result, 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; + typedef segmented_iterator_traits src_traits; + typedef typename src_traits::local_iterator src_local_iterator; + typedef typename src_traits::segment_iterator src_segment_iterator; + typedef typename segmented_iterator_traits::is_segmented_iterator src_is_local_seg_t; + typedef typename iterator_traits::iterator_category src_local_cat_t; - segment_iterator sfirst = traits::segment(first); - segment_iterator slast = traits::segment(last); + src_segment_iterator sfirst = src_traits::segment(first); + const src_segment_iterator slast = src_traits::segment(last); if(sfirst == slast) { - return (segmented_copy_dispatch)(traits::local(first), traits::local(last), result, is_local_seg_t(), local_cat_t()); + return (segmented_copy_dispatch) + (src_traits::local(first), src_traits::local(last), result, src_is_local_seg_t(), src_local_cat_t()); } else { - result = (segmented_copy_dispatch)(traits::local(first), traits::end(sfirst), result, is_local_seg_t(), local_cat_t()); + result = (segmented_copy_dispatch) + (src_traits::local(first), src_traits::end(sfirst), result, src_is_local_seg_t(), src_local_cat_t()); for(++sfirst; sfirst != slast; ++sfirst) - result = (segmented_copy_dispatch)(traits::begin(sfirst), traits::end(sfirst), result, is_local_seg_t(), local_cat_t()); + result = (segmented_copy_dispatch) + (src_traits::begin(sfirst), src_traits::end(sfirst), result, src_is_local_seg_t(), src_local_cat_t()); - return (segmented_copy_dispatch)(traits::begin(sfirst), traits::local(last), result, is_local_seg_t(), local_cat_t()); + return (segmented_copy_dispatch) + (src_traits::begin(slast), src_traits::local(last), result, src_is_local_seg_t(), src_local_cat_t()); } } } // namespace detail_algo //! Copies elements from [first, last) to the range beginning at \c result. -//! Segmentation is exploited on the input range only. +//! Segmentation is exploited on both input and output ranges. template BOOST_CONTAINER_FORCEINLINE OutIter segmented_copy(InIter first, Sent last, OutIter result)