pelib
2.0.0
|
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