352 lines
8.0 KiB
C++
352 lines
8.0 KiB
C++
/**
|
|
* @file llstringtable.cpp
|
|
* @brief The LLStringTable class provides a _fast_ method for finding
|
|
* unique copies of strings.
|
|
*
|
|
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "linden_common.h"
|
|
|
|
#include "llstringtable.h"
|
|
#include "llstl.h"
|
|
|
|
LLStringTable gStringTable(32768);
|
|
|
|
LLStringTableEntry::LLStringTableEntry(const char *str)
|
|
: mString(NULL), mCount(1)
|
|
{
|
|
// Copy string
|
|
U32 length = (U32)strlen(str) + 1; /*Flawfinder: ignore*/
|
|
length = llmin(length, MAX_STRINGS_LENGTH);
|
|
mString = new char[length];
|
|
strncpy(mString, str, length); /*Flawfinder: ignore*/
|
|
mString[length - 1] = 0;
|
|
}
|
|
|
|
LLStringTableEntry::~LLStringTableEntry()
|
|
{
|
|
delete [] mString;
|
|
mCount = 0;
|
|
}
|
|
|
|
LLStringTable::LLStringTable(int tablesize)
|
|
: mUniqueEntries(0)
|
|
{
|
|
S32 i;
|
|
if (!tablesize)
|
|
tablesize = 4096; // some arbitrary default
|
|
// Make sure tablesize is power of 2
|
|
for (i = 31; i>0; i--)
|
|
{
|
|
if (tablesize & (1<<i))
|
|
{
|
|
if (tablesize >= (3<<(i-1)))
|
|
tablesize = (1<<(i+1));
|
|
else
|
|
tablesize = (1<<i);
|
|
break;
|
|
}
|
|
}
|
|
mMaxEntries = tablesize;
|
|
|
|
#if !STRING_TABLE_HASH_MAP
|
|
// ALlocate strings
|
|
mStringList = new string_list_ptr_t[mMaxEntries];
|
|
// Clear strings
|
|
for (i = 0; i < mMaxEntries; i++)
|
|
{
|
|
mStringList[i] = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
LLStringTable::~LLStringTable()
|
|
{
|
|
#if !STRING_TABLE_HASH_MAP
|
|
if (mStringList)
|
|
{
|
|
for (S32 i = 0; i < mMaxEntries; i++)
|
|
{
|
|
if (mStringList[i])
|
|
{
|
|
for (LLStringTableEntry* entry : *mStringList[i])
|
|
delete entry;
|
|
}
|
|
delete mStringList[i];
|
|
}
|
|
delete [] mStringList;
|
|
mStringList = NULL;
|
|
}
|
|
#else
|
|
// Need to clean up the string hash
|
|
for_each(mStringHash.begin(), mStringHash.end(), DeletePairedPointer());
|
|
mStringHash.clear();
|
|
#endif
|
|
}
|
|
|
|
|
|
static U32 hash_my_string(const char *str, int max_entries)
|
|
{
|
|
U32 retval = 0;
|
|
#if 0
|
|
while (*str)
|
|
{
|
|
retval <<= 1;
|
|
retval += *str++;
|
|
}
|
|
#else
|
|
while (*str)
|
|
{
|
|
retval = (retval<<4) + *str;
|
|
U32 x = (retval & 0xf0000000);
|
|
if (x) retval = retval ^ (x>>24);
|
|
retval = retval & (~x);
|
|
str++;
|
|
}
|
|
#endif
|
|
return (retval & (max_entries-1)); // max_entries is gauranteed to be power of 2
|
|
}
|
|
|
|
char* LLStringTable::checkString(const std::string& str)
|
|
{
|
|
return checkString(str.c_str());
|
|
}
|
|
|
|
char* LLStringTable::checkString(const char *str)
|
|
{
|
|
LLStringTableEntry* entry = checkStringEntry(str);
|
|
if (entry)
|
|
{
|
|
return entry->mString;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
LLStringTableEntry* LLStringTable::checkStringEntry(const std::string& str)
|
|
{
|
|
return checkStringEntry(str.c_str());
|
|
}
|
|
|
|
LLStringTableEntry* LLStringTable::checkStringEntry(const char *str)
|
|
{
|
|
if (str)
|
|
{
|
|
char *ret_val;
|
|
U32 hash_value = hash_my_string(str, mMaxEntries);
|
|
#if STRING_TABLE_HASH_MAP
|
|
LLStringTableEntry *entry;
|
|
#if 1 // Microsoft
|
|
string_hash_t::iterator lower = mStringHash.lower_bound(hash_value);
|
|
string_hash_t::iterator upper = mStringHash.upper_bound(hash_value);
|
|
#else // stlport
|
|
std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value);
|
|
string_hash_t::iterator lower = P.first;
|
|
string_hash_t::iterator upper = P.second;
|
|
#endif
|
|
for (string_hash_t::iterator iter = lower; iter != upper; iter++)
|
|
{
|
|
entry = iter->second;
|
|
ret_val = entry->mString;
|
|
if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
|
|
{
|
|
return entry;
|
|
}
|
|
}
|
|
#else
|
|
string_list_t *strlist = mStringList[hash_value];
|
|
if (strlist)
|
|
{
|
|
for (LLStringTableEntry* entry : *strlist)
|
|
{
|
|
ret_val = entry->mString;
|
|
if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
|
|
{
|
|
return entry;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char* LLStringTable::addString(const std::string& str)
|
|
{
|
|
//RN: safe to use temporary c_str since string is copied
|
|
return addString(str.c_str());
|
|
}
|
|
|
|
char* LLStringTable::addString(const char *str)
|
|
{
|
|
|
|
LLStringTableEntry* entry = addStringEntry(str);
|
|
if (entry)
|
|
{
|
|
return entry->mString;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
LLStringTableEntry* LLStringTable::addStringEntry(const std::string& str)
|
|
{
|
|
return addStringEntry(str.c_str());
|
|
}
|
|
|
|
LLStringTableEntry* LLStringTable::addStringEntry(const char *str)
|
|
{
|
|
if (str)
|
|
{
|
|
char *ret_val = NULL;
|
|
U32 hash_value = hash_my_string(str, mMaxEntries);
|
|
#if STRING_TABLE_HASH_MAP
|
|
LLStringTableEntry *entry;
|
|
#if 1 // Microsoft
|
|
string_hash_t::iterator lower = mStringHash.lower_bound(hash_value);
|
|
string_hash_t::iterator upper = mStringHash.upper_bound(hash_value);
|
|
#else // stlport
|
|
std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value);
|
|
string_hash_t::iterator lower = P.first;
|
|
string_hash_t::iterator upper = P.second;
|
|
#endif
|
|
for (string_hash_t::iterator iter = lower; iter != upper; iter++)
|
|
{
|
|
entry = iter->second;
|
|
ret_val = entry->mString;
|
|
if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
|
|
{
|
|
entry->incCount();
|
|
return entry;
|
|
}
|
|
}
|
|
|
|
// not found, so add!
|
|
LLStringTableEntry* newentry = new LLStringTableEntry(str);
|
|
ret_val = newentry->mString;
|
|
mStringHash.insert(string_hash_t::value_type(hash_value, newentry));
|
|
#else
|
|
string_list_t *strlist = mStringList[hash_value];
|
|
|
|
if (strlist)
|
|
{
|
|
for (LLStringTableEntry* entry : *strlist)
|
|
{
|
|
ret_val = entry->mString;
|
|
if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
|
|
{
|
|
entry->incCount();
|
|
return entry;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mStringList[hash_value] = new string_list_t;
|
|
strlist = mStringList[hash_value];
|
|
}
|
|
|
|
// not found, so add!
|
|
LLStringTableEntry *newentry = new LLStringTableEntry(str);
|
|
//ret_val = newentry->mString;
|
|
strlist->push_front(newentry);
|
|
#endif
|
|
mUniqueEntries++;
|
|
return newentry;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void LLStringTable::removeString(const char *str)
|
|
{
|
|
if (str)
|
|
{
|
|
char *ret_val;
|
|
U32 hash_value = hash_my_string(str, mMaxEntries);
|
|
#if STRING_TABLE_HASH_MAP
|
|
{
|
|
LLStringTableEntry *entry;
|
|
#if 1 // Microsoft
|
|
string_hash_t::iterator lower = mStringHash.lower_bound(hash_value);
|
|
string_hash_t::iterator upper = mStringHash.upper_bound(hash_value);
|
|
#else // stlport
|
|
std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value);
|
|
string_hash_t::iterator lower = P.first;
|
|
string_hash_t::iterator upper = P.second;
|
|
#endif
|
|
for (string_hash_t::iterator iter = lower; iter != upper; iter++)
|
|
{
|
|
entry = iter->second;
|
|
ret_val = entry->mString;
|
|
if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
|
|
{
|
|
if (!entry->decCount())
|
|
{
|
|
mUniqueEntries--;
|
|
if (mUniqueEntries < 0)
|
|
{
|
|
LL_ERRS() << "LLStringTable:removeString trying to remove too many strings!" << LL_ENDL;
|
|
}
|
|
delete iter->second;
|
|
mStringHash.erase(iter);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
string_list_t *strlist = mStringList[hash_value];
|
|
|
|
if (strlist)
|
|
{
|
|
for (LLStringTableEntry* entry : *strlist)
|
|
{
|
|
ret_val = entry->mString;
|
|
if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
|
|
{
|
|
if (!entry->decCount())
|
|
{
|
|
mUniqueEntries--;
|
|
if (mUniqueEntries < 0)
|
|
{
|
|
LL_ERRS() << "LLStringTable:removeString trying to remove too many strings!" << LL_ENDL;
|
|
}
|
|
strlist->remove(entry);
|
|
delete entry;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|