summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/buflib.c67
1 files changed, 40 insertions, 27 deletions
diff --git a/firmware/buflib.c b/firmware/buflib.c
index d6cbf3049b..ac8cdc864c 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib.c
@@ -89,6 +89,9 @@
89 #define BDEBUGF(...) do { } while(0) 89 #define BDEBUGF(...) do { } while(0)
90#endif 90#endif
91 91
92static union buflib_data*
93find_block_before(struct buflib_context *ctx, union buflib_data* block,
94 bool is_free);
92/* Initialize buffer manager */ 95/* Initialize buffer manager */
93void 96void
94buflib_init(struct buflib_context *ctx, void *buf, size_t size) 97buflib_init(struct buflib_context *ctx, void *buf, size_t size)
@@ -227,7 +230,7 @@ buflib_compact(struct buflib_context *ctx)
227 int shift = 0, len; 230 int shift = 0, len;
228 /* Store the results of attempting to shrink the handle table */ 231 /* Store the results of attempting to shrink the handle table */
229 bool ret = handle_table_shrink(ctx); 232 bool ret = handle_table_shrink(ctx);
230 for(block = ctx->first_free_block; block != ctx->alloc_end; block += len) 233 for(block = ctx->first_free_block; block < ctx->alloc_end; block += len)
231 { 234 {
232 len = block->val; 235 len = block->val;
233 /* This block is free, add its length to the shift value */ 236 /* This block is free, add its length to the shift value */
@@ -241,11 +244,14 @@ buflib_compact(struct buflib_context *ctx)
241 if (-ctx->first_free_block->val > block->val) 244 if (-ctx->first_free_block->val > block->val)
242 { 245 {
243 intptr_t size = ctx->first_free_block->val; 246 intptr_t size = ctx->first_free_block->val;
247 union buflib_data* next_block = block + block->val;
244 if (move_block(ctx, block, ctx->first_free_block - block)) 248 if (move_block(ctx, block, ctx->first_free_block - block))
245 { 249 {
246 /* moving was successful. Mark the next block as the new 250 /* moving was successful. Mark the next block as the new
247 * first_free_block and merge it with the free space 251 * first_free_block and merge it with the free space
248 * that the move created */ 252 * that the move created */
253 if (ctx->alloc_end == next_block)
254 ctx->alloc_end = block;
249 ctx->first_free_block += block->val; 255 ctx->first_free_block += block->val;
250 ctx->first_free_block->val = size + block->val; 256 ctx->first_free_block->val = size + block->val;
251 continue; 257 continue;
@@ -307,12 +313,16 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints)
307 int ret; 313 int ret;
308 int handle = ctx->handle_table - this[1].handle; 314 int handle = ctx->handle_table - this[1].handle;
309 char* data = this[1].handle->alloc; 315 char* data = this[1].handle->alloc;
316 bool last = (this+this->val) == ctx->alloc_end;
310 ret = this[2].ops->shrink_callback(handle, shrink_hints, 317 ret = this[2].ops->shrink_callback(handle, shrink_hints,
311 data, (char*)(this+this->val)-data); 318 data, (char*)(this+this->val)-data);
312 result |= (ret == BUFLIB_CB_OK); 319 result |= (ret == BUFLIB_CB_OK);
313 /* this might have changed in the callback (if 320 /* this might have changed in the callback (if
314 * it shrinked from the top), get it again */ 321 * it shrinked from the top), get it again */
315 this = handle_to_block(ctx, handle); 322 this = handle_to_block(ctx, handle);
323 /* could also change with shrinking from back */
324 if (last)
325 ctx->alloc_end = this + this->val;
316 } 326 }
317 } 327 }
318 /* shrinking was successful at least once, try compaction again */ 328 /* shrinking was successful at least once, try compaction again */
@@ -407,26 +417,26 @@ handle_alloc:
407 /* If allocation has failed, and compaction has succeded, it may be 417 /* If allocation has failed, and compaction has succeded, it may be
408 * possible to get a handle by trying again. 418 * possible to get a handle by trying again.
409 */ 419 */
410 if (!ctx->compact && buflib_compact(ctx)) 420 union buflib_data* last_block = find_block_before(ctx,
411 goto handle_alloc; 421 ctx->alloc_end, false);
412 else 422 struct buflib_callbacks* ops = last_block[2].ops;
413 { /* first try to shrink the alloc before the handle table 423 unsigned hints = 0;
414 * to make room for new handles */ 424 if (!ops || !ops->shrink_callback)
415 int handle = ctx->handle_table - ctx->last_handle; 425 { /* the last one isn't shrinkable
416 union buflib_data* last_block = handle_to_block(ctx, handle); 426 * make room in front of a shrinkable and move this alloc */
417 struct buflib_callbacks* ops = last_block[2].ops; 427 hints = BUFLIB_SHRINK_POS_FRONT;
418 if (ops && ops->shrink_callback) 428 hints |= last_block->val * sizeof(union buflib_data);
419 {
420 char *data = buflib_get_data(ctx, handle);
421 unsigned hint = BUFLIB_SHRINK_POS_BACK | 10*sizeof(union buflib_data);
422 if (ops->shrink_callback(handle, hint, data,
423 (char*)(last_block+last_block->val)-data) == BUFLIB_CB_OK)
424 { /* retry one more time */
425 goto handle_alloc;
426 }
427 }
428 return -1;
429 } 429 }
430 else if (ops && ops->shrink_callback)
431 { /* the last is shrinkable, make room for handles directly */
432 hints = BUFLIB_SHRINK_POS_BACK;
433 hints |= 16*sizeof(union buflib_data);
434 }
435 /* buflib_compact_and_shrink() will compact and move last_block()
436 * if possible */
437 if (buflib_compact_and_shrink(ctx, hints))
438 goto handle_alloc;
439 return -1;
430 } 440 }
431 441
432buffer_alloc: 442buffer_alloc:
@@ -504,9 +514,10 @@ buffer_alloc:
504 514
505/* Finds the free block before block, and returns NULL if it's not free */ 515/* Finds the free block before block, and returns NULL if it's not free */
506static union buflib_data* 516static union buflib_data*
507find_free_block_before(struct buflib_context *ctx, union buflib_data* block) 517find_block_before(struct buflib_context *ctx, union buflib_data* block,
518 bool is_free)
508{ 519{
509 union buflib_data *ret = ctx->first_free_block, 520 union buflib_data *ret = ctx->buf_start,
510 *next_block = ret; 521 *next_block = ret;
511 522
512 /* find the block that's before the current one */ 523 /* find the block that's before the current one */
@@ -519,10 +530,12 @@ find_free_block_before(struct buflib_context *ctx, union buflib_data* block)
519 /* If next_block == block, the above loop didn't go anywhere. If it did, 530 /* If next_block == block, the above loop didn't go anywhere. If it did,
520 * and the block before this one is empty, that is the wanted one 531 * and the block before this one is empty, that is the wanted one
521 */ 532 */
522 if (next_block == block && ret < block && ret->val < 0) 533 if (next_block == block && ret < block)
534 {
535 if (is_free && ret->val >= 0) /* NULL if found block isn't free */
536 return NULL;
523 return ret; 537 return ret;
524 /* otherwise, e.g. if ret > block, or if the buffer is compact, 538 }
525 * there's no free block before */
526 return NULL; 539 return NULL;
527} 540}
528 541
@@ -536,7 +549,7 @@ buflib_free(struct buflib_context *ctx, int handle_num)
536 /* We need to find the block before the current one, to see if it is free 549 /* We need to find the block before the current one, to see if it is free
537 * and can be merged with this one. 550 * and can be merged with this one.
538 */ 551 */
539 block = find_free_block_before(ctx, freed_block); 552 block = find_block_before(ctx, freed_block, true);
540 if (block) 553 if (block)
541 { 554 {
542 block->val -= freed_block->val; 555 block->val -= freed_block->val;
@@ -652,7 +665,7 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
652 /* mark the old block unallocated */ 665 /* mark the old block unallocated */
653 block->val = block - new_block; 666 block->val = block - new_block;
654 /* find the block before in order to merge with the new free space */ 667 /* find the block before in order to merge with the new free space */
655 union buflib_data *free_before = find_free_block_before(ctx, block); 668 union buflib_data *free_before = find_block_before(ctx, block, true);
656 if (free_before) 669 if (free_before)
657 free_before->val += block->val; 670 free_before->val += block->val;
658 else if (ctx->first_free_block > block) 671 else if (ctx->first_free_block > block)