1
0
mirror of https://github.com/elua/elua.git synced 2025-01-08 20:56:17 +08:00

Added a very simple allocator to eLua. It's probably the most basic version of a chained blocks allocator. It's slow and it won't handle fragmentation nearly as well as dlmalloc(), but it's much smaller and it doesn't need the extra book-keeping space needed by dlmalloc (about 1KB for each memory space).

Recommended only for systems with very low memory (Flash/RAM), and prefferably systems running only precompiled Lua (if you need to compile the code, you might get into stack overflows, and this allocator is much more sensitive to this kind of stuff than dlmalloc()). In fact, this allocator seems to suggest that one should set the stack to at least 4k for the Lua parser to run properly even on small programs. I won't do this just yet, rather I'll keep on trying to move the Lua parser data structures from stack to heap. For now we're OK with the current configuration.
The allocator can handle multiple memory spaces.
Enable with "allocator=simple" on the scons command line.

...oh yes, also added a newline to the end of elua_adc.c :) (to avoid some annoying warnings)
This commit is contained in:
Bogdan Marinescu 2009-02-18 22:13:48 +00:00
parent f253c9003f
commit 59c35cc20d
6 changed files with 335 additions and 17 deletions

View File

@ -112,9 +112,9 @@ if allocator == '':
allocator = 'multiple'
else:
allocator = 'newlib'
elif allocator not in [ 'newlib', 'multiple' ]:
elif allocator not in [ 'newlib', 'multiple', 'simple' ]:
print "Unknown allocator", allocator
print "Allocator can be either 'newlib' or 'multiple'"
print "Allocator can be either 'newlib', 'multiple' or 'simple'"
sys.exit( -1 )
@ -135,6 +135,8 @@ output = 'elua_' + target + '_' + cputype.lower()
cdefs = '-DELUA_CPU=%s -DELUA_BOARD=%s -DELUA_PLATFORM=%s -D__BUFSIZ__=128' % ( cputype, boardname, platform.upper() )
if allocator == 'multiple':
cdefs = cdefs + " -DUSE_MULTIPLE_ALLOCATOR"
elif allocator == 'simple':
cdefs = cdefs + " -DUSE_SIMPLE_ALLOCATOR"
# Lua source files and include path
lua_files = """lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c
@ -155,7 +157,7 @@ cdefs = cdefs + " -DLUA_OPTIMIZE_MEMORY=%d" % ( optram != 0 and 2 or 0 )
local_libs = ''
# Application files
app_files = " src/main.c src/romfs.c src/xmodem.c src/shell.c src/term.c src/common.c src/buf.c src/elua_adc.c src/dlmalloc.c "
app_files = " src/main.c src/romfs.c src/xmodem.c src/shell.c src/term.c src/common.c src/buf.c src/elua_adc.c src/dlmalloc.c src/salloc.c "
# Newlib related files
newlib_files = " src/newlib/devman.c src/newlib/stubs.c src/newlib/genstd.c src/newlib/stdtcp.c"

14
inc/salloc.h Normal file
View File

@ -0,0 +1,14 @@
// A very simple, quite inneficient, yet very small memory allocator
#ifndef __SALLOC_H__
#define __SALLOC_H__
#include <stddef.h>
void* smalloc( size_t size );
void sfree( void* ptr );
void* scalloc( size_t nmemb, size_t size );
void* srealloc( void* ptr, size_t size );
#endif // #ifndef __SALLOC_H__

View File

@ -305,20 +305,35 @@ u32 platform_adc_op( unsigned id, int op, u32 data )
// ****************************************************************************
// Allocator support
#define MIN_ALIGN 8
#define MIN_ALIGN_SHIFT 3
extern char end[];
void* platform_get_first_free_ram( unsigned id )
{
void* mstart[] = MEM_START_ADDRESS;
return id >= sizeof( mstart ) / sizeof( void* ) ? NULL : mstart[ id ];
u32 p;
if( id >= sizeof( mstart ) / sizeof( void* ) )
return NULL;
p = ( u32 )mstart[ id ];
if( p & ( MIN_ALIGN - 1 ) )
p = ( ( p >> MIN_ALIGN_SHIFT ) + 1 ) << MIN_ALIGN_SHIFT;
return ( void* )p;
}
void* platform_get_last_free_ram( unsigned id )
{
void* mend[] = MEM_END_ADDRESS;
return id >= sizeof( mend ) / sizeof( void* ) ? NULL : mend[ id ];
u32 p;
if( id >= sizeof( mend ) / sizeof( void* ) )
return NULL;
p = ( u32 )mend[ id ];
if( p & ( MIN_ALIGN - 1 ) )
p = ( ( p >> MIN_ALIGN_SHIFT ) - 1 ) << MIN_ALIGN_SHIFT;
return ( void* )p;
}

View File

@ -156,4 +156,5 @@ void adc_wait_pending( unsigned id )
}
#endif
#endif

View File

@ -12,6 +12,7 @@
#include "platform_conf.h"
#include "genstd.h"
#include "utils.h"
#include "salloc.h"
#ifdef USE_MULTIPLE_ALLOCATOR
#include "dlmalloc.h"
@ -344,32 +345,35 @@ struct mallinfo mallinfo()
#endif
}
#if defined( USE_MULTIPLE_ALLOCATOR ) || defined( USE_SIMPLE_ALLOCATOR )
// Redirect all allocator calls to our dlmalloc/salloc
#ifdef USE_MULTIPLE_ALLOCATOR
// Redirect all allocator calls to our dlmalloc
#define CNAME( func ) dl##func
#else
#define CNAME( func ) s##func
#endif
void* _malloc_r( struct _reent* r, size_t size )
{
return dlmalloc( size );
return CNAME( malloc )( size );
}
void* _calloc_r( struct _reent* r, size_t nelem, size_t elem_size )
{
return dlcalloc( nelem, elem_size );
return CNAME( calloc )( nelem, elem_size );
}
void _free_r( struct _reent* r, void* ptr )
{
dlfree( ptr );
CNAME( free )( ptr );
}
void* _realloc_r( struct _reent* r, void* ptr, size_t size )
{
return dlrealloc( ptr, size );
return CNAME( realloc )( ptr, size );
}
void* _memalign_r( struct _reent* r, size_t align, size_t nbytes )
{
return dlmemalign( align, nbytes );
}
#endif // #ifdef USE_MULTIPLE_ALLOCATOR
// *****************************************************************************

282
src/salloc.c Normal file
View File

@ -0,0 +1,282 @@
// A very simple, quite inneficient, yet very small memory allocator
// It can do both fixed block and variable block allocation
#ifdef USE_SIMPLE_ALLOCATOR
#include <stddef.h>
#include <string.h>
#include "platform.h"
#include "platform_conf.h"
#include "type.h"
// Macros for the dynamic size allocator
// Dynamic structure: pointer to next, pointer to prev
// First bit of pointer is 0 if block free, 1 if block taken
// Pointer must be multiplied by DYN_MIN_SIZE to get actual address
// There are two 'guards' (at the beginning and at the end)
#define DYN_SIZE_MULT 8
#define DYN_SIZE_MULT_SHIFT 3
#define DYN_HEADER_SIZE 8
#define DYN_MIN_SPLIT_SIZE 16
static u8 s_initialized;
// ****************************************************************************
// Utility functions for the dynamic memory allocator
// Get actual size
static size_t s_act_size( size_t size )
{
if( size & ( DYN_SIZE_MULT - 1 ) )
size = ( ( size >> DYN_SIZE_MULT_SHIFT ) + 1 ) << DYN_SIZE_MULT_SHIFT;
return size;
}
// Get next block
static char* s_get_next_block( char* ptr )
{
return ( char* )( ( *( u32* )ptr & 0x7FFFFFFF ) << DYN_SIZE_MULT_SHIFT );
}
// Set next block
static void s_set_next_block( char* ptr, char* next )
{
u32 *temp = ( u32* )ptr;
*temp = ( *temp & 0x80000000 ) | ( ( u32 )next >> DYN_SIZE_MULT_SHIFT );
}
// Get prev block
static char* s_get_prev_block( char* ptr )
{
return ( char* )( ( *( ( u32* )ptr + 1 ) & 0x7FFFFFFF ) << DYN_SIZE_MULT_SHIFT );
}
// Set prev block
static void s_set_prev_block( char* ptr, char* prev )
{
u32 *temp = ( u32* )ptr + 1;
*temp = ( *temp & 0x80000000 ) | ( ( u32 )prev >> DYN_SIZE_MULT_SHIFT );
}
// Get block size
static size_t s_get_block_size( char* ptr )
{
char* next = s_get_next_block( ptr );
return next != NULL ? ( size_t )( next - ptr ) : 0;
}
// Mark block as taken
static void s_mark_block_taken( char* where )
{
*( u32* )where |= 0x80000000;
}
// Mark block as free
static void s_mark_block_free( char* where )
{
*( u32* )where &= 0x7FFFFFFF;
}
// Is the block free?
static int s_is_block_free( char* where )
{
return ( *( u32* )where & 0x80000000 ) == 0;
}
// Create a new block with the given neighbours
static void s_create_new_block( char* where, char* next, char* prev )
{
u32* temp = ( u32* )where;
*temp ++ = ( u32 )next >> DYN_SIZE_MULT_SHIFT;
*temp = ( u32 )prev >> DYN_SIZE_MULT_SHIFT;
}
// Tries to compact free blocks
static void s_compact_free( char* ptr )
{
char *temp1, *temp2;
s_mark_block_free( ptr );
// Look for free blocks before and after, concatenate if possible
temp1 = temp2 = ptr;
while( s_is_block_free( temp1 ) )
temp1 = s_get_prev_block( temp1 );
temp1 = s_get_next_block( temp1 );
while( s_is_block_free( temp2 ) )
temp2 = s_get_next_block( temp2 );
if( temp1 != ptr || s_get_prev_block( temp2 ) != ptr )
{
s_set_next_block( temp1, temp2 );
s_set_prev_block( temp2, temp1 );
}
}
// Utility function: find a free block in the dynamic memory part
// Returns pointer to block for success, NULL for error
static void* s_get_free_block( size_t size, void* pstart )
{
char *temp, *pblock = NULL, *next;
size_t minsize = ( size_t )~0, bsize;
if( !size )
return NULL;
size = s_act_size( size + DYN_HEADER_SIZE );
temp = s_get_next_block( pstart );
// Best-fit only for now
while( temp )
{
if( s_is_block_free( temp ) )
{
bsize = s_get_block_size( temp );
if( ( size <= bsize ) && ( bsize < minsize ) )
{
minsize = bsize;
pblock = temp;
}
}
temp = s_get_next_block( temp );
}
if( pblock == NULL )
return NULL;
s_mark_block_taken( pblock );
if( minsize > size && ( minsize - size ) >= DYN_MIN_SPLIT_SIZE )
{
temp = pblock + size;
next = s_get_next_block( pblock );
s_set_prev_block( temp, pblock );
s_set_next_block( temp, next );
s_set_prev_block( next, temp );
s_set_next_block( pblock, temp );
s_compact_free( temp );
}
return pblock + DYN_HEADER_SIZE;
}
// Utility function: free a memory block
static void s_free_block( char* ptr )
{
ptr -= DYN_HEADER_SIZE;
s_compact_free( ptr );
}
// Get 'real' block size
static size_t s_get_actual_block_size( char* ptr )
{
return s_get_block_size( ptr - DYN_HEADER_SIZE ) - DYN_HEADER_SIZE;
}
// Shrinks the given block to its new size
static void s_shrink_block( char* pblock, size_t size )
{
char *temp, *next;
pblock -= DYN_HEADER_SIZE;
size = s_act_size( size + DYN_HEADER_SIZE );
if( size >= s_get_block_size( pblock ) || ( s_get_block_size( pblock ) - size ) < DYN_MIN_SPLIT_SIZE )
return;
temp = pblock + size;
next = s_get_next_block( pblock );
s_set_prev_block( temp, pblock );
s_set_next_block( temp, next );
s_set_prev_block( next, temp );
s_set_next_block( pblock, temp );
s_compact_free( temp );
}
static void s_init()
{
unsigned i = 0;
size_t memspace;
char *crt, *g1, *g2, *pstart;
while( ( pstart = platform_get_first_free_ram( i ) ) != NULL )
{
memset( pstart, 0, memspace = ( u32 )platform_get_last_free_ram( i ) - ( u32 )pstart );
g1 = ( char* )pstart;
crt = g1 + DYN_SIZE_MULT;
g2 = g1 + memspace - DYN_SIZE_MULT;
s_create_new_block( g1, crt, NULL );
s_create_new_block( crt, g2, g1 );
s_create_new_block( g2, NULL, crt );
s_mark_block_taken( g1 );
s_mark_block_taken( g2 );
s_mark_block_free( crt );
i ++;
}
s_initialized = 1;
}
// ****************************************************************************
void* smalloc( size_t size )
{
unsigned i = 0;
void *ptr = NULL, *pstart;
if( !s_initialized )
s_init();
while( ( pstart = platform_get_first_free_ram( i ++ ) ) != NULL )
if( ( ptr = s_get_free_block( size, pstart ) ) != NULL )
break;
return ptr;
}
void sfree( void* ptr )
{
if( !ptr || !s_initialized )
return;
s_free_block( ptr );
}
void* scalloc( size_t nmemb, size_t size )
{
void* ptr;
if( !s_initialized )
s_init();
if( ( ptr = smalloc( nmemb * size ) ) != NULL )
memset( ptr, 0, nmemb * size );
return ptr;
}
void* srealloc( void* ptr, size_t size )
{
void* newptr = NULL;
if( !s_initialized )
s_init();
// Special cases:
// realloc with ptr == NULL -> malloc
// realloc with size == 0 -> free
if( ptr == NULL )
return smalloc( size );
else if( size == 0 )
{
sfree( ptr );
return NULL;
}
// Test new size versus the old size
if( s_get_actual_block_size( ptr ) == size )
return ptr;
else if( size < s_get_actual_block_size( ptr ) )
{
s_shrink_block( ptr, size );
return ptr;
}
else
{
if( ( newptr = smalloc( size ) ) == NULL )
return NULL;
memmove( newptr, ptr, s_get_actual_block_size( ptr ) );
sfree( ptr );
}
return newptr;
}
#endif // #ifdef USE_SIMPLE_ALLOCATOR