2012-04-19 14:35:52 +08:00
|
|
|
/*
|
|
|
|
Copyright 2010 Google Inc.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef GrTHashCache_DEFINED
|
|
|
|
#define GrTHashCache_DEFINED
|
|
|
|
|
|
|
|
#include "GrTDArray.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Key needs
|
|
|
|
* static bool EQ(const Entry&, const HashKey&);
|
|
|
|
* static bool LT(const Entry&, const HashKey&);
|
|
|
|
* uint32_t getHash() const;
|
|
|
|
*
|
|
|
|
* Allows duplicate key entries but on find you may get
|
|
|
|
* any of the duplicate entries returned.
|
|
|
|
*/
|
|
|
|
template <typename T, typename Key, size_t kHashBits> class GrTHashTable {
|
|
|
|
public:
|
|
|
|
GrTHashTable() { Gr_bzero(fHash, sizeof(fHash)); }
|
|
|
|
~GrTHashTable() {}
|
|
|
|
|
|
|
|
int count() const { return fSorted.count(); }
|
|
|
|
T* find(const Key&) const;
|
|
|
|
// return true if key was unique when inserted.
|
|
|
|
bool insert(const Key&, T*);
|
|
|
|
void remove(const Key&, const T*);
|
|
|
|
T* removeAt(int index, uint32_t hash);
|
|
|
|
void removeAll();
|
|
|
|
void deleteAll();
|
|
|
|
void unrefAll();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the index for the element, using a linear search.
|
|
|
|
*/
|
|
|
|
int slowFindIndex(T* elem) const { return fSorted.find(elem); }
|
|
|
|
|
|
|
|
#if GR_DEBUG
|
|
|
|
void validate() const;
|
|
|
|
bool contains(T*) const;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// testing
|
|
|
|
const GrTDArray<T*>& getArray() const { return fSorted; }
|
|
|
|
private:
|
|
|
|
enum {
|
|
|
|
kHashCount = 1 << kHashBits,
|
|
|
|
kHashMask = kHashCount - 1
|
|
|
|
};
|
|
|
|
static unsigned hash2Index(uint32_t hash) {
|
|
|
|
hash ^= hash >> 16;
|
|
|
|
if (kHashBits <= 8) {
|
|
|
|
hash ^= hash >> 8;
|
|
|
|
}
|
|
|
|
return hash & kHashMask;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutable T* fHash[kHashCount];
|
|
|
|
GrTDArray<T*> fSorted;
|
|
|
|
|
|
|
|
// search fSorted, and return the found index, or ~index of where it
|
|
|
|
// should be inserted
|
|
|
|
int searchArray(const Key&) const;
|
|
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
int GrTHashTable<T, Key, kHashBits>::searchArray(const Key& key) const {
|
|
|
|
int count = fSorted.count();
|
|
|
|
if (0 == count) {
|
|
|
|
// we should insert it at 0
|
|
|
|
return ~0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const T* const* array = fSorted.begin();
|
|
|
|
int high = count - 1;
|
|
|
|
int low = 0;
|
|
|
|
while (high > low) {
|
|
|
|
int index = (low + high) >> 1;
|
|
|
|
if (Key::LT(*array[index], key)) {
|
|
|
|
low = index + 1;
|
|
|
|
} else {
|
|
|
|
high = index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we found it
|
|
|
|
if (Key::EQ(*array[high], key)) {
|
|
|
|
// above search should have found the first occurrence if there
|
|
|
|
// are multiple.
|
|
|
|
GrAssert(0 == high || Key::LT(*array[high - 1], key));
|
|
|
|
return high;
|
|
|
|
}
|
|
|
|
|
|
|
|
// now return the ~ of where we should insert it
|
|
|
|
if (Key::LT(*array[high], key)) {
|
|
|
|
high += 1;
|
|
|
|
}
|
|
|
|
return ~high;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
T* GrTHashTable<T, Key, kHashBits>::find(const Key& key) const {
|
|
|
|
int hashIndex = hash2Index(key.getHash());
|
|
|
|
T* elem = fHash[hashIndex];
|
|
|
|
|
|
|
|
if (NULL == elem || !Key::EQ(*elem, key)) {
|
|
|
|
// bsearch for the key in our sorted array
|
|
|
|
int index = this->searchArray(key);
|
|
|
|
if (index < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
elem = fSorted[index];
|
|
|
|
// update the hash
|
|
|
|
fHash[hashIndex] = elem;
|
|
|
|
}
|
|
|
|
return elem;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
bool GrTHashTable<T, Key, kHashBits>::insert(const Key& key, T* elem) {
|
|
|
|
int index = this->searchArray(key);
|
|
|
|
bool first = index < 0;
|
|
|
|
if (first) {
|
|
|
|
// turn it into the actual index
|
|
|
|
index = ~index;
|
|
|
|
}
|
|
|
|
// add it to our array
|
|
|
|
*fSorted.insert(index) = elem;
|
|
|
|
// update our hash table (overwrites any dupe's position in the hash)
|
|
|
|
fHash[hash2Index(key.getHash())] = elem;
|
|
|
|
return first;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
void GrTHashTable<T, Key, kHashBits>::remove(const Key& key, const T* elem) {
|
|
|
|
int index = hash2Index(key.getHash());
|
|
|
|
if (fHash[index] == elem) {
|
|
|
|
fHash[index] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove from our sorted array
|
|
|
|
index = this->searchArray(key);
|
|
|
|
GrAssert(index >= 0);
|
|
|
|
// if there are multiple matches searchArray will give us the first match
|
|
|
|
// march forward until we find elem.
|
|
|
|
while (elem != fSorted[index]) {
|
|
|
|
++index;
|
|
|
|
GrAssert(index < fSorted.count());
|
|
|
|
}
|
|
|
|
GrAssert(elem == fSorted[index]);
|
|
|
|
fSorted.remove(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
T* GrTHashTable<T, Key, kHashBits>::removeAt(int elemIndex, uint32_t hash) {
|
|
|
|
int hashIndex = hash2Index(hash);
|
|
|
|
if (fHash[hashIndex] == fSorted[elemIndex]) {
|
|
|
|
fHash[hashIndex] = NULL;
|
|
|
|
}
|
|
|
|
// remove from our sorted array
|
|
|
|
T* elem = fSorted[elemIndex];
|
|
|
|
fSorted.remove(elemIndex);
|
|
|
|
return elem;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
void GrTHashTable<T, Key, kHashBits>::removeAll() {
|
|
|
|
fSorted.reset();
|
|
|
|
Gr_bzero(fHash, sizeof(fHash));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
void GrTHashTable<T, Key, kHashBits>::deleteAll() {
|
|
|
|
fSorted.deleteAll();
|
|
|
|
Gr_bzero(fHash, sizeof(fHash));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
void GrTHashTable<T, Key, kHashBits>::unrefAll() {
|
|
|
|
fSorted.unrefAll();
|
|
|
|
Gr_bzero(fHash, sizeof(fHash));
|
|
|
|
}
|
|
|
|
|
|
|
|
#if GR_DEBUG
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
void GrTHashTable<T, Key, kHashBits>::validate() const {
|
|
|
|
for (size_t i = 0; i < GR_ARRAY_COUNT(fHash); i++) {
|
|
|
|
if (fHash[i]) {
|
|
|
|
unsigned hashIndex = hash2Index(Key::GetHash(*fHash[i]));
|
|
|
|
GrAssert(hashIndex == i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int count = fSorted.count();
|
|
|
|
for (int i = 1; i < count; i++) {
|
|
|
|
GrAssert(Key::LT(*fSorted[i - 1], *fSorted[i]) ||
|
|
|
|
Key::EQ(*fSorted[i - 1], *fSorted[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename Key, size_t kHashBits>
|
|
|
|
bool GrTHashTable<T, Key, kHashBits>::contains(T* elem) const {
|
|
|
|
int index = fSorted.find(elem);
|
|
|
|
return index >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|