/* memmgr.c * This is the memory manager for Tap. The visible functions are * xmalloc - allocate a block of memory * xrealloc - change the size of a block of memory * xfree - free a block of memory * free_alloc_list - called at exit to free all memory */ /* Copyright (C) 1999 Jonathan duSaint * * This file is part of Tap, an interactive program for reading and * writing data to the serial port to facilitate the reverse * engineering of communication protocols. * * Tap is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * Tap is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * The GNU General Public License is generally kept in a file called * COPYING or LICENSE. If you do not have a copy of the license, write * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "tap.h" /* minimum size block to allocate */ #define MIN_ALLOC 8 /* the alloc / free list */ struct memory_block *alloc_list = NULL; /* the total available slots (the size of the alloc list) */ long number_of_blocks = 0; /* the number of blocks that have been filled */ long block_pointers_allocated = 0; /* This is called if malloc or realloc return NULL * * Give the user a choice to continue (and possibly * close other memory-intensive applications) or to * just exit. If this gets called more than * MAX_TIMES_OUT_OF_MEMORY then just abort. */ void out_of_memory (void) { char key; static int times_out_of_memory = 0; if (times_out_of_memory > MAX_TIMES_OUT_OF_MEMORY) { fprintf (stderr, "%s: virtual memory exhausted\n", pname); abort (); } waddstr (input_window, "!! out of virtual memory !!\n\ press to attempt to continue (and possibly close other applications)\n\ or any other key to abort: "); /* don't use sgetch () here */ key = wgetch (input_window); if (tolower (key) == 'c') { times_out_of_memory++; longjmp (start_of_input_loop, 1); } abort (); } /* This is passed a pointer to memory (the same as passed to xrealloc * or xfree). If it isn't found on the alloc list (using the mark) * then abort is called. */ long get_block_index (void *mem) { long mark; /* in other words, mark = (long) mem[-MARK_SIZE] */ mark = ((long)* ((long *) ((mem) - MARK_SIZE))); /* just check that it's somewhere on the list */ if (mark < block_pointers_allocated) return (mark); /* this should never be reached */ fprintf (stderr, "\n\r%s: tried to free or realloc block not on alloc list\n\r\ block index %ld\n\r", pname, mark); abort (); } /* Search for an unused block of memory with * size >= min_size. If nothing is found return -1 otherwise * return the index of a suitable block. */ long find_free_block (size_t min_size) { long index; /* scan through the whole list */ for (index = 0; index < block_pointers_allocated; index++) { if (alloc_list[index].in_use == FREE) { if (alloc_list[index].size >= min_size) return (index); /* got one */ } } /* nothing */ return (-1); } /* This function acts just like malloc except for using the * alloc list to keep track of used and unused memory and * checking for errors. */ void * xmalloc (size_t how_much) { long block_index; /* try to find a suitable block */ block_index = find_free_block (how_much); if (block_index == -1) { /* nothing found */ /* if the alloc list needs to be expanded */ if (block_pointers_allocated == number_of_blocks) { struct memory_block *temp; temp = realloc (alloc_list, sizeof (struct memory_block) * (number_of_blocks + GROW_SIZE)); if (temp == NULL) out_of_memory (); alloc_list = temp; number_of_blocks += GROW_SIZE; } /* now allocate a new block */ block_index = block_pointers_allocated; block_pointers_allocated++; alloc_list[block_index].memory = malloc ((how_much < MIN_ALLOC ? MIN_ALLOC : how_much) + MARK_SIZE); if (alloc_list[block_index].memory == NULL) out_of_memory (); alloc_list[block_index].size = (how_much < MIN_ALLOC) ? MIN_ALLOC : how_much; /* mark the block with the index */ * ((long *)alloc_list[block_index].memory) = block_index; } alloc_list[block_index].in_use = USED; alloc_list[block_index].size_used = how_much; return (alloc_list[block_index].memory + MARK_SIZE); } /* This function is just like realloc except that it uses the * alloc list and checks the validity of realloc'ed memory. */ void * xrealloc (void *mem, size_t how_much) { long index, orig_index; void *new; orig_index = get_block_index (mem); /* first check if the block is already big enough */ if (alloc_list[orig_index].size >= how_much) { alloc_list[orig_index].size_used = how_much; return (mem); } /* not big enough - try to find another one */ /* scan the alloc list */ for (index = 0; index < block_pointers_allocated; index++) { if (alloc_list[index].in_use == FREE) { if (alloc_list[index].size >= how_much) { /* found suitable block */ memcpy (alloc_list[index].memory + MARK_SIZE, alloc_list[orig_index].memory + MARK_SIZE, alloc_list[orig_index].size_used); alloc_list[index].in_use = USED; alloc_list[orig_index].in_use = FREE; return (alloc_list[index].memory + MARK_SIZE); } } } /* have to actually realloc */ new = realloc (alloc_list[orig_index].memory, how_much + MARK_SIZE); if (new == NULL) out_of_memory (); alloc_list[orig_index].memory = new; alloc_list[orig_index].size = how_much; alloc_list[orig_index].size_used = how_much; return (alloc_list[orig_index].memory + MARK_SIZE); } /* This function acts just like free except that it uses the alloc list. */ void xfree (void *mem) { long index; index = get_block_index (mem); alloc_list[index].in_use = FREE; } /* This is called at exit and it frees every block of memory allocated. */ void free_alloc_list (void) { long index; for (index = 0; index < block_pointers_allocated; index++) free (alloc_list[index].memory); free (alloc_list); }