Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5131 | clevermous | 1 | /* |
2 | Copyright (C) 1996-1997 Id Software, Inc. |
||
3 | |||
4 | This program is free software; you can redistribute it and/or |
||
5 | modify it under the terms of the GNU General Public License |
||
6 | as published by the Free Software Foundation; either version 2 |
||
7 | of the License, or (at your option) any later version. |
||
8 | |||
9 | This program is distributed in the hope that it will be useful, |
||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||
12 | |||
13 | See the GNU General Public License for more details. |
||
14 | |||
15 | You should have received a copy of the GNU General Public License |
||
16 | along with this program; if not, write to the Free Software |
||
17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
||
18 | |||
19 | */ |
||
20 | // Z_zone.c |
||
21 | |||
22 | #include "quakedef.h" |
||
23 | |||
24 | #define DYNAMIC_SIZE 0xc000 |
||
25 | |||
26 | #define ZONEID 0x1d4a11 |
||
27 | #define MINFRAGMENT 64 |
||
28 | |||
29 | typedef struct memblock_s |
||
30 | { |
||
31 | int size; // including the header and possibly tiny fragments |
||
32 | int tag; // a tag of 0 is a free block |
||
33 | int id; // should be ZONEID |
||
34 | struct memblock_s *next, *prev; |
||
35 | int pad; // pad to 64 bit boundary |
||
36 | } memblock_t; |
||
37 | |||
38 | typedef struct |
||
39 | { |
||
40 | int size; // total bytes malloced, including header |
||
41 | memblock_t blocklist; // start / end cap for linked list |
||
42 | memblock_t *rover; |
||
43 | } memzone_t; |
||
44 | |||
45 | void Cache_FreeLow (int new_low_hunk); |
||
46 | void Cache_FreeHigh (int new_high_hunk); |
||
47 | |||
48 | |||
49 | /* |
||
50 | ============================================================================== |
||
51 | |||
52 | ZONE MEMORY ALLOCATION |
||
53 | |||
54 | There is never any space between memblocks, and there will never be two |
||
55 | contiguous free memblocks. |
||
56 | |||
57 | The rover can be left pointing at a non-empty block |
||
58 | |||
59 | The zone calls are pretty much only used for small strings and structures, |
||
60 | all big things are allocated on the hunk. |
||
61 | ============================================================================== |
||
62 | */ |
||
63 | |||
64 | memzone_t *mainzone; |
||
65 | |||
66 | void Z_ClearZone (memzone_t *zone, int size); |
||
67 | |||
68 | |||
69 | /* |
||
70 | ======================== |
||
71 | Z_ClearZone |
||
72 | ======================== |
||
73 | */ |
||
74 | void Z_ClearZone (memzone_t *zone, int size) |
||
75 | { |
||
76 | memblock_t *block; |
||
77 | |||
78 | // set the entire zone to one free block |
||
79 | |||
80 | zone->blocklist.next = zone->blocklist.prev = block = |
||
81 | (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); |
||
82 | zone->blocklist.tag = 1; // in use block |
||
83 | zone->blocklist.id = 0; |
||
84 | zone->blocklist.size = 0; |
||
85 | zone->rover = block; |
||
86 | |||
87 | block->prev = block->next = &zone->blocklist; |
||
88 | block->tag = 0; // free block |
||
89 | block->id = ZONEID; |
||
90 | block->size = size - sizeof(memzone_t); |
||
91 | } |
||
92 | |||
93 | |||
94 | /* |
||
95 | ======================== |
||
96 | Z_Free |
||
97 | ======================== |
||
98 | */ |
||
99 | void Z_Free (void *ptr) |
||
100 | { |
||
101 | memblock_t *block, *other; |
||
102 | |||
103 | if (!ptr) |
||
104 | Sys_Error ("Z_Free: NULL pointer"); |
||
105 | |||
106 | block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); |
||
107 | if (block->id != ZONEID) |
||
108 | Sys_Error ("Z_Free: freed a pointer without ZONEID"); |
||
109 | if (block->tag == 0) |
||
110 | Sys_Error ("Z_Free: freed a freed pointer"); |
||
111 | |||
112 | block->tag = 0; // mark as free |
||
113 | |||
114 | other = block->prev; |
||
115 | if (!other->tag) |
||
116 | { // merge with previous free block |
||
117 | other->size += block->size; |
||
118 | other->next = block->next; |
||
119 | other->next->prev = other; |
||
120 | if (block == mainzone->rover) |
||
121 | mainzone->rover = other; |
||
122 | block = other; |
||
123 | } |
||
124 | |||
125 | other = block->next; |
||
126 | if (!other->tag) |
||
127 | { // merge the next free block onto the end |
||
128 | block->size += other->size; |
||
129 | block->next = other->next; |
||
130 | block->next->prev = block; |
||
131 | if (other == mainzone->rover) |
||
132 | mainzone->rover = block; |
||
133 | } |
||
134 | } |
||
135 | |||
136 | |||
137 | /* |
||
138 | ======================== |
||
139 | Z_Malloc |
||
140 | ======================== |
||
141 | */ |
||
142 | void *Z_Malloc (int size) |
||
143 | { |
||
144 | void *buf; |
||
145 | |||
146 | Z_CheckHeap (); // DEBUG |
||
147 | buf = Z_TagMalloc (size, 1); |
||
148 | if (!buf) |
||
149 | Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size); |
||
150 | Q_memset (buf, 0, size); |
||
151 | |||
152 | return buf; |
||
153 | } |
||
154 | |||
155 | void *Z_TagMalloc (int size, int tag) |
||
156 | { |
||
157 | int extra; |
||
158 | memblock_t *start, *rover, *new, *base; |
||
159 | |||
160 | if (!tag) |
||
161 | Sys_Error ("Z_TagMalloc: tried to use a 0 tag"); |
||
162 | |||
163 | // |
||
164 | // scan through the block list looking for the first free block |
||
165 | // of sufficient size |
||
166 | // |
||
167 | size += sizeof(memblock_t); // account for size of block header |
||
168 | size += 4; // space for memory trash tester |
||
169 | size = (size + 7) & ~7; // align to 8-byte boundary |
||
170 | |||
171 | base = rover = mainzone->rover; |
||
172 | start = base->prev; |
||
173 | |||
174 | do |
||
175 | { |
||
176 | if (rover == start) // scaned all the way around the list |
||
177 | return NULL; |
||
178 | if (rover->tag) |
||
179 | base = rover = rover->next; |
||
180 | else |
||
181 | rover = rover->next; |
||
182 | } while (base->tag || base->size < size); |
||
183 | |||
184 | // |
||
185 | // found a block big enough |
||
186 | // |
||
187 | extra = base->size - size; |
||
188 | if (extra > MINFRAGMENT) |
||
189 | { // there will be a free fragment after the allocated block |
||
190 | new = (memblock_t *) ((byte *)base + size ); |
||
191 | new->size = extra; |
||
192 | new->tag = 0; // free block |
||
193 | new->prev = base; |
||
194 | new->id = ZONEID; |
||
195 | new->next = base->next; |
||
196 | new->next->prev = new; |
||
197 | base->next = new; |
||
198 | base->size = size; |
||
199 | } |
||
200 | |||
201 | base->tag = tag; // no longer a free block |
||
202 | |||
203 | mainzone->rover = base->next; // next allocation will start looking here |
||
204 | |||
205 | base->id = ZONEID; |
||
206 | |||
207 | // marker for memory trash testing |
||
208 | *(int *)((byte *)base + base->size - 4) = ZONEID; |
||
209 | |||
210 | return (void *) ((byte *)base + sizeof(memblock_t)); |
||
211 | } |
||
212 | |||
213 | |||
214 | /* |
||
215 | ======================== |
||
216 | Z_Print |
||
217 | ======================== |
||
218 | */ |
||
219 | void Z_Print (memzone_t *zone) |
||
220 | { |
||
221 | memblock_t *block; |
||
222 | |||
223 | Con_Printf ("zone size: %i location: %p\n",mainzone->size,mainzone); |
||
224 | |||
225 | for (block = zone->blocklist.next ; ; block = block->next) |
||
226 | { |
||
227 | Con_Printf ("block:%p size:%7i tag:%3i\n", |
||
228 | block, block->size, block->tag); |
||
229 | |||
230 | if (block->next == &zone->blocklist) |
||
231 | break; // all blocks have been hit |
||
232 | if ( (byte *)block + block->size != (byte *)block->next) |
||
233 | Con_Printf ("ERROR: block size does not touch the next block\n"); |
||
234 | if ( block->next->prev != block) |
||
235 | Con_Printf ("ERROR: next block doesn't have proper back link\n"); |
||
236 | if (!block->tag && !block->next->tag) |
||
237 | Con_Printf ("ERROR: two consecutive free blocks\n"); |
||
238 | } |
||
239 | } |
||
240 | |||
241 | |||
242 | /* |
||
243 | ======================== |
||
244 | Z_CheckHeap |
||
245 | ======================== |
||
246 | */ |
||
247 | void Z_CheckHeap (void) |
||
248 | { |
||
249 | memblock_t *block; |
||
250 | |||
251 | for (block = mainzone->blocklist.next ; ; block = block->next) |
||
252 | { |
||
253 | if (block->next == &mainzone->blocklist) |
||
254 | break; // all blocks have been hit |
||
255 | if ( (byte *)block + block->size != (byte *)block->next) |
||
256 | Sys_Error ("Z_CheckHeap: block size does not touch the next block\n"); |
||
257 | if ( block->next->prev != block) |
||
258 | Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); |
||
259 | if (!block->tag && !block->next->tag) |
||
260 | Sys_Error ("Z_CheckHeap: two consecutive free blocks\n"); |
||
261 | } |
||
262 | } |
||
263 | |||
264 | //============================================================================ |
||
265 | |||
266 | #define HUNK_SENTINAL 0x1df001ed |
||
267 | |||
268 | typedef struct |
||
269 | { |
||
270 | int sentinal; |
||
271 | int size; // including sizeof(hunk_t), -1 = not allocated |
||
272 | char name[8]; |
||
273 | } hunk_t; |
||
274 | |||
275 | byte *hunk_base; |
||
276 | int hunk_size; |
||
277 | |||
278 | int hunk_low_used; |
||
279 | int hunk_high_used; |
||
280 | |||
281 | qboolean hunk_tempactive; |
||
282 | int hunk_tempmark; |
||
283 | |||
284 | void R_FreeTextures (void); |
||
285 | |||
286 | /* |
||
287 | ============== |
||
288 | Hunk_Check |
||
289 | |||
290 | Run consistancy and sentinal trahing checks |
||
291 | ============== |
||
292 | */ |
||
293 | void Hunk_Check (void) |
||
294 | { |
||
295 | hunk_t *h; |
||
296 | |||
297 | for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; ) |
||
298 | { |
||
299 | if (h->sentinal != HUNK_SENTINAL) |
||
300 | Sys_Error ("Hunk_Check: trahsed sentinal"); |
||
301 | if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size) |
||
302 | Sys_Error ("Hunk_Check: bad size"); |
||
303 | h = (hunk_t *)((byte *)h+h->size); |
||
304 | } |
||
305 | } |
||
306 | |||
307 | /* |
||
308 | ============== |
||
309 | Hunk_Print |
||
310 | |||
311 | If "all" is specified, every single allocation is printed. |
||
312 | Otherwise, allocations with the same name will be totaled up before printing. |
||
313 | ============== |
||
314 | */ |
||
315 | void Hunk_Print (qboolean all) |
||
316 | { |
||
317 | hunk_t *h, *next, *endlow, *starthigh, *endhigh; |
||
318 | int count, sum; |
||
319 | int totalblocks; |
||
320 | char name[9]; |
||
321 | |||
322 | name[8] = 0; |
||
323 | count = 0; |
||
324 | sum = 0; |
||
325 | totalblocks = 0; |
||
326 | |||
327 | h = (hunk_t *)hunk_base; |
||
328 | endlow = (hunk_t *)(hunk_base + hunk_low_used); |
||
329 | starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); |
||
330 | endhigh = (hunk_t *)(hunk_base + hunk_size); |
||
331 | |||
332 | Con_Printf (" :%8i total hunk size\n", hunk_size); |
||
333 | Con_Printf ("-------------------------\n"); |
||
334 | |||
335 | while (1) |
||
336 | { |
||
337 | // |
||
338 | // skip to the high hunk if done with low hunk |
||
339 | // |
||
340 | if ( h == endlow ) |
||
341 | { |
||
342 | Con_Printf ("-------------------------\n"); |
||
343 | Con_Printf (" :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used); |
||
344 | Con_Printf ("-------------------------\n"); |
||
345 | h = starthigh; |
||
346 | } |
||
347 | |||
348 | // |
||
349 | // if totally done, break |
||
350 | // |
||
351 | if ( h == endhigh ) |
||
352 | break; |
||
353 | |||
354 | // |
||
355 | // run consistancy checks |
||
356 | // |
||
357 | if (h->sentinal != HUNK_SENTINAL) |
||
358 | Sys_Error ("Hunk_Check: trahsed sentinal"); |
||
359 | if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size) |
||
360 | Sys_Error ("Hunk_Check: bad size"); |
||
361 | |||
362 | next = (hunk_t *)((byte *)h+h->size); |
||
363 | count++; |
||
364 | totalblocks++; |
||
365 | sum += h->size; |
||
366 | |||
367 | // |
||
368 | // print the single block |
||
369 | // |
||
370 | memcpy (name, h->name, 8); |
||
371 | if (all) |
||
372 | Con_Printf ("%8p :%8i %8s\n",h, h->size, name); |
||
373 | |||
374 | // |
||
375 | // print the total |
||
376 | // |
||
377 | if (next == endlow || next == endhigh || |
||
378 | strncmp (h->name, next->name, 8) ) |
||
379 | { |
||
380 | if (!all) |
||
381 | Con_Printf (" :%8i %8s (TOTAL)\n",sum, name); |
||
382 | count = 0; |
||
383 | sum = 0; |
||
384 | } |
||
385 | |||
386 | h = next; |
||
387 | } |
||
388 | |||
389 | Con_Printf ("-------------------------\n"); |
||
390 | Con_Printf ("%8i total blocks\n", totalblocks); |
||
391 | |||
392 | } |
||
393 | |||
394 | /* |
||
395 | =================== |
||
396 | Hunk_AllocName |
||
397 | =================== |
||
398 | */ |
||
399 | void *Hunk_AllocName (int size, char *name) |
||
400 | { |
||
401 | hunk_t *h; |
||
402 | |||
403 | #ifdef PARANOID |
||
404 | Hunk_Check (); |
||
405 | #endif |
||
406 | |||
407 | if (size < 0) |
||
408 | Sys_Error ("Hunk_Alloc: bad size: %i", size); |
||
409 | |||
410 | size = sizeof(hunk_t) + ((size+15)&~15); |
||
411 | |||
412 | if (hunk_size - hunk_low_used - hunk_high_used < size) |
||
413 | Sys_Error ("Hunk_Alloc: failed on %i bytes",size); |
||
414 | |||
415 | h = (hunk_t *)(hunk_base + hunk_low_used); |
||
416 | hunk_low_used += size; |
||
417 | |||
418 | Cache_FreeLow (hunk_low_used); |
||
419 | |||
420 | memset (h, 0, size); |
||
421 | |||
422 | h->size = size; |
||
423 | h->sentinal = HUNK_SENTINAL; |
||
424 | Q_strncpy (h->name, name, 8); |
||
425 | |||
426 | return (void *)(h+1); |
||
427 | } |
||
428 | |||
429 | /* |
||
430 | =================== |
||
431 | Hunk_Alloc |
||
432 | =================== |
||
433 | */ |
||
434 | void *Hunk_Alloc (int size) |
||
435 | { |
||
436 | return Hunk_AllocName (size, "unknown"); |
||
437 | } |
||
438 | |||
439 | int Hunk_LowMark (void) |
||
440 | { |
||
441 | return hunk_low_used; |
||
442 | } |
||
443 | |||
444 | void Hunk_FreeToLowMark (int mark) |
||
445 | { |
||
446 | if (mark < 0 || mark > hunk_low_used) |
||
447 | Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark); |
||
448 | memset (hunk_base + mark, 0, hunk_low_used - mark); |
||
449 | hunk_low_used = mark; |
||
450 | } |
||
451 | |||
452 | int Hunk_HighMark (void) |
||
453 | { |
||
454 | if (hunk_tempactive) |
||
455 | { |
||
456 | hunk_tempactive = false; |
||
457 | Hunk_FreeToHighMark (hunk_tempmark); |
||
458 | } |
||
459 | |||
460 | return hunk_high_used; |
||
461 | } |
||
462 | |||
463 | void Hunk_FreeToHighMark (int mark) |
||
464 | { |
||
465 | if (hunk_tempactive) |
||
466 | { |
||
467 | hunk_tempactive = false; |
||
468 | Hunk_FreeToHighMark (hunk_tempmark); |
||
469 | } |
||
470 | if (mark < 0 || mark > hunk_high_used) |
||
471 | Sys_Error ("Hunk_FreeToHighMark: bad mark %i", mark); |
||
472 | memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark); |
||
473 | hunk_high_used = mark; |
||
474 | } |
||
475 | |||
476 | |||
477 | /* |
||
478 | =================== |
||
479 | Hunk_HighAllocName |
||
480 | =================== |
||
481 | */ |
||
482 | void *Hunk_HighAllocName (int size, char *name) |
||
483 | { |
||
484 | hunk_t *h; |
||
485 | |||
486 | if (size < 0) |
||
487 | Sys_Error ("Hunk_HighAllocName: bad size: %i", size); |
||
488 | |||
489 | if (hunk_tempactive) |
||
490 | { |
||
491 | Hunk_FreeToHighMark (hunk_tempmark); |
||
492 | hunk_tempactive = false; |
||
493 | } |
||
494 | |||
495 | #ifdef PARANOID |
||
496 | Hunk_Check (); |
||
497 | #endif |
||
498 | |||
499 | size = sizeof(hunk_t) + ((size+15)&~15); |
||
500 | |||
501 | if (hunk_size - hunk_low_used - hunk_high_used < size) |
||
502 | { |
||
503 | Con_Printf ("Hunk_HighAlloc: failed on %i bytes\n",size); |
||
504 | return NULL; |
||
505 | } |
||
506 | |||
507 | hunk_high_used += size; |
||
508 | Cache_FreeHigh (hunk_high_used); |
||
509 | |||
510 | h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); |
||
511 | |||
512 | memset (h, 0, size); |
||
513 | h->size = size; |
||
514 | h->sentinal = HUNK_SENTINAL; |
||
515 | Q_strncpy (h->name, name, 8); |
||
516 | |||
517 | return (void *)(h+1); |
||
518 | } |
||
519 | |||
520 | |||
521 | /* |
||
522 | ================= |
||
523 | Hunk_TempAlloc |
||
524 | |||
525 | Return space from the top of the hunk |
||
526 | ================= |
||
527 | */ |
||
528 | void *Hunk_TempAlloc (int size) |
||
529 | { |
||
530 | void *buf; |
||
531 | |||
532 | size = (size+15)&~15; |
||
533 | |||
534 | if (hunk_tempactive) |
||
535 | { |
||
536 | Hunk_FreeToHighMark (hunk_tempmark); |
||
537 | hunk_tempactive = false; |
||
538 | } |
||
539 | |||
540 | hunk_tempmark = Hunk_HighMark (); |
||
541 | |||
542 | buf = Hunk_HighAllocName (size, "temp"); |
||
543 | |||
544 | hunk_tempactive = true; |
||
545 | |||
546 | return buf; |
||
547 | } |
||
548 | |||
549 | /* |
||
550 | =============================================================================== |
||
551 | |||
552 | CACHE MEMORY |
||
553 | |||
554 | =============================================================================== |
||
555 | */ |
||
556 | |||
557 | typedef struct cache_system_s |
||
558 | { |
||
559 | int size; // including this header |
||
560 | cache_user_t *user; |
||
561 | char name[16]; |
||
562 | struct cache_system_s *prev, *next; |
||
563 | struct cache_system_s *lru_prev, *lru_next; // for LRU flushing |
||
564 | } cache_system_t; |
||
565 | |||
566 | cache_system_t *Cache_TryAlloc (int size, qboolean nobottom); |
||
567 | |||
568 | cache_system_t cache_head; |
||
569 | |||
570 | /* |
||
571 | =========== |
||
572 | Cache_Move |
||
573 | =========== |
||
574 | */ |
||
575 | void Cache_Move ( cache_system_t *c) |
||
576 | { |
||
577 | cache_system_t *new; |
||
578 | |||
579 | // we are clearing up space at the bottom, so only allocate it late |
||
580 | new = Cache_TryAlloc (c->size, true); |
||
581 | if (new) |
||
582 | { |
||
583 | // Con_Printf ("cache_move ok\n"); |
||
584 | |||
585 | Q_memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) ); |
||
586 | new->user = c->user; |
||
587 | Q_memcpy (new->name, c->name, sizeof(new->name)); |
||
588 | Cache_Free (c->user); |
||
589 | new->user->data = (void *)(new+1); |
||
590 | } |
||
591 | else |
||
592 | { |
||
593 | // Con_Printf ("cache_move failed\n"); |
||
594 | |||
595 | Cache_Free (c->user); // tough luck... |
||
596 | } |
||
597 | } |
||
598 | |||
599 | /* |
||
600 | ============ |
||
601 | Cache_FreeLow |
||
602 | |||
603 | Throw things out until the hunk can be expanded to the given point |
||
604 | ============ |
||
605 | */ |
||
606 | void Cache_FreeLow (int new_low_hunk) |
||
607 | { |
||
608 | cache_system_t *c; |
||
609 | |||
610 | while (1) |
||
611 | { |
||
612 | c = cache_head.next; |
||
613 | if (c == &cache_head) |
||
614 | return; // nothing in cache at all |
||
615 | if ((byte *)c >= hunk_base + new_low_hunk) |
||
616 | return; // there is space to grow the hunk |
||
617 | Cache_Move ( c ); // reclaim the space |
||
618 | } |
||
619 | } |
||
620 | |||
621 | /* |
||
622 | ============ |
||
623 | Cache_FreeHigh |
||
624 | |||
625 | Throw things out until the hunk can be expanded to the given point |
||
626 | ============ |
||
627 | */ |
||
628 | void Cache_FreeHigh (int new_high_hunk) |
||
629 | { |
||
630 | cache_system_t *c, *prev; |
||
631 | |||
632 | prev = NULL; |
||
633 | while (1) |
||
634 | { |
||
635 | c = cache_head.prev; |
||
636 | if (c == &cache_head) |
||
637 | return; // nothing in cache at all |
||
638 | if ( (byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk) |
||
639 | return; // there is space to grow the hunk |
||
640 | if (c == prev) |
||
641 | Cache_Free (c->user); // didn't move out of the way |
||
642 | else |
||
643 | { |
||
644 | Cache_Move (c); // try to move it |
||
645 | prev = c; |
||
646 | } |
||
647 | } |
||
648 | } |
||
649 | |||
650 | void Cache_UnlinkLRU (cache_system_t *cs) |
||
651 | { |
||
652 | if (!cs->lru_next || !cs->lru_prev) |
||
653 | Sys_Error ("Cache_UnlinkLRU: NULL link"); |
||
654 | |||
655 | cs->lru_next->lru_prev = cs->lru_prev; |
||
656 | cs->lru_prev->lru_next = cs->lru_next; |
||
657 | |||
658 | cs->lru_prev = cs->lru_next = NULL; |
||
659 | } |
||
660 | |||
661 | void Cache_MakeLRU (cache_system_t *cs) |
||
662 | { |
||
663 | if (cs->lru_next || cs->lru_prev) |
||
664 | Sys_Error ("Cache_MakeLRU: active link"); |
||
665 | |||
666 | cache_head.lru_next->lru_prev = cs; |
||
667 | cs->lru_next = cache_head.lru_next; |
||
668 | cs->lru_prev = &cache_head; |
||
669 | cache_head.lru_next = cs; |
||
670 | } |
||
671 | |||
672 | /* |
||
673 | ============ |
||
674 | Cache_TryAlloc |
||
675 | |||
676 | Looks for a free block of memory between the high and low hunk marks |
||
677 | Size should already include the header and padding |
||
678 | ============ |
||
679 | */ |
||
680 | cache_system_t *Cache_TryAlloc (int size, qboolean nobottom) |
||
681 | { |
||
682 | cache_system_t *cs, *new; |
||
683 | |||
684 | // is the cache completely empty? |
||
685 | |||
686 | if (!nobottom && cache_head.prev == &cache_head) |
||
687 | { |
||
688 | if (hunk_size - hunk_high_used - hunk_low_used < size) |
||
689 | Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size); |
||
690 | |||
691 | new = (cache_system_t *) (hunk_base + hunk_low_used); |
||
692 | memset (new, 0, sizeof(*new)); |
||
693 | new->size = size; |
||
694 | |||
695 | cache_head.prev = cache_head.next = new; |
||
696 | new->prev = new->next = &cache_head; |
||
697 | |||
698 | Cache_MakeLRU (new); |
||
699 | return new; |
||
700 | } |
||
701 | |||
702 | // search from the bottom up for space |
||
703 | |||
704 | new = (cache_system_t *) (hunk_base + hunk_low_used); |
||
705 | cs = cache_head.next; |
||
706 | |||
707 | do |
||
708 | { |
||
709 | if (!nobottom || cs != cache_head.next) |
||
710 | { |
||
711 | if ( (byte *)cs - (byte *)new >= size) |
||
712 | { // found space |
||
713 | memset (new, 0, sizeof(*new)); |
||
714 | new->size = size; |
||
715 | |||
716 | new->next = cs; |
||
717 | new->prev = cs->prev; |
||
718 | cs->prev->next = new; |
||
719 | cs->prev = new; |
||
720 | |||
721 | Cache_MakeLRU (new); |
||
722 | |||
723 | return new; |
||
724 | } |
||
725 | } |
||
726 | |||
727 | // continue looking |
||
728 | new = (cache_system_t *)((byte *)cs + cs->size); |
||
729 | cs = cs->next; |
||
730 | |||
731 | } while (cs != &cache_head); |
||
732 | |||
733 | // try to allocate one at the very end |
||
734 | if ( hunk_base + hunk_size - hunk_high_used - (byte *)new >= size) |
||
735 | { |
||
736 | memset (new, 0, sizeof(*new)); |
||
737 | new->size = size; |
||
738 | |||
739 | new->next = &cache_head; |
||
740 | new->prev = cache_head.prev; |
||
741 | cache_head.prev->next = new; |
||
742 | cache_head.prev = new; |
||
743 | |||
744 | Cache_MakeLRU (new); |
||
745 | |||
746 | return new; |
||
747 | } |
||
748 | |||
749 | return NULL; // couldn't allocate |
||
750 | } |
||
751 | |||
752 | /* |
||
753 | ============ |
||
754 | Cache_Flush |
||
755 | |||
756 | Throw everything out, so new data will be demand cached |
||
757 | ============ |
||
758 | */ |
||
759 | void Cache_Flush (void) |
||
760 | { |
||
761 | while (cache_head.next != &cache_head) |
||
762 | Cache_Free ( cache_head.next->user ); // reclaim the space |
||
763 | } |
||
764 | |||
765 | |||
766 | /* |
||
767 | ============ |
||
768 | Cache_Print |
||
769 | |||
770 | ============ |
||
771 | */ |
||
772 | void Cache_Print (void) |
||
773 | { |
||
774 | cache_system_t *cd; |
||
775 | |||
776 | for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next) |
||
777 | { |
||
778 | Con_Printf ("%8i : %s\n", cd->size, cd->name); |
||
779 | } |
||
780 | } |
||
781 | |||
782 | /* |
||
783 | ============ |
||
784 | Cache_Report |
||
785 | |||
786 | ============ |
||
787 | */ |
||
788 | void Cache_Report (void) |
||
789 | { |
||
790 | Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) ); |
||
791 | } |
||
792 | |||
793 | /* |
||
794 | ============ |
||
795 | Cache_Compact |
||
796 | |||
797 | ============ |
||
798 | */ |
||
799 | void Cache_Compact (void) |
||
800 | { |
||
801 | } |
||
802 | |||
803 | /* |
||
804 | ============ |
||
805 | Cache_Init |
||
806 | |||
807 | ============ |
||
808 | */ |
||
809 | void Cache_Init (void) |
||
810 | { |
||
811 | cache_head.next = cache_head.prev = &cache_head; |
||
812 | cache_head.lru_next = cache_head.lru_prev = &cache_head; |
||
813 | |||
814 | Cmd_AddCommand ("flush", Cache_Flush); |
||
815 | } |
||
816 | |||
817 | /* |
||
818 | ============== |
||
819 | Cache_Free |
||
820 | |||
821 | Frees the memory and removes it from the LRU list |
||
822 | ============== |
||
823 | */ |
||
824 | void Cache_Free (cache_user_t *c) |
||
825 | { |
||
826 | cache_system_t *cs; |
||
827 | |||
828 | if (!c->data) |
||
829 | Sys_Error ("Cache_Free: not allocated"); |
||
830 | |||
831 | cs = ((cache_system_t *)c->data) - 1; |
||
832 | |||
833 | cs->prev->next = cs->next; |
||
834 | cs->next->prev = cs->prev; |
||
835 | cs->next = cs->prev = NULL; |
||
836 | |||
837 | c->data = NULL; |
||
838 | |||
839 | Cache_UnlinkLRU (cs); |
||
840 | } |
||
841 | |||
842 | |||
843 | |||
844 | /* |
||
845 | ============== |
||
846 | Cache_Check |
||
847 | ============== |
||
848 | */ |
||
849 | void *Cache_Check (cache_user_t *c) |
||
850 | { |
||
851 | cache_system_t *cs; |
||
852 | |||
853 | if (!c->data) |
||
854 | return NULL; |
||
855 | |||
856 | cs = ((cache_system_t *)c->data) - 1; |
||
857 | |||
858 | // move to head of LRU |
||
859 | Cache_UnlinkLRU (cs); |
||
860 | Cache_MakeLRU (cs); |
||
861 | |||
862 | return c->data; |
||
863 | } |
||
864 | |||
865 | |||
866 | /* |
||
867 | ============== |
||
868 | Cache_Alloc |
||
869 | ============== |
||
870 | */ |
||
871 | void *Cache_Alloc (cache_user_t *c, int size, char *name) |
||
872 | { |
||
873 | cache_system_t *cs; |
||
874 | |||
875 | if (c->data) |
||
876 | Sys_Error ("Cache_Alloc: allready allocated"); |
||
877 | |||
878 | if (size <= 0) |
||
879 | Sys_Error ("Cache_Alloc: size %i", size); |
||
880 | |||
881 | size = (size + sizeof(cache_system_t) + 15) & ~15; |
||
882 | |||
883 | // find memory for it |
||
884 | while (1) |
||
885 | { |
||
886 | cs = Cache_TryAlloc (size, false); |
||
887 | if (cs) |
||
888 | { |
||
889 | strncpy (cs->name, name, sizeof(cs->name)-1); |
||
890 | c->data = (void *)(cs+1); |
||
891 | cs->user = c; |
||
892 | break; |
||
893 | } |
||
894 | |||
895 | // free the least recently used cahedat |
||
896 | if (cache_head.lru_prev == &cache_head) |
||
897 | Sys_Error ("Cache_Alloc: out of memory"); |
||
898 | // not enough memory at all |
||
899 | Cache_Free ( cache_head.lru_prev->user ); |
||
900 | } |
||
901 | |||
902 | return Cache_Check (c); |
||
903 | } |
||
904 | |||
905 | //============================================================================ |
||
906 | |||
907 | |||
908 | /* |
||
909 | ======================== |
||
910 | Memory_Init |
||
911 | ======================== |
||
912 | */ |
||
913 | void Memory_Init (void *buf, int size) |
||
914 | { |
||
915 | int p; |
||
916 | int zonesize = DYNAMIC_SIZE; |
||
917 | |||
918 | hunk_base = buf; |
||
919 | hunk_size = size; |
||
920 | hunk_low_used = 0; |
||
921 | hunk_high_used = 0; |
||
922 | |||
923 | Cache_Init (); |
||
924 | p = COM_CheckParm ("-zone"); |
||
925 | if (p) |
||
926 | { |
||
927 | if (p < com_argc-1) |
||
928 | zonesize = Q_atoi (com_argv[p+1]) * 1024; |
||
929 | else |
||
930 | Sys_Error ("Memory_Init: you must specify a size in KB after -zone"); |
||
931 | } |
||
932 | mainzone = Hunk_AllocName (zonesize, "zone" ); |
||
933 | Z_ClearZone (mainzone, zonesize); |
||
934 | }>=>>=>>>>>>>>>> |
||
935 |