xref: /haiku/src/add-ons/kernel/file_systems/packagefs/util/StringPool.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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
27 StringData::Init()
28 {
29 	fEmptyString = new(sEmptyStringBuffer) StringData(StringDataKey("", 0));
30 }
31 
32 
33 // #pragma mark - StringPool
34 
35 
36 /*static*/ status_t
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
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*
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*
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
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
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