LibSWOC++ 1.5.14
Solid Wall of C++
Loading...
Searching...
No Matches
DiscreteRange.h
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2014 Network Geographics
9
10#pragma once
11#include <algorithm>
12#include <limits>
13#include <functional>
14
15#include "swoc/swoc_version.h"
16#include "swoc/swoc_meta.h"
17#include "swoc/RBTree.h"
18#include "swoc/MemArena.h"
19
20
21namespace swoc { inline namespace SWOC_VERSION_NS {
22namespace detail {
23
27
35template <typename M>
36constexpr auto
38 return std::numeric_limits<M>::max();
39}
40
48template <typename M>
49constexpr auto
50maximum(meta::CaseTag<1>) -> decltype(M::MAX) {
51 return M::MAX;
52}
53
59template <typename M>
60constexpr M
62 return maximum<M>(meta::CaseArg);
63}
64
72template <typename M>
73constexpr auto
75 return std::numeric_limits<M>::min();
76}
77
85template <typename M>
86constexpr auto
87minimum(meta::CaseTag<1>) -> decltype(M::MIN) {
88 return M::MIN;
89}
90
96template <typename M>
97constexpr M
99 return minimum<M>(meta::CaseArg);
100}
101
102} // namespace detail
103
113
121
139template <typename T> class DiscreteRange {
140 using self_type = DiscreteRange;
141
142protected:
145
146public:
147 using metric_type = T;
150
154 constexpr DiscreteRange() : _min(detail::maximum<T>()), _max(detail::minimum<T>()) {}
155
161 constexpr DiscreteRange(T const &value) : _min(value), _max(value){};
162
168 constexpr DiscreteRange(T const &min, T const &max) : _min(min), _max(max) {}
169
170 ~DiscreteRange() = default;
171
177 bool empty() const;
178
185 self_type &assign(metric_type const &min, metric_type const &max);
186
194 self_type &assign(metric_type const &value);
195
203 self_type &assign_min(metric_type const &min);
204
212 self_type &assign_max(metric_type const &max);
213
220 self_type &clip_max();
221
226 metric_type const &min() const;
227
232 metric_type const &max() const;
233
235 bool operator==(self_type const &that) const;
236
238 bool operator!=(self_type const &that) const;
239
245 bool contains(metric_type const &value) const;
246
250 bool has_intersection_with(self_type const &that) const;
251
259 self_type intersection(self_type const &that) const;
260
265 bool is_adjacent_to(self_type const &that) const;
266
272 bool is_left_adjacent_to(self_type const &that) const;
273
281 bool has_union(self_type const &that) const;
282
287 bool is_superset_of(self_type const &that) const;
288
293 bool is_subset_of(self_type const &that) const;
294
300 bool is_strict_superset_of(self_type const &that) const;
301
307 bool is_strict_subset_of(self_type const &that) const;
308
313 Relation relationship(self_type const &that) const;
314
327 EdgeRelation left_edge_relationship(self_type const &that) const;
328
333 self_type hull(self_type const &that) const;
334
336 bool is_singleton() const;
337
341 bool operator!() const;
342
347 explicit operator bool() const;
348
352 bool is_maximal() const;
353
357 self_type &operator&=(self_type const &that);
358
363 self_type &operator|=(self_type const &that);
364
366 self_type &clear();
367
383 using first_argument_type = self_type;
384 using second_argument_type = self_type;
385 using result_type = bool;
386
388 bool operator()(self_type const &lhs, self_type const &rhs) const;
389 };
390};
391
392template <typename T>
393bool
394DiscreteRange<T>::operator==(DiscreteRange::self_type const &that) const {
395 return _min == that._min && _max == that._max;
396}
397
398template <typename T>
399bool
400DiscreteRange<T>::operator!=(DiscreteRange::self_type const &that) const {
401 return _min != that._min | _max != that._max;
402}
403
404template <typename T>
405auto
407 --_max;
408 return *this;
409}
410
411template <typename T>
412bool
414 return _min > _max;
415}
416
417template <typename T> DiscreteRange<T>::operator bool() const {
418 return _min <= _max;
419}
420
421template <typename T>
422bool
424 return _min <= value && value <= _max;
425}
426
427template <typename T>
428auto
429DiscreteRange<T>::clear() -> self_type & {
432 return *this;
433}
434
435template <typename T>
436bool
437DiscreteRange<T>::lexicographic_order::operator()(DiscreteRange::self_type const &lhs, DiscreteRange::self_type const &rhs) const {
438 return lhs._min == rhs._min ? lhs._max < rhs._max : lhs._min < rhs._min;
439}
440
441template <typename T>
444 _min = min;
445 _max = max;
446 return *this;
447}
448
449template <typename T>
451DiscreteRange<T>::hull(DiscreteRange::self_type const &that) const {
452 // need to account for invalid ranges.
453 return !*this ? that : !that ? *this : self_type(std::min(_min, that._min), std::max(_max, that._max));
454}
455
456template <typename T>
457auto
459 if (_max < that._max) {
460 return ++metric_type(_max) < that._max ? EdgeRelation::GAP : EdgeRelation::ADJ;
461 }
462 return _min >= that._min ? EdgeRelation::NONE : EdgeRelation::OVLP;
463}
464
465template <typename T>
466auto
467DiscreteRange<T>::relationship(self_type const &that) const -> Relation {
468 Relation retval = Relation::NONE;
469 if (this->has_intersection_with(that)) {
470 if (*this == that)
471 retval = Relation::EQUAL;
472 else if (this->is_subset_of(that))
473 retval = Relation::SUBSET;
474 else if (this->is_superset_of(that))
475 retval = Relation::SUPERSET;
476 else
477 retval = Relation::OVERLAP;
478 } else if (this->is_adjacent_to(that)) {
479 retval = Relation::ADJACENT;
480 }
481 return retval;
482}
483
484template <typename T>
487 _min = singleton;
488 _max = singleton;
489 return *this;
490}
491
492template <typename T>
495 _min = min;
496 return *this;
497}
498
499template <typename T>
500bool
502 return _min == _max;
503}
504
505template <typename T>
506bool
508 return _min > _max;
509}
510
511template <typename T>
512bool
516
517template <typename T>
518bool
519DiscreteRange<T>::is_strict_superset_of(DiscreteRange::self_type const &that) const {
520 return (_min < that._min && that._max <= _max) || (_min <= that._min && that._max < _max);
521}
522
523template <typename T>
525DiscreteRange<T>::operator|=(DiscreteRange::self_type const &that) {
526 if (!*this) {
527 *this = that;
528 } else if (that) {
529 if (that._min < _min) {
530 _min = that._min;
531 }
532 if (that._max > _max) {
533 _max = that._max;
534 }
535 }
536 return *this;
537}
538
539template <typename T>
542 _max = max;
543 return *this;
544}
545
546template <typename T>
547bool
548DiscreteRange<T>::is_strict_subset_of(DiscreteRange::self_type const &that) const {
549 return that.is_strict_superset_of(*this);
550}
551
552template <typename T>
553bool
554DiscreteRange<T>::is_subset_of(DiscreteRange::self_type const &that) const {
555 return that.is_superset_of(*this);
556}
557
558template <typename T>
559T const &
561 return _min;
562}
563
564template <typename T>
565T const &
567 return _max;
568}
569
570template <typename T>
571bool
572DiscreteRange<T>::has_union(DiscreteRange::self_type const &that) const {
573 return this->has_intersection_with(that) || this->is_adjacent_to(that);
574}
575
576template <typename T>
578DiscreteRange<T>::operator&=(DiscreteRange::self_type const &that) {
579 *this = this->intersection(that);
580 return *this;
581}
582
583template <typename T>
584bool
585DiscreteRange<T>::has_intersection_with(DiscreteRange::self_type const &that) const {
586 return (that._min <= _min && _min <= that._max) || (_min <= that._min && that._min <= _max);
587}
588
589template <typename T>
590bool
591DiscreteRange<T>::is_superset_of(DiscreteRange::self_type const &that) const {
592 return _min <= that._min && that._max <= _max;
593}
594
595template <typename T>
596bool
597DiscreteRange<T>::is_adjacent_to(DiscreteRange::self_type const &that) const {
598 return this->is_left_adjacent_to(that) || that.is_left_adjacent_to(*this);
599}
600
601template <typename T>
602bool
603DiscreteRange<T>::is_left_adjacent_to(DiscreteRange::self_type const &that) const {
604 /* Need to be careful here. We don't know much about T and we certainly don't know if "t+1"
605 * even compiles for T. We do require the increment operator, however, so we can use that on a
606 * copy to get the equivalent of t+1 for adjacency testing. We must also handle the possibility
607 * T has a modulus and not depend on ++t > t always being true. However, we know that if t1 >
608 * t0 then ++t0 > t0.
609 */
610 metric_type max(_max); // some built in integer types don't support increment on rvalues.
611 return _max < that._min && ++max == that._min;
612}
613
614template <typename T>
616DiscreteRange<T>::intersection(DiscreteRange::self_type const &that) const {
617 return {std::max(_min, that._min), std::min(_max, that._max)};
618}
619
620template <typename T>
621bool
622operator==(DiscreteRange<T> const &lhs, DiscreteRange<T> const &rhs) {
623 return lhs.min() == rhs.min() && lhs.max() == rhs.max();
624}
625
630template <typename T>
631bool
633 return !(lhs == rhs);
634}
635
646template <typename T>
647bool
649 return lhs.has_intersection_with(rhs);
650}
651
657template <typename T>
658inline bool
659operator<(DiscreteRange<T> const &lhs, DiscreteRange<T> const &rhs) {
660 return rhs.is_strict_superset_of(lhs);
661}
662
668template <typename T>
669inline bool
670operator<=(DiscreteRange<T> const &lhs, DiscreteRange<T> const &rhs) {
671 return rhs.is_superset_of(lhs);
672}
673
679template <typename T>
680inline bool
682 return lhs.is_strict_superset_of(rhs);
683}
684
690template <typename T>
691inline bool
693 return lhs.is_superset_of(rhs);
694}
695
708template <typename METRIC, typename PAYLOAD> class DiscreteSpace {
709 using self_type = DiscreteSpace;
710
711protected:
712 using metric_type = METRIC;
713 using payload_type = PAYLOAD;
714 using range_type = DiscreteRange<METRIC>;
715
717 class Node : public detail::RBNode {
718 using self_type = Node;
719 using super_type = detail::RBNode;
720 friend class DiscreteSpace;
721
722 range_type _range;
723 range_type _hull;
724 PAYLOAD _payload{};
725
726 public:
728 using Linkage = swoc::IntrusiveLinkageRebind<self_type, super_type::Linkage>;
729
730 Node() = default;
731
733 Node(range_type const &range, PAYLOAD const &payload) : _range(range), _payload(payload) {}
734
736 Node(METRIC const &min, METRIC const &max, PAYLOAD const &payload) : _range(min, max), _payload(payload) {}
737
739 PAYLOAD &payload();
740
746 self_type &assign(range_type const &range);
747
753 self_type &assign(PAYLOAD const &payload);
754
755 range_type const &
756 range() const {
757 return _range;
758 }
759
760 self_type &
761 assign_min(METRIC const &m, bool update_tree = true) {
762 _range.assign_min(m);
763 if (update_tree)
764 {
765 this->ripple_structure_fixup();
766 }
767 return *this;
768 }
769
770 self_type &
771 assign_max(METRIC const &m, bool update_tree = true) {
772 _range.assign_max(m);
773 if (update_tree)
774 {
775 this->ripple_structure_fixup();
776 }
777 return *this;
778 }
779
780 METRIC const &
781 min() const {
782 return _range.min();
783 }
784
785 METRIC const &
786 max() const {
787 return _range.max();
788 }
789
790 void structure_fixup() override;
791
792 self_type *
793 left() {
794 return static_cast<self_type *>(_left);
795 }
796
797 self_type *
798 right() {
799 return static_cast<self_type *>(_right);
800 }
801 };
802
803 using Direction = typename Node::Direction;
804
805 Node *_root = nullptr;
806 IntrusiveDList<typename Node::Linkage> _list;
809
810 // Utility methods to avoid having casts scattered all over.
811 Node *
812 prev(Node *n) {
813 return Node::Linkage::prev_ptr(n);
814 }
815
816 Node *
817 next(Node *n) {
818 return Node::Linkage::next_ptr(n);
819 }
820
821 Node *
822 left(Node *n) {
823 return static_cast<Node *>(n->_left);
824 }
825
826 Node *
827 right(Node *n) {
828 return static_cast<Node *>(n->_right);
829 }
830
831public:
832 using iterator = typename decltype(_list)::iterator;
833 using const_iterator = typename decltype(_list)::const_iterator;
834
835 DiscreteSpace() = default;
836
837 ~DiscreteSpace();
838
845 self_type &mark_bulk(std::vector<std::pair<range_type, PAYLOAD>> &marks, bool is_sorted = false);
846
854 self_type &mark_bulk(std::pair<range_type, PAYLOAD>* start, size_t n, bool is_sorted = false);
855
865 self_type &mark(range_type const &range, PAYLOAD const &payload, bool update_tree = true);
866
874 self_type &erase(range_type const &range);
875
893 template <typename F, typename U = PAYLOAD> self_type &blend(range_type const &range, U const &color, F &&blender);
894
904 self_type &fill(range_type const &range, PAYLOAD const &payload);
905
911 iterator find(METRIC const &metric);
912
918 const_iterator find(METRIC const &metric) const;
919
925 iterator lower_bound(METRIC const &m);
926
932 iterator upper_bound(METRIC const &m);
933
942 std::pair<iterator, iterator> intersection(range_type const &range);
943
945 size_t count() const;
946
948 bool empty() const;
949
951 iterator
953 return _list.begin();
954 }
955
956 iterator
957 end() {
958 return _list.end();
959 }
960
961 const_iterator
962 begin() const {
963 return _list.begin();
964 }
965
966 const_iterator
967 end() const {
968 return _list.end();
969 }
970
972 void clear();
973
974protected:
981 Node *lower_node(METRIC const &target);
982
989 Node *upper_node(METRIC const &target);
990
992 Node *head();
993
995 Node *tail();
996
1003 void insert_before(Node *spot, Node *node, bool update_tree = true);
1004
1011 void insert_after(Node *spot, Node *node, bool update_tree = true);
1012
1020 void prepend(Node *node, bool update_tree = true);
1021
1029 void append(Node *node, bool update_tree = true);
1030
1036 void remove(Node *node, bool update_tree = true);
1037};
1038
1039// ---
1040
1041template <typename METRIC, typename PAYLOAD>
1042PAYLOAD &
1046
1047template <typename METRIC, typename PAYLOAD>
1048auto
1049DiscreteSpace<METRIC, PAYLOAD>::Node::assign(DiscreteSpace::range_type const &range) -> self_type & {
1050 _range = range;
1051 return *this;
1052}
1053
1054template <typename METRIC, typename PAYLOAD>
1055auto
1057 _payload = payload;
1058 return *this;
1059}
1060
1061template <typename METRIC, typename PAYLOAD>
1062void
1064 // Invariant: The hulls of all children are correct.
1065 if (_left && _right) {
1066 // If both children, local range must be inside the hull of the children and irrelevant.
1067 _hull.assign(this->left()->_hull.min(), this->right()->_hull.max());
1068 } else if (_left) {
1069 _hull.assign(this->left()->_hull.min(), _range.max());
1070 } else if (_right) {
1071 _hull.assign(_range.min(), this->right()->_hull.max());
1072 } else {
1073 _hull = _range;
1074 }
1075}
1076
1077// ---
1078// Discrete Space
1079// ---
1080
1081template <typename METRIC, typename PAYLOAD> DiscreteSpace<METRIC, PAYLOAD>::~DiscreteSpace() {
1082 // Destruct all the payloads - the nodes themselves are in the arena and disappear with it.
1083 for (auto &node : _list) {
1084 std::destroy_at(&node.payload());
1085 }
1086}
1087
1088template <typename METRIC, typename PAYLOAD>
1089size_t
1091 return _list.count();
1092}
1093
1094template <typename METRIC, typename PAYLOAD>
1095bool
1097 return _list.empty();
1098}
1099
1100template <typename METRIC, typename PAYLOAD>
1101auto
1103 return static_cast<Node *>(_list.head());
1104}
1105
1106template <typename METRIC, typename PAYLOAD>
1107auto
1109 return static_cast<Node *>(_list.tail());
1110}
1111
1112template <typename METRIC, typename PAYLOAD>
1113auto
1114DiscreteSpace<METRIC, PAYLOAD>::find(METRIC const &metric) -> iterator {
1115 auto n = _root; // current node to test.
1116 while (n) {
1117 if (metric < n->min()) {
1118 if (n->_hull.contains(metric)) {
1119 n = n->left();
1120 } else {
1121 return this->end();
1122 }
1123 } else if (n->max() < metric) {
1124 if (n->_hull.contains(metric)) {
1125 n = n->right();
1126 } else {
1127 return this->end();
1128 }
1129 } else {
1130 return _list.iterator_for(n);
1131 }
1132 }
1133 return this->end();
1134}
1135
1136template <typename METRIC, typename PAYLOAD>
1137auto
1138DiscreteSpace<METRIC, PAYLOAD>::find(METRIC const &metric) const -> const_iterator {
1139 return const_cast<self_type *>(this)->find(metric);
1140}
1141
1142template <typename METRIC, typename PAYLOAD>
1143auto
1145 Node *n = _root; // current node to test.
1146 Node *zret = nullptr; // best node so far.
1147
1148 // Fast check for sequential insertion
1149 if (auto ln = _list.tail(); ln != nullptr && ln->max() < target) {
1150 return ln;
1151 }
1152
1153 while (n) {
1154 if (target < n->min()) {
1155 n = left(n);
1156 } else {
1157 zret = n; // this is a better candidate.
1158 if (n->max() < target) {
1159 n = right(n);
1160 } else {
1161 break;
1162 }
1163 }
1164 }
1165 return zret;
1166}
1167
1168template <typename METRIC, typename PAYLOAD>
1169auto
1171 Node *n = _root; // current node to test.
1172 Node *zret = nullptr; // best node so far.
1173
1174 // Fast check if there is no range past @a target.
1175 if (auto ln = _list.tail(); ln == nullptr || ln->min() <= target) {
1176 return nullptr;
1177 }
1178
1179 while (n) {
1180 if (target > n->min()) {
1181 n = right(n);
1182 } else {
1183 zret = n; // this is a better candidate.
1184 if (n->min() > target) {
1185 n = left(n);
1186 } else {
1187 break;
1188 }
1189 }
1190 }
1191
1192 // Must be past any intersecting range due to STL iteration being half open.
1193 if (zret && (zret->min() <= target)) {
1194 zret = next(zret);
1195 }
1196
1197 return zret;
1198}
1199
1200template <typename METRIC, typename PAYLOAD>
1201auto
1203 auto n = this->lower_node(m);
1204 return n ? iterator(n) : this->end();
1205}
1206
1207template <typename METRIC, typename PAYLOAD>
1208auto
1210 auto n = this->upper_node(m);
1211 return n ? iterator(n) : this->end();
1212}
1213
1214template <typename METRIC, typename PAYLOAD>
1215auto
1216DiscreteSpace<METRIC, PAYLOAD>::intersection(DiscreteSpace::range_type const &range) -> std::pair<iterator, iterator> {
1217 // Quick checks for null intersection
1218 if (this->head() == nullptr || // empty
1219 this->head()->_range.min() > range.max() || // before first range
1220 this->tail()->_range.max() < range.min() // after last range
1221 ) {
1222 return {this->end(), this->end()};
1223 }
1224 // Invariant - the search range intersects the convex hull of the ranges. This doesn't require the search
1225 // range to intersect any of the actual ranges.
1226
1227 auto *lower = this->lower_node(range.min());
1228 auto *upper = this->upper_node(range.max());
1229
1230 if (lower == nullptr) {
1231 lower = this->head();
1232 } else if (lower->_range.max() < range.min()) {
1233 lower = next(lower); // lower is before @a range so @c next must be at or past.
1234 if (lower->_range.min() > range.max()) { // @a range is in an uncolored gap.
1235 return {this->end(), this->end()};
1236 }
1237 }
1238
1239 return {_list.iterator_for(lower), upper ? _list.iterator_for(upper) : this->end()};
1240}
1241
1242template <typename METRIC, typename PAYLOAD>
1243void
1245
1246 if (update_tree) {
1247 if (!_root) {
1248 _root = node;
1249 } else {
1250 _root = static_cast<Node *>(_list.head()->set_child(node, Direction::LEFT)->rebalance_after_insert());
1251 }
1252 }
1253
1254 _list.prepend(node);
1255}
1256
1257template <typename METRIC, typename PAYLOAD>
1258void
1260
1261 if (update_tree) {
1262 if (!_root) {
1263 _root = node;
1264 } else {
1265 // The last node has no right child, or it wouldn't be the last.
1266 _root = static_cast<Node *>(_list.tail()->set_child(node, Direction::RIGHT)->rebalance_after_insert());
1267 }
1268 }
1269
1270 _list.append(node);
1271}
1272
1273template <typename METRIC, typename PAYLOAD>
1274void
1276 _list.erase(node);
1277 _fa.destroy(node);
1278 if (!update_tree) {
1279 return;
1280 }
1281 _root = static_cast<Node *>(node->remove());
1282}
1283
1284template <typename METRIC, typename PAYLOAD>
1285void
1287
1288 if (update_tree) {
1289 if (left(spot) == nullptr) {
1290 spot->set_child(node, Direction::LEFT);
1291 } else {
1292 // If there's a left child, there's a previous node, therefore spot->_prev is valid.
1293 // Further, the previous node must be the rightmost descendant node of the left subtree
1294 // and therefore has no current right child.
1295 spot->_prev->set_child(node, Direction::RIGHT);
1296 }
1297 }
1298
1299 _list.insert_before(spot, node);
1300
1301 if (update_tree) {
1302 _root = static_cast<Node *>(node->rebalance_after_insert());
1303 }
1304}
1305
1306template <typename METRIC, typename PAYLOAD>
1307void
1309
1310 if (update_tree) {
1311 if (right(spot) == nullptr) {
1312 spot->set_child(node, Direction::RIGHT);
1313 } else {
1314 // If there's a right child, there's a successor node, and therefore @a _next is valid.
1315 // Further, the successor node must be the left most descendant of the right subtree
1316 // therefore it doesn't have a left child.
1317 spot->_next->set_child(node, Direction::LEFT);
1318 }
1319 }
1320
1321 _list.insert_after(spot, node);
1322
1323 if (update_tree) {
1324 _root = static_cast<Node *>(node->rebalance_after_insert());
1325 }
1326}
1327
1328template <typename METRIC, typename PAYLOAD>
1330DiscreteSpace<METRIC, PAYLOAD>::erase(DiscreteSpace::range_type const &range) {
1331 Node *n = this->lower_node(range.min()); // current node.
1332 while (n) {
1333 auto nn = next(n); // cache in case @a n disappears.
1334 if (n->min() > range.max()) { // cleared the target range, done.
1335 break;
1336 }
1337
1338 if (n->max() >= range.min()) { // some overlap
1339 if (n->max() <= range.max()) { // pure left overlap, clip.
1340 if (n->min() >= range.min()) { // covered, remove.
1341 this->remove(n);
1342 } else { // stub on the left, clip to that.
1343 n->assign_max(--metric_type{range.min()});
1344 }
1345 } else if (n->min() >= range.min()) { // pure left overlap, clip.
1346 n->assign_min(++metric_type{range.max()});
1347 } else { // @a n covers @a range, must split.
1348 auto y = _fa.make(range_type{n->min(), --metric_type{range.min()}}, n->payload());
1349 n->assign_min(++metric_type{range.max()});
1350 this->insert_before(n, y);
1351 break;
1352 }
1353 }
1354 n = nn;
1355 }
1356 return *this;
1357}
1358
1359template <typename METRIC, typename PAYLOAD>
1361DiscreteSpace<METRIC, PAYLOAD>::mark_bulk(std::vector<std::pair<DiscreteSpace::range_type, PAYLOAD>> &ranges, bool is_sorted) {
1362 return this->mark_bulk(ranges.data(), ranges.size(), is_sorted);
1363}
1364
1365template <typename METRIC, typename PAYLOAD>
1367DiscreteSpace<METRIC, PAYLOAD>::mark_bulk(std::pair<range_type, PAYLOAD>* start, size_t n, bool is_sorted)
1368{
1369 // Sort the input data in-place before processing, if applicable.
1370 if (!is_sorted)
1371 {
1372 // Stable sort allows for duplicate elements.
1373 std::stable_sort(start, start + n, [](const auto &lhs, const auto &rhs) {
1374 if (lhs.first.min() != rhs.first.min()) {
1375 return lhs.first.min() < rhs.first.min();
1376 }
1377 return lhs.first.max() < rhs.first.max();
1378 });
1379 }
1380
1381 // Verify that this is purely an append operation.
1382 // If it's not, then we need to rebuild the entire tree each iteration (suboptimal case).
1383 bool isAppend = !_list.empty() && _list.tail()->max() < start[0].first.min();
1384
1385 // Mark the ranges in the input data.
1386 for (size_t i = 0; i < n; ++i)
1387 {
1388 auto const& [range, payload] = start[i];
1389 this->mark(range, payload, !isAppend);
1390 }
1391
1392 // Rebuild the entire red-black tree.
1393 detail::RBNode* temp_head = _list.head();
1394 _root = static_cast<Node *>(detail::RBNode::buildTree(temp_head, _list.count()));
1395
1396 return *this;
1397}
1398
1399template <typename METRIC, typename PAYLOAD>
1401DiscreteSpace<METRIC, PAYLOAD>::mark(DiscreteSpace::range_type const &range, PAYLOAD const &payload, bool update_tree) {
1402 Node *n = this->lower_node(range.min()); // current node.
1403 Node *x = nullptr; // New node, gets set if we re-use an existing one.
1404 Node *y = nullptr; // Temporary for removing and advancing.
1405
1406 // Use carefully, only in situations where it is known there is no overflow.
1407 auto max_plus_1 = ++metric_type{range.max()};
1408
1409 /* We have lots of special cases here primarily to minimize memory
1410 allocation by re-using an existing node as often as possible.
1411 */
1412 if (n) {
1413 // Watch for wrap.
1414 auto min_minus_1 = --metric_type{range.min()};
1415 if (n->min() == range.min()) {
1416 // Could be another span further left which is adjacent.
1417 // Coalesce if the data is the same. min_minus_1 is OK because
1418 // if there is a previous range, min is not zero.
1419 Node *p = prev(n);
1420 if (p && p->payload() == payload && p->max() == min_minus_1) {
1421 x = p;
1422 n = x; // need to back up n because frame of reference moved.
1423 x->assign_max(range.max(), update_tree);
1424 } else if (n->max() <= range.max()) {
1425 // Span will be subsumed by request span so it's available for use.
1426 x = n;
1427 x->assign_max(range.max()).assign(payload);
1428 } else if (n->payload() == payload) {
1429 return *this; // request is covered by existing span with the same data
1430 } else {
1431 // request span is covered by existing span.
1432 x = _fa.make(range, payload); //
1433 n->assign_min(max_plus_1, update_tree); // clip existing.
1434 this->insert_before(n, x, update_tree);
1435 return *this;
1436 }
1437 } else if (n->payload() == payload && n->max() >= min_minus_1) {
1438 // min_minus_1 is safe here because n->min() < min so min is not zero.
1439 x = n;
1440 // If the existing span covers the requested span, we're done.
1441 if (x->max() >= range.max()) {
1442 return *this;
1443 }
1444 x->assign_max(range.max(), update_tree);
1445 } else if (n->max() <= range.max()) {
1446 // Can only have left skew overlap, otherwise disjoint.
1447 // Clip if overlap.
1448 if (n->max() >= range.min()) {
1449 n->assign_max(min_minus_1, update_tree);
1450 } else if (nullptr != (y = next(n)) && y->max() <= range.max()) {
1451 // because @a n was selected as the minimum it must be the case that
1452 // y->min >= min (or y would have been selected). Therefore in this
1453 // case the request covers the next node therefore it can be reused.
1454 x = y;
1455 x->assign(range).assign(payload);
1456 if (update_tree)
1457 {
1459 }
1460 n = x; // this gets bumped again, which is correct.
1461 }
1462 } else {
1463 // Existing span covers new span but with a different payload.
1464 // We split it, put the new span in between and we're done.
1465 // max_plus_1 is valid because n->max() > max.
1466 Node *r;
1467 x = _fa.make(range, payload);
1468 r = _fa.make(range_type{max_plus_1, n->max()}, n->payload());
1469 n->assign_max(min_minus_1, update_tree);
1470 this->insert_after(n, x, update_tree);
1471 this->insert_after(x, r, update_tree);
1472 return *this; // done.
1473 }
1474 n = next(n); // lower bound span handled, move on.
1475 if (!x) {
1476 x = _fa.make(range, payload);
1477 if (n) {
1478 this->insert_before(n, x, update_tree);
1479 } else {
1480 this->append(x, update_tree); // note that since n == 0 we'll just return.
1481 }
1482 }
1483 } else if (nullptr != (n = this->head()) && // at least one node in tree.
1484 n->payload() == payload && // payload matches
1485 (n->max() <= range.max() || n->min() <= max_plus_1) // overlap or adj.
1486 ) {
1487 // Same payload with overlap, re-use.
1488 x = n;
1489 n = next(n);
1490 x->assign_min(range.min(), update_tree);
1491 if (x->max() < range.max()) {
1492 x->assign_max(range.max(), update_tree);
1493 }
1494 } else {
1495 x = _fa.make(range, payload);
1496 this->prepend(x, update_tree);
1497 }
1498
1499 // At this point, @a x has the node for this span and all existing spans of
1500 // interest start at or past this span.
1501 while (n) {
1502 if (n->max() <= range.max()) { // completely covered, drop span, continue
1503 y = n;
1504 n = next(n);
1505 this->remove(y, update_tree);
1506 } else if (max_plus_1 < n->min()) { // no overlap, done.
1507 break;
1508 } else if (n->payload() == payload) { // skew overlap or adj., same payload
1509 x->assign_max(n->max(), update_tree);
1510 y = n;
1511 n = next(n);
1512 this->remove(y, update_tree);
1513 } else if (n->min() <= range.max()) { // skew overlap different payload
1514 n->assign_min(max_plus_1, update_tree);
1515 break;
1516 } else { // n->min() > range.max(), different payloads - done.
1517 break;
1518 }
1519 }
1520
1521 return *this;
1522}
1523
1524template <typename METRIC, typename PAYLOAD>
1526DiscreteSpace<METRIC, PAYLOAD>::fill(DiscreteSpace::range_type const &range, PAYLOAD const &payload) {
1527 // Rightmost node of interest with n->min() <= min.
1528 Node *n = this->lower_node(range.min());
1529 Node *x = nullptr; // New node (if any).
1530 // Need copies because we will modify these.
1531 auto min = range.min();
1532 auto max = range.max();
1533
1534 // Handle cases involving a node of interest to the left of the
1535 // range.
1536 if (n) {
1537 if (n->min() < min) {
1538 auto min_1 = min;
1539 --min_1; // dec is OK because min isn't zero.
1540 if (n->max() < min_1) { // no overlap, not adjacent.
1541 n = next(n);
1542 } else if (n->max() >= max) { // incoming range is covered, just discard.
1543 return *this;
1544 } else if (n->payload() != payload) { // different payload, clip range on left.
1545 min = n->max();
1546 ++min;
1547 n = next(n);
1548 } else { // skew overlap or adjacent to predecessor with same payload, use node and continue.
1549 x = n;
1550 n = next(n);
1551 }
1552 }
1553 } else {
1554 n = this->head();
1555 }
1556
1557 // Work through the rest of the nodes of interest.
1558 // Invariant: n->min() >= min
1559
1560 // Careful here -- because max_plus1 might wrap we need to use it only if we can be certain it
1561 // didn't. This is done by ordering the range tests so that when max_plus1 is used when we know
1562 // there exists a larger value than max.
1563 auto max_plus1 = max;
1564 ++max_plus1;
1565
1566 /* Notes:
1567 - max (and thence also max_plus1) never change during the loop.
1568 - we must have either x != 0 or adjust min but not both for each loop iteration.
1569 */
1570 while (n) {
1571 if (n->payload() == payload) {
1572 if (x) {
1573 if (n->max() <= max) { // next range is covered, so we can remove and continue.
1574 this->remove(n);
1575 n = next(x);
1576 } else if (n->min() <= max_plus1) {
1577 // Overlap or adjacent with larger max - absorb and finish.
1578 x->assign_max(n->max());
1579 this->remove(n);
1580 return *this;
1581 } else {
1582 // have the space to finish off the range.
1583 x->assign_max(max);
1584 return *this;
1585 }
1586 } else { // not carrying a span.
1587 if (n->max() <= max) { // next range is covered - use it.
1588 x = n;
1589 x->assign_min(min);
1590 n = next(n);
1591 } else if (n->min() <= max_plus1) {
1592 n->assign_min(min);
1593 return *this;
1594 } else { // no overlap, space to complete range.
1595 this->insert_before(n, _fa.make(min, max, payload));
1596 return *this;
1597 }
1598 }
1599 } else { // different payload
1600 if (x) {
1601 if (max < n->min()) { // range ends before n starts, done.
1602 x->assign_max(max);
1603 return *this;
1604 } else if (max <= n->max()) { // range ends before n, done.
1605 x->assign_max(--metric_type(n->min()));
1606 return *this;
1607 } else { // n is contained in range, skip over it.
1608 x->assign_max(--metric_type(n->min()));
1609 x = nullptr;
1610 min = n->max();
1611 ++min; // OK because n->max() maximal => next is null.
1612 n = next(n);
1613 }
1614 } else { // no carry node.
1615 if (max < n->min()) { // entirely before next span.
1616 this->insert_before(n, _fa.make(min, max, payload));
1617 return *this;
1618 } else {
1619 if (min < n->min()) { // leading section, need node.
1620 auto y = _fa.make(min, --metric_type(n->min()), payload);
1621 this->insert_before(n, y);
1622 }
1623 if (max <= n->max()) { // nothing past node
1624 return *this;
1625 }
1626 min = n->max();
1627 ++min;
1628 n = next(n);
1629 }
1630 }
1631 }
1632 }
1633 // Invariant: min is larger than any existing range maximum.
1634 if (x) {
1635 x->assign_max(max);
1636 } else {
1637 this->append(_fa.make(min, max, payload));
1638 }
1639 return *this;
1640}
1641
1642template <typename METRIC, typename PAYLOAD>
1643template <typename F, typename U>
1644auto
1645DiscreteSpace<METRIC, PAYLOAD>::blend(DiscreteSpace::range_type const &range, U const &color, F &&blender) -> self_type & {
1646 // Do a base check for the color to use on unmapped values. If self blending on @a color
1647 // is @c false, then do not color currently unmapped values.
1648 PAYLOAD plain_color{}; // color to paint uncolored metrics.
1649 bool plain_color_p = blender(plain_color, color); // start with default and blend in @a color.
1650
1651 auto node_cleaner = [&](Node *ptr) -> void { _fa.destroy(ptr); };
1652 // Used to hold a temporary blended node - @c release if put in space, otherwise cleaned up.
1653 using unique_node = std::unique_ptr<Node, decltype(node_cleaner)>;
1654
1655 // Rightmost node of interest with n->min() <= range.min().
1656 Node *n = this->lower_node(range.min());
1657
1658 // This doesn't change, compute outside loop.
1659 auto range_max_plus_1 = range.max();
1660 ++range_max_plus_1; // only use in contexts where @a max < METRIC max value.
1661
1662 // Update every loop to track what remains to be filled.
1663 auto remaining = range;
1664
1665 if (nullptr == n) {
1666 n = this->head();
1667 }
1668
1669 // Process @a n, covering the values from the previous range to @a n.max
1670 while (n) {
1671 // If there's no overlap, skip because this will be checked next loop, or it's the last node
1672 // and will be checked post loop. Loop logic is simpler if n->max() >= remaining.min()
1673 if (n->max() < remaining.min()) {
1674 n = next(n);
1675 continue;
1676 }
1677 // Invariant - n->max() >= remaining.min();
1678
1679 // Check for left extension. If found, clip that node to be adjacent and put in a
1680 // temporary that covers the overlap with the original payload.
1681 if (n->min() < remaining.min()) {
1682 // @a fill is inserted iff n->max() < remaining.max(), in which case the max is correct.
1683 // This is needed in other cases only for the color blending result.
1684 unique_node fill{_fa.make(remaining.min(), n->max(), n->payload()), node_cleaner};
1685 bool fill_p = blender(fill->payload(), color); // fill or clear?
1686
1687 if (fill_p) {
1688 bool same_color_p = fill->payload() == n->payload();
1689 if (same_color_p && n->max() >= remaining.max()) {
1690 return *this; // incoming range is completely covered by @a n in the same color, done.
1691 }
1692 if (!same_color_p) {
1693 auto fn = fill.release();
1694 auto n_max = n->max(); // save this so @a n can be clipped.
1695 n->assign_max(--metric_type(remaining.min())); // clip @a n down.
1696 this->insert_after(n, fn); // add intersection node in different color.
1697 if (n_max > remaining.max()) { // right extent too - split and done.
1698 fn->assign_max(remaining.max()); // fill node stops at end of target range.
1699 this->insert_after(fn, _fa.make(++metric_type(remaining.max()), n_max, n->payload()));
1700 return *this;
1701 }
1702 n = fn; // skip to use new node as current node.
1703 }
1704 remaining.assign_min(++metric_type(n->max())); // going to fill up n->max(), clip target.
1705 } else { // clear, don't fill.
1706 auto n_r = n->range(); // cache to avoid ordering requirements.
1707 if (n_r.max() > remaining.max()) { // overhang on the right, must split.
1708 fill.release(); // not going to use it,
1709 n->assign_min(++metric_type(remaining.max()));
1710 this->insert_before(n, _fa.make(n_r.min(), --metric_type(remaining.min()), n->payload()));
1711 return *this;
1712 }
1713 n->assign_max(--metric_type(remaining.min())); // clip @a n down.
1714 if (n_r.max() == remaining.max()) {
1715 return *this;
1716 }
1717 remaining.assign_min(++metric_type(n_r.max()));
1718 }
1719 continue;
1720 }
1721
1722 Node *pred = prev(n);
1723 // invariant
1724 // - remaining.min() <= n->max()
1725 // - !pred || pred->max() < remaining.min()
1726
1727 // Calculate and cache key relationships between @a n and @a remaining.
1728
1729 // @a n extends past @a remaining, so the trailing segment must be dealt with.
1730 bool right_ext_p = n->max() > remaining.max();
1731 // @a n strictly right overlaps with @a remaining.
1732 bool right_overlap_p = remaining.contains(n->min());
1733 // @a n is adjacent on the right to @a remaining.
1734 bool right_adj_p = !right_overlap_p && remaining.is_left_adjacent_to(n->range());
1735 // @a n has the same color as would be used for unmapped values.
1736 bool n_plain_colored_p = plain_color_p && (n->payload() == plain_color);
1737
1738 // Check for no right overlap - that means @a n is past the target range.
1739 // It may be possible to extend @a n or the previous range to cover
1740 // the target range. Regardless, all of @a range can be filled at this point.
1741 if (!right_overlap_p) {
1742 // @a pred has the same color as would be used for unmapped values
1743 // and is adjacent to @a remaining.
1744 bool pred_plain_colored_p = pred && ++metric_type(pred->max()) == remaining.min() && pred->payload() == plain_color;
1745
1746 if (right_adj_p && n_plain_colored_p) { // can pull @a n left to cover
1747 n->assign_min(remaining.min());
1748 if (pred_plain_colored_p) { // if that touches @a pred with same color, collapse.
1749 auto pred_min = pred->min();
1750 this->remove(pred);
1751 n->assign_min(pred_min);
1752 }
1753 } else if (pred_plain_colored_p) { // can pull @a pred right to cover.
1754 pred->assign_max(remaining.max());
1755 } else if (!remaining.empty() && plain_color_p) { // Must add new range if plain color valid.
1756 this->insert_before(n, _fa.make(remaining.min(), remaining.max(), plain_color));
1757 }
1758 return *this;
1759 }
1760
1761 // Invariant: @n has right overlap with @a remaining
1762
1763 // If there's a gap on the left, fill from @a r.min to @a n.min - 1
1764 if (plain_color_p && remaining.min() < n->min()) {
1765 if (n->payload() == plain_color) {
1766 if (pred && pred->payload() == n->payload()) {
1767 auto pred_min{pred->min()};
1768 this->remove(pred);
1769 n->assign_min(pred_min);
1770 } else {
1771 n->assign_min(remaining.min());
1772 }
1773 } else {
1774 auto n_min_minus_1{n->min()};
1775 --n_min_minus_1;
1776 if (pred && pred->payload() == plain_color) {
1777 pred->assign_max(n_min_minus_1);
1778 } else {
1779 this->insert_before(n, _fa.make(remaining.min(), n_min_minus_1, plain_color));
1780 }
1781 }
1782 }
1783
1784 // Invariant: Space in @a range and to the left of @a n has been filled.
1785
1786 // Create a node with the blend for the overlap and then update / replace @a n as needed.
1787 auto max{right_ext_p ? remaining.max() : n->max()}; // smallest boundary of range and @a n.
1788 unique_node fill{_fa.make(n->min(), max, n->payload()), node_cleaner};
1789 bool fill_p = blender(fill->payload(), color); // fill or clear?
1790 auto next_n = next(n); // cache this in case @a n is removed.
1791 remaining.assign_min(++METRIC{fill->max()}); // Update what is left to fill.
1792
1793 // Clean up the range for @a n
1794 if (fill_p) {
1795 // Check if @a pred is suitable for extending right to cover the target range.
1796 bool pred_adj_p =
1797 nullptr != (pred = prev(n)) && pred->range().is_left_adjacent_to(fill->range()) && pred->payload() == fill->payload();
1798
1799 if (right_ext_p) {
1800 if (n->payload() == fill->payload()) {
1801 n->assign_min(fill->min());
1802 } else {
1803 n->assign_min(range_max_plus_1);
1804 if (pred_adj_p) {
1805 pred->assign_max(fill->max());
1806 } else {
1807 this->insert_before(n, fill.release());
1808 }
1809 }
1810 return *this; // @a n extends past @a remaining, -> all done.
1811 } else {
1812 // Collapse in to previous range if it's adjacent and the color matches.
1813 if (pred_adj_p) {
1814 this->remove(n);
1815 pred->assign_max(fill->max());
1816 } else {
1817 this->insert_before(n, fill.release());
1818 this->remove(n);
1819 }
1820 }
1821 } else if (right_ext_p) {
1822 n->assign_min(range_max_plus_1);
1823 return *this;
1824 } else {
1825 this->remove(n);
1826 }
1827
1828 // Everything up to @a n.max is correct, time to process next node.
1829 n = next_n;
1830 }
1831
1832 // Arriving here means there are no more ranges past @a range (those cases return from the loop).
1833 // Therefore the final fill node is always last in the tree.
1834 if (plain_color_p && !remaining.empty()) {
1835 // Check if the last node can be extended to cover because it's left adjacent.
1836 // Can decrement @a range_min because if there's a range to the left, @a range_min is not minimal.
1837 n = _list.tail();
1838 if (n && n->max() >= --metric_type{remaining.min()} && n->payload() == plain_color) {
1839 n->assign_max(range.max());
1840 } else {
1841 this->append(_fa.make(remaining.min(), remaining.max(), plain_color));
1842 }
1843 }
1844
1845 return *this;
1846}
1847
1848template <typename METRIC, typename PAYLOAD>
1849void
1851 for (auto &node : _list) {
1852 std::destroy_at(&node.payload());
1853 }
1854 _list.clear();
1855 _root = nullptr;
1856 _arena.clear();
1857 _fa.clear();
1858}
1859
1860}} // namespace swoc::SWOC_VERSION_NS
constexpr auto maximum(meta::CaseTag< 0 >) -> M
constexpr auto minimum(meta::CaseTag< 0 >) -> M
metric_type const & max() const
Relation relationship(self_type const &that) const
T metric_type
Export metric type.
bool is_strict_subset_of(self_type const &that) const
bool operator!=(self_type const &that) const
Inequality.
bool is_subset_of(self_type const &that) const
bool has_union(self_type const &that) const
self_type & assign_max(metric_type const &max)
bool is_singleton() const
Check if the interval is exactly one element.
bool operator<(DiscreteRange< METRIC > const &lhs, DiscreteRange< METRIC > const &rhs)
bool operator==(self_type const &that) const
Equality.
constexpr DiscreteRange()
bool operator>=(DiscreteRange< T > const &lhs, DiscreteRange< T > const &rhs)
bool has_intersection_with(self_type const &that) const
self_type & assign_min(metric_type const &min)
bool operator!() const
constexpr DiscreteRange(T const &value)
DiscreteRangeEdgeRelation EdgeRelation
Import type for convenience.
DiscreteRangeRelation Relation
Import type for convenience.
bool operator<=(DiscreteRange< METRIC > const &lhs, DiscreteRange< METRIC > const &rhs)
bool is_superset_of(self_type const &that) const
bool contains(metric_type const &value) const
self_type & clear()
Make the range empty.
EdgeRelation left_edge_relationship(self_type const &that) const
bool is_adjacent_to(self_type const &that) const
bool operator^(DiscreteRange< T > const &lhs, DiscreteRange< T > const &rhs)
self_type intersection(self_type const &that) const
self_type hull(self_type const &that) const
bool is_left_adjacent_to(self_type const &that) const
bool operator>(DiscreteRange< T > const &lhs, DiscreteRange< T > const &rhs)
self_type & clip_max()
metric_type const & min() const
self_type & operator|=(self_type const &that)
self_type & assign(metric_type const &min, metric_type const &max)
self_type & operator&=(self_type const &that)
bool is_maximal() const
self_type & assign(metric_type const &value)
constexpr DiscreteRange(T const &min, T const &max)
bool is_strict_superset_of(self_type const &that) const
bool operator!=(DiscreteRange< T > const &lhs, DiscreteRange< T > const &rhs)
A node in the range tree.
swoc::IntrusiveLinkageRebind< self_type, super_type::Linkage > Linkage
Linkage for IntrusiveDList.
self_type & assign(range_type const &range)
Node(METRIC const &min, METRIC const &max, PAYLOAD const &payload)
Construct from two metrics and a payload.
Node()=default
Construct empty node.
Node(range_type const &range, PAYLOAD const &payload)
Construct from range and payload.
self_type & erase(range_type const &range)
iterator lower_bound(METRIC const &m)
self_type & mark(range_type const &range, PAYLOAD const &payload, bool update_tree=true)
void insert_after(Node *spot, Node *node, bool update_tree=true)
const_iterator end() const
Node * lower_node(METRIC const &target)
iterator upper_bound(METRIC const &m)
self_type & fill(range_type const &range, PAYLOAD const &payload)
void prepend(Node *node, bool update_tree=true)
self_type & mark_bulk(std::vector< std::pair< range_type, PAYLOAD > > &marks, bool is_sorted=false)
PAYLOAD payload_type
Export.
void insert_before(Node *spot, Node *node, bool update_tree=true)
METRIC metric_type
Export.
const_iterator find(METRIC const &metric) const
Node * upper_node(METRIC const &target)
std::pair< iterator, iterator > intersection(range_type const &range)
void remove(Node *node, bool update_tree=true)
void append(Node *node, bool update_tree=true)
const_iterator begin() const
size_t count() const
iterator find(METRIC const &metric)
self_type & mark_bulk(std::pair< range_type, PAYLOAD > *start, size_t n, bool is_sorted=false)
self_type & blend(range_type const &range, U const &color, F &&blender)
void clear()
Remove all ranges.
IntrusiveDList< typename Node::Linkage > _list
For template deduction guides.
Definition ArenaWriter.cc:9
DiscreteRangeEdgeRelation
Relationship between one edge of an interval and the "opposite" edge of another.
@ OVLP
Edge is inside interval.
@ ADJ
The edges are adjacent.
@ GAP
There is a gap between the edges.
DiscreteRangeRelation
Relationship between two intervals.
@ ADJACENT
The two intervals are adjacent and disjoint.
@ SUPERSET
Every element in RHS is in LHS.
@ OVERLAP
There exists at least one element in both LHS and RHS.
@ SUBSET
All elements in LHS are also in RHS.
@ NONE
No common elements.
bool operator==(IPAddr const &lhs, sockaddr const *sa)
Equality.
Definition swoc_ip.cc:650
bool operator()(self_type const &lhs, self_type const &rhs) const
self_type * rebalance_after_insert()
Rebalance the tree starting at this node.
Definition RBTree.cc:110
self_type * _prev
Previous node.
Definition RBTree.h:210
static self_type * buildTree(self_type *&head, int n)
Recursively build a balanced RB tree from a sorted linked list.
Definition RBTree.cc:299
self_type * remove()
Definition RBTree.cc:152
self_type * set_child(self_type *child, Direction dir)
Definition RBTree.cc:62
self_type * ripple_structure_fixup()
Definition RBTree.cc:75
self_type * _right
right child
Definition RBTree.h:208
self_type * _left
left child
Definition RBTree.h:207
self_type * _next
Next node.
Definition RBTree.h:209
Case hierarchy.
Definition swoc_meta.h:66
bool remove(path const &p, std::error_code &ec)
Definition swoc_file.cc:430