-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
taipool.c
245 lines (206 loc) · 6.55 KB
/
taipool.c
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#include <vitasdk.h>
#include "taipool.h"
#define POOL_PADDING 0x100 // Difference between stack pointer and mempool start in bytes
static int dummy_thread(SceSize args, void *argp){return 0;}
static void* mempool_addr = NULL;
static SceUID mempool_id = 0;
static size_t mempool_size = 0;
static size_t mempool_free = 0;
static SceUID taipool_sema = 0;
// memblock header struct
typedef struct mempool_block_hdr{
uint8_t used;
size_t size;
} mempool_block_hdr;
// Locks taipool mempool access (blocking)
static void _taipool_lock_access(){
sceKernelWaitSema(taipool_sema, 1, NULL);
}
// Unlocks taipool mempool access
static void _taipool_unlock_access(){
sceKernelSignalSema(taipool_sema, 1);
}
// Allocks a new block on mempool
static void* _taipool_alloc_block(size_t size){
size_t i = 0;
mempool_block_hdr* hdr = (mempool_block_hdr*)mempool_addr;
// Checking for a big enough free memblock
while (i < mempool_size){
if (!hdr->used){
if (hdr->size >= size){
// Reserving memory
hdr->used = 1;
size_t old_size = hdr->size;
hdr->size = size;
// Splitting blocks
mempool_block_hdr* new_hdr = (mempool_block_hdr*)(mempool_addr + i + sizeof(mempool_block_hdr) + size);
new_hdr->used = 0;
new_hdr->size = old_size - (size + sizeof(mempool_block_hdr));
mempool_free -= (sizeof(mempool_block_hdr) + size);
return (void*)(mempool_addr + i + sizeof(mempool_block_hdr));
}
}
// Jumping to next block
i += (hdr->size + sizeof(mempool_block_hdr));
hdr = (mempool_block_hdr*)(mempool_addr + i);
}
return NULL;
}
// Frees a block on mempool
static void _taipool_free_block(void* ptr){
mempool_block_hdr* hdr = (mempool_block_hdr*)(ptr - sizeof(mempool_block_hdr));
hdr->used = 0;
mempool_free += hdr->size;
}
// Merge contiguous free blocks in a bigger one
static void _taipool_merge_blocks(){
size_t i = 0;
mempool_block_hdr* hdr = (mempool_block_hdr*)mempool_addr;
mempool_block_hdr* previousBlock = NULL;
while (i < mempool_size){
if (!hdr->used){
if (previousBlock != NULL){
previousBlock->size += (hdr->size + sizeof(mempool_block_hdr));
mempool_free += sizeof(mempool_block_hdr);
}else{
previousBlock = hdr;
}
}else{
previousBlock = NULL;
}
// Jumping to next block
i += hdr->size + sizeof(mempool_block_hdr);
hdr = (mempool_block_hdr*)(mempool_addr + i);
}
}
// Extend an allocated block by a given size
static void* _taipool_extend_block(void* ptr, size_t size){
mempool_block_hdr* hdr = (mempool_block_hdr*)(ptr - sizeof(mempool_block_hdr));
mempool_block_hdr* next_block = (mempool_block_hdr*)(ptr + hdr->size);
size_t extra_size = size - hdr->size;
// Checking if enough contiguous blocks are available
while (extra_size > 0){
if (next_block->used) return NULL;
extra_size -= (next_block->size + sizeof(mempool_block_hdr));
next_block = (mempool_block_hdr*)(next_block + sizeof(mempool_block_hdr) + next_block->size);
}
// Extending current block
hdr->size = size;
mempool_free -= extra_size;
return ptr;
}
// Compact an allocated block to a given size
static void _taipool_compact_block(void* ptr, size_t size){
mempool_block_hdr* hdr = (mempool_block_hdr*)(ptr - sizeof(mempool_block_hdr));
size_t old_size = hdr->size;
hdr->size = size;
mempool_block_hdr* new_block = (mempool_block_hdr*)(ptr + hdr->size);
new_block->used = 0;
new_block->size = old_size - (size + sizeof(mempool_block_hdr));
mempool_free += new_block->size;
}
// Resets taipool mempool
void taipool_reset(void){
if (mempool_addr != NULL){
_taipool_lock_access();
mempool_block_hdr* master_block = (mempool_block_hdr*)mempool_addr;
master_block->used = 0;
master_block->size = mempool_size - sizeof(mempool_block_hdr);
mempool_free = master_block->size;
_taipool_unlock_access();
}
}
// Terminate taipool mempool
void taipool_term(void){
if (mempool_addr != NULL){
_taipool_lock_access();
sceKernelDeleteThread(mempool_id);
sceKernelDeleteSema(taipool_sema);
mempool_addr = NULL;
mempool_size = 0;
mempool_free = 0;
}
}
// Initialize taipool mempool
int taipool_init(size_t size){
if (mempool_addr != NULL) taipool_term();
// Creating a thread in order to reserve requested memory
SceUID pool_id = sceKernelCreateThread("mempool_thread", dummy_thread, 0x40, size, 0, 0, NULL);
if (pool_id >= 0){
SceKernelThreadInfo mempool_info;
mempool_info.size = sizeof(SceKernelThreadInfo);
if (!sceKernelGetThreadInfo(pool_id, &mempool_info)){
mempool_addr = mempool_info.stack + POOL_PADDING;
mempool_id = pool_id;
mempool_size = mempool_info.stackSize - POOL_PADDING;
// Initializing mempool as a single block
taipool_reset();
// Setting up a semaphore for mutual exclusion
taipool_sema = sceKernelCreateSema("taipool_sema", 0, 1, 1, 0);
return 0;
}
}
return pool_id;
}
// Frees a block on taipool mempool
void taipool_free(void* ptr){
_taipool_lock_access();
_taipool_free_block(ptr);
_taipool_merge_blocks();
_taipool_unlock_access();
}
// Allocates a new block on taipool mempool
void* taipool_alloc(size_t size){
_taipool_lock_access();
void* res = NULL;
if (size <= mempool_free) res = _taipool_alloc_block(size);
_taipool_unlock_access();
return res;
}
// Allocates a new block on taipool mempool and zero-initialize it
void* taipool_calloc(size_t num, size_t size){
_taipool_lock_access();
void* res = taipool_alloc(num * size);
if (res != NULL) memset(res, 0, num * size);
_taipool_unlock_access();
return res;
}
// Reallocates a currently allocated block on taipool mempool
void* taipool_realloc(void* ptr, size_t size){
_taipool_lock_access();
mempool_block_hdr* hdr = (mempool_block_hdr*)(ptr - sizeof(mempool_block_hdr));
void* res = NULL;
if (hdr->size < size){ // Increasing size
// Trying to extend the block with successive contiguous blocks
void* res = _taipool_extend_block(ptr, size);
if (res == NULL){
// Trying to extend the block by fully relocating it
res = taipool_alloc(size);
if (res != NULL){
memcpy(res, ptr, hdr->size);
taipool_free(ptr);
}else{
// Trying to extend the block with contiguous blocks
size_t orig_size = hdr->size;
taipool_free(ptr);
res = taipool_alloc(size);
if (res == NULL){
hdr->used = 1;
hdr->size = orig_size;
}else if (res != ptr){
memmove(res, ptr, orig_size);
}
}
}
}else{ // Reducing size
_taipool_compact_block(ptr, size);
_taipool_merge_blocks();
res = ptr;
}
_taipool_unlock_access();
return res;
}
// Returns currently free space on mempool
size_t taipool_get_free_space(void){
return mempool_free;
}