1 /**********************************************************************
2 * File: memry.c (Formerly memory.c)
3 * Description: Memory allocation with builtin safety checks.
4 * Author: Ray Smith
5 * Created: Wed Jan 22 09:43:33 GMT 1992
6 *
7 * (C) Copyright 1992, Hewlett-Packard Ltd.
8 ** Licensed under the Apache License, Version 2.0 (the "License");
9 ** you may not use this file except in compliance with the License.
10 ** You may obtain a copy of the License at
11 ** http://www.apache.org/licenses/LICENSE-2.0
12 ** Unless required by applicable law or agreed to in writing, software
13 ** distributed under the License is distributed on an "AS IS" BASIS,
14 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 ** See the License for the specific language governing permissions and
16 ** limitations under the License.
17 *
18 **********************************************************************/
19
20 #include "mfcpch.h"
21 #include <stdlib.h>
22 #ifdef __UNIX__
23 #include <assert.h>
24 #endif
25 #include "stderr.h"
26 #include "tprintf.h"
27 #include "memblk.h"
28 #include "memry.h"
29
30 //#define COUNTING_CLASS_STRUCTURES
31
32 /**********************************************************************
33 * new
34 *
35 * Replace global new to get at memory leaks etc.
36 **********************************************************************/
37 /*
38 void* operator new( //allocate memory
39 size_t size //amount to allocate
40 )
41 {
42 if (size==0)
43 {
44 err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES,
45 ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR,
46 "Zero requested of new");
47 size=1;
48 }
49 return alloc_big_mem(size);
50 }
51
52 void operator delete( //free memory
53 void* addr //mem to free
54 )
55 {
56 free_big_mem(addr);
57 }*/
58
59 /**********************************************************************
60 * check_mem
61 *
62 * Check consistency of all memory controlled by alloc_mem.
63 **********************************************************************/
64
check_mem(const char * string,inT8 level)65 DLLSYM void check_mem( //check consistency
66 const char *string, //context message
67 inT8 level //level of check
68 ) {
69 big_mem.check (string, level);
70 main_mem.check (string, level);
71 check_structs(level);
72 }
73
74
75 /**********************************************************************
76 * alloc_string
77 *
78 * Allocate space for a string. The space can only be used for chars as
79 * it is not aligned. Allocation is guaranteed to be fast and not cause
80 * fragmentation for small strings (upto 10*worst alignment). Calls for
81 * larger strings will be satisfied with alloc_mem.
82 * Use free_string to free the space from alloc_string.
83 **********************************************************************/
84
alloc_string(inT32 count)85 DLLSYM char *alloc_string( //allocate string
86 inT32 count //no of chars required
87 ) {
88 #ifdef RAYS_MALLOC
89 char *string; //allocated string
90
91 if (count < 1 || count > MAX_CHUNK) {
92 tprintf ("Invalid size %d requested of alloc_string", count);
93 return NULL;
94 }
95
96 count++; //add size byte
97 if (count <= MAX_STRUCTS * sizeof (MEMUNION)) {
98 string = (char *) alloc_struct (count, "alloc_string");
99 //get a fast structure
100 if (string == NULL) {
101 tprintf ("No memory for alloc_string");
102 return NULL;
103 }
104 string[0] = (inT8) count; //save its length
105 }
106 else {
107 //get a big block
108 string = (char *) alloc_mem (count);
109 if (string == NULL) {
110 tprintf ("No memory for alloc_string");
111 return NULL;
112 }
113 string[0] = 0; //mark its id
114 }
115 return &string[1]; //string for user
116 #else
117 // Round up the amount allocated to a multiple of 4
118 return static_cast<char*>(malloc((count + 3) & ~3));
119 #endif
120 }
121
122
123 /**********************************************************************
124 * free_string
125 *
126 * Free a string allocated by alloc_string.
127 **********************************************************************/
128
free_string(char * string)129 DLLSYM void free_string( //free a string
130 char *string //string to free
131 ) {
132 #ifdef RAYS_MALLOC
133 if (((ptrdiff_t) string & 3) == 1) { //one over word
134 string--; //get id marker
135 if (*string == 0) {
136 free_mem(string); //generally free it
137 return;
138 }
139 else if (*string <= MAX_STRUCTS * sizeof (MEMUNION)) {
140 //free structure
141 free_struct (string, *string, "alloc_string");
142 return;
143 }
144 }
145 tprintf ("Non-string given to free_string");
146 #else
147 free(string);
148 #endif
149 }
150
151
152 /**********************************************************************
153 * alloc_struct
154 *
155 * Allocate space for a structure. This function is designed to be
156 * fast and fragmentation free for arbitrary combinations of small
157 * objects. (Upto 40 bytes in length.)
158 * It can be used for any size of object up to 512K, but you must use
159 * free_struct to release the memory it gives. alloc_mem is better
160 * for arbitrary data blocks of large size (>40 bytes.)
161 * alloc_struct always aborts if the allocation fails.
162 **********************************************************************/
163
164 DLLSYM void *
alloc_struct(inT32 count,const char * name)165 alloc_struct ( //allocate memory
166 inT32 count, //no of chars required
167 #if defined COUNTING_CLASS_STRUCTURES
168 const char *name //name of type
169 #else
170 const char * //name of type
171 #endif
172 ) {
173 #ifdef RAYS_MALLOC
174 MEMUNION *element; //current element
175 MEMUNION *returnelement; //return value
176 inT32 struct_count; //no of required structs
177 inT32 blocksize; //no of structs in block
178 inT32 index; //index to structure
179
180 if (count < 1 || count > MAX_CHUNK) {
181 tprintf ("Invalid size %d requested of alloc_struct", count);
182 return NULL;
183 }
184
185 // tprintf("Allocating structure of size %d\n",count);
186 //no of MEMUNIONS-1
187 struct_count = (count - 1) / sizeof (MEMUNION);
188 if (struct_count < MAX_STRUCTS) {
189 //can do fixed sizes
190 #ifdef COUNTING_CLASS_STRUCTURES
191 if (name != NULL) {
192 index = identify_struct_owner (struct_count, name);
193 if (index < MAX_CLASSES)
194 owner_counts[struct_count][index]++;
195 }
196 #endif
197 //head of freelist
198 returnelement = free_structs[struct_count];
199 if (returnelement == NULL) {
200 //need a new block
201 //get one
202 element = (MEMUNION *) new_struct_block ();
203 if (element == NULL) {
204 tprintf ("No memory to satisfy request for %d", (int) count);
205 return NULL;
206 }
207 //add to block list
208 element->ptr = struct_blocks[struct_count];
209 struct_blocks[struct_count] = element;
210 blocks_in_use[struct_count]++;
211 element++; //free cell
212 returnelement = element; //going to return 1st
213 blocksize = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1;
214
215 for (index = 1; index < blocksize; index++) {
216 //make links
217 element->ptr = element + struct_count + 1;
218 element += struct_count + 1;
219 }
220 element->ptr = NULL; //end of freelist
221 }
222 //new free one
223 free_structs[struct_count] = returnelement->ptr;
224 //count number issued
225 structs_in_use[struct_count]++;
226 }
227 else {
228 //just get some
229 returnelement = (MEMUNION *) alloc_mem (count);
230 if (returnelement == NULL) {
231 tprintf ("No memory to satisfy request for %d", (int) count);
232 return NULL;
233 }
234 }
235 return returnelement; //free cell
236 #else
237 return malloc(count);
238 #endif
239 }
240
241
242 /**********************************************************************
243 * free_struct
244 *
245 * Free memory allocated by alloc_struct. The size must be supplied.
246 **********************************************************************/
247
248 DLLSYM void
free_struct(void * deadstruct,inT32 count,const char * name)249 free_struct ( //free a structure
250 void *deadstruct, //structure to free
251 inT32 count, //no of bytes
252 #if defined COUNTING_CLASS_STRUCTURES
253 const char *name //name of type
254 #else
255 const char * //name of type
256 #endif
257 ) {
258 #ifdef RAYS_MALLOC
259 MEMUNION *end_element; //current element
260 MEMUNION *element; //current element
261 MEMUNION *prev_element; //previous element
262 MEMUNION *prev_block; //previous element
263 MEMUNION *nextblock; //next block in list
264 MEMUNION *block; //next block in list
265 inT32 struct_count; //no of required structs
266 inT32 index; //to structure counts
267
268 if (count < 1 || count > MAX_CHUNK) {
269 tprintf ("Invalid size %d requested of free_struct", count);
270 return;
271 }
272
273 // tprintf("Freeing structure of size %d\n",count);
274 //no of MEMUNIONS-1
275 struct_count = (count - 1) / sizeof (MEMUNION);
276
277 if (deadstruct == NULL) {
278 //not really legal
279 check_struct(MEMCHECKS, count);
280 }
281 else {
282 if (struct_count < MAX_STRUCTS) {
283 //can do fixed sizes
284 #ifdef COUNTING_CLASS_STRUCTURES
285 if (name != NULL) {
286 index = identify_struct_owner (struct_count, name);
287 if (index < MAX_CLASSES) {
288 owner_counts[struct_count][index]--;
289 ASSERT_HOST (owner_counts[struct_count][index] >= 0);
290 }
291 }
292 #endif
293 element = (MEMUNION *) deadstruct;
294 //add to freelist
295 element->ptr = free_structs[struct_count];
296 free_structs[struct_count] = element;
297 //one less in use
298 structs_in_use[struct_count]--;
299 if (structs_in_use[struct_count] == 0) {
300 index = 0;
301 for (element = struct_blocks[struct_count];
302 element != NULL; element = nextblock) {
303 //traverse and destroy
304 nextblock = element->ptr;
305 //free all the blocks
306 old_struct_block(element);
307 index++;
308 }
309 //none left any more
310 struct_blocks[struct_count] = NULL;
311 //no free structs
312 free_structs[struct_count] = NULL;
313 blocks_in_use[struct_count] = 0;
314 }
315 else if (structs_in_use[struct_count] < 0) {
316 tprintf ("Negative number of structs of size %d in use",
317 (int) count);
318 }
319 else if (structs_in_use[struct_count] < blocks_in_use[struct_count]) {
320 prev_block = NULL;
321 for (block = struct_blocks[struct_count];
322 block != NULL; block = nextblock) {
323 nextblock = block;
324 index = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1;
325 end_element = block + STRUCT_BLOCK_SIZE;
326 for (element = free_structs[struct_count];
327 element != NULL; element = element->ptr) {
328 if (element > nextblock && element < end_element) {
329 index--;
330 if (index == 0)
331 break;
332 }
333 }
334 if (index == 0) {
335 index = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1;
336 for (element =
337 free_structs[struct_count], prev_element = NULL;
338 element != NULL; element = element->ptr) {
339 if (element > nextblock && element < end_element) {
340 index--;
341 if (prev_element != NULL)
342 prev_element->ptr = element->ptr;
343 else
344 free_structs[struct_count] = element->ptr;
345 if (index == 0)
346 break;
347 }
348 else
349 prev_element = element;
350 }
351 if (prev_block != NULL)
352 prev_block->ptr = block->ptr;
353 else
354 struct_blocks[struct_count] = block->ptr;
355 nextblock = block->ptr;
356 blocks_in_use[struct_count]--;
357 //free all the blocks
358 old_struct_block(block);
359 }
360 else {
361 prev_block = block;
362 //traverse and destroy
363 nextblock = block->ptr;
364 }
365 }
366 }
367 }
368 else
369 free_mem(deadstruct); //free directly
370 }
371 #else
372 free(deadstruct);
373 #endif
374 }
375
376
377 /**********************************************************************
378 * alloc_mem_p
379 *
380 * Allocate permanent space which will never be returned.
381 * This space is allocated from the top end of a memory block to
382 * avoid the fragmentation which would result from alternate use
383 * of alloc_mem for permanent and temporary blocks.
384 **********************************************************************/
385
386 //#ifdef __UNIX__
387 //#pragma OPT_LEVEL 0
388 //#endif
alloc_mem_p(inT32 count)389 DLLSYM void *alloc_mem_p( //allocate permanent space
390 inT32 count //block size to allocate
391 ) {
392 #ifdef RAYS_MALLOC
393 #ifdef TESTING_BIGSTUFF
394 if (main_mem.biggestblock == 0)
395 main_mem.init (alloc_big_mem, free_big_mem,
396 FIRSTSIZE, LASTSIZE, MAX_CHUNK);
397 #else
398 if (main_mem.biggestblock == 0)
399 main_mem.init ((void *(*)(inT32)) malloc, free,
400 FIRSTSIZE, LASTSIZE, MAX_CHUNK);
401 #endif
402 if (mem_mallocdepth > 0)
403 return main_mem.alloc_p (count, trace_caller (mem_mallocdepth));
404 else
405 return main_mem.alloc_p (count, NULL);
406 #else
407 return malloc ((size_t) count);
408 #endif
409 }
410
411
412 /**********************************************************************
413 * alloc_mem
414 *
415 * Return a pointer to a buffer of count bytes aligned for any type.
416 **********************************************************************/
417
alloc_mem(inT32 count)418 DLLSYM void *alloc_mem( //get some memory
419 inT32 count //no of bytes to get
420 ) {
421 #ifdef RAYS_MALLOC
422 #ifdef TESTING_BIGSTUFF
423 if (main_mem.biggestblock == 0)
424 main_mem.init (alloc_big_mem, free_big_mem,
425 FIRSTSIZE, LASTSIZE, MAX_CHUNK);
426 #else
427 if (main_mem.biggestblock == 0)
428 main_mem.init ((void *(*)(inT32)) malloc, free,
429 FIRSTSIZE, LASTSIZE, MAX_CHUNK);
430 #endif
431 if (mem_mallocdepth > 0)
432 return main_mem.alloc (count, trace_caller (mem_mallocdepth));
433 else
434 return main_mem.alloc (count, NULL);
435 #else
436 return malloc ((size_t) count);
437 #endif
438 }
439
440
441 /**********************************************************************
442 * alloc_big_mem
443 *
444 * Return a pointer to a buffer of count bytes aligned for any type.
445 **********************************************************************/
446
alloc_big_mem(inT32 count)447 DLLSYM void *alloc_big_mem( //get some memory
448 inT32 count //no of bytes to get
449 ) {
450 #ifdef TESTING_BIGSTUFF
451 if (big_mem.biggestblock == 0)
452 big_mem.init ((void *(*)(inT32)) malloc, free,
453 BIGSIZE, BIGSIZE, MAX_BIGCHUNK);
454 if (mem_mallocdepth > 0)
455 return big_mem.alloc (count, trace_caller (mem_mallocdepth));
456 else
457 return big_mem.alloc (count, NULL);
458 #else
459 return malloc ((size_t) count);
460 #endif
461 }
462
463
464 /**********************************************************************
465 * alloc_big_zeros
466 *
467 * Return a pointer to a buffer of count bytes aligned for any type.
468 **********************************************************************/
469
alloc_big_zeros(inT32 count)470 DLLSYM void *alloc_big_zeros( //get some memory
471 inT32 count //no of bytes to get
472 ) {
473 #ifdef TESTING_BIGSTUFF
474 if (big_mem.biggestblock == 0)
475 big_mem.init ((void *(*)(inT32)) malloc, free,
476 BIGSIZE, BIGSIZE, MAX_BIGCHUNK);
477 void *buf; //return value
478
479 if (mem_mallocdepth > 0)
480 buf = big_mem.alloc (count, trace_caller (mem_mallocdepth));
481 else
482 buf = big_mem.alloc (count, NULL);
483 memset (buf, 0, count);
484 return buf;
485 #else
486 return calloc ((size_t) count, 1);
487 #endif
488 }
489
490
491 /**********************************************************************
492 * free_mem
493 *
494 * Free a block allocated by alloc_mem (or alloc_mem_p).
495 * It checks that the pointer is legal and maintains counts of the
496 * amount of free memory above and below the current free pointer.
497 **********************************************************************/
498
free_mem(void * oldchunk)499 DLLSYM void free_mem( //free mem from alloc_mem
500 void *oldchunk //chunk to free
501 ) {
502 #ifdef RAYS_MALLOC
503 if (mem_freedepth > 0 && main_mem.callers != NULL)
504 main_mem.dealloc (oldchunk, trace_caller (mem_freedepth));
505 else
506 main_mem.dealloc (oldchunk, NULL);
507 #else
508 free(oldchunk);
509 #endif
510 }
511
512
513 /**********************************************************************
514 * free_big_mem
515 *
516 * Free a block allocated by alloc_big_mem.
517 * It checks that the pointer is legal and maintains counts of the
518 * amount of free memory above and below the current free pointer.
519 **********************************************************************/
520
free_big_mem(void * oldchunk)521 DLLSYM void free_big_mem( //free mem from alloc_mem
522 void *oldchunk //chunk to free
523 ) {
524 #ifdef TESTING_BIGSTUFF
525 if (mem_freedepth > 0 && main_mem.callers != NULL)
526 big_mem.dealloc (oldchunk, trace_caller (mem_freedepth));
527 else
528 big_mem.dealloc (oldchunk, NULL);
529 #else
530 free(oldchunk);
531 #endif
532 }
533