| /********************************************************************** |
| * File: memry.c (Formerly memory.c) |
| * Description: Memory allocation with builtin safety checks. |
| * Author: Ray Smith |
| * Created: Wed Jan 22 09:43:33 GMT 1992 |
| * |
| * (C) Copyright 1992, Hewlett-Packard Ltd. |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| * |
| **********************************************************************/ |
| |
| #include "mfcpch.h" |
| #include <stdlib.h> |
| #ifdef __UNIX__ |
| #include <assert.h> |
| #endif |
| #include "stderr.h" |
| #include "tprintf.h" |
| #include "memblk.h" |
| #include "memry.h" |
| |
| //#define COUNTING_CLASS_STRUCTURES |
| |
| /********************************************************************** |
| * new |
| * |
| * Replace global new to get at memory leaks etc. |
| **********************************************************************/ |
| /* |
| void* operator new( //allocate memory |
| size_t size //amount to allocate |
| ) |
| { |
| if (size==0) |
| { |
| err.log(RESULT_LOGICAL_ERROR,E_LOC,ERR_PRIMITIVES, |
| ERR_SCROLLING,ERR_CONTINUE,ERR_ERROR, |
| "Zero requested of new"); |
| size=1; |
| } |
| return alloc_big_mem(size); |
| } |
| |
| void operator delete( //free memory |
| void* addr //mem to free |
| ) |
| { |
| free_big_mem(addr); |
| }*/ |
| |
| /********************************************************************** |
| * check_mem |
| * |
| * Check consistency of all memory controlled by alloc_mem. |
| **********************************************************************/ |
| |
| DLLSYM void check_mem( //check consistency |
| const char *string, //context message |
| inT8 level //level of check |
| ) { |
| big_mem.check (string, level); |
| main_mem.check (string, level); |
| check_structs(level); |
| } |
| |
| |
| /********************************************************************** |
| * alloc_string |
| * |
| * Allocate space for a string. The space can only be used for chars as |
| * it is not aligned. Allocation is guaranteed to be fast and not cause |
| * fragmentation for small strings (upto 10*worst alignment). Calls for |
| * larger strings will be satisfied with alloc_mem. |
| * Use free_string to free the space from alloc_string. |
| **********************************************************************/ |
| |
| DLLSYM char *alloc_string( //allocate string |
| inT32 count //no of chars required |
| ) { |
| #ifdef RAYS_MALLOC |
| char *string; //allocated string |
| |
| if (count < 1 || count > MAX_CHUNK) { |
| tprintf ("Invalid size %d requested of alloc_string", count); |
| return NULL; |
| } |
| |
| count++; //add size byte |
| if (count <= MAX_STRUCTS * sizeof (MEMUNION)) { |
| string = (char *) alloc_struct (count, "alloc_string"); |
| //get a fast structure |
| if (string == NULL) { |
| tprintf ("No memory for alloc_string"); |
| return NULL; |
| } |
| string[0] = (inT8) count; //save its length |
| } |
| else { |
| //get a big block |
| string = (char *) alloc_mem (count); |
| if (string == NULL) { |
| tprintf ("No memory for alloc_string"); |
| return NULL; |
| } |
| string[0] = 0; //mark its id |
| } |
| return &string[1]; //string for user |
| #else |
| // Round up the amount allocated to a multiple of 4 |
| return static_cast<char*>(malloc((count + 3) & ~3)); |
| #endif |
| } |
| |
| |
| /********************************************************************** |
| * free_string |
| * |
| * Free a string allocated by alloc_string. |
| **********************************************************************/ |
| |
| DLLSYM void free_string( //free a string |
| char *string //string to free |
| ) { |
| #ifdef RAYS_MALLOC |
| if (((ptrdiff_t) string & 3) == 1) { //one over word |
| string--; //get id marker |
| if (*string == 0) { |
| free_mem(string); //generally free it |
| return; |
| } |
| else if (*string <= MAX_STRUCTS * sizeof (MEMUNION)) { |
| //free structure |
| free_struct (string, *string, "alloc_string"); |
| return; |
| } |
| } |
| tprintf ("Non-string given to free_string"); |
| #else |
| free(string); |
| #endif |
| } |
| |
| |
| /********************************************************************** |
| * alloc_struct |
| * |
| * Allocate space for a structure. This function is designed to be |
| * fast and fragmentation free for arbitrary combinations of small |
| * objects. (Upto 40 bytes in length.) |
| * It can be used for any size of object up to 512K, but you must use |
| * free_struct to release the memory it gives. alloc_mem is better |
| * for arbitrary data blocks of large size (>40 bytes.) |
| * alloc_struct always aborts if the allocation fails. |
| **********************************************************************/ |
| |
| DLLSYM void * |
| alloc_struct ( //allocate memory |
| inT32 count, //no of chars required |
| #if defined COUNTING_CLASS_STRUCTURES |
| const char *name //name of type |
| #else |
| const char * //name of type |
| #endif |
| ) { |
| #ifdef RAYS_MALLOC |
| MEMUNION *element; //current element |
| MEMUNION *returnelement; //return value |
| inT32 struct_count; //no of required structs |
| inT32 blocksize; //no of structs in block |
| inT32 index; //index to structure |
| |
| if (count < 1 || count > MAX_CHUNK) { |
| tprintf ("Invalid size %d requested of alloc_struct", count); |
| return NULL; |
| } |
| |
| // tprintf("Allocating structure of size %d\n",count); |
| //no of MEMUNIONS-1 |
| struct_count = (count - 1) / sizeof (MEMUNION); |
| if (struct_count < MAX_STRUCTS) { |
| //can do fixed sizes |
| #ifdef COUNTING_CLASS_STRUCTURES |
| if (name != NULL) { |
| index = identify_struct_owner (struct_count, name); |
| if (index < MAX_CLASSES) |
| owner_counts[struct_count][index]++; |
| } |
| #endif |
| //head of freelist |
| returnelement = free_structs[struct_count]; |
| if (returnelement == NULL) { |
| //need a new block |
| //get one |
| element = (MEMUNION *) new_struct_block (); |
| if (element == NULL) { |
| tprintf ("No memory to satisfy request for %d", (int) count); |
| return NULL; |
| } |
| //add to block list |
| element->ptr = struct_blocks[struct_count]; |
| struct_blocks[struct_count] = element; |
| blocks_in_use[struct_count]++; |
| element++; //free cell |
| returnelement = element; //going to return 1st |
| blocksize = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1; |
| |
| for (index = 1; index < blocksize; index++) { |
| //make links |
| element->ptr = element + struct_count + 1; |
| element += struct_count + 1; |
| } |
| element->ptr = NULL; //end of freelist |
| } |
| //new free one |
| free_structs[struct_count] = returnelement->ptr; |
| //count number issued |
| structs_in_use[struct_count]++; |
| } |
| else { |
| //just get some |
| returnelement = (MEMUNION *) alloc_mem (count); |
| if (returnelement == NULL) { |
| tprintf ("No memory to satisfy request for %d", (int) count); |
| return NULL; |
| } |
| } |
| return returnelement; //free cell |
| #else |
| return malloc(count); |
| #endif |
| } |
| |
| |
| /********************************************************************** |
| * free_struct |
| * |
| * Free memory allocated by alloc_struct. The size must be supplied. |
| **********************************************************************/ |
| |
| DLLSYM void |
| free_struct ( //free a structure |
| void *deadstruct, //structure to free |
| inT32 count, //no of bytes |
| #if defined COUNTING_CLASS_STRUCTURES |
| const char *name //name of type |
| #else |
| const char * //name of type |
| #endif |
| ) { |
| #ifdef RAYS_MALLOC |
| MEMUNION *end_element; //current element |
| MEMUNION *element; //current element |
| MEMUNION *prev_element; //previous element |
| MEMUNION *prev_block; //previous element |
| MEMUNION *nextblock; //next block in list |
| MEMUNION *block; //next block in list |
| inT32 struct_count; //no of required structs |
| inT32 index; //to structure counts |
| |
| if (count < 1 || count > MAX_CHUNK) { |
| tprintf ("Invalid size %d requested of free_struct", count); |
| return; |
| } |
| |
| // tprintf("Freeing structure of size %d\n",count); |
| //no of MEMUNIONS-1 |
| struct_count = (count - 1) / sizeof (MEMUNION); |
| |
| if (deadstruct == NULL) { |
| //not really legal |
| check_struct(MEMCHECKS, count); |
| } |
| else { |
| if (struct_count < MAX_STRUCTS) { |
| //can do fixed sizes |
| #ifdef COUNTING_CLASS_STRUCTURES |
| if (name != NULL) { |
| index = identify_struct_owner (struct_count, name); |
| if (index < MAX_CLASSES) { |
| owner_counts[struct_count][index]--; |
| ASSERT_HOST (owner_counts[struct_count][index] >= 0); |
| } |
| } |
| #endif |
| element = (MEMUNION *) deadstruct; |
| //add to freelist |
| element->ptr = free_structs[struct_count]; |
| free_structs[struct_count] = element; |
| //one less in use |
| structs_in_use[struct_count]--; |
| if (structs_in_use[struct_count] == 0) { |
| index = 0; |
| for (element = struct_blocks[struct_count]; |
| element != NULL; element = nextblock) { |
| //traverse and destroy |
| nextblock = element->ptr; |
| //free all the blocks |
| old_struct_block(element); |
| index++; |
| } |
| //none left any more |
| struct_blocks[struct_count] = NULL; |
| //no free structs |
| free_structs[struct_count] = NULL; |
| blocks_in_use[struct_count] = 0; |
| } |
| else if (structs_in_use[struct_count] < 0) { |
| tprintf ("Negative number of structs of size %d in use", |
| (int) count); |
| } |
| else if (structs_in_use[struct_count] < blocks_in_use[struct_count]) { |
| prev_block = NULL; |
| for (block = struct_blocks[struct_count]; |
| block != NULL; block = nextblock) { |
| nextblock = block; |
| index = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1; |
| end_element = block + STRUCT_BLOCK_SIZE; |
| for (element = free_structs[struct_count]; |
| element != NULL; element = element->ptr) { |
| if (element > nextblock && element < end_element) { |
| index--; |
| if (index == 0) |
| break; |
| } |
| } |
| if (index == 0) { |
| index = STRUCT_BLOCK_SIZE / (struct_count + 1) - 1; |
| for (element = |
| free_structs[struct_count], prev_element = NULL; |
| element != NULL; element = element->ptr) { |
| if (element > nextblock && element < end_element) { |
| index--; |
| if (prev_element != NULL) |
| prev_element->ptr = element->ptr; |
| else |
| free_structs[struct_count] = element->ptr; |
| if (index == 0) |
| break; |
| } |
| else |
| prev_element = element; |
| } |
| if (prev_block != NULL) |
| prev_block->ptr = block->ptr; |
| else |
| struct_blocks[struct_count] = block->ptr; |
| nextblock = block->ptr; |
| blocks_in_use[struct_count]--; |
| //free all the blocks |
| old_struct_block(block); |
| } |
| else { |
| prev_block = block; |
| //traverse and destroy |
| nextblock = block->ptr; |
| } |
| } |
| } |
| } |
| else |
| free_mem(deadstruct); //free directly |
| } |
| #else |
| free(deadstruct); |
| #endif |
| } |
| |
| |
| /********************************************************************** |
| * alloc_mem_p |
| * |
| * Allocate permanent space which will never be returned. |
| * This space is allocated from the top end of a memory block to |
| * avoid the fragmentation which would result from alternate use |
| * of alloc_mem for permanent and temporary blocks. |
| **********************************************************************/ |
| |
| //#ifdef __UNIX__ |
| //#pragma OPT_LEVEL 0 |
| //#endif |
| DLLSYM void *alloc_mem_p( //allocate permanent space |
| inT32 count //block size to allocate |
| ) { |
| #ifdef RAYS_MALLOC |
| #ifdef TESTING_BIGSTUFF |
| if (main_mem.biggestblock == 0) |
| main_mem.init (alloc_big_mem, free_big_mem, |
| FIRSTSIZE, LASTSIZE, MAX_CHUNK); |
| #else |
| if (main_mem.biggestblock == 0) |
| main_mem.init ((void *(*)(inT32)) malloc, free, |
| FIRSTSIZE, LASTSIZE, MAX_CHUNK); |
| #endif |
| if (mem_mallocdepth > 0) |
| return main_mem.alloc_p (count, trace_caller (mem_mallocdepth)); |
| else |
| return main_mem.alloc_p (count, NULL); |
| #else |
| return malloc ((size_t) count); |
| #endif |
| } |
| |
| |
| /********************************************************************** |
| * alloc_mem |
| * |
| * Return a pointer to a buffer of count bytes aligned for any type. |
| **********************************************************************/ |
| |
| DLLSYM void *alloc_mem( //get some memory |
| inT32 count //no of bytes to get |
| ) { |
| #ifdef RAYS_MALLOC |
| #ifdef TESTING_BIGSTUFF |
| if (main_mem.biggestblock == 0) |
| main_mem.init (alloc_big_mem, free_big_mem, |
| FIRSTSIZE, LASTSIZE, MAX_CHUNK); |
| #else |
| if (main_mem.biggestblock == 0) |
| main_mem.init ((void *(*)(inT32)) malloc, free, |
| FIRSTSIZE, LASTSIZE, MAX_CHUNK); |
| #endif |
| if (mem_mallocdepth > 0) |
| return main_mem.alloc (count, trace_caller (mem_mallocdepth)); |
| else |
| return main_mem.alloc (count, NULL); |
| #else |
| return malloc ((size_t) count); |
| #endif |
| } |
| |
| |
| /********************************************************************** |
| * alloc_big_mem |
| * |
| * Return a pointer to a buffer of count bytes aligned for any type. |
| **********************************************************************/ |
| |
| DLLSYM void *alloc_big_mem( //get some memory |
| inT32 count //no of bytes to get |
| ) { |
| #ifdef TESTING_BIGSTUFF |
| if (big_mem.biggestblock == 0) |
| big_mem.init ((void *(*)(inT32)) malloc, free, |
| BIGSIZE, BIGSIZE, MAX_BIGCHUNK); |
| if (mem_mallocdepth > 0) |
| return big_mem.alloc (count, trace_caller (mem_mallocdepth)); |
| else |
| return big_mem.alloc (count, NULL); |
| #else |
| return malloc ((size_t) count); |
| #endif |
| } |
| |
| |
| /********************************************************************** |
| * alloc_big_zeros |
| * |
| * Return a pointer to a buffer of count bytes aligned for any type. |
| **********************************************************************/ |
| |
| DLLSYM void *alloc_big_zeros( //get some memory |
| inT32 count //no of bytes to get |
| ) { |
| #ifdef TESTING_BIGSTUFF |
| if (big_mem.biggestblock == 0) |
| big_mem.init ((void *(*)(inT32)) malloc, free, |
| BIGSIZE, BIGSIZE, MAX_BIGCHUNK); |
| void *buf; //return value |
| |
| if (mem_mallocdepth > 0) |
| buf = big_mem.alloc (count, trace_caller (mem_mallocdepth)); |
| else |
| buf = big_mem.alloc (count, NULL); |
| memset (buf, 0, count); |
| return buf; |
| #else |
| return calloc ((size_t) count, 1); |
| #endif |
| } |
| |
| |
| /********************************************************************** |
| * free_mem |
| * |
| * Free a block allocated by alloc_mem (or alloc_mem_p). |
| * It checks that the pointer is legal and maintains counts of the |
| * amount of free memory above and below the current free pointer. |
| **********************************************************************/ |
| |
| DLLSYM void free_mem( //free mem from alloc_mem |
| void *oldchunk //chunk to free |
| ) { |
| #ifdef RAYS_MALLOC |
| if (mem_freedepth > 0 && main_mem.callers != NULL) |
| main_mem.dealloc (oldchunk, trace_caller (mem_freedepth)); |
| else |
| main_mem.dealloc (oldchunk, NULL); |
| #else |
| free(oldchunk); |
| #endif |
| } |
| |
| |
| /********************************************************************** |
| * free_big_mem |
| * |
| * Free a block allocated by alloc_big_mem. |
| * It checks that the pointer is legal and maintains counts of the |
| * amount of free memory above and below the current free pointer. |
| **********************************************************************/ |
| |
| DLLSYM void free_big_mem( //free mem from alloc_mem |
| void *oldchunk //chunk to free |
| ) { |
| #ifdef TESTING_BIGSTUFF |
| if (mem_freedepth > 0 && main_mem.callers != NULL) |
| big_mem.dealloc (oldchunk, trace_caller (mem_freedepth)); |
| else |
| big_mem.dealloc (oldchunk, NULL); |
| #else |
| free(oldchunk); |
| #endif |
| } |