/* 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) { char* new_start; union buflib_data *new_block, *tmp = block[1].handle; struct buflib_callbacks *ops = block[2].ops; if (!IS_MOVABLE(block)) 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*(int)sizeof(union buflib_data)); new_block = block + shift; new_start = tmp->alloc + shift*sizeof(union buflib_data); /* If move must be synchronized with use, user should have specified a callback that handles this */ if (ops && ops->sync_callback) ops->sync_callback(handle, true); bool retval = false; if (!ops || ops->move_callback(handle, tmp->alloc, new_start) != BUFLIB_CB_CANNOT_MOVE) { tmp->alloc = new_start; /* update handle table */ memmove(new_block, block, block->val * sizeof(union buflib_data)); retval = true; } if (ops && ops->sync_callback) ops->sync_callback(handle, false); return retval; }
/* 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) { char* new_start; union buflib_data *new_block, *tmp = block[1].handle; struct buflib_callbacks *ops = block[2].ops; if (!IS_MOVABLE(block)) 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*(int)sizeof(union buflib_data)); new_block = block + shift; new_start = tmp->alloc + shift*sizeof(union buflib_data); /* disable IRQs to make accessing the buffer from interrupt context safe. */ /* protect the move callback, as a cached global pointer might be updated * in it. and protect "tmp->alloc = new_start" for buflib_get_data() */ /* call the callback before moving */ if (ops && ops->sync_callback) { ops->sync_callback(handle, true); } else { disable_irq(); } bool retval = false; if (!ops || ops->move_callback(handle, tmp->alloc, new_start) != BUFLIB_CB_CANNOT_MOVE) { tmp->alloc = new_start; /* update handle table */ memmove(new_block, block, block->val * sizeof(union buflib_data)); retval = true; } if (ops && ops->sync_callback) { ops->sync_callback(handle, false); } else { enable_irq(); } return retval; }
/* Return the maximum allocatable contiguous memory in bytes */ size_t buflib_allocatable(struct buflib_context* ctx) { union buflib_data *this; size_t free_space = 0, max_free_space = 0; /* make sure buffer is as contiguous as possible */ if (!ctx->compact) buflib_compact(ctx); /* now look if there's free in holes */ for(this = find_first_free(ctx); this < ctx->alloc_end; this += abs(this->val)) { if (this->val < 0) { free_space += -this->val; continue; } /* an unmovable section resets the count as free space * can't be contigous */ if (!IS_MOVABLE(this)) { if (max_free_space < free_space) max_free_space = free_space; free_space = 0; } } /* select the best */ max_free_space = MAX(max_free_space, free_space); max_free_space *= sizeof(union buflib_data); max_free_space = MAX(max_free_space, free_space_at_end(ctx)); if (max_free_space > 0) return max_free_space; else return 0; }