⛏️ index : haiku.git

/*
 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "StringPool.h"

#include "DebugSupport.h"


static const size_t kInitialStringTableSize = 128;

static char sStringsBuffer[sizeof(StringDataHash)];
static char sEmptyStringBuffer[sizeof(StringData) + 1];

StringData* StringData::fEmptyString;

mutex StringPool::sLock;
StringDataHash* StringPool::sStrings;


// #pragma mark - StringData


/*static*/ void
StringData::Init()
{
	fEmptyString = new(sEmptyStringBuffer) StringData(StringDataKey("", 0));
}


// #pragma mark - StringPool


/*static*/ status_t
StringPool::Init()
{
	sStrings = new(sStringsBuffer) StringDataHash;
	status_t error = sStrings->Init(kInitialStringTableSize);
	if (error != B_OK) {
		sStrings->~StringDataHash();
		sStrings = NULL;
		return error;
	}

	mutex_init(&sLock, "string pool");

	StringData::Init();
	sStrings->Insert(StringData::Empty());

	return B_OK;
}


/*static*/ void
StringPool::Cleanup()
{
	sStrings->Remove(StringData::Empty());

	sStrings->~StringDataHash();
	sStrings = NULL;

	mutex_destroy(&sLock);
}


/*static*/ inline StringData*
StringPool::_GetLocked(const StringDataKey& key)
{
	if (StringData* string = sStrings->Lookup(key)) {
		if (!string->AcquireReference())
			return string;

		// The object was fully dereferenced and will be deleted. Remove it
		// from the hash table, so it isn't in the way.
		sStrings->Remove(string);
	}

	return NULL;
}


/*static*/ StringData*
StringPool::Get(const char* string, size_t length)
{
	MutexLocker locker(sLock);
	StringDataKey key(string, length);
	StringData* data = _GetLocked(key);
	if (data != NULL)
		return data;

	locker.Unlock();

	StringData* newString = StringData::Create(key);
	if (newString == NULL)
		return NULL;

	locker.Lock();

	data = _GetLocked(key);
	if (data != NULL) {
		locker.Unlock();
		newString->Delete();
		return data;
	}

	sStrings->Insert(newString);
	return newString;
}


/*static*/ void
StringPool::LastReferenceReleased(StringData* data)
{
	MutexLocker locker(sLock);
	sStrings->Remove(data);
	locker.Unlock();
	data->Delete();
}


/*static*/ void
StringPool::DumpUsageStatistics()
{
	size_t unsharedStringCount = 0;
	size_t totalReferenceCount = 0;
	size_t totalStringSize = 0;
	size_t totalStringSizeWithDuplicates = 0;

	MutexLocker locker(sLock);
	for (StringDataHash::Iterator it = sStrings->GetIterator(); it.HasNext();) {
		StringData* data = it.Next();
		int32 referenceCount = data->CountReferences();
		totalReferenceCount += referenceCount;
		if (referenceCount == 1)
			unsharedStringCount++;

		size_t stringSize = strlen(data->String() + 1);
		totalStringSize += stringSize;
		totalStringSizeWithDuplicates += stringSize * referenceCount;
	}

	size_t stringCount = sStrings->CountElements();
	size_t overhead = stringCount * sizeof(StringData);
	size_t tableSize = sStrings->TableSize() * sizeof(void*);

	INFORM("StringPool usage:\n");
	INFORM("  total unique strings:    %8zu, %8zu bytes, overhead: %zu bytes + %zu bytes\n",
		stringCount, totalStringSize, overhead, tableSize);
	INFORM("  total strings with dups: %8zu, %8zu bytes\n", totalReferenceCount,
		totalStringSizeWithDuplicates);
	INFORM("  unshared strings:        %8zu\n", unsharedStringCount);
	INFORM("  bytes saved:             %8zd\n",
		(ssize_t)(totalStringSizeWithDuplicates - (totalStringSize + overhead + tableSize)));
}