-
Notifications
You must be signed in to change notification settings - Fork 0
/
allocator.h
139 lines (107 loc) · 3.62 KB
/
allocator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
// publish, and distribute this file as you see fit.
#ifndef ALLOCATOR_H
#define ALLOCATOR_H
#ifdef ALLOCATOR_IMPLEMENTATION
// linked list style
static struct Block {
size_t len;
struct Block *previous, *next;
} Block;
// this aligns the memory size by pointer size
// this is something like if ((n % pointer size) == 0) aligned_address == address else aligned_address == address + adjustment (16 - offset)
#define ALIGN(n) ((n + sizeof(void *) - 1) & ~(sizeof(void *) - 1))
#define BLOCK_LEN (ALIGN(sizeof(Block)))
static struct Block *memory = NULL;
// "block" is our heap, which we will allocate from
int mem_init(void *block, size_t len) {
if (block == NULL || len < BLOCK_LEN) {
// not enough size to store a single block
return 1;
}
memory = (struct Block *)block;
// the size of the block, subtracting the header
memory->len = len - BLOCK_LEN;
memory->previous = NULL;
memory->next = NULL;
return 0;
}
void *mem_alloc(size_t len) {
if (memory == NULL) {
// no more heap!
return NULL;
}
size_t adjusted_len = ALIGN(len + BLOCK_LEN);
struct Block *next = memory->next;
struct Block *previous = memory;
struct Block *current = NULL;
char *pos = (char *)memory + BLOCK_LEN;
while (1) {
if (next != NULL) {
// the size between both of these blocks in bytes
size_t size_between = (char *)next - pos;
if (size_between >= adjusted_len) {
// we've found our header
current = (struct Block *)pos;
break;
}
pos = (char *)next + next->len;
previous = next;
next = next->next;
} else {
// the size between both of these blocks in bytes
size_t size_between = (char *)memory + memory->len - pos;
if (size_between < adjusted_len) {
// not enough space!
return NULL;
}
current = (struct Block *)pos;
break;
}
}
current->len = adjusted_len;
current->previous = previous;
current->next = next;
// if there is a block found after our newly allocated one, assign it as the previous block of the block found after the recently allocated block
if (current->next != NULL) {
current->next->previous = current;
}
previous->next = current;
return (void *)pos + BLOCK_LEN;
}
void mem_free(void *ptr) {
if (ptr == NULL) {
return;
}
// get start of block
struct Block *block = (struct Block *)((char *)ptr - BLOCK_LEN);
block->previous->next = block->next;
if (block->previous->next != NULL) {
block->next->previous = block->previous;
}
}
void mem_defrag() {
struct Block *next = memory->next;
struct Block *previous = memory;
char *pos = (char *)memory + BLOCK_LEN;
while (next != NULL) {
// the size between both of these blocks in bytes
size_t size_between = (char *)next - pos;
if (size_between > 0) {
// shift the next address to the left
char *src = (char *)next;
char *dst = pos;
for (size_t i = 0; i < next->len; i++) {
dst[i] = src[i];
}
previous->next = (struct Block *)pos;
next = (struct Block *)pos;
}
pos = (char *)next + next->len;
previous = next;
next = next->next;
}
}
#endif
#endif // ALLOCATOR_H