xref: /haiku/src/add-ons/kernel/file_systems/packagefs/util/StringPool.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 
16 StringData StringData::fEmptyString(StringDataKey("", 0));
17 
18 mutex StringPool::sLock;
19 StringDataHash* StringPool::sStrings;
20 
21 
22 // #pragma mark - StringData
23 
24 
25 /*static*/ void
26 StringData::Init()
27 {
28 	new(&fEmptyString) StringData(StringDataKey("", 0));
29 }
30 
31 
32 // #pragma mark - StringPool
33 
34 
35 /*static*/ status_t
36 StringPool::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
56 StringPool::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*
68 StringPool::_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*
84 StringPool::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
113 StringPool::LastReferenceReleased(StringData* data)
114 {
115 	MutexLocker locker(sLock);
116 	sStrings->Remove(data);
117 	locker.Unlock();
118 	data->Delete();
119 }
120 
121 
122 /*static*/ void
123 StringPool::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