/* * Copyright (C) 2008 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. */ /* * Maintain an expanding set of unique pointer values. */ #include "Dalvik.h" /* * Sorted, expanding list of pointers. */ struct PointerSet { u2 alloc; u2 count; const void** list; }; /* * Verify that the set is in sorted order. */ #ifndef NDEBUG static bool verifySorted(PointerSet* pSet) { const void* last = NULL; int i; for (i = 0; i < pSet->count; i++) { const void* cur = pSet->list[i]; if (cur < last) return false; last = cur; } return true; } #endif /* * Allocate a new PointerSet. * * Returns NULL on failure. */ PointerSet* dvmPointerSetAlloc(int initialSize) { PointerSet* pSet = (PointerSet*)calloc(1, sizeof(PointerSet)); if (pSet != NULL) { if (initialSize > 0) { pSet->list = (const void**)malloc(sizeof(void*) * initialSize); if (pSet->list == NULL) { free(pSet); return NULL; } pSet->alloc = initialSize; } } return pSet; } /* * Free up a PointerSet. */ void dvmPointerSetFree(PointerSet* pSet) { if (pSet == NULL) return; if (pSet->list != NULL) { free(pSet->list); pSet->list = NULL; } free(pSet); } /* * Clear the contents of a pointer set. */ void dvmPointerSetClear(PointerSet* pSet) { pSet->count = 0; } /* * Get the number of pointers currently stored in the list. */ int dvmPointerSetGetCount(const PointerSet* pSet) { return pSet->count; } /* * Get the Nth entry from the list. */ const void* dvmPointerSetGetEntry(const PointerSet* pSet, int i) { return pSet->list[i]; } /* * Insert a new entry into the list. If it already exists, this returns * without doing anything. * * Returns "true" if the value was added. */ bool dvmPointerSetAddEntry(PointerSet* pSet, const void* ptr) { int nearby; if (dvmPointerSetHas(pSet, ptr, &nearby)) return false; /* ensure we have space to add one more */ if (pSet->count == pSet->alloc) { /* time to expand */ const void** newList; if (pSet->alloc == 0) pSet->alloc = 4; else pSet->alloc *= 2; LOGVV("expanding %p to %d", pSet, pSet->alloc); newList = (const void**)realloc(pSet->list, pSet->alloc * sizeof(void*)); if (newList == NULL) { ALOGE("Failed expanding ptr set (alloc=%d)", pSet->alloc); dvmAbort(); } pSet->list = newList; } if (pSet->count == 0) { /* empty list */ assert(nearby == 0); } else { /* * Determine the insertion index. The binary search might have * terminated "above" or "below" the value. */ if (nearby != 0 && ptr < pSet->list[nearby-1]) { //ALOGD("nearby-1=%d %p, inserting %p at -1", // nearby-1, pSet->list[nearby-1], ptr); nearby--; } else if (ptr < pSet->list[nearby]) { //ALOGD("nearby=%d %p, inserting %p at +0", // nearby, pSet->list[nearby], ptr); } else { //ALOGD("nearby+1=%d %p, inserting %p at +1", // nearby+1, pSet->list[nearby+1], ptr); nearby++; } /* * Move existing values, if necessary. */ if (nearby != pSet->count) { /* shift up */ memmove(&pSet->list[nearby+1], &pSet->list[nearby], (pSet->count - nearby) * sizeof(pSet->list[0])); } } pSet->list[nearby] = ptr; pSet->count++; assert(verifySorted(pSet)); return true; } /* * Returns "true" if the element was successfully removed. */ bool dvmPointerSetRemoveEntry(PointerSet* pSet, const void* ptr) { int where; if (!dvmPointerSetHas(pSet, ptr, &where)) return false; if (where != pSet->count-1) { /* shift down */ memmove(&pSet->list[where], &pSet->list[where+1], (pSet->count-1 - where) * sizeof(pSet->list[0])); } pSet->count--; pSet->list[pSet->count] = (const void*) 0xdecadead; // debug return true; } /* * Returns the index if "ptr" appears in the list. If it doesn't appear, * this returns a negative index for a nearby element. */ bool dvmPointerSetHas(const PointerSet* pSet, const void* ptr, int* pIndex) { int hi, lo, mid; lo = mid = 0; hi = pSet->count-1; /* array is sorted, use a binary search */ while (lo <= hi) { mid = (lo + hi) / 2; const void* listVal = pSet->list[mid]; if (ptr > listVal) { lo = mid + 1; } else if (ptr < listVal) { hi = mid - 1; } else /* listVal == ptr */ { if (pIndex != NULL) *pIndex = mid; return true; } } if (pIndex != NULL) *pIndex = mid; return false; } /* * Compute the intersection of the set and the array of pointers passed in. * * Any pointer in "pSet" that does not appear in "ptrArray" is removed. */ void dvmPointerSetIntersect(PointerSet* pSet, const void** ptrArray, int count) { int i, j; for (i = 0; i < pSet->count; i++) { for (j = 0; j < count; j++) { if (pSet->list[i] == ptrArray[j]) { /* match, keep this one */ break; } } if (j == count) { /* no match, remove entry */ if (i != pSet->count-1) { /* shift down */ memmove(&pSet->list[i], &pSet->list[i+1], (pSet->count-1 - i) * sizeof(pSet->list[0])); } pSet->count--; pSet->list[pSet->count] = (const void*) 0xdecadead; // debug i--; /* adjust loop counter */ } } } /* * Print the list contents to stdout. For debugging. */ void dvmPointerSetDump(const PointerSet* pSet) { ALOGI("PointerSet %p", pSet); int i; for (i = 0; i < pSet->count; i++) ALOGI(" %2d: %p", i, pSet->list[i]); }