]> git.proxmox.com Git - ceph.git/blob - ceph/src/dmclock/support/src/indirect_intrusive_heap.h
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / dmclock / support / src / indirect_intrusive_heap.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 /*
5 * Copyright (C) 2016 Red Hat Inc.
6 *
7 * Author: J. Eric Ivancich <ivancich@redhat.com>
8 *
9 * This is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License version
11 * 2.1, as published by the Free Software Foundation. See file
12 * COPYING.
13 */
14
15
16 #pragma once
17
18
19 #include <memory>
20 #include <vector>
21 #include <string>
22 #include <iostream>
23 #include <functional>
24 #include <algorithm>
25
26 #include "assert.h"
27
28
29 namespace crimson {
30 using IndIntruHeapData = size_t;
31
32 /* T is the ultimate data that's being stored in the heap, although
33 * through indirection.
34 *
35 * I is the indirect type that will actually be stored in the heap
36 * and that must allow dereferencing (via operator*) to yield a
37 * T&.
38 *
39 * C is a functor when given two T&'s will return true if the first
40 * must precede the second.
41 *
42 * heap_info is a data member pointer as to where the heap data in T
43 * is stored.
44 *
45 * K is the branching factor of the heap, default is 2 (binary heap).
46 */
47 template<typename I,
48 typename T,
49 IndIntruHeapData T::*heap_info,
50 typename C,
51 uint K = 2>
52 class IndIntruHeap {
53
54 // shorthand
55 using HeapIndex = IndIntruHeapData;
56
57 static_assert(
58 std::is_same<T,typename std::pointer_traits<I>::element_type>::value,
59 "class I must resolve to class T by indirection (pointer dereference)");
60
61 static_assert(
62 std::is_same<bool,
63 typename std::result_of<C(const T&,const T&)>::type>::value,
64 "class C must define operator() to take two const T& and return a bool");
65
66 static_assert(K >= 2, "K (degree of branching) must be at least 2");
67
68 class Iterator {
69 friend IndIntruHeap<I, T, heap_info, C, K>;
70
71 IndIntruHeap<I, T, heap_info, C, K>* heap;
72 HeapIndex index;
73
74 Iterator(IndIntruHeap<I, T, heap_info, C, K>& _heap, HeapIndex _index) :
75 heap(&_heap),
76 index(_index)
77 {
78 // empty
79 }
80
81 public:
82
83 Iterator(Iterator&& other) :
84 heap(other.heap),
85 index(other.index)
86 {
87 // empty
88 }
89
90 Iterator(const Iterator& other) :
91 heap(other.heap),
92 index(other.index)
93 {
94 // empty
95 }
96
97 Iterator& operator=(Iterator&& other) {
98 std::swap(heap, other.heap);
99 std::swap(index, other.index);
100 return *this;
101 }
102
103 Iterator& operator=(const Iterator& other) {
104 heap = other.heap;
105 index = other.index;
106 }
107
108 Iterator& operator++() {
109 if (index <= heap->count) {
110 ++index;
111 }
112 return *this;
113 }
114
115 bool operator==(const Iterator& other) const {
116 return heap == other.heap && index == other.index;
117 }
118
119 bool operator!=(const Iterator& other) const {
120 return !(*this == other);
121 }
122
123 T& operator*() {
124 return *heap->data[index];
125 }
126
127 T* operator->() {
128 return &(*heap->data[index]);
129 }
130
131 #if 0
132 // the item this iterator refers to
133 void increase() {
134 heap.sift_up(index);
135 }
136 #endif
137 }; // class Iterator
138
139
140 class ConstIterator {
141 friend IndIntruHeap<I, T, heap_info, C, K>;
142
143 const IndIntruHeap<I, T, heap_info, C, K>* heap;
144 HeapIndex index;
145
146 ConstIterator(const IndIntruHeap<I, T, heap_info, C, K>& _heap,
147 HeapIndex _index) :
148 heap(&_heap),
149 index(_index)
150 {
151 // empty
152 }
153
154 public:
155
156 ConstIterator(ConstIterator&& other) :
157 heap(other.heap),
158 index(other.index)
159 {
160 // empty
161 }
162
163 ConstIterator(const ConstIterator& other) :
164 heap(other.heap),
165 index(other.index)
166 {
167 // empty
168 }
169
170 ConstIterator& operator=(ConstIterator&& other) {
171 std::swap(heap, other.heap);
172 std::swap(index, other.index);
173 return *this;
174 }
175
176 ConstIterator& operator=(const ConstIterator& other) {
177 heap = other.heap;
178 index = other.index;
179 }
180
181 ConstIterator& operator++() {
182 if (index <= heap->count) {
183 ++index;
184 }
185 return *this;
186 }
187
188 bool operator==(const ConstIterator& other) const {
189 return heap == other.heap && index == other.index;
190 }
191
192 bool operator!=(const ConstIterator& other) const {
193 return !(*this == other);
194 }
195
196 const T& operator*() {
197 return *heap->data[index];
198 }
199
200 const T* operator->() {
201 return &(*heap->data[index]);
202 }
203 }; // class ConstIterator
204
205
206 protected:
207
208 std::vector<I> data;
209 HeapIndex count;
210 C comparator;
211
212 public:
213
214 IndIntruHeap() :
215 count(0)
216 {
217 // empty
218 }
219
220 IndIntruHeap(const IndIntruHeap<I,T,heap_info,C,K>& other) :
221 count(other.count)
222 {
223 for (HeapIndex i = 0; i < other.count; ++i) {
224 data.push_back(other.data[i]);
225 }
226 }
227
228 bool empty() const { return 0 == count; }
229
230 size_t size() const { return (size_t) count; }
231
232 T& top() { return *data[0]; }
233
234 const T& top() const { return *data[0]; }
235
236 I& top_ind() { return data[0]; }
237
238 const I& top_ind() const { return data[0]; }
239
240 void push(I&& item) {
241 HeapIndex i = count++;
242 intru_data_of(item) = i;
243 data.emplace_back(std::move(item));
244 sift_up(i);
245 }
246
247 void push(const I& item) {
248 I copy(item);
249 push(std::move(copy));
250 }
251
252 void pop() {
253 remove(HeapIndex(0));
254 }
255
256 void remove(Iterator& i) {
257 remove(i.index);
258 i = end();
259 }
260
261 Iterator find(const I& ind_item) {
262 for (HeapIndex i = 0; i < count; ++i) {
263 if (data[i] == ind_item) {
264 return Iterator(*this, i);
265 }
266 }
267 return end();
268 }
269
270 // when passing in value we do a comparison via operator==
271 Iterator find(const T& item) {
272 for (HeapIndex i = 0; i < count; ++i) {
273 if (*data[i] == item) {
274 return Iterator(*this, i);
275 }
276 }
277 return end();
278 }
279
280 // reverse find -- start looking from bottom of heap
281 Iterator rfind(const I& ind_item) {
282 // HeapIndex is unsigned, so we can't allow to go negative; so
283 // we'll keep it one more than actual index
284 for (HeapIndex i = count; i > 0; --i) {
285 if (data[i-1] == ind_item) {
286 return Iterator(*this, i-1);
287 }
288 }
289 return end();
290 }
291
292 // reverse find -- start looking from bottom of heap
293 Iterator rfind(const T& item) {
294 // HeapIndex is unsigned, so we can't allow to go negative; so
295 // we'll keep it one more than actual index
296 for (HeapIndex i = count; i > 0; --i) {
297 if (*data[i-1] == item) {
298 return Iterator(*this, i-1);
299 }
300 }
301 return end();
302 }
303
304 ConstIterator find(const I& ind_item) const {
305 for (HeapIndex i = 0; i < count; ++i) {
306 if (data[i] == ind_item) {
307 return ConstIterator(*this, i);
308 }
309 }
310 return cend();
311 }
312
313 // when passing in value we do a comparison via operator==
314 ConstIterator find(const T& item) const {
315 for (HeapIndex i = 0; i < count; ++i) {
316 if (*data[i] == item) {
317 return ConstIterator(*this, i);
318 }
319 }
320 return cend();
321 }
322
323 // reverse find -- start looking from bottom of heap
324 ConstIterator rfind(const I& ind_item) const {
325 // HeapIndex is unsigned, so we can't allow to go negative; so
326 // we'll keep it one more than actual index
327 for (HeapIndex i = count; i > 0; --i) {
328 if (data[i-1] == ind_item) {
329 return ConstIterator(*this, i-1);
330 }
331 }
332 return cend();
333 }
334
335 // reverse find -- start looking from bottom of heap
336 ConstIterator rfind(const T& item) const {
337 // HeapIndex is unsigned, so we can't allow to go negative; so
338 // we'll keep it one more than actual index
339 for (HeapIndex i = count; i > 0; --i) {
340 if (*data[i-1] == item) {
341 return ConstIterator(*this, i-1);
342 }
343 }
344 return cend();
345 }
346
347 Iterator at(const I& ind_item) {
348 auto ind = intru_data_of(ind_item);
349 if (ind >= count) {
350 throw std::out_of_range(
351 std::to_string(ind) + " >= " + std::to_string(count));
352 }
353 assert(data[ind] == ind_item);
354 return Iterator(*this, ind);
355 }
356
357 void promote(T& item) {
358 sift_up(item.*heap_info);
359 }
360
361 void demote(T& item) {
362 sift_down(item.*heap_info);
363 }
364
365 void adjust(T& item) {
366 sift(item.*heap_info);
367 }
368
369 Iterator begin() {
370 return Iterator(*this, 0);
371 }
372
373 Iterator end() {
374 return Iterator(*this, count);
375 }
376
377 ConstIterator cbegin() const {
378 return ConstIterator(*this, 0);
379 }
380
381 ConstIterator cend() const {
382 return ConstIterator(*this, count);
383 }
384
385 friend std::ostream& operator<<(std::ostream& out, const IndIntruHeap& h) {
386 auto i = h.data.cbegin();
387 if (i != h.data.cend()) {
388 out << **i;
389 ++i;
390 while (i != h.data.cend()) {
391 out << ", " << **i;
392 }
393 }
394 return out;
395 }
396
397 // can only be called if I is copyable; copies heap into a vector
398 // and sorts it before displaying it
399 std::ostream&
400 display_sorted(std::ostream& out,
401 std::function<bool(const T&)> filter = all_filter) const {
402 static_assert(std::is_copy_constructible<I>::value,
403 "cannot call display_sorted when class I is not copy"
404 " constructible");
405 auto compare = [this] (const I first, const I second) -> bool {
406 return this->comparator(*first, *second);
407 };
408 std::vector<I> copy(data);
409 std::sort(copy.begin(), copy.end(), compare);
410
411 bool first = true;
412 for (auto c = copy.begin(); c != copy.end(); ++c) {
413 if (filter(**c)) {
414 if (!first) {
415 out << ", ";
416 } else {
417 first = false;
418 }
419 out << **c;
420 }
421 }
422
423 return out;
424 }
425
426
427 protected:
428
429 static IndIntruHeapData& intru_data_of(const I& item) {
430 return (*item).*heap_info;
431 }
432
433 void remove(HeapIndex i) {
434 std::swap(data[i], data[--count]);
435 intru_data_of(data[i]) = i;
436 data.pop_back();
437
438 // the following needs to be sift (and not sift_down) as it can
439 // go up or down the heap; imagine the heap vector contains 0,
440 // 10, 100, 20, 30, 200, 300, 40; then 200 is removed, and 40
441 // would have to be sifted upwards
442 // sift(i);
443 sift(i);
444 }
445
446 // default value of filter parameter to display_sorted
447 static bool all_filter(const T& data) { return true; }
448
449 // when i is negative?
450 static inline HeapIndex parent(HeapIndex i) {
451 assert(0 != i);
452 return (i - 1) / K;
453 }
454
455 // index of left child when K==2, index of left-most child when K>2
456 static inline HeapIndex lhs(HeapIndex i) { return K*i + 1; }
457
458 // index of right child when K==2, index of right-most child when K>2
459 static inline HeapIndex rhs(HeapIndex i) { return K*i + K; }
460
461 void sift_up(HeapIndex i) {
462 while (i > 0) {
463 HeapIndex pi = parent(i);
464 if (!comparator(*data[i], *data[pi])) {
465 break;
466 }
467
468 std::swap(data[i], data[pi]);
469 intru_data_of(data[i]) = i;
470 intru_data_of(data[pi]) = pi;
471 i = pi;
472 }
473 } // sift_up
474
475 // use this sift_down definition when K>2; it's more general and
476 // uses a loop; EnableBool insures template uses a template
477 // parameter
478 template<bool EnableBool=true>
479 typename std::enable_if<(K>2)&&EnableBool,void>::type sift_down(HeapIndex i) {
480 if (i >= count) return;
481 while (true) {
482 HeapIndex li = lhs(i);
483
484 if (li < count) {
485 HeapIndex ri = std::min(rhs(i), count - 1);
486
487 // find the index of min. child
488 HeapIndex min_i = li;
489 for (HeapIndex k = li + 1; k <= ri; ++k) {
490 if (comparator(*data[k], *data[min_i])) {
491 min_i = k;
492 }
493 }
494
495 if (comparator(*data[min_i], *data[i])) {
496 std::swap(data[i], data[min_i]);
497 intru_data_of(data[i]) = i;
498 intru_data_of(data[min_i]) = min_i;
499 i = min_i;
500 } else {
501 // no child is smaller
502 break;
503 }
504 } else {
505 // no children
506 break;
507 }
508 }
509 } // sift_down
510
511 // use this sift_down definition when K==2; EnableBool insures
512 // template uses a template parameter
513 template<bool EnableBool=true>
514 typename std::enable_if<K==2&&EnableBool,void>::type sift_down(HeapIndex i) {
515 if (i >= count) return;
516 while (true) {
517 const HeapIndex li = lhs(i);
518 const HeapIndex ri = 1 + li;
519
520 if (li < count) {
521 if (comparator(*data[li], *data[i])) {
522 if (ri < count && comparator(*data[ri], *data[li])) {
523 std::swap(data[i], data[ri]);
524 intru_data_of(data[i]) = i;
525 intru_data_of(data[ri]) = ri;
526 i = ri;
527 } else {
528 std::swap(data[i], data[li]);
529 intru_data_of(data[i]) = i;
530 intru_data_of(data[li]) = li;
531 i = li;
532 }
533 } else if (ri < count && comparator(*data[ri], *data[i])) {
534 std::swap(data[i], data[ri]);
535 intru_data_of(data[i]) = i;
536 intru_data_of(data[ri]) = ri;
537 i = ri;
538 } else {
539 // no child is smaller
540 break;
541 }
542 } else {
543 // no children
544 break;
545 }
546 } // while
547 } // sift_down
548
549 void sift(HeapIndex i) {
550 if (i == 0) {
551 // if we're at top, can only go down
552 sift_down(i);
553 } else {
554 HeapIndex pi = parent(i);
555 if (comparator(*data[i], *data[pi])) {
556 // if we can go up, we will
557 sift_up(i);
558 } else {
559 // otherwise we'll try to go down
560 sift_down(i);
561 }
562 }
563 } // sift
564 }; // class IndIntruHeap
565
566 } // namespace crimson