diff options
Diffstat (limited to 'firmware/buflib.c')
-rw-r--r-- | firmware/buflib.c | 67 |
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 | ||
92 | static union buflib_data* | ||
93 | find_block_before(struct buflib_context *ctx, union buflib_data* block, | ||
94 | bool is_free); | ||
92 | /* Initialize buffer manager */ | 95 | /* Initialize buffer manager */ |
93 | void | 96 | void |
94 | buflib_init(struct buflib_context *ctx, void *buf, size_t size) | 97 | buflib_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 | ||
432 | buffer_alloc: | 442 | buffer_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 */ |
506 | static union buflib_data* | 516 | static union buflib_data* |
507 | find_free_block_before(struct buflib_context *ctx, union buflib_data* block) | 517 | find_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) |