1/*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "StringPool.h"
8
9#include "DebugSupport.h"
10
11
12static const size_t kInitialStringTableSize = 128;
13
14static char sStringsBuffer[sizeof(StringDataHash)];
15
16StringData StringData::fEmptyString(StringDataKey("", 0));
17
18mutex StringPool::sLock;
19StringDataHash* StringPool::sStrings;
20
21
22// #pragma mark - StringData
23
24
25/*static*/ void
26StringData::Init()
27{
28	new(&fEmptyString) StringData(StringDataKey("", 0));
29}
30
31
32// #pragma mark - StringPool
33
34
35/*static*/ status_t
36StringPool::Init()
37{
38	sStrings = new(sStringsBuffer) StringDataHash;
39	status_t error = sStrings->Init(kInitialStringTableSize);
40	if (error != B_OK) {
41		sStrings->~StringDataHash();
42		sStrings = NULL;
43		return error;
44	}
45
46	mutex_init(&sLock, "string pool");
47
48	StringData::Init();
49	sStrings->Insert(StringData::Empty());
50
51	return B_OK;
52}
53
54
55/*static*/ void
56StringPool::Cleanup()
57{
58	sStrings->Remove(StringData::Empty());
59
60	sStrings->~StringDataHash();
61	sStrings = NULL;
62
63	mutex_destroy(&sLock);
64}
65
66
67/*static*/ inline StringData*
68StringPool::_GetLocked(const StringDataKey& key)
69{
70	if (StringData* string = sStrings->Lookup(key)) {
71		if (!string->AcquireReference())
72			return string;
73
74		// The object was fully dereferenced and will be deleted. Remove it
75		// from the hash table, so it isn't in the way.
76		sStrings->Remove(string);
77	}
78
79	return NULL;
80}
81
82
83/*static*/ StringData*
84StringPool::Get(const char* string, size_t length)
85{
86	MutexLocker locker(sLock);
87	StringDataKey key(string, length);
88	StringData* data = _GetLocked(key);
89	if (data != NULL)
90		return data;
91
92	locker.Unlock();
93
94	StringData* newString = StringData::Create(key);
95	if (newString == NULL)
96		return NULL;
97
98	locker.Lock();
99
100	data = _GetLocked(key);
101	if (data != NULL) {
102		locker.Unlock();
103		newString->Delete();
104		return data;
105	}
106
107	sStrings->Insert(newString);
108	return newString;
109}
110
111
112/*static*/ void
113StringPool::LastReferenceReleased(StringData* data)
114{
115	MutexLocker locker(sLock);
116	sStrings->Remove(data);
117	locker.Unlock();
118	data->Delete();
119}
120
121
122/*static*/ void
123StringPool::DumpUsageStatistics()
124{
125	size_t unsharedStringCount = 0;
126	size_t totalReferenceCount = 0;
127	size_t totalStringSize = 0;
128	size_t totalStringSizeWithDuplicates = 0;
129
130	MutexLocker locker(sLock);
131	for (StringDataHash::Iterator it = sStrings->GetIterator(); it.HasNext();) {
132		StringData* data = it.Next();
133		int32 referenceCount = data->CountReferences();
134		totalReferenceCount += referenceCount;
135		if (referenceCount == 1)
136			unsharedStringCount++;
137
138		size_t stringSize = strlen(data->String() + 1);
139		totalStringSize += stringSize;
140		totalStringSizeWithDuplicates += stringSize * referenceCount;
141	}
142
143	size_t stringCount = sStrings->CountElements();
144	size_t overhead = stringCount * (sizeof(StringData) - 1);
145
146	INFORM("StringPool usage:\n");
147	INFORM("  total unique strings:    %8zu, %8zu bytes, overhead: %zu bytes\n",
148		stringCount, totalStringSize, overhead);
149	INFORM("  total strings with dups: %8zu, %8zu bytes\n", totalReferenceCount,
150		totalStringSizeWithDuplicates);
151	INFORM("  unshared strings:        %8zu\n", unsharedStringCount);
152	INFORM("  bytes saved:             %8zd\n",
153		(ssize_t)(totalStringSizeWithDuplicates - totalStringSize - overhead));
154}
155