www.pudn.com > gandalf.1.zip > memory_stack.c
/** * File: $RCSfile: memory_stack.c,v $ * Module: Stack-style first-in first-out memory allocation module * Part of: Gandalf Library * * Revision: $Revision: 1.7 $ * Last edited: $Date: 2005/10/18 22:01:50 $ * Author: $Author: pm $ * Copyright: (c) 2002 Imagineer Software Limited */ /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Module for temporary stack storage. Memory must be freed in reverse of the * order it was allocated. Much faster than malloc(). */ #include#include #include #include #include #include #include #include /** * \addtogroup Common * \{ */ /** * \addtogroup CommonAllocate * \{ */ /* set TM_TEST_MAGIC to 0 to eliminate tests for overwriting a block */ #define TM_TEST_MAGIC 1 #if TM_TEST_MAGIC #define TM_MAGIC_NUMBER 0x2f3ee7b1 /* value checked for block overwrite */ #endif /** * \brief Initialise temporary memory allocation structure. * \param ms Memoru stack structure pointer * \param nblocks Maximum number of blocks of memory to allow * \param bsize Size of each block in bytes * \return Pointer to initialised structure, or \c NULL on failure. */ Gan_MemoryStack * gan_memory_stack_form ( Gan_MemoryStack *ms, int nblocks, size_t bsize ) { if ( ms == NULL ) { ms = gan_malloc_object(Gan_MemoryStack); if ( ms == NULL ) { gan_err_flush_trace(); gan_err_register_with_number ( "gan_memory_stack_form", GAN_ERROR_MALLOC_FAILED, "", sizeof(*ms) ); return NULL; } ms->alloc = GAN_TRUE; } else ms->alloc = GAN_FALSE; if ( nblocks <= 0 ) { gan_err_flush_trace(); gan_err_register ( "gan_memory_stack_form", GAN_ERROR_ILLEGAL_ARGUMENT, "" ); return NULL; } ms->block_ptr = (Gan_BigType **) malloc ( nblocks*sizeof(Gan_BigType *) ); ms->block_end = (int *) malloc ( nblocks*sizeof(int) ); if ( ms->block_ptr == NULL || ms->block_end == NULL ) { gan_err_flush_trace(); gan_err_register_with_number ( "gan_memory_stack_form", GAN_ERROR_MALLOC_FAILED, "", nblocks*sizeof(Gan_BigType*) ); return NULL; } /* compute block size in terms of Gan_BigType's */ ms->tm_bsize = 1 + (bsize-1)/sizeof(Gan_BigType); ms->max_tm_blocks = nblocks; /* see if you we can get magic number and size into a single Gan_BigType */ if ( sizeof(size_t) == sizeof(gan_uint32) && sizeof(size_t) + sizeof(gan_uint32) == sizeof(Gan_BigType) ) ms->squeeze_OK = GAN_TRUE; else ms->squeeze_OK = GAN_FALSE; /* can't manage it */ /* initialise other fields */ ms->last_free = NULL; ms->tm_total = 0; ms->current_tm_block = 0; ms->next_start = 0; ms->alloc_tm_blocks = 0; /* success */ return ms; } /** * \brief Temporary memory allocation routine, faster than malloc(). * \param ms Pointer to memory stack structure * \param size Amount of memory to allocate in bytes * \return Non \c NULL successfully allocated block, \c NULL on failure * * For allocating chunks of memory which can be freed in reverse order, * The module allocates large blocks at a time using \a malloc(), the blocks * being "a lot" bigger than the chunks to be \a gan_ms_malloc()'d, to avoid * wasting memory. Calls to gan_ms_malloc() then fill up the current block, and * when the end is reached another block is \a malloc()'d. There's a dirty bit * concerning memory alignment where I assume that everything should be * aligned according to a "big" type (double). \a malloc() guarantees that * it returns pointers that may be safely cast to any pointer type, but I * (PFM) don't know how to do that properly in C. */ void * gan_ms_malloc ( Gan_MemoryStack *ms, size_t size ) { Gan_BigType *tm_ptr = NULL; /* let's decide to return a valid if you want a pointer to zero bytes, and leave NULL for errors */ if ( size == 0 ) return ( (void *)(ms->block_ptr[ms->current_tm_block] + ms->next_start) ); /* make size into number of sizeof(Gan_BigType)'s rather than bytes */ size = 1 + (size-1)/sizeof(Gan_BigType); /* increment size for magic number and stored size */ if ( ms->squeeze_OK ) size++; else size += 2; /* check whether there is no block currently allocated, i.e. this is the first call to gan_ms_malloc() since the call to gan_memory_stack_form() or gan_memory_stack_free() */ if ( ms->alloc_tm_blocks == 0 ) { if ( size > ms->tm_bsize ) { gan_err_flush_trace(); gan_err_register ( "gan_ms_malloc", GAN_ERROR_FAILURE, "requested size too big" ); return NULL; } ms->block_ptr[0] = gan_malloc_array ( Gan_BigType, ms->tm_bsize ); tm_ptr = ms->block_ptr[0]; ms->block_end[0] = 0; ms->current_tm_block = 0; ms->alloc_tm_blocks = 1; ms->next_start = size; } else if ( ms->next_start + size > ms->tm_bsize ) /* we have reached the end of the current temporary memory block */ { if ( size > ms->tm_bsize ) { gan_err_flush_trace(); gan_err_register ( "gan_ms_malloc", GAN_ERROR_FAILURE, "requested size too big" ); return NULL; } ms->block_end[ms->current_tm_block++] = ms->next_start; /* see whether we need to allocate a new block */ if ( ms->current_tm_block == ms->alloc_tm_blocks ) { if ( ms->alloc_tm_blocks >= ms->max_tm_blocks ) { gan_err_flush_trace(); gan_err_register ( "gan_ms_malloc", GAN_ERROR_FAILURE, "too many blocks allocated" ); } ms->block_ptr[ms->current_tm_block] = gan_malloc_array ( Gan_BigType, ms->tm_bsize ); if ( ms->block_ptr[ms->current_tm_block] == NULL ) { gan_err_flush_trace(); gan_err_register_with_number ( "gan_ms_malloc", GAN_ERROR_MALLOC_FAILED, "", ms->tm_bsize*sizeof(Gan_BigType) ); return NULL; } ms->alloc_tm_blocks++; } ms->next_start = size; tm_ptr = ms->block_ptr[ms->current_tm_block]; } else { ms->next_start += size; tm_ptr = ms->block_ptr[ms->current_tm_block] + ms->next_start - size; } /* store magic number to test for illegal overwriting test */ *((gan_uint32 *) tm_ptr) = TM_MAGIC_NUMBER; /* store size */ if ( ms->squeeze_OK ) { *(((size_t *) tm_ptr)+1) = size; tm_ptr++; } else { *((size_t *) (tm_ptr+1)) = size; tm_ptr += 2; } /* keep a running total */ ms->tm_total += size; /* there is no meaning for last freed pointer */ ms->last_free = NULL; #if 0 printf ( "talloc of %d words at %x (total=%d)\n", size, (gan_uint32) tm_ptr, ms->tm_total ); #endif return ( (void *) tm_ptr ); } /** * \brief Temporary memory free routine. * \param ms Pointer to memory stack structure * \param ptr Pointer to memory area to free, as returned by gan_ms_malloc() * * Temporary memory free routine, Memory must be freed in reverse of the * order it was allocated using gan_ms_malloc(). gan_ms_free() does not * actually free any memory, but allows the marked memory to be used in * subsequent gan_ms_malloc() calls. After using the temporary memory, call * gan_memory_stack_free() to actually free the memory blocks. */ void gan_ms_free ( Gan_MemoryStack *ms, void *ptr ) { Gan_BigType *tm_ptr = (Gan_BigType *) ptr; size_t size; if ( tm_ptr == NULL ) return; if ( ms->squeeze_OK ) { tm_ptr--; size = *(((size_t *) tm_ptr)+1); } else { tm_ptr -= 2; size = *((size_t *) (tm_ptr+1)); } /* check for illegal overwriting */ gan_assert ( *((gan_uint32 *) tm_ptr) == TM_MAGIC_NUMBER, "talloc magic number overwritten" ); /* subtract stored size from running total */ ms->tm_total -= size; #if 0 printf ( "ms_free of %d words at %x (total=%d)\n", size, (gan_uint32) (tm_ptr+(ms->squeeze_OK ? 1 : 2)), ms->tm_total ); #endif /* check whether calls to gan_ms_free() have wound back to the start of a block */ if ( tm_ptr == ms->block_ptr[ms->current_tm_block] ) if ( ms->current_tm_block == 0 ) { ms->next_start = 0; ms->last_free = tm_ptr; } else { ms->next_start = ms->block_end[--ms->current_tm_block]; ms->last_free = NULL; } else { gan_assert ( ms->last_free == NULL || tm_ptr <= ms->last_free, "temporary memory freed in wrong order" ); ms->next_start = tm_ptr - ms->block_ptr[ms->current_tm_block]; ms->last_free = tm_ptr; } } /** * \brief Frees a list of temporaray blocks terminated by \c NULL. * \param ms Pointer to memory stack structure * \param ptr The first memory block to free * \param ... List of other blocks to free, terminated by \c NULL * * Frees a list of temporaray blocks allocated by gan_ms_malloc(), teminated * by \c NULL. gan_ms_free() is called for each block, preserving the order * of the arguments in the calls. */ void gan_ms_free_va ( Gan_MemoryStack *ms, void *ptr, ... ) { va_list ap; if ( ptr == NULL ) return; gan_ms_free ( ms, ptr ); va_start ( ap, ptr ); for(;;) { ptr = va_arg ( ap, void * ); if ( ptr == NULL ) break; gan_ms_free ( ms, ptr ); } va_end ( ap ); } /** * \brief Frees all temporary memory. * \param ms Pointer to memory stack structure * * Frees all memory allocated using calls to gan_ms_malloc(). */ void gan_memory_stack_free ( Gan_MemoryStack *ms ) { int blk; for ( blk = 0; blk < ms->alloc_tm_blocks; blk++ ) free ( ms->block_ptr[blk] ); free ( ms->block_end ); free ( ms->block_ptr ); if ( ms->alloc ) free(ms); } /** * \brief Frees unused temporary memory. * \param ms Pointer to memory stack structure * * Frees memory stack down to the last gan_ms_free() call. */ void gan_memory_stack_clean ( Gan_MemoryStack *ms ) { int blk; for ( blk = ms->current_tm_block + 1; blk < ms->alloc_tm_blocks; blk++ ) free ( ms->block_ptr[blk] ); ms->alloc_tm_blocks = ms->current_tm_block+1; } /** * \brief Returns the total temporary memory currently allocated. * \param ms Pointer to memory stack structure * * Returns the total temporary memory currently allocated by calls * to gan_ms_malloc(). */ size_t gan_memory_stack_total ( Gan_MemoryStack *ms ) { return ms->tm_total; } /** * \} */ /** * \} */