スパース ファイルを (別のスパース ファイルに) コピーするコードを見ていますDeviceIoControl(... FSCTL_QUERY_ALLOCATED_RANGES ...)
。実際のデータを含む範囲のリストを取得するために使用します。
結果に次の範囲が含まれることは保証されていますか?
交わらない?
FileOffset
フィールドごとに並べられていますか?空じゃない?
ありますか
FileOffset + Length > FileOffset
(つまり、ラップアラウンド wrt はありませんuint64_t
)?
編集:
OS がこれらの保証のいずれも提供しない場合に備えて、検証を実装しました。
// version of std::remove_if that permits coalescing
template<class ForwardIt, class BinaryPredicate>
ForwardIt coalesce_neighbors(ForwardIt first, ForwardIt last, BinaryPredicate p)
{
for(ForwardIt i = first; i != last; ++i)
if (!p(*first, *i))
{
if (first != i)
*first = std::move(*i);
++first;
}
return first;
}
// for given range set do: sort by offset, check for validity, discard empty ones, coalesce intersecting/adjacent ones
FILE_ALLOCATED_RANGE_BUFFER* sanitize_ranges_(FILE_ALLOCATED_RANGE_BUFFER* p, FILE_ALLOCATED_RANGE_BUFFER* p_end)
{
auto ui = [](LARGE_INTEGER const& v){ return static_cast<ULONGLONG>(v.QuadPart); };
std::sort(p, p_end, [=](auto& l, auto& r){ return ui(l.FileOffset) < ui(r.FileOffset); }); // sort ranges by offset
return coalesce_neighbors(p, p_end, [=](auto& l, auto& r){
if (std::numeric_limits<ULONGLONG>::max() - ui(r.FileOffset) < ui(r.Length)) // no wraparounds allowed
throw std::logic_error("invalid range (wraparound)");
if (ui(r.Length) == 0) return true; // discard empty ranges
if (&l != &r && ui(l.FileOffset) + ui(l.Length) >= ui(r.FileOffset)) // 'l.offset <= r.offset' is guranteed due to sorting
{
l.Length.QuadPart = ui(r.FileOffset) + ui(r.Length) - ui(l.FileOffset); // coalesce intersecting/adjacent ranges
return true; // ... and discard neighbor we ate
}
return false;
});
}