Initial revision
[ssr] / StraySrc / Libraries / Steel / c / mem
1 /*
2 * mem
3 * Store handling for Steel
4 *
5 * version 1.00 6 September 1993
6 *
7 * © 1993-1998 Straylight
8 */
9
10 /*----- Licensing note ----------------------------------------------------*
11 *
12 * This file is part of Straylight's Steel library.
13 *
14 * Steel is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2, or (at your option)
17 * any later version.
18 *
19 * Steel is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Steel. If not, write to the Free Software Foundation,
26 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 #include "flex.h"
30 #include "heap.h"
31 #include "wimpt.h"
32 #include "mem.h"
33 #include "kernel.h"
34 #include "bbc.h"
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdio.h>
38
39 #ifndef BOOL
40 #define BOOL int
41 #define TRUE 1
42 #define FALSE 0
43 #endif
44
45 #define mem__MALLOC 1
46 #define mem__HEAP 2
47 #define mem__RMA 3
48 #define mem__USER 4
49
50 /* #define mem__DEBUG_MALLOC */
51 /* Uncomment previous line for memDebug */
52
53 #define mem__MAGIC 0x45464153 /* Says 'SAFE' */
54 #define mem__SENTINEL 0x59524442 /* Says 'BDRY' */
55
56 #define OS_Module 0x2001E
57
58 typedef struct mem__rmaListstr
59 {
60 struct mem__rmaListstr *next;
61 struct mem__rmaListstr *prev;
62 #ifdef mem__DEBUG_MALLOC
63 int length;
64 int check;
65 int *sentinel;
66 #endif
67 }
68 mem__rmaListstr;
69
70 static BOOL mem__initedFlex;
71 static BOOL mem__initedHeap;
72 static int mem__currentAlloc=mem__MALLOC;
73 static mem__rmaListstr *mem__rmaList;
74 #ifdef mem__DEBUG_MALLOC
75 static mem__rmaListstr *mem__mallocList;
76 #endif
77 static BOOL mem__usedRMA;
78
79 static mem_allocProc mem__userAlloc;
80 static mem_freeProc mem__userFree;
81
82 /*
83 * void mem_flexdInit(const char *name)
84 *
85 * Use
86 * Initialises flex system. If flex is already initialised, nothing
87 * happens. This call should be used in preference to flex_init().
88 */
89
90 void mem_flexdInit(const char *name, long max)
91 {
92 if (!mem__initedFlex)
93 {
94 flex_dinit(name, max);
95 atexit(flex_die);
96 mem__initedFlex=TRUE;
97 }
98 }
99
100 #ifdef _DLL
101 void (mem_flexInit)(void) { mem_flexInit(); }
102 #endif
103
104 /*
105 * void mem_heapInit(void)
106 *
107 * Use
108 * Initialises heap system. If heap is already initialised, nothing
109 * happens. If flex is not initialised, it is initialised for you. This
110 * call should be used in preference to heap_init(). Note that unlike
111 * earlier versioms, this call will NOT set up ArmenLib to use heap by
112 * default. This would be duplicating the functionality of mem_useHeap().
113 */
114
115 void mem_heapInit(void)
116 {
117 if (!mem__initedHeap)
118 {
119 mem_flexInit();
120 heap_init();
121 mem__initedHeap=TRUE;
122 }
123 }
124
125 /*
126 * void mem_useMalloc(void)
127 *
128 * Use
129 * Makes ArmenLib use malloc() etc. for memory allocation.
130 */
131
132 void mem_useMalloc(void)
133 {
134 mem__currentAlloc=mem__MALLOC;
135 }
136
137 /*
138 * void mem_useHeap(void)
139 *
140 * Use
141 * Makes ArmenLib use heap_alloc() etc. for memory allocation. It is
142 * initialised if necessary.
143 */
144
145 void mem_useHeap(void)
146 {
147 mem_heapInit();
148 mem__currentAlloc=mem__HEAP;
149 }
150
151 /*
152 * void mem_useUser(mem_allocProc alloc,mem_freeProc free)
153 *
154 * Use
155 * Makes ArmenLib use your own user-defined memory allocation procedures.
156 *
157 * Parameters
158 * mem_allocProc alloc == a routine to allocate a block of memory. If a
159 * block of sufficient size cannot be allocated, return 0. Problems
160 * about allocating blocks of 0 size are handled before your routine is
161 * called.
162 * mem_freeProc free == a routine to free a block of memory. A valid
163 * pointer will be passed to your routine.
164 */
165
166 void mem_useUser(mem_allocProc alloc,mem_freeProc free)
167 {
168 mem__currentAlloc=mem__USER;
169 mem__userAlloc=alloc;
170 mem__userFree=free;
171 }
172
173 /*
174 * void mem_useRMA(void)
175 *
176 * Use
177 * Makes ArmenLib use the RMA for memory allocation.
178 */
179
180 void mem_useRMA(void)
181 {
182 mem__currentAlloc=mem__RMA;
183 }
184
185 /*
186 * void mem__tidyRMA(void)
187 *
188 * Use
189 * Frees any blocks allocated from the RMA at the end of the program, to
190 * avoid any problems with blocks being non-deallocatable.
191 */
192
193 static void mem__tidyRMA(void)
194 {
195 mem__rmaListstr *l=mem__rmaList;
196 mem__rmaListstr *m;
197 while (l!=0)
198 {
199 m=l;
200 l=m->next;
201 mem_RMAfree(m+1);
202 }
203 }
204
205 /*
206 * void *mem_RMAalloc(size_t size)
207 *
208 * Use
209 * Allocates a block of memory from the relocatable module area.
210 *
211 * Parameters
212 * size_t size == the size of the block
213 *
214 * Returns
215 * A pointer to the block (or 0)
216 */
217
218 void *mem_RMAalloc(size_t size)
219 {
220 _kernel_swi_regs r;
221 mem__rmaListstr *l;
222 if (mem__usedRMA==FALSE)
223 {
224 atexit(mem__tidyRMA);
225 mem__usedRMA=TRUE;
226 }
227 r.r[0]=6;
228 r.r[3]=size+sizeof(mem__rmaListstr);
229 if (_kernel_swi(OS_Module,&r,&r))
230 return (0);
231 else
232 {
233 l=(mem__rmaListstr *)r.r[2];
234 if (mem__rmaList)
235 mem__rmaList->prev=l;
236 l->next=mem__rmaList;
237 l->prev=0;
238 mem__rmaList=l;
239 return ((void *)(l+1));
240 }
241 }
242
243 /*
244 * void mem_RMAfree(void *ptr)
245 *
246 * Use
247 * Frees a block allocated using RMAalloc.
248 *
249 * Parameters
250 * void *ptr == a pointer to the block.
251 */
252
253 void mem_RMAfree(void *ptr)
254 {
255 _kernel_swi_regs r;
256 mem__rmaListstr *l=((mem__rmaListstr *)(ptr))-1;
257 if (l->prev!=0)
258 l->prev->next=l->next;
259 else
260 mem__rmaList=l->next;
261 if (l->next!=0)
262 l->next->prev=l->prev;
263 r.r[0]=7;
264 r.r[2]=(int)l;
265 wimpt_noerr((os_error *)_kernel_swi(OS_Module,&r,&r));
266 }
267
268 #ifdef mem__DEBUG_MALLOC
269
270 #define mem__PASS_TEST 1
271 #define mem__PASS_DONE 0
272 #define mem__PASS_BAD 2
273 #define mem__PASS_DUMP 3
274
275 #define mem__walkmsg(x) \
276 { \
277 if (pass==mem__PASS_DUMP) \
278 printf(x); \
279 else \
280 pass=mem__PASS_BAD; \
281 }
282
283 /*
284 * static void mem__walkMalloc(char *op)
285 *
286 * Use
287 * Part of checked malloc routines. Checks that all allocated blocks are
288 * behaving well.
289 *
290 * Parameters
291 * char *op == are we allocating or deallocating?
292 */
293
294 static void mem__walkMalloc(char *op)
295 {
296 mem__rmaListstr *l;
297 int pass=mem__PASS_TEST;
298 while (pass!=mem__PASS_DONE)
299 {
300 l=mem__mallocList;
301 while (l)
302 {
303 if (pass==mem__PASS_DUMP)
304 printf("\n%p %-8i ",(l+1),l->length);
305 if (l->check!=mem__MAGIC)
306 mem__walkmsg("Bad block header ");
307 if (*(l->sentinel)!=mem__SENTINEL)
308 mem__walkmsg("Block overflow ");
309 if ((int)l->next<0x8000 && l->next!=0)
310 mem__walkmsg("Bad forward link ");
311 if (l->next)
312 {
313 if (l->next->prev!=l)
314 mem__walkmsg("Link corrupted ");
315 }
316 if (l)
317 l=l->next;
318 }
319 switch (pass)
320 {
321 case mem__PASS_BAD:
322 pass=mem__PASS_DUMP;
323 bbc_vdu(26);
324 bbc_vdu(4);
325 bbc_vdu(12);
326 bbc_vdu(14);
327 bbc_vdu(7);
328 printf("Error found in malloc chain during %s\n",op);
329 printf(" malloc block dump follows\n\n");
330 printf("Address Length Faults");
331 /* 12345678 12345678 */
332 break;
333 case mem__PASS_DUMP:
334 printf
335 (
336 "\n"
337 "\n"
338 "Please report this error to Straylight.\n"
339 "\n"
340 "If debugging, press SHIFT-F12 for backtrace, otherwise, press\n"
341 "any key to quit program.\n"
342 );
343 bbc_get();
344 wimpt_forceredraw();
345 exit(1);
346 break;
347 case mem__PASS_TEST:
348 pass=mem__PASS_DONE;
349 break;
350 }
351 }
352 }
353
354 /*
355 * void *mem__debugAlloc(size_t size)
356 *
357 * Use
358 * malloc debugging allocation routine.
359 *
360 * Parameters
361 * size_t size == the size of the block
362 *
363 * Returns
364 * A pointer to the block (or 0)
365 */
366
367 static void *mem__debugAlloc(size_t size)
368 {
369 mem__rmaListstr *l;
370 int sent;
371 mem__walkMalloc("alloc");
372 if (l=malloc(size+sizeof(mem__rmaListstr)+4),!l)
373 return (0);
374 else
375 {
376 if (mem__mallocList)
377 mem__mallocList->prev=l;
378 l->next=mem__mallocList;
379 l->prev=0;
380 l->check=mem__MAGIC;
381 mem__mallocList=l;
382 sent=(int)(l+1)+size;
383 if (sent%4)
384 sent+=4-sent%4;
385 l->sentinel=(int *)sent;
386 l->length=size;
387 *(l->sentinel)=mem__SENTINEL;
388 return ((void *)(l+1));
389 }
390 }
391
392 /*
393 * void mem__debugFree(void *ptr)
394 *
395 * Use
396 * Frees a block allocated using mem__debugAlloc
397 *
398 * Parameters
399 * void *ptr == a pointer to the block.
400 */
401
402 static void mem__debugFree(void *ptr)
403 {
404 mem__rmaListstr *l=((mem__rmaListstr *)(ptr))-1;
405 mem__walkMalloc("free");
406 if (l->prev!=0)
407 l->prev->next=l->next;
408 else
409 mem__mallocList=l->next;
410 if (l->next!=0)
411 l->next->prev=l->prev;
412 free(l);
413 }
414
415 #endif
416
417 /*
418 * void *mem_alloc(size_t size)
419 *
420 * Use
421 * Allocates a block of memory using malloc (or heap if initialised). If
422 * size is zero, or allocation fails then a NULL pointer is returned.
423 *
424 * Parameters
425 * size_t size == how big you want the block.
426 *
427 * Returns
428 * A pointer to the block allocated.
429 */
430
431 void *mem_alloc(size_t size)
432 {
433 int *ptr=0;
434 if (size==0)
435 return (0);
436 size+=2*sizeof(int);
437 switch (mem__currentAlloc)
438 {
439 case mem__HEAP:
440 ptr=heap_alloc(size);
441 break;
442 case mem__MALLOC:
443 #ifdef mem__DEBUG_MALLOC
444 ptr=mem__debugAlloc(size);
445 #else
446 ptr=malloc(size);
447 #endif
448 break;
449 case mem__RMA:
450 ptr=mem_RMAalloc(size);
451 break;
452 case mem__USER:
453 ptr=mem__userAlloc(size);
454 break;
455 }
456 if (ptr==0)
457 return (0);
458 ptr[0]=mem__currentAlloc;
459 ptr[1]=size;
460 return ((void *)(ptr+2));
461 }
462
463 /*
464 * void mem_free(void *ptr)
465 *
466 * Purpose
467 * Frees a block of memory. It must have been allocated using mem_alloc().
468 * If ptr is NULL, then mem_free() does nothing.
469 *
470 * Parameters
471 * void *ptr == the pointer returned by mem_alloc()
472 */
473
474 void mem_free(void *ptr)
475 {
476 int *i=((int *)ptr)-2;
477 if (ptr==0)
478 return;
479 switch (i[0])
480 {
481 case mem__MALLOC:
482 #ifdef mem__DEBUG_MALLOC
483 mem__debugFree(i);
484 #else
485 free(i);
486 #endif
487 break;
488 case mem__HEAP:
489 heap_free(i);
490 break;
491 case mem__RMA:
492 mem_RMAfree(i);
493 break;
494 case mem__USER:
495 mem__userFree(i);
496 break;
497 }
498 }
499
500 /*
501 * size_t mem_sizeOfBlock(void *ptr)
502 *
503 * Use
504 * Returns the allocated size of the block.
505 *
506 * Parameters
507 * void *ptr == the pointer to the block
508 *
509 * Returns
510 * The size of the block.
511 */
512
513 size_t mem_sizeOfBlock(void *ptr)
514 {
515 int *i=((int *)ptr)-2;
516 if (ptr==0)
517 return (0);
518 else
519 return (i[1]);
520 }
521
522 /*
523 * void *mem_reAlloc(void *ptr,size_t newSize)
524 *
525 * Use
526 * Alters the size of the block given. If the block could not be resized,
527 * its contents are unchanged. Note that the block may move as a result of
528 * this call.
529 *
530 * Parameters
531 * void *ptr == a pointer to the block. This may be NULL, in which case,
532 * this call behaves exactly like mem_alloc().
533 * size_t newSize == the new size to make the block. This may be zero, in
534 * which case the block is freed.
535 *
536 * Returns
537 * A pointer to the block.
538 */
539
540 void *mem_reAlloc(void *ptr,size_t newSize)
541 {
542 void *newPtr;
543 int size=mem_sizeOfBlock(ptr);
544 if (ptr==0)
545 return (mem_alloc(newSize));
546 if (newPtr=mem_alloc(newSize),newSize==0)
547 return (0);
548 if (newSize<size)
549 size=newSize;
550 memcpy(newPtr,ptr,size);
551 mem_free(ptr);
552 return (newPtr);
553 }
554
555 /*
556 * void mem_allowFlexBudge(BOOL allow)
557 *
558 * Use
559 * A slightly more sensible way to allow flex store to be shunted around.
560 *
561 * Parameters
562 * BOOL allow == whether you want flex store to be shifted around willy-
563 * nilly.
564 */
565
566 void mem_allowFlexBudge(BOOL allow)
567 {
568 _kernel_register_slotextend(allow ? flex_budge : flex_dont_budge);
569 }