iterative api
This commit is contained in:
@@ -1,45 +1,212 @@
|
|||||||
|
/*
|
||||||
|
* Bit stuff required to understand the code:
|
||||||
|
*
|
||||||
|
* 1. Division and multiplication using shifts
|
||||||
|
*
|
||||||
|
* It is possible to perform multiplication and division by
|
||||||
|
* a power of 2 using shift operations.
|
||||||
|
*
|
||||||
|
* Starting by the simple case, the binary representation
|
||||||
|
* of 1 and 2 is:
|
||||||
|
*
|
||||||
|
* x = 0000 0001
|
||||||
|
* y = 0000 0010
|
||||||
|
*
|
||||||
|
* So 2 is 1 shifted left by 1. It's pretty intuitive that
|
||||||
|
* this works for any power of 2. Shifting left by 1 equals
|
||||||
|
* multiplying by 2. Shifting by more than 1 has the effect
|
||||||
|
* of multiplying by a power of 2 with the shift amount as
|
||||||
|
* exponent.
|
||||||
|
*
|
||||||
|
* For values that aren't powers of 2, we can see them as
|
||||||
|
* sums of such powers:
|
||||||
|
*
|
||||||
|
* 453 = 0000 0001 1100 0101
|
||||||
|
* = 2^0 + 2^2 + 2^6 + 2^7 + 2^8
|
||||||
|
* = (1 << 0) + (1 << 2) + (1 << 6) + (1 << 7) + (1 << 8)
|
||||||
|
*
|
||||||
|
* Multiplying by 2, each power of 2 that makes up the value
|
||||||
|
* shifts by 1, making the entire value shift too. Here is
|
||||||
|
* the proof:
|
||||||
|
*
|
||||||
|
* 2 * 453 = 2 * (2^0 + 2^2 + 2^6 + 2^7 + 2^8)
|
||||||
|
* = 2 * ((1 << 0) + (1 << 2) + (1 << 6) + (1 << 7) + (1 << 8))
|
||||||
|
* = ((1 << 0) + (1 << 2) + (1 << 6) + (1 << 7) + (1 << 8)) << 1
|
||||||
|
* = (1 << (0 + 1)) + (1 << (2 + 1)) + (1 << (6 + 1)) + (1 << (7 + 1)) + (1 << (8 + 1))
|
||||||
|
* = ((1 << 1) + (1 << 3) + (1 << 7) + (1 << 8) + (1 << 9))
|
||||||
|
* = 0000 0011 1000 1010
|
||||||
|
*
|
||||||
|
* So this works for all values. Similarly, shifting right
|
||||||
|
* divides by 2.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 2. Modulo using bitwise ands
|
||||||
|
*
|
||||||
|
* The modulo operator returns the remainder of the division:
|
||||||
|
*
|
||||||
|
* 104 % 10 = 4
|
||||||
|
*
|
||||||
|
* When the right operand is a power of the base the two numbers
|
||||||
|
* are represented in, getting the result is easy. In base 10 this
|
||||||
|
* works when the right operand is 10, 100, 1000, etc. If N is the
|
||||||
|
* number of zeros of the right operand, the remainder is the number
|
||||||
|
* made by the lower N digits of the left operand. For instance:
|
||||||
|
*
|
||||||
|
* 435430598 % 1000 = 598
|
||||||
|
*
|
||||||
|
* This works the same way in base 2 when the right operand is a
|
||||||
|
* power of 2:
|
||||||
|
*
|
||||||
|
* 10001011010 % 100 = 10
|
||||||
|
* 10001011010 % 10000 = 1010
|
||||||
|
*
|
||||||
|
* In base 2 getting the lower N digits is very easy and can be
|
||||||
|
* done using a mask with a bitwise and operation. The mask can
|
||||||
|
* be calculate subtracting 1 by the right operand:
|
||||||
|
*
|
||||||
|
* 100-1 = 011
|
||||||
|
* 10000-1 = 01111
|
||||||
|
*
|
||||||
|
* So finally, when the right operand is a power of 2:
|
||||||
|
*
|
||||||
|
* x % y == x & (y - 1)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 3. Check if a word is a power of 2. A power of 2 has only
|
||||||
|
* one high bit:
|
||||||
|
*
|
||||||
|
* x = 0000 0100
|
||||||
|
*
|
||||||
|
* Subtracting 1 from it will result in the only high bit to
|
||||||
|
* become 0 and all of the lower 0 to become 1.
|
||||||
|
* to become 1 and:
|
||||||
|
*
|
||||||
|
* y = x - 1 = 0000 0011
|
||||||
|
*
|
||||||
|
* This makes it so x and y share no high bits and the
|
||||||
|
* bitwise "and" operation is 0.
|
||||||
|
*
|
||||||
|
* On the other hand, for something other than a power of 2
|
||||||
|
* at least 2 bits are high. Subtracting 1 will lower the least
|
||||||
|
* significant bit but keep the most significant ones:
|
||||||
|
*
|
||||||
|
* z = 0100 0100
|
||||||
|
* w = z - 1 = 0100 0011
|
||||||
|
*
|
||||||
|
* So z and w will share at least one high bit. The bitwise
|
||||||
|
* "and" operation is never zero for something that's not a
|
||||||
|
* power of 2.
|
||||||
|
*
|
||||||
|
* In conclusion, we can test a power of 2 using:
|
||||||
|
*
|
||||||
|
* n & (n - 1) == 0
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 4. Aligning to power of 2 boundary
|
||||||
|
*
|
||||||
|
* Given an integer x, we call it "aligned to y" when it's
|
||||||
|
* a multiple of y. Sometimes we need a way to calculate
|
||||||
|
* the first integer aligned to a boundary that comes a
|
||||||
|
* given number.
|
||||||
|
*
|
||||||
|
* Calculating ho far the integer is from the last boundary
|
||||||
|
* is possible using the modulo operator
|
||||||
|
*
|
||||||
|
* delta_from_last_boundary = x % boundary
|
||||||
|
*
|
||||||
|
* therefore we can calculate the distance from the following
|
||||||
|
* boundary by doing:
|
||||||
|
*
|
||||||
|
* delta_from_next_boundary = boundary - delta_from_last_boundary
|
||||||
|
* = boundary - x % boundary
|
||||||
|
*
|
||||||
|
* There is also one other and faster way. Lets say x is a
|
||||||
|
* positive number lower than boundary, therefore the last
|
||||||
|
* boundary is 0 and the next is boundary exactly.
|
||||||
|
*
|
||||||
|
* last boundary
|
||||||
|
* | next boundary
|
||||||
|
* v v
|
||||||
|
* - -- --- -----+----------+-------x--+----- --- -- -
|
||||||
|
* -B 0 B
|
||||||
|
*
|
||||||
|
* Negating x, the distance from the two boundaries is inverted:
|
||||||
|
*
|
||||||
|
* last boundary
|
||||||
|
* | next boundary
|
||||||
|
* v v
|
||||||
|
* - -- --- -----+--y-------+----------+----- --- -- -
|
||||||
|
* -B 0 B
|
||||||
|
*
|
||||||
|
* y = -x
|
||||||
|
*
|
||||||
|
* So we can get the distance from the next boundary from x
|
||||||
|
* calculating the modulo on -x.
|
||||||
|
*
|
||||||
|
* delta_from_next_boundary = -x % boundary
|
||||||
|
*
|
||||||
|
* When the boundary is a power of 2, the modulo can be calculated
|
||||||
|
* using a bitwise and:
|
||||||
|
*
|
||||||
|
* delta_from_next_boundart = -x & (boundary - 1)
|
||||||
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define BUDDY_DEBUG
|
||||||
|
#ifdef BUDDY_DEBUG
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "buddy.h"
|
#include "buddy.h"
|
||||||
|
|
||||||
enum {
|
#define MAX_BLOCK_LOG2 BUDDY_ALLOC_MAX_BLOCK_LOG2
|
||||||
INDEX_256B,
|
#define MIN_BLOCK_LOG2 BUDDY_ALLOC_MIN_BLOCK_LOG2
|
||||||
INDEX_512B,
|
#define MAX_BLOCK_SIZE (1 << MAX_BLOCK_LOG2)
|
||||||
INDEX_1K,
|
#define MIN_BLOCK_SIZE (1 << MIN_BLOCK_LOG2)
|
||||||
INDEX_2K,
|
#define MAX_BLOCK_ALIGN_MASK (MAX_BLOCK_SIZE - 1)
|
||||||
INDEX_4K,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct page {
|
struct page {
|
||||||
struct page *next;
|
struct page *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
void init_buddy_alloc(struct buddy_alloc *alloc,
|
static struct page*
|
||||||
char *base, size_t size,
|
page_index_to_ptr(char *base, int i)
|
||||||
uint32_t *bitsets,
|
|
||||||
int num_bitsets)
|
|
||||||
{
|
{
|
||||||
size_t page_size = 1 << 12;
|
return (struct page*) (base + (i << MAX_BLOCK_LOG2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct buddy_alloc startup_empty()
|
||||||
|
{
|
||||||
|
struct buddy_alloc alloc;
|
||||||
|
alloc.base = NULL;
|
||||||
|
alloc.info = NULL;
|
||||||
|
alloc.num_info = 0;
|
||||||
|
for (int i = 0; i < BUDDY_ALLOC_NUM_LISTS; i++)
|
||||||
|
alloc.lists[i] = NULL;
|
||||||
|
return alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct buddy_alloc buddy_startup(char *base, size_t size,
|
||||||
|
struct page_info *info,
|
||||||
|
int num_info)
|
||||||
|
{
|
||||||
|
if (base == NULL || info == NULL)
|
||||||
|
return startup_empty();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ad some padding to the start of the
|
* Ad some padding to the start of the
|
||||||
* memory pool to align at a page boundary.
|
* memory pool to align at a page boundary.
|
||||||
*/
|
*/
|
||||||
size_t pad = -(uintptr_t) base & (page_size - 1);
|
size_t pad = -(uintptr_t) base & MAX_BLOCK_ALIGN_MASK;
|
||||||
|
|
||||||
if (pad > size) {
|
if (pad > size) {
|
||||||
/*
|
/*
|
||||||
* Pool doesn't have a whole page
|
* Pool doesn't even have a page
|
||||||
*/
|
*/
|
||||||
alloc->base = NULL;
|
return startup_empty();
|
||||||
alloc->lists[INDEX_256B] = NULL;
|
|
||||||
alloc->lists[INDEX_512B] = NULL;
|
|
||||||
alloc->lists[INDEX_1K] = NULL;
|
|
||||||
alloc->lists[INDEX_2K] = NULL;
|
|
||||||
alloc->lists[INDEX_4K] = NULL;
|
|
||||||
alloc->bitsets = NULL;
|
|
||||||
alloc->num_bitsets = 0;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base += pad;
|
base += pad;
|
||||||
@@ -48,45 +215,44 @@ void init_buddy_alloc(struct buddy_alloc *alloc,
|
|||||||
/*
|
/*
|
||||||
* Make the size a multiple of 4K
|
* Make the size a multiple of 4K
|
||||||
*/
|
*/
|
||||||
size_t rem = size % page_size;
|
size_t rem = size & MAX_BLOCK_ALIGN_MASK;
|
||||||
|
|
||||||
size -= rem;
|
size -= rem;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each page requires a bitset to keep track of its state
|
* Each page requires a bitset to keep track of its state
|
||||||
*/
|
*/
|
||||||
size_t max_bytes = (size_t) num_bitsets * page_size;
|
size_t max_bytes = (size_t) num_info << MAX_BLOCK_LOG2;
|
||||||
if (size > max_bytes)
|
if (size > max_bytes)
|
||||||
size = max_bytes;
|
size = max_bytes;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make the linked list of pages
|
* Make the linked list of pages
|
||||||
*/
|
*/
|
||||||
struct page *head;
|
struct page *head = NULL;
|
||||||
struct page **tail = &head;
|
struct page **tail = &head;
|
||||||
size_t num_pages = size / page_size;
|
int num_pages = size >> MAX_BLOCK_LOG2;
|
||||||
|
for (int i = 0; i < num_pages; i++) {
|
||||||
for (size_t i = 0; i < num_pages; i++) {
|
struct page *p = page_index_to_ptr(base, i);
|
||||||
struct page *p = (struct page*) (base + i * page_size);
|
|
||||||
*tail = p;
|
*tail = p;
|
||||||
tail = &p->next;
|
tail = &p->next;
|
||||||
}
|
}
|
||||||
*tail = NULL;
|
*tail = NULL;
|
||||||
|
|
||||||
alloc->base = base;
|
assert(info);
|
||||||
alloc->lists[INDEX_256B] = NULL;
|
memset(info, 0, num_info * sizeof(struct page_info));
|
||||||
alloc->lists[INDEX_512B] = NULL;
|
|
||||||
alloc->lists[INDEX_1K] = NULL;
|
|
||||||
alloc->lists[INDEX_2K] = NULL;
|
|
||||||
alloc->lists[INDEX_4K] = head;
|
|
||||||
alloc->bitsets = bitsets;
|
|
||||||
alloc->num_bitsets = num_bitsets;
|
|
||||||
|
|
||||||
for (int i = 0; i < num_bitsets; i++)
|
struct buddy_alloc alloc;
|
||||||
alloc->bitsets[i] = 0;
|
alloc.base = base,
|
||||||
|
alloc.info = info;
|
||||||
|
alloc.num_info = num_info;
|
||||||
|
for (int i = 0; i < BUDDY_ALLOC_NUM_LISTS-1; i++)
|
||||||
|
alloc.lists[i] = NULL;
|
||||||
|
alloc.lists[BUDDY_ALLOC_NUM_LISTS-1] = head;
|
||||||
|
|
||||||
|
return alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void free_buddy_alloc(struct buddy_alloc *alloc)
|
void buddy_cleanup(struct buddy_alloc *alloc)
|
||||||
{
|
{
|
||||||
(void) alloc;
|
(void) alloc;
|
||||||
}
|
}
|
||||||
@@ -110,43 +276,8 @@ static size_t round_pow2(size_t v)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int first_set_8(uint8_t x)
|
||||||
* Returns the index from the right of the
|
|
||||||
* first set bit or -1 otherwise.
|
|
||||||
*/
|
|
||||||
static int first_set_bit(size_t bits)
|
|
||||||
{
|
{
|
||||||
// First check that at least one bit is set
|
|
||||||
if (bits == 0) return -1;
|
|
||||||
|
|
||||||
size_t bits_no_rightmost = bits & (bits - 1);
|
|
||||||
size_t bits_only_rightmost = bits - bits_no_rightmost;
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
size_t temp;
|
|
||||||
|
|
||||||
if (sizeof(size_t) > 4) {
|
|
||||||
// The index of the rightmost bit is the log2
|
|
||||||
temp = bits_only_rightmost >> 32;
|
|
||||||
if (temp) {
|
|
||||||
// Bit is in the upper 32 bits
|
|
||||||
index += 32;
|
|
||||||
bits_only_rightmost = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
temp = bits_only_rightmost >> 16;
|
|
||||||
if (temp) {
|
|
||||||
index += 16;
|
|
||||||
bits_only_rightmost = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
temp = bits_only_rightmost >> 8;
|
|
||||||
if (temp) {
|
|
||||||
index += 8;
|
|
||||||
bits_only_rightmost = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const unsigned char table[] = {
|
static const unsigned char table[] = {
|
||||||
0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
|
||||||
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
@@ -165,297 +296,392 @@ static int first_set_bit(size_t bits)
|
|||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
};
|
};
|
||||||
|
return table[x];
|
||||||
index += table[bits_only_rightmost];
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *malloc_internal(struct buddy_alloc *alloc, size_t len)
|
// Returns the index from the right of the first set bit or -1 otherwise.
|
||||||
|
static int first_set(size_t x)
|
||||||
{
|
{
|
||||||
size_t page_size = 1 << 12;
|
size_t y;
|
||||||
|
size_t z;
|
||||||
|
size_t t;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (len > page_size)
|
// First check that at least one bit is set
|
||||||
return NULL;
|
if (x == 0) return -1;
|
||||||
|
|
||||||
|
y = x & (x - 1);
|
||||||
|
z = x - y;
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
if (sizeof(size_t) > 4) {
|
||||||
|
t = z >> 32;
|
||||||
|
if (t) {
|
||||||
|
i += 32;
|
||||||
|
z = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t = z >> 16;
|
||||||
|
if (t) {
|
||||||
|
i += 16;
|
||||||
|
z = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = z >> 8;
|
||||||
|
if (t) {
|
||||||
|
i += 8;
|
||||||
|
z = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += first_set_8(z);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t page_index(struct buddy_alloc *alloc, void *ptr)
|
||||||
|
{
|
||||||
|
uintptr_t x = (uintptr_t) ptr;
|
||||||
|
uintptr_t y = (uintptr_t) alloc->base;
|
||||||
|
assert(x >= y);
|
||||||
|
return (x - y) >> MAX_BLOCK_LOG2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
4K ------------------------ 0
|
||||||
|
2K -------------------- 1
|
||||||
|
1K ---------------- 2
|
||||||
|
512B ---------- 3
|
||||||
|
256B ------ 4
|
||||||
|
256B ------ 5
|
||||||
|
512B ---------- 6
|
||||||
|
256B ------ 7
|
||||||
|
256B ------ 8
|
||||||
|
1K ---------------- 9
|
||||||
|
512B ---------- 10
|
||||||
|
256B ------ 11
|
||||||
|
256B ------ 12
|
||||||
|
512B ---------- 13
|
||||||
|
256B ------ 14
|
||||||
|
256B ------ 15
|
||||||
|
2K -------------------- 16
|
||||||
|
1K ---------------- 17
|
||||||
|
512B ---------- 18
|
||||||
|
256B ------ 19
|
||||||
|
256B ------ 20
|
||||||
|
512B ---------- 21
|
||||||
|
256B ------ 22
|
||||||
|
256B ------ 23
|
||||||
|
1K ---------------- 24
|
||||||
|
512B ---------- 25
|
||||||
|
256B ------ 26
|
||||||
|
256B ------ 27
|
||||||
|
512B ---------- 28
|
||||||
|
256B ------ 29
|
||||||
|
256B ------ 30
|
||||||
|
*/
|
||||||
|
|
||||||
|
static size_t block_info_index(void *ptr, size_t len)
|
||||||
|
{
|
||||||
|
int len_log2 = first_set(len);
|
||||||
|
size_t reloff = ((uintptr_t) ptr) & MAX_BLOCK_ALIGN_MASK;
|
||||||
|
return (1U << (MAX_BLOCK_LOG2 - len_log2)) + (reloff >> len_log2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_allocated(struct buddy_alloc *alloc,
|
||||||
|
void *ptr, size_t len)
|
||||||
|
{
|
||||||
|
assert(is_pow2(len));
|
||||||
|
|
||||||
|
size_t i = page_index(alloc, ptr);
|
||||||
|
size_t j = block_info_index(ptr, len);
|
||||||
|
|
||||||
|
int bits_per_word_log2 = 5;
|
||||||
|
int bits_per_word = 1 << bits_per_word_log2;
|
||||||
|
|
||||||
|
int u = j >> bits_per_word_log2;
|
||||||
|
int v = j & (bits_per_word - 1);
|
||||||
|
|
||||||
|
uint32_t mask = 1U << v;
|
||||||
|
|
||||||
|
return (alloc->info[i].bits[u] & mask) == mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_allocated(struct buddy_alloc *alloc,
|
||||||
|
void *ptr, size_t len, bool value)
|
||||||
|
{
|
||||||
|
assert(is_pow2(len));
|
||||||
|
|
||||||
|
size_t i = page_index(alloc, ptr);
|
||||||
|
size_t j = block_info_index(ptr, len);
|
||||||
|
|
||||||
|
int bits_per_word_log2 = 5;
|
||||||
|
int bits_per_word = 1 << bits_per_word_log2;
|
||||||
|
|
||||||
|
int u = j >> bits_per_word_log2;
|
||||||
|
int v = j & (bits_per_word - 1);
|
||||||
|
|
||||||
|
uint32_t mask = 1U << v;
|
||||||
|
if (value)
|
||||||
|
alloc->info[i].bits[u] |= mask;
|
||||||
|
else
|
||||||
|
alloc->info[i].bits[u] &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_allocated_considering_splits(struct buddy_alloc *alloc,
|
||||||
|
void *ptr, size_t len)
|
||||||
|
{
|
||||||
|
if (len == MIN_BLOCK_SIZE)
|
||||||
|
return is_allocated(alloc, ptr, len);
|
||||||
|
|
||||||
|
char *sib = ptr + (len >> 1);
|
||||||
|
return is_allocated(alloc, ptr, len)
|
||||||
|
|| is_allocated_considering_splits(alloc, ptr, len >> 1)
|
||||||
|
|| is_allocated_considering_splits(alloc, sib, len >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t normalize_len(size_t len)
|
||||||
|
{
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return NULL;
|
return 0;
|
||||||
|
|
||||||
if (len < 256)
|
if (len < MIN_BLOCK_SIZE)
|
||||||
len = 256;
|
return MIN_BLOCK_SIZE;
|
||||||
else {
|
|
||||||
len = round_pow2(len);
|
|
||||||
if (len > page_size)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = first_set_bit(len);
|
return round_pow2(len);
|
||||||
|
|
||||||
assert(first_set_bit(256) == 8);
|
|
||||||
int list_idx = i - 8;
|
|
||||||
|
|
||||||
struct page *p = alloc->lists[list_idx];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there isn't a page of the appropriate size,
|
|
||||||
* allocate a block twice as big, allocate one half
|
|
||||||
* and put the other one in a list.
|
|
||||||
*/
|
|
||||||
if (p == NULL) {
|
|
||||||
|
|
||||||
char *ptr = malloc_internal(alloc, len << 1);
|
|
||||||
if (ptr == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
p = (struct page*) ptr;
|
|
||||||
p->next = NULL;
|
|
||||||
|
|
||||||
struct page *p2;
|
|
||||||
p2 = (struct page*) (ptr + len);
|
|
||||||
p2->next = NULL;
|
|
||||||
alloc->lists[list_idx] = p2;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
alloc->lists[list_idx] = p->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *get_sibling(void *ptr, size_t len)
|
static int list_index_for_size(size_t len)
|
||||||
{
|
{
|
||||||
size_t double_len = len << 1;
|
return first_set(len) - MIN_BLOCK_LOG2;
|
||||||
|
}
|
||||||
|
|
||||||
if ((uintptr_t) ptr & (double_len - 1))
|
// Get the sibling block of the one at position "ptr". If the block
|
||||||
|
// is aligned at double its size, the sibling is "len" bytes after
|
||||||
|
// it, else its len bytes before.
|
||||||
|
static char *sibling_of(char *ptr, size_t len)
|
||||||
|
{
|
||||||
|
assert(is_pow2(len));
|
||||||
|
|
||||||
|
// There is no such thing as a sibling of a page
|
||||||
|
assert(len < MAX_BLOCK_SIZE);
|
||||||
|
|
||||||
|
if (((uintptr_t) ptr & ((len << 1) - 1)) == 0)
|
||||||
return ptr + len;
|
return ptr + len;
|
||||||
else
|
else
|
||||||
return ptr - len;
|
return ptr - len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *parent_chunk(void *ptr, size_t len)
|
static char *parent_of(char *ptr, size_t len)
|
||||||
{
|
{
|
||||||
void *sib = get_sibling(ptr, len);
|
char *sib = sibling_of(ptr, len);
|
||||||
if ((uintptr_t) sib < (uintptr_t) ptr)
|
if ((uintptr_t) sib < (uintptr_t) ptr)
|
||||||
return sib;
|
return sib;
|
||||||
else
|
else
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
append_to_list(struct buddy_alloc *alloc,
|
sibling_allocated_considering_splits(struct buddy_alloc *alloc,
|
||||||
void *ptr, size_t len)
|
void *ptr, size_t len)
|
||||||
{
|
{
|
||||||
assert(is_pow2(len));
|
char *sib = sibling_of(ptr, len);
|
||||||
assert(len >= 256);
|
return is_allocated_considering_splits(alloc, sib, len);
|
||||||
|
|
||||||
int list_idx = first_set_bit(len) - 8;
|
|
||||||
|
|
||||||
assert(list_idx >= 0 && list_idx <= INDEX_4K);
|
|
||||||
|
|
||||||
struct page *p = ptr;
|
|
||||||
|
|
||||||
p->next = alloc->lists[list_idx];
|
|
||||||
alloc->lists[list_idx] = p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void free_internal(struct buddy_alloc *alloc,
|
static void
|
||||||
void *ptr, size_t len)
|
remove_sibling_from_list(struct buddy_alloc *alloc,
|
||||||
|
int i, void *ptr)
|
||||||
{
|
{
|
||||||
if (len < 256)
|
size_t len = 1U << (i + MIN_BLOCK_LOG2);
|
||||||
len = 256;
|
struct page *sibling = (struct page*) sibling_of(ptr, len);
|
||||||
else {
|
struct page *curs = (struct page*) alloc->lists[i];
|
||||||
if (len > 4096)
|
struct page **prev = (struct page**) &alloc->lists[i];
|
||||||
return;
|
while (curs != (struct page*) sibling) {
|
||||||
len = round_pow2(len);
|
assert(curs);
|
||||||
}
|
|
||||||
|
|
||||||
size_t page_size = 1 << 12;
|
|
||||||
assert(len > 0 && len <= page_size);
|
|
||||||
|
|
||||||
if (len == page_size) {
|
|
||||||
/*
|
|
||||||
* Deallocation is easy, just push into
|
|
||||||
* the last list.
|
|
||||||
*/
|
|
||||||
append_to_list(alloc, ptr, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Before placing this chunk in the free list
|
|
||||||
* look for its sibling and pop it.
|
|
||||||
*
|
|
||||||
* If the chunk is aligned to double its size,
|
|
||||||
* its sibling is the one after it, else it's
|
|
||||||
* the one before it.
|
|
||||||
*/
|
|
||||||
bool found = false;
|
|
||||||
char *sibling = get_sibling(ptr, len);
|
|
||||||
{
|
|
||||||
int list_idx = first_set_bit(len) - 8;
|
|
||||||
struct page *curs = (struct page*) alloc->lists[list_idx];
|
|
||||||
struct page **prev = (struct page**) &alloc->lists[list_idx];
|
|
||||||
|
|
||||||
while (curs) {
|
|
||||||
if (curs == (struct page*) sibling) {
|
|
||||||
*prev = curs->next;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prev = &curs->next;
|
prev = &curs->next;
|
||||||
curs = curs->next;
|
curs = curs->next;
|
||||||
|
assert(curs);
|
||||||
}
|
}
|
||||||
}
|
assert(sibling == curs);
|
||||||
|
*prev = sibling->next;
|
||||||
if (found == false) {
|
|
||||||
/*
|
|
||||||
* No sybling so just push this chunk in
|
|
||||||
* the list.
|
|
||||||
*/
|
|
||||||
append_to_list(alloc, ptr, len);
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Deallocate the larger chunk
|
|
||||||
*/
|
|
||||||
struct page *p = parent_chunk(ptr, len);
|
|
||||||
free_internal(alloc, p, len << 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_chunk_mask(void *ptr, size_t len)
|
/*
|
||||||
|
* Append the chunk at "ptr" to the i-th list.
|
||||||
|
* The size of the block can be calculated as:
|
||||||
|
*
|
||||||
|
* len = 1 << (i + MIN_BLOCK_LOG2)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void append(struct buddy_alloc *alloc,
|
||||||
|
int i, void *ptr)
|
||||||
{
|
{
|
||||||
assert(len > 0);
|
assert(i >= 0 && i < BUDDY_ALLOC_NUM_LISTS);
|
||||||
|
|
||||||
len = round_pow2(len);
|
struct page *pag = ptr;
|
||||||
|
|
||||||
size_t page_size = 1 << 12;
|
pag->next = alloc->lists[i];
|
||||||
|
alloc->lists[i] = pag;
|
||||||
uintptr_t x = (uintptr_t) ptr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the bit associated to the chunk
|
|
||||||
*
|
|
||||||
* The first bit refers to the entire page,
|
|
||||||
* the following 2 bits refer to its halfs,
|
|
||||||
* then the following 4 the halfs of the
|
|
||||||
* halfs and so on.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int len_log2 = first_set_bit(len);
|
|
||||||
assert(len_log2 <= 12);
|
|
||||||
|
|
||||||
// Pointer relative to its page
|
|
||||||
size_t reloff = x & (page_size - 1);
|
|
||||||
|
|
||||||
size_t chidx = reloff / len;
|
|
||||||
|
|
||||||
size_t sh = 12 - len_log2 + chidx;
|
|
||||||
|
|
||||||
uint32_t mask = 1;
|
|
||||||
mask <<= sh;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Each bit is associated to a chunk. Chunk bits are
|
|
||||||
* grouped by their size. The bit index of the first
|
|
||||||
* chunk if its length can be calculated as:
|
|
||||||
*
|
|
||||||
* 12 - log2(len)
|
|
||||||
*
|
|
||||||
* From there, the bit index is displaced as the chunk
|
|
||||||
* in the page:
|
|
||||||
*
|
|
||||||
* 12 - log2(len) + (ptr - page_ptr) / len
|
|
||||||
*
|
|
||||||
* For convenience, here's the list of the bits:
|
|
||||||
*
|
|
||||||
* 1 - 4K - 2^12 -> 2^0
|
|
||||||
* 2 - 2K - 2^11 -> 2^1
|
|
||||||
* 3 - 2K
|
|
||||||
* 4 - 1K - 2^10 -> 2^2
|
|
||||||
* 5 - 1K
|
|
||||||
* 6 - 1K
|
|
||||||
* 7 - 1K
|
|
||||||
* 8 - 512b - 2^9 -> 2^3
|
|
||||||
* 9 - 512b
|
|
||||||
* 10 - 512b
|
|
||||||
* 11 - 512b
|
|
||||||
* 12 - 512b
|
|
||||||
* 13 - 512b
|
|
||||||
* 14 - 512b
|
|
||||||
* 15 - 512b
|
|
||||||
* 16 - 256b - 2^8 -> 2^4
|
|
||||||
* 17 - 256b
|
|
||||||
* 18 - 256b
|
|
||||||
* 19 - 256b
|
|
||||||
* 20 - 256b
|
|
||||||
* 21 - 256b
|
|
||||||
* 22 - 256b
|
|
||||||
* 23 - 256b
|
|
||||||
* 24 - 256b
|
|
||||||
* 25 - 256b
|
|
||||||
* 26 - 256b
|
|
||||||
* 27 - 256b
|
|
||||||
* 28 - 256b
|
|
||||||
* 29 - 256b
|
|
||||||
* 30 - 256b
|
|
||||||
* 31 - 256b
|
|
||||||
*/
|
|
||||||
|
|
||||||
return mask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_chunk_state(struct buddy_alloc *alloc,
|
static char *pop(struct buddy_alloc *alloc, int i)
|
||||||
void *ptr, size_t len, bool used)
|
|
||||||
{
|
{
|
||||||
len = round_pow2(len);
|
assert(i >= 0 && i < BUDDY_ALLOC_NUM_LISTS);
|
||||||
|
|
||||||
size_t page_size = 1 << 12;
|
char *ptr = alloc->lists[i];
|
||||||
|
assert(ptr);
|
||||||
|
|
||||||
uintptr_t x = (uintptr_t) ptr;
|
alloc->lists[i] = ((struct page*) ptr)->next;
|
||||||
|
return ptr;
|
||||||
int page_index = (x - (uintptr_t) alloc->base) / page_size;
|
|
||||||
|
|
||||||
uint32_t mask = get_chunk_mask(ptr, len);
|
|
||||||
|
|
||||||
if (used)
|
|
||||||
alloc->bitsets[page_index] |= mask;
|
|
||||||
else
|
|
||||||
alloc->bitsets[page_index] &= ~mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_chunk_state(struct buddy_alloc *alloc,
|
|
||||||
void *ptr, size_t len)
|
|
||||||
{
|
|
||||||
size_t page_size = 1 << 12;
|
|
||||||
|
|
||||||
uintptr_t x = (uintptr_t) ptr;
|
|
||||||
|
|
||||||
int page_index = (x - (uintptr_t) alloc->base) / page_size;
|
|
||||||
|
|
||||||
uint32_t mask = get_chunk_mask(ptr, len);
|
|
||||||
|
|
||||||
return (alloc->bitsets[page_index] & mask) == mask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *buddy_malloc(struct buddy_alloc *alloc, size_t len)
|
void *buddy_malloc(struct buddy_alloc *alloc, size_t len)
|
||||||
{
|
{
|
||||||
void *ptr = malloc_internal(alloc, len);
|
if (len == 0 || len > MAX_BLOCK_SIZE)
|
||||||
if (ptr)
|
return NULL;
|
||||||
set_chunk_state(alloc, ptr, len, 1);
|
if (alloc->base == NULL)
|
||||||
|
return NULL;
|
||||||
|
len = normalize_len(len);
|
||||||
|
|
||||||
|
// Index of the list of blocks with size "len"
|
||||||
|
int i = list_index_for_size(len);
|
||||||
|
|
||||||
|
// Get the index of the first non-empty list
|
||||||
|
int j = i;
|
||||||
|
while (j < BUDDY_ALLOC_NUM_LISTS && alloc->lists[j] == NULL)
|
||||||
|
j++;
|
||||||
|
|
||||||
|
// If the index went over the list of full pages
|
||||||
|
// then the allocator can't handle this allocation.
|
||||||
|
if (j == BUDDY_ALLOC_NUM_LISTS)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Pop one block from the non-empty list.
|
||||||
|
char *ptr = pop(alloc, j);
|
||||||
|
|
||||||
|
// If we got a larger block than what we needed,
|
||||||
|
// we need to split it in halfs until we got it
|
||||||
|
// to the right size.
|
||||||
|
//
|
||||||
|
// We are basically shaving off the last half of
|
||||||
|
// the chunk multiple times, so the block's pointer
|
||||||
|
// doesn't change.
|
||||||
|
while (j > i) {
|
||||||
|
j--;
|
||||||
|
char *sibling = sibling_of(ptr, 1U << (j + MIN_BLOCK_LOG2));
|
||||||
|
append(alloc, j, sibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_allocated(alloc, ptr, len, true);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void buddy_free(struct buddy_alloc *alloc,
|
void buddy_free(struct buddy_alloc *alloc,
|
||||||
void *ptr, size_t len)
|
size_t len, void *ptr)
|
||||||
{
|
{
|
||||||
if (get_chunk_state(alloc, ptr, len)) {
|
if (ptr == NULL || len == 0) return;
|
||||||
set_chunk_state(alloc, ptr, len, 0);
|
if (len > MAX_BLOCK_SIZE) return;
|
||||||
free_internal(alloc, ptr, len);
|
len = normalize_len(len);
|
||||||
|
|
||||||
|
if (!is_allocated(alloc, ptr, len))
|
||||||
|
return;
|
||||||
|
set_allocated(alloc, ptr, len, false);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
int i = list_index_for_size(len);
|
||||||
|
|
||||||
|
if (len == MAX_BLOCK_SIZE || sibling_allocated_considering_splits(alloc, ptr, len)) {
|
||||||
|
append(alloc, i, ptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(alloc->lists[i]);
|
||||||
|
remove_sibling_from_list(alloc, i, ptr);
|
||||||
|
|
||||||
|
ptr = parent_of(ptr, len);
|
||||||
|
len <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool buddy_allocated(struct buddy_alloc *alloc,
|
/*
|
||||||
void *ptr, size_t len)
|
#define ANSI_COLOR_RED "\x1b[31m"
|
||||||
|
#define ANSI_COLOR_GREEN "\x1b[32m"
|
||||||
|
#define ANSI_COLOR_YELLOW "\x1b[33m"
|
||||||
|
#define ANSI_COLOR_BLUE "\x1b[34m"
|
||||||
|
#define ANSI_COLOR_MAGENTA "\x1b[35m"
|
||||||
|
#define ANSI_COLOR_CYAN "\x1b[36m"
|
||||||
|
#define ANSI_COLOR_RESET "\x1b[0m"
|
||||||
|
|
||||||
|
static const char *block_size_label(size_t len)
|
||||||
{
|
{
|
||||||
return get_chunk_state(alloc, ptr, len);
|
const char *label = "???";
|
||||||
|
switch (len) {
|
||||||
|
case 1<<12: label = "4K"; break;
|
||||||
|
case 1<<11: label = "2K"; break;
|
||||||
|
case 1<<10: label = "1K"; break;
|
||||||
|
case 1<<9 : label = "512B"; break;
|
||||||
|
case 1<<8 : label = "256B"; break;
|
||||||
|
}
|
||||||
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void print_lists(struct buddy_alloc *alloc)
|
||||||
|
{
|
||||||
|
for (int i = MIN_BLOCK_LOG2; i <= MAX_BLOCK_LOG2; i++) {
|
||||||
|
fprintf(stderr, "%s = {", block_size_label(1U<<i));
|
||||||
|
struct page *p = alloc->lists[i - MIN_BLOCK_LOG2];
|
||||||
|
while (p) {
|
||||||
|
assert((uintptr_t) p >= (uintptr_t) alloc->base);
|
||||||
|
fprintf(stderr, "%lu", (uintptr_t) p - (uintptr_t) alloc->base);
|
||||||
|
p = p->next;
|
||||||
|
if (p)
|
||||||
|
fprintf(stderr, ", ");
|
||||||
|
}
|
||||||
|
fprintf(stderr, "}\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void buddy_dump(struct buddy_alloc *alloc, FILE *out)
|
||||||
|
{
|
||||||
|
fprintf(out, "\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < alloc->num_info; i++) {
|
||||||
|
for (int j = 0; j < 32; j++) {
|
||||||
|
if (alloc->info[i].bits[0] & (1U << j))
|
||||||
|
fprintf(stderr, "1");
|
||||||
|
else
|
||||||
|
fprintf(stderr, "0");
|
||||||
|
}
|
||||||
|
fprintf(stderr, " ");
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < alloc->num_info; i++) {
|
||||||
|
char *page = alloc->base + i * MAX_BLOCK_SIZE;
|
||||||
|
for (int j = MAX_BLOCK_LOG2; j >= MIN_BLOCK_LOG2; j--) {
|
||||||
|
|
||||||
|
size_t len = 1U << j;
|
||||||
|
|
||||||
|
const char *label = block_size_label(len);
|
||||||
|
|
||||||
|
for (size_t k = 0; k < MAX_BLOCK_SIZE / len; k++) {
|
||||||
|
|
||||||
|
char *ptr = page + k * len;
|
||||||
|
|
||||||
|
fprintf(out, "%-4lX ", (uintptr_t) ptr - (uintptr_t) alloc->base);
|
||||||
|
|
||||||
|
for (int q = 0; q < MAX_BLOCK_LOG2 - j; q++)
|
||||||
|
fprintf(out, " ");
|
||||||
|
|
||||||
|
if (is_allocated(alloc, ptr, len))
|
||||||
|
fprintf(out, ANSI_COLOR_GREEN "%s - allocated\n" ANSI_COLOR_RESET, label);
|
||||||
|
else
|
||||||
|
fprintf(out, "%s - free\n", label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -1,24 +1,38 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct buddy_alloc {
|
#define BUDDY_ALLOC_MAX_BLOCK_LOG2 12
|
||||||
void *base;
|
#define BUDDY_ALLOC_MIN_BLOCK_LOG2 8
|
||||||
void *lists[5];
|
#define BUDDY_ALLOC_NUM_LISTS (BUDDY_ALLOC_MAX_BLOCK_LOG2 - BUDDY_ALLOC_MIN_BLOCK_LOG2 + 1)
|
||||||
uint32_t *bitsets;
|
|
||||||
int num_bitsets;
|
/*
|
||||||
|
* To keep track of the allocation state of a page,
|
||||||
|
* we need one bit for each possible block that can
|
||||||
|
* be made out of it. For instance, if the page can
|
||||||
|
* only be allocated in its entirety, 1 bit is required.
|
||||||
|
* If the blocks halfs can be allocated too, 3 bits
|
||||||
|
* are required: 1 for the page, 1 for the frist half
|
||||||
|
* and 1 for the second half. Allowing the allocation
|
||||||
|
* of page quarters requires 4 more bits, for a total
|
||||||
|
* of 7. In general, if we allow splitting a page N
|
||||||
|
* times (N=0 means only the entire page can be allocated),
|
||||||
|
* then 2^(N+1)-1 bits are necessary.
|
||||||
|
*/
|
||||||
|
#define BUDDY_ALLOC_BITS_PER_PAGE ((1U << (BUDDY_ALLOC_NUM_LISTS)) - 1)
|
||||||
|
#define BUDDY_ALLOC_WORDS_PER_PAGE ((BUDDY_ALLOC_BITS_PER_PAGE + 31) / 32)
|
||||||
|
|
||||||
|
struct page_info {
|
||||||
|
uint32_t bits[BUDDY_ALLOC_WORDS_PER_PAGE];
|
||||||
};
|
};
|
||||||
|
|
||||||
void init_buddy_alloc(struct buddy_alloc *alloc,
|
struct buddy_alloc {
|
||||||
char *base, size_t size,
|
void *base;
|
||||||
uint32_t *bitsets, int num_bitsets);
|
void *lists[BUDDY_ALLOC_NUM_LISTS];
|
||||||
|
struct page_info *info;
|
||||||
|
int num_info;
|
||||||
|
};
|
||||||
|
|
||||||
void free_buddy_alloc(struct buddy_alloc *alloc);
|
struct buddy_alloc buddy_startup(char *base, size_t size, struct page_info *page_info, int num_page_info);
|
||||||
|
void buddy_cleanup(struct buddy_alloc *alloc);
|
||||||
void *buddy_malloc(struct buddy_alloc *alloc,
|
void *buddy_malloc (struct buddy_alloc *alloc, size_t len);
|
||||||
size_t len);
|
void buddy_free (struct buddy_alloc *alloc, size_t len, void *ptr);
|
||||||
|
|
||||||
void buddy_free(struct buddy_alloc *alloc,
|
|
||||||
void *ptr, size_t len);
|
|
||||||
|
|
||||||
bool buddy_allocated(struct buddy_alloc *alloc,
|
|
||||||
void *ptr, size_t len);
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "buddy.h"
|
#include "buddy.h"
|
||||||
|
|
||||||
#define NUM_PAGES 32
|
#define NUM_PAGES 2
|
||||||
#define PAGE_SIZE (1 << 12)
|
#define PAGE_SIZE (1 << 12)
|
||||||
#define MAX_ALLOCS (1 << 14)
|
#define MAX_ALLOCS (1 << 14)
|
||||||
|
|
||||||
@@ -30,36 +30,53 @@ static bool contains_ptr(struct alloc_info *array, int count, void *ptr)
|
|||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
_Alignas(PAGE_SIZE) char mem[NUM_PAGES * PAGE_SIZE];
|
_Alignas(PAGE_SIZE) char mem[NUM_PAGES * PAGE_SIZE];
|
||||||
struct buddy_alloc alloc;
|
|
||||||
uint32_t alloc_bits[NUM_PAGES];
|
struct page_info info[NUM_PAGES];
|
||||||
init_buddy_alloc(&alloc, mem, sizeof(mem), alloc_bits, NUM_PAGES);
|
struct buddy_alloc alloc = buddy_startup(mem, sizeof(mem), info, NUM_PAGES);
|
||||||
|
|
||||||
struct alloc_info current_allocs[MAX_ALLOCS];
|
struct alloc_info current_allocs[MAX_ALLOCS];
|
||||||
int num_current_allocs = 0;
|
int num_current_allocs = 0;
|
||||||
|
|
||||||
|
int failed = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
if (num_current_allocs > 0) {
|
if (failed == 10 || num_current_allocs == MAX_ALLOCS) {
|
||||||
|
failed = 0;
|
||||||
|
while (1 + rand() % 10 < 6 && num_current_allocs > 0) {
|
||||||
int i = rand() % num_current_allocs;
|
int i = rand() % num_current_allocs;
|
||||||
struct alloc_info deallocating = current_allocs[i];
|
struct alloc_info deallocating = current_allocs[i];
|
||||||
current_allocs[i] = current_allocs[--num_current_allocs];
|
current_allocs[i] = current_allocs[--num_current_allocs];
|
||||||
buddy_free(&alloc, (void*) deallocating.ptr, deallocating.len);
|
fprintf(stderr, "buddy_free(%lu, %lu)\n", deallocating.len, (uintptr_t) deallocating.ptr - (uintptr_t) alloc.base);
|
||||||
|
buddy_free(&alloc, deallocating.len, (void*) deallocating.ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t len = 1 + (rand() % PAGE_SIZE);
|
size_t len = 1 + (rand() % PAGE_SIZE);
|
||||||
assert(len > 0 && len <= PAGE_SIZE);
|
assert(len > 0 && len <= PAGE_SIZE);
|
||||||
|
|
||||||
void *ptr = buddy_malloc(&alloc, len);
|
void *ptr = buddy_malloc(&alloc, len);
|
||||||
if (ptr == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
if (ptr == NULL) {
|
||||||
* Check that it isn't currently allocated
|
failed++;
|
||||||
*/
|
//fprintf(stderr, "buddy_malloc(%lu) = NULL\n", len);
|
||||||
assert(contains_ptr(current_allocs, num_current_allocs, ptr) == false);
|
} else {
|
||||||
current_allocs[num_current_allocs++] = (struct alloc_info) {.ptr=(uintptr_t)ptr, .len=len};
|
//buddy_dump(&alloc, stderr);
|
||||||
|
fprintf(stderr, "buddy_malloc(%lu) = %lu\n", len, (uintptr_t) ptr - (uintptr_t) alloc.base);
|
||||||
}
|
}
|
||||||
|
|
||||||
free_buddy_alloc(&alloc);
|
if (ptr != NULL) {
|
||||||
|
|
||||||
|
assert(contains_ptr(current_allocs, num_current_allocs, ptr) == false);
|
||||||
|
current_allocs[num_current_allocs++] = (struct alloc_info) {.ptr=(uintptr_t)ptr, .len=len};
|
||||||
|
/*
|
||||||
|
fprintf(stderr, "Allocations:\n");
|
||||||
|
for (int i = 0; i < num_current_allocs; i++) {
|
||||||
|
fprintf(stderr, " len: %-4lu | ptr: %lu\n", current_allocs[i].len, (uintptr_t) current_allocs[i].ptr - (uintptr_t) alloc.base);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buddy_cleanup(&alloc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user