From d347b81e725753ed1310ba31e30f34505cc26af4 Mon Sep 17 00:00:00 2001 From: Nathan Giddings Date: Mon, 9 Jan 2023 01:54:03 -0600 Subject: [PATCH] Initial commit --- .gitignore | 19 ++ Makefile.am | 1 + README.md | 13 ++ configure.ac | 23 +++ include/Makefile.am | 1 + include/libmalloc/bitmap_alloc.h | 153 ++++++++++++++ include/libmalloc/common.h | 13 ++ include/libmalloc/memmap.h | 27 +++ include/util.h | 69 +++++++ src/Makefile.am | 4 + src/bitmap_alloc.c | 332 +++++++++++++++++++++++++++++++ src/memmap.c | 144 ++++++++++++++ tests/Makefile.am | 6 + tests/test_buddyalloc.c | 51 +++++ 14 files changed, 856 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile.am create mode 100644 README.md create mode 100644 configure.ac create mode 100644 include/Makefile.am create mode 100644 include/libmalloc/bitmap_alloc.h create mode 100644 include/libmalloc/common.h create mode 100644 include/libmalloc/memmap.h create mode 100644 include/util.h create mode 100644 src/Makefile.am create mode 100644 src/bitmap_alloc.c create mode 100644 src/memmap.c create mode 100644 tests/Makefile.am create mode 100644 tests/test_buddyalloc.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f91747 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +aclocal.m4 +ar-lib +config.log +config.status +configure +configure~ +compile +depcomp +install-sh +**/Makefile +autom4te.cache +**/Makefile.in +**/*.a +**/*.o +**/*.Po +.vscode +include/config.h* +missing +stamp-h1 \ No newline at end of file diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..39089fb --- /dev/null +++ b/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src include tests diff --git a/README.md b/README.md new file mode 100644 index 0000000..b97fcc7 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Quark libmalloc + +Quark Kernel is a hobbyist OS kernel mainly indended to be a challenging side-project. + +## Building + +You will need: +- GNU Autotools + +To build the kernel for the x86 platform, run: +- `autoreconf -i` +- `./configure [--enable-tests] [--host=] [--prefix=] [CFLAGS=-ffreestanding LDFLAGS=-nostdlib]` +- `make` diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..e15b4aa --- /dev/null +++ b/configure.ac @@ -0,0 +1,23 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.69]) +AC_INIT([libmalloc], [pre-alpha]) +AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) +AC_CONFIG_SRCDIR([src/bitmap_alloc.c]) +AC_CONFIG_HEADERS([include/config.h]) + +# Checks for programs. +AC_PROG_CC +AM_PROG_AS +AM_PROG_AR +AC_PROG_RANLIB + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_INLINE + +AC_ARG_ENABLE([tests], [Compile test programs], [tests=true]) +AM_CONDITIONAL([BUILD_TESTS], [test x$tests = xtrue]) + +AC_CONFIG_FILES([Makefile src/Makefile include/Makefile tests/Makefile]) +AC_OUTPUT diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..d300281 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1 @@ +nobase_include_HEADERS = libmalloc/bitmap_alloc.h libmalloc/memmap.h libmalloc/common.h \ No newline at end of file diff --git a/include/libmalloc/bitmap_alloc.h b/include/libmalloc/bitmap_alloc.h new file mode 100644 index 0000000..06d57c9 --- /dev/null +++ b/include/libmalloc/bitmap_alloc.h @@ -0,0 +1,153 @@ +#ifndef _LIBMALLOC_BUDDYALLOC_H +#define _LIBMALLOC_BUDDYALLOC_H + +#include "memmap.h" +#include "common.h" + +/** + * @brief + * + */ +typedef struct +{ + /** + * @brief The underlying bitmap representing the availability of chunks of + * physical memory. + * + */ + unsigned long *bitmap; + + /** + * @brief Stores a list of available blocks of memory to speed up allocation. + * + */ + unsigned long *cache; + + /** + * @brief The size of the bitmap in bytes. + * + */ + unsigned long bitmap_size; + + /** + * @brief The size of the array pointed to by `cache`. + * + */ + unsigned long cache_capacity; + + /** + * @brief The size in bytes of the smallest unit of allocation. + * + * This value should either be the size of a page on the host system, or + * possibly some number of pages. + * + */ + unsigned long block_size; + + /** + * @brief The height of the binary tree representation of the heap's memory + * map. + * + */ + unsigned long height; + + /** + * @brief The number of available blocks of memory. + * + * Due to memory fragmentation, it may not be possible to allocate all + * available memory at once. + * + */ + unsigned long free_block_count; + +} bitmap_heap_descriptor_t; + +/** + * @brief Reserves a region of memory within the heap containing at least `size` + * bytes. + * + * If `size` is not a power of two times the block size, it will be rounded up + * to the smallest possible number which satisfies this condition. After + * + * @param heap + * @param size + * @return unsigned long + */ +unsigned long reserve_region(bitmap_heap_descriptor_t *heap, unsigned long size); + +/** + * @brief Marks the region of memory indicated by `location` and `size` as + * available to be allocated. + * + * `location` must have been previously returned by `reserve_region`. `size` + * is expected to be a power of two times the block size. + * + * @param heap + * @param location + * @param size + */ +void free_region(bitmap_heap_descriptor_t *heap, unsigned long location, + unsigned long size); + +/** + * @brief Computes the amount of space required to store the heap's internal + * bitmaps. + * + * @param map A pointer to the structure providing an initial memory layout + * @param block_size The minimum unit of allocation + * @return unsigned long + */ +unsigned long bitmap_size(const memory_map_t *map, unsigned long block_size); + +/** + * @brief Builds the heap's internal structures according to the memory + * layout provided in `map`. Assumes that the layout in `map` refers to the + * caller's virtual address space, and utilizes some of the memory marked as + * 'available' to store the heap's internal structures. + * + * A callback function `mmap` may be provided, which will be used to map the + * space required by the heap to store its internal bitmaps. If `mmap` is NULL, + * this function assumes that all space within the heap is already mapped. + * + * There are several requirements for the initial state of the `heap` structure: + * + * - The `cache` field must be defined, and point to an array of unsigned longs + * of sufficient size. + * + * - The `block_size` field must be set to the desired smallest unit of allocation. + * + * @param heap A pointer to the structure describing the heap + * @param map A pointer to the structure providing an initial memory layout + * @param mmap A callback function used to map memory in the virtual address space + * @return int + */ +int initialize_virtual_heap(bitmap_heap_descriptor_t *heap, const memory_map_t *map, + int (*mmap)(void *location, unsigned long size)); + +/** + * @brief Builds the heap's internal structures according to the memory + * layout provided in `map`. Assumes that physical memory space is being alocated, + * and therefore does not make assumptions about the caller's address space or + * attempt to utilize the memory inside the heap. The caller is responsible for + * providing space to store the heap's internal structures. + * + * There are several requirements for the initial state of the `heap` structure: + * + * - The `bitmap` field must be defined, and sufficient memory reserved at that + * location to contain the heap's bitmap. + * + * - The `cache` field must be defined, and point to an array of unsigned longs + * of sufficient size. + * + * - The `cache_capacity` field must be set to the size of the array pointed to + * by `cache` + * + * - The `block_size` field must be set to the desired smallest unit of allocation. + * + * @param heap A pointer to the structure describing the heap + * @param map A pointer to the structure providing an initial memory layout + * @return int 0 upon success; nonzero upon failure + */ +int initialize_physical_heap(bitmap_heap_descriptor_t *heap, const memory_map_t *map); + +#endif \ No newline at end of file diff --git a/include/libmalloc/common.h b/include/libmalloc/common.h new file mode 100644 index 0000000..231599f --- /dev/null +++ b/include/libmalloc/common.h @@ -0,0 +1,13 @@ +#ifndef _LIBMALLOC_COMMON_H +#define _LIBMALLOC_COMMON_H + +/* + * '0' may possibly refer to a valid memory location for the heap to reserve in + * some circumstances; as a result, NULL is an inappropriate value to use to + * represent the failure to allocate space for the purposes of this library. + * NOMEM shall be returned by any malloc-like function upon failure, rather than + * NULL. + */ +#define NOMEM ~0 + +#endif \ No newline at end of file diff --git a/include/libmalloc/memmap.h b/include/libmalloc/memmap.h new file mode 100644 index 0000000..3a99799 --- /dev/null +++ b/include/libmalloc/memmap.h @@ -0,0 +1,27 @@ +#ifndef _LIBMALLOC_MEMMAP_H +#define _LIBMALLOC_MEMMAP_H + +typedef enum +{ + M_AVAILABLE = 1, + M_UNAVAILABLE = 2, + M_DEFECTIVE = 3 +} memory_type_t; + +typedef struct +{ + memory_type_t type; + unsigned long location; + unsigned long size; +} memory_region_t; + +typedef struct +{ + memory_region_t *array; + unsigned long size; + unsigned long capacity; +} memory_map_t; + +int memmap_insert_region(memory_map_t *map, unsigned long location, unsigned long size, memory_type_t type); + +#endif \ No newline at end of file diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..21f1b84 --- /dev/null +++ b/include/util.h @@ -0,0 +1,69 @@ +#ifndef _LIBMALLOC_UTIL_H +#define _LIBMALLOC_UTIL_H + +static inline int ilog2(unsigned int x) +{ +#if defined __GNUC__ + if(x <= 1) + return 0; + return 32 - __builtin_clz(x - 1); +#else + static const int table[32] = { + 0, 9, 1, 10, 13, 21, 2, 29, + 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, + 19, 27, 23, 6, 26, 5, 4, 31}; + + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + + return table[(x * 0x07C4ACDD) >> 27]; +#endif +} + +static inline int llog2(unsigned long x) +{ +#if (defined __GNUC__) && (__SIZEOF_LONG__ == 4) + if(x <= 1) + return 0; + return 32 - __builtin_clzl(x - 1); +#elif (defined __GNUC__) && (__SIZEOF_LONG__ == 8) + if(x <= 1) + return 0; + return 64 - __builtin_clzl(x - 1); +#elif __SIZEOF_LONG__ == 8 + static const int table[64] = { + 0, 58, 1, 59, 47, 53, 2, 60, 39, 48, 27, 54, 33, 42, 3, 61, + 51, 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22, 4, 62, + 57, 46, 52, 38, 26, 32, 41, 50, 36, 17, 19, 29, 10, 13, 21, 56, + 45, 25, 31, 35, 16, 9, 12, 44, 24, 15, 8, 23, 7, 6, 5, 63}; + + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x |= x >> 32; + + return table[(x * 0x03f6eaf2cd271461) >> 58]; +#else + static const int table[32] = { + 0, 9, 1, 10, 13, 21, 2, 29, + 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, + 19, 27, 23, 6, 26, 5, 4, 31}; + + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + + return table[(x * 0x07C4ACDD) >> 27]; +#endif +} + +#endif \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..c4f539b --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,4 @@ +lib_LIBRARIES = libmalloc.a +libmalloc_a_SOURCES = bitmap_alloc.c memmap.c + +libmalloc_a_CFLAGS = -I$(prefix)/include \ No newline at end of file diff --git a/src/bitmap_alloc.c b/src/bitmap_alloc.c new file mode 100644 index 0000000..ceb28a8 --- /dev/null +++ b/src/bitmap_alloc.c @@ -0,0 +1,332 @@ +#include "libmalloc/bitmap_alloc.h" +#include "libmalloc/common.h" +#include "util.h" + +/* + * The number of bits contained in a single integer inside the heap's bitmap. + * Should be either 32 or 64 depending on the host machine. + */ +static const int bitmap_word_size = 8 * sizeof(unsigned long); + +/* + * Sets all elements in the cache's underlying array to 0. + */ +static inline void clear_cache(bitmap_heap_descriptor_t *heap) +{ + for(int i = 0; i < heap->cache_capacity; i++) + { + heap->cache[i] = 0; + } +} + +/* + * Clears all bits in the heap's bitmap. + */ +static inline void clear_bitmap(bitmap_heap_descriptor_t *heap) +{ + for(int i = 0; i < heap->bitmap_size / sizeof(*heap->bitmap); i++) + { + heap->bitmap[i] = 0; + } +} + +/* + * Sets bit `index` in the heap's bitmap, marking the underlying block as + * available. + */ +static inline void set_bit(bitmap_heap_descriptor_t *heap, int index) +{ + int bitmap_index = index / bitmap_word_size; + int bitmap_offset = index % bitmap_word_size; + heap->bitmap[bitmap_index] |= (unsigned long)1 << bitmap_offset; +} + +/* + * Clears bit `index` in the heap's bitmap, marking the underlying block as + * reserved. + */ +static inline void clear_bit(bitmap_heap_descriptor_t *heap, int index) +{ + int bitmap_index = index / bitmap_word_size; + int bitmap_offset = index % bitmap_word_size; + heap->bitmap[bitmap_index] &= ~((unsigned long)1 << bitmap_offset); +} + +/* + * Sets bit `index` and its buddy in the heap's bitmap, marking the underlying + * blocks as available. Operation is used while spltting a block to reserve one + * of its child blocks. + */ +static inline void set_pair(bitmap_heap_descriptor_t *heap, int index) +{ + int bitmap_index = index / bitmap_word_size; + int bitmap_offset = index % bitmap_word_size; + heap->bitmap[bitmap_index] |= (unsigned long)1 << bitmap_offset; + heap->bitmap[bitmap_index] |= (unsigned long)1 << (bitmap_offset ^ 1); +} + +/* + * Clears bit `index` and its buddy in the heap's bitmap, marking the underlying + * blocks as reserved. Used when merging two child blocks into a single parent block. + */ +static inline void clear_pair(bitmap_heap_descriptor_t *heap, int index) +{ + int bitmap_index = index / bitmap_word_size; + int bitmap_offset = index % bitmap_word_size; + heap->bitmap[bitmap_index] &= ~((unsigned long)1 << bitmap_offset); + heap->bitmap[bitmap_index] &= ~((unsigned long)1 << (bitmap_offset ^ 1)); +} + +/* + * Computes the location in the cache that `index` would be stored at, if it + * were cached. + */ +static inline int cache_location_from_index(int index) +{ + return llog2(index + 1) - llog2(bitmap_word_size) - 1; +} + +/* + * Returns a cached value from the desired location, removing that value from + * the cache. + */ +static inline int check_cache(bitmap_heap_descriptor_t *heap, int height) +{ + unsigned long n = heap->cache[heap->height - height - llog2(bitmap_word_size)]; + heap->cache[heap->height - height - llog2(bitmap_word_size)] = 0; + return n; +} + +/* + * If space in the cache exists, stores the provided index at the appropriate + * location. + */ +static inline void store_cache(bitmap_heap_descriptor_t *heap, int index) +{ + int level = cache_location_from_index(index); + if(level >= 0 && heap->cache[level] == 0) + { + heap->cache[level] = index; + } +} + +/* + * If `index` is stored in the cache, remove it. Otherwise, leave the cache + * as is. + */ +static inline void uncache(bitmap_heap_descriptor_t *heap, int index) +{ + int level = cache_location_from_index(index); + if(level >= 0 && heap->cache[level] == index) + { + heap->cache[level] = 0; + } +} + +/* + * Marks the indicated block as unavailable, and marks its children as both + * available. Stores the right-hand child in the cache, and returns the index + * of the left-hand child. The caller is expected to either split or allocate + * the left-hand child. + */ +static inline int split_block(bitmap_heap_descriptor_t *heap, int index) +{ + if(index) + { + clear_bit(heap, index); + index *= 2; + set_pair(heap, index); + store_cache(heap, index + 1); + } + return index; +} + +/* + * If the buddy of the indicated block is marked as available, marks the + * indicated block and its buddy as unavailable, and marks their parent + * as available. If the buddy of the indicated block is cached, it kill be + * removed from the cache. + * + * If the buddy of the indicated block is marked as unavailable, this function + * does nothing. The block indicated by `index` is assumed to be available. + */ +static int merge_block(bitmap_heap_descriptor_t *heap, int index) +{ + while(index > 1 && (heap->bitmap[index / bitmap_word_size] & ((unsigned long)1 << ((index % bitmap_word_size) ^ 1)))) + { + uncache(heap, index ^ 1); + clear_pair(heap, index); + index /= 2; + set_bit(heap, index); + } + return index; +} + +/* + * Finds the index of the first available block at `height`. If no such block + * is available, recursively searches higher blocks and splits them until an + * appropriate block is available. Returns 0 if no available blocks of sufficient + * size exist. + */ +static int find_free_region(bitmap_heap_descriptor_t *heap, int height) +{ + if (height > heap->height || height < 0) + { + return 0; + } + else if (height <= heap->height - ilog2(bitmap_word_size)) + { + unsigned long cached_index = check_cache(heap, height); + if(cached_index) + { + return cached_index; + } + unsigned long start = (1 << (heap->height - height)) / bitmap_word_size; + unsigned long end = ((1 << (heap->height - height + 1)) / bitmap_word_size); + for (int index = start; index < end; index++) + { + if (heap->bitmap[index] != 0) + { + return bitmap_word_size * index + __builtin_ctzl(heap->bitmap[index]); + } + } + } + else + { +#if __SIZEOF_LONG__ == 8 + static const unsigned long bitmasks[] = {0x00000002, 0x0000000C, 0x000000F0, 0x0000FF00, 0xFFFF0000, 0xFFFFFFFF00000000}; +#else + static const unsigned long bitmasks[] = {0x00000002, 0x0000000C, 0x000000F0, 0x0000FF00, 0xFFFF0000}; +#endif + int depth = heap->height - height; + if (heap->bitmap[0] & bitmasks[depth]) + { + return __builtin_ctzl(heap->bitmap[0] & bitmasks[depth]); + } + } + return split_block(heap, find_free_region(heap, height + 1)); +} + + +unsigned long reserve_region(bitmap_heap_descriptor_t *heap, unsigned long size) +{ + int height = llog2(size / heap->block_size); + int index = find_free_region(heap, height); + if(index) + { + clear_bit(heap, index); + heap->free_block_count -= 1 << height; + return (heap->block_size << height) * (index - ((unsigned long)1 << (heap->height - height))); + } + else + { + return NOMEM; + } +} + +void free_region(bitmap_heap_descriptor_t *heap, unsigned long location, unsigned long size) +{ + int height = llog2(size / heap->block_size); + int index = (location / (heap->block_size * ((unsigned long)1 << height))) + (1 << (heap->height - height)); + set_bit(heap, index); + index = merge_block(heap, index); + store_cache(heap, index); + heap->free_block_count += 1 << height; +} + +unsigned long bitmap_size(const memory_map_t *map, unsigned long block_size) +{ + // Find the last available region in the memory map. + int map_index = map->size - 1; + while(map->array[map_index].type != M_AVAILABLE) + { + map_index--; + } + + // Take memory_size to be the last available location in the memory map. + // Round memory_size up to nearest power of 2 + unsigned long memory_size = 1 << llog2(map->array[map_index].location + map->array[map_index].size); + return (memory_size / block_size) / 4; +} + +int initialize_virtual_heap(bitmap_heap_descriptor_t *heap, const memory_map_t *map, int (*mmap)(void *location, unsigned long size)) +{ + /* Not yet implemented */ + return -1; +} + +int initialize_physical_heap(bitmap_heap_descriptor_t *heap, const memory_map_t *map) +{ + // Find the last available region in the memory map. + int map_index = map->size - 1; + while(map->array[map_index].type != M_AVAILABLE) + { + map_index--; + } + + // Take memory_size to be the last available location in the memory map. + // Round memory_size up to nearest power of 2 + unsigned long memory_size = 1 << llog2(map->array[map_index].location + map->array[map_index].size); + heap->bitmap_size = (memory_size / heap->block_size) / 4; + heap->height = llog2(memory_size / heap->block_size); + heap->free_block_count = 0; + clear_bitmap(heap); + for(int i = 0; i < map->size; i++) + { + if(map->array[i].type != M_AVAILABLE) + { + continue; + } + + unsigned long location = (map->array[i].location + heap->block_size - 1) & ~(heap->block_size - 1); + unsigned long region_end = map->array[i].location + map->array[i].size; + + while(location + heap->block_size <= region_end) + { + int bit_offset = (location / heap->block_size) % bitmap_word_size; + int bitmap_index = ((1 << (heap->height - 0)) / bitmap_word_size) + (location / heap->block_size) / bitmap_word_size; + unsigned long chunk_size = (bitmap_word_size - bit_offset) * heap->block_size; + if(bit_offset == 0 && (region_end - location) >= chunk_size) + { + // Set all bits in the word + heap->bitmap[bitmap_index] = ~0; + heap->free_block_count += bitmap_word_size; + } + else if(bit_offset == 0) + { + // Set the first 'count' bits + int count = (region_end - location) / heap->block_size; + heap->bitmap[bitmap_index] |= (1 << count) - 1; + heap->free_block_count += count; + } + else if((region_end - location) >= chunk_size) + { + // Set all bits starting at 'bit_offset' + heap->bitmap[bitmap_index] |= ~((1 << bit_offset) - 1); + heap->free_block_count += bitmap_word_size - bit_offset; + } + else + { + // Set all bits starting at 'bit_offset' up to 'count' + int count = (region_end - location) / heap->block_size; + heap->bitmap[bitmap_index] |= ((1 << count) - 1) & ~((1 << bit_offset) - 1); + heap->free_block_count += count - bit_offset; + } + + // Merge 'buddies' when both available + unsigned long mask = 3; + for(int j = 0; j < bitmap_word_size / 2; j++) + { + if((heap->bitmap[bitmap_index] & mask) == mask) + { + merge_block(heap, bitmap_index * bitmap_word_size + j * 2); + } + mask <<= 2; + } + + location += chunk_size; + } + } + clear_cache(heap); + return 0; +} diff --git a/src/memmap.c b/src/memmap.c new file mode 100644 index 0000000..c823a01 --- /dev/null +++ b/src/memmap.c @@ -0,0 +1,144 @@ +#include "libmalloc/memmap.h" +#include + +static int compare_regions(memory_region_t *lhs, memory_region_t *rhs) +{ + if(lhs->location == rhs->location) + { + return lhs->size > rhs->size ? 1 + : (lhs->size == rhs->size ? 0 + : -1); + } + else + { + return lhs->location > rhs->location ? 1 : -1; + } +} + +static bool region_overlaps(memory_region_t *lhs, memory_region_t *rhs) +{ + if(rhs->location < lhs->location) + { + return rhs->location + rhs->size > lhs->location; + } + return rhs->location < lhs->location + lhs->size; +} + +static bool region_contains(memory_region_t *lhs, memory_region_t *rhs) +{ + return (rhs->location >= lhs->location) && + (rhs->location + rhs->size <= lhs->location + lhs->size); +} + +static void insert_map_entry(memory_map_t *map, unsigned long location, unsigned long size, unsigned int type) +{ + memory_region_t new_region = {.location = location, .size = size, .type = type}; + unsigned int i = 0; + while(i < map->size) + { + if(compare_regions(&new_region, &map->array[i]) < 0) + { + memory_region_t buffer = new_region; + new_region = map->array[i]; + map->array[i] = buffer; + } + i++; + } + map->array[i] = new_region; + map->size++; +} + +void remove_map_entry(memory_map_t *map, int index) +{ + if(index >= 0 && index < map->size) + { + for(int i = index; i < map->size - 1; i++) + { + map->array[i] = map->array[i + 1]; + } + map->size--; + } +} + +static int trim_map(memory_map_t *map, int index) +{ + if(index + 1 >= map->size) + { + return -1; + } + memory_region_t *left = &map->array[index]; + memory_region_t *right = &map->array[index + 1]; + if(region_overlaps(left, right)) + { + if(left->type == right->type) + { + left->size = (right->location + right->size > left->location + left->size ? right->location + right->size : left->location + left->size) - left->location; + remove_map_entry(map, index + 1); + return index; + } + else if(left->type < right->type) + { + if(region_contains(right, left)) + { + remove_map_entry(map, index); + return index; + } + else if(left->location + left->size <= right->location + right->size) + { + left->size = (right->location > left->location) ? right->location - left->location : 0; + return index + 1; + } + else + { + memory_region_t new_right = { + .location = right->location + right->size, + .size = (left->location + left->size) - (right->location + right->size), + .type = left->type}; + left->size = (right->location > left->location) ? right->location - left->location : 0; + if(left->size == 0) + remove_map_entry(map, index); + insert_map_entry(map, new_right.location, new_right.size, new_right.type); + return index + 2; + } + } + else + { + if(region_contains(left, right)) + { + remove_map_entry(map, index + 1); + return index; + } + else + { + right->size = (right->location + right->size) - (left->location + left->size); + right->location = left->location + left->size; + return index + 1; + } + } + } + else if((left->location + left->size == right->location) && left->type == right->type) + { + left->size = right->location + right->size - left->location; + remove_map_entry(map, index + 1); + return index; + } + return index + 1; +} + +int memmap_insert_region(memory_map_t *map, unsigned long location, unsigned long size, memory_type_t type) +{ + if(map->size <= map->capacity - 2) + { + insert_map_entry(map, location, size, type); + int i = 0; + while(i >= 0) + { + i = trim_map(map, i); + } + return 0; + } + else + { + return -1; + } +} \ No newline at end of file diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..1e03683 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,6 @@ +if BUILD_TESTS + noinst_PROGRAMS = test_buddyalloc + + test_buddyalloc_SOURCES = test_buddyalloc.c + test_buddyalloc_LDADD = ../src/libmalloc.a +endif \ No newline at end of file diff --git a/tests/test_buddyalloc.c b/tests/test_buddyalloc.c new file mode 100644 index 0000000..358e56e --- /dev/null +++ b/tests/test_buddyalloc.c @@ -0,0 +1,51 @@ +#include "libmalloc/bitmap_alloc.h" +#include "util.h" +#include +#include +#include + +int main(int argc, char **args) +{ + const int memory_map_capacity = 32; + const int heap_size = 1 << 20; + const int cache_capacity = 20; + memory_region_t arr[memory_map_capacity]; + unsigned long heap_cache[cache_capacity]; + + memory_map_t memory_map = { + .array = arr, + .capacity = memory_map_capacity, + .size = 0 + }; + + bitmap_heap_descriptor_t heap = { + .bitmap = malloc(heap_size / 4), + .block_size = 1, + .cache = heap_cache, + .cache_capacity = cache_capacity + }; + + memmap_insert_region(&memory_map, 0, heap_size, M_AVAILABLE); + initialize_physical_heap(&heap, &memory_map); + + unsigned long *reserved_blocks = malloc(sizeof(unsigned long) * heap_size); + for(int i = 0; i < heap_size; i++) + { + reserved_blocks[i] = reserve_region(&heap, 1); + } + + /*for(int i = 0; i < heap_size; i++) + { + for(int j = i + 1; j < heap_size; j++) + { + assert(reserved_blocks[i] != reserved_blocks[j]); + } + }*/ + + for(int i = 0; i < heap_size; i++) + { + free_region(&heap, reserved_blocks[i], 1); + } + + assert(heap.free_block_count == heap_size); +} \ No newline at end of file