diff --git a/apps/debug_menu.c b/apps/debug_menu.c index b557f46..8621427 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -79,6 +79,7 @@ #include "peakmeter.h" #endif #include "logfdisp.h" +#include "core_alloc.h" #if CONFIG_CODEC == SWCODEC #include "pcmbuf.h" #include "buffering.h" @@ -416,6 +417,40 @@ static bool dbg_buffering_thread(void) #endif /* CONFIG_CODEC */ #endif /* HAVE_LCD_BITMAP */ +static char bf_buf[1024]; +static int bf_buf_used; +static int bf_items; + +static void print(const char* buf) +{ + char* dst = &bf_buf[bf_buf_used]; + snprintf(dst, sizeof(bf_buf) - bf_buf_used, "%s", buf); + bf_buf_used += strlen(dst) + 1; + bf_items++; +} + +static const char* bf_getname(int selected_item, void *data, + char *buffer, size_t buffer_len) +{ + int i = 0; + (void)data;(void)buffer;(void)buffer_len; + char* src = bf_buf; + while(i++ < selected_item) + src = strchr(src, 0) + 1; + + return src; +} + +static bool dbg_buflib_allocs(void) +{ + struct simplelist_info info; + bf_items = 0; + bf_buf_used = 0; + core_print_blocks(print); + simplelist_info_init(&info, "mem allocs", bf_items, NULL); + info.get_name = bf_getname; + return simplelist_show_list(&info); +} #if (CONFIG_PLATFORM & PLATFORM_NATIVE) static const char* dbg_partitions_getname(int selected_item, void *data, char *buffer, size_t buffer_len) @@ -2036,6 +2071,7 @@ static const struct the_menu_item menuitems[] = { #elif !defined(SIMULATOR) { "View audio thread", dbg_audio_thread }, #endif + { "View buflib allocs", dbg_buflib_allocs }, #ifdef PM_DEBUG { "pm histogram", peak_meter_histogram}, #endif /* PM_DEBUG */ diff --git a/apps/dsp.c b/apps/dsp.c index 3cff191..fd2d4e2 100644 --- a/apps/dsp.c +++ b/apps/dsp.c @@ -30,7 +30,7 @@ #include "settings.h" #include "replaygain.h" #include "tdspeed.h" -#include "buffer.h" +#include "core_alloc.h" #include "fixedpoint.h" #include "fracmul.h" @@ -318,6 +318,20 @@ static void tdspeed_setup(struct dsp_config *dspc) resample_buf = big_resample_buf; } + +static int move_callback(int handle, void* current, void* new) +{ + /* TODO */ + (void)handle;(void)current;(void)new; + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; + + void dsp_timestretch_enable(bool enabled) { /* Hook to set up timestretch buffer on first call to settings_apply() */ @@ -325,10 +339,16 @@ void dsp_timestretch_enable(bool enabled) { if (enabled) { + int handle; /* Set up timestretch buffers */ big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO; big_sample_buf = small_resample_buf; - big_resample_buf = (int32_t *) buffer_alloc(big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t)); + handle = core_alloc_ex("resample buf", + big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t), &ops); + if (handle > 0) + big_resample_buf = core_get_data(handle); + else + big_sample_buf_count = 0; } else { @@ -1204,8 +1224,8 @@ int dsp_callback(int msg, intptr_t param) * channel. Returns number of bytes written to dst. */ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count) -{ - int32_t *tmp[2]; +{ + static int32_t *tmp[2]; /* tdspeed_doit() needs it static */ static long last_yield; long tick; int written = 0; diff --git a/apps/filetypes.c b/apps/filetypes.c index 779337e..9487454 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -36,7 +36,7 @@ #include "dir.h" #include "file.h" #include "splash.h" -#include "buffer.h" +#include "core_alloc.h" #include "icons.h" #include "logf.h" @@ -161,8 +161,8 @@ void tree_get_filetypes(const struct filetype** types, int* count) struct file_type { enum themable_icons icon; /* the icon which shall be used for it, NOICON if unknown */ unsigned char attr; /* FILE_ATTR_MASK >> 8 */ - char* plugin; /* Which plugin to use, NULL if unknown, or builtin */ - char* extension; /* NULL for none */ + unsigned char* plugin; /* Which plugin to use, NULL if unknown, or builtin */ + unsigned char* extension; /* NULL for none */ }; static struct file_type filetypes[MAX_FILETYPES]; static int custom_filetype_icons[MAX_FILETYPES]; @@ -176,12 +176,34 @@ static int filetype_count = 0; static unsigned char highest_attr = 0; static int viewer_count = 0; +static int strdup_handle, strdup_bufsize, strdup_cur_idx; +static int move_callback(int handle, void* current, void* new) +{ + /*could compare to strdup_handle, but ops is only used once */ + (void)handle; + size_t diff = new - current; +#define FIX_PTR(x) \ + { if ((void*)x > current && (void*)x < (current+strdup_bufsize)) x+= diff; } + for(int i = 0; i < filetype_count; i++) + { + FIX_PTR(filetypes[i].extension); + FIX_PTR(filetypes[i].plugin); + } + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; + static char *filetypes_strdup(char* string) { - char *buffer = (char*)buffer_alloc(strlen(string)+1); - strcpy(buffer, string); + char *buffer = core_get_data(strdup_handle) + strdup_cur_idx; + strdup_cur_idx += strlcpy(buffer, string, strdup_bufsize-strdup_cur_idx)+1; return buffer; } + static char *filetypes_store_plugin(char *plugin, int n) { int i; @@ -212,7 +234,7 @@ static int find_extension(const char* extension) } static void read_builtin_types(void); -static void read_config(const char* config_file); +static void read_config(int fd); #ifdef HAVE_LCD_COLOR /* Colors file format is similar to icons: * ext:hex_color @@ -305,16 +327,28 @@ void filetype_init(void) filetypes[0].attr = 0; filetypes[0].icon = Icon_Folder; + /* estimate bufsize with the filesize, will not be larger */ viewer_count = 0; filetype_count = 1; + + int fd = open(VIEWERS_CONFIG, O_RDONLY); + if (fd < 0) + return; + + strdup_bufsize = filesize(fd); + strdup_handle = core_alloc_ex("filetypes", strdup_bufsize, &ops); + if (strdup_handle <= 0) + return; read_builtin_types(); - read_config(VIEWERS_CONFIG); + read_config(fd); #ifdef HAVE_LCD_BITMAP read_viewer_theme_file(); #endif #ifdef HAVE_LCD_COLOR read_color_theme_file(); #endif + close(fd); + core_shrink(strdup_handle, core_get_data(strdup_handle), strdup_cur_idx); } /* remove all white spaces from string */ @@ -348,13 +382,10 @@ static void read_builtin_types(void) } } -static void read_config(const char* config_file) +static void read_config(int fd) { char line[64], *s, *e; char *extension, *plugin; - int fd = open(config_file, O_RDONLY); - if (fd < 0) - return; /* config file is in the format ,, ignore line if either of the first two are missing */ diff --git a/apps/gui/skin_engine/skin_engine.c b/apps/gui/skin_engine/skin_engine.c index fbedbb9..d136f90 100644 --- a/apps/gui/skin_engine/skin_engine.c +++ b/apps/gui/skin_engine/skin_engine.c @@ -48,10 +48,9 @@ void theme_init_buffer(void) skins_initialising = false; } #else -static char *skin_buffer = NULL; +static char skin_buffer[SKIN_BUFFER_SIZE]; void theme_init_buffer(void) { - skin_buffer = buffer_alloc(SKIN_BUFFER_SIZE); skins_initialising = false; } #endif diff --git a/apps/main.c b/apps/main.c index 9cb7245..b62d87a 100644 --- a/apps/main.c +++ b/apps/main.c @@ -53,7 +53,7 @@ #include "language.h" #include "wps.h" #include "playlist.h" -#include "buffer.h" +#include "core_alloc.h" #include "rolo.h" #include "screens.h" #include "usb_screen.h" @@ -337,7 +337,7 @@ static void init_tagcache(void) static void init(void) { system_init(); - buffer_init(); + core_allocator_init(); kernel_init(); #ifdef APPLICATION paths_init(); @@ -428,7 +428,7 @@ static void init(void) #endif system_init(); - buffer_init(); + core_allocator_init(); kernel_init(); #ifdef HAVE_ADJUSTABLE_CPU_FREQ diff --git a/apps/playback.c b/apps/playback.c index 82bf333..7a35756 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -24,7 +24,7 @@ #include "system.h" #include "kernel.h" #include "panic.h" -#include "buffer.h" +#include "core_alloc.h" #include "sound.h" #include "ata.h" #include "usb.h" @@ -733,6 +733,7 @@ static void scratch_mem_init(void *mem) } } +static int audiobuf_handle; /* Set up the audio buffer for playback */ static void audio_reset_buffer(void) { @@ -744,16 +745,19 @@ static void audio_reset_buffer(void) /* see audio_get_recording_buffer if this is modified */ logf("%s()", __func__); - /* release the buffer on behalf of any caller of audio_get_buffer() */ - buffer_release_buffer(0); - /* If the setup of anything allocated before the file buffer is changed, do check the adjustments after the buffer_alloc call as it will likely be affected and need sliding over */ /* Initially set up file buffer as all space available */ size_t filebuflen, allocsize; - unsigned char *filebuf = buffer_get_buffer(&filebuflen); + if (audiobuf_handle > 0) + { + core_free(audiobuf_handle); + audiobuf_handle = 0; + } + audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, NULL); + unsigned char *filebuf = core_get_data(audiobuf_handle); /* Subtract whatever voice needs */ allocsize = talkbuf_init(filebuf); @@ -3344,7 +3348,11 @@ void audio_hard_stop(void) #ifdef PLAYBACK_VOICE voice_stop(); #endif - buffer_release_buffer(0); + if (audiobuf_handle > 0) + { + core_free(audiobuf_handle); + audiobuf_handle = 0; + } } /* Resume playback if paused */ @@ -3467,6 +3475,12 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) return NULL; } + if (audiobuf_handle > 0) + { + core_free(audiobuf_handle); + audiobuf_handle = 0; + } + if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED || !talk_voice_required()) { @@ -3484,7 +3498,8 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) talk_buffer_steal(); buffer_state = AUDIOBUF_STATE_TRASHED; } - buf = buffer_get_buffer(buffer_size); + audiobuf_handle = core_alloc_maximum("audiobuf #1", buffer_size, NULL); + buf = core_get_data(audiobuf_handle); } else { @@ -3496,7 +3511,8 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) size_t siz, talkbuf_size; logf("get buffer: audio"); /* call buffer_get_buffer() to make use of the locking mechanism */ - buf = buffer_get_buffer(&siz); + audiobuf_handle = core_alloc_maximum("audiobuf #2", &siz, NULL); + buf = core_get_data(audiobuf_handle); buf += talkbuf_size = talkbuf_init(buf); siz -= talkbuf_size + pcmbuf_init(buf + siz); siz -= voicebuf_init(buf + siz); @@ -3513,10 +3529,7 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) unsigned char * audio_get_recording_buffer(size_t *buffer_size) { audio_hard_stop(); - talk_buffer_steal(); - - buffer_state = AUDIOBUF_STATE_TRASHED; - return buffer_get_buffer(buffer_size); + return audio_get_buffer(true, buffer_size); } #endif /* HAVE_RECORDING */ diff --git a/apps/playlist.c b/apps/playlist.c index b5386cd..934b472 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -84,7 +84,7 @@ #include "status.h" #include "applimits.h" #include "screens.h" -#include "buffer.h" +#include "core_alloc.h" #include "misc.h" #include "filefuncs.h" #include "button.h" @@ -1920,11 +1920,30 @@ static int rotate_index(const struct playlist_info* playlist, int index) return index; } +static int move_callback(int handle, void* current, void* new) +{ + (void)handle; + struct playlist_info* playlist = ¤t_playlist; + if (current == playlist->indices) + playlist->indices = new; + else if (current == playlist->buffer) + playlist->buffer = new; + else /* current == playlist->filenames */ + playlist->filenames = new; + + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; /* * Initialize playlist entries at startup */ void playlist_init(void) { + int handle; struct playlist_info* playlist = ¤t_playlist; mutex_init(¤t_playlist_mutex); @@ -1936,18 +1955,22 @@ void playlist_init(void) playlist->fd = -1; playlist->control_fd = -1; playlist->max_playlist_size = global_settings.max_files_in_playlist; - playlist->indices = buffer_alloc( - playlist->max_playlist_size * sizeof(int)); + handle = core_alloc_ex("playlist idx", + playlist->max_playlist_size * sizeof(int), &ops); + playlist->indices = core_get_data(handle); playlist->buffer_size = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; - playlist->buffer = buffer_alloc(playlist->buffer_size); + handle = core_alloc_ex("playlist buf", + playlist->buffer_size, &ops); + playlist->buffer = core_get_data(handle); playlist->control_mutex = ¤t_playlist_mutex; empty_playlist(playlist, true); #ifdef HAVE_DIRCACHE - playlist->filenames = buffer_alloc( - playlist->max_playlist_size * sizeof(int)); + handle = core_alloc_ex("playlist dc", + playlist->max_playlist_size * sizeof(int), &ops); + playlist->filenames = core_get_data(handle); memset(playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int)); create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack), @@ -3345,7 +3368,6 @@ int playlist_save(struct playlist_info* playlist, char *filename) char tmp_buf[MAX_PATH+1]; int result = 0; bool overwrite_current = false; - int* index_buf = NULL; if (!playlist) playlist = ¤t_playlist; @@ -3369,10 +3391,6 @@ int playlist_save(struct playlist_info* playlist, char *filename) return -1; } - /* in_ram buffer is unused for m3u files so we'll use for storing - updated indices */ - index_buf = (int*)playlist->buffer; - /* use temporary pathname */ snprintf(path, sizeof(path), "%s_temp", playlist->filename); overwrite_current = true; @@ -3426,7 +3444,7 @@ int playlist_save(struct playlist_info* playlist, char *filename) } if (overwrite_current) - index_buf[count] = lseek(fd, 0, SEEK_CUR); + playlist->seek_buf[count] = lseek(fd, 0, SEEK_CUR); if (fdprintf(fd, "%s\n", tmp_buf) < 0) { @@ -3471,7 +3489,7 @@ int playlist_save(struct playlist_info* playlist, char *filename) { if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK)) { - playlist->indices[index] = index_buf[count]; + playlist->indices[index] = playlist->seek_buf[count]; count++; } index = (index+1)%playlist->amount; diff --git a/apps/playlist.h b/apps/playlist.h index d994f6e..f14b5c6 100644 --- a/apps/playlist.h +++ b/apps/playlist.h @@ -85,7 +85,11 @@ struct playlist_info int max_playlist_size; /* Max number of files in playlist. Mirror of global_settings.max_files_in_playlist */ bool in_ram; /* playlist stored in ram (dirplay) */ - char *buffer; /* buffer for in-ram playlists */ + + union { + char *buffer; /* buffer for in-ram playlists */ + int *seek_buf; /* buffer for seeks in real playlists */ + }; int buffer_size; /* size of buffer */ int buffer_end_pos; /* last position where buffer was written */ int index; /* index of current playing track */ diff --git a/apps/scrobbler.c b/apps/scrobbler.c index 14cb4d3..891e635 100644 --- a/apps/scrobbler.c +++ b/apps/scrobbler.c @@ -30,7 +30,7 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging #include "metadata.h" #include "kernel.h" #include "audio.h" -#include "buffer.h" +#include "core_alloc.h" #include "settings.h" #include "ata_idle_notify.h" #include "filefuncs.h" @@ -52,7 +52,7 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging /* longest entry I've had is 323, add a safety margin */ #define SCROBBLER_CACHE_LEN 512 -static char* scrobbler_cache; +static int scrobbler_cache; static int cache_pos; static struct mp3entry scrobbler_entry; @@ -139,11 +139,11 @@ static void write_cache(void) if(fd >= 0) { logf("SCROBBLER: writing %d entries", cache_pos); - + char* scrobbler_buf = core_get_data(scrobbler_cache); for ( i=0; i < cache_pos; i++ ) { logf("SCROBBLER: write %d", i); - fdprintf(fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i)); + fdprintf(fd, "%s", scrobbler_buf+(SCROBBLER_CACHE_LEN*i)); } close(fd); } @@ -170,6 +170,7 @@ static void add_to_cache(unsigned long play_length) int ret; char rating = 'S'; /* Skipped */ + char* scrobbler_buf = core_get_data(scrobbler_cache); logf("SCROBBLER: add_to_cache[%d]", cache_pos); @@ -178,7 +179,7 @@ static void add_to_cache(unsigned long play_length) if (scrobbler_entry.tracknum > 0) { - ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos), + ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), SCROBBLER_CACHE_LEN, "%s\t%s\t%s\t%d\t%d\t%c\t%ld\t%s\n", scrobbler_entry.artist, @@ -190,7 +191,7 @@ static void add_to_cache(unsigned long play_length) (long)timestamp, scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:""); } else { - ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos), + ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), SCROBBLER_CACHE_LEN, "%s\t%s\t%s\t\t%d\t%c\t%ld\t%s\n", scrobbler_entry.artist, @@ -248,7 +249,7 @@ int scrobbler_init(void) if(!global_settings.audioscrobbler) return -1; - scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); + scrobbler_cache = core_alloc("scrobller", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); cache_pos = 0; diff --git a/apps/tagcache.c b/apps/tagcache.c index a125347..a035287 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -74,7 +74,7 @@ #include "usb.h" #include "metadata.h" #include "tagcache.h" -#include "buffer.h" +#include "core_alloc.h" #include "crc32.h" #include "misc.h" #include "settings.h" @@ -3044,6 +3044,7 @@ static bool commit(void) return true; } +static int tempbuf_handle; static void allocate_tempbuf(void) { /* Yeah, malloc would be really nice now :) */ @@ -3051,7 +3052,8 @@ static void allocate_tempbuf(void) tempbuf_size = 32*1024*1024; tempbuf = malloc(tempbuf_size); #else - buffer_get_buffer(&tempbuf_size); + tempbuf_handle = core_alloc_maximum("tc tempbuf", &tempbuf_size, NULL); + tempbuf = core_get_data(tempbuf_handle); #endif } @@ -3063,7 +3065,7 @@ static void free_tempbuf(void) #ifdef __PCTOOL__ free(tempbuf); #else - buffer_release_buffer(0); + core_free(tempbuf_handle); #endif tempbuf = NULL; tempbuf_size = 0; @@ -3787,9 +3789,10 @@ static bool allocate_tagcache(void) * Now calculate the required cache size plus * some extra space for alignment fixes. */ - tc_stat.ramcache_allocated = tcmh.tch.datasize + 128 + TAGCACHE_RESERVE + + tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE + sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); - hdr = buffer_alloc(tc_stat.ramcache_allocated + 128); + int handle = core_alloc("tc ramcache", tc_stat.ramcache_allocated); + hdr = core_get_data(handle); memset(hdr, 0, sizeof(struct ramcache_header)); memcpy(¤t_tcmh, &tcmh, sizeof current_tcmh); logf("tagcache: %d bytes allocated.", tc_stat.ramcache_allocated); diff --git a/apps/tagtree.c b/apps/tagtree.c index 3df8d9d..1ba01cd 100644 --- a/apps/tagtree.c +++ b/apps/tagtree.c @@ -44,7 +44,7 @@ #include "playlist.h" #include "keyboard.h" #include "gui/list.h" -#include "buffer.h" +#include "core_alloc.h" #include "yesno.h" #include "misc.h" #include "filetypes.h" @@ -176,9 +176,14 @@ static int current_entry_count; static struct tree_context *tc; /* a few memory alloc helper */ +static int tagtree_handle; +static size_t tagtree_bufsize, tagtree_buf_used; static void* tagtree_alloc(size_t size) { - return buffer_alloc(size); + char* buf = core_get_data(tagtree_handle) + tagtree_buf_used; + size = (size+sizeof(char*)-1) & ~sizeof(char*); + tagtree_buf_used += size; + return buf; } static void* tagtree_alloc0(size_t size) @@ -1034,6 +1039,7 @@ void tagtree_init(void) menu_count = 0; menu = NULL; rootmenu = -1; + tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, NULL); parse_menu(FILE_SEARCH_INSTRUCTIONS); /* If no root menu is set, assume it's the first single menu @@ -1045,6 +1051,8 @@ void tagtree_init(void) add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event); add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event); + + core_shrink(tagtree_handle, core_get_data(tagtree_handle), tagtree_buf_used); } static bool show_search_progress(bool init, int count) diff --git a/apps/talk.c b/apps/talk.c index da03380..0d1ced6 100644 --- a/apps/talk.c +++ b/apps/talk.c @@ -27,7 +27,7 @@ #include #include "string-extra.h" #include "file.h" -#include "buffer.h" +#include "core_alloc.h" #include "system.h" #include "kernel.h" #include "settings.h" @@ -607,10 +607,11 @@ static void alloc_thumbnail_buf(void) /* Allocate a dedicated thumbnail buffer - once */ if (p_thumbnail == NULL) { - size_for_thumbnail = buffer_available(); + size_for_thumbnail = core_available(); if (size_for_thumbnail > MAX_THUMBNAIL_BUFSIZE) size_for_thumbnail = MAX_THUMBNAIL_BUFSIZE; - p_thumbnail = buffer_alloc(size_for_thumbnail); + int handle = core_alloc("thumbnail", size_for_thumbnail); + p_thumbnail = core_get_data(handle); } #else /* use the audio buffer now, need to release before loading a voice */ @@ -705,7 +706,7 @@ void talk_init(void) /* test if we can open and if it fits in the audiobuffer */ - size_t audiobufsz = buffer_available(); + size_t audiobufsz = core_available(); if (voicefile_size <= audiobufsz) { has_voicefile = true; } else { diff --git a/apps/tdspeed.c b/apps/tdspeed.c index 8cb495c..9acaddb 100644 --- a/apps/tdspeed.c +++ b/apps/tdspeed.c @@ -25,7 +25,7 @@ #include #include #include "sound.h" -#include "buffer.h" +#include "core_alloc.h" #include "system.h" #include "tdspeed.h" #include "settings.h" @@ -38,6 +38,44 @@ #define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */ +static int32_t** dsp_src; +static int32_t *overlap_buffer[2] = { NULL, NULL }; +static int32_t *outbuf[2] = { NULL, NULL }; + +static int move_callback(int handle, void* current, void* new) +{ + /* TODO */ + (void)handle; + if (dsp_src) + { + int ch = (current == outbuf[0]) ? 0 : 1; + dsp_src[ch] = outbuf[ch] = new; + } + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; +static int ovl_move_callback(int handle, void* current, void* new) +{ + /* TODO */ + (void)handle; + if (dsp_src) + { + int ch = (current == overlap_buffer[0]) ? 0 : 1; + overlap_buffer[ch] = new; + } + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ovl_ops = { + .move_callback = ovl_move_callback, + .shrink_callback = NULL, +}; + + struct tdspeed_state_s { bool stereo; @@ -52,22 +90,32 @@ struct tdspeed_state_s }; static struct tdspeed_state_s tdspeed_state; -static int32_t *overlap_buffer[2] = { NULL, NULL }; -static int32_t *outbuf[2] = { NULL, NULL }; - void tdspeed_init() { if (global_settings.timestretch_enabled) { /* Allocate buffers */ + int handle; if (overlap_buffer[0] == NULL) - overlap_buffer[0] = (int32_t *) buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t)); + { + handle = core_alloc_ex("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); + overlap_buffer[0] = core_get_data(handle); + } if (overlap_buffer[1] == NULL) - overlap_buffer[1] = (int32_t *) buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t)); + { + handle = core_alloc_ex("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops); + overlap_buffer[1] = core_get_data(handle); + } if (outbuf[0] == NULL) - outbuf[0] = (int32_t *) buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t)); + { + handle = core_alloc_ex("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); + outbuf[0] = core_get_data(handle); + } if (outbuf[1] == NULL) - outbuf[1] = (int32_t *) buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t)); + { + handle = core_alloc_ex("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops); + outbuf[1] = core_get_data(handle); + } } } @@ -325,6 +373,7 @@ long tdspeed_est_input_size(long size) int tdspeed_doit(int32_t *src[], int count) { + dsp_src = src; count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] }, src, count, 0, TDSPEED_OUTBUFSIZE); src[0] = outbuf[0]; diff --git a/apps/tree.c b/apps/tree.c index a9e6a6e..d953542 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -46,7 +46,7 @@ #include "keyboard.h" #include "bookmark.h" #include "onplay.h" -#include "buffer.h" +#include "core_alloc.h" #include "power.h" #include "action.h" #include "talk.h" @@ -1005,17 +1005,19 @@ int rockbox_browse(struct browse_context *browse) void tree_mem_init(void) { /* initialize tree context struct */ + int handle; memset(&tc, 0, sizeof(tc)); tc.dirfilter = &global_settings.dirfilter; tc.sort_dir = global_settings.sort_dir; tc.name_buffer_size = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; - tc.name_buffer = buffer_alloc(tc.name_buffer_size); + handle = core_alloc("tree names", tc.name_buffer_size); + tc.name_buffer = core_get_data(handle); tc.dircache_count = global_settings.max_files_in_dir; - tc.dircache = buffer_alloc(global_settings.max_files_in_dir * - sizeof(struct entry)); + handle = core_alloc("tree entries", global_settings.max_files_in_dir * sizeof(struct entry)); + tc.dircache = core_get_data(handle); tree_get_filetypes(&filetypes, &filetypes_count); } diff --git a/firmware/SOURCES b/firmware/SOURCES index a4be2a5..0e066b6 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -2,6 +2,8 @@ ata_idle_notify.c events.c backlight.c buffer.c +buflib.c +core_alloc.c general.c load_code.c powermgmt.c diff --git a/firmware/buflib.c b/firmware/buflib.c new file mode 100644 index 0000000..4ab2dad --- /dev/null +++ b/firmware/buflib.c @@ -0,0 +1,726 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* This is a memory allocator designed to provide reasonable management of free +* space and fast access to allocated data. More than one allocator can be used +* at a time by initializing multiple contexts. +* +* Copyright (C) 2009 Andrew Mahone +* +* This program 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 +* of the License, or (at your option) any later version. +* +* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +* KIND, either express or implied. +* +****************************************************************************/ + +#include /* for abs() */ +#include /* for snprintf() */ +#include "buflib.h" +#include "string-extra.h" +#include "debug.h" +#include "buffer.h" +#include "system.h" /* for ALIGN_*() */ +/* The main goal of this design is fast fetching of the pointer for a handle. + * For that reason, the handles are stored in a table at the end of the buffer + * with a fixed address, so that returning the pointer for a handle is a simple + * table lookup. To reduce the frequency with which allocated blocks will need + * to be moved to free space, allocations grow up in address from the start of + * the buffer. The buffer is treated as an array of union buflib_data. Blocks + * start with a length marker, which is included in their length. Free blocks + * are marked by negative length, allocated ones use the a buflib_data in + * the block to store a pointer to their handle table entry, so that it can be + * quickly found and updated during compaction. Followed by that, there's + * the pointer to the corresponding struct buflib. That pointer follows a + * character array containing the string identifier of the allocation. After the + * array there is another buflib_data containing the length of that string + + * the sizeo of this buflib_data. + * The allocator functions are passed a context struct so that two allocators + * can be run, for example, one per core may be used, with convenience wrappers + * for the single-allocator case that use a predefined context. + */ + +/* Use this for the default callbacks. + * + * The default callbacks do nothing, therefore the address of this + * acts as a magic as to not even call the default callbacks + */ +static struct buflib_callbacks default_callbacks; + +#if defined(ROCKBOX) +#define YIELD() yield() +#elif defined(__unix) && (__unix == 1) +#include +#define YIELD() sched_yield() +#else +#warning YIELD not defined. Will busy-wait +#define YIELD() +#endif + +#define B_ALIGN_DOWN(x) \ + ALIGN_DOWN(x, sizeof(union buflib_data)) + +#define B_ALIGN_UP(x) \ + ALIGN_UP(x, sizeof(union buflib_data)) + +#ifdef DEBUG + #include + #define BDEBUGF DEBUGF +#else + #define BDEBUGF(...) do { } while(0) +#endif + +/* Initialize buffer manager */ +void +buflib_init(struct buflib_context *ctx, void *buf, size_t size) +{ + union buflib_data *bd_buf = buf; + + /* Align on sizeof(buflib_data), to prevent unaligned access */ + ALIGN_BUFFER(bd_buf, size, sizeof(union buflib_data)); + size /= sizeof(union buflib_data); + /* The handle table is initialized with no entries */ + ctx->handle_table = bd_buf + size; + ctx->last_handle = bd_buf + size; + ctx->first_free_handle = bd_buf + size - 1; + ctx->first_free_block = bd_buf; + ctx->buf_start = bd_buf; + /* A marker is needed for the end of allocated data, to make sure that it + * does not collide with the handle table, and to detect end-of-buffer. + */ + ctx->alloc_end = bd_buf; + ctx->compact = true; + + BDEBUGF("buflib initialized with %d.%2d kiB", size / 1024, (size%1000)/10); +} + +/* Allocate a new handle, returning 0 on failure */ +static inline +union buflib_data* handle_alloc(struct buflib_context *ctx) +{ + union buflib_data *handle; + /* first_free_handle is a lower bound on free handles, work through the + * table from there until a handle containing NULL is found, or the end + * of the table is reached. + */ + for (handle = ctx->first_free_handle; handle >= ctx->last_handle; handle--) + if (!handle->alloc) + break; + /* If the search went past the end of the table, it means we need to extend + * the table to get a new handle. + */ + if (handle < ctx->last_handle) + { + if (handle >= ctx->alloc_end) + ctx->last_handle--; + else + return NULL; + } + handle->val = -1; + return handle; +} + +/* Free one handle, shrinking the handle table if it's the last one */ +static inline +void handle_free(struct buflib_context *ctx, union buflib_data *handle) +{ + handle->alloc = 0; + /* Update free handle lower bound if this handle has a lower index than the + * old one. + */ + if (handle > ctx->first_free_handle) + ctx->first_free_handle = handle; + if (handle == ctx->last_handle) + ctx->last_handle++; + else + ctx->compact = false; +} + +/* Get the start block of an allocation */ +static union buflib_data* handle_to_block(struct buflib_context* ctx, int handle) +{ + union buflib_data* name_field = + (union buflib_data*)buflib_get_name(ctx, handle); + + return name_field - 3; +} + +/* Shrink the handle table, returning true if its size was reduced, false if + * not + */ +static inline +bool +handle_table_shrink(struct buflib_context *ctx) +{ + bool rv; + union buflib_data *handle; + for (handle = ctx->last_handle; !(handle->alloc); handle++); + if (handle > ctx->first_free_handle) + ctx->first_free_handle = handle - 1; + rv = handle == ctx->last_handle; + ctx->last_handle = handle; + return rv; +} + + +/* If shift is non-zero, it represents the number of places to move + * blocks in memory. Calculate the new address for this block, + * update its entry in the handle table, and then move its contents. + */ +static bool +move_block(struct buflib_context* ctx, union buflib_data* block, int shift) +{ +#if 1 /* moving temporarily disabled */ + (void)ctx;(void)block;(void)shift; + return false; +#else + char* new_start; + union buflib_data *new_block, *tmp = block[1].handle; + struct buflib_callbacks *ops = block[2].ops; + if (ops && !ops->move_callback) + return false; + + int handle = ctx->handle_table - tmp; + BDEBUGF("%s(): moving \"%s\"(id=%d) by %d(%d)\n", __func__, block[3].name, + handle, shift, shift*sizeof(union buflib_data)); + new_block = block + shift; + new_start = tmp->alloc + shift*sizeof(union buflib_data); + /* call the callback before moving, the default one needn't be called */ + if (ops) + ops->move_callback(handle, tmp->alloc, new_start); + + tmp->alloc = new_start; /* update handle table */ + memmove(new_block, block, block->val * sizeof(union buflib_data)); + + return true; +#endif +} + +/* Compact allocations and handle table, adjusting handle pointers as needed. + * Return true if any space was freed or consolidated, false otherwise. + */ +static bool +buflib_compact(struct buflib_context *ctx) +{ + BDEBUGF("%s(): Compacting!\n", __func__); + union buflib_data *first_free = ctx->first_free_block, *block; + int shift = 0, len; + /* Store the results of attempting to shrink the handle table */ + bool ret = handle_table_shrink(ctx); + for(block = first_free; block != ctx->alloc_end; block += len) + { + len = block->val; + /* This block is free, add its length to the shift value */ + if (len < 0) + { + shift += len; + len = -len; + continue; + } + /* attempt to fill any hole */ + if (abs(ctx->first_free_block->val) > block->val) + { + intptr_t size = first_free->val; + if (move_block(ctx, block, first_free - block)) + { + block->val *= -1; + block = ctx->first_free_block; + ctx->first_free_block += block->val; + ctx->first_free_block->val = size + block->val; + continue; + } + } + /* attempt move the allocation by shift */ + if (shift) + { + /* failing to move creates a hole, therefore mark this + * block as not allocated anymore and move first_free_block up */ + if (!move_block(ctx, block, shift)) + { + union buflib_data* hole = block + shift; + hole->val = shift; + if (ctx->first_free_block > hole) + ctx->first_free_block = hole; + shift = 0; + } + } + } + /* Move the end-of-allocation mark, and return true if any new space has + * been freed. + */ + ctx->alloc_end += shift; + /* only move first_free_block up if it wasn't already by a hole */ + if (ctx->first_free_block > ctx->alloc_end) + ctx->first_free_block = ctx->alloc_end; + ctx->compact = true; + return ret || shift; +} + +/* Compact the buffer by trying both shrinking and moving. + * + * Try to move first. If unsuccesfull, try to shrink. If that was successful + * try to move once more as there might be more room now. + */ +static bool +buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints) +{ + bool result = false; + /* if something compacted before already there will be no further gain */ + if (!ctx->compact) + result = buflib_compact(ctx); + if (!result) + { + union buflib_data* this; + for(this = ctx->buf_start; this < ctx->alloc_end; this += abs(this->val)) + { + if (this->val > 0 && this[2].ops + && this[2].ops->shrink_callback) + { + int ret; + int handle = ctx->handle_table - this[1].handle; + char* data = this[1].handle->alloc; + ret = this[2].ops->shrink_callback(handle, shrink_hints, + data, (char*)(this+this->val)-data); + result |= (ret == BUFLIB_CB_OK); + /* this might have changed in the callback (if + * it shrinked from the top), get it again */ + this = handle_to_block(ctx, handle); + } + } + /* shrinking was successful at least once, try compaction again */ + if (result) + result |= buflib_compact(ctx); + } + + return result; +} + +/* Shift buffered items by size units, and update handle pointers. The shift + * value must be determined to be safe *before* calling. + */ +static void +buflib_buffer_shift(struct buflib_context *ctx, int shift) +{ + memmove(ctx->buf_start + shift, ctx->buf_start, + (ctx->alloc_end - ctx->buf_start) * sizeof(union buflib_data)); + union buflib_data *handle; + for (handle = ctx->last_handle; handle < ctx->handle_table; handle++) + if (handle->alloc) + handle->alloc += shift; + ctx->first_free_block += shift; + ctx->buf_start += shift; + ctx->alloc_end += shift; +} + +/* Shift buffered items up by size bytes, or as many as possible if size == 0. + * Set size to the number of bytes freed. + */ +void* +buflib_buffer_out(struct buflib_context *ctx, size_t *size) +{ + if (!ctx->compact) + buflib_compact(ctx); + size_t avail = ctx->last_handle - ctx->alloc_end; + size_t avail_b = avail * sizeof(union buflib_data); + if (*size && *size < avail_b) + { + avail = (*size + sizeof(union buflib_data) - 1) + / sizeof(union buflib_data); + avail_b = avail * sizeof(union buflib_data); + } + *size = avail_b; + void *ret = ctx->buf_start; + buflib_buffer_shift(ctx, avail); + return ret; +} + +/* Shift buffered items down by size bytes */ +void +buflib_buffer_in(struct buflib_context *ctx, int size) +{ + size /= sizeof(union buflib_data); + buflib_buffer_shift(ctx, -size); +} + +/* Allocate a buffer of size bytes, returning a handle for it */ +int +buflib_alloc(struct buflib_context *ctx, size_t size) +{ + return buflib_alloc_ex(ctx, size, "", &default_callbacks); +} + +/* Allocate a buffer of size bytes, returning a handle for it. + * + * The additional name parameter gives the allocation a human-readable name, + * the ops parameter points to caller-implemented callbacks for moving and + * shrinking. NULL for default callbacks + */ + +int +buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name, + struct buflib_callbacks *ops) +{ + /* busy wait if there's a thread owning the lock */ + while (ctx->handle_lock != 0) YIELD(); + + union buflib_data *handle, *block; + size_t name_len = name ? B_ALIGN_UP(strlen(name)+1) : 0; + bool last; + /* This really is assigned a value before use */ + int block_len; + size += name_len; + size = (size + sizeof(union buflib_data) - 1) / + sizeof(union buflib_data) + /* add 4 objects for alloc len, pointer to handle table entry and + * name length, and the ops pointer */ + + 4; +handle_alloc: + handle = handle_alloc(ctx); + if (!handle) + { + /* If allocation has failed, and compaction has succeded, it may be + * possible to get a handle by trying again. + */ + if (!ctx->compact && buflib_compact(ctx)) + goto handle_alloc; + else + { /* first try to shrink the alloc before the handle table + * to make room for new handles */ + int handle = ctx->handle_table - ctx->last_handle; + union buflib_data* last_block = handle_to_block(ctx, handle); + struct buflib_callbacks* ops = last_block[2].ops; + if (ops && ops->shrink_callback) + { + char *data = buflib_get_data(ctx, handle); + unsigned hint = BUFLIB_SHRINK_POS_BACK | 10*sizeof(union buflib_data); + if (ops->shrink_callback(handle, hint, data, + (char*)(last_block+last_block->val)-data) == BUFLIB_CB_OK) + { /* retry one more time */ + goto handle_alloc; + } + } + return 0; + } + } + +buffer_alloc: + /* need to re-evaluate last before the loop because the last allocation + * possibly made room in its front to fit this, so last would be wrong */ + last = false; + for (block = ctx->first_free_block;;block += block_len) + { + /* If the last used block extends all the way to the handle table, the + * block "after" it doesn't have a header. Because of this, it's easier + * to always find the end of allocation by saving a pointer, and always + * calculate the free space at the end by comparing it to the + * last_handle pointer. + */ + if(block == ctx->alloc_end) + { + last = true; + block_len = ctx->last_handle - block; + if ((size_t)block_len < size) + block = NULL; + break; + } + block_len = block->val; + /* blocks with positive length are already allocated. */ + if(block_len > 0) + continue; + block_len = -block_len; + /* The search is first-fit, any fragmentation this causes will be + * handled at compaction. + */ + if ((size_t)block_len >= size) + break; + } + if (!block) + { + /* Try compacting if allocation failed */ + if (buflib_compact_and_shrink(ctx, + (size*sizeof(union buflib_data))&BUFLIB_SHRINK_SIZE_MASK)) + { + goto buffer_alloc; + } else { + handle->val=1; + handle_free(ctx, handle); + return 0; + } + } + + /* Set up the allocated block, by marking the size allocated, and storing + * a pointer to the handle. + */ + union buflib_data *name_len_slot; + block->val = size; + block[1].handle = handle; + block[2].ops = ops ?: &default_callbacks; + strcpy(block[3].name, name); + name_len_slot = (union buflib_data*)B_ALIGN_UP(block[3].name + name_len); + name_len_slot->val = 1 + name_len/sizeof(union buflib_data); + handle->alloc = (char*)(name_len_slot + 1); + /* If we have just taken the first free block, the next allocation search + * can save some time by starting after this block. + */ + if (block == ctx->first_free_block) + ctx->first_free_block += size; + block += size; + /* alloc_end must be kept current if we're taking the last block. */ + if (last) + ctx->alloc_end = block; + /* Only free blocks *before* alloc_end have tagged length. */ + else if ((size_t)block_len > size) + block->val = size - block_len; + /* Return the handle index as a positive integer. */ + return ctx->handle_table - handle; +} + +/* Free the buffer associated with handle_num. */ +void +buflib_free(struct buflib_context *ctx, int handle_num) +{ + union buflib_data *handle = ctx->handle_table - handle_num, + *freed_block = handle_to_block(ctx, handle_num), + *block = ctx->first_free_block, + *next_block = block; + /* We need to find the block before the current one, to see if it is free + * and can be merged with this one. + */ + while (next_block < freed_block) + { + block = next_block; + next_block += abs(block->val); + } + /* If next_block == block, the above loop didn't go anywhere. If it did, + * and the block before this one is empty, we can combine them. + */ + if (next_block == freed_block && next_block != block && block->val < 0) + block->val -= freed_block->val; + /* Otherwise, set block to the newly-freed block, and mark it free, before + * continuing on, since the code below exects block to point to a free + * block which may have free space after it. + */ + else + { + block = freed_block; + block->val = -block->val; + } + next_block = block - block->val; + /* Check if we are merging with the free space at alloc_end. */ + if (next_block == ctx->alloc_end) + ctx->alloc_end = block; + /* Otherwise, the next block might still be a "normal" free block, and the + * mid-allocation free means that the buffer is no longer compact. + */ + else { + ctx->compact = false; + if (next_block->val < 0) + block->val += next_block->val; + } + handle_free(ctx, handle); + handle->alloc = NULL; + /* If this block is before first_free_block, it becomes the new starting + * point for free-block search. + */ + if (block < ctx->first_free_block) + ctx->first_free_block = block; + + /* if the handle is the one aquired with buflib_alloc_maximum() + * unlock buflib_alloc() as part of the shrink */ + if (ctx->handle_lock == handle_num) + ctx->handle_lock = 0; +} + +/* Return the maximum allocatable memory in bytes */ +size_t +buflib_available(struct buflib_context* ctx) +{ + /* subtract 5 elements for + * val, handle, name_len, ops and the handle table entry*/ + size_t diff = (ctx->last_handle - ctx->alloc_end - 5); + diff *= sizeof(union buflib_data); /* make it bytes */ + diff -= 16; /* reserve 16 for the name */ + + if (diff > 0) + return diff; + else + return 0; +} + +/* + * Allocate all available (as returned by buflib_available()) memory and return + * a handle to it + * + * This grabs a lock which can only be unlocked by buflib_free() or + * buflib_shrink(), to protect from further allocations (which couldn't be + * serviced anyway). + */ +int +buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size, struct buflib_callbacks *ops) +{ + int handle; + + /* limit name to 16 since that's what buflib_available() accounts for it */ + char buf[16]; + *size = buflib_available(ctx); + strlcpy(buf, name, sizeof(buf)); + handle = buflib_alloc_ex(ctx, *size, buf, ops); + + if (handle > 0) /* shouldn't happen ?? */ + ctx->handle_lock = handle; + + return handle; +} + +/* Shrink the allocation indicated by the handle according to new_start and + * new_size. Grow is not possible, therefore new_start and new_start + new_size + * must be within the original allocation + */ +bool +buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size) +{ + char* oldstart = buflib_get_data(ctx, handle); + char* newstart = new_start; + char* newend = newstart + new_size; + + /* newstart must be higher and new_size not "negative" */ + if (newstart < oldstart || newend < newstart) + return false; + union buflib_data *block = handle_to_block(ctx, handle), + *old_next_block = block + block->val, + /* newstart isn't necessarily properly aligned but it + * needn't be since it's only dereferenced by the user code */ + *aligned_newstart = (union buflib_data*)B_ALIGN_DOWN(newstart), + *aligned_oldstart = (union buflib_data*)B_ALIGN_DOWN(oldstart), + *new_next_block = (union buflib_data*)B_ALIGN_UP(newend), + *new_block, metadata_size; + + /* growing is not supported */ + if (new_next_block > old_next_block) + return false; + + metadata_size.val = aligned_oldstart - block; + /* update val and the handle table entry */ + new_block = aligned_newstart - metadata_size.val; + block[0].val = new_next_block - new_block; + + block[1].handle->alloc = newstart; + if (block != new_block) + { + /* move metadata over, i.e. pointer to handle table entry and name + * This is actually the point of no return. Data in the allocation is + * being modified, and therefore we must successfully finish the shrink + * operation */ + memmove(new_block, block, metadata_size.val*sizeof(metadata_size)); + /* mark the old block unallocated */ + block->val = block - new_block; + union buflib_data *freed_block = block, + *free_before = ctx->first_free_block, + *next_block = free_before; + /* We need to find the block before the current one, to see if it is free + * and can be merged with this one. + */ + while (next_block < freed_block) + { + free_before = next_block; + next_block += abs(block->val); + } + /* If next_block == free_before, the above loop didn't go anywhere. + * If it did, and the block before this one is empty, we can combine them. + */ + if (next_block == freed_block && next_block != free_before && free_before->val < 0) + free_before->val += freed_block->val; + else if (next_block == free_before) + ctx->first_free_block = freed_block; + + /* We didn't handle size changes yet, assign block to the new one + * the code below the wants block whether it changed or not */ + block = new_block; + } + + /* Now deal with size changes that create free blocks after the allocation */ + if (old_next_block != new_next_block) + { + if (ctx->alloc_end == old_next_block) + ctx->alloc_end = new_next_block; + else if (old_next_block->val < 0) + { /* enlarge next block by moving it up */ + new_next_block->val = old_next_block->val - (old_next_block - new_next_block); + } + else if (old_next_block != new_next_block) + { /* creating a hole */ + /* must be negative to indicate being unallocated */ + new_next_block->val = new_next_block - old_next_block; + } + /* update first_free_block for the newly created free space */ + if (ctx->first_free_block > new_next_block) + ctx->first_free_block = new_next_block; + } + + /* if the handle is the one aquired with buflib_alloc_maximum() + * unlock buflib_alloc() as part of the shrink */ + if (ctx->handle_lock == handle) + ctx->handle_lock = 0; + + return true; +} + +const char* buflib_get_name(struct buflib_context *ctx, int handle) +{ + union buflib_data *data = (union buflib_data*)ALIGN_DOWN((intptr_t)buflib_get_data(ctx, handle), sizeof (*data)); + size_t len = data[-1].val; + if (len <= 1) + return NULL; + return data[-len].name; +} + +void buflib_print_allocs(struct buflib_context *ctx, void (*print)(const char*)) +{ + union buflib_data *this, *end = ctx->handle_table; + char buf[128]; + for(this = end - 1; this >= ctx->last_handle; this--) + { + if (!this->alloc) continue; + + int handle_num; + const char *name; + union buflib_data *block_start, *alloc_start; + intptr_t alloc_len; + + handle_num = end - this; + alloc_start = buflib_get_data(ctx, handle_num); + name = buflib_get_name(ctx, handle_num); + block_start = (union buflib_data*)name - 3; + alloc_len = block_start->val * sizeof(union buflib_data); + + snprintf(buf, sizeof(buf), + "%s(%d):\t%p\n" + " \t%p\n" + " \t%ld\n", + name?:"(null)", handle_num, block_start, alloc_start, alloc_len); + print(buf); + } +} + +void buflib_print_blocks(struct buflib_context *ctx, void (*print)(const char*)) +{ + for(union buflib_data* this = ctx->buf_start; + this < ctx->alloc_end; + this += abs(this->val)) + { + char buf[128] = { 0 }; + snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)", + this, this->val, + this->val > 0? this[3].name:""); + print(buf); + } +} diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c index d43e313..b10df1c 100644 --- a/firmware/common/dircache.c +++ b/firmware/common/dircache.c @@ -38,7 +38,7 @@ #include "kernel.h" #include "usb.h" #include "file.h" -#include "buffer.h" +#include "core_alloc.h" #include "dir.h" #include "storage.h" #if CONFIG_RTC @@ -57,6 +57,8 @@ #else #define MAX_OPEN_DIRS 8 #endif +static DIR_CACHED opendirs[MAX_OPEN_DIRS]; +static char opendir_dnames[MAX_OPEN_DIRS][MAX_PATH]; #define MAX_PENDING_BINDINGS 2 struct fdbind_queue { @@ -811,6 +813,19 @@ static void generate_dot_d_names(void) strcpy(dot, "."); strcpy(dotdot, ".."); } + +static int move_callback(int handle, void* current, void* new) +{ + /* TODO */ + (void)handle;(void)current;(void)new; + return BUFLIB_CB_OK; +} + +static struct buflib_callbacks ops = { + .move_callback = move_callback, + .shrink_callback = NULL, +}; + /** * Start scanning the disk to build the dircache. * Either transparent or non-transparent build method is used. @@ -838,12 +853,15 @@ int dircache_build(int last_size) queue_post(&dircache_queue, DIRCACHE_BUILD, 0); return 2; } - + if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) { + int handle; allocated_size = last_size + DIRCACHE_RESERVE; - dircache_root = buffer_alloc(allocated_size); - ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); + handle = core_alloc_ex("dircache", allocated_size, &ops); + dircache_root = core_get_data(handle); + ALIGN_BUFFER(dircache_root, allocated_size, + sizeof(struct dircache_entry)); d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; dircache_size = 0; @@ -861,8 +879,9 @@ int dircache_build(int last_size) * after generation the buffer will be compacted with DIRCACHE_RESERVE * free bytes inbetween */ size_t got_size; - char* buf = buffer_get_buffer(&got_size); - ALIGN_BUFFER(buf, got_size, sizeof(struct dircache_entry)); + int handle = core_alloc_maximum("dircache", &got_size, &ops); + dircache_root = core_get_data(handle); + ALIGN_BUFFER(dircache_root, got_size, sizeof(struct dircache_entry)); d_names_start = d_names_end = (char*)dircache_root + got_size - 1; dircache_size = 0; generate_dot_d_names(); @@ -896,11 +915,11 @@ int dircache_build(int last_size) allocated_size = (d_names_end - (char*)dircache_root); reserve_used = 0; - buffer_release_buffer(allocated_size); + core_shrink(handle, dircache_root, allocated_size); return res; fail: dircache_disable(); - buffer_release_buffer(0); + core_free(handle); return res; } @@ -936,7 +955,7 @@ void dircache_init(void) memset(opendirs, 0, sizeof(opendirs)); for (i = 0; i < MAX_OPEN_DIRS; i++) { - opendirs[i].theent.d_name = buffer_alloc(MAX_PATH); + opendirs[i].theent.d_name = opendir_dnames[i]; } queue_init(&dircache_queue, true); diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c new file mode 100644 index 0000000..93c4f70 --- /dev/null +++ b/firmware/core_alloc.c @@ -0,0 +1,61 @@ + +#include +#include "core_alloc.h" +#include "buflib.h" +#include "buffer.h" + +/* not static so it can be discovered by core_get_data() */ +struct buflib_context core_ctx; + +void core_allocator_init(void) +{ + buffer_init(); + size_t size; + void *start = buffer_get_buffer(&size); + buflib_init(&core_ctx, start, size); + buffer_release_buffer(size); +} + +int core_alloc(const char* name, size_t size) +{ + return buflib_alloc_ex(&core_ctx, size, name, NULL); +} + +int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops) +{ + return buflib_alloc_ex(&core_ctx, size, name, ops); +} + +size_t core_available(void) +{ + return buflib_available(&core_ctx); +} + +void core_free(int handle) +{ + buflib_free(&core_ctx, handle); +} + +int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops) +{ + return buflib_alloc_maximum(&core_ctx, name, size, ops); +} + +bool core_shrink(int handle, void* new_start, size_t new_size) +{ + return buflib_shrink(&core_ctx, handle, new_start, new_size); +} + +void core_print_allocs(void (*print)(const char*)) +{ + buflib_print_allocs(&core_ctx, print); +} +void core_print_blocks(void (*print)(const char*)) +{ + buflib_print_blocks(&core_ctx, print); +} + +const char* core_get_alloc_name(int handle) +{ + return buflib_get_name(&core_ctx, handle); +} diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h new file mode 100644 index 0000000..6ab5e74 --- /dev/null +++ b/firmware/include/buflib.h @@ -0,0 +1,238 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* This is a memory allocator designed to provide reasonable management of free +* space and fast access to allocated data. More than one allocator can be used +* at a time by initializing multiple contexts. +* +* Copyright (C) 2009 Andrew Mahone +* Copyright (C) 2010 Thomas Martitz +* +* This program 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 +* of the License, or (at your option) any later version. +* +* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +* KIND, either express or implied. +* +****************************************************************************/ + +#ifndef _BUFLIB_H_ +#define _BUFLIB_H_ +#include +#include +#include + + + +union buflib_data +{ + intptr_t val; + char name[1]; /* actually a variable sized string */ + struct buflib_callbacks* ops; + char* alloc; + union buflib_data *handle; +}; + +struct buflib_context +{ + union buflib_data *handle_table; + union buflib_data *first_free_handle; + union buflib_data *last_handle; + union buflib_data *first_free_block; + union buflib_data *buf_start; + union buflib_data *alloc_end; + volatile int handle_lock; + bool compact; +}; + +/** + * Callbacks used by the buflib to inform allocation that compaction + * is happening (before data is moved) + * + * Note that buflib tries to move to satisfy new allocations before shrinking. + * So if you have something to resize try to do it outside of the callback. + * + * Regardless of the above, if the allocation is SHRINKABLE, but not + * MUST_NOT_MOVE buflib will move the allocation before even attempting to + * shrink. + */ +struct buflib_callbacks { + /** + * This is called before data is moved. Use this to fix up any pointers + * pointing to within the allocation. The size is unchanged + * + * handle: The corresponding handle + * current: The current start of the allocation + * new: The new start of the allocation, after data movement + * + * Return: Return BUFLIB_CB_OK + * + * If NULL: this allocation must not be moved around by the buflib when + * compation occurs + */ + int (*move_callback)(int handle, void* current, void* new); + /** + * This is called when the buflib desires to shrink a SHRINKABLE buffer + * in order to satisfy new allocation and if moving other allocations + * failed. + * Move data around as you need and call core_shrink() from within the + * callback to do the shrink (buflib will not move data as part of shrinking) + * + * hint: bit mask containing hints on how shrinking is desired + * handle: The corresponding handle + * start: The old start of the allocation + * + * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_SHRINK if shirinking + * is impossible at this moment. + * + * if NULL: this allocation cannot be resized. + * It is recommended that allocation that must not move are + * at least shrinkable + */ + int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size); +}; + +#define BUFLIB_SHRINK_POS_MASK ((1<<0|1<<1)<<30) +#define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK) +#define BUFLIB_SHRINK_POS_FRONT (1u<<31) +#define BUFLIB_SHRINK_POS_BACK (1u<<30) + +/** + * Possible return values for the callbacks, some of them can cause + * compaction to fail and therefore new allocations to fail + */ +/* Everything alright */ +#define BUFLIB_CB_OK 0 +/* Tell buflib that resizing failed, possibly future making allocations fail */ +#define BUFLIB_CB_CANNOT_SHRINK 1 + +/** + * Initializes buflib with a caller allocated context and memory pool + */ +void buflib_init(struct buflib_context *context, void *buf, size_t size); + +/** + * Returns how many bytes left the buflib has to satisfy allocations (not + * accounting possible compaction) + * + * There might be more after a future compaction which is not handled by + * this function. + */ +size_t buflib_available(struct buflib_context *ctx); + + +/** + * Allocates memory from buflib's memory pool + * + * name: A string identifier giving this allocation a name + * size: How many bytes to allocate + * + * Returns: An integer handle identifying this allocation + */ +int buflib_alloc(struct buflib_context *context, size_t size); + + +/** + * Allocates memory from the buflib's memory pool with additional callbacks + * and flags + * + * name: A string identifier giving this allocation a name + * size: How many bytes to allocate + * flags: Flags giving information how this allocation needs to be handled (see below) + * ops: a struct with pointers to callback functions (see below) + * + * Returns: An integer handle identifying this allocation + */ +int buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name, + struct buflib_callbacks *ops); + +/** + * Gets all available memory from buflib, for temporary use. + * It aquires a lock so allocations from other threads will wait until the + * lock is released (by core_shrink()). + * + * Buflib may call the shrin_callback() after some time if it's in need of + * memory and core_shrink() has not been called yet. + * + * name: A string identifier giving this allocation a name + * size: The actual size will be returned into size + * ops: a struct with pointers to callback functions + * + * Returns: An integer handle identifying this allocation + */ +int buflib_alloc_maximum(struct buflib_context* ctx, const char* name, + size_t *size, struct buflib_callbacks *ops); + +/** + * Shrink the memory allocation associated with the given handle + * Mainly intended to be used with the shrink callback (call this in the + * callback and get return BUFLIB_CB_OK, but it can also be called outside + * + * If a lock was aquired for this handle, the lock will be unlocked, assuming + * the allocation has freed memory for future allocation by other threads. + * + * Note that you must move/copy data around yourself before calling this, + * buflib will not do this as part of shrinking. + * + * handle: The handle identifying this allocation + * new_start: the new start of the allocation + * new_size: the new size of the allocation + * + * Returns: true if shrinking was successful. Otherwise it returns false, + * without having modified memory and without having unlocked the lock. + * + */ +bool buflib_shrink(struct buflib_context *ctx, int handle, void* newstart, size_t new_size); + +/** + * Frees memory associated with the given handle + */ +void buflib_free(struct buflib_context *context, int handle); +void* buflib_buffer_out(struct buflib_context *ctx, size_t *size); +void buflib_buffer_in(struct buflib_context *ctx, int size); + +/* debugging */ + +/** + * Returns the name, as given to core_alloc() and core_allloc_ex(), of the + * allocation associated with the given handle + * + * handle: The handle indicating the allocation + * + * Returns: A pointer to the string identifier of the allocation + */ +const char* buflib_get_name(struct buflib_context *ctx, int handle); + +/** + * Prints an overview of all current allocations with the help + * of the passed printer helper + */ +void buflib_print_allocs(struct buflib_context *ctx, void (*print)(const char*)); +void buflib_print_blocks(struct buflib_context *ctx, void (*print)(const char*)); + +/** + * Queries the data pointer for the given handle. It's actually a cheap operation, + * so don't hesitate using it extensivly. + * + * Notice that you need to re-query after every direct or indirect yield(), + * because compaction can happen by other threads which may get your data + * moved around (or you can get notified about changes by callbacks, + * see further below). + * + * handle: The handle corresponding to the allocation + * + * Returns: The start pointer of the allocation + */ +static inline void* buflib_get_data(struct buflib_context *context, int handle) +{ + return (void*)(context->handle_table[-handle].alloc); +} +#endif diff --git a/firmware/include/core_alloc.h b/firmware/include/core_alloc.h new file mode 100644 index 0000000..c7d7651 --- /dev/null +++ b/firmware/include/core_alloc.h @@ -0,0 +1,34 @@ + +#ifndef __CORE_ALLOC_H__ +#define __CORE_ALLOC_H__ +#include +#include +#include "buflib.h" + +/* All functions below are wrappers for functions in buflib.h, except + * they have a predefined context + */ +void core_allocator_init(void); +int core_alloc(const char* name, size_t size); +int core_alloc_ex(const char* name, size_t size, struct buflib_callbacks *ops); + +void core_free(int handle); + + +int core_alloc_maximum(const char* name, size_t *size, struct buflib_callbacks *ops); + +bool core_shrink(int handle, void* new_start, size_t new_size); + +size_t core_available(void); + +void core_print_allocs(void (*print)(const char*)); +void core_print_blocks(void (*print)(const char*)); + +const char* core_get_alloc_name(int handle); + +static inline void* core_get_data(int handle) +{ + extern struct buflib_context core_ctx; + return buflib_get_data(&core_ctx, handle); +} +#endif /* __CORE_ALLOC_H__ */ diff --git a/firmware/rolo.c b/firmware/rolo.c index 2d1a91c..d1603c0 100644 --- a/firmware/rolo.c +++ b/firmware/rolo.c @@ -31,7 +31,7 @@ #include "i2c.h" #include "adc.h" #include "string.h" -#include "buffer.h" +#include "core_alloc.h" #include "storage.h" #include "rolo.h" @@ -48,6 +48,7 @@ #define IRQ0_EDGE_TRIGGER 0x80 +static int rolo_handle; #ifdef CPU_PP /* Handle the COP properly - it needs to jump to a function outside SDRAM while * the new firmware is being loaded, and then jump to the start of SDRAM @@ -99,7 +100,7 @@ void rolo_restart_cop(void) static void rolo_error(const char *text) { - buffer_release_buffer(0); + core_free(rolo_handle); lcd_clear_display(); lcd_puts(0, 0, "ROLO error:"); lcd_puts_scroll(0, 1, text); @@ -240,7 +241,8 @@ int rolo_load(const char* filename) /* get the system buffer. release only in case of error, otherwise * we don't return anyway */ - filebuf = buffer_get_buffer(&filebuf_size); + rolo_handle = core_alloc_maximum("rolo", &filebuf_size, NULL); + filebuf = core_get_data(rolo_handle); #if CONFIG_CPU != SH7034 /* Read and save checksum */ diff --git a/firmware/target/hosted/sdl/button-sdl.c b/firmware/target/hosted/sdl/button-sdl.c index f14e53f..92ffb1a 100644 --- a/firmware/target/hosted/sdl/button-sdl.c +++ b/firmware/target/hosted/sdl/button-sdl.c @@ -293,7 +293,7 @@ void gui_message_loop(void) do { /* wait for the next event */ - while(SDL_WaitEvent(&event) == 0) + if(SDL_WaitEvent(&event) == 0) printf("SDL_WaitEvent() error\n"); sim_enter_irq_handler();