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