]>
git.proxmox.com Git - ceph.git/blob - ceph/src/include/interval_set.h
f1a21e5f96e1ae290e077386b16692bdf80e038a
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
16 #ifndef CEPH_INTERVAL_SET_H
17 #define CEPH_INTERVAL_SET_H
28 * This class is written to work with a variety of map-like containers,
29 * *include* ones that invalidate iterators when they are modified (e.g.,
30 * flat_map and btree_map).
33 template<typename T
, template<typename
, typename
, typename
...> class C
= std::map
>
37 using value_type
= typename
Map::value_type
;
38 using offset_type
= T
;
39 using length_type
= T
;
40 using reference
= value_type
&;
41 using const_reference
= const value_type
&;
42 using size_type
= typename
Map::size_type
;
49 using difference_type
= ssize_t
;
50 using value_type
= typename
Map::value_type
;
51 using pointer
= typename
Map::value_type
*;
52 using reference
= typename
Map::value_type
&;
53 using iterator_category
= std::forward_iterator_tag
;
55 explicit iterator(typename
Map::iterator iter
)
59 // For the copy constructor and assignment operator, the compiler-generated functions, which
60 // perform simple bitwise copying, should be fine.
62 bool operator==(const iterator
& rhs
) const {
63 return (_iter
== rhs
._iter
);
66 bool operator!=(const iterator
& rhs
) const {
67 return (_iter
!= rhs
._iter
);
70 // Dereference this iterator to get a pair.
71 reference
operator*() const {
75 // Return the interval start.
76 offset_type
get_start() const {
80 // Return the interval length.
81 length_type
get_len() const {
85 offset_type
get_end() const {
86 return _iter
->first
+ _iter
->second
;
89 // Set the interval length.
90 void set_len(const length_type
& len
) {
95 iterator
& operator++()
102 iterator
operator++(int)
104 iterator
prev(_iter
);
109 friend class interval_set::const_iterator
;
112 typename
Map::iterator _iter
;
113 friend class interval_set
;
119 using difference_type
= ssize_t
;
120 using value_type
= const typename
Map::value_type
;
121 using pointer
= const typename
Map::value_type
*;
122 using reference
= const typename
Map::value_type
&;
123 using iterator_category
= std::forward_iterator_tag
;
125 explicit const_iterator(typename
Map::const_iterator iter
)
129 const_iterator(const iterator
&i
)
133 // For the copy constructor and assignment operator, the compiler-generated functions, which
134 // perform simple bitwise copying, should be fine.
136 bool operator==(const const_iterator
& rhs
) const {
137 return (_iter
== rhs
._iter
);
140 bool operator!=(const const_iterator
& rhs
) const {
141 return (_iter
!= rhs
._iter
);
144 // Dereference this iterator to get a pair.
145 reference
operator*() const {
149 // Return the interval start.
150 offset_type
get_start() const {
153 offset_type
get_end() const {
154 return _iter
->first
+ _iter
->second
;
157 // Return the interval length.
158 length_type
get_len() const {
159 return _iter
->second
;
163 const_iterator
& operator++()
170 const_iterator
operator++(int)
172 const_iterator
prev(_iter
);
178 typename
Map::const_iterator _iter
;
181 interval_set() = default;
182 interval_set(Map
&& other
) {
184 for (const auto& p
: m
) {
189 size_type
num_intervals() const
195 return iterator(m
.begin());
198 iterator
lower_bound(T start
) {
199 return iterator(find_inc_m(start
));
203 return iterator(m
.end());
206 const_iterator
begin() const {
207 return const_iterator(m
.begin());
210 const_iterator
lower_bound(T start
) const {
211 return const_iterator(find_inc(start
));
214 const_iterator
end() const {
215 return const_iterator(m
.end());
220 auto find_inc(T start
) const {
221 auto p
= m
.lower_bound(start
); // p->first >= start
222 if (p
!= m
.begin() &&
223 (p
== m
.end() || p
->first
> start
)) {
224 --p
; // might overlap?
225 if (p
->first
+ p
->second
<= start
)
231 auto find_inc_m(T start
) {
232 auto p
= m
.lower_bound(start
);
233 if (p
!= m
.begin() &&
234 (p
== m
.end() || p
->first
> start
)) {
235 --p
; // might overlap?
236 if (p
->first
+ p
->second
<= start
)
242 auto find_adj(T start
) const {
243 auto p
= m
.lower_bound(start
);
244 if (p
!= m
.begin() &&
245 (p
== m
.end() || p
->first
> start
)) {
247 if (p
->first
+ p
->second
< start
)
253 auto find_adj_m(T start
) {
254 auto p
= m
.lower_bound(start
);
255 if (p
!= m
.begin() &&
256 (p
== m
.end() || p
->first
> start
)) {
258 if (p
->first
+ p
->second
< start
)
264 void intersection_size_asym(const interval_set
&s
, const interval_set
&l
) {
265 auto ps
= s
.m
.begin();
266 ceph_assert(ps
!= s
.m
.end());
267 auto offset
= ps
->first
;
274 auto pl
= l
.find_inc(offset
);
277 while (ps
!= s
.m
.end() && ps
->first
+ ps
->second
<= pl
->first
)
281 offset
= pl
->first
+ pl
->second
;
282 if (offset
<= ps
->first
) {
289 mi
= m
.insert(mi
, *ps
);
293 } while (ps
!= s
.m
.end() && pl
!= l
.m
.end() && *ps
== *pl
);
300 auto start
= std::max
<T
>(ps
->first
, pl
->first
);
301 auto en
= std::min
<T
>(ps
->first
+ ps
->second
, offset
);
302 ceph_assert(en
> start
);
303 mi
= m
.emplace_hint(mi
, start
, en
- start
);
305 if (ps
->first
+ ps
->second
<= offset
) {
314 bool subset_size_sym(const interval_set
&b
) const {
315 auto pa
= m
.begin(), pb
= b
.m
.begin();
316 const auto a_end
= m
.end(), b_end
= b
.m
.end();
318 while (pa
!= a_end
&& pb
!= b_end
) {
319 while (pb
->first
+ pb
->second
<= pa
->first
) {
329 } while (pa
!= a_end
&& pb
!= b_end
&& *pa
== *pb
);
333 // interval begins before other
334 if (pa
->first
< pb
->first
)
336 // interval is longer than other
337 if (pa
->first
+ pa
->second
> pb
->first
+ pb
->second
)
347 bool operator==(const interval_set
& other
) const {
348 return _size
== other
._size
&& m
== other
.m
;
351 uint64_t size() const {
355 void bound_encode(size_t& p
) const {
356 denc_traits
<Map
>::bound_encode(m
, p
);
358 void encode(ceph::buffer::list::contiguous_appender
& p
) const {
361 void decode(ceph::buffer::ptr::const_iterator
& p
) {
364 for (const auto& p
: m
) {
368 void decode(ceph::buffer::list::iterator
& p
) {
371 for (const auto& p
: m
) {
376 void encode_nohead(ceph::buffer::list::contiguous_appender
& p
) const {
377 denc_traits
<Map
>::encode_nohead(m
, p
);
379 void decode_nohead(int n
, ceph::buffer::ptr::const_iterator
& p
) {
380 denc_traits
<Map
>::decode_nohead(n
, m
, p
);
382 for (const auto& p
: m
) {
392 bool contains(T i
, T
*pstart
=0, T
*plen
=0) const {
393 auto p
= find_inc(i
);
394 if (p
== m
.end()) return false;
395 if (p
->first
> i
) return false;
396 if (p
->first
+p
->second
<= i
) return false;
397 ceph_assert(p
->first
<= i
&& p
->first
+p
->second
> i
);
404 bool contains(T start
, T len
) const {
405 auto p
= find_inc(start
);
406 if (p
== m
.end()) return false;
407 if (p
->first
> start
) return false;
408 if (p
->first
+p
->second
<= start
) return false;
409 ceph_assert(p
->first
<= start
&& p
->first
+p
->second
> start
);
410 if (p
->first
+p
->second
< start
+len
) return false;
413 bool intersects(T start
, T len
) const {
415 a
.insert(start
, len
);
417 i
.intersection_of( *this, a
);
418 if (i
.empty()) return false;
422 // outer range of set
426 offset_type
range_start() const {
427 ceph_assert(!empty());
431 offset_type
range_end() const {
432 ceph_assert(!empty());
434 return p
->first
+ p
->second
;
437 // interval start after p (where p not in set)
438 bool starts_after(T i
) const {
439 ceph_assert(!contains(i
));
440 auto p
= find_inc(i
);
441 if (p
== m
.end()) return false;
444 offset_type
start_after(T i
) const {
445 ceph_assert(!contains(i
));
446 auto p
= find_inc(i
);
450 // interval end that contains start
451 offset_type
end_after(T start
) const {
452 ceph_assert(contains(start
));
453 auto p
= find_inc(start
);
454 return p
->first
+p
->second
;
461 void insert(T start
, T len
, T
*pstart
=0, T
*plen
=0) {
462 //cout << "insert " << start << "~" << len << endl;
463 ceph_assert(len
> 0);
465 auto p
= find_adj_m(start
);
467 m
[start
] = len
; // new interval
473 if (p
->first
< start
) {
475 if (p
->first
+ p
->second
!= start
) {
476 //cout << "p is " << p->first << "~" << p->second << ", start is " << start << ", len is " << len << endl;
480 p
->second
+= len
; // append to end
487 start
+len
== n
->first
) { // combine with next, too!
488 p
->second
+= n
->second
;
497 if (start
+len
== p
->first
) {
501 *plen
= len
+ p
->second
;
502 T psecond
= p
->second
;
504 m
[start
] = len
+ psecond
; // append to front
506 ceph_assert(p
->first
> start
+len
);
511 m
[start
] = len
; // new interval
517 void swap(interval_set
& other
) {
519 std::swap(_size
, other
._size
);
522 void erase(const iterator
&i
) {
523 _size
-= i
.get_len();
531 void erase(T start
, T len
,
532 std::function
<bool(T
, T
)> claim
= {}) {
533 auto p
= find_inc_m(start
);
537 ceph_assert(p
!= m
.end());
538 ceph_assert(p
->first
<= start
);
540 T before
= start
- p
->first
;
541 ceph_assert(p
->second
>= before
+len
);
542 T after
= p
->second
- before
- len
;
544 if (claim
&& claim(p
->first
, before
)) {
548 p
->second
= before
; // shorten bit before
554 if (claim
&& claim(start
+ len
, after
)) {
557 m
[start
+ len
] = after
;
562 void subtract(const interval_set
&a
) {
563 for (const auto& [start
, len
] : a
.m
) {
568 void insert(const interval_set
&a
) {
569 for (const auto& [start
, len
] : a
.m
) {
575 void intersection_of(const interval_set
&a
, const interval_set
&b
) {
576 ceph_assert(&a
!= this);
577 ceph_assert(&b
!= this);
580 const interval_set
*s
, *l
;
582 if (a
.size() < b
.size()) {
594 * Use the lower_bound algorithm for larger size ratios
595 * where it performs better, but not for smaller size
596 * ratios where sequential search performs better.
598 if (l
->size() / s
->size() >= 10) {
599 intersection_size_asym(*s
, *l
);
603 auto pa
= a
.m
.begin();
604 auto pb
= b
.m
.begin();
607 while (pa
!= a
.m
.end() && pb
!= b
.m
.end()) {
609 if (pa
->first
+ pa
->second
<= pb
->first
)
611 if (pb
->first
+ pb
->second
<= pa
->first
)
616 mi
= m
.insert(mi
, *pa
);
620 } while (pa
!= a
.m
.end() && pb
!= b
.m
.end() && *pa
== *pb
);
624 T start
= std::max(pa
->first
, pb
->first
);
625 T en
= std::min(pa
->first
+pa
->second
, pb
->first
+pb
->second
);
626 ceph_assert(en
> start
);
627 mi
= m
.emplace_hint(mi
, start
, en
- start
);
629 if (pa
->first
+pa
->second
> pb
->first
+pb
->second
)
635 void intersection_of(const interval_set
& b
) {
638 intersection_of(a
, b
);
641 void union_of(const interval_set
&a
, const interval_set
&b
) {
642 ceph_assert(&a
!= this);
643 ceph_assert(&b
!= this);
646 //cout << "union_of" << endl;
654 ab
.intersection_of(a
, b
);
661 void union_of(const interval_set
&b
) {
666 void union_insert(T off
, T len
) {
672 bool subset_of(const interval_set
&big
) const {
675 if (size() > big
.size())
677 if (range_end() > big
.range_end())
681 * Use the lower_bound algorithm for larger size ratios
682 * where it performs better, but not for smaller size
683 * ratios where sequential search performs better.
685 if (big
.size() / size() < 10)
686 return subset_size_sym(big
);
688 for (const auto& [start
, len
] : m
) {
689 if (!big
.contains(start
, len
)) return false;
695 * build a subset of @other, starting at or after @start, and including
696 * @len worth of values, skipping holes. e.g.,
697 * span_of([5~10,20~5], 8, 5) -> [8~2,20~3]
699 void span_of(const interval_set
&other
, T start
, T len
) {
701 auto p
= other
.find_inc(start
);
702 if (p
== other
.m
.end())
704 if (p
->first
< start
) {
705 if (p
->first
+ p
->second
< start
)
707 if (p
->first
+ p
->second
< start
+ len
) {
708 T howmuch
= p
->second
- (start
- p
->first
);
709 insert(start
, howmuch
);
717 while (p
!= other
.m
.end() && len
> 0) {
718 if (p
->second
< len
) {
719 insert(p
->first
, p
->second
);
723 insert(p
->first
, len
);
730 * Move contents of m into another Map. Use that instead of
731 * encoding interval_set into bufferlist then decoding it back into Map.
740 Map m
; // map start -> len
743 // declare traits explicitly because (1) it's templatized, and (2) we
744 // want to include _nohead variants.
745 template<typename T
, template<typename
, typename
, typename
...> class C
>
746 struct denc_traits
<interval_set
<T
, C
>> {
748 using container_t
= interval_set
<T
, C
>;
750 static constexpr bool supported
= true;
751 static constexpr bool bounded
= false;
752 static constexpr bool featured
= false;
753 static constexpr bool need_contiguous
= denc_traits
<T
, C
<T
,T
>>::need_contiguous
;
754 static void bound_encode(const container_t
& v
, size_t& p
) {
757 static void encode(const container_t
& v
,
758 ceph::buffer::list::contiguous_appender
& p
) {
761 static void decode(container_t
& v
, ceph::buffer::ptr::const_iterator
& p
) {
764 template<typename U
=T
>
765 static typename
std::enable_if
<sizeof(U
) && !need_contiguous
>::type
766 decode(container_t
& v
, ceph::buffer::list::iterator
& p
) {
769 static void encode_nohead(const container_t
& v
,
770 ceph::buffer::list::contiguous_appender
& p
) {
773 static void decode_nohead(size_t n
, container_t
& v
,
774 ceph::buffer::ptr::const_iterator
& p
) {
775 v
.decode_nohead(n
, p
);
780 template<typename T
, template<typename
, typename
, typename
...> class C
>
781 inline std::ostream
& operator<<(std::ostream
& out
, const interval_set
<T
,C
> &s
) {
784 for (const auto& [start
, len
] : s
) {
785 if (!first
) out
<< ",";
786 out
<< start
<< "~" << len
;