1 /* void * malloc( size_t )
3 This file is part of the Public Domain C Library (PDCLib).
4 Permission is granted to use, modify, and / or redistribute at will.
13 #include <_PDCLIB_glue.h>
15 /* TODO: Primitive placeholder. Much room for improvement. */
17 /* Keeping pointers to the first and the last element of the free list. */
18 struct _PDCLIB_headnode_t _PDCLIB_memlist = { NULL, NULL };
20 void * malloc( size_t size )
26 if ( size < _PDCLIB_MINALLOC )
28 size = _PDCLIB_MINALLOC;
31 struct _PDCLIB_memnode_t * current = _PDCLIB_memlist.first;
32 struct _PDCLIB_memnode_t * previous = NULL;
33 struct _PDCLIB_memnode_t * firstfit = NULL;
34 struct _PDCLIB_memnode_t * firstfit_previous = NULL;
35 /* Trying exact fit */
36 while ( current != NULL )
38 if ( current->size == size )
40 /* Found exact fit, allocate node */
41 if ( previous != NULL )
43 /* Node in the middle of the list */
44 previous->next = current->next;
48 /* Node is first in list */
49 _PDCLIB_memlist.first = current->next;
51 if ( _PDCLIB_memlist.last == current )
53 /* Node is last in list */
54 _PDCLIB_memlist.last = previous;
56 return (char *)current + sizeof( struct _PDCLIB_memnode_t );
58 else if ( current->size > size && ( firstfit == NULL || current->size < firstfit->size ) )
60 /* Remember previous node in case we do not get an exact fit.
61 Note that this is the node *pointing to* the first fit,
62 as we need that for allocating (i.e., changing next pointer).
64 firstfit_previous = previous;
67 /* Skip to next node */
69 current = current->next;
71 /* No exact fit; go for first fit */
72 if ( firstfit != NULL )
74 bool node_split = false;
75 if ( ( firstfit->size - size ) > ( _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ) ) )
77 /* Oversized - split into two nodes */
78 struct _PDCLIB_memnode_t * newnode = (struct _PDCLIB_memnode_t *)( (char *)firstfit + sizeof( struct _PDCLIB_memnode_t ) + size );
79 newnode->size = firstfit->size - size - sizeof( struct _PDCLIB_memnode_t );
80 newnode->next = firstfit->next;
81 firstfit->next = newnode;
82 firstfit->size = firstfit->size - newnode->size - sizeof( struct _PDCLIB_memnode_t );
85 if ( firstfit_previous != NULL )
87 /* Node in the middle of the list */
88 firstfit_previous->next = firstfit->next;
92 /* Node is first in list */
93 _PDCLIB_memlist.first = firstfit->next;
95 if ( _PDCLIB_memlist.last == firstfit )
97 /* Node is last in list */
100 _PDCLIB_memlist.last = firstfit->next;
104 _PDCLIB_memlist.last = firstfit_previous;
107 return (char *)firstfit + sizeof( struct _PDCLIB_memnode_t );
111 /* No fit possible; how many additional pages do we need? */
112 size_t pages = ( ( size + sizeof( struct _PDCLIB_memnode_t ) - 1 ) / _PDCLIB_PAGESIZE ) + 1;
113 /* Allocate more pages */
114 struct _PDCLIB_memnode_t * newnode = (struct _PDCLIB_memnode_t *)_PDCLIB_allocpages( (int)pages );
115 if ( newnode != NULL )
117 newnode->next = NULL;
118 newnode->size = pages * _PDCLIB_PAGESIZE - sizeof( struct _PDCLIB_memnode_t );
119 if ( ( newnode->size - size ) > ( _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ) ) )
121 /* Oversized - split into two nodes */
122 struct _PDCLIB_memnode_t * splitnode = (struct _PDCLIB_memnode_t *)( (char *)newnode + sizeof( struct _PDCLIB_memnode_t ) + size );
123 splitnode->size = newnode->size - size - sizeof( struct _PDCLIB_memnode_t );
124 newnode->size = size;
125 /* Add splitted node as last element to free node list */
126 if ( _PDCLIB_memlist.last == NULL )
128 _PDCLIB_memlist.first = splitnode;
132 _PDCLIB_memlist.last->next = splitnode;
134 splitnode->next = NULL; /* TODO: This is bug #7, uncovered by testdriver yet. */
135 _PDCLIB_memlist.last = splitnode;
137 return (char *)newnode + sizeof( struct _PDCLIB_memnode_t );
140 /* No fit, heap extension not possible - out of memory */
148 #include <_PDCLIB_test.h>
156 /* Effective page size, i.e. how many bytes can be allocated and still be on
159 #define EFFECTIVE _PDCLIB_PAGESIZE - sizeof( struct _PDCLIB_memnode_t )
160 #define MEMTEST( ptr, size ) ( ( ptr = malloc( size ) ) != NULL ) && ( memset( ptr, 0, size ) == ptr )
162 char * pages_start = 0;
163 int test_nodes( char const * const, int, ... );
164 void PRINT( char const * const, ... );
166 /* This can be enabled to give a dump of node information */
168 void PRINT( char const * const format, ... )
171 va_start( ap, format );
172 vprintf( format, ap );
175 void PRINT( char const * const format, ... )
181 /* Helper function checking number of allocated memory pages and the nodes
182 in the free memory list against expectations.
184 int test_nodes( char const * const action, int expected_pages, ... )
186 static int count = 1;
189 /* Determining the amount of allocated pages */
190 int allocated_pages = ( (intptr_t)_PDCLIB_allocpages( 0 ) - (intptr_t)pages_start ) / _PDCLIB_PAGESIZE;
191 PRINT( "Test #%2d, %d allocated pages", count++, allocated_pages );
192 if ( allocated_pages != expected_pages )
194 PRINT( " - MISMATCH, expected\n %d pages\n", expected_pages );
201 /* Now moving through the free nodes list */
203 va_start( ap, expected_pages );
204 struct _PDCLIB_memnode_t * tracer = _PDCLIB_memlist.first;
207 while ( tracer != NULL )
210 size_t node_location = (char *)tracer - (char *)pages_start;
211 PRINT( " - node %.4p, size %#.4x", node_location, tracer->size );
213 size_t expected_location = va_arg( ap, size_t );
214 if ( expected_location == 0 )
216 PRINT( " - UNEXPECTED NODE\n" );
220 /* Memorizing first and last expected node for later comparison. */
221 if ( firstnode == 0 )
223 firstnode = expected_location;
225 lastnode = expected_location;
226 /* Comparing expected node against current node */
227 size_t expected_size = va_arg( ap, size_t );
228 if ( ( node_location != expected_location ) || ( tracer->size != expected_size ) )
230 PRINT( " - MISMATCH, expected values\n %.4p %#.4p\n", expected_location, expected_size );
237 tracer = tracer->next;
239 /* Comparing first and last node in memlist against expectations. */
240 PRINT( " - memlist first: %#.4x - last: %#.4x",
241 ( _PDCLIB_memlist.first == NULL ) ? NULL : (char *)_PDCLIB_memlist.first - (char *)pages_start,
242 ( _PDCLIB_memlist.last == NULL ) ? NULL : (char *)_PDCLIB_memlist.last - (char *)pages_start );
243 if ( ( firstnode != 0 ) &&
244 ( ( ( (char *)_PDCLIB_memlist.first - (char *)pages_start ) != firstnode )
245 || ( ( (char *)_PDCLIB_memlist.last - (char *)pages_start ) != lastnode ) ) )
247 PRINT( " - MISMATCH, expected values\n %#.4x - last: %#.4x\n", firstnode, lastnode );
260 /* Note that this test driver heavily tests *internals* of the implementation
261 above (and of free() and realloc(), too). That means that changes in the
262 implementation must be accompanied with appropriate changes of the test
263 driver. It does *not* make a good regression tester for the implementation,
264 I am afraid, and thus there is no REGTEST equivalent.
270 void * ptr1, * ptr2, * ptr3, * ptr4, * ptr5, * ptr6, * ptr7, * ptr8, * ptr9, * ptrA, * ptrB, * ptrC;
272 pages_start = _PDCLIB_allocpages( 0 );
273 PRINT( "\nEffective is: %#.4x\nsizeof( memnode ) is: %#.2x\n\n", EFFECTIVE, sizeof( struct _PDCLIB_memnode_t ) );
275 /* Allocating 10 bytes; expecting one page allocation and a node split */
276 TESTCASE( MEMTEST( ptr1, 10 ) );
277 TESTCASE( test_nodes( "Allocating 10 bytes.", 1,
278 sizeof( struct _PDCLIB_memnode_t ) + 10, EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - 10,
281 /* Allocating the rest of the page; expecting no page allocation and assignment of the remaining node */
282 TESTCASE( MEMTEST( ptr2, EFFECTIVE - 10 - sizeof( struct _PDCLIB_memnode_t ) ) );
283 TESTCASE( test_nodes( "Allocating the rest of the page.", 1,
286 /* Allocating a full page; expecting one page allocation, no node split */
287 TESTCASE( MEMTEST( ptr3, EFFECTIVE ) );
288 TESTCASE( test_nodes( "Allocating a full page.", 2,
291 /* Allocating *almost* a full page; expecting one page allocation, no node split */
292 TESTCASE( MEMTEST( ptr4, EFFECTIVE - 4 ) );
293 TESTCASE( test_nodes( "Allocating *almost* a full page.", 3,
296 /* Freeing and re-allocating the "almost" full page; expecting no page allocation, no node split */
298 TESTCASE( MEMTEST( ptr5, EFFECTIVE - 4 ) );
299 TESTCASE( ptr4 == ptr5 );
300 TESTCASE( test_nodes( "Freeing and re-allocating the \"almost\" full page.", 3 ) );
302 /* Freeing the full page from test #3; expecting a full-sized free node. */
304 TESTCASE( test_nodes( "Freeing the full page from test #3.", 3,
305 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
308 /* Allocating two full pages; expecting two page allocations, no node split */
309 TESTCASE( MEMTEST( ptr3, EFFECTIVE + _PDCLIB_PAGESIZE ) );
310 TESTCASE( test_nodes( "Allocating two full pages.", 5,
311 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
314 /* Re-allocating to size of 10 bytes; expecting no page allocation, no node split */
315 /* TODO: Shouldn't realloc() split the now much-too-large node? */
316 TESTCASE( realloc( ptr3, 10 ) == ptr3 );
317 TESTCASE( test_nodes( "Re-allocating to size of 10 bytes.", 5,
318 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
321 /* Re-allocating to size of two full pages; expecting no page allocation, no node split */
322 TESTCASE( realloc( ptr3, EFFECTIVE + _PDCLIB_PAGESIZE ) == ptr3 );
323 TESTCASE( test_nodes( "Re-allocating to size of two full pages.", 5,
324 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
327 /* Re-allocating to size of three full pages; expecting three page allocation, freeing of two-page node */
328 TESTCASE( realloc( ptr3, EFFECTIVE + _PDCLIB_PAGESIZE * 2 ) != ptr3 );
329 TESTCASE( test_nodes( "Re-allocating to size of three full pages.", 8,
330 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
331 _PDCLIB_PAGESIZE * 3, EFFECTIVE + _PDCLIB_PAGESIZE,
334 /* Allocating two full pages; expecting allocation of the available two-page node */
335 TESTCASE( MEMTEST( ptr4, EFFECTIVE + _PDCLIB_PAGESIZE ) );
336 TESTCASE( test_nodes( "Allocating two full pages.", 8,
337 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
340 /* Allocating zero bytes; expecting no change */
341 TESTCASE( ! MEMTEST( ptr6, 0 ) );
342 TESTCASE( test_nodes( "Allocating zero bytes.", 8,
343 _PDCLIB_PAGESIZE * 1, EFFECTIVE,
346 /* Allocating 4 bytes; expecting upsizing of requestupsizing of size, node split */
347 TESTCASE( MEMTEST( ptr7, 4 ) );
348 TESTCASE( test_nodes( "Allocating 4 bytes.", 8,
349 _PDCLIB_PAGESIZE * 1 + _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ),
350 EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ),
353 /* Allocating the rest of the page; expecting no page allocation and assignment of the remaining node */
354 TESTCASE( MEMTEST( ptr8, EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) ) );
355 TESTCASE( test_nodes( "Allocating the rest of the page.", 8, 0 ) );
357 /* Freeing the node from the previous test; expecting node to re-appear in free list */
359 TESTCASE( test_nodes( "Freeing the node from the previous test.", 8,
360 _PDCLIB_PAGESIZE * 1 + _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ),
361 EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ),
364 /* Allocating one byte more than available in free node; expecting page allocation */
365 TESTCASE( MEMTEST( ptr8, EFFECTIVE + 1 - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) ) );
366 TESTCASE( test_nodes( "Allocating one byte more than available in free node.", 9,
367 _PDCLIB_PAGESIZE * 1 + _PDCLIB_MINALLOC + sizeof( struct _PDCLIB_memnode_t ),
368 EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ),
371 /* Re-allocating with NULL pointer; expecting no page allocation, no node split */
372 ptr9 = realloc( NULL, EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) );
373 TESTCASE( ptr9 != NULL );
374 TESTCASE( memset( ptr9, 0, EFFECTIVE - _PDCLIB_MINALLOC - sizeof( struct _PDCLIB_memnode_t ) ) == ptr9 );
375 TESTCASE( test_nodes( "Re-allocating with NULL pointer.", 9, 0 ) );
377 /* Allocating a bit more than half a page; expecting page allocation, node split */
378 #define TESTSIZE 3000
379 TESTCASE( MEMTEST( ptrA, TESTSIZE ) );
380 TESTCASE( test_nodes( "Allocating a bit more than half a page.", 10,
381 _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
382 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
385 /* Allocating a bit more than half a page; expecting page allocation, node split */
386 TESTCASE( MEMTEST( ptrB, TESTSIZE ) );
387 TESTCASE( test_nodes( "Allocating a bit more than half a page.", 11,
388 _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
389 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
390 _PDCLIB_PAGESIZE * 10 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
391 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
394 /* Allocating a bit more than half a page; expecting page allocation, node split */
395 TESTCASE( MEMTEST( ptrC, TESTSIZE ) );
396 TESTCASE( test_nodes( "Allocating a bit more than half a page.", 12,
397 _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
398 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
399 _PDCLIB_PAGESIZE * 10 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
400 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
401 _PDCLIB_PAGESIZE * 11 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
402 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
405 /* Freeing the middle node */
407 TESTCASE( test_nodes( "Freeing the middle node.", 12,
408 _PDCLIB_PAGESIZE * 9 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
409 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
410 _PDCLIB_PAGESIZE * 10 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
411 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
412 _PDCLIB_PAGESIZE * 11 + sizeof( struct _PDCLIB_memnode_t ) + TESTSIZE,
413 EFFECTIVE - sizeof( struct _PDCLIB_memnode_t ) - TESTSIZE,
414 _PDCLIB_PAGESIZE * 10,
419 puts( " NOTEST malloc() test driver is PDCLib-specific." );