/*
 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */
#ifndef STRING_POOL_H
#define STRING_POOL_H
 
 
#include <SupportDefs.h>
 
#include <stdlib.h>
#include <string.h>
 
#include <new>
 
#include <util/AutoLock.h>
#include <util/OpenHashTable.h>
#include <util/StringHash.h>
 
 
class StringData;
 
 
class StringDataKey {
public:
	StringDataKey(const char* string, size_t length)
		:
		fString(string),
		fLength(length),
		fHash(hash_hash_string_part(string, length))
	{
	}
 
	const char* String() const
	{
		return fString;
	}
 
	size_t Length() const
	{
		return fLength;
	}
 
	uint32 Hash() const
	{
		return fHash;
	}
 
private:
	const char*	fString;
	size_t		fLength;
	uint32		fHash;
};
 
 
struct StringDataHashDefinition;
typedef BOpenHashTable<StringDataHashDefinition> StringDataHash;
 
 
class StringPool {
public:
	static	status_t			Init();
	static	void				Cleanup();
 
	static	StringData*			Get(const char* string, size_t length);
	static	void				LastReferenceReleased(StringData* data);
 
	static	void				DumpUsageStatistics();
 
private:
	static	StringData*			_GetLocked(const StringDataKey& key);
 
private:
	static	mutex				sLock;
	static	StringDataHash*		sStrings;
};
 
 
class StringData {
public:
	static void Init();
 
	static StringData* Create(const StringDataKey& key)
	{
		void* data = malloc(sizeof(StringData) + key.Length());
		if (data == NULL)
			return NULL;
 
		return new(data) StringData(key);
	}
 
	static StringData* Empty()
	{
		return &fEmptyString;
	}
 
	static StringData* GetEmpty()
	{
		fEmptyString.AcquireReference();
		return &fEmptyString;
	}
 
	void Delete()
	{
		free(this);
	}
 
	bool AcquireReference()
	{
		return atomic_add(&fReferenceCount, 1) == 0;
	}
 
	void ReleaseReference()
	{
		if (atomic_add(&fReferenceCount, -1) == 1)
			StringPool::LastReferenceReleased(this);
	}
 
	// debugging only
	int32 CountReferences() const
	{
		return fReferenceCount;
	}
 
	const char* String() const
	{
		return fString;
	}
 
	uint32 Hash() const
	{
		return fHash;
	}
 
	StringData*& HashNext()
	{
		return fHashNext;
	}
 
private:
	StringData(const StringDataKey& key)
		:
		fReferenceCount(1),
		fHash(key.Hash())
	{
		memcpy(fString, key.String(), key.Length());
		fString[key.Length()] = '\0';
	}
 
	~StringData()
	{
	}
 
private:
	static StringData	fEmptyString;
 
	StringData*	fHashNext;
	int32		fReferenceCount;
	uint32		fHash;
	char		fString[1];
};
 
 
struct StringDataHashDefinition {
	typedef StringDataKey	KeyType;
	typedef	StringData		ValueType;
 
	size_t HashKey(const StringDataKey& key) const
	{
		return key.Hash();
	}
 
	size_t Hash(const StringData* value) const
	{
		return value->Hash();
	}
 
	bool Compare(const StringDataKey& key, const StringData* value) const
	{
		return key.Hash() == value->Hash()
			&& strncmp(value->String(), key.String(), key.Length()) == 0
			&& value->String()[key.Length()] == '\0';
	}
 
	StringData*& GetLink(StringData* value) const
	{
		return value->HashNext();
	}
};
 
 
#endif	// STRING_POOL_H

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fHashNext.