knight/shared/KeLumpAllocator.cpp
author Scott Ehlert <ds@alliedmods.net>
Thu May 13 04:23:43 2010 -0500 (2010-05-13)
changeset 2994 1ef5e4e002e2
parent 2541 0352326a1f3f
permissions -rw-r--r--
Appended OS names to package archives (bug 4395, r=dvander).
     1 #include "KePlatform.h"
     2 #include <KeLumpAllocator.h>
     3 #include <KeVector.h>
     4 
     5 using namespace Knight;
     6 
     7 /**
     8  * :TODO: don't make this part of the page, because touching it means 
     9  * dirtying a page.  Instead, we should have a separate linked list.
    10  * Maybe that linked list itself could be marshalled from one page.
    11  */
    12 struct KeLumpRegion
    13 {
    14 	char *base;
    15 	char *cur;
    16 	size_t size;
    17 	size_t avail;
    18 	KeLumpRegion *next;
    19 	KeLumpRegion *prev;
    20 };
    21 
    22 class KeLumpAllocator
    23 {
    24 public:
    25 	KeLumpAllocator() : m_pUsableRegions(NULL), m_pUnusableRegions(NULL)
    26 	{
    27 		m_DefLumpSize = 65536;
    28 
    29 #if defined KE_PLATFORM_WINDOWS
    30 		SYSTEM_INFO info;
    31 
    32 		GetSystemInfo(&info);
    33 		if (info.dwAllocationGranularity > m_DefLumpSize)
    34 		{
    35 			m_DefLumpSize = info.dwAllocationGranularity;
    36 		}
    37 #endif
    38 	}
    39 
    40 	~KeLumpAllocator()
    41 	{
    42 		FreeRegionChain(m_pUsableRegions);
    43 		FreeRegionChain(m_pUnusableRegions);
    44 	}
    45 
    46 	void Reset()
    47 	{
    48 		KeLumpRegion *region;
    49 
    50 		/* Find the tail of the usable regions. */
    51 		region = m_pUsableRegions;
    52 		while (region != NULL)
    53 		{
    54 			if (region->next == NULL)
    55 			{
    56 				break;
    57 			}
    58 		}
    59 
    60 		/* Link the unusable chain into the usable chain. */
    61 		if (region == NULL)
    62 		{
    63 			m_pUsableRegions = m_pUnusableRegions;
    64 		}
    65 		else
    66 		{
    67 			region->next = m_pUnusableRegions;
    68 			m_pUnusableRegions->prev = region;
    69 		}
    70 		m_pUnusableRegions = NULL;
    71 
    72 		region = m_pUsableRegions;
    73 		while (region != NULL)
    74 		{
    75 			region->avail = region->size;
    76 			region->cur = region->base;
    77 			region = region->next;
    78 		}
    79 	}
    80 
    81 	void FreeRegionChain(KeLumpRegion *region)
    82 	{
    83 		KeLumpRegion *next;
    84 
    85 		while (region != NULL)
    86 		{
    87 			next = region->next;
    88 			
    89 #if defined KE_PLATFORM_WINDOWS
    90 			VirtualFree(region, 0, MEM_RELEASE);
    91 #else
    92             free(region);
    93 #endif
    94 			region = next;
    95 		}
    96 	}
    97 
    98 	void *Alloc(size_t size)
    99 	{
   100 		char *blob;
   101 		KeLumpRegion *region;
   102 
   103 		if (size % 8 != 0)
   104 		{
   105 			size += 8;
   106 			size -= size % 8;
   107 		}
   108 
   109 		region = FindRegionForSize(size);
   110 
   111 		blob = region->cur;
   112 		region->avail -= size;
   113 		region->cur += size;
   114 
   115 		/** 
   116 		 * Technically we could make one last small allocation, but 
   117 		 * this edge case is not worth the extra work.
   118 		 */
   119 		if (region->avail < 8)
   120 		{
   121 			/* Unlink us from the usable list */
   122 			if (region == m_pUsableRegions)
   123 			{
   124 				m_pUsableRegions = m_pUsableRegions->next;
   125 				m_pUsableRegions->prev = NULL;
   126 			}
   127 			else
   128 			{
   129 				region->prev->next = region->next;
   130 				if (region->next != NULL)
   131 				{
   132 					region->next->prev = region->prev;
   133 				}
   134 			}
   135 
   136 			/* Link us into the unusable list */
   137 			region->prev = NULL;
   138 			region->next = m_pUnusableRegions;
   139 
   140 			if (m_pUnusableRegions != NULL)
   141 			{
   142 				m_pUnusableRegions->prev = region;
   143 			}
   144 
   145 			m_pUnusableRegions = region;
   146 		}
   147 
   148 		return blob;
   149 	}
   150 
   151 private:
   152 	KeLumpRegion *FindRegionForSize(size_t size)
   153 	{
   154 		char *base;
   155 		KeLumpRegion *region;
   156 		size_t size_of_region;
   157 
   158 		/**
   159 		 * :TODO: replace this with a priority queue or something 
   160 		 * that's actually fast.  Even worse is we dirty pages by 
   161 		 * doing this.  Ouch!
   162 		 */
   163 		region = m_pUsableRegions;
   164 		while (region != NULL)
   165 		{
   166 			if (region->avail >= size)
   167 			{
   168 				return region;
   169 			}
   170 			region = region->next;
   171 		}
   172 
   173 		/* Make sure regions end at 8-byte alignment. */
   174 		size_of_region = sizeof(KeLumpRegion);
   175 		if (size_of_region % 8 != 0)
   176 		{
   177 			size_of_region += 8;
   178 			size_of_region -= size_of_region % 8;
   179 		}
   180 
   181 		/* If the size is too big, fix that. */
   182 		if (size > m_DefLumpSize - size_of_region)
   183 		{
   184 			size += m_DefLumpSize;
   185 			size -= size % m_DefLumpSize;
   186 		}
   187 		else
   188 		{
   189 			size = m_DefLumpSize;
   190 		}
   191 
   192 #if defined KE_PLATFORM_WINDOWS
   193 		base = (char *)VirtualAlloc(
   194 			NULL,
   195 			m_DefLumpSize,
   196 			MEM_COMMIT|MEM_RESERVE,
   197 			PAGE_READWRITE);
   198 #else
   199         base = (char*)valloc(m_DefLumpSize);
   200 #endif
   201 
   202 		/* Initialize the region */
   203 		region = (KeLumpRegion *)base;
   204 		region->base = &base[size_of_region];
   205 		region->size = size - size_of_region;
   206 		region->cur = region->base;
   207 		region->avail = region->size;
   208 		region->prev = NULL;
   209 		region->next = m_pUsableRegions;
   210 
   211 		if (m_pUsableRegions != NULL)
   212 		{
   213 			m_pUsableRegions->prev = region;
   214 		}
   215 
   216 		m_pUsableRegions = region;
   217 
   218 		return region;
   219 	}
   220 
   221 private:
   222 	KeLumpRegion *m_pUsableRegions;
   223 	KeLumpRegion *m_pUnusableRegions;
   224 	size_t m_DefLumpSize;
   225 };
   226 
   227 inline KeLumpAllocator *ke_LumpFromAllocator(ke_allocator_t *arena)
   228 {
   229 	return (KeLumpAllocator *)arena->user;
   230 }
   231 
   232 void *ke_LumpAlloc(ke_allocator_t *arena, size_t size)
   233 {
   234 	return ke_LumpFromAllocator(arena)->Alloc(size);
   235 }
   236 
   237 void ke_LumpFree(ke_allocator_t *arena, void *ptr)
   238 {
   239 }
   240 
   241 ke_allocator_t * KE_LINK Knight::KE_CreateLumpAllocator()
   242 {
   243 	ke_allocator_t *alloc;
   244 
   245 	alloc = new ke_allocator_t;
   246 	alloc->alloc = ke_LumpAlloc;
   247 	alloc->dealloc = ke_LumpFree;
   248 	alloc->user = new KeLumpAllocator();
   249 
   250 	return alloc;
   251 }
   252 
   253 void KE_LINK Knight::KE_DestroyLumpAllocator(ke_allocator_t *alloc)
   254 {
   255 	delete ke_LumpFromAllocator(alloc);
   256 	delete alloc;
   257 }
   258 
   259 void KE_LINK Knight::KE_ResetLumpAllocator(ke_allocator_t *alloc)
   260 {
   261 	ke_LumpFromAllocator(alloc)->Reset();
   262 }
   263