1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QSET_H |
5 | #define QSET_H |
6 | |
7 | #include <QtCore/qhash.h> |
8 | #include <QtCore/qcontainertools_impl.h> |
9 | |
10 | #include <initializer_list> |
11 | #include <iterator> |
12 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | |
16 | template <class T> |
17 | class QSet |
18 | { |
19 | typedef QHash<T, QHashDummyValue> Hash; |
20 | |
21 | public: |
22 | inline QSet() noexcept {} |
23 | inline QSet(std::initializer_list<T> list) |
24 | : QSet(list.begin(), list.end()) {} |
25 | template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true> |
26 | inline QSet(InputIterator first, InputIterator last) |
27 | { |
28 | QtPrivate::reserveIfForwardIterator(this, first, last); |
29 | for (; first != last; ++first) |
30 | insert(*first); |
31 | } |
32 | |
33 | // compiler-generated copy/move ctor/assignment operators are fine! |
34 | // compiler-generated destructor is fine! |
35 | |
36 | inline void swap(QSet<T> &other) noexcept { q_hash.swap(other.q_hash); } |
37 | |
38 | #ifndef Q_QDOC |
39 | template <typename U = T> |
40 | QTypeTraits::compare_eq_result_container<QSet, U> operator==(const QSet<T> &other) const |
41 | { return q_hash == other.q_hash; } |
42 | template <typename U = T> |
43 | QTypeTraits::compare_eq_result_container<QSet, U> operator!=(const QSet<T> &other) const |
44 | { return q_hash != other.q_hash; } |
45 | #else |
46 | bool operator==(const QSet &other) const; |
47 | bool operator!=(const QSet &other) const; |
48 | #endif |
49 | |
50 | inline qsizetype size() const { return q_hash.size(); } |
51 | |
52 | inline bool isEmpty() const { return q_hash.isEmpty(); } |
53 | |
54 | inline qsizetype capacity() const { return q_hash.capacity(); } |
55 | inline void reserve(qsizetype size); |
56 | inline void squeeze() { q_hash.squeeze(); } |
57 | |
58 | inline void detach() { q_hash.detach(); } |
59 | inline bool isDetached() const { return q_hash.isDetached(); } |
60 | |
61 | inline void clear() { q_hash.clear(); } |
62 | |
63 | inline bool remove(const T &value) { return q_hash.remove(value) != 0; } |
64 | |
65 | template <typename Pred> |
66 | inline qsizetype removeIf(Pred predicate) |
67 | { |
68 | return QtPrivate::qset_erase_if(*this, predicate); |
69 | } |
70 | |
71 | inline bool contains(const T &value) const { return q_hash.contains(value); } |
72 | |
73 | bool contains(const QSet<T> &set) const; |
74 | |
75 | class const_iterator; |
76 | |
77 | class iterator |
78 | { |
79 | typedef QHash<T, QHashDummyValue> Hash; |
80 | typename Hash::iterator i; |
81 | friend class const_iterator; |
82 | friend class QSet<T>; |
83 | |
84 | public: |
85 | typedef std::forward_iterator_tag iterator_category; |
86 | typedef qptrdiff difference_type; |
87 | typedef T value_type; |
88 | typedef const T *pointer; |
89 | typedef const T &reference; |
90 | |
91 | inline iterator() {} |
92 | inline iterator(typename Hash::iterator o) : i(o) {} |
93 | inline iterator(const iterator &o) : i(o.i) {} |
94 | inline iterator &operator=(const iterator &o) { i = o.i; return *this; } |
95 | inline const T &operator*() const { return i.key(); } |
96 | inline const T *operator->() const { return &i.key(); } |
97 | inline bool operator==(const iterator &o) const { return i == o.i; } |
98 | inline bool operator!=(const iterator &o) const { return i != o.i; } |
99 | inline bool operator==(const const_iterator &o) const |
100 | { return i == o.i; } |
101 | inline bool operator!=(const const_iterator &o) const |
102 | { return i != o.i; } |
103 | inline iterator &operator++() { ++i; return *this; } |
104 | inline iterator operator++(int) { iterator r = *this; ++i; return r; } |
105 | }; |
106 | |
107 | class const_iterator |
108 | { |
109 | typedef QHash<T, QHashDummyValue> Hash; |
110 | typename Hash::const_iterator i; |
111 | friend class iterator; |
112 | friend class QSet<T>; |
113 | |
114 | public: |
115 | typedef std::forward_iterator_tag iterator_category; |
116 | typedef qptrdiff difference_type; |
117 | typedef T value_type; |
118 | typedef const T *pointer; |
119 | typedef const T &reference; |
120 | |
121 | inline const_iterator() {} |
122 | inline const_iterator(typename Hash::const_iterator o) : i(o) {} |
123 | inline const_iterator(const const_iterator &o) : i(o.i) {} |
124 | inline const_iterator(const iterator &o) |
125 | : i(o.i) {} |
126 | inline const_iterator &operator=(const const_iterator &o) { i = o.i; return *this; } |
127 | inline const T &operator*() const { return i.key(); } |
128 | inline const T *operator->() const { return &i.key(); } |
129 | inline bool operator==(const const_iterator &o) const { return i == o.i; } |
130 | inline bool operator!=(const const_iterator &o) const { return i != o.i; } |
131 | inline const_iterator &operator++() { ++i; return *this; } |
132 | inline const_iterator operator++(int) { const_iterator r = *this; ++i; return r; } |
133 | }; |
134 | |
135 | // STL style |
136 | inline iterator begin() { return q_hash.begin(); } |
137 | inline const_iterator begin() const noexcept { return q_hash.begin(); } |
138 | inline const_iterator cbegin() const noexcept { return q_hash.begin(); } |
139 | inline const_iterator constBegin() const noexcept { return q_hash.constBegin(); } |
140 | inline iterator end() { return q_hash.end(); } |
141 | inline const_iterator end() const noexcept { return q_hash.end(); } |
142 | inline const_iterator cend() const noexcept { return q_hash.end(); } |
143 | inline const_iterator constEnd() const noexcept { return q_hash.constEnd(); } |
144 | |
145 | iterator erase(const_iterator i) |
146 | { |
147 | Q_ASSERT(i != constEnd()); |
148 | return q_hash.erase(i.i); |
149 | } |
150 | |
151 | // more Qt |
152 | typedef iterator Iterator; |
153 | typedef const_iterator ConstIterator; |
154 | inline qsizetype count() const { return q_hash.size(); } |
155 | inline iterator insert(const T &value) |
156 | { return q_hash.insert(value, QHashDummyValue()); } |
157 | inline iterator insert(T &&value) |
158 | { return q_hash.emplace(std::move(value), QHashDummyValue()); } |
159 | iterator find(const T &value) { return q_hash.find(value); } |
160 | const_iterator find(const T &value) const { return q_hash.find(value); } |
161 | inline const_iterator constFind(const T &value) const { return find(value); } |
162 | QSet<T> &unite(const QSet<T> &other); |
163 | QSet<T> &intersect(const QSet<T> &other); |
164 | bool intersects(const QSet<T> &other) const; |
165 | QSet<T> &subtract(const QSet<T> &other); |
166 | |
167 | // STL compatibility |
168 | typedef T key_type; |
169 | typedef T value_type; |
170 | typedef value_type *pointer; |
171 | typedef const value_type *const_pointer; |
172 | typedef value_type &reference; |
173 | typedef const value_type &const_reference; |
174 | typedef qptrdiff difference_type; |
175 | typedef qsizetype size_type; |
176 | |
177 | inline bool empty() const { return isEmpty(); } |
178 | |
179 | iterator insert(const_iterator, const T &value) { return insert(value); } |
180 | |
181 | // comfort |
182 | inline QSet<T> &operator<<(const T &value) { insert(value); return *this; } |
183 | inline QSet<T> &operator|=(const QSet<T> &other) { unite(other); return *this; } |
184 | inline QSet<T> &operator|=(const T &value) { insert(value); return *this; } |
185 | inline QSet<T> &operator&=(const QSet<T> &other) { intersect(other); return *this; } |
186 | inline QSet<T> &operator&=(const T &value) |
187 | { QSet<T> result; if (contains(value)) result.insert(value); return (*this = result); } |
188 | inline QSet<T> &operator+=(const QSet<T> &other) { unite(other); return *this; } |
189 | inline QSet<T> &operator+=(const T &value) { insert(value); return *this; } |
190 | inline QSet<T> &operator-=(const QSet<T> &other) { subtract(other); return *this; } |
191 | inline QSet<T> &operator-=(const T &value) { remove(value); return *this; } |
192 | |
193 | friend QSet operator|(const QSet &lhs, const QSet &rhs) { return QSet(lhs) |= rhs; } |
194 | friend QSet operator|(QSet &&lhs, const QSet &rhs) { lhs |= rhs; return std::move(lhs); } |
195 | |
196 | friend QSet operator&(const QSet &lhs, const QSet &rhs) { return QSet(lhs) &= rhs; } |
197 | friend QSet operator&(QSet &&lhs, const QSet &rhs) { lhs &= rhs; return std::move(lhs); } |
198 | |
199 | friend QSet operator+(const QSet &lhs, const QSet &rhs) { return QSet(lhs) += rhs; } |
200 | friend QSet operator+(QSet &&lhs, const QSet &rhs) { lhs += rhs; return std::move(lhs); } |
201 | |
202 | friend QSet operator-(const QSet &lhs, const QSet &rhs) { return QSet(lhs) -= rhs; } |
203 | friend QSet operator-(QSet &&lhs, const QSet &rhs) { lhs -= rhs; return std::move(lhs); } |
204 | |
205 | QList<T> values() const; |
206 | |
207 | private: |
208 | Hash q_hash; |
209 | }; |
210 | |
211 | template <typename InputIterator, |
212 | typename ValueType = typename std::iterator_traits<InputIterator>::value_type, |
213 | QtPrivate::IfIsInputIterator<InputIterator> = true> |
214 | QSet(InputIterator, InputIterator) -> QSet<ValueType>; |
215 | |
216 | template <typename T> |
217 | size_t qHash(const QSet<T> &key, size_t seed = 0) |
218 | noexcept(noexcept(qHashRangeCommutative(key.begin(), key.end(), seed))) |
219 | { |
220 | return qHashRangeCommutative(key.begin(), key.end(), seed); |
221 | } |
222 | |
223 | // inline function implementations |
224 | |
225 | template <class T> |
226 | Q_INLINE_TEMPLATE void QSet<T>::reserve(qsizetype asize) { q_hash.reserve(asize); } |
227 | |
228 | template <class T> |
229 | Q_INLINE_TEMPLATE QSet<T> &QSet<T>::unite(const QSet<T> &other) |
230 | { |
231 | if (!q_hash.isSharedWith(other.q_hash)) { |
232 | for (const T &e : other) |
233 | insert(e); |
234 | } |
235 | return *this; |
236 | } |
237 | |
238 | template <class T> |
239 | Q_INLINE_TEMPLATE QSet<T> &QSet<T>::intersect(const QSet<T> &other) |
240 | { |
241 | QSet<T> copy1; |
242 | QSet<T> copy2; |
243 | if (size() <= other.size()) { |
244 | copy1 = *this; |
245 | copy2 = other; |
246 | } else { |
247 | copy1 = other; |
248 | copy2 = *this; |
249 | *this = copy1; |
250 | } |
251 | for (const auto &e : std::as_const(copy1)) { |
252 | if (!copy2.contains(e)) |
253 | remove(value: e); |
254 | } |
255 | return *this; |
256 | } |
257 | |
258 | template <class T> |
259 | Q_INLINE_TEMPLATE bool QSet<T>::intersects(const QSet<T> &other) const |
260 | { |
261 | const bool otherIsBigger = other.size() > size(); |
262 | const QSet &smallestSet = otherIsBigger ? *this : other; |
263 | const QSet &biggestSet = otherIsBigger ? other : *this; |
264 | typename QSet::const_iterator i = smallestSet.cbegin(); |
265 | typename QSet::const_iterator e = smallestSet.cend(); |
266 | |
267 | while (i != e) { |
268 | if (biggestSet.contains(*i)) |
269 | return true; |
270 | ++i; |
271 | } |
272 | |
273 | return false; |
274 | } |
275 | |
276 | template <class T> |
277 | Q_INLINE_TEMPLATE QSet<T> &QSet<T>::subtract(const QSet<T> &other) |
278 | { |
279 | if (q_hash.isSharedWith(other.q_hash)) { |
280 | clear(); |
281 | } else { |
282 | for (const auto &e : other) |
283 | remove(value: e); |
284 | } |
285 | return *this; |
286 | } |
287 | |
288 | template <class T> |
289 | Q_INLINE_TEMPLATE bool QSet<T>::contains(const QSet<T> &other) const |
290 | { |
291 | typename QSet<T>::const_iterator i = other.constBegin(); |
292 | while (i != other.constEnd()) { |
293 | if (!contains(*i)) |
294 | return false; |
295 | ++i; |
296 | } |
297 | return true; |
298 | } |
299 | |
300 | template <typename T> |
301 | Q_OUTOFLINE_TEMPLATE QList<T> QSet<T>::values() const |
302 | { |
303 | QList<T> result; |
304 | result.reserve(size()); |
305 | typename QSet<T>::const_iterator i = constBegin(); |
306 | while (i != constEnd()) { |
307 | result.append(*i); |
308 | ++i; |
309 | } |
310 | return result; |
311 | } |
312 | |
313 | Q_DECLARE_SEQUENTIAL_ITERATOR(Set) |
314 | |
315 | #if !defined(QT_NO_JAVA_STYLE_ITERATORS) |
316 | template <typename T> |
317 | class QMutableSetIterator |
318 | { |
319 | typedef typename QSet<T>::iterator iterator; |
320 | QSet<T> *c; |
321 | iterator i, n; |
322 | inline bool item_exists() const { return c->constEnd() != n; } |
323 | |
324 | public: |
325 | inline QMutableSetIterator(QSet<T> &container) |
326 | : c(&container) |
327 | { i = c->begin(); n = c->end(); } |
328 | inline QMutableSetIterator &operator=(QSet<T> &container) |
329 | { c = &container; i = c->begin(); n = c->end(); return *this; } |
330 | inline void toFront() { i = c->begin(); n = c->end(); } |
331 | inline void toBack() { i = c->end(); n = i; } |
332 | inline bool hasNext() const { return c->constEnd() != i; } |
333 | inline const T &next() { n = i++; return *n; } |
334 | inline const T &peekNext() const { return *i; } |
335 | inline void remove() |
336 | { if (c->constEnd() != n) { i = c->erase(n); n = c->end(); } } |
337 | inline const T &value() const { Q_ASSERT(item_exists()); return *n; } |
338 | inline bool findNext(const T &t) |
339 | { while (c->constEnd() != (n = i)) if (*i++ == t) return true; return false; } |
340 | }; |
341 | #endif // QT_NO_JAVA_STYLE_ITERATORS |
342 | |
343 | template <typename T, typename Predicate> |
344 | qsizetype erase_if(QSet<T> &set, Predicate pred) |
345 | { |
346 | return QtPrivate::qset_erase_if(set, pred); |
347 | } |
348 | |
349 | QT_END_NAMESPACE |
350 | |
351 | #endif // QSET_H |
352 | |