| /* |
| * 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]); |
| } |