summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-08-30 14:01:33 +0000
committerThomas Martitz <kugel@rockbox.org>2011-08-30 14:01:33 +0000
commitd0b72e25903574acb1cf9184a6052cdd646dbc37 (patch)
tree5be8db5ee00b2a727e4821cf51a5f7bcf3991073
parentc940811ade7d99a0e0d414df7c6509672413684a (diff)
downloadrockbox-d0b72e25903574acb1cf9184a6052cdd646dbc37.tar.gz
rockbox-d0b72e25903574acb1cf9184a6052cdd646dbc37.tar.bz2
rockbox-d0b72e25903574acb1cf9184a6052cdd646dbc37.zip
GSoC/Buflib: Add buflib memory alocator to the core.
The buflib memory allocator is handle based and can free and compact, move or resize memory on demand. This allows to effeciently allocate memory dynamically without an MMU, by avoiding fragmentation through memory compaction. This patch adds the buflib library to the core, along with convinience wrappers to omit the context parameter. Compaction is not yet enabled, but will be in a later patch. Therefore, this acts as a replacement for buffer_alloc/buffer_get_buffer() with the benifit of a debug menu. See buflib.h for some API documentation. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30380 a1c6a512-1295-4272-9138-f99709370657
-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;
}