pelib  2.0.0
src/malloc.c
Go to the documentation of this file.
00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include <pelib/malloc.h>
00004 
00005 #if 0
00006 #define debug(var) printf("[%s:%s:%d] %s = \"%s\"\n", __FILE__, __FUNCTION__, __LINE__, #var, var); fflush(NULL)
00007 #define debug_addr(var) printf("[%s:%s:%d] %s = \"%p\"\n", __FILE__, __FUNCTION__, __LINE__, #var, var); fflush(NULL)
00008 #define debug_int(var) printf("[%s:%s:%d] %s = \"%d\"\n", __FILE__, __FUNCTION__, __LINE__, #var, var); fflush(NULL)
00009 #define debug_size_t(var) printf("[%s:%s:%d] %s = \"%zu\"\n", __FILE__, __FUNCTION__, __LINE__, #var, var); fflush(NULL)
00010 #else
00011 #define debug(var)
00012 #define debug_addr(var)
00013 #define debug_int(var)
00014 #define debug_size_t(var)
00015 #endif
00016 
00017 void
00018 pelib_mem_malloc_init(pelib_malloc_queue_t* spacep, void* mem, size_t size)
00019 {
00020         spacep->tail = (pelib_malloc_block_t*) malloc(sizeof(pelib_malloc_block_t));
00021         debug_size_t(size);
00022         spacep->tail->free_size = size;
00023         spacep->tail->space = mem;
00024         /* make a circular list by connecting tail to itself */
00025         spacep->tail->next = spacep->tail;
00026         spacep->mem = mem;
00027 }
00028 
00029 void*
00030 pelib_mem_malloc(pelib_malloc_queue_t *spacep, size_t size, int alignment)
00031 {
00032         // simple memory allocator, loosely based on public domain code developed by
00033         // Michael B. Allen and published on "The Scripts--IT /Developers Network".
00034         // Approach: 
00035         // - maintain linked list of pointers to memory. A block is either completely
00036         //      malloced (free_size = 0), or completely free (free_size > 0).
00037         //      The space field always points to the beginning of the block
00038         // - malloc: traverse linked list for first block that has enough space
00039         // - free: Check if pointer exists. If yes, check if the new block should be
00040         //      merged with neighbors. Could be one or two neighbors.
00041 
00042         pelib_malloc_block_t *b1, *b2, *b3;      // running pointers for blocks
00043 
00044         if (size == 0) 
00045         {
00046                 debug_addr(NULL);
00047                 return NULL;
00048         }
00049 
00050         // always first check if the tail block has enough space, because that
00051         // is the most likely. If it does and it is exactly enough, we still
00052         // create a new block that will be the new tail, whose free space is
00053         // zero. This acts as a marker of where free space of predecessor ends
00054         b1 = spacep->tail;
00055         // If we request (alignment > 1) memory, then it's ok if the tail free memory can handle the size requested. Otherwise, we need to check if the tail can handle the size requested even after skipping as many bytes as necessary to get some (alignment > 1) address.
00056         /*
00057         debug_addr(b1);
00058         debug_size_t(b1->free_size);
00059         debug_size_t(size);
00060         debug_int(b1->free_size >= size);
00061         debug_int(alignment);
00062         debug_int(alignment > 1);
00063         debug_int((b1->free_size >= size && !(alignment > 1)));
00064         debug_addr(b1->space);
00065         debug_size_t(b1->free_size);
00066         */
00067         if ((b1->free_size >= size && !(alignment > 1)) || ((alignment > 1) && ((((size_t)b1->space % alignment) == 0) || (b1->free_size - alignment + ((size_t)b1->space % alignment) >= size))))
00068         {
00069                 // need to insert new block; new order is: b1->b2 (= new tail)
00070                 b2 = (pelib_malloc_block_t*) malloc(sizeof(pelib_malloc_block_t));
00071                 b2->next = b1->next;
00072                 b1->next = b2;
00073                 b2->free_size = b1->free_size - size;
00074                 b2->space  = b1->space + size;
00075                 
00076                 // Display the block used  
00077                 if(!(alignment > 1) || ((size_t)b1->space % alignment) == 0)
00078                 {
00079                         // Display the new block that holds the unused memory
00080 
00081 
00082                         // need to update the tail
00083                         spacep->tail = b2;
00084 
00085                         b1->free_size = 0;
00086                         debug_addr(spacep->mem);
00087                         debug_size_t(b1->space - spacep->mem);
00088                         debug_size_t(size);
00089                         return b1->space;
00090                 }
00091                 else
00092                 {
00093                         // need to update the tail
00094                         spacep->tail = b2;
00095 
00096                         b3 = (pelib_malloc_block_t*) malloc(sizeof(pelib_malloc_block_t));
00097                         b3->next = b1->next;
00098                         b1->next = b3;
00099                         b3->space = b1->space + alignment - ((size_t)b1->space % alignment);
00100                         b1->free_size = alignment - ((size_t)b1->space % alignment);
00101                         b2->space += alignment - ((size_t)b1->space % alignment);
00102                         b2->free_size -= alignment - ((size_t)b1->space % alignment);
00103 
00104                         // Display the new block that holds the unused memory
00105 
00106                         // Display the new block holding memory skipped for alignment
00107 
00108                         // Display the final block allocated 
00109 
00110                         b3->free_size = 0;
00111 
00112                         /*
00113                         debug_addr(spacep->tail);
00114                         debug_addr(spacep->tail->next);
00115                         debug_addr(spacep->tail->space);
00116                         debug_addr(spacep->tail->next->space);
00117                         */
00118                         if(spacep->tail->next >= spacep->tail)
00119                         {
00120                                 debug_size_t(spacep->tail->next->space - spacep->tail->space);
00121                         }
00122                         else
00123                         {
00124                                 debug_size_t(spacep->tail->space - spacep->tail->next->space);
00125                         }
00126                         debug_addr(b3->space);
00127                         return b3->space;
00128                 }
00129         }
00130 
00131         // tail didn't have enough space; loop over whole list from beginning
00132         // As for above, stop at the first free space big enough to handle the request if no (alignment > 1) memory is requested
00133         // Otherwise, check each space after memory alignment
00134         while   (((b1->next->free_size < size) && !(alignment > 1)) || ((alignment > 1) && (((b1->free_size - alignment + ((size_t)b1->space % alignment) < size) && (((size_t)b1->space % alignment) != 0)) || ((((size_t)b1->space % alignment) == 0) && (b1->next->free_size < size)))))
00135         {
00136                 if (b1->next == spacep->tail)
00137                 {
00138                         return NULL; // we came full circle
00139                 }
00140                 b1 = b1->next;
00141         }
00142 
00143         b2 = b1->next;
00144 
00145         // Display the block selected
00146 
00147         // If memory alignment is requested and the block is strictly bigger than requested, then we need to skip some bytes before allocating
00148         // If the block is not strictly bigger than requested, then we know that the free block is already (alignment > 1) because otherwise the loop above 
00149         // would have returned an allocation failure
00150         if (b2->free_size > size && (alignment > 1))
00151         {
00152                 b3 = (pelib_malloc_block_t*) malloc(sizeof(pelib_malloc_block_t));
00153                 b3->next = b2->next;
00154                 b2->next = b3;
00155                 b3->free_size = b2->free_size - alignment + ((size_t)b2->space % alignment);
00156                 b2->free_size = b2->free_size - b3->free_size;
00157                 b3->space = b2->space + alignment - ((size_t)b2->space % alignment);
00158 
00159                 // Display the block that handles the memory skipped
00160 
00161                 b2 = b3;
00162         }
00163 
00164         // If, perhaps after having split b2 into b2 and b3, b2 is still too big, then we need to create yet another block.
00165         if (b2->free_size > size)
00166         {
00167                 // split block; new block order: b1->b2->b3
00168                 b3 = (pelib_malloc_block_t*) malloc(sizeof(pelib_malloc_block_t));
00169                 b3->next = b2->next; // reconnect pointers to add block b3
00170                 b2->next = b3;
00171                 b3->free_size = b2->free_size - size; // b3 gets remainder free space
00172                 b3->space = b2->space + size; // need to shift space pointer
00173         }
00174 
00175 
00176         // Now b2 is the exact size and (alignment > 1) as requested
00177         b2->free_size = 0; // block b2 is completely used
00178         debug_addr(b2->space);
00179         return b2->space;
00180 }
00181 
00182 void
00183 pelib_mem_free(pelib_malloc_queue_t *spacep, void *ptr)
00184 {
00185         pelib_malloc_block_t *b1, *b2, *b3;     // running block pointers
00186         int j1, j2;                     // booleans determining merging of blocks
00187 
00188         // loop over whole list from the beginning until we locate space ptr
00189         b1 = spacep->tail;
00190         while (b1->next->space != ptr && b1->next != spacep->tail)
00191         {
00192                 b1 = b1->next;
00193         }
00194 
00195         // b2 is target block whose space must be freed
00196         b2 = b1->next;
00197         // tail either has zero free space, or hasn't been malloc'ed
00198         if (b2 == spacep->tail)
00199         {
00200                 return;
00201         }
00202 
00203         // reset free space for target block (entire block)
00204         b3 = b2->next;
00205         b2->free_size = b3->space - b2->space;
00206 
00207         // determine with what non-empty blocks the target block can be merged
00208         j1 = (b1->free_size > 0 && b1 != spacep->tail); // predecessor block
00209         j2 = (b3->free_size > 0 || b3 == spacep->tail); // successor block
00210 
00211         if (j1)
00212         {
00213                 if (j2)
00214                 {
00215                         // splice all three blocks together: (b1,b2,b3) into b1
00216                         b1->next = b3->next;
00217                         b1->free_size += b3->free_size + b2->free_size;
00218                         if (b3 == spacep->tail)
00219                         {
00220                                 spacep->tail = b1;
00221                         }
00222                         free(b3);
00223                 }
00224                 else
00225                 {
00226                         // only merge (b1,b2) into b1
00227                         b1->free_size += b2->free_size;
00228                         b1->next = b3;
00229                 }
00230                 free(b2);
00231         }
00232         else
00233         {
00234                 if (j2)
00235                 {
00236                         // only merge (b2,b3) into b2
00237                         b2->next = b3->next;
00238                         b2->free_size += b3->free_size;
00239                         if (b3 == spacep->tail)
00240                         {
00241                                 spacep->tail = b2;
00242                         }
00243                         free(b3);
00244                 }
00245         }
00246 }
00247