File: CStrIntern.cpp

package info (click to toggle)
0ad 0.0.23.1-5
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 78,412 kB
  • sloc: cpp: 245,162; ansic: 200,249; javascript: 19,244; python: 13,754; sh: 6,104; perl: 4,620; makefile: 977; xml: 810; java: 533; ruby: 229; erlang: 46; pascal: 30; sql: 21; tcl: 4
file content (158 lines) | stat: -rw-r--r-- 3,949 bytes parent folder | download | duplicates (4)
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;
}