From: Alex Ainscow Date: Wed, 8 Jan 2025 22:18:35 +0000 (+0000) Subject: interval_set: erase and subtract enhancements X-Git-Tag: v20.0.0~286^2~7 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=fd67fd5f6a990ac4db696e2178769c49a1001e60;p=ceph.git interval_set: erase and subtract enhancements The old erase would only allow intervals which exist to be erased. It is often useful to erase any interval, even if it does not exist or partially overlaps one or many intervals. Signed-off-by: Alex Ainscow --- diff --git a/src/include/interval_set.h b/src/include/interval_set.h index 6372b35f818a..7e935b2b0463 100644 --- a/src/include/interval_set.h +++ b/src/include/interval_set.h @@ -560,8 +560,13 @@ class interval_set { erase(val, 1); } - void erase(T start, T len, - std::function claim = {}) { + /* This variant of erase allows the client to determine whether touching + * intervals should also be erased. This variant will assert that the + * intersection of the interval (start~len) is entirely contained by a single + * interval in *this. + */ + void erase(T start, T len, + std::function claim) { auto p = find_inc_m(start); _size -= len; @@ -591,9 +596,79 @@ class interval_set { } } + /** This variant of erase allows for general erases (making it useful for + * functions like subtract). It can cope with any overlaps and will erase + * multiple. entries. + */ + void erase(T start, T len) { + T begin = start; + T end = start + len; + + auto p = find_inc_m(begin); + + while ( p != m.end() && begin < end && end > p->first) { + T pend = p->first + p->second; + + // Skip any gap. + if (begin < p->first) begin = p->first; + _size -= pend - begin; + + // Truncate (delete later if empty) + p->second = begin - p->first; + + // Handle splits + if (end < pend) { + _size += pend - end; + // For some maps, inserting here corrupts p, so we need + // to insert, then recover p, so that we can delete it if needed. + p = m.insert(p, std::pair(end, pend-end)); + --p; + } + + // Erase empty interval or move on. + if (!p->second) p = m.erase(p); + else ++p; + + begin = pend; + } + } + + /** This general erase method erases after a particular offset. + */ + void erase_after(T start) { + T begin = start; + + auto p = find_inc_m(begin); + + while ( p != m.end()) { + T pend = p->first + p->second; + + // Skip any gap. + if (begin < p->first) begin = p->first; + _size -= pend - begin; + + // Truncate (delete later if empty) + p->second = begin - p->first; + + // Erase empty interval or move on. + if (!p->second) p = m.erase(p); + else ++p; + + begin = pend; + } + } + void subtract(const interval_set &a) { - for (const auto& [start, len] : a.m) { - erase(start, len); + if (empty() || a.empty()) return; + + auto start = range_start(); + auto end = range_end(); + + /* Only loop over the overlapping range of a */ + for (auto ap = a.find_inc(start); + ap != a.m.end() && ap->first <= end; + ++ap) { + erase(ap->first, ap->second); } }