summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/debug_menu.c18
-rw-r--r--apps/dsp.c10
-rw-r--r--apps/filetypes.c29
-rw-r--r--apps/gui/skin_engine/skin_engine.c3
-rw-r--r--apps/main.c12
-rw-r--r--apps/menus/main_menu.c4
-rw-r--r--apps/mpeg.c68
-rw-r--r--apps/playback.c54
-rw-r--r--apps/playlist.c23
-rw-r--r--apps/playlist.h6
-rw-r--r--apps/scrobbler.c20
-rw-r--r--apps/tagcache.c21
-rw-r--r--apps/tagtree.c12
-rw-r--r--apps/talk.c3
-rw-r--r--apps/tdspeed.c27
-rw-r--r--apps/tree.c9
-rw-r--r--firmware/SOURCES2
-rw-r--r--firmware/buflib.c777
-rw-r--r--firmware/common/dircache.c23
-rw-r--r--firmware/core_alloc.c57
-rw-r--r--firmware/export/audio.h1
-rw-r--r--firmware/include/buflib.h319
-rw-r--r--firmware/include/core_alloc.h36
-rw-r--r--firmware/rolo.c8
-rw-r--r--firmware/target/arm/ata-nand-telechips.c15
-rw-r--r--firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c21
26 files changed, 1453 insertions, 125 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 9e4621b749..fb8575ec62 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,22 @@ static bool dbg_buffering_thread(void)
#endif /* CONFIG_CODEC */
#endif /* HAVE_LCD_BITMAP */
+static const char* bf_getname(int selected_item, void *data,
+ char *buffer, size_t buffer_len)
+{
+ (void)data;
+ core_print_block_at(selected_item, buffer, buffer_len);
+ return buffer;
+}
+
+static bool dbg_buflib_allocs(void)
+{
+ struct simplelist_info info;
+ simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), 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)
@@ -2040,6 +2057,7 @@ static const struct the_menu_item menuitems[] = {
{ "pm histogram", peak_meter_histogram},
#endif /* PM_DEBUG */
#endif /* HAVE_LCD_BITMAP */
+ { "View buflib allocs", dbg_buflib_allocs },
#ifndef SIMULATOR
#if CONFIG_TUNER
{ "FM Radio", dbg_fm_radio },
diff --git a/apps/dsp.c b/apps/dsp.c
index 3cff1918d7..a728dd75ea 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"
@@ -325,10 +325,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("resample buf",
+ big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t));
+ if (handle > 0)
+ big_resample_buf = core_get_data(handle);
+ else
+ big_sample_buf_count = 0;
}
else
{
diff --git a/apps/filetypes.c b/apps/filetypes.c
index 17a16db4ec..c52c734a1d 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"
@@ -183,12 +183,14 @@ 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 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;
@@ -219,7 +221,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
@@ -312,16 +314,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("filetypes", strdup_bufsize);
+ 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 */
@@ -355,13 +369,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
<extension>,<plugin>,<icon code>
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 fbedbb96fd..d136f90aa5 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 9cb724562c..cc9c9e8d8e 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();
@@ -400,9 +400,6 @@ static void init(void)
global_settings.mdb_shape,
global_settings.mdb_enable,
global_settings.superbass);
-
- /* audio_init must to know the size of voice buffer so init voice first */
- talk_init();
#endif /* CONFIG_CODEC != SWCODEC */
scrobbler_init();
@@ -428,7 +425,7 @@ static void init(void)
#endif
system_init();
- buffer_init();
+ core_allocator_init();
kernel_init();
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
@@ -684,9 +681,6 @@ static void init(void)
global_settings.mdb_shape,
global_settings.mdb_enable,
global_settings.superbass);
-
- /* audio_init must to know the size of voice buffer so init voice first */
- talk_init();
#endif /* CONFIG_CODEC != SWCODEC */
CHART(">audio_init");
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c
index c5758d1274..e88317aeab 100644
--- a/apps/menus/main_menu.c
+++ b/apps/menus/main_menu.c
@@ -182,7 +182,7 @@ static const char* info_getname(int selected_item, void *data,
case INFO_BUFFER: /* buffer */
{
- long kib = buffer_available() / 1024; /* to KiB */
+ long kib = audio_buffer_available() / 1024; /* to KiB */
output_dyn_value(s1, sizeof(s1), kib, kbyte_units, true);
snprintf(buffer, buffer_len, "%s %s", str(LANG_BUFFER_STAT), s1);
}
@@ -272,7 +272,7 @@ static int info_speak_item(int selected_item, void * data)
case INFO_BUFFER: /* buffer */
{
talk_id(LANG_BUFFER_STAT, false);
- long kib = buffer_available() / 1024; /* to KiB */
+ long kib = audio_buffer_available() / 1024; /* to KiB */
output_dyn_value(NULL, 0, kib, kbyte_units, true);
break;
}
diff --git a/apps/mpeg.c b/apps/mpeg.c
index 6dd55b741c..595f943545 100644
--- a/apps/mpeg.c
+++ b/apps/mpeg.c
@@ -35,7 +35,7 @@
#include "thread.h"
#include "errno.h"
#include "mp3data.h"
-#include "buffer.h"
+#include "core_alloc.h"
#include "mp3_playback.h"
#include "talk.h"
#include "sound.h"
@@ -145,7 +145,8 @@ static unsigned int mpeg_errno;
static bool playing = false; /* We are playing an MP3 stream */
static bool is_playing = false; /* We are (attempting to) playing MP3 files */
static bool paused; /* playback is paused */
-static char* mpeg_audiobuf; /* the audio buffer */
+static int audiobuf_handle; /* handle to the audio buffer */
+static char* mpeg_audiobuf; /* poiunter to the audio buffer */
static long audiobuflen; /* length of the audio buffer */
#ifdef SIMULATOR
@@ -491,15 +492,27 @@ unsigned long mpeg_get_last_header(void)
#endif /* !SIMULATOR */
}
-
+/* Buffer must not move. And not shrink for now */
+static struct buflib_callbacks ops = { NULL, NULL };
unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
{
(void)talk_buf; /* always grab the voice buffer for now */
- audio_hard_stop();
if (buffer_size) /* special case for talk_init() */
- return buffer_get_buffer(buffer_size);
- return NULL;
+ {
+ size_t bufsize;
+ audio_hard_stop();
+ /* audio_hard_stop() frees audiobuf, so re-aquire */
+ audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
+ audiobuflen = bufsize;
+ *buffer_size = audiobuflen;
+ }
+ mpeg_audiobuf = core_get_data(audiobuf_handle);
+
+ if (!buffer_size) /* special case for talk_init() */
+ talkbuf_init(mpeg_audiobuf);
+
+ return mpeg_audiobuf;
}
@@ -2659,17 +2672,26 @@ void audio_set_recording_options(struct audio_recording_options *options)
#endif /* SIMULATOR */
#endif /* CONFIG_CODEC == MAS3587F */
+size_t audio_buffer_available(void)
+{
+ if (audiobuf_handle > 0)
+ return audiobuflen;
+ return core_available();
+}
+
static void audio_reset_buffer(void)
{
- size_t bufsize; /* dont break strict-aliasing */
talk_buffer_steal(); /* will use the mp3 buffer */
- /* release buffer on behalf of any audio_get_buffer() caller,
- * non-fatal if there was none */
- buffer_release_buffer(0);
- /* re-aquire */
- mpeg_audiobuf = buffer_get_buffer(&bufsize);
- audiobuflen = bufsize;
+ /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */
+ if (!audiobuf_handle)
+ {
+ size_t bufsize; /* dont break strict-aliasing */
+ audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
+ mpeg_audiobuf = core_get_data(audiobuf_handle);
+ audiobuflen = bufsize;
+ }
+ talkbuf_init(mpeg_audiobuf);
}
void audio_play(long offset)
@@ -2742,7 +2764,11 @@ void audio_hard_stop(void)
audio_stop();
/* tell voice we obtain the buffer before freeing */
talk_buffer_steal();
- buffer_release_buffer(0);
+ if (audiobuf_handle > 0)
+ {
+ audiobuf_handle = core_free(audiobuf_handle);
+ mpeg_audiobuf = NULL;
+ }
}
void audio_pause(void)
@@ -2899,13 +2925,15 @@ void audio_init(void)
mpeg_errno = 0;
/* cuesheet support */
if (global_settings.cuesheet)
- curr_cuesheet = (struct cuesheet*)buffer_alloc(sizeof(struct cuesheet));
+ {
+ int handle = core_alloc("cuesheet", sizeof(struct cuesheet));
+ if (handle > 0)
+ curr_cuesheet = core_get_data(handle);
+ }
+
+ talk_init();
+ audio_reset_buffer();
- size_t bufsize; /* don't break strict-aliasing */
- mpeg_audiobuf = buffer_get_buffer(&bufsize);
- audiobuflen = bufsize;
- /* give voice buffer until we start to play */
- talkbuf_init(mpeg_audiobuf);
#ifndef SIMULATOR
queue_init(&mpeg_queue, true);
#endif /* !SIMULATOR */
diff --git a/apps/playback.c b/apps/playback.c
index 7dad08644a..e35d652ffb 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"
@@ -732,6 +732,18 @@ static void scratch_mem_init(void *mem)
}
}
+/* Buffer must not move. And not shrink for now */
+static struct buflib_callbacks ops = { NULL, NULL };
+static int audiobuf_handle;
+static size_t filebuflen;
+
+size_t audio_buffer_available(void)
+{
+ if (audiobuf_handle > 0) /* if allocated return what we got */
+ return filebuflen;
+ return core_available();
+}
+
/* Set up the audio buffer for playback */
static void audio_reset_buffer(void)
{
@@ -743,16 +755,17 @@ 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);
+ size_t allocsize;
+ if (audiobuf_handle > 0)
+ audiobuf_handle = core_free(audiobuf_handle);
+
+ audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
+ unsigned char *filebuf = core_get_data(audiobuf_handle);
/* Subtract whatever voice needs */
allocsize = talkbuf_init(filebuf);
@@ -3324,7 +3337,8 @@ void audio_hard_stop(void)
#ifdef PLAYBACK_VOICE
voice_stop();
#endif
- buffer_release_buffer(0);
+ if (audiobuf_handle > 0)
+ audiobuf_handle = core_free(audiobuf_handle);
}
/* Resume playback if paused */
@@ -3447,6 +3461,14 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
return NULL;
}
+ /* make sure buffer is freed and re-allocated to simplify code below
+ * (audio_hard_stop() likely has done that already) */
+ if (audiobuf_handle > 0)
+ audiobuf_handle = core_free(audiobuf_handle);
+
+ audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
+ buf = core_get_data(audiobuf_handle);
+
if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED
|| !talk_voice_required())
{
@@ -3464,27 +3486,24 @@ 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);
}
else
{
+ logf("get buffer: audio");
/* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or
still AUDIOBUF_STATE_INITIALIZED */
/* Skip talk buffer and move pcm buffer to end to maximize available
contiguous memory - no audio running means voice will not need the
swap space */
- 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);
+ size_t talkbuf_size;
buf += talkbuf_size = talkbuf_init(buf);
- siz -= talkbuf_size;
- siz -= voicebuf_init(buf + siz);
- *buffer_size = siz;
+ filebuflen -= talkbuf_size;
+ filebuflen -= voicebuf_init(buf + filebuflen);
buffer_state = AUDIOBUF_STATE_VOICED_ONLY;
}
+ *buffer_size = filebuflen;
return buf;
}
@@ -3492,11 +3511,8 @@ unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
/* Stop audio, voice and obtain all available buffer space */
unsigned char * audio_get_recording_buffer(size_t *buffer_size)
{
- talk_buffer_steal();
audio_hard_stop();
-
- 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 3d3b5f44f8..77d8141af8 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"
@@ -1929,6 +1929,7 @@ static int rotate_index(const struct playlist_info* playlist, int index)
*/
void playlist_init(void)
{
+ int handle;
struct playlist_info* playlist = &current_playlist;
mutex_init(&current_playlist_mutex);
@@ -1940,18 +1941,19 @@ 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("playlist idx", playlist->max_playlist_size * sizeof(int));
+ 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("playlist buf", playlist->buffer_size);
+ playlist->buffer = core_get_data(handle);
playlist->control_mutex = &current_playlist_mutex;
empty_playlist(playlist, true);
#ifdef HAVE_DIRCACHE
- playlist->filenames = buffer_alloc(
- playlist->max_playlist_size * sizeof(int));
+ handle = core_alloc("playlist dc", playlist->max_playlist_size * sizeof(int));
+ 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),
@@ -3356,7 +3358,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;
char* old_buffer = NULL;
size_t old_buffer_size = 0;
@@ -3391,10 +3392,6 @@ int playlist_save(struct playlist_info* playlist, char *filename)
}
}
- /* 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;
@@ -3453,7 +3450,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)
{
@@ -3498,7 +3495,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 d994f6e800..f14b5c6460 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 6ed9bbb037..a6307d5dd7 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,16 @@ static void write_cache(void)
if(fd >= 0)
{
logf("SCROBBLER: writing %d entries", cache_pos);
-
+ /* copy data to temporary storage in case data moves during I/O */
+ char temp_buf[SCROBBLER_CACHE_LEN];
for ( i=0; i < cache_pos; i++ )
{
logf("SCROBBLER: write %d", i);
- fdprintf(fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i));
+ char* scrobbler_buf = core_get_data(scrobbler_cache);
+ ssize_t len = strlcpy(temp_buf, scrobbler_buf+(SCROBBLER_CACHE_LEN*i),
+ sizeof(temp_buf));
+ if (write(fd, temp_buf, len) != len)
+ break;
}
close(fd);
}
@@ -170,6 +175,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 +184,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 +196,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 +254,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("scrobbler", 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 52e059a04a..753675f906 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"
@@ -3082,6 +3082,7 @@ static bool commit(void)
return true;
}
+static int tempbuf_handle;
static void allocate_tempbuf(void)
{
/* Yeah, malloc would be really nice now :) */
@@ -3089,7 +3090,8 @@ static void allocate_tempbuf(void)
tempbuf_size = 32*1024*1024;
tempbuf = malloc(tempbuf_size);
#else
- tempbuf = buffer_get_buffer(&tempbuf_size);
+ tempbuf_handle = core_alloc_maximum("tc tempbuf", &tempbuf_size, NULL);
+ tempbuf = core_get_data(tempbuf_handle);
#endif
}
@@ -3101,7 +3103,7 @@ static void free_tempbuf(void)
#ifdef __PCTOOL__
free(tempbuf);
#else
- buffer_release_buffer(0);
+ tempbuf_handle = core_free(tempbuf_handle);
#endif
tempbuf = NULL;
tempbuf_size = 0;
@@ -3829,9 +3831,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 *);
- ramcache_hdr = buffer_alloc(tc_stat.ramcache_allocated + 128);
+ int handle = core_alloc("tc ramcache", tc_stat.ramcache_allocated);
+ ramcache_hdr = core_get_data(handle);
memset(ramcache_hdr, 0, sizeof(struct ramcache_header));
memcpy(&current_tcmh, &tcmh, sizeof current_tcmh);
logf("tagcache: %d bytes allocated.", tc_stat.ramcache_allocated);
@@ -3845,7 +3848,7 @@ static bool tagcache_dumpload(void)
struct statefile_header shdr;
int fd, rc;
long offpos;
- int i;
+ int i, handle;
fd = open(TAGCACHE_STATEFILE, O_RDONLY);
if (fd < 0)
@@ -3855,7 +3858,6 @@ static bool tagcache_dumpload(void)
}
/* Check the statefile memory placement */
- ramcache_hdr = buffer_alloc(0);
rc = read(fd, &shdr, sizeof(struct statefile_header));
if (rc != sizeof(struct statefile_header)
|| shdr.magic != TAGCACHE_STATEFILE_MAGIC
@@ -3867,13 +3869,14 @@ static bool tagcache_dumpload(void)
return false;
}
- offpos = (long)ramcache_hdr - (long)shdr.hdr;
/* Lets allocate real memory and load it */
- ramcache_hdr = buffer_alloc(shdr.tc_stat.ramcache_allocated);
+ handle = core_alloc("tc ramcache", shdr.tc_stat.ramcache_allocated);
+ ramcache_hdr = core_get_data(handle);
rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated);
close(fd);
+ offpos = (long)ramcache_hdr - (long)shdr.hdr;
if (rc != shdr.tc_stat.ramcache_allocated)
{
logf("read failure!");
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 5012c084d0..0d4330bac8 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 = ALIGN_UP(size, sizeof(void*));
+ tagtree_buf_used += size;
+ return buf;
}
static void* tagtree_alloc0(size_t size)
@@ -1035,6 +1040,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
@@ -1046,6 +1052,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 0c3b769d82..29657385a7 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -27,7 +27,6 @@
#include <stddef.h>
#include "string-extra.h"
#include "file.h"
-#include "buffer.h"
#include "system.h"
#include "kernel.h"
#include "settings.h"
@@ -711,7 +710,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 = audio_buffer_available();
if (voicefile_size <= audiobufsz) {
has_voicefile = true;
} else {
diff --git a/apps/tdspeed.c b/apps/tdspeed.c
index b940c928fc..476995a271 100644
--- a/apps/tdspeed.c
+++ b/apps/tdspeed.c
@@ -25,7 +25,7 @@
#include <stdio.h>
#include <string.h>
#include "sound.h"
-#include "buffer.h"
+#include "core_alloc.h"
#include "system.h"
#include "tdspeed.h"
#include "settings.h"
@@ -56,21 +56,32 @@ static int32_t *outbuf[2] = { NULL, NULL };
void tdspeed_init(void)
{
+ int handle;
+
if (!global_settings.timestretch_enabled)
return;
/* Allocate buffers */
if (overlap_buffer[0] == NULL)
- overlap_buffer[0] = (int32_t *)buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t));
-
+ {
+ handle = core_alloc("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t));
+ 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("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t));
+ 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("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t));
+ outbuf[0] = core_get_data(handle);
+ }
if (outbuf[1] == NULL)
- outbuf[1] = (int32_t *)buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t));
+ {
+ handle = core_alloc("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t));
+ outbuf[1] = core_get_data(handle);
+ }
}
diff --git a/apps/tree.c b/apps/tree.c
index e981aeecfc..211ddb2f9b 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"
@@ -1002,6 +1002,7 @@ int rockbox_browse(struct browse_context *browse)
void tree_mem_init(void)
{
/* initialize tree context struct */
+ int handle;
struct tree_cache* cache = &tc.cache;
memset(&tc, 0, sizeof(tc));
tc.dirfilter = &global_settings.dirfilter;
@@ -1009,10 +1010,12 @@ void tree_mem_init(void)
cache->name_buffer_size = AVERAGE_FILENAME_LENGTH *
global_settings.max_files_in_dir;
- cache->name_buffer = buffer_alloc(cache->name_buffer_size);
+ handle = core_alloc("tree names", cache->name_buffer_size);
+ cache->name_buffer = core_get_data(handle);
cache->max_entries = global_settings.max_files_in_dir;
- cache->entries = buffer_alloc(cache->max_entries*(sizeof(struct entry)));
+ handle = core_alloc("tree entries", cache->max_entries*(sizeof(struct entry)));
+ cache->entries = core_get_data(handle);
tree_get_filetypes(&filetypes, &filetypes_count);
}
diff --git a/firmware/SOURCES b/firmware/SOURCES
index f685ed7dc7..4517c37e7f 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 0000000000..51cf86bf5b
--- /dev/null
+++ b/firmware/buflib.c
@@ -0,0 +1,777 @@
+/***************************************************************************
+* __________ __ ___.
+* 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) 2011 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.
+*
+****************************************************************************/
+
+#include <stdlib.h> /* for abs() */
+#include <stdio.h> /* for snprintf() */
+#include "buflib.h"
+#include "string-extra.h" /* strlcpy() */
+#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 blocks have a positiv length marker,
+ * and additional metadata forllowing that: It follows a pointer
+ * (union buflib_data*) to the corresponding handle table entry. so that it can
+ * be quickly found and updated during compaction. After that follows
+ * the pointer to the struct buflib_callbacks associated with this allocation
+ * (may be NULL). That pointer follows a variable length character array
+ * containing the nul-terminated string identifier of the allocation. After this
+ * array there's a length marker for the length of the character array including
+ * this length marker (counted in n*sizeof(union buflib_data)), which allows
+ * to find the start of the character array (and therefore the start of the
+ * entire block) when only the handle or payload start is known.
+ *
+ * Example:
+ * |<- alloc block #1 ->|<- unalloc block ->|<- alloc block #2 ->|<-handle table->|
+ * |L|H|C|cccc|L2|XXXXXX|-L|YYYYYYYYYYYYYYYY|L|H|C|cc|L2|XXXXXXXXXXXXX|AAA|
+ *
+ * L - length marker (negative if block unallocated)
+ * H - handle table enry pointer
+ * C - pointer to struct buflib_callbacks
+ * c - variable sized string identifier
+ * L2 - second length marker for string identifier
+ * X - actual payload
+ * Y - unallocated space
+ *
+ * A - pointer to start of payload (first X) in the handle table (may be null)
+ *
+ * The blocks can be walked by jumping the abs() of the L length marker, i.e.
+ * union buflib_data* L;
+ * for(L = start; L < end; L += abs(L->val)) { .... }
+ *
+ *
+ * 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.
+ */
+
+#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 <stdio.h>
+ #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.
+ *
+ * Returns false if moving was unsucessful
+ * (NULL callback or BUFLIB_CB_CANNOT_MOVE was returned)
+ */
+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 */
+ if (ops)
+ {
+ if (ops->move_callback(handle, tmp->alloc, new_start)
+ == BUFLIB_CB_CANNOT_MOVE)
+ return false;
+ }
+ 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 *block;
+ int shift = 0, len;
+ /* Store the results of attempting to shrink the handle table */
+ bool ret = handle_table_shrink(ctx);
+ for(block = ctx->first_free_block; 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 (-ctx->first_free_block->val > block->val)
+ {
+ intptr_t size = ctx->first_free_block->val;
+ if (move_block(ctx, block, ctx->first_free_block - block))
+ {
+ /* moving was successful. Mark the next block as the new
+ * first_free_block and merge it with the free space
+ * that the move created */
+ 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;
+ }
+ /* if move was successful, the just moved block is now
+ * possibly in place of the first free one, so move this thing up */
+ else if (ctx->first_free_block == block+shift)
+ {
+ ctx->first_free_block += ctx->first_free_block->val;
+ ctx->first_free_block->val = shift;
+ }
+ }
+ }
+ /* 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, "<anonymous>", NULL);
+}
+
+/* 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 (which do nothing but don't
+ * prevent moving or shrinking)
+ */
+
+int
+buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name,
+ struct buflib_callbacks *ops)
+{
+ 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 */
+ unsigned hint = BUFLIB_SHRINK_POS_FRONT |
+ ((size*sizeof(union buflib_data))&BUFLIB_SHRINK_SIZE_MASK);
+ if (buflib_compact_and_shrink(ctx, hint))
+ {
+ 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;
+ 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;
+}
+
+/* Finds the free block before block, and returns NULL if it's not free */
+static union buflib_data*
+find_free_block_before(struct buflib_context *ctx, union buflib_data* block)
+{
+ union buflib_data *ret = ctx->first_free_block,
+ *next_block = ret;
+
+ /* find the block that's before the current one */
+ while (next_block < block)
+ {
+ ret = next_block;
+ next_block += abs(ret->val);
+ }
+
+ /* If next_block == block, the above loop didn't go anywhere. If it did,
+ * and the block before this one is empty, that is the wanted one
+ */
+ if (next_block == block && ret < block && ret->val < 0)
+ return ret;
+ /* otherwise, e.g. if ret > block, or if the buffer is compact,
+ * there's no free block before */
+ return NULL;
+}
+
+/* Free the buffer associated with handle_num. */
+int
+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, *next_block;
+ /* We need to find the block before the current one, to see if it is free
+ * and can be merged with this one.
+ */
+ block = find_free_block_before(ctx, freed_block);
+ if (block)
+ {
+ block->val -= freed_block->val;
+ }
+ else
+ {
+ /* 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.
+ */
+ 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;
+
+ return 0; /* unconditionally */
+}
+
+/* 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*/
+ ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - 5);
+ diff -= 16; /* space for future handles */
+ 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)
+{
+ /* 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));
+
+ return buflib_alloc_ex(ctx, *size, buf, ops);
+}
+
+/* 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;
+ /* find the block before in order to merge with the new free space */
+ union buflib_data *free_before = find_free_block_before(ctx, block);
+ if (free_before)
+ free_before->val += block->val;
+ else if (ctx->first_free_block > block)
+ ctx->first_free_block = 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;
+ }
+
+ 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;
+}
+
+#ifdef BUFLIB_DEBUG_BLOCKS
+void buflib_print_allocs(struct buflib_context *ctx,
+ void (*print)(int, 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);
+ /* handle_num is 1-based */
+ print(handle_num - 1, buf);
+ }
+}
+
+void buflib_print_blocks(struct buflib_context *ctx,
+ void (*print)(int, const char*))
+{
+ char buf[128];
+ int i = 0;
+ for(union buflib_data* this = ctx->buf_start;
+ this < ctx->alloc_end;
+ this += abs(this->val))
+ {
+ snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)",
+ this, this->val,
+ this->val > 0? this[3].name:"<unallocated>");
+ print(i++, buf);
+ }
+}
+#endif
+
+#ifdef BUFLIB_DEBUG_BLOCK_SINGLE
+int buflib_get_num_blocks(struct buflib_context *ctx)
+{
+ int i = 0;
+ for(union buflib_data* this = ctx->buf_start;
+ this < ctx->alloc_end;
+ this += abs(this->val))
+ {
+ i++;
+ }
+ return i;
+}
+
+void buflib_print_block_at(struct buflib_context *ctx, int block_num,
+ char* buf, size_t bufsize)
+{
+ union buflib_data* this = ctx->buf_start;
+ while(block_num > 0 && this < ctx->alloc_end)
+ {
+ this += abs(this->val);
+ block_num -= 1;
+ }
+ snprintf(buf, bufsize, "%8p: val: %4ld (%s)",
+ this, this->val,
+ this->val > 0? this[3].name:"<unallocated>");
+}
+
+#endif
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index f47e65e428..334801ce57 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 {
@@ -571,7 +573,8 @@ int dircache_load(void)
}
allocated_size = maindata.size + DIRCACHE_RESERVE;
- dircache_root = buffer_alloc(allocated_size);
+ int handle = core_alloc("dircache", allocated_size);
+ dircache_root = core_get_data(handle);
/* needs to be struct-size aligned so that the pointer arithmetic below works */
ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry));
entry_count = maindata.entry_count;
@@ -814,6 +817,7 @@ static void generate_dot_d_names(void)
strcpy(dot, ".");
strcpy(dotdot, "..");
}
+
/**
* Start scanning the disk to build the dircache.
* Either transparent or non-transparent build method is used.
@@ -841,11 +845,13 @@ 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);
+ handle = core_alloc("dircache", allocated_size);
+ 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;
@@ -863,7 +869,8 @@ 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);
+ int handle = core_alloc_maximum("dircache", &got_size, NULL);
+ char* buf = core_get_data(handle);
dircache_root = (struct dircache_entry*)ALIGN_UP(buf,
sizeof(struct dircache_entry));
d_names_start = d_names_end = buf + got_size - 1;
@@ -902,11 +909,11 @@ int dircache_build(int last_size)
allocated_size = (d_names_end - buf);
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;
}
@@ -942,7 +949,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 0000000000..75dfc75b86
--- /dev/null
+++ b/firmware/core_alloc.c
@@ -0,0 +1,57 @@
+
+#include <string.h>
+#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);
+}
+
+int core_free(int handle)
+{
+ return 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);
+}
+
+int core_get_num_blocks(void)
+{
+ return buflib_get_num_blocks(&core_ctx);
+}
+
+void core_print_block_at(int block_num, char* buf, size_t bufsize)
+{
+ buflib_print_block_at(&core_ctx, block_num, buf, bufsize);
+}
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 2835d8f4c4..57f3c24aae 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -58,6 +58,7 @@ void audio_resume(void);
void audio_next(void);
void audio_prev(void);
int audio_status(void);
+size_t audio_buffer_available(void);
void audio_ff_rewind(long newpos);
void audio_flush_and_reload_tracks(void);
struct mp3entry* audio_current_track(void);
diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h
new file mode 100644
index 0000000000..db7b5ec50a
--- /dev/null
+++ b/firmware/include/buflib.h
@@ -0,0 +1,319 @@
+/***************************************************************************
+* __________ __ ___.
+* 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) 2011 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 <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/* enable single block debugging */
+#define BUFLIB_DEBUG_BLOCK_SINGLE
+
+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;
+ 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 cached
+ * pointers pointing to inside the allocation. The size is unchanged.
+ *
+ * This is not needed if you don't cache the data pointer (but always
+ * call buflib_get_data()) and don't pass pointer to the data to yielding
+ * functions.
+ *
+ * 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, or BUFLIB_CB_CANNOT_MOVE if movement
+ * is impossible at this moment.
+ *
+ * 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 buffer
+ * in order to satisfy new allocation. This happens when buflib runs
+ * out of memory, e.g. because buflib_alloc_maximum() was called.
+ * Move data around as you need to make space and call core_shrink() as
+ * appropriate from within the callback to complete the shrink operation.
+ * buflib will not move data as part of shrinking.
+ *
+ * hint: bit mask containing hints on how shrinking is desired (see below)
+ * 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 moving failed. Buflib may retry to move at any point */
+#define BUFLIB_CB_CANNOT_MOVE 1
+/* Tell buflib that resizing failed, possibly future making allocations fail */
+#define BUFLIB_CB_CANNOT_SHRINK 1
+
+/**
+ * Initializes buflib with a caller allocated context instance and memory pool.
+ *
+ * The buflib_context instance needs to be passed to every other buflib
+ * function. It's should be considered opaque, even though it is not yet
+ * (that's to make inlining core_get_data() possible). The documentation
+ * of the other functions will not describe the context
+ * instance paramter further as it's obligatory.
+ *
+ * context: The new buflib instance to be initialized, allocated by the caller
+ * size: The size of the 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.
+ *
+ * This function does not yet consider possible compaction so there might
+ * be more space left. This may change in the future.
+ *
+ * Returns: The number of bytes left in the memory pool.
+ */
+size_t buflib_available(struct buflib_context *ctx);
+
+
+/**
+ * Allocates memory from buflib's memory pool
+ *
+ * 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
+ * ops: a struct with pointers to callback functions (see above)
+ *
+ * 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.
+ *
+ * Since this effectively makes all future allocations fail (unless
+ * another allocation is freed in the meantime), you should definitely provide
+ * a shrink callback if you plan to hold the buffer for a longer period. This
+ * will allow buflib to permit allocations by shrinking the buffer returned by
+ * this function.
+ *
+ * Note that this currently gives whatever buflib_available() returns. However,
+ * do not depend on this behavior, it may change.
+ *
+ * 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);
+
+/**
+ * Queries the data pointer for the given handle. It's actually a cheap
+ * operation, 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 above).
+ *
+ * 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);
+}
+
+/**
+ * Shrink the memory allocation associated with the given handle
+ * Mainly intended to be used with the shrink callback, but it can also
+ * be called outside as well, e.g. to give back buffer space allocated
+ * with buflib_alloc_maximum().
+ *
+ * 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.
+ *
+ */
+bool buflib_shrink(struct buflib_context *ctx, int handle, void* newstart, size_t new_size);
+
+/**
+ * Frees memory associated with the given handle
+ *
+ * Returns: 0 (to invalidate handles in one line)
+ */
+int buflib_free(struct buflib_context *context, int handle);
+
+/**
+ * Moves the underlying buflib buffer up by size bytes (as much as
+ * possible for size == 0) without moving the end. This effectively
+ * reduces the available space by taking away managable space from the
+ * front. This space is not available for new allocations anymore.
+ *
+ * To make space available in the front, everything is moved up.
+ * It does _NOT_ call the move callbacks
+ *
+ *
+ * size: size in bytes to move the buffer up (take away). The actual
+ * bytes moved is returned in this
+ * Returns: The new start of the underlying buflib buffer
+ */
+void* buflib_buffer_out(struct buflib_context *ctx, size_t *size);
+
+/**
+ * Moves the underlying buflib buffer down by size bytes without
+ * moving the end. This grows the buflib buffer by adding space to the front.
+ * The new bytes are available for new allocations.
+ *
+ * Everything is moved down, and the new free space will be in the middle.
+ * It does _NOT_ call the move callbacks.
+ *
+ * size: size in bytes to move the buffer down (new free space)
+ */
+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
+ *
+ * This walks only the handle table and prints only valid allocations
+ *
+ * Only available if BUFLIB_DEBUG_BLOCKS is defined
+ */
+void buflib_print_allocs(struct buflib_context *ctx, void (*print)(int, const char*));
+
+/**
+ * Prints an overview of all blocks in the buflib buffer, allocated
+ * or unallocated, with the help pf the passted printer helper
+ *
+ * This walks the entire buffer and prints unallocated space also.
+ * The output is also different from buflib_print_allocs().
+ *
+ * Only available if BUFLIB_DEBUG_BLOCKS is defined
+ */
+void buflib_print_blocks(struct buflib_context *ctx, void (*print)(int, const char*));
+
+/**
+ * Gets the number of blocks in the entire buffer, allocated or unallocated
+ *
+ * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined
+ */
+int buflib_get_num_blocks(struct buflib_context *ctx);
+
+/**
+ * Print information about a single block as indicated by block_num
+ * into buf
+ *
+ * buflib_get_num_blocks() beforehand to get the total number of blocks,
+ * as passing an block_num higher than that is undefined
+ *
+ * Only available if BUFLIB_DEBUG_BLOCK_SIGNLE is defined
+ */
+void buflib_print_block_at(struct buflib_context *ctx, int block_num,
+ char* buf, size_t bufsize);
+#endif
diff --git a/firmware/include/core_alloc.h b/firmware/include/core_alloc.h
new file mode 100644
index 0000000000..f5206c9db9
--- /dev/null
+++ b/firmware/include/core_alloc.h
@@ -0,0 +1,36 @@
+
+#ifndef __CORE_ALLOC_H__
+#define __CORE_ALLOC_H__
+#include <string.h>
+#include <stdbool.h>
+#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);
+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);
+int core_free(int handle);
+size_t core_available(void);
+
+/* DO NOT ADD wrappers for buflib_buffer_out/in. They do not call
+ * the move callbacks and are therefore unsafe in the core */
+
+#ifdef BUFLIB_DEBUG_BLOCKS
+void core_print_allocs(void (*print)(const char*));
+void core_print_blocks(void (*print)(const char*));
+#endif
+#ifdef BUFLIB_DEBUG_BLOCK_SINGLE
+int core_get_num_blocks(void);
+void core_print_block_at(int block_num, char* buf, size_t bufsize);
+#endif
+
+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 9b6f4fec4a..283779d7ee 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);
+ rolo_handle = 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/arm/ata-nand-telechips.c b/firmware/target/arm/ata-nand-telechips.c
index 81dde33938..2ae425f4c6 100644
--- a/firmware/target/arm/ata-nand-telechips.c
+++ b/firmware/target/arm/ata-nand-telechips.c
@@ -26,7 +26,6 @@
#include "panic.h"
#include "nand_id.h"
#include "storage.h"
-#include "buffer.h"
#define SECTOR_SIZE 512
@@ -122,8 +121,9 @@ struct lpt_entry
#ifdef BOOTLOADER
static struct lpt_entry lpt_lookup[MAX_SEGMENTS];
#else
-/* buffer_alloc'd in nand_init() when the correct size has been determined */
-static struct lpt_entry* lpt_lookup = NULL;
+/* core_alloc()'d in nand_init() when the correct size has been determined */
+#include "core_alloc.h"
+static int lpt_handle;
#endif
/* Write Caches */
@@ -607,6 +607,9 @@ static bool nand_read_sector_of_logical_segment(int log_segment, int sector,
int page_in_segment = sector / sectors_per_page;
int sector_in_page = sector % sectors_per_page;
+#ifndef BOOTLOADER
+ struct lpt_entry* lpt_lookup = core_get_data(lpt_handle);
+#endif
int bank = lpt_lookup[log_segment].bank;
int phys_segment = lpt_lookup[log_segment].phys_segment;
@@ -918,7 +921,8 @@ int nand_init(void)
#ifndef BOOTLOADER
/* Use chip info to allocate the correct size LPT buffer */
lptbuf_size = sizeof(struct lpt_entry) * segments_per_bank * total_banks;
- lpt_lookup = buffer_alloc(lptbuf_size);
+ lpt_handle = core_alloc("lpt lookup", lptbuf_size);
+ struct lpt_entry* lpt_lookup = core_get_data(lpt_handle);
#else
/* Use a static array in the bootloader */
lptbuf_size = sizeof(lpt_lookup);
@@ -968,6 +972,9 @@ int nand_init(void)
if (log_segment < segments_per_bank * total_banks)
{
+#ifndef BOOTLOADER
+ lpt_lookup = core_get_data(lpt_handle);
+#endif
if (lpt_lookup[log_segment].bank == -1 ||
lpt_lookup[log_segment].phys_segment == -1)
{
diff --git a/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c b/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c
index afb8d5cf62..ad10502f2d 100644
--- a/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c
+++ b/firmware/target/arm/tms320dm320/creative-zvm/ata-creativezvm.c
@@ -30,7 +30,7 @@
#include "dm320.h"
#include "ata.h"
#include "string.h"
-#include "buffer.h"
+#include "core_alloc.h"
#include "logf.h"
#include "ata-defines.h"
@@ -202,7 +202,11 @@ struct cfs_direntry_item
static bool cfs_inited = false;
static unsigned long cfs_start;
+#ifdef BOOTLOADER
static unsigned long *sectors;
+#else
+static int sectors_handle;
+#endif
#define CFS_START ( ((hdr->partitions[1].start*hdr->sector_size) & ~0xFFFF) + 0x10000 )
#define CFS_CLUSTER2CLUSTER(x) ( (CFS_START/512)+((x)-1)*64 )
@@ -299,7 +303,8 @@ static void cfs_init(void)
_ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_inodes_nr[1]), 1, &sector);
inode = (struct cfs_inode*)&sector;
#ifndef BOOTLOADER
- sectors = (unsigned long*)buffer_alloc(VFAT_SECTOR_SIZE(inode->filesize));
+ sectors_handle = core_alloc("ata sectors", VFAT_SECTOR_SIZE(inode->filesize));
+ unsigned long *sectors = core_get_data(sectors_handle);
#else
static unsigned long _sector[VFAT_SECTOR_SIZE(1024*1024*1024)]; /* 1GB guess */
sectors = _sector;
@@ -322,6 +327,9 @@ static void cfs_init(void)
_ata_read_sectors(CFS_CLUSTER2CLUSTER(inode->second_class_chain_second_cluster), 64, &vfat_data[1]);
/* First class chain */
+#ifndef BOOTLOADER
+ sectors = core_get_data(sectors_handle);
+#endif
for(j=0; j<12; j++)
{
if( (inode->first_class_chain[j] & 0xFFFF) != 0xFFFF &&
@@ -331,6 +339,9 @@ static void cfs_init(void)
}
/* Second class chain */
+#ifndef BOOTLOADER
+ sectors = core_get_data(sectors_handle);
+#endif
for(j=0; j<0x8000/4; j++)
{
if( (vfat_data[0][j] & 0xFFFF) != 0xFFFF &&
@@ -351,6 +362,9 @@ static void cfs_init(void)
/* Read third class subchain(s) */
_ata_read_sectors(CFS_CLUSTER2CLUSTER(vfat_data[1][j]), 64, &vfat_data[0]);
+#ifndef BOOTLOADER
+ sectors = core_get_data(sectors_handle);
+#endif
for(k=0; k<0x8000/4; k++)
{
if( (vfat_data[0][k] & 0xFFFF) != 0xFFFF &&
@@ -376,6 +390,9 @@ static inline unsigned long map_sector(unsigned long sector)
* Sector mapping: start of CFS + FAT_SECTOR2CFS_SECTOR(sector) + missing part
* FAT works with sectors of 0x200 bytes, CFS with sectors of 0x8000 bytes.
*/
+#ifndef BOOTLOADER
+ unsigned long *sectors = core_get_data(sectors_handle);
+#endif
return cfs_start+sectors[sector/64]*64+sector%64;
}