#include "stdafx.h"
#include "IntCache.h"

DWORD IntCache::tlsIdx = TlsAlloc();

void IntCache::CreateNewThreadStorage()
{
	ThreadStorage *tls = new ThreadStorage();
	TlsSetValue(tlsIdx, (void *)tls);
	tls->maxSize = TINY_CUTOFF;
}

IntCache::ThreadStorage::~ThreadStorage()
{
	for(unsigned int i = 0; i < tcache.size(); i++ )
	{
		delete [] tcache[i].data;
	}
	for(unsigned int i = 0; i < tallocated.size(); i++ )
	{
		delete [] tallocated[i].data;
	}
	for(unsigned int i = 0; i < cache.size(); i++ )
	{
		delete [] cache[i].data;
	}
	for(unsigned int i = 0; i < allocated.size(); i++ )
	{
		delete [] allocated[i].data;
	}
	for( int i = 0; i < toosmall.size(); i++ )
	{
		delete [] toosmall[i].data;
	}
}

void IntCache::ReleaseThreadStorage()
{
	ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx);


	delete tls;
}

intArray IntCache::allocate(int size)
{
	ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx);

	if (size <= TINY_CUTOFF)
	{
		if (tls->tcache.empty())
		{
			intArray result = intArray(TINY_CUTOFF, true);
			tls->tallocated.push_back(result);
			return result;
		}
		else
		{
			intArray result = tls->tcache.back();
			tls->tcache.pop_back();
			tls->tallocated.push_back(result);
			return result;
		}
	}

	if (size > tls->maxSize)
	{
//		app.DebugPrintf("IntCache: New max size: %d\n" , size);
		tls->maxSize = size;

		// 4J - added - all the vectors in cache & allocated are smaller than maxSize so should be discarded. However, we
		// can't delete them until the next releaseAll so copy into another vector until then
		tls->toosmall.insert(tls->toosmall.end(),tls->cache.begin(),tls->cache.end());
		tls->toosmall.insert(tls->toosmall.end(),tls->allocated.begin(),tls->allocated.end());

		tls->cache.clear();
		tls->allocated.clear();

		intArray result = intArray(tls->maxSize, true);
		tls->allocated.push_back(result);
		return result;
	}
	else
	{
		if (tls->cache.empty())
		{
			intArray result = intArray(tls->maxSize, true);
			tls->allocated.push_back(result);
			return result;
		}
		else
		{
			intArray result = tls->cache.back();
			tls->cache.pop_back();
			tls->allocated.push_back(result);
			return result;
		}
	}
}

void IntCache::releaseAll()
{
	ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx);

	// 4J - added - we can now remove the vectors that were deemed as too small (see comment in IntCache::allocate)
	for( int i = 0; i < tls->toosmall.size(); i++ )
	{
		delete [] tls->toosmall[i].data;
	}
	tls->toosmall.clear();

	if (!tls->cache.empty())
	{
		delete [] tls->cache.back().data;
		tls->cache.pop_back();
	}
	if (!tls->tcache.empty())
	{
		delete [] tls->tcache.back().data;
		tls->tcache.pop_back();
	}

	tls->cache.insert(tls->cache.end(),tls->allocated.begin(),tls->allocated.end());
	tls->tcache.insert(tls->tcache.end(),tls->tallocated.begin(),tls->tallocated.end());

	tls->allocated.clear();
	tls->tallocated.clear();
}

// 4J added so that we can fully reset between levels
void IntCache::Reset()
{
	ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx);
	tls->maxSize = TINY_CUTOFF;
	for( int i = 0; i < tls->allocated.size(); i++ )
	{
		delete [] tls->allocated[i].data;
	}
	tls->allocated.clear();

	for( int i = 0; i < tls->cache.size(); i++ )
	{
		delete [] tls->cache[i].data;
	}
	tls->cache.clear();

	for( int i = 0; i < tls->tallocated.size(); i++ )
	{
		delete [] tls->tallocated[i].data;
	}
	tls->tallocated.clear();

	for( int i = 0; i < tls->tcache.size(); i++ )
	{
		delete [] tls->tcache[i].data;
	}
	tls->tcache.clear();

	for( int i = 0; i < tls->toosmall.size(); i++ )
	{
		delete [] tls->toosmall[i].data;
	}
	tls->toosmall.clear();
}