264 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			264 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "KePlatform.h"
 | 
						|
#include <KeLumpAllocator.h>
 | 
						|
#include <KeVector.h>
 | 
						|
 | 
						|
using namespace Knight;
 | 
						|
 | 
						|
/**
 | 
						|
 * :TODO: don't make this part of the page, because touching it means 
 | 
						|
 * dirtying a page.  Instead, we should have a separate linked list.
 | 
						|
 * Maybe that linked list itself could be marshalled from one page.
 | 
						|
 */
 | 
						|
struct KeLumpRegion
 | 
						|
{
 | 
						|
	char *base;
 | 
						|
	char *cur;
 | 
						|
	size_t size;
 | 
						|
	size_t avail;
 | 
						|
	KeLumpRegion *next;
 | 
						|
	KeLumpRegion *prev;
 | 
						|
};
 | 
						|
 | 
						|
class KeLumpAllocator
 | 
						|
{
 | 
						|
public:
 | 
						|
	KeLumpAllocator() : m_pUsableRegions(NULL), m_pUnusableRegions(NULL)
 | 
						|
	{
 | 
						|
		m_DefLumpSize = 65536;
 | 
						|
 | 
						|
#if defined KE_PLATFORM_WINDOWS
 | 
						|
		SYSTEM_INFO info;
 | 
						|
 | 
						|
		GetSystemInfo(&info);
 | 
						|
		if (info.dwAllocationGranularity > m_DefLumpSize)
 | 
						|
		{
 | 
						|
			m_DefLumpSize = info.dwAllocationGranularity;
 | 
						|
		}
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	~KeLumpAllocator()
 | 
						|
	{
 | 
						|
		FreeRegionChain(m_pUsableRegions);
 | 
						|
		FreeRegionChain(m_pUnusableRegions);
 | 
						|
	}
 | 
						|
 | 
						|
	void Reset()
 | 
						|
	{
 | 
						|
		KeLumpRegion *region;
 | 
						|
 | 
						|
		/* Find the tail of the usable regions. */
 | 
						|
		region = m_pUsableRegions;
 | 
						|
		while (region != NULL)
 | 
						|
		{
 | 
						|
			if (region->next == NULL)
 | 
						|
			{
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* Link the unusable chain into the usable chain. */
 | 
						|
		if (region == NULL)
 | 
						|
		{
 | 
						|
			m_pUsableRegions = m_pUnusableRegions;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			region->next = m_pUnusableRegions;
 | 
						|
			m_pUnusableRegions->prev = region;
 | 
						|
		}
 | 
						|
		m_pUnusableRegions = NULL;
 | 
						|
 | 
						|
		region = m_pUsableRegions;
 | 
						|
		while (region != NULL)
 | 
						|
		{
 | 
						|
			region->avail = region->size;
 | 
						|
			region->cur = region->base;
 | 
						|
			region = region->next;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	void FreeRegionChain(KeLumpRegion *region)
 | 
						|
	{
 | 
						|
		KeLumpRegion *next;
 | 
						|
 | 
						|
		while (region != NULL)
 | 
						|
		{
 | 
						|
			next = region->next;
 | 
						|
			
 | 
						|
#if defined KE_PLATFORM_WINDOWS
 | 
						|
			VirtualFree(region, 0, MEM_RELEASE);
 | 
						|
#else
 | 
						|
            free(region);
 | 
						|
#endif
 | 
						|
			region = next;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	void *Alloc(size_t size)
 | 
						|
	{
 | 
						|
		char *blob;
 | 
						|
		KeLumpRegion *region;
 | 
						|
 | 
						|
		if (size % 8 != 0)
 | 
						|
		{
 | 
						|
			size += 8;
 | 
						|
			size -= size % 8;
 | 
						|
		}
 | 
						|
 | 
						|
		region = FindRegionForSize(size);
 | 
						|
 | 
						|
		blob = region->cur;
 | 
						|
		region->avail -= size;
 | 
						|
		region->cur += size;
 | 
						|
 | 
						|
		/** 
 | 
						|
		 * Technically we could make one last small allocation, but 
 | 
						|
		 * this edge case is not worth the extra work.
 | 
						|
		 */
 | 
						|
		if (region->avail < 8)
 | 
						|
		{
 | 
						|
			/* Unlink us from the usable list */
 | 
						|
			if (region == m_pUsableRegions)
 | 
						|
			{
 | 
						|
				m_pUsableRegions = m_pUsableRegions->next;
 | 
						|
				m_pUsableRegions->prev = NULL;
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				region->prev->next = region->next;
 | 
						|
				if (region->next != NULL)
 | 
						|
				{
 | 
						|
					region->next->prev = region->prev;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			/* Link us into the unusable list */
 | 
						|
			region->prev = NULL;
 | 
						|
			region->next = m_pUnusableRegions;
 | 
						|
 | 
						|
			if (m_pUnusableRegions != NULL)
 | 
						|
			{
 | 
						|
				m_pUnusableRegions->prev = region;
 | 
						|
			}
 | 
						|
 | 
						|
			m_pUnusableRegions = region;
 | 
						|
		}
 | 
						|
 | 
						|
		return blob;
 | 
						|
	}
 | 
						|
 | 
						|
private:
 | 
						|
	KeLumpRegion *FindRegionForSize(size_t size)
 | 
						|
	{
 | 
						|
		char *base;
 | 
						|
		KeLumpRegion *region;
 | 
						|
		size_t size_of_region;
 | 
						|
 | 
						|
		/**
 | 
						|
		 * :TODO: replace this with a priority queue or something 
 | 
						|
		 * that's actually fast.  Even worse is we dirty pages by 
 | 
						|
		 * doing this.  Ouch!
 | 
						|
		 */
 | 
						|
		region = m_pUsableRegions;
 | 
						|
		while (region != NULL)
 | 
						|
		{
 | 
						|
			if (region->avail >= size)
 | 
						|
			{
 | 
						|
				return region;
 | 
						|
			}
 | 
						|
			region = region->next;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Make sure regions end at 8-byte alignment. */
 | 
						|
		size_of_region = sizeof(KeLumpRegion);
 | 
						|
		if (size_of_region % 8 != 0)
 | 
						|
		{
 | 
						|
			size_of_region += 8;
 | 
						|
			size_of_region -= size_of_region % 8;
 | 
						|
		}
 | 
						|
 | 
						|
		/* If the size is too big, fix that. */
 | 
						|
		if (size > m_DefLumpSize - size_of_region)
 | 
						|
		{
 | 
						|
			size += m_DefLumpSize;
 | 
						|
			size -= size % m_DefLumpSize;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			size = m_DefLumpSize;
 | 
						|
		}
 | 
						|
 | 
						|
#if defined KE_PLATFORM_WINDOWS
 | 
						|
		base = (char *)VirtualAlloc(
 | 
						|
			NULL,
 | 
						|
			m_DefLumpSize,
 | 
						|
			MEM_COMMIT|MEM_RESERVE,
 | 
						|
			PAGE_READWRITE);
 | 
						|
#else
 | 
						|
        base = (char*)valloc(m_DefLumpSize);
 | 
						|
#endif
 | 
						|
 | 
						|
		/* Initialize the region */
 | 
						|
		region = (KeLumpRegion *)base;
 | 
						|
		region->base = &base[size_of_region];
 | 
						|
		region->size = size - size_of_region;
 | 
						|
		region->cur = region->base;
 | 
						|
		region->avail = region->size;
 | 
						|
		region->prev = NULL;
 | 
						|
		region->next = m_pUsableRegions;
 | 
						|
 | 
						|
		if (m_pUsableRegions != NULL)
 | 
						|
		{
 | 
						|
			m_pUsableRegions->prev = region;
 | 
						|
		}
 | 
						|
 | 
						|
		m_pUsableRegions = region;
 | 
						|
 | 
						|
		return region;
 | 
						|
	}
 | 
						|
 | 
						|
private:
 | 
						|
	KeLumpRegion *m_pUsableRegions;
 | 
						|
	KeLumpRegion *m_pUnusableRegions;
 | 
						|
	size_t m_DefLumpSize;
 | 
						|
};
 | 
						|
 | 
						|
inline KeLumpAllocator *ke_LumpFromAllocator(ke_allocator_t *arena)
 | 
						|
{
 | 
						|
	return (KeLumpAllocator *)arena->user;
 | 
						|
}
 | 
						|
 | 
						|
void *ke_LumpAlloc(ke_allocator_t *arena, size_t size)
 | 
						|
{
 | 
						|
	return ke_LumpFromAllocator(arena)->Alloc(size);
 | 
						|
}
 | 
						|
 | 
						|
void ke_LumpFree(ke_allocator_t *arena, void *ptr)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
ke_allocator_t * KE_LINK Knight::KE_CreateLumpAllocator()
 | 
						|
{
 | 
						|
	ke_allocator_t *alloc;
 | 
						|
 | 
						|
	alloc = new ke_allocator_t;
 | 
						|
	alloc->alloc = ke_LumpAlloc;
 | 
						|
	alloc->dealloc = ke_LumpFree;
 | 
						|
	alloc->user = new KeLumpAllocator();
 | 
						|
 | 
						|
	return alloc;
 | 
						|
}
 | 
						|
 | 
						|
void KE_LINK Knight::KE_DestroyLumpAllocator(ke_allocator_t *alloc)
 | 
						|
{
 | 
						|
	delete ke_LumpFromAllocator(alloc);
 | 
						|
	delete alloc;
 | 
						|
}
 | 
						|
 | 
						|
void KE_LINK Knight::KE_ResetLumpAllocator(ke_allocator_t *alloc)
 | 
						|
{
 | 
						|
	ke_LumpFromAllocator(alloc)->Reset();
 | 
						|
}
 | 
						|
 |