1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CStrIntern.h"
#include "lib/fnv_hash.h"
#include "ps/CLogger.h"
#include <boost/unordered_map.hpp>
class CStrInternInternals
{
public:
CStrInternInternals(const char* str, size_t len)
: data(str, str+len), hash(fnv_hash(str, len))
{
// LOGWARNING("New interned string '%s'", data.c_str());
}
bool operator==(const CStrInternInternals& b) const
{
// Compare hash first for quick rejection of inequal strings
return (hash == b.hash && data == b.data);
}
const std::string data;
const u32 hash; // fnv_hash of data
private:
CStrInternInternals& operator=(const CStrInternInternals&);
};
// Interned strings are stored in a hash table, indexed by string:
typedef std::string StringsKey;
struct StringsKeyHash
{
size_t operator()(const StringsKey& key) const
{
return fnv_hash(key.c_str(), key.length());
}
};
// To avoid std::string memory allocations when GetString does lookups in the
// hash table of interned strings, we make use of boost::unordered_map's ability
// to do lookups with a functionally equivalent proxy object:
struct StringsKeyProxy
{
const char* str;
size_t len;
};
struct StringsKeyProxyHash
{
size_t operator()(const StringsKeyProxy& key) const
{
return fnv_hash(key.str, key.len);
}
};
struct StringsKeyProxyEq
{
bool operator()(const StringsKeyProxy& proxy, const StringsKey& key) const
{
return (proxy.len == key.length() && memcmp(proxy.str, key.c_str(), proxy.len) == 0);
}
};
static boost::unordered_map<StringsKey, shared_ptr<CStrInternInternals>, StringsKeyHash> g_Strings;
#define X(id) CStrIntern str_##id(#id);
#define X2(id, str) CStrIntern str_##id(str);
#include "CStrInternStatic.h"
#undef X
#undef X2
static CStrInternInternals* GetString(const char* str, size_t len)
{
// g_Strings is not thread-safe, so complain if anyone is using this
// type in non-main threads. (If that's desired, g_Strings should be changed
// to be thread-safe, preferably without sacrificing performance.)
ENSURE(ThreadUtil::IsMainThread());
#if BOOST_VERSION >= 104200
StringsKeyProxy proxy = { str, len };
boost::unordered_map<StringsKey, shared_ptr<CStrInternInternals> >::iterator it =
g_Strings.find(proxy, StringsKeyProxyHash(), StringsKeyProxyEq());
#else
// Boost <= 1.41 doesn't support the new find(), so do a slightly less efficient lookup
boost::unordered_map<StringsKey, shared_ptr<CStrInternInternals> >::iterator it =
g_Strings.find(str);
#endif
if (it != g_Strings.end())
return it->second.get();
shared_ptr<CStrInternInternals> internals(new CStrInternInternals(str, len));
g_Strings.insert(std::make_pair(internals->data, internals));
return internals.get();
}
CStrIntern::CStrIntern()
{
*this = str__emptystring;
}
CStrIntern::CStrIntern(const char* str)
{
m = GetString(str, strlen(str));
}
CStrIntern::CStrIntern(const std::string& str)
{
m = GetString(str.c_str(), str.length());
}
u32 CStrIntern::GetHash() const
{
return m->hash;
}
const char* CStrIntern::c_str() const
{
return m->data.c_str();
}
size_t CStrIntern::length() const
{
return m->data.length();
}
bool CStrIntern::empty() const
{
return m->data.empty();
}
const std::string& CStrIntern::string() const
{
return m->data;
}
|