| 141 |
Kevin |
1 |
/*
|
|
|
2 |
* This implementation of a memory allocation utilizes a segregated fits
|
|
|
3 |
* appraoch. Multiple buckets are used for each size range and a doubly
|
|
|
4 |
* linked list used to hold the list of free blocks for each bucket.
|
|
|
5 |
* Free blocks for each bucket is choosen based on first fit, with the
|
|
|
6 |
* search going into the next sized bucket if no fit is found in the
|
|
|
7 |
* current bucket. If no free blocks are found that holds the requested
|
|
|
8 |
* size, more memory is requested by incrementing the brk pointer using
|
|
|
9 |
* mem_sbrk. The requested memory is in a multiple of the page size for
|
|
|
10 |
* higher allocating efficiency. Freeing a previously allocated block
|
|
|
11 |
* coalesces the block with the previous and next blocks if they are
|
|
|
12 |
* empty. The last 4 bytes in each block is used as a boundary tag that
|
|
|
13 |
* holds the size of the block it is in. This allows the code to
|
|
|
14 |
* traverse backwards in the address space and check if the previous
|
|
|
15 |
* block is empty or allocated.
|
|
|
16 |
*/
|
|
|
17 |
#include <stdio.h>
|
|
|
18 |
#include <stdlib.h>
|
|
|
19 |
#include <assert.h>
|
|
|
20 |
#include <unistd.h>
|
|
|
21 |
#include <string.h>
|
|
|
22 |
#include <stddef.h>
|
|
|
23 |
#include <limits.h>
|
|
|
24 |
|
|
|
25 |
#include "mm.h"
|
|
|
26 |
#include "memlib.h"
|
|
|
27 |
#include "list.h"
|
|
|
28 |
#include "config.h" /* defines ALIGNMENT */
|
|
|
29 |
|
|
|
30 |
#define FREE_SPACE_SPLIT_OVERHEAD 512
|
|
|
31 |
|
|
|
32 |
/*********************************************************
|
|
|
33 |
* NOTE TO STUDENTS: Before you do anything else, please
|
|
|
34 |
* provide your team information in the following struct.
|
|
|
35 |
********************************************************/
|
|
|
36 |
team_t team = {
|
|
|
37 |
/* Team name */
|
|
|
38 |
"Team K",
|
|
|
39 |
/* First member's full name */
|
|
|
40 |
"Kevin Lee",
|
|
|
41 |
/* First member's SLO (@cs.vt.edu) email address */
|
|
|
42 |
"klee482@vt.edu",
|
|
|
43 |
/* Second member's full name (leave blank if none) */
|
|
|
44 |
"",
|
|
|
45 |
/* Second member's SLO (@cs.vt.edu) email address (leave blank if none) */
|
|
|
46 |
""
|
|
|
47 |
};
|
|
|
48 |
|
|
|
49 |
/*
|
|
|
50 |
* If size is a multiple of ALIGNMENT, return size.
|
|
|
51 |
* Else, return next larger multiple of ALIGNMENT:
|
|
|
52 |
* (size/ALIGNMENT + 1) * ALIGNMENT
|
|
|
53 |
* Does so without requiring integer division, assuming
|
|
|
54 |
* ALIGNMENT is a power of 2.
|
|
|
55 |
*/
|
|
|
56 |
static size_t roundup(size_t size)
|
|
|
57 |
{
|
|
|
58 |
return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
|
|
|
59 |
}
|
|
|
60 |
|
|
|
61 |
/*
|
|
|
62 |
* This C struct captures an allocated header.
|
|
|
63 |
*
|
|
|
64 |
* By casting a memory location to a pointer to a allocated_block_header,
|
|
|
65 |
* we are able to treat a part of memory as if a header had been allocated
|
|
|
66 |
* in it.
|
|
|
67 |
*
|
|
|
68 |
* Note: you should never define instances of 'struct allocated_block_header' -
|
|
|
69 |
* all accesses will be through pointers.
|
|
|
70 |
*/
|
|
|
71 |
struct allocated_block_header {
|
|
|
72 |
size_t size; // Size of the block, includes header and padding
|
|
|
73 |
|
|
|
74 |
/*
|
|
|
75 |
* Zero length arrays do not add size to the structure, they simply
|
|
|
76 |
* provide a syntactic form to refer to a char array following the
|
|
|
77 |
* structure.
|
|
|
78 |
* See http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
|
|
|
79 |
*
|
|
|
80 |
* The 'aligned' attribute forces 'payload' to be aligned at a
|
|
|
81 |
* multiple of alignment, counted from the beginning of the struct
|
|
|
82 |
* See http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html
|
|
|
83 |
*/
|
|
|
84 |
char payload[0] __attribute__((aligned(ALIGNMENT)));
|
|
|
85 |
};
|
|
|
86 |
|
|
|
87 |
/*
|
|
|
88 |
* This C struct captures a free header.
|
|
|
89 |
*
|
|
|
90 |
* By casting a memory location to a pointer to a free_block_header,
|
|
|
91 |
* we are able to treat a part of memory as if a header had been allocated
|
|
|
92 |
* in it.
|
|
|
93 |
*/
|
|
|
94 |
struct free_block_header {
|
|
|
95 |
size_t size; // Size of the block, includes header and padding
|
|
|
96 |
struct list_elem elem; // Allows the struct to be used in a doublely linked list
|
|
|
97 |
};
|
|
|
98 |
|
|
|
99 |
/*
|
|
|
100 |
* This C struct captures a bucket of free blocks
|
|
|
101 |
*
|
|
|
102 |
* This struct is an element of a list that holds list of free blocks as
|
|
|
103 |
* well as relevant information for the bucket (size of free blocks).
|
|
|
104 |
*/
|
|
|
105 |
struct free_list_index {
|
|
|
106 |
struct list free_block_list; // List of free blocks
|
|
|
107 |
int size_min; // Minimum size of each block
|
|
|
108 |
int size_max; // Maximum size of each block
|
|
|
109 |
struct list_elem elem; // Allows the struct to be used in a doublely linked list
|
|
|
110 |
};
|
|
|
111 |
|
|
|
112 |
/*
|
|
|
113 |
* This C struct captures a boundary tag
|
|
|
114 |
*
|
|
|
115 |
* By casting a memory location to a pointer to a boundary tag,
|
|
|
116 |
* we can read the size of the block from the front or the back.
|
|
|
117 |
*/
|
|
|
118 |
struct boundary_block {
|
|
|
119 |
size_t size;
|
|
|
120 |
};
|
|
|
121 |
|
|
|
122 |
// Global list holding the main list of buckets and each bucket's list
|
|
|
123 |
struct list index_of_free_lists;
|
|
|
124 |
struct free_list_index bucket_1 = {.size_min = 1, .size_max = 2};
|
|
|
125 |
struct free_list_index bucket_2 = {.size_min = 3, .size_max = 8};
|
|
|
126 |
struct free_list_index bucket_3 = {.size_min = 9, .size_max = 32};
|
|
|
127 |
struct free_list_index bucket_4 = {.size_min = 33, .size_max = 128};
|
|
|
128 |
struct free_list_index bucket_5 = {.size_min = 129, .size_max = 512};
|
|
|
129 |
struct free_list_index bucket_6 = {.size_min = 513, .size_max = 1024};
|
|
|
130 |
struct free_list_index bucket_7 = {.size_min = 1025, .size_max = 2048};
|
|
|
131 |
struct free_list_index bucket_8 = {.size_min = 2049, .size_max = 4096};
|
|
|
132 |
struct free_list_index bucket_9 = {.size_min = 4097, .size_max = 8192};
|
|
|
133 |
struct free_list_index bucket_10 = {.size_min = 8193, .size_max = 16384};
|
|
|
134 |
struct free_list_index bucket_11 = {.size_min = 16385, .size_max = 32768};
|
|
|
135 |
struct free_list_index bucket_12 = {.size_min = 32769, .size_max = 65536};
|
|
|
136 |
struct free_list_index bucket_13 = {.size_min = 65537, .size_max = INT_MAX};
|
|
|
137 |
|
|
|
138 |
/*
|
|
|
139 |
* alloc_block_set_boundary_tags - Sets the boundary tags for allocated blocks
|
|
|
140 |
*/
|
|
|
141 |
void alloc_block_set_boundary_tags(void *alloc_block) {
|
|
|
142 |
struct allocated_block_header *block = alloc_block;
|
|
|
143 |
int size = block->size;
|
|
|
144 |
block->size |= 1;
|
|
|
145 |
|
|
|
146 |
struct boundary_block *f = alloc_block + size - sizeof(size_t);
|
|
|
147 |
|
|
|
148 |
f->size = size |= 1;
|
|
|
149 |
}
|
|
|
150 |
|
|
|
151 |
/*
|
|
|
152 |
* free_block_set_boundary_tags - Sets the boundary tags for free blocks
|
|
|
153 |
*/
|
|
|
154 |
void free_block_set_boundary_tags(void *free_block) {
|
|
|
155 |
struct free_block_header *block = free_block;
|
|
|
156 |
|
|
|
157 |
struct boundary_block *f = free_block + block->size - sizeof(size_t);
|
|
|
158 |
|
|
|
159 |
f->size = block->size;
|
|
|
160 |
}
|
|
|
161 |
|
|
|
162 |
/*
|
|
|
163 |
* free_block_insert - Takes a free block and pushes it to the front of the
|
|
|
164 |
* correct list that it fits in. Also coalesces the block with the blocks
|
|
|
165 |
* before and after it in the address space.
|
|
|
166 |
*/
|
|
|
167 |
void free_block_insert(void *free_block) {
|
|
|
168 |
struct free_block_header *block = free_block;
|
|
|
169 |
|
|
|
170 |
// Check if the block can be combined with any free blocks before or after
|
|
|
171 |
bool prev_block_empty = false;
|
|
|
172 |
bool next_block_empty = false;
|
|
|
173 |
|
|
|
174 |
struct boundary_block *prev = free_block - sizeof(size_t);
|
|
|
175 |
struct boundary_block *next = free_block + block->size;
|
|
|
176 |
|
|
|
177 |
if (free_block != mem_heap_lo()) {
|
|
|
178 |
if ((prev->size & 1) == 0) {
|
|
|
179 |
prev_block_empty = true;
|
|
|
180 |
}
|
|
|
181 |
}
|
|
|
182 |
|
|
|
183 |
if (free_block + block->size != mem_heap_hi() + 1) {
|
|
|
184 |
if ((next->size & 1) == 0) {
|
|
|
185 |
next_block_empty = true;
|
|
|
186 |
}
|
|
|
187 |
}
|
|
|
188 |
|
|
|
189 |
// Combine blocks accordingly
|
|
|
190 |
if (prev_block_empty && next_block_empty) {
|
|
|
191 |
// Both previous and next blocks are empty
|
|
|
192 |
struct free_block_header *prev_block = free_block - prev->size;
|
|
|
193 |
struct free_block_header *next_block = free_block + block->size;
|
|
|
194 |
list_remove(&prev_block->elem);
|
|
|
195 |
list_remove(&next_block->elem);
|
|
|
196 |
prev_block->size += block->size;
|
|
|
197 |
prev_block->size += next_block->size;
|
|
|
198 |
block = prev_block;
|
|
|
199 |
} else if (prev_block_empty && !next_block_empty) {
|
|
|
200 |
// Only the previous block is empty
|
|
|
201 |
struct free_block_header *prev_block = free_block - prev->size;
|
|
|
202 |
list_remove(&prev_block->elem);
|
|
|
203 |
prev_block->size += block->size;
|
|
|
204 |
block = prev_block;
|
|
|
205 |
} else if (!prev_block_empty && next_block_empty) {
|
|
|
206 |
// Only the next block is empty
|
|
|
207 |
struct free_block_header *next_block = free_block + block->size;
|
|
|
208 |
list_remove(&next_block->elem);
|
|
|
209 |
block->size += next_block->size;
|
|
|
210 |
}
|
|
|
211 |
|
|
|
212 |
free_block_set_boundary_tags(block);
|
|
|
213 |
|
|
|
214 |
// Insert the free block into the bucket that it belongs to
|
|
|
215 |
struct list_elem *index = list_begin(&index_of_free_lists);
|
|
|
216 |
while (index != list_end(&index_of_free_lists)) {
|
|
|
217 |
struct free_list_index *index_entry = list_entry(index, struct free_list_index, elem);
|
|
|
218 |
|
|
|
219 |
if (block->size >= index_entry->size_min && block->size <= index_entry->size_max) {
|
|
|
220 |
list_push_front(&index_entry->free_block_list, &block->elem);
|
|
|
221 |
return;
|
|
|
222 |
}
|
|
|
223 |
|
|
|
224 |
index = list_next(index);
|
|
|
225 |
}
|
|
|
226 |
}
|
|
|
227 |
|
|
|
228 |
/*
|
|
|
229 |
* free_block_alloc - Uses the free space specified in free_block_header to
|
|
|
230 |
* hold a new allocated block of size alloc_size.
|
|
|
231 |
*/
|
|
|
232 |
void *free_block_alloc(void *free_block, size_t alloc_size, bool block_in_list) {
|
|
|
233 |
struct free_block_header *block = free_block;
|
|
|
234 |
if (block_in_list)
|
|
|
235 |
list_remove(&block->elem);
|
|
|
236 |
|
|
|
237 |
// Before we split up the free block, we need to check if there is enough
|
|
|
238 |
// space for holding another free block header.
|
|
|
239 |
if (block->size - alloc_size > sizeof(struct free_block_header)+FREE_SPACE_SPLIT_OVERHEAD) {
|
|
|
240 |
// There is enough space for another free block + overhead
|
|
|
241 |
int combined_size = block->size;
|
|
|
242 |
block = free_block + alloc_size;
|
|
|
243 |
block->size = combined_size - alloc_size;
|
|
|
244 |
|
|
|
245 |
struct allocated_block_header *ret = free_block;
|
|
|
246 |
ret->size = alloc_size;
|
|
|
247 |
alloc_block_set_boundary_tags(ret);
|
|
|
248 |
|
|
|
249 |
free_block_insert(block);
|
|
|
250 |
return ret;
|
|
|
251 |
} else {
|
|
|
252 |
// If there is not enough space for a free block header, simply
|
|
|
253 |
// convert to an allocated block header and return
|
|
|
254 |
struct allocated_block_header *ret = free_block;
|
|
|
255 |
alloc_block_set_boundary_tags(ret);
|
|
|
256 |
return ret;
|
|
|
257 |
}
|
|
|
258 |
}
|
|
|
259 |
|
|
|
260 |
/*
|
|
|
261 |
* free_block_search - Given a list of free_block_headers, finds
|
|
|
262 |
* and returns the first block that can hold the requested size.
|
|
|
263 |
* If no blocks were found, returns NULL.
|
|
|
264 |
*/
|
|
|
265 |
void *free_block_search(void *free_block_list, int requested_size) {
|
|
|
266 |
struct list *block_list = free_block_list;
|
|
|
267 |
struct list_elem *e = list_begin(block_list);
|
|
|
268 |
while (e != list_end(block_list)) {
|
|
|
269 |
struct free_block_header *entry= list_entry(e, struct free_block_header, elem);
|
|
|
270 |
if (entry->size >= requested_size) {
|
|
|
271 |
return entry;
|
|
|
272 |
}
|
|
|
273 |
e = list_next(e);
|
|
|
274 |
}
|
|
|
275 |
|
|
|
276 |
return NULL;
|
|
|
277 |
}
|
|
|
278 |
|
|
|
279 |
/*
|
|
|
280 |
* mm_check - Checks the heap for consistency. Returns a non-zero value
|
|
|
281 |
* only if the heap is consistent.
|
|
|
282 |
*/
|
|
|
283 |
int mm_check(void)
|
|
|
284 |
{
|
|
|
285 |
// Prints out the free blocks in each bucket when called
|
|
|
286 |
printf("Heap allocated from %d to %d of size %d\n", (int)mem_heap_lo(), (int)mem_heap_hi(), mem_heapsize());
|
|
|
287 |
printf("----------------------------------------\n");
|
|
|
288 |
struct list_elem *index = list_begin(&index_of_free_lists);
|
|
|
289 |
while (index != list_end(&index_of_free_lists)) {
|
|
|
290 |
struct free_list_index *index_entry = list_entry(index, struct free_list_index, elem);
|
|
|
291 |
printf("Bucket of size %d - %d:\n", index_entry->size_min, index_entry->size_max);
|
|
|
292 |
|
|
|
293 |
struct list_elem *entry = list_begin(&index_entry->free_block_list);
|
|
|
294 |
while(entry != list_end(&index_entry->free_block_list)) {
|
|
|
295 |
struct free_block_header *block = list_entry(entry, struct free_block_header, elem);
|
|
|
296 |
printf("\tFree Block at %d of size %d\n", (int)block, block->size);
|
|
|
297 |
entry = list_next(entry);
|
|
|
298 |
}
|
|
|
299 |
|
|
|
300 |
index = list_next(index);
|
|
|
301 |
}
|
|
|
302 |
printf("----------------------------------------\n");
|
|
|
303 |
return 1;
|
|
|
304 |
}
|
|
|
305 |
|
|
|
306 |
/*
|
|
|
307 |
* mm_init - Initialize the malloc package.
|
|
|
308 |
*/
|
|
|
309 |
int mm_init(void)
|
|
|
310 |
{
|
|
|
311 |
/* Sanity checks. */
|
|
|
312 |
assert((ALIGNMENT & (ALIGNMENT - 1)) == 0); // power of 2
|
|
|
313 |
assert(sizeof(struct allocated_block_header) == ALIGNMENT);
|
|
|
314 |
assert(offsetof(struct allocated_block_header, size) == 0);
|
|
|
315 |
assert(offsetof(struct allocated_block_header, payload) % ALIGNMENT == 0);
|
|
|
316 |
|
|
|
317 |
// Initialize the overall list of free lists
|
|
|
318 |
list_init(&index_of_free_lists);
|
|
|
319 |
|
|
|
320 |
// Initialize the list for each bucket size
|
|
|
321 |
list_init(&bucket_1.free_block_list);
|
|
|
322 |
list_init(&bucket_2.free_block_list);
|
|
|
323 |
list_init(&bucket_3.free_block_list);
|
|
|
324 |
list_init(&bucket_4.free_block_list);
|
|
|
325 |
list_init(&bucket_5.free_block_list);
|
|
|
326 |
list_init(&bucket_6.free_block_list);
|
|
|
327 |
list_init(&bucket_7.free_block_list);
|
|
|
328 |
list_init(&bucket_8.free_block_list);
|
|
|
329 |
list_init(&bucket_9.free_block_list);
|
|
|
330 |
list_init(&bucket_10.free_block_list);
|
|
|
331 |
list_init(&bucket_11.free_block_list);
|
|
|
332 |
list_init(&bucket_12.free_block_list);
|
|
|
333 |
list_init(&bucket_13.free_block_list);
|
|
|
334 |
|
|
|
335 |
// Insert the list into the main list
|
|
|
336 |
list_push_back(&index_of_free_lists, &bucket_1.elem);
|
|
|
337 |
list_push_back(&index_of_free_lists, &bucket_2.elem);
|
|
|
338 |
list_push_back(&index_of_free_lists, &bucket_3.elem);
|
|
|
339 |
list_push_back(&index_of_free_lists, &bucket_4.elem);
|
|
|
340 |
list_push_back(&index_of_free_lists, &bucket_5.elem);
|
|
|
341 |
list_push_back(&index_of_free_lists, &bucket_6.elem);
|
|
|
342 |
list_push_back(&index_of_free_lists, &bucket_7.elem);
|
|
|
343 |
list_push_back(&index_of_free_lists, &bucket_8.elem);
|
|
|
344 |
list_push_back(&index_of_free_lists, &bucket_9.elem);
|
|
|
345 |
list_push_back(&index_of_free_lists, &bucket_10.elem);
|
|
|
346 |
list_push_back(&index_of_free_lists, &bucket_11.elem);
|
|
|
347 |
list_push_back(&index_of_free_lists, &bucket_12.elem);
|
|
|
348 |
list_push_back(&index_of_free_lists, &bucket_13.elem);
|
|
|
349 |
|
|
|
350 |
return 0;
|
|
|
351 |
}
|
|
|
352 |
|
|
|
353 |
/*
|
|
|
354 |
* mm_malloc - Allocate a block by incrementing the brk pointer.
|
|
|
355 |
* Always allocate a block whose size is a multiple of the alignment.
|
|
|
356 |
*/
|
|
|
357 |
void *mm_malloc(size_t size)
|
|
|
358 |
{
|
|
|
359 |
int newsize = roundup(size + sizeof(struct free_block_header));
|
|
|
360 |
int searchsize = newsize;
|
|
|
361 |
|
|
|
362 |
// Look for free block list that contains the right sized blocks
|
|
|
363 |
struct list_elem *index = list_begin(&index_of_free_lists);
|
|
|
364 |
while (index != list_end(&index_of_free_lists)) {
|
|
|
365 |
struct free_list_index *index_entry = list_entry(index, struct free_list_index, elem);
|
|
|
366 |
|
|
|
367 |
if (searchsize >= index_entry->size_min && searchsize <= index_entry->size_max) {
|
|
|
368 |
// Try to get a free block that is large enough to hold the requested size
|
|
|
369 |
struct free_block_header *free_block = free_block_search(&index_entry->free_block_list, newsize);
|
|
|
370 |
if (free_block == NULL) {
|
|
|
371 |
// List is empty, look at the next block size
|
|
|
372 |
searchsize = index_entry->size_max + 1;
|
|
|
373 |
} else {
|
|
|
374 |
// Empty block was found, use the block to allocate space
|
|
|
375 |
struct allocated_block_header *alloc_block = free_block_alloc(free_block, newsize, true);
|
|
|
376 |
return alloc_block->payload;
|
|
|
377 |
}
|
|
|
378 |
}
|
|
|
379 |
index = list_next(index);
|
|
|
380 |
}
|
|
|
381 |
|
|
|
382 |
|
|
|
383 |
// If no blocks were found, expand the heap and allocate the block from there
|
|
|
384 |
|
|
|
385 |
// Calculage how many pages are needed to fit the block
|
|
|
386 |
int alloc_size = (newsize / mem_pagesize() + 1) * mem_pagesize();
|
|
|
387 |
|
|
|
388 |
// Get a number of pages from the heap and allocate from there
|
|
|
389 |
struct free_block_header *free_block = mem_sbrk(alloc_size);
|
|
|
390 |
if (free_block == NULL)
|
|
|
391 |
return NULL;
|
|
|
392 |
free_block->size = alloc_size;
|
|
|
393 |
struct allocated_block_header *alloc_block = free_block_alloc(free_block, newsize, false);
|
|
|
394 |
return alloc_block->payload;
|
|
|
395 |
}
|
|
|
396 |
|
|
|
397 |
/*
|
|
|
398 |
* mm_free - Frees the specified block, inserting it into the list of free blocks
|
|
|
399 |
*/
|
|
|
400 |
void mm_free(void *ptr)
|
|
|
401 |
{
|
|
|
402 |
// Get a pointer to the original block
|
|
|
403 |
struct free_block_header *new_block = ptr - offsetof(struct allocated_block_header, payload);
|
|
|
404 |
|
|
|
405 |
// Mark the block as free and insert it into the list of free blocks
|
|
|
406 |
new_block->size ^= 1;
|
|
|
407 |
free_block_insert(new_block);
|
|
|
408 |
}
|
|
|
409 |
|
|
|
410 |
/*
|
|
|
411 |
* mm_realloc - Resizes the specified block, leaving it if the size is smaller and allocating
|
|
|
412 |
* and copying the data to a new block if the size is larger. Extra space is left for
|
|
|
413 |
* future resizing.
|
|
|
414 |
*/
|
|
|
415 |
void *mm_realloc(void *oldptr, size_t size)
|
|
|
416 |
{
|
|
|
417 |
struct allocated_block_header *old_block = oldptr - offsetof(struct allocated_block_header, payload);
|
|
|
418 |
// old_block->size ^= 1;
|
|
|
419 |
|
|
|
420 |
// If the block is already large enough to hold the new size, simply return
|
|
|
421 |
if (size <= old_block->size - sizeof(struct free_block_header) - sizeof(size_t))
|
|
|
422 |
return old_block->payload;
|
|
|
423 |
|
|
|
424 |
// // Otherwise we need to allocate a new block of the request size
|
|
|
425 |
void *new_block = mm_malloc(size + 6 * mem_pagesize());
|
|
|
426 |
if (new_block == NULL)
|
|
|
427 |
return NULL;
|
|
|
428 |
|
|
|
429 |
// Copy the old data to the new block
|
|
|
430 |
size_t copySize = old_block->size;
|
|
|
431 |
if (size < copySize)
|
|
|
432 |
copySize = size;
|
|
|
433 |
|
|
|
434 |
memcpy(new_block, oldptr, copySize);
|
|
|
435 |
|
|
|
436 |
// Free the old block and return the new one
|
|
|
437 |
mm_free(oldptr);
|
|
|
438 |
return new_block;
|
|
|
439 |
}
|
|
|
440 |
|