⛏️ index : haiku.git

/*
 * 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() + 1);
		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[];
};


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