z_zone.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. // Emacs style mode select -*- C++ -*-
  2. //-----------------------------------------------------------------------------
  3. //
  4. // $Id:$
  5. //
  6. // Copyright (C) 1993-1996 by id Software, Inc.
  7. //
  8. // This source is available for distribution and/or modification
  9. // only under the terms of the DOOM Source Code License as
  10. // published by id Software. All rights reserved.
  11. //
  12. // The source is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
  15. // for more details.
  16. //
  17. // $Log:$
  18. //
  19. // DESCRIPTION:
  20. // Zone Memory Allocation. Neat.
  21. //
  22. //-----------------------------------------------------------------------------
  23. static const char
  24. rcsid[] = "$Id: z_zone.c,v 1.4 1997/02/03 16:47:58 b1 Exp $";
  25. #include "z_zone.h"
  26. #include "i_system.h"
  27. #include "doomdef.h"
  28. //
  29. // ZONE MEMORY ALLOCATION
  30. //
  31. // There is never any space between memblocks,
  32. // and there will never be two contiguous free memblocks.
  33. // The rover can be left pointing at a non-empty block.
  34. //
  35. // It is of no value to free a cachable block,
  36. // because it will get overwritten automatically if needed.
  37. //
  38. #define ZONEID 0x1d4a11
  39. typedef struct
  40. {
  41. // total bytes malloced, including header
  42. int size;
  43. // start / end cap for linked list
  44. memblock_t blocklist;
  45. memblock_t* rover;
  46. } memzone_t;
  47. memzone_t* mainzone;
  48. //
  49. // Z_ClearZone
  50. //
  51. void Z_ClearZone (memzone_t* zone)
  52. {
  53. memblock_t* block;
  54. // set the entire zone to one free block
  55. zone->blocklist.next =
  56. zone->blocklist.prev =
  57. block = (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
  58. zone->blocklist.user = (void *)zone;
  59. zone->blocklist.tag = PU_STATIC;
  60. zone->rover = block;
  61. block->prev = block->next = &zone->blocklist;
  62. // NULL indicates a free block.
  63. block->user = NULL;
  64. block->size = zone->size - sizeof(memzone_t);
  65. }
  66. //
  67. // Z_Init
  68. //
  69. void Z_Init (void)
  70. {
  71. memblock_t* block;
  72. int size;
  73. mainzone = (memzone_t *)I_ZoneBase (&size);
  74. mainzone->size = size;
  75. // set the entire zone to one free block
  76. mainzone->blocklist.next =
  77. mainzone->blocklist.prev =
  78. block = (memblock_t *)( (byte *)mainzone + sizeof(memzone_t) );
  79. mainzone->blocklist.user = (void *)mainzone;
  80. mainzone->blocklist.tag = PU_STATIC;
  81. mainzone->rover = block;
  82. block->prev = block->next = &mainzone->blocklist;
  83. // NULL indicates a free block.
  84. block->user = NULL;
  85. block->size = mainzone->size - sizeof(memzone_t);
  86. }
  87. //
  88. // Z_Free
  89. //
  90. void Z_Free (void* ptr)
  91. {
  92. memblock_t* block;
  93. memblock_t* other;
  94. block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
  95. if (block->id != ZONEID)
  96. I_Error ("Z_Free: freed a pointer without ZONEID");
  97. if (block->user > (void **)0x100)
  98. {
  99. // smaller values are not pointers
  100. // Note: OS-dependend?
  101. // clear the user's mark
  102. *block->user = 0;
  103. }
  104. // mark as free
  105. block->user = NULL;
  106. block->tag = 0;
  107. block->id = 0;
  108. other = block->prev;
  109. if (!other->user)
  110. {
  111. // merge with previous free block
  112. other->size += block->size;
  113. other->next = block->next;
  114. other->next->prev = other;
  115. if (block == mainzone->rover)
  116. mainzone->rover = other;
  117. block = other;
  118. }
  119. other = block->next;
  120. if (!other->user)
  121. {
  122. // merge the next free block onto the end
  123. block->size += other->size;
  124. block->next = other->next;
  125. block->next->prev = block;
  126. if (other == mainzone->rover)
  127. mainzone->rover = block;
  128. }
  129. }
  130. //
  131. // Z_Malloc
  132. // You can pass a NULL user if the tag is < PU_PURGELEVEL.
  133. //
  134. #define MINFRAGMENT 64
  135. void*
  136. Z_Malloc
  137. ( int size,
  138. int tag,
  139. void* user )
  140. {
  141. int extra;
  142. memblock_t* start;
  143. memblock_t* rover;
  144. memblock_t* newblock;
  145. memblock_t* base;
  146. size = (size + 3) & ~3;
  147. // scan through the block list,
  148. // looking for the first free block
  149. // of sufficient size,
  150. // throwing out any purgable blocks along the way.
  151. // account for size of block header
  152. size += sizeof(memblock_t);
  153. // if there is a free block behind the rover,
  154. // back up over them
  155. base = mainzone->rover;
  156. if (!base->prev->user)
  157. base = base->prev;
  158. rover = base;
  159. start = base->prev;
  160. do
  161. {
  162. if (rover == start)
  163. {
  164. // scanned all the way around the list
  165. I_Error ("Z_Malloc: failed on allocation of %i bytes", size);
  166. }
  167. if (rover->user)
  168. {
  169. if (rover->tag < PU_PURGELEVEL)
  170. {
  171. // hit a block that can't be purged,
  172. // so move base past it
  173. base = rover = rover->next;
  174. }
  175. else
  176. {
  177. // free the rover block (adding the size to base)
  178. // the rover can be the base block
  179. base = base->prev;
  180. Z_Free ((byte *)rover+sizeof(memblock_t));
  181. base = base->next;
  182. rover = base->next;
  183. }
  184. }
  185. else
  186. rover = rover->next;
  187. } while (base->user || base->size < size);
  188. // found a block big enough
  189. extra = base->size - size;
  190. if (extra > MINFRAGMENT)
  191. {
  192. // there will be a free fragment after the allocated block
  193. newblock = (memblock_t *) ((byte *)base + size );
  194. newblock->size = extra;
  195. // NULL indicates free block.
  196. newblock->user = NULL;
  197. newblock->tag = 0;
  198. newblock->prev = base;
  199. newblock->next = base->next;
  200. newblock->next->prev = newblock;
  201. base->next = newblock;
  202. base->size = size;
  203. }
  204. if (user)
  205. {
  206. // mark as an in use block
  207. base->user = user;
  208. *(void **)user = (void *) ((byte *)base + sizeof(memblock_t));
  209. }
  210. else
  211. {
  212. if (tag >= PU_PURGELEVEL)
  213. I_Error ("Z_Malloc: an owner is required for purgable blocks");
  214. // mark as in use, but unowned
  215. base->user = (void *)2;
  216. }
  217. base->tag = tag;
  218. // next allocation will start looking here
  219. mainzone->rover = base->next;
  220. base->id = ZONEID;
  221. return (void *) ((byte *)base + sizeof(memblock_t));
  222. }
  223. //
  224. // Z_FreeTags
  225. //
  226. void
  227. Z_FreeTags
  228. ( int lowtag,
  229. int hightag )
  230. {
  231. memblock_t* block;
  232. memblock_t* next;
  233. for (block = mainzone->blocklist.next ;
  234. block != &mainzone->blocklist ;
  235. block = next)
  236. {
  237. // get link before freeing
  238. next = block->next;
  239. // free block?
  240. if (!block->user)
  241. continue;
  242. if (block->tag >= lowtag && block->tag <= hightag)
  243. Z_Free ( (byte *)block+sizeof(memblock_t));
  244. }
  245. }
  246. //
  247. // Z_DumpHeap
  248. // Note: TFileDumpHeap( stdout ) ?
  249. //
  250. void
  251. Z_DumpHeap
  252. ( int lowtag,
  253. int hightag )
  254. {
  255. memblock_t* block;
  256. printf ("zone size: %i location: %p\n",
  257. mainzone->size,mainzone);
  258. printf ("tag range: %i to %i\n",
  259. lowtag, hightag);
  260. for (block = mainzone->blocklist.next ; ; block = block->next)
  261. {
  262. if (block->tag >= lowtag && block->tag <= hightag)
  263. printf ("block:%p size:%7i user:%p tag:%3i\n",
  264. block, block->size, block->user, block->tag);
  265. if (block->next == &mainzone->blocklist)
  266. {
  267. // all blocks have been hit
  268. break;
  269. }
  270. if ( (byte *)block + block->size != (byte *)block->next)
  271. printf ("ERROR: block size does not touch the next block\n");
  272. if ( block->next->prev != block)
  273. printf ("ERROR: next block doesn't have proper back link\n");
  274. if (!block->user && !block->next->user)
  275. printf ("ERROR: two consecutive free blocks\n");
  276. }
  277. }
  278. //
  279. // Z_FileDumpHeap
  280. //
  281. void Z_FileDumpHeap (FILE* f)
  282. {
  283. memblock_t* block;
  284. fprintf (f,"zone size: %i location: %p\n",mainzone->size,mainzone);
  285. for (block = mainzone->blocklist.next ; ; block = block->next)
  286. {
  287. fprintf (f,"block:%p size:%7i user:%p tag:%3i\n",
  288. block, block->size, block->user, block->tag);
  289. if (block->next == &mainzone->blocklist)
  290. {
  291. // all blocks have been hit
  292. break;
  293. }
  294. if ( (byte *)block + block->size != (byte *)block->next)
  295. fprintf (f,"ERROR: block size does not touch the next block\n");
  296. if ( block->next->prev != block)
  297. fprintf (f,"ERROR: next block doesn't have proper back link\n");
  298. if (!block->user && !block->next->user)
  299. fprintf (f,"ERROR: two consecutive free blocks\n");
  300. }
  301. }
  302. //
  303. // Z_CheckHeap
  304. //
  305. void Z_CheckHeap (void)
  306. {
  307. memblock_t* block;
  308. for (block = mainzone->blocklist.next ; ; block = block->next)
  309. {
  310. if (block->next == &mainzone->blocklist)
  311. {
  312. // all blocks have been hit
  313. break;
  314. }
  315. if ( (byte *)block + block->size != (byte *)block->next)
  316. I_Error ("Z_CheckHeap: block size does not touch the next block\n");
  317. if ( block->next->prev != block)
  318. I_Error ("Z_CheckHeap: next block doesn't have proper back link\n");
  319. if (!block->user && !block->next->user)
  320. I_Error ("Z_CheckHeap: two consecutive free blocks\n");
  321. }
  322. }
  323. //
  324. // Z_ChangeTag
  325. //
  326. void
  327. Z_ChangeTag2
  328. ( void* ptr,
  329. int tag )
  330. {
  331. memblock_t* block;
  332. block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
  333. if (block->id != ZONEID)
  334. I_Error ("Z_ChangeTag: freed a pointer without ZONEID");
  335. if (tag >= PU_PURGELEVEL && (unsigned)block->user < 0x100)
  336. I_Error ("Z_ChangeTag: an owner is required for purgable blocks");
  337. block->tag = tag;
  338. }
  339. //
  340. // Z_FreeMemory
  341. //
  342. int Z_FreeMemory (void)
  343. {
  344. memblock_t* block;
  345. int free;
  346. free = 0;
  347. for (block = mainzone->blocklist.next ;
  348. block != &mainzone->blocklist;
  349. block = block->next)
  350. {
  351. if (!block->user || block->tag >= PU_PURGELEVEL)
  352. free += block->size;
  353. }
  354. return free;
  355. }