3 /* void * malloc( size_t )
5 This file is part of the Public Domain C Library (PDCLib).
6 Permission is granted to use, modify, and / or redistribute at will.
15 #include <_PDCLIB_glue.h>
17 /* TODO: Primitive placeholder. Much room for improvement. */
19 /* Keeping pointers to the first and the last element of the free list. */
20 struct _PDCLIB_headnode_t _PDCLIB_memlist = { NULL, NULL };
22 void * malloc( size_t size )
28 if ( size < _PDCLIB_MINALLOC )
30 size = _PDCLIB_MINALLOC;
33 struct _PDCLIB_memnode_t * current = _PDCLIB_memlist.first;
34 struct _PDCLIB_memnode_t * previous = NULL;
35 struct _PDCLIB_memnode_t * firstfit = NULL;
36 struct _PDCLIB_memnode_t * firstfit_previous = NULL;
37 /* Trying exact fit */
38 while ( current != NULL )
40 if ( current->size == size )
42 /* Found exact fit, allocate node */
43 if ( previous != NULL )
45 /* Node in the middle of the list */
46 previous->next = current->next;
50 /* Node is first in list */
51 _PDCLIB_memlist.first = current->next;
53 if ( _PDCLIB_memlist.last == current )
55 /* Node is last in list */
56 _PDCLIB_memlist.last = previous;
58 return (char *)current + sizeof( struct _PDCLIB_memnode_t );
60 else if ( current->size > size && ( firstfit == NULL || current->size < firstfit->size ) )
62 /* Remember previous node in case we do not get an exact fit.
63 Note that this is the node *pointing to* the first fit,
64 as we need that for allocating (i.e., changing next pointer).
66 firstfit_previous = previous;
69 /* Skip to next node */
71 current = current->next;
73 /* No exact fit; go for first fit */
74 if ( firstfit != NULL )
76 bool node_split = false;
77 if ( ( firstfit->size - size ) > ( _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ) ) )
79 /* Oversized - split into two nodes */
80 struct _PDCLIB_memnode_t * newnode = (struct _PDCLIB_memnode_t *)( (char *)firstfit + sizeof( struct _PDCLIB_memnode_t ) + size );
81 newnode->size = firstfit->size - size - sizeof( struct _PDCLIB_memnode_t );
82 newnode->next = firstfit->next;
83 firstfit->next = newnode;
84 firstfit->size = firstfit->size - newnode->size - sizeof( struct _PDCLIB_memnode_t );
87 if ( firstfit_previous != NULL )
89 /* Node in the middle of the list */
90 firstfit_previous->next = firstfit->next;
94 /* Node is first in list */
95 _PDCLIB_memlist.first = firstfit->next;
97 if ( _PDCLIB_memlist.last == firstfit )
99 /* Node is last in list */
102 _PDCLIB_memlist.last = firstfit->next;
106 _PDCLIB_memlist.last = firstfit_previous;
109 return (char *)firstfit + sizeof( struct _PDCLIB_memnode_t );
113 /* No fit possible; how many additional pages do we need? */
114 size_t pages = ( ( size + sizeof( struct _PDCLIB_memnode_t ) - 1 ) / _PDCLIB_PAGESIZE ) + 1;
115 /* Allocate more pages */
116 struct _PDCLIB_memnode_t * newnode = (struct _PDCLIB_memnode_t *)_PDCLIB_allocpages( (int)pages );
117 if ( newnode != NULL )
119 newnode->next = NULL;
120 newnode->size = pages * _PDCLIB_PAGESIZE - sizeof( struct _PDCLIB_memnode_t );
121 if ( ( newnode->size - size ) > ( _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ) ) )
123 /* Oversized - split into two nodes */
124 struct _PDCLIB_memnode_t * splitnode = (struct _PDCLIB_memnode_t *)( (char *)newnode + sizeof( struct _PDCLIB_memnode_t ) + size );
125 splitnode->size = newnode->size - size - sizeof( struct _PDCLIB_memnode_t );
126 newnode->size = size;
127 /* Add splitted node as last element to free node list */
128 if ( _PDCLIB_memlist.last == NULL )
130 _PDCLIB_memlist.first = splitnode;
134 _PDCLIB_memlist.last->next = splitnode;
136 splitnode->next = NULL; /* TODO: This is bug #7, uncovered by testdriver yet. */
137 _PDCLIB_memlist.last = splitnode;
139 return (char *)newnode + sizeof( struct _PDCLIB_memnode_t );
142 /* No fit, heap extension not possible - out of memory */
150 #include <_PDCLIB_test.h>
158 /* Effective page size, i.e. how many bytes can be allocated and still be on
161 #define EFFECTIVE _PDCLIB_PAGESIZE - sizeof( struct _PDCLIB_memnode_t )
162 #define MEMTEST( ptr, size ) ( ( ptr = malloc( size ) ) != NULL ) && ( memset( ptr, 0, size ) == ptr )
164 char * pages_start = 0;
165 int test_nodes( char const * const, int, ... );
166 void PRINT( char const * const, ... );
168 /* This can be enabled to give a dump of node information */
170 void PRINT( char const * const format, ... )
173 va_start( ap, format );
174 vprintf( format, ap );
177 void PRINT( char const * const format, ... )
183 /* Helper function checking number of allocated memory pages and the nodes
184 in the free memory list against expectations.
186 int test_nodes( char const * const action, int expected_pages, ... )
188 static int count = 1;
191 /* Determining the amount of allocated pages */
192 int allocated_pages = ( (intptr_t)_PDCLIB_allocpages( 0 ) - (intptr_t)pages_start ) / _PDCLIB_PAGESIZE;
193 PRINT( "Test #%2d, %d allocated pages", count++, allocated_pages );
194 if ( allocated_pages != expected_pages )
196 PRINT( " - MISMATCH, expected\n %d pages\n", expected_pages );
203 /* Now moving through the free nodes list */
205 va_start( ap, expected_pages );
206 struct _PDCLIB_memnode_t * tracer = _PDCLIB_memlist.first;
209 while ( tracer != NULL )
212 size_t node_location = (char *)tracer - (char *)pages_start;
213 PRINT( " - node %.4p, size %#.4x", node_location, tracer->size );
215 size_t expected_location = va_arg( ap, size_t );
216 if ( expected_location == 0 )
218 PRINT( " - UNEXPECTED NODE\n" );
222 /* Memorizing first and last expected node for later comparison. */
223 if ( firstnode == 0 )
225 firstnode = expected_location;
227 lastnode = expected_location;
228 /* Comparing expected node against current node */
229 size_t expected_size = va_arg( ap, size_t );
230 if ( ( node_location != expected_location ) || ( tracer->size != expected_size ) )
232 PRINT( " - MISMATCH, expected values\n %.4p %#.4p\n", expected_location, expected_size );
239 tracer = tracer->next;
241 /* Comparing first and last node in memlist against expectations. */
242 PRINT( " - memlist first: %#.4x - last: %#.4x",
243 ( _PDCLIB_memlist.first == NULL ) ? NULL : (char *)_PDCLIB_memlist.first - (char *)pages_start,
244 ( _PDCLIB_memlist.last == NULL ) ? NULL : (char *)_PDCLIB_memlist.last - (char *)pages_start );
245 if ( ( firstnode != 0 ) &&
246 ( ( ( (char *)_PDCLIB_memlist.first - (char *)pages_start ) != firstnode )
247 || ( ( (char *)_PDCLIB_memlist.last - (char *)pages_start ) != lastnode ) ) )
249 PRINT( " - MISMATCH, expected values\n %#.4x - last: %#.4x\n", firstnode, lastnode );
262 /* Note that this test driver heavily tests *internals* of the implementation
263 above (and of free() and realloc(), too). That means that changes in the
264 implementation must be accompanied with appropriate changes of the test
265 driver. It does *not* make a good regression tester for the implementation,
266 I am afraid, and thus there is no REGTEST equivalent.
272 void * ptr1, * ptr2, * ptr3, * ptr4, * ptr5, * ptr6, * ptr7, * ptr8, * ptr9, * ptrA, * ptrB, * ptrC;
274 pages_start = _PDCLIB_allocpages( 0 );
275 PRINT( "\nEffective is: %#.4x\nsizeof( memnode ) is: %#.2x\n\n", EFFECTIVE, sizeof( struct _PDCLIB_memnode_t ) );
277 /* Allocating 10 bytes; expecting one page allocation and a node split */
278 TESTCASE( MEMTEST( ptr1, 10 ) );
279 TESTCASE( test_nodes( "Allocating 10 bytes.", 1,
280 sizeof( struct _PDCLIB_memnode_t ) + 10, EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - 10,
283 /* Allocating the rest of the page; expecting no page allocation and assignment of the remaining node */
284 TESTCASE( MEMTEST( ptr2, EFFECTIVE - 10 - sizeof( struct _PDCLIB_memnode_t ) ) );
285 TESTCASE( test_nodes( "Allocating the rest of the page.", 1,
288 /* Allocating a full page; expecting one page allocation, no node split */
289 TESTCASE( MEMTEST( ptr3, EFFECTIVE ) );
290 TESTCASE( test_nodes( "Allocating a full page.", 2,
293 /* Allocating *almost* a full page; expecting one page allocation, no node split */
294 TESTCASE( MEMTEST( ptr4, EFFECTIVE - 4 ) );
295 TESTCASE( test_nodes( "Allocating *almost* a full page.", 3,
298 /* Freeing and re-allocating the "almost" full page; expecting no page allocation, no node split */
300 TESTCASE( MEMTEST( ptr5, EFFECTIVE - 4 ) );
301 TESTCASE( ptr4 == ptr5 );
302 TESTCASE( test_nodes( "Freeing and re-allocating the \"almost\" full page.", 3 ) );
304 /* Freeing the full page from test #3; expecting a full-sized free node. */
306 TESTCASE( test_nodes( "Freeing the full page from test #3.", 3,
307 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
310 /* Allocating two full pages; expecting two page allocations, no node split */
311 TESTCASE( MEMTEST( ptr3, EFFECTIVE + _PDCLIB_PAGESIZE ) );
312 TESTCASE( test_nodes( "Allocating two full pages.", 5,
313 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
316 /* Re-allocating to size of 10 bytes; expecting no page allocation, no node split */
317 /* TODO: Shouldn't realloc() split the now much-too-large node? */
318 TESTCASE( realloc( ptr3, 10 ) == ptr3 );
319 TESTCASE( test_nodes( "Re-allocating to size of 10 bytes.", 5,
320 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
323 /* Re-allocating to size of two full pages; expecting no page allocation, no node split */
324 TESTCASE( realloc( ptr3, EFFECTIVE + _PDCLIB_PAGESIZE ) == ptr3 );
325 TESTCASE( test_nodes( "Re-allocating to size of two full pages.", 5,
326 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
329 /* Re-allocating to size of three full pages; expecting three page allocation, freeing of two-page node */
330 TESTCASE( realloc( ptr3, EFFECTIVE + _PDCLIB_PAGESIZE * 2 ) != ptr3 );
331 TESTCASE( test_nodes( "Re-allocating to size of three full pages.", 8,
332 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
333 _PDCLIB_PAGESIZE * 3, EFFECTIVE + _PDCLIB_PAGESIZE,
336 /* Allocating two full pages; expecting allocation of the available two-page node */
337 TESTCASE( MEMTEST( ptr4, EFFECTIVE + _PDCLIB_PAGESIZE ) );
338 TESTCASE( test_nodes( "Allocating two full pages.", 8,
339 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
342 /* Allocating zero bytes; expecting no change */
343 TESTCASE( ! MEMTEST( ptr6, 0 ) );
344 TESTCASE( test_nodes( "Allocating zero bytes.", 8,
345 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
348 /* Allocating 4 bytes; expecting upsizing of requestupsizing of size, node split */
349 TESTCASE( MEMTEST( ptr7, 4 ) );
350 TESTCASE( test_nodes( "Allocating 4 bytes.", 8,
351 _PDCLIB_PAGESIZE * 1 + _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ),
352 EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ),
355 /* Allocating the rest of the page; expecting no page allocation and assignment of the remaining node */
356 TESTCASE( MEMTEST( ptr8, EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) ) );
357 TESTCASE( test_nodes( "Allocating the rest of the page.", 8, 0 ) );
359 /* Freeing the node from the previous test; expecting node to re-appear in free list */
361 TESTCASE( test_nodes( "Freeing the node from the previous test.", 8,
362 _PDCLIB_PAGESIZE * 1 + _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ),
363 EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ),
366 /* Allocating one byte more than available in free node; expecting page allocation */
367 TESTCASE( MEMTEST( ptr8, EFFECTIVE + 1 - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) ) );
368 TESTCASE( test_nodes( "Allocating one byte more than available in free node.", 9,
369 _PDCLIB_PAGESIZE * 1 + _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ),
370 EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ),
373 /* Re-allocating with NULL pointer; expecting no page allocation, no node split */
374 ptr9 = realloc( NULL, EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) );
375 TESTCASE( ptr9 != NULL );
376 TESTCASE( memset( ptr9, 0, EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) ) == ptr9 );
377 TESTCASE( test_nodes( "Re-allocating with NULL pointer.", 9, 0 ) );
379 /* Allocating a bit more than half a page; expecting page allocation, node split */
380 #define TESTSIZE 3000
381 TESTCASE( MEMTEST( ptrA, TESTSIZE ) );
382 TESTCASE( test_nodes( "Allocating a bit more than half a page.", 10,
383 _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
384 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
387 /* Allocating a bit more than half a page; expecting page allocation, node split */
388 TESTCASE( MEMTEST( ptrB, TESTSIZE ) );
389 TESTCASE( test_nodes( "Allocating a bit more than half a page.", 11,
390 _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
391 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
392 _PDCLIB_PAGESIZE * 10 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
393 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
396 /* Allocating a bit more than half a page; expecting page allocation, node split */
397 TESTCASE( MEMTEST( ptrC, TESTSIZE ) );
398 TESTCASE( test_nodes( "Allocating a bit more than half a page.", 12,
399 _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
400 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
401 _PDCLIB_PAGESIZE * 10 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
402 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
403 _PDCLIB_PAGESIZE * 11 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
404 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
407 /* Freeing the middle node */
409 TESTCASE( test_nodes( "Freeing the middle node.", 12,
410 _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
411 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
412 _PDCLIB_PAGESIZE * 10 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
413 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
414 _PDCLIB_PAGESIZE * 11 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
415 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
416 _PDCLIB_PAGESIZE * 10,
421 puts( " NOTEST malloc() test driver is PDCLib-specific." );