diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES index 9f112dd..45a189a 100644 --- a/apps/plugins/lib/SOURCES +++ b/apps/plugins/lib/SOURCES @@ -4,6 +4,7 @@ configfile.c fixedpoint.c playback_control.c rgb_hsv.c +buffer_alloc.c #if defined(HAVE_LCD_BITMAP) && (LCD_DEPTH < 4) /* The scaler is not provided in core on mono targets, but is built in diff --git a/apps/plugins/lib/buffer_alloc.c b/apps/plugins/lib/buffer_alloc.c new file mode 100644 index 0000000..51fad6c --- /dev/null +++ b/apps/plugins/lib/buffer_alloc.c @@ -0,0 +1,193 @@ +#include "buffer_alloc.h" + +#define ABS(x) \ +({ \ + typeof(x) xtmp_abs_ = x; \ + xtmp_abs_ = xtmp_abs_ < 0 ? -xtmp_abs_ : xtmp_abs_; \ + xtmp_abs_; \ +}) + +struct buffer_context global_context; + +/* Initialize buffer manager */ +void +buf_init_ctx(struct buffer_context *ctx, void *buf, size_t size) +{ + union buffer_data *bd_buf = buf; + + ALIGN_BUFFER(bd_buf, size, sizeof(union buffer_data)); + size /= sizeof(union buffer_data); + 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->alloc_end = bd_buf; +} + +/* Allocate a new handle, returning 0 on failure */ +static inline +union buffer_data* handle_alloc_ctx(struct buffer_context *ctx) +{ + union buffer_data *handle; + for (handle = ctx->first_free_handle; handle >= ctx->last_handle; handle--) + if (!handle->ptr) + break; + if (handle < ctx->last_handle) + { + if (handle >= ctx->alloc_end) + ctx->last_handle--; + else + return NULL; + } + return handle; +} + +/* Free one handle */ +static inline +void handle_free_ctx(struct buffer_context *ctx, union buffer_data *handle) +{ + handle->ptr = 0; + if (handle > ctx->first_free_handle) + ctx->first_free_handle = handle; + if (handle == ctx->last_handle) + ctx->last_handle++; +} + +/* Shrink the handle table */ +static inline +bool +handle_table_shrink_ctx(struct buffer_context *ctx) +{ + bool rv; + union buffer_data *handle; + for (handle = ctx->last_handle; !(handle->ptr); handle++); + if (handle > ctx->first_free_handle) + ctx->first_free_handle = handle - 1; + rv = handle == ctx->last_handle; + ctx->last_handle = handle; + return rv; +} + +/* Compact allocations and handle table, adjusting handle pointers as needed */ +static bool +buf_compact_ctx(struct buffer_context *ctx) +{ + union buffer_data *block = ctx->first_free_block, *new_block; + int shift = 0, len; + bool ret = handle_table_shrink_ctx(ctx); + for(; block != ctx->alloc_end; block += len) + { + len = block->val; + if (len < 0) + { + shift += len; + len = -len; + continue; + } + if (shift) + { + new_block = block + shift; + block[1].ptr->ptr = new_block; + rb->memmove(new_block, block, len * sizeof(union buffer_data)); + } + } + ctx->alloc_end += shift; + return ret || shift; +} + +/* Allocate a buffer of size bytes, returning a handle for it */ +int +buf_alloc_ctx(struct buffer_context *ctx, size_t size) +{ + union buffer_data *handle, *block; + bool last = false; + /* This really is assigned a value before use */ + int block_len; + bool retry = true; + size = (size + sizeof(union buffer_data) - 1) / + sizeof(union buffer_data) + 2; +handle_alloc: + handle = handle_alloc_ctx(ctx); + if (!handle) + { + if (retry && buf_compact_ctx(ctx)) + { + retry = false; + goto handle_alloc; + } else + return 0; + } + +buffer_alloc: + for (block = ctx->first_free_block;; block += block_len) + { + 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; + if(block_len > 0) + continue; + block_len = -block_len; + if ((size_t)block_len >= size) + break; + } + if (!block) + { + if (retry && buf_compact_ctx(ctx)) + { + retry = false; + goto buffer_alloc; + } else { + handle_free_ctx(ctx, handle); + return 0; + } + } + + block->val = size; + block[1].ptr = handle; + handle->ptr = block + 2; + if (block == ctx->first_free_block) + ctx->first_free_block += size; + block += size; + if (last) + ctx->alloc_end = block; + else if ((size_t)block_len > size) + block->val = size - block_len; + return ctx->handle_table - handle; +} + +/* Free the buffer associated with handle_num */ +void +buf_free_ctx(struct buffer_context *ctx, int handle_num) +{ + union buffer_data *handle = ctx->handle_table - handle_num, + *freed_block = handle->ptr - 2, + *block = ctx->first_free_block, + *next_block = block; + while (next_block < freed_block) + { + block = next_block; + next_block += ABS(block->val); + } + if (next_block == freed_block && next_block != block && block->val < 0) + block->val -= freed_block->val; + else + { + block = freed_block; + block->val = -block->val; + } + next_block = block - block->val; + if (next_block == ctx->alloc_end) + ctx->alloc_end = block; + else if (next_block->val < 0) + block->val += next_block->val; + handle_free_ctx(ctx, handle); + handle->ptr = NULL; + if (block < ctx->first_free_block) + ctx->first_free_block = block; +} diff --git a/apps/plugins/lib/buffer_alloc.h b/apps/plugins/lib/buffer_alloc.h new file mode 100644 index 0000000..96c13da --- /dev/null +++ b/apps/plugins/lib/buffer_alloc.h @@ -0,0 +1,40 @@ +#include + +union buffer_data +{ + intptr_t val; + union buffer_data *ptr; +}; + +struct buffer_context +{ + union buffer_data *handle_table; + union buffer_data *first_free_handle; + union buffer_data *last_handle; + union buffer_data *first_free_block; + union buffer_data *alloc_end; +}; + +//define handle_table_len(ctx) +extern struct buffer_context global_context; + +void buf_init_ctx(struct buffer_context *context, void *buf, size_t size); +int buf_alloc_ctx(struct buffer_context *context, size_t size); +void buf_free_ctx(struct buffer_context *context, int handle); + +/* always_inline is due to this not getting inlined when not optimizing, which + * leads to an unresolved reference since it doesn't exist as a non-inline + * function + */ +extern inline __attribute__((always_inline)) +void* buf_get_data_ctx(struct buffer_context *context, int handle) +{ + struct buffer_context *ctx = (struct buffer_context*)(context); + return (void*)(ctx->handle_table[-handle].ptr); +} + +#define buf_init(...) buf_init_ctx(&global_context , ## __VA_ARGS__) +#define buf_alloc(...) buf_alloc_ctx(&global_context , ## __VA_ARGS__) +#define buf_get_data(...) buf_get_data_ctx(&global_context , ## __VA_ARGS__) +#define buf_free(...) buf_free_ctx(&global_context , ## __VA_ARGS__) +#define buf_compact(...) buf_compact_ctx(&global_context , ## __VA_ARGS__)