summaryrefslogtreecommitdiff
path: root/firmware/buflib.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/buflib.c')
-rw-r--r--firmware/buflib.c68
1 files changed, 62 insertions, 6 deletions
diff --git a/firmware/buflib.c b/firmware/buflib.c
index 4ffd6cfce3..43fc4bd3de 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib.c
@@ -89,6 +89,7 @@
89 #define BDEBUGF(...) do { } while(0) 89 #define BDEBUGF(...) do { } while(0)
90#endif 90#endif
91 91
92#define IS_MOVABLE(a) (!a[2].ops || a[2].ops->move_callback)
92static union buflib_data* find_first_free(struct buflib_context *ctx); 93static union buflib_data* find_first_free(struct buflib_context *ctx);
93static union buflib_data* find_block_before(struct buflib_context *ctx, 94static union buflib_data* find_block_before(struct buflib_context *ctx,
94 union buflib_data* block, 95 union buflib_data* block,
@@ -198,7 +199,7 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
198 char* new_start; 199 char* new_start;
199 union buflib_data *new_block, *tmp = block[1].handle; 200 union buflib_data *new_block, *tmp = block[1].handle;
200 struct buflib_callbacks *ops = block[2].ops; 201 struct buflib_callbacks *ops = block[2].ops;
201 if (ops && !ops->move_callback) 202 if (!IS_MOVABLE(block))
202 return false; 203 return false;
203 204
204 int handle = ctx->handle_table - tmp; 205 int handle = ctx->handle_table - tmp;
@@ -312,8 +313,10 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints)
312 result = buflib_compact(ctx); 313 result = buflib_compact(ctx);
313 if (!result) 314 if (!result)
314 { 315 {
315 union buflib_data* this; 316 union buflib_data *this, *before;
316 for(this = ctx->buf_start; this < ctx->alloc_end; this += abs(this->val)) 317 for(this = ctx->buf_start, before = this;
318 this < ctx->alloc_end;
319 before = this, this += abs(this->val))
317 { 320 {
318 if (this->val > 0 && this[2].ops 321 if (this->val > 0 && this[2].ops
319 && this[2].ops->shrink_callback) 322 && this[2].ops->shrink_callback)
@@ -322,6 +325,20 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints)
322 int handle = ctx->handle_table - this[1].handle; 325 int handle = ctx->handle_table - this[1].handle;
323 char* data = this[1].handle->alloc; 326 char* data = this[1].handle->alloc;
324 bool last = (this+this->val) == ctx->alloc_end; 327 bool last = (this+this->val) == ctx->alloc_end;
328 unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK;
329 /* adjust what we ask for if there's free space in the front
330 * this isn't too unlikely assuming this block is
331 * shrinkable but not movable */
332 if (pos_hints == BUFLIB_SHRINK_POS_FRONT
333 && before != this && before->val < 0)
334 {
335 size_t free_space = (-before->val) * sizeof(union buflib_data);
336 size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK;
337 if (wanted < free_space) /* no shrink needed? */
338 continue;
339 wanted -= free_space;
340 shrink_hints = pos_hints | wanted;
341 }
325 ret = this[2].ops->shrink_callback(handle, shrink_hints, 342 ret = this[2].ops->shrink_callback(handle, shrink_hints,
326 data, (char*)(this+this->val)-data); 343 data, (char*)(this+this->val)-data);
327 result |= (ret == BUFLIB_CB_OK); 344 result |= (ret == BUFLIB_CB_OK);
@@ -598,9 +615,8 @@ buflib_free(struct buflib_context *ctx, int handle_num)
598 return 0; /* unconditionally */ 615 return 0; /* unconditionally */
599} 616}
600 617
601/* Return the maximum allocatable memory in bytes */ 618static size_t
602size_t 619free_space_at_end(struct buflib_context* ctx)
603buflib_available(struct buflib_context* ctx)
604{ 620{
605 /* subtract 5 elements for 621 /* subtract 5 elements for
606 * val, handle, name_len, ops and the handle table entry*/ 622 * val, handle, name_len, ops and the handle table entry*/
@@ -615,6 +631,46 @@ buflib_available(struct buflib_context* ctx)
615 return 0; 631 return 0;
616} 632}
617 633
634/* Return the maximum allocatable memory in bytes */
635size_t
636buflib_available(struct buflib_context* ctx)
637{
638 union buflib_data *this;
639 size_t free_space = 0, max_free_space = 0;
640
641 /* make sure buffer is as contiguous as possible */
642 if (!ctx->compact)
643 buflib_compact(ctx);
644
645 /* now look if there's free in holes */
646 for(this = find_first_free(ctx); this < ctx->alloc_end; this += abs(this->val))
647 {
648 if (this->val < 0)
649 {
650 free_space += -this->val;
651 continue;
652 }
653 /* an unmovable section resets the count as free space
654 * can't be contigous */
655 if (!IS_MOVABLE(this))
656 {
657 if (max_free_space < free_space)
658 max_free_space = free_space;
659 free_space = 0;
660 }
661 }
662
663 /* select the best */
664 max_free_space = MAX(max_free_space, free_space);
665 max_free_space *= sizeof(union buflib_data);
666 max_free_space = MAX(max_free_space, free_space_at_end(ctx));
667
668 if (max_free_space > 0)
669 return max_free_space;
670 else
671 return 0;
672}
673
618/* 674/*
619 * Allocate all available (as returned by buflib_available()) memory and return 675 * Allocate all available (as returned by buflib_available()) memory and return
620 * a handle to it 676 * a handle to it