|  | /* | 
|  | * Copyright 2012 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #ifndef SkTSet_DEFINED | 
|  | #define SkTSet_DEFINED | 
|  |  | 
|  | #include "SkTSort.h" | 
|  | #include "SkTDArray.h" | 
|  | #include "SkTypes.h" | 
|  |  | 
|  | /** \class SkTSet<T> | 
|  |  | 
|  | The SkTSet template class defines a set. Elements are additionally | 
|  | guaranteed to be sorted by their insertion order. | 
|  | Main operations supported now are: add, merge, find and contains. | 
|  |  | 
|  | TSet<T> is mutable. | 
|  | */ | 
|  |  | 
|  | // TODO: Add remove, intersect and difference operations. | 
|  | // TODO: Add bench tests. | 
|  | template <typename T> class SkTSet { | 
|  | public: | 
|  | SkTSet() { | 
|  | fSetArray = SkNEW(SkTDArray<T>); | 
|  | fOrderedArray = SkNEW(SkTDArray<T>); | 
|  | } | 
|  |  | 
|  | ~SkTSet() { | 
|  | SkASSERT(fSetArray); | 
|  | SkDELETE(fSetArray); | 
|  | SkASSERT(fOrderedArray); | 
|  | SkDELETE(fOrderedArray); | 
|  | } | 
|  |  | 
|  | SkTSet(const SkTSet<T>& src) { | 
|  | this->fSetArray = SkNEW_ARGS(SkTDArray<T>, (*src.fSetArray)); | 
|  | this->fOrderedArray = SkNEW_ARGS(SkTDArray<T>, (*src.fOrderedArray)); | 
|  | #ifdef SK_DEBUG | 
|  | validate(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | SkTSet<T>& operator=(const SkTSet<T>& src) { | 
|  | *this->fSetArray = *src.fSetArray; | 
|  | *this->fOrderedArray = *src.fOrderedArray; | 
|  | #ifdef SK_DEBUG | 
|  | validate(); | 
|  | #endif | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | /** Merges src elements into this, and returns the number of duplicates | 
|  | * found. Elements from src will retain their ordering and will be ordered | 
|  | * after the elements currently in this set. | 
|  | * | 
|  | * Implementation note: this uses a 2-stage merge to obtain O(n log n) time. | 
|  | * The first stage goes through src.fOrderedArray, checking if | 
|  | * this->contains() is false before adding to this.fOrderedArray. | 
|  | * The second stage does a standard sorted list merge on the fSetArrays. | 
|  | */ | 
|  | int mergeInto(const SkTSet<T>& src) { | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fOrderedArray); | 
|  |  | 
|  | // Do fOrderedArray merge. | 
|  | for (int i = 0; i < src.count(); ++i) { | 
|  | if (!contains((*src.fOrderedArray)[i])) { | 
|  | fOrderedArray->push((*src.fOrderedArray)[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Do fSetArray merge. | 
|  | int duplicates = 0; | 
|  |  | 
|  | SkTDArray<T>* fArrayNew = new SkTDArray<T>(); | 
|  | fArrayNew->setReserve(fOrderedArray->count()); | 
|  | int i = 0; | 
|  | int j = 0; | 
|  |  | 
|  | while (i < fSetArray->count() && j < src.count()) { | 
|  | if ((*fSetArray)[i] < (*src.fSetArray)[j]) { | 
|  | fArrayNew->push((*fSetArray)[i]); | 
|  | i++; | 
|  | } else if ((*fSetArray)[i] > (*src.fSetArray)[j]) { | 
|  | fArrayNew->push((*src.fSetArray)[j]); | 
|  | j++; | 
|  | } else { | 
|  | duplicates++; | 
|  | j++; // Skip one of the duplicates. | 
|  | } | 
|  | } | 
|  |  | 
|  | while (i < fSetArray->count()) { | 
|  | fArrayNew->push((*fSetArray)[i]); | 
|  | i++; | 
|  | } | 
|  |  | 
|  | while (j < src.count()) { | 
|  | fArrayNew->push((*src.fSetArray)[j]); | 
|  | j++; | 
|  | } | 
|  | SkDELETE(fSetArray); | 
|  | fSetArray = fArrayNew; | 
|  | fArrayNew = NULL; | 
|  |  | 
|  | #ifdef SK_DEBUG | 
|  | validate(); | 
|  | #endif | 
|  | return duplicates; | 
|  | } | 
|  |  | 
|  | /** Adds a new element into set and returns false if the element is already | 
|  | * in this set. | 
|  | */ | 
|  | bool add(const T& elem) { | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fOrderedArray); | 
|  |  | 
|  | int pos = 0; | 
|  | int i = find(elem, &pos); | 
|  | if (i >= 0) { | 
|  | return false; | 
|  | } | 
|  | *fSetArray->insert(pos) = elem; | 
|  | fOrderedArray->push(elem); | 
|  | #ifdef SK_DEBUG | 
|  | validate(); | 
|  | #endif | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** Returns true if this set is empty. | 
|  | */ | 
|  | bool isEmpty() const { | 
|  | SkASSERT(fOrderedArray); | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fSetArray->isEmpty() == fOrderedArray->isEmpty()); | 
|  | return fOrderedArray->isEmpty(); | 
|  | } | 
|  |  | 
|  | /** Return the number of elements in the set. | 
|  | */ | 
|  | int count() const { | 
|  | SkASSERT(fOrderedArray); | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fSetArray->count() == fOrderedArray->count()); | 
|  | return fOrderedArray->count(); | 
|  | } | 
|  |  | 
|  | /** Return the number of bytes in the set: count * sizeof(T). | 
|  | */ | 
|  | size_t bytes() const { | 
|  | SkASSERT(fOrderedArray); | 
|  | return fOrderedArray->bytes(); | 
|  | } | 
|  |  | 
|  | /** Return the beginning of a set iterator. | 
|  | * Elements in the iterator will be sorted ascending. | 
|  | */ | 
|  | const T*  begin() const { | 
|  | SkASSERT(fOrderedArray); | 
|  | return fOrderedArray->begin(); | 
|  | } | 
|  |  | 
|  | /** Return the end of a set iterator. | 
|  | */ | 
|  | const T*  end() const { | 
|  | SkASSERT(fOrderedArray); | 
|  | return fOrderedArray->end(); | 
|  | } | 
|  |  | 
|  | const T&  operator[](int index) const { | 
|  | SkASSERT(fOrderedArray); | 
|  | return (*fOrderedArray)[index]; | 
|  | } | 
|  |  | 
|  | /** Resets the set (deletes memory and initiates an empty set). | 
|  | */ | 
|  | void reset() { | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fOrderedArray); | 
|  | fSetArray->reset(); | 
|  | fOrderedArray->reset(); | 
|  | } | 
|  |  | 
|  | /** Rewinds the set (preserves memory and initiates an empty set). | 
|  | */ | 
|  | void rewind() { | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fOrderedArray); | 
|  | fSetArray->rewind(); | 
|  | fOrderedArray->rewind(); | 
|  | } | 
|  |  | 
|  | /** Reserves memory for the set. | 
|  | */ | 
|  | void setReserve(int reserve) { | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fOrderedArray); | 
|  | fSetArray->setReserve(reserve); | 
|  | fOrderedArray->setReserve(reserve); | 
|  | } | 
|  |  | 
|  | /** Returns true if the array contains this element. | 
|  | */ | 
|  | bool contains(const T& elem) const { | 
|  | SkASSERT(fSetArray); | 
|  | return (this->find(elem) >= 0); | 
|  | } | 
|  |  | 
|  | /** Copies internal array to destination. | 
|  | */ | 
|  | void copy(T* dst) const { | 
|  | SkASSERT(fOrderedArray); | 
|  | fOrderedArray->copyRange(dst, 0, fOrderedArray->count()); | 
|  | } | 
|  |  | 
|  | /** Returns a const reference to the internal vector. | 
|  | */ | 
|  | const SkTDArray<T>& toArray() { | 
|  | SkASSERT(fOrderedArray); | 
|  | return *fOrderedArray; | 
|  | } | 
|  |  | 
|  | /** Unref all elements in the set. | 
|  | */ | 
|  | void unrefAll() { | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fOrderedArray); | 
|  | fOrderedArray->unrefAll(); | 
|  | // Also reset the other array, as SkTDArray::unrefAll does an | 
|  | // implcit reset | 
|  | fSetArray->reset(); | 
|  | } | 
|  |  | 
|  | /** safeUnref all elements in the set. | 
|  | */ | 
|  | void safeUnrefAll() { | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fOrderedArray); | 
|  | fOrderedArray->safeUnrefAll(); | 
|  | // Also reset the other array, as SkTDArray::safeUnrefAll does an | 
|  | // implcit reset | 
|  | fSetArray->reset(); | 
|  | } | 
|  |  | 
|  | #ifdef SK_DEBUG | 
|  | void validate() const { | 
|  | SkASSERT(fSetArray); | 
|  | SkASSERT(fOrderedArray); | 
|  | fSetArray->validate(); | 
|  | fOrderedArray->validate(); | 
|  | SkASSERT(isSorted() && !hasDuplicates() && arraysConsistent()); | 
|  | } | 
|  |  | 
|  | bool hasDuplicates() const { | 
|  | for (int i = 0; i < fSetArray->count() - 1; ++i) { | 
|  | if ((*fSetArray)[i] == (*fSetArray)[i + 1]) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool isSorted() const { | 
|  | for (int i = 0; i < fSetArray->count() - 1; ++i) { | 
|  | // Use only < operator | 
|  | if (!((*fSetArray)[i] < (*fSetArray)[i + 1])) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** Checks if fSetArray is consistent with fOrderedArray | 
|  | */ | 
|  | bool arraysConsistent() const { | 
|  | if (fSetArray->count() != fOrderedArray->count()) { | 
|  | return false; | 
|  | } | 
|  | if (fOrderedArray->count() == 0) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Copy and sort fOrderedArray, then compare to fSetArray. | 
|  | // A O(n log n) algorithm is necessary as O(n^2) will choke some GMs. | 
|  | SkAutoMalloc sortedArray(fOrderedArray->bytes()); | 
|  | T* sortedBase = reinterpret_cast<T*>(sortedArray.get()); | 
|  | int count = fOrderedArray->count(); | 
|  | fOrderedArray->copyRange(sortedBase, 0, count); | 
|  |  | 
|  | SkTQSort<T>(sortedBase, sortedBase + count - 1); | 
|  |  | 
|  | for (int i = 0; i < count; ++i) { | 
|  | if (sortedBase[i] != (*fSetArray)[i]) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | private: | 
|  | SkTDArray<T>* fSetArray;        // Sorted by pointer address for fast | 
|  | // lookup. | 
|  | SkTDArray<T>* fOrderedArray;    // Sorted by insertion order for | 
|  | // deterministic output. | 
|  |  | 
|  | /** Returns the index in fSetArray where an element was found. | 
|  | * Returns -1 if the element was not found, and it fills *posToInsertSorted | 
|  | * with the index of the place where elem should be inserted to preserve the | 
|  | * internal array sorted. | 
|  | * If element was found, *posToInsertSorted is undefined. | 
|  | */ | 
|  | int find(const T& elem, int* posToInsertSorted = NULL) const { | 
|  | SkASSERT(fSetArray); | 
|  |  | 
|  | if (fSetArray->count() == 0) { | 
|  | if (posToInsertSorted) { | 
|  | *posToInsertSorted = 0; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  | int iMin = 0; | 
|  | int iMax = fSetArray->count(); | 
|  |  | 
|  | while (iMin < iMax - 1) { | 
|  | int iMid = (iMin + iMax) / 2; | 
|  | if (elem < (*fSetArray)[iMid]) { | 
|  | iMax = iMid; | 
|  | } else { | 
|  | iMin = iMid; | 
|  | } | 
|  | } | 
|  | if (elem == (*fSetArray)[iMin]) { | 
|  | return iMin; | 
|  | } | 
|  | if (posToInsertSorted) { | 
|  | if (elem < (*fSetArray)[iMin]) { | 
|  | *posToInsertSorted = iMin; | 
|  | } else { | 
|  | *posToInsertSorted = iMin + 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  | }; | 
|  |  | 
|  | #endif |