chiark / gitweb /
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 }