mirror of https://github.com/ipxe/ipxe.git
Implemented realloc(), and changed the semantics of malloc(0) to allow
for realloc(0) being a valid way to free memory.pull/1/head
parent
6a18721ce8
commit
c4677c7e73
|
@ -49,6 +49,23 @@ struct autosized_block {
|
||||||
char data[0];
|
char data[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address for zero-length memory blocks
|
||||||
|
*
|
||||||
|
* @c malloc(0) or @c realloc(ptr,0) will return the special value @c
|
||||||
|
* NOWHERE. Calling @c free(NOWHERE) will have no effect.
|
||||||
|
*
|
||||||
|
* This is consistent with the ANSI C standards, which state that
|
||||||
|
* "either NULL or a pointer suitable to be passed to free()" must be
|
||||||
|
* returned in these cases. Using a special non-NULL value means that
|
||||||
|
* the caller can take a NULL return value to indicate failure,
|
||||||
|
* without first having to check for a requested size of zero.
|
||||||
|
*
|
||||||
|
* Code outside of malloc.c do not ever need to refer to the actual
|
||||||
|
* value of @c NOWHERE; this is an internal definition.
|
||||||
|
*/
|
||||||
|
#define NOWHERE ( ( void * ) ~( ( intptr_t ) 0 ) )
|
||||||
|
|
||||||
/** List of free memory blocks */
|
/** List of free memory blocks */
|
||||||
static LIST_HEAD ( free_blocks );
|
static LIST_HEAD ( free_blocks );
|
||||||
|
|
||||||
|
@ -193,6 +210,66 @@ void free_memblock ( void *ptr, size_t size ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reallocate memory
|
||||||
|
*
|
||||||
|
* @v old_ptr Memory previously allocated by malloc(), or NULL
|
||||||
|
* @v new_size Requested size
|
||||||
|
* @ret new_ptr Allocated memory, or NULL
|
||||||
|
*
|
||||||
|
* Allocates memory with no particular alignment requirement. @c
|
||||||
|
* new_ptr will be aligned to at least a multiple of sizeof(void*).
|
||||||
|
* If @c old_ptr is non-NULL, then the contents of the newly allocated
|
||||||
|
* memory will be the same as the contents of the previously allocated
|
||||||
|
* memory, up to the minimum of the old and new sizes. The old memory
|
||||||
|
* will be freed.
|
||||||
|
*
|
||||||
|
* If allocation fails the previously allocated block is left
|
||||||
|
* untouched and NULL is returned.
|
||||||
|
*
|
||||||
|
* Calling realloc() with a new size of zero is a valid way to free a
|
||||||
|
* memory block.
|
||||||
|
*/
|
||||||
|
void * realloc ( void *old_ptr, size_t new_size ) {
|
||||||
|
struct autosized_block *old_block;
|
||||||
|
struct autosized_block *new_block;
|
||||||
|
size_t old_total_size;
|
||||||
|
size_t new_total_size;
|
||||||
|
size_t old_size;
|
||||||
|
void *new_ptr = NOWHERE;
|
||||||
|
|
||||||
|
/* Allocate new memory if necessary. If allocation fails,
|
||||||
|
* return without touching the old block.
|
||||||
|
*/
|
||||||
|
if ( new_size ) {
|
||||||
|
new_total_size = ( new_size +
|
||||||
|
offsetof ( struct autosized_block, data ) );
|
||||||
|
new_block = alloc_memblock ( new_total_size, 1 );
|
||||||
|
if ( ! new_block )
|
||||||
|
return NULL;
|
||||||
|
new_block->size = new_total_size;
|
||||||
|
new_ptr = &new_block->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy across relevant part of the old data region (if any),
|
||||||
|
* then free it. Note that at this point either (a) new_ptr
|
||||||
|
* is valid, or (b) new_size is 0; either way, the memcpy() is
|
||||||
|
* valid.
|
||||||
|
*/
|
||||||
|
if ( old_ptr && ( old_ptr != NOWHERE ) ) {
|
||||||
|
old_block = container_of ( old_ptr, struct autosized_block,
|
||||||
|
data );
|
||||||
|
old_total_size = old_block->size;
|
||||||
|
old_size = ( old_total_size -
|
||||||
|
offsetof ( struct autosized_block, data ) );
|
||||||
|
memcpy ( new_ptr, old_ptr,
|
||||||
|
( ( old_size < new_size ) ? old_size : new_size ) );
|
||||||
|
free_memblock ( old_block, old_total_size );
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate memory
|
* Allocate memory
|
||||||
*
|
*
|
||||||
|
@ -203,15 +280,7 @@ void free_memblock ( void *ptr, size_t size ) {
|
||||||
* will be aligned to at least a multiple of sizeof(void*).
|
* will be aligned to at least a multiple of sizeof(void*).
|
||||||
*/
|
*/
|
||||||
void * malloc ( size_t size ) {
|
void * malloc ( size_t size ) {
|
||||||
size_t total_size;
|
return realloc ( NULL, size );
|
||||||
struct autosized_block *block;
|
|
||||||
|
|
||||||
total_size = size + offsetof ( struct autosized_block, data );
|
|
||||||
block = alloc_memblock ( total_size, 1 );
|
|
||||||
if ( ! block )
|
|
||||||
return NULL;
|
|
||||||
block->size = total_size;
|
|
||||||
return &block->data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -225,12 +294,7 @@ void * malloc ( size_t size ) {
|
||||||
* If @c ptr is NULL, no action is taken.
|
* If @c ptr is NULL, no action is taken.
|
||||||
*/
|
*/
|
||||||
void free ( void *ptr ) {
|
void free ( void *ptr ) {
|
||||||
struct autosized_block *block;
|
realloc ( ptr, 0 );
|
||||||
|
|
||||||
if ( ptr ) {
|
|
||||||
block = container_of ( ptr, struct autosized_block, data );
|
|
||||||
free_memblock ( block, block->size );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
extern void * alloc_memblock ( size_t size, size_t align );
|
extern void * alloc_memblock ( size_t size, size_t align );
|
||||||
extern void free_memblock ( void *ptr, size_t size );
|
extern void free_memblock ( void *ptr, size_t size );
|
||||||
|
extern void * realloc ( void *old_ptr, size_t new_size );
|
||||||
extern void * malloc ( size_t size );
|
extern void * malloc ( size_t size );
|
||||||
extern void free ( void *ptr );
|
extern void free ( void *ptr );
|
||||||
extern void mpopulate ( void *start, size_t len );
|
extern void mpopulate ( void *start, size_t len );
|
||||||
|
|
Loading…
Reference in New Issue