Initial revision
[blender-staging.git] / intern / guardedalloc / intern / mallocn.c
1 /**
2  * $Id$
3  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version. The Blender
9  * Foundation also sells licenses for use in proprietary software under
10  * the Blender License.  See http://www.blender.org/BL/ for information
11  * about this.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 /**
33
34  * $Id$
35  * Copyright (C) 2001 NaN Technologies B.V.
36  * Guarded memory allocation, and boundary-write detection.
37  */
38
39 #include <stdlib.h>
40
41 #include "MEM_guardedalloc.h"
42
43 /* --------------------------------------------------------------------- */
44 /* Data definition                                                       */
45 /* --------------------------------------------------------------------- */
46 /* all memory chunks are put in linked lists */
47 typedef struct localLink
48 {
49         struct localLink *next,*prev;
50 } localLink;
51
52 typedef struct localListBase 
53 {
54         void *first, *last;
55 } localListBase;
56
57 typedef struct MemHead {
58         int tag1;
59         int len;
60         struct MemHead *next,*prev;
61         char * name;
62         char * nextname;
63         /*  int level; */ /* historical, can be removed, but check alignment issues - zr */
64         int tag2;
65 } MemHead;
66
67 typedef struct MemTail {
68         int tag3, pad;
69 } MemTail;
70
71 /* --------------------------------------------------------------------- */
72 /* local functions                                                       */
73 /* --------------------------------------------------------------------- */
74
75 static void addtail(localListBase *listbase, void *vlink);
76 static void remlink(localListBase *listbase, void *vlink);
77 static void rem_memblock(MemHead *memh);
78 static void MemorY_ErroR(char *block, char *error);
79 static char *check_memlist(MemHead *memh);
80
81 /* --------------------------------------------------------------------- */
82 /* locally used defines                                                  */
83 /* --------------------------------------------------------------------- */
84
85 #if defined( __sgi) || defined (__sun__) || defined (__PPC__) || defined (__APPLE__)
86 #define MAKE_ID(a,b,c,d) ( (int)(a)<<24 | (int)(b)<<16 | (c)<<8 | (d) )
87 #else
88 #define MAKE_ID(a,b,c,d) ( (int)(d)<<24 | (int)(c)<<16 | (b)<<8 | (a) )
89 #endif
90
91 #define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
92 #define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
93 #define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
94 #define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
95
96 #define MEMNEXT(x) ((MemHead *)(((char *) x) - ((char *) & (((MemHead *)0)->next))))
97         
98 /* --------------------------------------------------------------------- */
99 /* vars                                                                  */
100 /* --------------------------------------------------------------------- */
101         
102 int totblock= 0;
103 int mem_in_use= 0;
104
105 static struct localListBase _membase;
106 static struct localListBase *membase = &_membase;
107 static FILE* err_stream = NULL;
108
109 #ifdef malloc
110 #undef malloc
111 #endif
112
113 #ifdef calloc
114 #undef calloc
115 #endif
116
117 #ifdef free
118 #undef free
119 #endif
120
121
122 /* --------------------------------------------------------------------- */
123 /* implementation                                                        */
124 /* --------------------------------------------------------------------- */
125
126 int MEM_check_memory_integrity()
127 {
128         char* err_val = NULL;
129         MemHead* listend;
130         /* check_memlist starts from the front, and runs until it finds
131          * the requested chunk. For this test, that's the last one. */
132         listend = membase->last;
133         
134         err_val = check_memlist(listend);
135
136         return (int)err_val;
137 }
138
139
140 void MEM_set_error_stream(FILE* i)
141 {
142         err_stream = i;
143 }
144
145
146 int MEM_allocN_len(void *vmemh)
147 {
148         if (vmemh) {
149                 MemHead *memh= vmemh;
150         
151                 memh--;
152                 return memh->len;
153         } else
154                 return 0;
155 }
156
157 void *MEM_dupallocN(void *vmemh)
158 {
159         void *newp= NULL;
160         
161         if (vmemh) {
162                 MemHead *memh= vmemh;
163                 memh--;
164
165                 if (memh->len) {
166                         newp= MEM_mallocN(memh->len, "dupli_alloc");
167                         memcpy(newp, vmemh, memh->len);
168                 } else
169                         if (err_stream) fprintf(err_stream, "error: MEM_dupallocN with len==0 %s\n", memh->name);
170         }
171
172         return newp;
173 }
174
175 void *MEM_mallocN(unsigned int len, char *str)
176 {
177         MemHead *memh;
178         MemTail *memt;
179
180         if(sizeof(long)==8)
181                 len = (len + 3 ) & ~3;  /* eenheden van 4 */
182         else 
183                 len = (len + 7 ) & ~7;  /* eenheden van 8 */
184         
185         memh= (MemHead *)malloc(len+sizeof(MemHead)+sizeof(MemTail));
186
187         if(memh!=0) {
188                 memh->tag1 = MEMTAG1;
189                 memh->name = str;
190                 memh->nextname = 0;
191                 memh->len = len;
192 /*              memh->level = 0; */
193                 memh->tag2 = MEMTAG2;
194
195                 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
196                 memt->tag3 = MEMTAG3;
197
198                 addtail(membase,&memh->next);
199                 if (memh->next) memh->nextname = MEMNEXT(memh->next)->name;
200
201                 totblock++;
202                 mem_in_use += len;
203                 return (++memh);
204         }
205         if (err_stream) fprintf(err_stream, "Malloc returns nill: len=%d in %s\n",len,str);
206         return 0;
207 }
208
209 void *MEM_callocN(unsigned int len, char *str)
210 {
211         MemHead *memh;
212         MemTail *memt;
213
214         if(sizeof(long)==8)
215                 len = (len + 3 ) & ~3;  /* eenheden van 4 */
216         else 
217                 len = (len + 7 ) & ~7;  /* eenheden van 8 */
218
219         memh= (MemHead *)calloc(len+sizeof(MemHead)+sizeof(MemTail),1);
220
221         if(memh!=0) {
222                 memh->tag1 = MEMTAG1;
223                 memh->name = str;
224                 memh->nextname = 0;
225                 memh->len = len;
226 /*              memh->level = 0; */
227                 memh->tag2 = MEMTAG2;
228
229                 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
230                 memt->tag3 = MEMTAG3;
231
232                 addtail(membase,&memh->next);
233                 if (memh->next) memh->nextname = MEMNEXT(memh->next)->name;
234
235                 totblock++;
236                 mem_in_use += len;
237                 return (++memh);
238         }
239         if (err_stream) fprintf(err_stream, "Calloc returns nill: len=%d in %s\n",len,str);
240         return 0;
241 }
242
243
244 void MEM_printmemlist()
245 {
246         MemHead *membl;
247
248         membl = membase->first;
249         if (membl) membl = MEMNEXT(membl);
250         while(membl) {
251                 if (err_stream) fprintf(err_stream, "%s len: %d %p\n",membl->name,membl->len, membl+1);
252                 if(membl->next)
253                         membl= MEMNEXT(membl->next);
254                 else break;
255         }
256 }
257
258 short MEM_freeN(void *vmemh)            /* anders compileertie niet meer */
259 {
260         short error = 0;
261         MemTail *memt;
262         MemHead *memh= vmemh;
263         char *name;
264
265         if (memh == 0){
266                 MemorY_ErroR("free","attempt to free NULL pointer");
267                 /* if (err_stream) fprintf(err_stream, "%d\n", (memh+4000)->tag1); */
268                 return(-1);
269         }
270
271         if(sizeof(long)==8) {
272                 if (((long) memh) & 0x7) {
273                         MemorY_ErroR("free","attempt to free illegal pointer");
274                         return(-1);
275                 }
276         }
277         else {
278                 if (((long) memh) & 0x3) {
279                         MemorY_ErroR("free","attempt to free illegal pointer");
280                         return(-1);
281                 }
282         }
283         
284         memh--;
285         if(memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
286                 MemorY_ErroR(memh->name,"double free");
287                 return(-1);
288         }
289
290         if ((memh->tag1 == MEMTAG1) && (memh->tag2 == MEMTAG2) && ((memh->len & 0x3) == 0)) {
291                 memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
292                 if (memt->tag3 == MEMTAG3){
293                         
294                         memh->tag1 = MEMFREE;
295                         memh->tag2 = MEMFREE;
296                         memt->tag3 = MEMFREE;
297                         /* na tags !!! */
298                         rem_memblock(memh);
299                         
300                         return(0);
301                 }
302                 error = 2;
303                 MemorY_ErroR(memh->name,"end corrupt");
304                 name = check_memlist(memh);
305                 if (name != 0){
306                         if (name != memh->name) MemorY_ErroR(name,"is also corrupt");
307                 }
308         } else{
309                 error = -1;
310                 name = check_memlist(memh);
311                 if (name == 0) MemorY_ErroR("free","pointer not in memlist");
312                 else MemorY_ErroR(name,"error in header");
313         }
314
315         totblock--;
316         /* hier moet een DUMP plaatsvinden */
317
318         return(error);
319 }
320
321 /* --------------------------------------------------------------------- */
322 /* local functions                                                       */
323 /* --------------------------------------------------------------------- */
324
325 static void addtail(localListBase *listbase, void *vlink)
326 {
327         struct localLink *link= vlink;
328
329         if (link == 0) return;
330         if (listbase == 0) return;
331
332         link->next = 0;
333         link->prev = listbase->last;
334
335         if (listbase->last) ((struct localLink *)listbase->last)->next = link;
336         if (listbase->first == 0) listbase->first = link;
337         listbase->last = link;
338 }
339
340 static void remlink(localListBase *listbase, void *vlink)
341 {
342         struct localLink *link= vlink;
343
344         if (link == 0) return;
345         if (listbase == 0) return;
346
347         if (link->next) link->next->prev = link->prev;
348         if (link->prev) link->prev->next = link->next;
349
350         if (listbase->last == link) listbase->last = link->prev;
351         if (listbase->first == link) listbase->first = link->next;
352 }
353
354 static void rem_memblock(MemHead *memh)
355 {
356         remlink(membase,&memh->next);
357         if (memh->prev){
358                 if (memh->next) MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
359                 else MEMNEXT(memh->prev)->nextname = 0;
360         }
361
362         totblock--;
363         mem_in_use -= memh->len;
364         free(memh);
365 }
366
367 static void MemorY_ErroR(char *block, char *error)
368 {
369         if (err_stream) fprintf(err_stream,"Memoryblock %s: %s\n",block,error);
370 }
371
372 static char *check_memlist(MemHead *memh)
373 {
374         MemHead *forw,*back,*forwok,*backok;
375         char *name;
376
377         forw = membase->first;
378         if (forw) forw = MEMNEXT(forw);
379         forwok = 0;
380         while(forw){
381                 if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
382                 forwok = forw;
383                 if (forw->next) forw = MEMNEXT(forw->next);
384                 else forw = 0;
385         }
386
387         back = (MemHead *) membase->last;
388         if (back) back = MEMNEXT(back);
389         backok = 0;
390         while(back){
391                 if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
392                 backok = back;
393                 if (back->prev) back = MEMNEXT(back->prev);
394                 else back = 0;
395         }
396
397         if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
398
399         if (forw == 0 && back == 0){
400                 /* geen foute headers gevonden dan maar op zoek naar memblock*/
401
402                 forw = membase->first;
403                 if (forw) forw = MEMNEXT(forw);
404                 forwok = 0;
405                 while(forw){
406                         if (forw == memh) break;
407                         if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
408                         forwok = forw;
409                         if (forw->next) forw = MEMNEXT(forw->next);
410                         else forw = 0;
411                 }
412                 if (forw == 0) return (0);
413
414                 back = (MemHead *) membase->last;
415                 if (back) back = MEMNEXT(back);
416                 backok = 0;
417                 while(back){
418                         if (back == memh) break;
419                         if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
420                         backok = back;
421                         if (back->prev) back = MEMNEXT(back->prev);
422                         else back = 0;
423                 }
424         }
425
426         if (forwok) name = forwok->nextname;
427         else name = "No name found";
428
429         if (forw == memh){
430                 /* voor alle zekerheid wordt dit block maar uit de lijst gehaald */
431                 if (forwok){
432                         if (backok){
433                                 forwok->next = (MemHead *)&backok->next;
434                                 backok->prev = (MemHead *)&forwok->next;
435                                 forwok->nextname = backok->name;
436                         } else{
437                                 forwok->next = 0;
438                                 membase->last = (struct localLink *) &forwok->next; 
439 /*                              membase->last = (struct Link *) &forwok->next; */
440                         }
441                 } else{
442                         if (backok){
443                                 backok->prev = 0;
444                                 membase->first = &backok->next;
445                         } else{
446                                 membase->first = membase->last = 0;
447                         }
448                 }
449         } else{
450                 MemorY_ErroR(name,"Additional error in header");
451                 return("Additional error in header");
452         }
453
454         return(name);
455 }
456
457 /* eof */