/* * 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. */ /* * String interning. */ #include "Dalvik.h" #include <stddef.h> /* * Prep string interning. */ bool dvmStringInternStartup(void) { dvmInitMutex(&gDvm.internLock); gDvm.internedStrings = dvmHashTableCreate(256, NULL); if (gDvm.internedStrings == NULL) return false; gDvm.literalStrings = dvmHashTableCreate(256, NULL); if (gDvm.literalStrings == NULL) return false; return true; } /* * Chuck the intern list. * * The contents of the list are StringObjects that live on the GC heap. */ void dvmStringInternShutdown(void) { if (gDvm.internedStrings != NULL || gDvm.literalStrings != NULL) { dvmDestroyMutex(&gDvm.internLock); } dvmHashTableFree(gDvm.internedStrings); gDvm.internedStrings = NULL; dvmHashTableFree(gDvm.literalStrings); gDvm.literalStrings = NULL; } static StringObject* lookupInternedString(StringObject* strObj, bool isLiteral) { StringObject* found; u4 hash; assert(strObj != NULL); hash = dvmComputeStringHash(strObj); dvmLockMutex(&gDvm.internLock); if (isLiteral) { /* * Check the literal table for a match. */ StringObject* literal = dvmHashTableLookup(gDvm.literalStrings, hash, strObj, dvmHashcmpStrings, false); if (literal != NULL) { /* * A match was found in the literal table, the easy case. */ found = literal; } else { /* * There is no match in the literal table, check the * interned string table. */ StringObject* interned = dvmHashTableLookup(gDvm.internedStrings, hash, strObj, dvmHashcmpStrings, false); if (interned != NULL) { /* * A match was found in the interned table. Move the * matching string to the literal table. */ dvmHashTableRemove(gDvm.internedStrings, hash, interned); found = dvmHashTableLookup(gDvm.literalStrings, hash, interned, dvmHashcmpStrings, true); assert(found == interned); } else { /* * No match in the literal table or the interned * table. Insert into the literal table. */ found = dvmHashTableLookup(gDvm.literalStrings, hash, strObj, dvmHashcmpStrings, true); assert(found == strObj); } } } else { /* * Check the literal table for a match. */ found = dvmHashTableLookup(gDvm.literalStrings, hash, strObj, dvmHashcmpStrings, false); if (found == NULL) { /* * No match was found in the literal table. Insert into * the intern table. */ found = dvmHashTableLookup(gDvm.internedStrings, hash, strObj, dvmHashcmpStrings, true); } } assert(found != NULL); dvmUnlockMutex(&gDvm.internLock); return found; } /* * Find an entry in the interned string table. * * If the string doesn't already exist, the StringObject is added to * the table. Otherwise, the existing entry is returned. */ StringObject* dvmLookupInternedString(StringObject* strObj) { return lookupInternedString(strObj, false); } /* * Same as dvmLookupInternedString(), but guarantees that the * returned string is a literal. */ StringObject* dvmLookupImmortalInternedString(StringObject* strObj) { return lookupInternedString(strObj, true); } /* * Returns true if the object is a weak interned string. Any string * interned by the user is weak. */ bool dvmIsWeakInternedString(const StringObject* strObj) { StringObject* found; u4 hash; assert(strObj != NULL); if (gDvm.internedStrings == NULL) { return false; } dvmLockMutex(&gDvm.internLock); hash = dvmComputeStringHash(strObj); found = dvmHashTableLookup(gDvm.internedStrings, hash, (void*)strObj, dvmHashcmpStrings, false); dvmUnlockMutex(&gDvm.internLock); return found == strObj; } static int markStringObject(void* strObj, void* arg) { UNUSED_PARAMETER(arg); dvmMarkObjectNonNull(strObj); return 0; } /* * Blacken string references from the literal string table. The * literal table is a root. */ void dvmGcScanInternedStrings() { /* It's possible for a GC to happen before dvmStringInternStartup() * is called. */ if (gDvm.literalStrings != NULL) { dvmHashForeach(gDvm.literalStrings, markStringObject, NULL); } } /* * Clear white references from the intern table. */ void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *)) { /* It's possible for a GC to happen before dvmStringInternStartup() * is called. */ if (gDvm.internedStrings != NULL) { dvmLockMutex(&gDvm.internLock); dvmHashForeachRemove(gDvm.internedStrings, isUnmarkedObject); dvmUnlockMutex(&gDvm.internLock); } }