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