complete lab (+tests)
Showing
src/main.c
0 → 100644
... | ... | @@ -32,15 +32,26 @@ static size_t region_actual_size( size_t query ) { return size_max( round_pages( |
extern inline bool region_is_invalid( const struct region* r ); | ||
static void* map_pages(void const* addr, size_t length, int additional_flags) { | ||
return mmap( (void*) addr, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | additional_flags , 0, 0 ); | ||
} | ||
/* аллоцировать регион памяти и инициализировать его блоком */ | ||
static struct region alloc_region ( void const * addr, size_t query ) { | ||
/* ??? */ | ||
static struct region alloc_region(void const* addr, size_t query) { | ||
struct region allocated_region; | ||
allocated_region.addr = map_pages(addr, query, MAP_FIXED); | ||
size_t region_size = region_actual_size(query); | ||
if (allocated_region.addr == MAP_FAILED || allocated_region.addr == NULL) { | ||
allocated_region = (struct region) { map_pages(addr, query, 0), region_size, false }; | ||
} else { | ||
allocated_region = (struct region) { allocated_region.addr, region_size, true }; | ||
} | ||
block_init(allocated_region.addr, (block_size) { region_size }, NULL); | ||
return allocated_region; | ||
} | ||
static void* block_after( struct block_header const* block ) ; | ||
... | ... | @@ -56,22 +67,29 @@ void* heap_init( size_t initial ) { |
/* --- Разделение блоков (если найденный свободный блок слишком большой )--- */ | ||
static bool block_splittable( struct block_header* restrict block, size_t query) { | ||
static bool block_splittable(struct block_header* restrict block, size_t query) { | ||
return block-> is_free && query + offsetof( struct block_header, contents ) + BLOCK_MIN_CAPACITY <= block->capacity.bytes; | ||
} | ||
static bool split_if_too_big( struct block_header* block, size_t query ) { | ||
/* ??? */ | ||
} | ||
static bool split_if_too_big(struct block_header* block, size_t query) { | ||
if (block_splittable(block, query)) { | ||
const block_capacity new_capacity = { .bytes = block->capacity.bytes - query }; | ||
|
||
block->capacity.bytes = query; | ||
block_init(block_after(block), size_from_capacity(new_capacity), block->next); | ||
block->next = block_after(block); | ||
return true; | ||
} | ||
return false; | ||
} | ||
/* --- Слияние соседних свободных блоков --- */ | ||
static void* block_after( struct block_header const* block ) { | ||
static void* block_after(struct block_header const* block) { | ||
return (void*) (block->contents + block->capacity.bytes); | ||
} | ||
static bool blocks_continuous ( | ||
struct block_header const* fst, | ||
static bool blocks_continuous (struct block_header const* fst, | ||
struct block_header const* snd ) { | ||
return (void*)snd == block_after(fst); | ||
} | ||
... | ... | @@ -80,10 +98,16 @@ static bool mergeable(struct block_header const* restrict fst, struct block_head |
return fst->is_free && snd->is_free && blocks_continuous( fst, snd ) ; | ||
} | ||
static bool try_merge_with_next( struct block_header* block ) { | ||
/* ??? */ | ||
} | ||
static bool try_merge_with_next(struct block_header* block) { | ||
if (block->next && mergeable(block, block->next)) { | ||
block->capacity.bytes = block->capacity.bytes + size_from_capacity(block->next->capacity).bytes; | ||
block->next = block->next->next; | ||
return true; | ||
} | ||
return false; | ||
} | ||
/* --- ... ecли размера кучи хватает --- */ | ||
... | ... | @@ -92,34 +116,60 @@ struct block_search_result { |
struct block_header* block; | ||
}; | ||
static struct block_search_result find_good_or_last (struct block_header* restrict block, size_t sz) { | ||
struct block_header* current_block = block; | ||
while (current_block->next) { | ||
while (try_merge_with_next(block)); | ||
if (current_block->is_free && block_is_big_enough(sz, current_block)) { | ||
return (struct block_search_result){ .type = BSR_FOUND_GOOD_BLOCK, .block = current_block }; | ||
} | ||
current_block = current_block->next; | ||
} | ||
static struct block_search_result find_good_or_last ( struct block_header* restrict block, size_t sz ) { | ||
/*??? */ | ||
return (struct block_search_result) { .type=BSR_REACHED_END_NOT_FOUND, .block=current_block}; | ||
} | ||
/* Попробовать выделить память в куче начиная с блока `block` не пытаясь расширить кучу | ||
Можно переиспользовать как только кучу расширили. */ | ||
static struct block_search_result try_memalloc_existing ( size_t query, struct block_header* block ) { | ||
} | ||
struct block_search_result result = find_good_or_last(block, query); | ||
if (result.type == BSR_FOUND_GOOD_BLOCK) { | ||
split_if_too_big(result.block, query); | ||
result.block->is_free = false; | ||
} | ||
return result; | ||
} | ||
static struct block_header *grow_heap(struct block_header* restrict last, size_t query) { | ||
const struct region new_region = alloc_region(block_after(last), query); | ||
|
||
last->next = new_region.addr; | ||
static struct block_header* grow_heap( struct block_header* restrict last, size_t query ) { | ||
/* ??? */ | ||
return last; | ||
} | ||
/* Реализует основную логику malloc и возвращает заголовок выделенного блока */ | ||
static struct block_header* memalloc( size_t query, struct block_header* heap_start) { | ||
/* ??? */ | ||
static struct block_header* memalloc(size_t query, struct block_header* heap_start) { | ||
struct block_search_result result = try_memalloc_existing(query, heap_start); | ||
if (result.type != BSR_FOUND_GOOD_BLOCK) { | ||
result = try_memalloc_existing(query, grow_heap(result.block, query)); | ||
} | ||
return result.block; | ||
} | ||
void* _malloc( size_t query ) { | ||
struct block_header* const addr = memalloc( query, (struct block_header*) HEAP_START ); | ||
if (addr) return addr->contents; | ||
else return NULL; | ||
void* _malloc(size_t query) { | ||
struct block_header* const addr = memalloc(query, (struct block_header*) HEAP_START); | ||
if (addr) { | ||
addr->is_free = false; | ||
return addr->contents; | ||
} else { | ||
return NULL; | ||
} | ||
} | ||
static struct block_header* block_get_header(void* contents) { | ||
... | ... | @@ -127,8 +177,8 @@ static struct block_header* block_get_header(void* contents) { |
} | ||
void _free( void* mem ) { | ||
if (!mem) return ; | ||
struct block_header* header = block_get_header( mem ); | ||
if (!mem) return; | ||
struct block_header* header = block_get_header(mem); | ||
header->is_free = true; | ||
/* ??? */ | ||
} | ||
while(try_merge_with_next(header)); | ||
} | ||
\ No newline at end of file |
src/tests.c
0 → 100644
#include <sys/mman.h> | ||
#include <stdbool.h> | ||
#include <stdio.h> | ||
#include "util.h" | ||
#include "mem.h" | ||
#include "tests.h" | ||
#include "mem_internals.h" | ||
// void* heap; | ||
static struct block_header* block_get_header(void* contents) { | ||
return (struct block_header*) (((uint8_t*)contents) - offsetof(struct block_header, contents)); | ||
} | ||
static size_t count_nonfree_blocks(struct block_header* heap) { | ||
size_t res = 0; | ||
while (heap) { | ||
if (!heap->is_free) res++; | ||
heap = heap->next; | ||
} | ||
return res; | ||
} | ||
// Проверка выделения памяти | ||
void test1(struct block_header* heap) { | ||
size_t block_size = 500; | ||
printf("Test 1:\n"); | ||
printf("Allocating a single block\n"); | ||
void* block1 = _malloc(block_size); | ||
if(block1 == NULL) fprintf(stdout, "Allocation failed\n"); | ||
struct block_header* block_header = block_get_header(block1); | ||
if (block_header->is_free) fprintf(stdout, "Block should not be free\n"); | ||
if (block_header->capacity.bytes != block_size || heap->capacity.bytes != block_size) fprintf(stdout, "Block capacity set incorrectly\n"); | ||
_free(block1); | ||
printf("\nTest 1 passed\n\n"); | ||
} | ||
// Освобождение одного блока из нескольких выделенных | ||
void test2(struct block_header* heap) { | ||
size_t block_size1 = 200; | ||
size_t block_size2 = 300; | ||
printf("Test 2:\n"); | ||
printf("Freeing a single block from mutilpe blocks\n"); | ||
void* block1 = _malloc(block_size1); | ||
if(block1 == NULL) fprintf(stdout, "Block 1 allocation failed\n"); | ||
void* block2 = _malloc(block_size2); | ||
if(block2 == NULL) fprintf(stdout, "Block 2 allocation failed\n"); | ||
_free(block2); | ||
struct block_header* block_header = block_get_header(block2); | ||
if (!block_header->is_free) fprintf(stdout, "Block should be free\n"); | ||
if (count_nonfree_blocks(heap) != 1) fprintf(stdout, "Block 2 was not cleared correctly\n"); | ||
_free(block1); | ||
printf("\nTest 2 passed\n\n"); | ||
} | ||
// Освобождение двух блоков из нескольких выделенных | ||
void test3(struct block_header* heap) { | ||
size_t block_size1 = 200; | ||
size_t block_size2 = 300; | ||
size_t block_size3 = 400; | ||
printf("Test 3:\n"); | ||
printf("Freeing two blocks from mutilpe blocks\n"); | ||
void* block1 = _malloc(block_size1); | ||
if(block1 == NULL) fprintf(stdout, "Block 1 allocation failed\n"); | ||
void* block2 = _malloc(block_size2); | ||
if(block2 == NULL) fprintf(stdout, "Block 2 allocation failed\n"); | ||
void* block3 = _malloc(block_size3); | ||
if(block3 == NULL) fprintf(stdout, "Block 3 allocation failed\n"); | ||
_free(block3); | ||
_free(block2); | ||
struct block_header* block_header3 = block_get_header(block3); | ||
if (!block_header3->is_free) fprintf(stdout, "Block 3 should be free\n"); | ||
struct block_header* block_header2 = block_get_header(block2); | ||
if (!block_header2->is_free) fprintf(stdout, "Block 2 should be free\n"); | ||
if (count_nonfree_blocks(heap) != 1) fprintf(stdout, "Block 2 or/and 3 were not cleared correctly\n"); | ||
_free(block1); | ||
printf("\nTest 3 passed\n\n"); | ||
} | ||
// Память закончилась, новый регион памяти расширяет старый | ||
void test4(struct block_header* heap) { | ||
|
||
size_t block_size1 = 10000; | ||
printf("Test 4:\n"); | ||
printf("Out of memory, extending region\n"); | ||
void* block1 = _malloc(block_size1); | ||
if(block1 == NULL) fprintf(stdout, "Block 1 allocation failed\n"); | ||
if (count_nonfree_blocks(heap) != 1) fprintf(stdout, "Block extending region was not allocated correctly\n"); | ||
_free(block1); | ||
printf("\nTest 4 passed\n\n"); | ||
} | ||
\ No newline at end of file |
src/tests.h
0 → 100644