/** * @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 } }