diff --git a/source/ps/CStrIntern.cpp b/source/ps/CStrIntern.cpp new file mode 100644 index 0000000000..3c18b32757 --- /dev/null +++ b/source/ps/CStrIntern.cpp @@ -0,0 +1,137 @@ +/* Copyright (C) 2012 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 . + */ + +#include "precompiled.h" + +#include "CStrIntern.h" + +#include "lib/fnv_hash.h" +#include "ps/CLogger.h" + +#include + +class CStrInternInternals +{ +public: + CStrInternInternals(const char* str, size_t len) + : data(str, str+len), hash(fnv_hash(str, len)) + { +// LOGWARNING(L"New interned string '%hs'", 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, StringsKeyHash> g_Strings; + + +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()); + + StringsKeyProxy proxy = { str, len }; + boost::unordered_map >::iterator it = + g_Strings.find(proxy, StringsKeyProxyHash(), StringsKeyProxyEq()); + + if (it != g_Strings.end()) + return it->second.get(); + + shared_ptr internals(new CStrInternInternals(str, len)); + g_Strings.insert(std::make_pair(internals->data, internals)); + return internals.get(); +} + +CStrIntern::CStrIntern() +{ + m = GetString("", 0); +} + +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(); +} + +const std::string& CStrIntern::string() const +{ + return m->data; +} diff --git a/source/ps/CStrIntern.h b/source/ps/CStrIntern.h new file mode 100644 index 0000000000..10f63405e4 --- /dev/null +++ b/source/ps/CStrIntern.h @@ -0,0 +1,81 @@ +/* Copyright (C) 2012 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 . + */ + +#ifndef INCLUDED_CSTRINTERN +#define INCLUDED_CSTRINTERN + +class CStrInternInternals; + +/** + * Interned 8-bit strings. + * Each instance with the same string content is a pointer to the same piece of + * memory, allowing very fast string comparisons. + * + * Since a CStrIntern is just a dumb pointer, copying is very fast, + * and pass-by-value should be preferred over pass-by-reference. + * + * Memory allocated for strings will never be freed, so don't use this for + * unbounded numbers of strings (e.g. text rendered by gameplay scripts) - + * it's intended for a small number of short frequently-used strings. + * + * Not thread-safe - only allocate these strings from the main thread. + */ +class CStrIntern +{ +public: + CStrIntern(); + explicit CStrIntern(const char* str); + explicit CStrIntern(const std::string& str); + + /** + * Returns cached FNV1-A hash of the string. + */ + u32 GetHash() const; + + /** + * Returns null-terminated string. + */ + const char* c_str() const; + + /** + * Returns as std::string. + */ + const std::string& string() const; + + /** + * String equality. + */ + bool operator==(const CStrIntern& b) const + { + return m == b.m; + } + + /** + * Compare with some arbitrary total order. + * (In particular, this is not alphabetic order, + * and is not consistent between runs of the game.) + */ + bool operator<(const CStrIntern& b) const + { + return m < b.m; + } + +private: + CStrInternInternals* m; +}; + +#endif // INCLUDED_CSTRINTERN \ No newline at end of file