blob: 00a18caad2ae007c72bcd5f0601ecb6cf8ac076f [file] [log] [blame]
// Copyright 2014 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/base/containers/PointerSet.h"
#include "android/base/containers/HashUtils.h"
#include "android/base/Log.h"
#include <stdlib.h>
namespace android {
namespace base {
namespace {
// Special values used in the set's items array to indicate either
// unused slots, or tombstone ones.
#define UNUSED_SLOT NULL
#define TOMBSTONE ((void*)(~(uintptr_t)0))
// Returns true if value |obj| corresponds to a real object in the set,
// false otherwise.
bool validValue(void* obj) {
return obj != UNUSED_SLOT && obj != TOMBSTONE;
}
// Probe the array |items| holding |1U << shift| items for object |obj|.
// |objHash| is the object hash.
// If |obj| is found, return its index in the array.
// If |obj| is not found, return the index of a unused or tombstone slot
// that can be used to add it.
size_t probeItems(const void* obj, size_t objHash, void** items, size_t shift) {
DCHECK(obj);
DCHECK(shift < 32U);
size_t probe_mask = ((1U << shift) - 1U);
const size_t probe_index0 = (objHash % internal::kPrimes[shift]) & probe_mask;
size_t probe_index = probe_index0;
size_t step = 0;
intptr_t tombstone = -1;
for (;;) {
void* item = items[probe_index];
if (item == obj) {
return probe_index;
}
if (item == UNUSED_SLOT) {
break;
}
if (item == TOMBSTONE && tombstone < 0) {
tombstone = (intptr_t)probe_index;
}
step++;
if (step > probe_mask) {
break;
}
probe_index = (probe_index + step) & probe_mask;
}
if (tombstone >= 0) {
return (size_t)tombstone;
}
return probe_index;
}
} // namespace
PointerSetBase::PointerSetBase() :
mShift(internal::kMinShift),
mCount(0),
mItems(NULL) {
size_t capacity = 1U << mShift;
mItems = static_cast<void**>(::calloc(capacity, sizeof(mItems[0])));
}
PointerSetBase::~PointerSetBase() {
mCount = 0;
mShift = 0;
::free(mItems);
}
PointerSetBase::Iterator::Iterator(PointerSetBase* set) :
mItems(set->mItems),
mCapacity(1U << set->mShift),
mPos(0U) {}
void* PointerSetBase::Iterator::next() {
while (mPos < mCapacity) {
void* item = mItems[mPos++];
if (validValue(item)) {
return item;
}
}
return NULL;
}
bool PointerSetBase::contains(const void* obj, HashFunction hashFunc) const {
if (!obj) {
return false;
}
size_t objHash = hashFunc(obj);
size_t pos = probeItems(obj, objHash, mItems, mShift);
DCHECK(pos < (1U << mShift));
return validValue(mItems[pos]);
}
void PointerSetBase::clear() {
mCount = 0;
mShift = internal::kMinShift;
size_t capacity = 1U << mShift;
mItems = static_cast<void**>(
::realloc(mItems, sizeof(mItems[0]) * capacity));
for (size_t n = 0; n < capacity; ++n) {
mItems[n] = UNUSED_SLOT;
}
}
void* PointerSetBase::addItem(void* obj, HashFunction hashFunc) {
if (!validValue(obj)) {
return NULL;
}
if(mCount >= (1U << mShift)) maybeResize(hashFunc);
size_t objHash = hashFunc(obj);
size_t pos = probeItems(obj, objHash, mItems, mShift);
DCHECK(pos < (1U << mShift));
void* result = mItems[pos];
if (validValue(result)) {
// Simple replacement.
DCHECK(result == obj);
return result;
}
mItems[pos] = obj;
mCount++;
if (result != TOMBSTONE) {
// No need to resize when replacing tombstone.
maybeResize(hashFunc);
}
return NULL;
}
void* PointerSetBase::removeItem(void* obj, HashFunction hashFunc) {
if (!validValue(obj)) {
return NULL;
}
size_t objHash = hashFunc(obj);
size_t pos = probeItems(obj, objHash, mItems, mShift);
DCHECK(pos < (1U << mShift));
void* result = mItems[pos];
if (!validValue(result)) {
// Item was not in the array.
return NULL;
}
mItems[pos] = TOMBSTONE;
mCount--;
return result;
}
void** PointerSetBase::toArray() const {
if (!mCount) {
return NULL;
}
void** result = static_cast<void**>(::calloc(mCount, sizeof(mItems[0])));
size_t capacity = 1U << mShift;
size_t count = 0;
for (size_t n = 0; n < capacity; ++n) {
void* item = mItems[n];
if (!validValue(item)) {
continue;
}
result[count++] = item;
}
DCHECK(count == mCount);
return result;
}
void PointerSetBase::maybeResize(HashFunction hashFunc) {
size_t newShift = internal::hashShiftAdjust(mCount, mShift);
if (newShift == mShift) {
// Nothing to resize
return;
}
size_t capacity = 1U << mShift;
size_t newCapacity = 1U << newShift;
void** newItems = static_cast<void**>(
::calloc(newCapacity, sizeof(newItems[0])));
for (size_t n = 0; n < capacity; ++n) {
void* item = mItems[n];
if (!validValue(item)) {
continue;
}
size_t itemHash = hashFunc(item);
size_t slot = probeItems(item, itemHash, newItems, newShift);
DCHECK(!validValue(newItems[slot]));
newItems[slot] = item;
}
::free(mItems);
mItems = newItems;
mShift = newShift;
}
} // namespace base
} // namespace android