213 lines
5.5 KiB
C
213 lines
5.5 KiB
C
|
/* heap-1.c */
|
||
|
|
||
|
#include <stdbool.h>
|
||
|
#include <stddef.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#define HEAP_BLOCKS 16
|
||
|
#define BLOCK_CAPACITY 1024
|
||
|
|
||
|
enum block_status { BLK_FREE = 0, BLK_ONE, BLK_FIRST, BLK_CONT, BLK_LAST };
|
||
|
|
||
|
struct heap {
|
||
|
struct block {
|
||
|
char contents[BLOCK_CAPACITY];
|
||
|
} blocks[HEAP_BLOCKS];
|
||
|
enum block_status status[HEAP_BLOCKS];
|
||
|
} global_heap = {0};
|
||
|
|
||
|
struct block_id {
|
||
|
size_t value;
|
||
|
bool valid;
|
||
|
struct heap* heap;
|
||
|
};
|
||
|
|
||
|
struct block_id block_id_new(size_t value, struct heap* from) {
|
||
|
return (struct block_id){.valid = true, .value = value, .heap = from};
|
||
|
}
|
||
|
struct block_id block_id_invalid() {
|
||
|
return (struct block_id){.valid = false};
|
||
|
}
|
||
|
|
||
|
bool block_id_is_valid(struct block_id bid) {
|
||
|
return bid.valid && bid.value < HEAP_BLOCKS;
|
||
|
}
|
||
|
|
||
|
/* Find block */
|
||
|
|
||
|
bool block_is_free(struct block_id bid) {
|
||
|
if (!block_id_is_valid(bid))
|
||
|
return false;
|
||
|
return bid.heap->status[bid.value] == BLK_FREE;
|
||
|
}
|
||
|
|
||
|
/* Allocate */
|
||
|
struct block_id block_allocate(struct heap* heap, size_t size) {
|
||
|
if (size <= 0) return block_id_invalid();
|
||
|
enum block_status* first_status = NULL;
|
||
|
enum block_status* last_status = NULL;
|
||
|
for (size_t i = 0; i < HEAP_BLOCKS; i++) {
|
||
|
if (i + size > HEAP_BLOCKS) return block_id_invalid();
|
||
|
first_status = &(heap->status[i]);
|
||
|
if (*first_status != BLK_FREE) continue;
|
||
|
bool not_enough_space = false;
|
||
|
for (size_t j = i + 1; j < i + size; j++) {
|
||
|
last_status = &(heap->status[j]);
|
||
|
if (*last_status != BLK_FREE) {
|
||
|
not_enough_space = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (not_enough_space) continue;
|
||
|
if (size == 1) *first_status = BLK_ONE;
|
||
|
else {
|
||
|
*first_status = BLK_FIRST;
|
||
|
*last_status = BLK_LAST;
|
||
|
for (size_t j = i + 1; j < i + size - 1; j++)
|
||
|
heap->status[j] = BLK_CONT;
|
||
|
}
|
||
|
return block_id_new(i, heap);
|
||
|
}
|
||
|
return block_id_invalid();
|
||
|
}
|
||
|
|
||
|
void heap_debug_info(struct heap*, FILE*);
|
||
|
|
||
|
/* Free */
|
||
|
void block_free(struct block_id b) {
|
||
|
if (!b.valid) return;
|
||
|
if (b.heap->status[b.value] == BLK_FREE) return;
|
||
|
bool is_last = b.heap->status[b.value] == BLK_ONE;
|
||
|
b.heap->status[b.value] = BLK_FREE;
|
||
|
for (size_t i = b.value + 1; !is_last; i++) {
|
||
|
is_last = b.heap->status[i] == BLK_LAST;
|
||
|
b.heap->status[i] = BLK_FREE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Printer */
|
||
|
const char* block_repr(struct block_id b) {
|
||
|
static const char* const repr[] = {[BLK_FREE] = " .",
|
||
|
[BLK_ONE] = " *",
|
||
|
[BLK_FIRST] = "[=",
|
||
|
[BLK_LAST] = "=]",
|
||
|
[BLK_CONT] = " ="};
|
||
|
if (b.valid)
|
||
|
return repr[b.heap->status[b.value]];
|
||
|
else
|
||
|
return "INVALID";
|
||
|
}
|
||
|
|
||
|
void block_debug_info(struct block_id b, FILE* f) {
|
||
|
fprintf(f, "%s", block_repr(b));
|
||
|
}
|
||
|
|
||
|
void block_foreach_printer(struct heap* h, size_t count,
|
||
|
void printer(struct block_id, FILE* f), FILE* f) {
|
||
|
for (size_t c = 0; c < count; c++)
|
||
|
printer(block_id_new(c, h), f);
|
||
|
}
|
||
|
|
||
|
void heap_debug_info(struct heap* h, FILE* f) {
|
||
|
block_foreach_printer(h, HEAP_BLOCKS, block_debug_info, f);
|
||
|
fprintf(f, "\n");
|
||
|
}
|
||
|
/* -------- */
|
||
|
|
||
|
void heap_free(struct heap* heap) {
|
||
|
for (size_t i = 0; i < HEAP_BLOCKS; i++)
|
||
|
heap->status[i] = BLK_FREE;
|
||
|
}
|
||
|
|
||
|
void allocate_block_test(struct heap* heap) {
|
||
|
printf("allocate_block_test: ");
|
||
|
block_allocate(heap, 1);
|
||
|
heap_debug_info(heap, stdout);
|
||
|
heap_free(heap);
|
||
|
}
|
||
|
|
||
|
void allocate_big_block_test(struct heap* heap) {
|
||
|
printf("allocate_big_block_test: ");
|
||
|
block_allocate(heap, 5);
|
||
|
heap_debug_info(heap, stdout);
|
||
|
heap_free(heap);
|
||
|
}
|
||
|
|
||
|
void allocate_multiple_blocks_test(struct heap* heap) {
|
||
|
printf("allocate_multiple_blocks_test: ");
|
||
|
block_allocate(heap, 7);
|
||
|
block_allocate(heap, 1);
|
||
|
block_allocate(heap, 3);
|
||
|
heap_debug_info(heap, stdout);
|
||
|
heap_free(heap);
|
||
|
}
|
||
|
|
||
|
void allocate_more_than_available_test(struct heap* heap) {
|
||
|
printf("allocate_more_than_available_test: ");
|
||
|
block_allocate(heap, 17);
|
||
|
heap_debug_info(heap, stdout);
|
||
|
heap_free(heap);
|
||
|
}
|
||
|
|
||
|
void allocate_limit_test(struct heap* heap) {
|
||
|
printf("allocate_limit_test: ");
|
||
|
block_allocate(heap, 16);
|
||
|
heap_debug_info(heap, stdout);
|
||
|
heap_free(heap);
|
||
|
}
|
||
|
|
||
|
void allocate_middle_test(struct heap* heap) {
|
||
|
printf("allocate_middle_test: ");
|
||
|
block_allocate(heap, 5);
|
||
|
struct block_id id = block_allocate(heap, 3);
|
||
|
block_allocate(heap, 7);
|
||
|
block_free(id);
|
||
|
block_allocate(heap, 2);
|
||
|
heap_debug_info(heap, stdout);
|
||
|
heap_free(heap);
|
||
|
}
|
||
|
|
||
|
void free_block_test(struct heap* heap) {
|
||
|
printf("free_block_test: ");
|
||
|
struct block_id id = block_allocate(heap, 1);
|
||
|
block_free(id);
|
||
|
heap_debug_info(heap, stdout);
|
||
|
heap_free(heap);
|
||
|
}
|
||
|
|
||
|
void free_big_block_test(struct heap* heap) {
|
||
|
printf("free_big_block_test: ");
|
||
|
struct block_id id = block_allocate(heap, 5);
|
||
|
block_free(id);
|
||
|
heap_debug_info(heap, stdout);
|
||
|
heap_free(heap);
|
||
|
}
|
||
|
|
||
|
void free_multiple_blocks_test(struct heap* heap) {
|
||
|
printf("free_multiple_blocks_test: ");
|
||
|
struct block_id id1 = block_allocate(heap, 7);
|
||
|
struct block_id id2 = block_allocate(heap, 1);
|
||
|
struct block_id id3 = block_allocate(heap, 5);
|
||
|
block_free(id1);
|
||
|
block_free(id2);
|
||
|
block_free(id3);
|
||
|
heap_debug_info(heap, stdout);
|
||
|
heap_free(heap);
|
||
|
}
|
||
|
|
||
|
int main() {
|
||
|
printf("\n");
|
||
|
allocate_block_test(&global_heap);
|
||
|
allocate_big_block_test(&global_heap);
|
||
|
allocate_multiple_blocks_test(&global_heap);
|
||
|
allocate_more_than_available_test(&global_heap);
|
||
|
allocate_limit_test(&global_heap);
|
||
|
allocate_middle_test(&global_heap);
|
||
|
free_block_test(&global_heap);
|
||
|
free_big_block_test(&global_heap);
|
||
|
free_multiple_blocks_test(&global_heap);
|
||
|
printf("\n");
|
||
|
return 0;
|
||
|
}
|