blob: dfd1f905dc01f7a8c01f75baa752051815c1b1c7 [file] [log] [blame]
/**********************************************************************
* 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
}