1 #pragma once
2
3 #include <QtDebug>
4 #include <iosfwd>
5 #include <utility>
6
7 #include "util/assert.h"
8 #include "util/optional.h"
9 #include "util/types.h"
10
11 namespace mixxx {
12
13 // A half-open, directed range of indices with limited mutability.
14 class IndexRange final: private std::pair<SINT, SINT> {
15 typedef std::pair<SINT, SINT> Super;
16
17 public:
18 using Super::swap;
19
IndexRange()20 IndexRange()
21 : Super(0, 0) {
22 DEBUG_ASSERT(empty());
23 }
24
between(SINT start,SINT end)25 static IndexRange between(SINT start, SINT end) {
26 return IndexRange(start, end);
27 }
forward(SINT start,SINT length)28 static IndexRange forward(SINT start, SINT length) {
29 DEBUG_ASSERT(length >= 0);
30 return IndexRange(start, start + length);
31 }
backward(SINT start,SINT length)32 static IndexRange backward(SINT start, SINT length) {
33 DEBUG_ASSERT(length >= 0);
34 return IndexRange(start, start - length);
35 }
36
37 // The first index within this range (inclusive)
start()38 SINT start() const {
39 return first;
40 }
41 // The next index beyond this range (exclusive)
end()42 SINT end() const {
43 return second;
44 }
45
empty()46 bool empty() const {
47 return start() == end();
48 }
49
50 enum class Orientation {
51 Empty,
52 Forward,
53 Backward,
54 };
orientation()55 Orientation orientation() const {
56 if (empty()) {
57 return Orientation::Empty; // undefined
58 } else {
59 if (start() < end()) {
60 return Orientation::Forward;
61 } else {
62 return Orientation::Backward;
63 }
64 }
65 }
66
length()67 SINT length() const {
68 return (start() <= end()) ? (end() - start()) : (start() - end());
69 }
70
71 // Clamps index by this range including both start() and end()
72 // boundaries.
clampIndex(SINT index)73 SINT clampIndex(SINT index) const {
74 if (start() <= end()) {
75 return std::max(start(), std::min(end(), index));
76 } else {
77 return std::min(start(), std::max(end(), index));
78 }
79 }
80
containsIndex(SINT index)81 bool containsIndex(SINT index) const {
82 if (start() <= end()) {
83 return (start() <= index) && (index < end());
84 } else {
85 return (start() >= index) && (index > end());
86 }
87 }
88
89 // Grow the range by appending the given length at the front side.
growFront(SINT frontLength)90 void growFront(SINT frontLength) {
91 DEBUG_ASSERT(frontLength >= 0);
92 if (start() <= end()) {
93 first -= frontLength;
94 } else {
95 first += frontLength;
96 }
97 }
98
99 // Grow the range by appending the given length at the back side.
growBack(SINT backLength)100 void growBack(SINT backLength) {
101 DEBUG_ASSERT(backLength >= 0);
102 if (start() <= end()) {
103 second += backLength;
104 } else {
105 second -= backLength;
106 }
107 }
108
109 // Shrink the range by cutting off the given length at the front side.
shrinkFront(SINT frontLength)110 void shrinkFront(SINT frontLength) {
111 DEBUG_ASSERT(frontLength >= 0);
112 DEBUG_ASSERT(frontLength <= length());
113 if (start() <= end()) {
114 first += frontLength;
115 } else {
116 first -= frontLength;
117 }
118 }
119
120 // Shrink the range by cutting off the given length at the back side.
shrinkBack(SINT backLength)121 void shrinkBack(SINT backLength) {
122 DEBUG_ASSERT(backLength >= 0);
123 DEBUG_ASSERT(backLength <= length());
124 if (start() <= end()) {
125 second -= backLength;
126 } else {
127 second += backLength;
128 }
129 }
130
131 // Splits this range into two adjacent parts by slicing off
132 // and returning a range of given length and same direction
133 // from the front side. The given front length must not exceed
134 // the length of this range.
135 IndexRange splitAndShrinkFront(SINT frontLength);
136
137 // Splits this range into two adjacent parts by slicing off
138 // and returning a range of given length and same direction
139 // from the back side. The given back length must not exceed
140 // the length of this range.
141 IndexRange splitAndShrinkBack(SINT backLength);
142
143 bool isSubrangeOf(IndexRange outerIndexRange) const;
144
145 friend
146 bool operator==(IndexRange lhs, IndexRange rhs) {
147 return (lhs.first == rhs.first) && (lhs.second == rhs.second);
148 }
149
150 private:
IndexRange(SINT start,SINT end)151 IndexRange(SINT start, SINT end)
152 : Super(start, end) {
153 }
154 };
155
156 /// Intersect two ranges with compatible orientations.
157 ///
158 /// Returns std::nullopt of the ranges are disconnected without
159 /// any junction point or on orientation mismatch.
160 ///
161 /// TODO: Rename as intersect() after removing the original function
162 /// with the non-optional return type
163 std::optional<IndexRange> intersect2(IndexRange lhs, IndexRange rhs);
164
165 /// Deprecated, only needed for backward compatibility
166 ///
167 /// TODO: Replace with intersect2()
intersect(IndexRange lhs,IndexRange rhs)168 inline IndexRange intersect(IndexRange lhs, IndexRange rhs) {
169 auto res = intersect2(lhs, rhs);
170 return res ? *res : IndexRange();
171 }
172
173 inline
174 bool operator!=(IndexRange lhs, IndexRange rhs) {
175 return !(lhs == rhs);
176 }
177
178 std::ostream& operator<<(std::ostream& os, IndexRange arg);
179
180 QDebug operator<<(QDebug dbg, IndexRange arg);
181
182
183 } // namespace mixxx
184