mariadb/newbrt/block_allocator.c
2013-04-16 23:57:20 -04:00

207 lines
6.2 KiB
C

/* -*- mode: C; c-basic-offset: 4 -*- */
#include "includes.h"
// Here's a very simple implementation.
// It's not very fast at allocating or freeing.
struct blockpair {
u_int64_t offset;
u_int64_t size;
};
struct block_allocator {
u_int64_t reserve_at_beginning; // How much to reserve at the beginning
u_int64_t alignment; // Block alignment
u_int64_t n_blocks; // How many blocks
u_int64_t blocks_array_size; // How big is the blocks_array. Must be >= n_blocks.
struct blockpair *blocks_array; // These blocks are sorted by address.
u_int64_t next_fit_counter; // Used for the next_fit algorithm.
};
void
block_allocator_validate (BLOCK_ALLOCATOR ba) {
u_int64_t i;
for (i=0; i<ba->n_blocks; i++) {
if (i>0) {
assert(ba->blocks_array[i].offset > ba->blocks_array[i-1].offset);
assert(ba->blocks_array[i].offset >= ba->blocks_array[i-1].offset + ba->blocks_array[i-1].size );
}
}
}
#if 0
#define VALIDATE(b) block_allocator_validate(b)
#else
#define VALIDATE(b) ((void)0)
#endif
#if 0
void
block_allocator_print (BLOCK_ALLOCATOR ba) {
u_int64_t i;
for (i=0; i<ba->n_blocks; i++) {
printf("%" PRId64 ":%" PRId64 " ", ba->blocks_array[i].offset, ba->blocks_array[i].size);
}
printf("\n");
VALIDATE(ba);
}
#endif
void
create_block_allocator (BLOCK_ALLOCATOR *ba, u_int64_t reserve_at_beginning, u_int64_t alignment) {
BLOCK_ALLOCATOR XMALLOC(result);
result->reserve_at_beginning = reserve_at_beginning;
result->alignment = alignment;
result->n_blocks = 0;
result->blocks_array_size = 1;
XMALLOC_N(result->blocks_array_size, result->blocks_array);
result->next_fit_counter = 0;
*ba = result;
VALIDATE(result);
}
void
destroy_block_allocator (BLOCK_ALLOCATOR *bap) {
BLOCK_ALLOCATOR ba = *bap;
*bap = 0;
toku_free(ba->blocks_array);
toku_free(ba);
}
static void
grow_blocks_array (BLOCK_ALLOCATOR ba) {
if (ba->n_blocks >= ba->blocks_array_size) {
ba->blocks_array_size *= 2;
XREALLOC_N(ba->blocks_array_size, ba->blocks_array);
}
}
void
block_allocator_alloc_block_at (BLOCK_ALLOCATOR ba, u_int64_t size, u_int64_t offset) {
assert(offset%ba->alignment == 0);
u_int64_t i;
VALIDATE(ba);
assert(offset >= ba->reserve_at_beginning);
grow_blocks_array(ba);
// Just do a linear search for the block
for (i=0; i<ba->n_blocks; i++) {
if (ba->blocks_array[i].offset > offset) {
// allocate it in that slot
// Don't do error checking, since we require that the blocks don't overlap.
// Slide everything over
memmove(ba->blocks_array+i+1, ba->blocks_array+i, (ba->n_blocks - i)*sizeof(struct blockpair));
ba->blocks_array[i].offset = offset;
ba->blocks_array[i].size = size;
ba->n_blocks++;
VALIDATE(ba);
return;
}
}
// Goes at the end
ba->blocks_array[ba->n_blocks].offset = offset;
ba->blocks_array[ba->n_blocks].size = size;
ba->n_blocks++;
VALIDATE(ba);
}
static inline u_int64_t
align (u_int64_t value, BLOCK_ALLOCATOR ba) {
return ((value+ba->alignment-1)/ba->alignment)*ba->alignment;
}
void
block_allocator_alloc_block (BLOCK_ALLOCATOR ba, u_int64_t size, u_int64_t *offset) {
grow_blocks_array(ba);
if (ba->n_blocks==0) {
ba->blocks_array[0].offset = ba->reserve_at_beginning;
ba->blocks_array[0].size = size;
*offset = ba->reserve_at_beginning;
ba->n_blocks++;
return;
}
u_int64_t i;
u_int64_t blocknum = ba->next_fit_counter;
// Implement next fit.
for (i=0; i<ba->n_blocks; i++, blocknum++) {
if (blocknum>=ba->n_blocks) blocknum=0;
// Consider the space after blocknum
if (blocknum+1 == ba->n_blocks) continue; // Can't use the space after the last block, since that would be new space.
struct blockpair *bp = &ba->blocks_array[blocknum];
u_int64_t this_offset = bp[0].offset;
u_int64_t this_size = bp[0].size;
u_int64_t answer_offset = align(this_offset + this_size, ba);
if (answer_offset + size > bp[1].offset) continue; // The block we want doesn't fit after this block.
// It fits, so allocate it here.
memmove(bp+2, bp+1, (ba->n_blocks - blocknum -1)*sizeof(struct blockpair));
bp[1].offset = answer_offset;
bp[1].size = size;
ba->n_blocks++;
ba->next_fit_counter = blocknum;
*offset = answer_offset;
VALIDATE(ba);
return;
}
// It didn't fit anywhere, so fit it on the end.
struct blockpair *bp = &ba->blocks_array[ba->n_blocks];
u_int64_t answer_offset = align(bp[-1].offset+bp[-1].size, ba);
bp->offset = answer_offset;
bp->size = size;
ba->n_blocks++;
*offset = answer_offset;
VALIDATE(ba);
}
static int64_t
find_block (BLOCK_ALLOCATOR ba, u_int64_t offset)
// Find the index in the blocks array that has a particular offset. Requires that the block exist.
// Use binary search so it runs fast.
{
VALIDATE(ba);
if (ba->n_blocks==1) {
assert(ba->blocks_array[0].offset == offset);
return 0;
}
u_int64_t lo = 0;
u_int64_t hi = ba->n_blocks;
while (1) {
assert(lo<hi); // otherwise no such block exists.
u_int64_t mid = (lo+hi)/2;
u_int64_t thisoff = ba->blocks_array[mid].offset;
//printf("lo=%" PRId64 " hi=%" PRId64 " mid=%" PRId64 " thisoff=%" PRId64 " offset=%" PRId64 "\n", lo, hi, mid, thisoff, offset);
if (thisoff < offset) {
lo = mid+1;
} else if (thisoff > offset) {
hi = mid;
} else {
return mid;
}
}
}
void
block_allocator_free_block (BLOCK_ALLOCATOR ba, u_int64_t offset) {
VALIDATE(ba);
int64_t bn = find_block(ba, offset);
assert(bn>=0); // we require that there is a block with that offset. Might as well abort if no such block exists.
memmove(&ba->blocks_array[bn], &ba->blocks_array[bn+1], (ba->n_blocks-bn-1) * sizeof(struct blockpair));
ba->n_blocks--;
VALIDATE(ba);
}
u_int64_t
block_allocator_block_size (BLOCK_ALLOCATOR ba, u_int64_t offset) {
int64_t bn = find_block(ba, offset);
assert(bn>=0); // we require that there is a block with that offset. Might as well abort if no such block exists.
return ba->blocks_array[bn].size;
}
u_int64_t
block_allocator_allocated_limit (BLOCK_ALLOCATOR ba) {
if (ba->n_blocks==0) return ba->reserve_at_beginning;
else {
struct blockpair *last = &ba->blocks_array[ba->n_blocks-1];
return last->offset + last->size;
}
}