11#include <stdlib.h>
22#include <stddef.h>
33#include <process.h>
4+ #include <limits.h>
45#include <stdio.h>
56#include <sys/syscall.h>
67#include <math.h>
78
9+ // heap memory parameter
10+
11+ // Full search on heap allocated memory can be a bit slow.
12+ // #define HEAP_MALLOC_SEARCH_MOST_APPROPRIATE_BLOCK
13+
14+ // During reallocating a block should extra space be created
15+ // as a new heap block.
16+ #define HEAP_MALLOC_SPLIT_ON_REALLOCATING_BLOCK
17+
18+ // During free operating should a block merge with free neighbours.
19+ #define HEAP_MALLOC_FREE_MERGE_NEIGHBOURS
20+
21+
822int min (int a , int b ) {
923 return (a < b )?a :b ;
1024}
@@ -156,19 +170,40 @@ cons
156170memory_layout
157171-------------
158172_heap_start: # static marker defined at link time
159- heap_entry{is_free, size}
160- heap_entry{is_free, size}
161- heap_entry{is_free, size}
173+ heap_entry{HEAP_BLOCK_FIRST, size=0}
174+ heap_entry{HEAP_BLOCK_ALLOCATED, size}
175+ heap_entry{HEAP_BLOCK_FREE, size}
176+ heap_entry{HEAP_BLOCK_ALLOCATED, size}
162177...
163- ... heap_entry_count times
178+ heap_entry{HEAP_BLOCK_LAST, size=0}
164179... unused memory
165180_heap_end: # dynamic marker based on current esp
166181
167182*/
183+
184+ static union heap_entry {
185+ // header of each allocated or unallocated block
186+ struct {
187+ // including the header size
188+ uint32_t size ; // size of current block
189+ uint32_t prev_size ; // size of prev block for doubly linked list
190+ uint8_t state ;
191+ } content ;
192+ uint32_t __padding [3 ]; // force heap_entry size to be 12 bytes.
193+ } ;
194+
195+ #define HEAP_HEADER_SIZE (sizeof(union heap_entry))
196+
197+ #define HEAP_BLOCK_FREE 0
198+ #define HEAP_BLOCK_ALLOCATED 1
199+ #define HEAP_BLOCK_FIRST 2
200+ #define HEAP_BLOCK_LAST 3
201+
168202extern void * get_current_esp (); // defined in stdlib.asm
169- extern char _heap_start []; // defined in linker.ld
170- static int heap_entry_count = 0 ;
203+ // symbol name is defined in linker.ld
204+ extern char _heap_start [] ;
171205static const int heap_stack_safety_gap = 1024 ; // keep 1 kb free between stack and heap
206+ static int heap_initialized = 0 ; // false
172207
173208// benchmarking
174209static int benchmark_heap_inuse = 0 ;
@@ -184,104 +219,179 @@ int benchmark_get_heap_area() {
184219 return benchmark_heap_area ;
185220}
186221
187- static union heap_entry {
188- // header of each allocated or unallocated block
189- struct {
190- uint32_t size ; // including the header size
191- uint8_t is_free ;
192- } content ;
193- uint32_t __padding [2 ]; // force heap_entry size to be 8 bytes.
194- } ;
222+ static inline void heap_panic (const char * msg ) {
223+ printf ("heap memory err: %s\n" , msg );
224+ exit (-1 );
225+ }
226+
227+ static inline void heap_assert (int is_not_panic , const char * msg ) {
228+ if (!is_not_panic ) {
229+ heap_panic (msg );
230+ }
231+ return ;
232+ }
195233
196- static union heap_entry * malloc_allocate_new_block (union heap_entry * block , size_t size ) {
197- void * max_loc = get_current_esp ()- heap_stack_safety_gap - size ;
198- if ((void * )block > max_loc ) {
199- // panic if no memory to allocate
200- printf ("failed to allocate more memory\n" );
201- exit (-1 );
234+ static inline void heap_may_init () {
235+ if (!heap_initialized ) {
236+ union heap_entry * first = & (((union heap_entry * )_heap_start )[0 ]);
237+ union heap_entry * last = & (((union heap_entry * )_heap_start )[1 ]);
238+ first -> content .state = HEAP_BLOCK_FIRST ;
239+ last -> content .state = HEAP_BLOCK_LAST ;
240+ first -> content .prev_size = 0 ;
241+ last -> content .size = HEAP_HEADER_SIZE ;
242+ first -> content .size = last -> content .prev_size = HEAP_HEADER_SIZE ;
243+ heap_initialized = 1 ;
202244 }
203- benchmark_heap_area = max ( benchmark_heap_area , ( int )((( void * ) block ) + size - ( void * ) _heap_start ));
245+ }
204246
205- block -> content .size = size ;
206- block -> content .is_free = 1 ; // new block is free at init.
207- heap_entry_count ++ ;
208- return block ;
247+ static inline union heap_entry * heap_next_block (union heap_entry * current ) {
248+ return (((void * )current )+ current -> content .size );
209249}
210250
211- static union heap_entry * malloc_split_block (union heap_entry * block , size_t size ) {
212- // returns the first one of the two splitted blocks
251+ static inline union heap_entry * heap_prev_block (union heap_entry * current ) {
252+ return (((void * )current )- current -> content .prev_size );
253+ }
213254
214- // [ --------- old-free-block-------------- ]
215- // [ allocating-block] [new-left-over-block]
216- size_t left_over_size = block -> content .size - size ;
217- if (left_over_size < sizeof (union heap_entry )+8 ) {
218- // do not split block if left-over block for less than 8 bytes allocation.
219- // i.e. do not split block
220- return block ;
221- }
222- // allocate new-left-over-block
223- union heap_entry * block_second = malloc_allocate_new_block ((union heap_entry * )(((void * )block )+ size ), left_over_size );
224- // resize first block
225- block -> content .size = size ;
226- return block ;
255+ static inline union heap_entry * heap_push_back (union heap_entry * olast , size_t new_size ) {
256+ heap_assert (olast -> content .state == HEAP_BLOCK_LAST ,
257+ "heap_push_back not called on last" );
258+ void * _heap_end = get_current_esp ()- heap_stack_safety_gap ;
259+
260+ olast -> content .state = HEAP_BLOCK_FREE ;
261+ olast -> content .size = new_size ;
262+ union heap_entry * nlast = heap_next_block (olast );
263+
264+ benchmark_heap_area = max (benchmark_heap_area , (int )nlast - (int )_heap_start );
265+
266+ // panic if no memory to allocate
267+ // using nlast instead of heap_next_block(nlast) as diff
268+ // should be negligible compared to heap_stack_safety_gap.
269+ heap_assert (_heap_end > (void * )nlast , "failed to allocate more memory" );
270+
271+ // new last block
272+ nlast -> content .state = HEAP_BLOCK_LAST ;
273+ nlast -> content .size = HEAP_HEADER_SIZE ;
274+ nlast -> content .prev_size = new_size ;
275+ return olast ;
227276}
228277
229- static void malloc_merge_onfree (union heap_entry * block ) {
230- // merge recently free block to neighboring block
231- // if they are free are also free to form large free
232- // block.
233- if (!block -> content .is_free ) return ;
278+ static inline union heap_entry * heap_freeblock_split (union heap_entry * node , size_t first_size ) {
279+ heap_assert (node -> content .state == HEAP_BLOCK_FREE ,
280+ "heap_freeblock_split not called on free block" );
281+
282+ // before: [ -------- old-free-block--------- ] [out-block]
283+ // after : [ new-first-block][new-second-block] [out-block]
284+
285+ size_t old_size = node -> content .size ;
286+ size_t second_block_size = old_size - first_size ;
287+
288+ heap_assert (first_size <=old_size ,
289+ "heap_freeblock_split !(first_size<=old_size)" );
290+
291+ if (second_block_size < HEAP_HEADER_SIZE + 4 ) {
292+ // do not split block if second_block_data_size < 4
293+ return node ;
294+ }
234295
235- // TODO(scopeinfinity): implementation pending.
296+ union heap_entry * out_block = heap_next_block (node );
297+ node -> content .size = first_size ;
298+ union heap_entry * second = heap_next_block (node );
299+ second -> content .state = second -> content .state ;
300+ second -> content .size = second_block_size ;
301+ second -> content .prev_size = node -> content .size ;
302+
303+ out_block -> content .prev_size = second -> content .size ;
304+ return node ;
236305}
237306
238- static union heap_entry * malloc_find_freeblock (size_t size ) {
239- // size includes header size
240- void * loc = (void * )_heap_start ; // start
241- int block_id = 0 ;
242- while (1 ) {
307+ static inline union heap_entry * heap_freeblocks_merge (union heap_entry * node ) {
308+ heap_assert (node -> content .state == HEAP_BLOCK_FREE ,
309+ "heap_freeblocks_merge not called on free block" );
310+
311+ // before: [prev] [ node ] [next]
312+ // after : [prev] [ node ] [next] OR
313+ // after : [prev + node ] [next] OR
314+ // after : [prev] [ node + next] OR
315+ // after : [prev + node + next]
316+
317+ union heap_entry * prev = heap_prev_block (node );
318+ union heap_entry * next = heap_prev_block (node );
319+ if (prev == HEAP_BLOCK_FREE ) {
320+ // merge
321+ prev -> content .size += node -> content .size ;
322+ next -> content .prev_size = prev -> content .size ;
323+ node = prev ;
324+ }
325+ if (next == HEAP_BLOCK_FREE ) {
326+ // merge
327+ union heap_entry * next_2 = heap_prev_block (next );
328+ node -> content .size += next -> content .size ;
329+ next_2 -> content .prev_size = node -> content .size ;
330+ }
331+ return node ;
332+ }
243333
244- union heap_entry * block = loc ;
245- if (block_id == heap_entry_count ) {
246- // create new node
247- return malloc_allocate_new_block (block , size );
334+ static inline union heap_entry * heap_get_free_block (size_t new_size ) {
335+ union heap_entry * block = (union heap_entry * )_heap_start ; // start
336+ uint32_t most_appropriate_value = UINT_MAX ;
337+ union heap_entry * most_appropriate_block = NULL ;
338+ while (1 ) {
339+ if (block -> content .state == HEAP_BLOCK_LAST ) {
340+ break ;
248341 }
249- if ((!block -> content .is_free ) || block -> content .size < size ) {
250- // block is not free
251- // not sufficient memory in this block
252- loc += block -> content .size ;
253- block_id ++ ;
254- continue ;
342+ if (block -> content .state == HEAP_BLOCK_FREE && block -> content .size >= new_size ) {
343+ // block is free and have sufficient memory in this block
344+ uint32_t my_value = (block -> content .size - new_size );
345+ if (my_value < most_appropriate_value ) {
346+ most_appropriate_value = my_value ;
347+ most_appropriate_block = block ;
348+ #ifndef HEAP_MALLOC_SEARCH_MOST_APPROPRIATE_BLOCK
349+ break ;
350+ #endif
351+ }
255352 }
256- // returns the block after split or no-split
257- return malloc_split_block (block , size );
353+ block = heap_next_block (block );
354+ }
355+ if (most_appropriate_block == NULL ) {
356+ return heap_push_back (block , new_size );
357+ } else {
358+ #ifdef HEAP_MALLOC_SPLIT_ON_REALLOCATING_BLOCK
359+ return heap_freeblock_split (most_appropriate_block , new_size );
360+ #else
361+ return most_appropriate_block ;
362+ #endif
258363 }
259364}
260365
261366void * malloc (size_t size ) {
367+ heap_may_init ();
262368 // allocates in chunk of 4
263369 size = ((size + 3 ) >> 2 ) << 2 ;
264370 // allocate header
265- size += sizeof ( union heap_entry ) ;
371+ size += HEAP_HEADER_SIZE ;
266372
267- union heap_entry * header = malloc_find_freeblock (size );
373+ union heap_entry * header = heap_get_free_block (size );
268374
269375 benchmark_heap_inuse += size ;
270- header -> content .is_free = 0 ; // false
271- return (((void * )header )+ sizeof ( union heap_entry ) );
376+ header -> content .state = HEAP_BLOCK_ALLOCATED ;
377+ return (((void * )header )+ HEAP_HEADER_SIZE );
272378}
273379
274380void free (void * ptr ) {
275381 if (ptr == NULL ) return ;
382+ heap_may_init ();
383+
276384 // current version of malloc is non-optimal and doesn't
277385 // do any free operation.
278- union heap_entry * header = ptr - sizeof ( union heap_entry ) ;
279- if (header -> content .is_free != 0 ) {
386+ union heap_entry * header = ptr - HEAP_HEADER_SIZE ;
387+ if (header -> content .state != HEAP_BLOCK_ALLOCATED ) {
280388 // trying to free unallocated memory
281389 return ;
282390 }
283- header -> content .is_free = 1 ; // true
284- size_t size = header -> content .size ;
285- benchmark_heap_inuse -= size ;
286- malloc_merge_onfree (header );
391+ header -> content .state = HEAP_BLOCK_FREE ;
392+ benchmark_heap_inuse -= header -> content .size ;
393+
394+ #ifdef HEAP_MALLOC_FREE_MERGE_NEIGHBOURS
395+ heap_freeblocks_merge (header );
396+ #endif
287397}
0 commit comments