1/*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5#ifndef STRING_POOL_H
6#define STRING_POOL_H
7
8
9#include <SupportDefs.h>
10
11#include <stdlib.h>
12#include <string.h>
13
14#include <new>
15
16#include <util/AutoLock.h>
17#include <util/OpenHashTable.h>
18#include <util/StringHash.h>
19
20
21class StringData;
22
23
24class StringDataKey {
25public:
26	StringDataKey(const char* string, size_t length)
27		:
28		fString(string),
29		fLength(length),
30		fHash(hash_hash_string_part(string, length))
31	{
32	}
33
34	const char* String() const
35	{
36		return fString;
37	}
38
39	size_t Length() const
40	{
41		return fLength;
42	}
43
44	uint32 Hash() const
45	{
46		return fHash;
47	}
48
49private:
50	const char*	fString;
51	size_t		fLength;
52	uint32		fHash;
53};
54
55
56struct StringDataHashDefinition;
57typedef BOpenHashTable<StringDataHashDefinition> StringDataHash;
58
59
60class StringPool {
61public:
62	static	status_t			Init();
63	static	void				Cleanup();
64
65	static	StringData*			Get(const char* string, size_t length);
66	static	void				LastReferenceReleased(StringData* data);
67
68	static	void				DumpUsageStatistics();
69
70private:
71	static	StringData*			_GetLocked(const StringDataKey& key);
72
73private:
74	static	mutex				sLock;
75	static	StringDataHash*		sStrings;
76};
77
78
79class StringData {
80public:
81	static void Init();
82
83	static StringData* Create(const StringDataKey& key)
84	{
85		void* data = malloc(sizeof(StringData) + key.Length());
86		if (data == NULL)
87			return NULL;
88
89		return new(data) StringData(key);
90	}
91
92	static StringData* Empty()
93	{
94		return &fEmptyString;
95	}
96
97	static StringData* GetEmpty()
98	{
99		fEmptyString.AcquireReference();
100		return &fEmptyString;
101	}
102
103	void Delete()
104	{
105		free(this);
106	}
107
108	bool AcquireReference()
109	{
110		return atomic_add(&fReferenceCount, 1) == 0;
111	}
112
113	void ReleaseReference()
114	{
115		if (atomic_add(&fReferenceCount, -1) == 1)
116			StringPool::LastReferenceReleased(this);
117	}
118
119	// debugging only
120	int32 CountReferences() const
121	{
122		return fReferenceCount;
123	}
124
125	const char* String() const
126	{
127		return fString;
128	}
129
130	uint32 Hash() const
131	{
132		return fHash;
133	}
134
135	StringData*& HashNext()
136	{
137		return fHashNext;
138	}
139
140private:
141	StringData(const StringDataKey& key)
142		:
143		fReferenceCount(1),
144		fHash(key.Hash())
145	{
146		memcpy(fString, key.String(), key.Length());
147		fString[key.Length()] = '\0';
148	}
149
150	~StringData()
151	{
152	}
153
154private:
155	static StringData	fEmptyString;
156
157	StringData*	fHashNext;
158	int32		fReferenceCount;
159	uint32		fHash;
160	char		fString[1];
161};
162
163
164struct StringDataHashDefinition {
165	typedef StringDataKey	KeyType;
166	typedef	StringData		ValueType;
167
168	size_t HashKey(const StringDataKey& key) const
169	{
170		return key.Hash();
171	}
172
173	size_t Hash(const StringData* value) const
174	{
175		return value->Hash();
176	}
177
178	bool Compare(const StringDataKey& key, const StringData* value) const
179	{
180		return key.Hash() == value->Hash()
181			&& strncmp(value->String(), key.String(), key.Length()) == 0
182			&& value->String()[key.Length()] == '\0';
183	}
184
185	StringData*& GetLink(StringData* value) const
186	{
187		return value->HashNext();
188	}
189};
190
191
192#endif	// STRING_POOL_H
193