-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Define a variant of the `copy_n` algorithm for copying bytes in the output stream during decode of a compressed block. `copy_n` specifically handles overlap of the source and destination ranges. Change-Id: I756a8416a82f4a2007c7f2461de04353aac667d6
- Loading branch information
Showing
4 changed files
with
184 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
#pragma once | ||
|
||
#include <algorithm> | ||
#include <cassert> | ||
#include <concepts> | ||
#include <cstring> | ||
#include <iterator> | ||
#include <ranges> | ||
#include <type_traits> | ||
|
||
namespace starflate { | ||
|
||
/// Return type of `copy_n` | ||
/// @tparam I input_iterator of the source range | ||
/// @tparam O output_iterator of the destination range | ||
/// | ||
template <class I, class O> | ||
using copy_n_result = std::ranges::in_out_result<I, O>; | ||
|
||
/// Copies a number of elements to a new location | ||
/// @tparam I input_iterator of the source range | ||
/// @tparam O output_iterator of the destination range | ||
/// @param first beginning of the range to copy from | ||
/// @param n number of element to copy | ||
/// @param result beginning of the destination range | ||
/// | ||
/// Copies exactly `n` values from the range beginning at first to the range | ||
/// beginning at `result` by performing the equivalent of `*(result + i) = | ||
/// *(first + i)` for each integer in [`0`, `n`). | ||
/// | ||
/// @return `std::ranges::in_out_result` that contains an `std::input_iterator` | ||
/// iterator equal to `std::ranges::next(first, n)` and a | ||
/// `std::weakly_incrementable` iterator equal to `ranges::next(result, n)`. | ||
/// | ||
/// @note Unlike `std::copy_n` and `std::ranges::copy_n`, `starflate::copy_n` | ||
/// specifically handles a destination range overlapping the right side of a | ||
/// source range. | ||
/// | ||
/// @note This is implemented as a global function object. | ||
/// | ||
/// @see https://en.cppreference.com/w/cpp/algorithm/copy_n | ||
/// | ||
inline constexpr class | ||
{ | ||
template <class I, class D, class O> | ||
static constexpr auto | ||
impl(std::true_type, I first, D n, O result) -> copy_n_result<I, O> | ||
{ | ||
assert((result + n <= first or first < result) and | ||
"destination range overlaps left side of source range"); | ||
|
||
if (std::is_constant_evaluated()) { | ||
return impl(std::false_type{}, first, n, result); | ||
} | ||
|
||
const auto dist = result - first; | ||
|
||
while (n != D{}) { | ||
const auto m = std::min(dist, n); | ||
std::memcpy(result, first, static_cast<std::size_t>(m) * sizeof(*first)); | ||
first += m; | ||
result += m; | ||
n -= m; | ||
} | ||
|
||
return {first, result}; | ||
} | ||
|
||
template <class I, class D, class O> | ||
static constexpr auto | ||
impl(std::false_type, I first, D n, O result) -> copy_n_result<I, O> | ||
{ | ||
while (n-- != D{}) { | ||
*result++ = *first++; | ||
} | ||
|
||
return {first, result}; | ||
} | ||
|
||
public: | ||
template <std::input_iterator I, std::weakly_incrementable O> | ||
requires std::indirectly_copyable<I, O> | ||
constexpr auto | ||
operator()(I first, std::iter_difference_t<I> n, O result) const | ||
-> copy_n_result<I, O> | ||
{ | ||
using try_bulk_copy = std::bool_constant< | ||
std::contiguous_iterator<I> and // | ||
std::contiguous_iterator<O> and // | ||
std::is_same_v<std::iter_value_t<I>, std::iter_value_t<O>> and // | ||
std::is_trivially_copyable_v<std::iter_value_t<I>>>; | ||
|
||
return impl(try_bulk_copy{}, first, n, result); | ||
} | ||
} copy_n{}; | ||
|
||
} // namespace starflate |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#include "src/copy_n.hpp" | ||
|
||
#include <boost/ut.hpp> | ||
|
||
#include <algorithm> | ||
#include <array> | ||
#include <iterator> | ||
#include <list> | ||
|
||
auto main() -> int | ||
{ | ||
using ::boost::ut::aborts; | ||
using ::boost::ut::expect; | ||
using ::boost::ut::range_eq; | ||
using ::boost::ut::test; | ||
|
||
test("copy with overlap, non-contiguous range") = [] { | ||
constexpr auto expected = std::array{1, 2, 3, 1, 2, 3, 1, 2}; | ||
|
||
auto data = std::list{1, 2, 3, 0, 0, 0, 0, 0}; | ||
|
||
const auto result = | ||
starflate::copy_n(data.begin(), 5, std::next(data.begin(), 3)); | ||
|
||
expect(std::next(data.begin(), 5) == result.in); | ||
expect(data.end() == result.out); | ||
expect(range_eq(expected, data)); | ||
}; | ||
|
||
test("copy, adjacent ranges, contiguous range") = [] { | ||
constexpr auto expected = std::array{1, 2, 3, 4, 1, 2, 3, 4}; | ||
|
||
auto data = std::array{1, 2, 3, 4, 0, 0, 0, 0}; | ||
|
||
auto result = starflate::copy_n(data.begin(), 4, data.begin() + 4); | ||
|
||
expect(data.begin() + 4 == result.in); | ||
expect(data.end() == result.out); | ||
expect(range_eq(expected, data)); | ||
}; | ||
|
||
test("copy with overlap, contiguous range") = [] { | ||
constexpr auto expected = std::array{1, 2, 3, 1, 2, 3, 1, 2}; | ||
|
||
auto data = std::array{1, 2, 3, 0, 0, 0, 0, 0}; | ||
|
||
auto result = starflate::copy_n(data.begin(), 5, data.begin() + 3); | ||
|
||
expect(data.begin() + 5 == result.in); | ||
expect(data.end() == result.out); | ||
expect(range_eq(expected, data)); | ||
}; | ||
|
||
test("copy with overlap, contiguous, constexpr") = [] { | ||
constexpr auto expected = std::array{1, 2, 3, 1, 2, 3, 1, 2}; | ||
|
||
constexpr auto data = [] { | ||
auto data = std::array{1, 2, 3, 0, 0, 0, 0, 0}; | ||
starflate::copy_n(data.begin(), 5, data.begin() + 3); | ||
return data; | ||
}(); | ||
|
||
expect(range_eq(expected, data)); | ||
}; | ||
|
||
test("copy with overlap, contiguous range, dest overlaps left side of " | ||
"source") = [] { | ||
expect(aborts([] { | ||
auto data = std::array<int, 8>{}; | ||
starflate::copy_n(data.begin() + 3, 5, data.begin()); | ||
})); | ||
}; | ||
} |