/* Shrink the allocation indicated by the handle according to new_start and * new_size. Grow is not possible, therefore new_start and new_start + new_size * must be within the original allocation */ bool buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size) { char* oldstart = buflib_get_data(ctx, handle); char* newstart = new_start; char* newend = newstart + new_size; /* newstart must be higher and new_size not "negative" */ if (newstart < oldstart || newend < newstart) return false; union buflib_data *block = handle_to_block(ctx, handle), *old_next_block = block + block->val, /* newstart isn't necessarily properly aligned but it * needn't be since it's only dereferenced by the user code */ *aligned_newstart = (union buflib_data*)B_ALIGN_DOWN(newstart), *aligned_oldstart = (union buflib_data*)B_ALIGN_DOWN(oldstart), *new_next_block = (union buflib_data*)B_ALIGN_UP(newend), *new_block, metadata_size; /* growing is not supported */ if (new_next_block > old_next_block) return false; metadata_size.val = aligned_oldstart - block; /* update val and the handle table entry */ new_block = aligned_newstart - metadata_size.val; block[0].val = new_next_block - new_block; block[1].handle->alloc = newstart; if (block != new_block) { /* move metadata over, i.e. pointer to handle table entry and name * This is actually the point of no return. Data in the allocation is * being modified, and therefore we must successfully finish the shrink * operation */ memmove(new_block, block, metadata_size.val*sizeof(metadata_size)); /* mark the old block unallocated */ block->val = block - new_block; /* find the block before in order to merge with the new free space */ union buflib_data *free_before = find_block_before(ctx, block, true); if (free_before) free_before->val += block->val; /* We didn't handle size changes yet, assign block to the new one * the code below the wants block whether it changed or not */ block = new_block; } /* Now deal with size changes that create free blocks after the allocation */ if (old_next_block != new_next_block) { if (ctx->alloc_end == old_next_block) ctx->alloc_end = new_next_block; else if (old_next_block->val < 0) { /* enlarge next block by moving it up */ new_next_block->val = old_next_block->val - (old_next_block - new_next_block); } else if (old_next_block != new_next_block) { /* creating a hole */ /* must be negative to indicate being unallocated */ new_next_block->val = new_next_block - old_next_block; } } return true; }
/* Shrink the allocation indicated by the handle according to new_start and * new_size. Grow is not possible, therefore new_start and new_start + new_size * must be within the original allocation */ bool buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size) { char* oldstart = buflib_get_data(ctx, handle); char* newstart = new_start; char* newend = newstart + new_size; /* newstart must be higher and new_size not "negative" */ if (newstart < oldstart || newend < newstart) return false; union buflib_data *block = handle_to_block(ctx, handle), *old_next_block = block + block->val, /* newstart isn't necessarily properly aligned but it * needn't be since it's only dereferenced by the user code */ *aligned_newstart = (union buflib_data*)B_ALIGN_DOWN(newstart), *aligned_oldstart = (union buflib_data*)B_ALIGN_DOWN(oldstart), *new_next_block = (union buflib_data*)B_ALIGN_UP(newend), *new_block, metadata_size; /* growing is not supported */ if (new_next_block > old_next_block) return false; metadata_size.val = aligned_oldstart - block; /* update val and the handle table entry */ new_block = aligned_newstart - metadata_size.val; block[0].val = new_next_block - new_block; block[1].handle->alloc = newstart; if (block != new_block) { /* move metadata over, i.e. pointer to handle table entry and name * This is actually the point of no return. Data in the allocation is * being modified, and therefore we must successfully finish the shrink * operation */ memmove(new_block, block, metadata_size.val*sizeof(metadata_size)); /* mark the old block unallocated */ block->val = block - new_block; union buflib_data *freed_block = block, *free_before = ctx->first_free_block, *next_block = free_before; /* We need to find the block before the current one, to see if it is free * and can be merged with this one. */ while (next_block < freed_block) { free_before = next_block; next_block += abs(block->val); } /* If next_block == free_before, the above loop didn't go anywhere. * If it did, and the block before this one is empty, we can combine them. */ if (next_block == freed_block && next_block != free_before && free_before->val < 0) free_before->val += freed_block->val; else if (next_block == free_before) ctx->first_free_block = freed_block; /* We didn't handle size changes yet, assign block to the new one * the code below the wants block whether it changed or not */ block = new_block; } /* Now deal with size changes that create free blocks after the allocation */ if (old_next_block != new_next_block) { if (ctx->alloc_end == old_next_block) ctx->alloc_end = new_next_block; else if (old_next_block->val < 0) { /* enlarge next block by moving it up */ new_next_block->val = old_next_block->val - (old_next_block - new_next_block); } else if (old_next_block != new_next_block) { /* creating a hole */ /* must be negative to indicate being unallocated */ new_next_block->val = new_next_block - old_next_block; } /* update first_free_block for the newly created free space */ if (ctx->first_free_block > new_next_block) ctx->first_free_block = new_next_block; } /* if the handle is the one aquired with buflib_alloc_maximum() * unlock buflib_alloc() as part of the shrink */ if (ctx->handle_lock == handle) ctx->handle_lock = 0; return true; }