Initial commit
This commit is contained in:
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@@ -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
|
||||||
1
Makefile.am
Normal file
1
Makefile.am
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SUBDIRS = src include tests
|
||||||
13
README.md
Normal file
13
README.md
Normal file
@@ -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=<host>] [--prefix=<your_desired_prefix>] [CFLAGS=-ffreestanding LDFLAGS=-nostdlib]`
|
||||||
|
- `make`
|
||||||
23
configure.ac
Normal file
23
configure.ac
Normal file
@@ -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
|
||||||
1
include/Makefile.am
Normal file
1
include/Makefile.am
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nobase_include_HEADERS = libmalloc/bitmap_alloc.h libmalloc/memmap.h libmalloc/common.h
|
||||||
153
include/libmalloc/bitmap_alloc.h
Normal file
153
include/libmalloc/bitmap_alloc.h
Normal file
@@ -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
|
||||||
13
include/libmalloc/common.h
Normal file
13
include/libmalloc/common.h
Normal file
@@ -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
|
||||||
27
include/libmalloc/memmap.h
Normal file
27
include/libmalloc/memmap.h
Normal file
@@ -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
|
||||||
69
include/util.h
Normal file
69
include/util.h
Normal file
@@ -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
|
||||||
4
src/Makefile.am
Normal file
4
src/Makefile.am
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
lib_LIBRARIES = libmalloc.a
|
||||||
|
libmalloc_a_SOURCES = bitmap_alloc.c memmap.c
|
||||||
|
|
||||||
|
libmalloc_a_CFLAGS = -I$(prefix)/include
|
||||||
332
src/bitmap_alloc.c
Normal file
332
src/bitmap_alloc.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
144
src/memmap.c
Normal file
144
src/memmap.c
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
#include "libmalloc/memmap.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
tests/Makefile.am
Normal file
6
tests/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
if BUILD_TESTS
|
||||||
|
noinst_PROGRAMS = test_buddyalloc
|
||||||
|
|
||||||
|
test_buddyalloc_SOURCES = test_buddyalloc.c
|
||||||
|
test_buddyalloc_LDADD = ../src/libmalloc.a
|
||||||
|
endif
|
||||||
51
tests/test_buddyalloc.c
Normal file
51
tests/test_buddyalloc.c
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "libmalloc/bitmap_alloc.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user