// Copyright (C) 2014 The Android Open Source Project // // 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. #include "emugl/common/id_to_object_map.h" #include namespace emugl { namespace { typedef IdToObjectMapBase::KeyType KeyType; enum { kMinShift = 3, kMaxShift = 31, kMinCapacity = (1 << kMinShift), kLoadScale = 1024, kMinLoad = kLoadScale/4, // 25% minimum load. kMaxLoad = kLoadScale*3/4, // 75% maximum load. kInvalidKey = IdToObjectMapBase::kMaxId + 1U, kTombstone = IdToObjectMapBase::kMaxId + 2U, }; // Return a number that indicates if the current |capacity| is appropriate // to hold |size| items in our map. // -1 -> the capacity is too small and needs to be increased. // 0 -> the capacity is ok. // +1 -> the capacity is too large and needs to be decreased. int capacityCompare(size_t shift, size_t size) { size_t capacity = 1U << shift; // Essentially, one can rewrite: // load < minLoad // as: // size / capacity < minLoad // capacity * minLoad > size if (capacity * kMinLoad > size * kLoadScale) return +1; // Similarly, one can rewrite: // load > maxLoad // as: // size / capacity > maxLoad // capacity * maxLoad < size if (capacity * kMaxLoad < size * kLoadScale) return -1; return 0; } size_t probeKeys(const KeyType* keys, size_t shift, KeyType key) { static const int kPrimes[] = { 1, /* For 1 << 0 */ 2, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, /* For 1 << 16 */ 131071, 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647 /* For 1 << 31 */ }; size_t slot = key % kPrimes[shift]; size_t step = 0; for (;;) { KeyType k = keys[slot]; if (k == kInvalidKey || k == kTombstone || k == key) return slot; step += 1; slot = (slot + step) & (1U << shift); } } } // namespace IdToObjectMapBase::IdToObjectMapBase() : mCount(0), mShift(kMinShift) { size_t capacity = 1U << mShift; mKeys = static_cast(::calloc(sizeof(mKeys[0]), capacity)); mValues = static_cast(::calloc(sizeof(mValues[0]), capacity)); for (size_t n = 0; n < capacity; ++n) { mKeys[n] = kInvalidKey; } } IdToObjectMapBase::~IdToObjectMapBase() { mShift = 0; mCount = 0; ::free(mKeys); ::free(mValues); } bool IdToObjectMapBase::contains(KeyType key) const { size_t slot = probeKeys(mKeys, mShift, key); switch (mKeys[slot]) { case kInvalidKey: case kTombstone: return false; default: ; } return true; } bool IdToObjectMapBase::find(KeyType key, void** value) const { size_t slot = probeKeys(mKeys, mShift, key); if (!isValidKey(mKeys[slot])) { *value = NULL; return false; } *value = mValues[slot]; return true; } void* IdToObjectMapBase::set(KeyType key, void* value) { if (!value) return remove(key); size_t slot = probeKeys(mKeys, mShift, key); void* result; if (isValidKey(mKeys[slot])) { result = mValues[slot]; mValues[slot] = value; } else { mKeys[slot] = key; mValues[slot] = value; result = NULL; mCount++; resize(mCount); } return result; } void* IdToObjectMapBase::remove(KeyType key) { size_t slot = probeKeys(mKeys, mShift, key); if (!isValidKey(mKeys[slot])) return NULL; void* result = mValues[slot]; mValues[slot] = NULL; mKeys[slot] = kTombstone; mCount--; return result; } void IdToObjectMapBase::resize(size_t newSize) { int ret = capacityCompare(mShift, newSize); if (!ret) return; size_t oldCapacity = 1U << mShift; size_t newShift = mShift; if (ret < 0) { // Capacity is too small and must be increased. do { if (newShift == kMaxShift) break; ++newShift; } while (capacityCompare(newShift, newSize) < 0); } else { // Capacity is too large and must be decreased. do { if (newShift == kMinShift) break; newShift--; } while (capacityCompare(newShift, newSize) > 0); } if (newShift == mShift) return; // Allocate new arrays. size_t newCapacity = 1U << newShift; KeyType* newKeys = static_cast( ::calloc(sizeof(newKeys[0]), newCapacity)); void** newValues = static_cast( ::calloc(sizeof(newValues[0]), newCapacity)); for (size_t n = 0; n < newCapacity; ++n) newKeys[n] = kInvalidKey; // Copy old entries into new arrays. for (size_t n = 0; n < oldCapacity; ++n) { KeyType key = mKeys[n]; if (isValidKey(key)) { size_t newSlot = probeKeys(newKeys, newShift, key); newKeys[newSlot] = key; newValues[newSlot] = mValues[n]; } } // Swap arrays, and get rid of old ones. ::free(mKeys); ::free(mValues); mKeys = newKeys; mValues = newValues; mShift = newShift; } } // namespace emugl