blob: f57d30eccb858b543479717b6a45444c11548885 [file] [log] [blame]
edisonn@google.com04115a12013-02-25 20:07:24 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SkTSet_DEFINED
9#define SkTSet_DEFINED
10
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000011#include "SkTSort.h"
edisonn@google.com04115a12013-02-25 20:07:24 +000012#include "SkTDArray.h"
13#include "SkTypes.h"
14
15/** \class SkTSet<T>
16
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000017 The SkTSet template class defines a set. Elements are additionally
18 guaranteed to be sorted by their insertion order.
edisonn@google.com04115a12013-02-25 20:07:24 +000019 Main operations supported now are: add, merge, find and contains.
20
21 TSet<T> is mutable.
22*/
23
24// TODO: Add remove, intersect and difference operations.
25// TODO: Add bench tests.
edisonn@google.coma20e42c2013-05-31 18:04:20 +000026template <typename T> class SkTSet {
edisonn@google.com04115a12013-02-25 20:07:24 +000027public:
28 SkTSet() {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000029 fSetArray = SkNEW(SkTDArray<T>);
30 fOrderedArray = SkNEW(SkTDArray<T>);
edisonn@google.com04115a12013-02-25 20:07:24 +000031 }
32
33 ~SkTSet() {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000034 SkASSERT(fSetArray);
35 SkDELETE(fSetArray);
36 SkASSERT(fOrderedArray);
37 SkDELETE(fOrderedArray);
edisonn@google.com04115a12013-02-25 20:07:24 +000038 }
39
40 SkTSet(const SkTSet<T>& src) {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000041 this->fSetArray = SkNEW_ARGS(SkTDArray<T>, (*src.fSetArray));
42 this->fOrderedArray = SkNEW_ARGS(SkTDArray<T>, (*src.fOrderedArray));
edisonn@google.com04115a12013-02-25 20:07:24 +000043#ifdef SK_DEBUG
44 validate();
45#endif
46 }
47
48 SkTSet<T>& operator=(const SkTSet<T>& src) {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000049 *this->fSetArray = *src.fSetArray;
50 *this->fOrderedArray = *src.fOrderedArray;
edisonn@google.com04115a12013-02-25 20:07:24 +000051#ifdef SK_DEBUG
52 validate();
53#endif
54 return *this;
55 }
56
57 /** Merges src elements into this, and returns the number of duplicates
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000058 * found. Elements from src will retain their ordering and will be ordered
59 * after the elements currently in this set.
60 *
61 * Implementation note: this uses a 2-stage merge to obtain O(n log n) time.
62 * The first stage goes through src.fOrderedArray, checking if
63 * this->contains() is false before adding to this.fOrderedArray.
64 * The second stage does a standard sorted list merge on the fSetArrays.
65 */
edisonn@google.com04115a12013-02-25 20:07:24 +000066 int mergeInto(const SkTSet<T>& src) {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000067 SkASSERT(fSetArray);
68 SkASSERT(fOrderedArray);
69
70 // Do fOrderedArray merge.
71 for (int i = 0; i < src.count(); ++i) {
72 if (!contains((*src.fOrderedArray)[i])) {
73 fOrderedArray->push((*src.fOrderedArray)[i]);
74 }
75 }
76
77 // Do fSetArray merge.
edisonn@google.com04115a12013-02-25 20:07:24 +000078 int duplicates = 0;
79
80 SkTDArray<T>* fArrayNew = new SkTDArray<T>();
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000081 fArrayNew->setReserve(fOrderedArray->count());
edisonn@google.com04115a12013-02-25 20:07:24 +000082 int i = 0;
83 int j = 0;
84
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000085 while (i < fSetArray->count() && j < src.count()) {
86 if ((*fSetArray)[i] < (*src.fSetArray)[j]) {
87 fArrayNew->push((*fSetArray)[i]);
edisonn@google.com04115a12013-02-25 20:07:24 +000088 i++;
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000089 } else if ((*fSetArray)[i] > (*src.fSetArray)[j]) {
90 fArrayNew->push((*src.fSetArray)[j]);
edisonn@google.com04115a12013-02-25 20:07:24 +000091 j++;
92 } else {
93 duplicates++;
94 j++; // Skip one of the duplicates.
95 }
96 }
skia.committer@gmail.com5ca3bd02013-02-26 07:01:22 +000097
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +000098 while (i < fSetArray->count()) {
99 fArrayNew->push((*fSetArray)[i]);
edisonn@google.com04115a12013-02-25 20:07:24 +0000100 i++;
101 }
102
103 while (j < src.count()) {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000104 fArrayNew->push((*src.fSetArray)[j]);
edisonn@google.com04115a12013-02-25 20:07:24 +0000105 j++;
106 }
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000107 SkDELETE(fSetArray);
108 fSetArray = fArrayNew;
edisonn@google.com04115a12013-02-25 20:07:24 +0000109 fArrayNew = NULL;
110
111#ifdef SK_DEBUG
112 validate();
113#endif
114 return duplicates;
skia.committer@gmail.com5ca3bd02013-02-26 07:01:22 +0000115 }
edisonn@google.com04115a12013-02-25 20:07:24 +0000116
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000117 /** Adds a new element into set and returns false if the element is already
edisonn@google.com04115a12013-02-25 20:07:24 +0000118 * in this set.
119 */
120 bool add(const T& elem) {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000121 SkASSERT(fSetArray);
122 SkASSERT(fOrderedArray);
edisonn@google.com04115a12013-02-25 20:07:24 +0000123
124 int pos = 0;
125 int i = find(elem, &pos);
126 if (i >= 0) {
127 return false;
128 }
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000129 *fSetArray->insert(pos) = elem;
130 fOrderedArray->push(elem);
edisonn@google.com04115a12013-02-25 20:07:24 +0000131#ifdef SK_DEBUG
132 validate();
133#endif
134 return true;
135 }
136
137 /** Returns true if this set is empty.
138 */
139 bool isEmpty() const {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000140 SkASSERT(fOrderedArray);
141 SkASSERT(fSetArray);
142 SkASSERT(fSetArray->isEmpty() == fOrderedArray->isEmpty());
143 return fOrderedArray->isEmpty();
edisonn@google.com04115a12013-02-25 20:07:24 +0000144 }
145
146 /** Return the number of elements in the set.
147 */
148 int count() const {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000149 SkASSERT(fOrderedArray);
150 SkASSERT(fSetArray);
151 SkASSERT(fSetArray->count() == fOrderedArray->count());
152 return fOrderedArray->count();
edisonn@google.com04115a12013-02-25 20:07:24 +0000153 }
154
155 /** Return the number of bytes in the set: count * sizeof(T).
156 */
157 size_t bytes() const {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000158 SkASSERT(fOrderedArray);
159 return fOrderedArray->bytes();
edisonn@google.com04115a12013-02-25 20:07:24 +0000160 }
161
skia.committer@gmail.com5ca3bd02013-02-26 07:01:22 +0000162 /** Return the beginning of a set iterator.
edisonn@google.com04115a12013-02-25 20:07:24 +0000163 * Elements in the iterator will be sorted ascending.
164 */
165 const T* begin() const {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000166 SkASSERT(fOrderedArray);
167 return fOrderedArray->begin();
edisonn@google.com04115a12013-02-25 20:07:24 +0000168 }
169
skia.committer@gmail.com5ca3bd02013-02-26 07:01:22 +0000170 /** Return the end of a set iterator.
edisonn@google.com04115a12013-02-25 20:07:24 +0000171 */
172 const T* end() const {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000173 SkASSERT(fOrderedArray);
174 return fOrderedArray->end();
edisonn@google.com04115a12013-02-25 20:07:24 +0000175 }
176
177 const T& operator[](int index) const {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000178 SkASSERT(fOrderedArray);
179 return (*fOrderedArray)[index];
edisonn@google.com04115a12013-02-25 20:07:24 +0000180 }
181
182 /** Resets the set (deletes memory and initiates an empty set).
183 */
184 void reset() {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000185 SkASSERT(fSetArray);
186 SkASSERT(fOrderedArray);
187 fSetArray->reset();
188 fOrderedArray->reset();
edisonn@google.com04115a12013-02-25 20:07:24 +0000189 }
190
191 /** Rewinds the set (preserves memory and initiates an empty set).
192 */
193 void rewind() {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000194 SkASSERT(fSetArray);
195 SkASSERT(fOrderedArray);
196 fSetArray->rewind();
197 fOrderedArray->rewind();
edisonn@google.com04115a12013-02-25 20:07:24 +0000198 }
199
200 /** Reserves memory for the set.
201 */
robertphillips@google.come9cd27d2013-10-16 17:48:11 +0000202 void setReserve(int reserve) {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000203 SkASSERT(fSetArray);
204 SkASSERT(fOrderedArray);
205 fSetArray->setReserve(reserve);
206 fOrderedArray->setReserve(reserve);
edisonn@google.com04115a12013-02-25 20:07:24 +0000207 }
208
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000209 /** Returns true if the array contains this element.
210 */
211 bool contains(const T& elem) const {
212 SkASSERT(fSetArray);
213 return (this->find(elem) >= 0);
214 }
215
216 /** Copies internal array to destination.
217 */
218 void copy(T* dst) const {
219 SkASSERT(fOrderedArray);
220 fOrderedArray->copyRange(dst, 0, fOrderedArray->count());
221 }
222
223 /** Returns a const reference to the internal vector.
224 */
225 const SkTDArray<T>& toArray() {
226 SkASSERT(fOrderedArray);
227 return *fOrderedArray;
228 }
229
230 /** Unref all elements in the set.
231 */
232 void unrefAll() {
233 SkASSERT(fSetArray);
234 SkASSERT(fOrderedArray);
235 fOrderedArray->unrefAll();
236 // Also reset the other array, as SkTDArray::unrefAll does an
237 // implcit reset
238 fSetArray->reset();
239 }
240
241 /** safeUnref all elements in the set.
242 */
243 void safeUnrefAll() {
244 SkASSERT(fSetArray);
245 SkASSERT(fOrderedArray);
246 fOrderedArray->safeUnrefAll();
247 // Also reset the other array, as SkTDArray::safeUnrefAll does an
248 // implcit reset
249 fSetArray->reset();
250 }
251
252#ifdef SK_DEBUG
253 void validate() const {
254 SkASSERT(fSetArray);
255 SkASSERT(fOrderedArray);
256 fSetArray->validate();
257 fOrderedArray->validate();
258 SkASSERT(isSorted() && !hasDuplicates() && arraysConsistent());
259 }
260
261 bool hasDuplicates() const {
262 for (int i = 0; i < fSetArray->count() - 1; ++i) {
263 if ((*fSetArray)[i] == (*fSetArray)[i + 1]) {
264 return true;
265 }
266 }
267 return false;
268 }
269
270 bool isSorted() const {
271 for (int i = 0; i < fSetArray->count() - 1; ++i) {
272 // Use only < operator
273 if (!((*fSetArray)[i] < (*fSetArray)[i + 1])) {
274 return false;
275 }
276 }
277 return true;
278 }
279
280 /** Checks if fSetArray is consistent with fOrderedArray
281 */
282 bool arraysConsistent() const {
283 if (fSetArray->count() != fOrderedArray->count()) {
284 return false;
285 }
286 if (fOrderedArray->count() == 0) {
287 return true;
288 }
289
290 // Copy and sort fOrderedArray, then compare to fSetArray.
291 // A O(n log n) algorithm is necessary as O(n^2) will choke some GMs.
292 SkAutoMalloc sortedArray(fOrderedArray->bytes());
293 T* sortedBase = reinterpret_cast<T*>(sortedArray.get());
robertphillips@google.com8b169312013-10-15 17:47:36 +0000294 int count = fOrderedArray->count();
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000295 fOrderedArray->copyRange(sortedBase, 0, count);
296
297 SkTQSort<T>(sortedBase, sortedBase + count - 1);
298
robertphillips@google.com8b169312013-10-15 17:47:36 +0000299 for (int i = 0; i < count; ++i) {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000300 if (sortedBase[i] != (*fSetArray)[i]) {
301 return false;
302 }
303 }
304
305 return true;
306 }
307#endif
308
309private:
310 SkTDArray<T>* fSetArray; // Sorted by pointer address for fast
311 // lookup.
312 SkTDArray<T>* fOrderedArray; // Sorted by insertion order for
313 // deterministic output.
314
315 /** Returns the index in fSetArray where an element was found.
edisonn@google.com04115a12013-02-25 20:07:24 +0000316 * Returns -1 if the element was not found, and it fills *posToInsertSorted
317 * with the index of the place where elem should be inserted to preserve the
318 * internal array sorted.
319 * If element was found, *posToInsertSorted is undefined.
320 */
321 int find(const T& elem, int* posToInsertSorted = NULL) const {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000322 SkASSERT(fSetArray);
edisonn@google.com04115a12013-02-25 20:07:24 +0000323
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000324 if (fSetArray->count() == 0) {
edisonn@google.com04115a12013-02-25 20:07:24 +0000325 if (posToInsertSorted) {
326 *posToInsertSorted = 0;
327 }
328 return -1;
329 }
330 int iMin = 0;
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000331 int iMax = fSetArray->count();
edisonn@google.com04115a12013-02-25 20:07:24 +0000332
333 while (iMin < iMax - 1) {
334 int iMid = (iMin + iMax) / 2;
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000335 if (elem < (*fSetArray)[iMid]) {
edisonn@google.com04115a12013-02-25 20:07:24 +0000336 iMax = iMid;
337 } else {
338 iMin = iMid;
339 }
340 }
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000341 if (elem == (*fSetArray)[iMin]) {
edisonn@google.com04115a12013-02-25 20:07:24 +0000342 return iMin;
343 }
344 if (posToInsertSorted) {
commit-bot@chromium.orga7aa8102013-07-24 01:51:08 +0000345 if (elem < (*fSetArray)[iMin]) {
edisonn@google.com04115a12013-02-25 20:07:24 +0000346 *posToInsertSorted = iMin;
347 } else {
348 *posToInsertSorted = iMin + 1;
349 }
350 }
351
352 return -1;
353 }
edisonn@google.com04115a12013-02-25 20:07:24 +0000354};
355
356#endif