Nut/OS  4.10.3
API Reference
heap.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009 by egnite GmbH
00003  * Copyright (C) 2001-2005 by egnite Software GmbH
00004  *
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. Neither the name of the copyright holders nor the names of
00017  *    contributors may be used to endorse or promote products derived
00018  *    from this software without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00027  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00028  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00029  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00030  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  *
00033  * For additional information see http://www.ethernut.de/
00034  */
00035 
00036 /*
00037  * $Id: heap.c 2613 2009-04-15 13:51:10Z haraldkipp $
00038  */
00039 
00045 
00046 #include <cfg/os.h>
00047 #include <sys/heap.h>
00048 
00049 #include <string.h>
00050 #include <stdint.h>
00051 
00052 #ifdef NUTDEBUG_HEAP
00053 #include <sys/nutdebug.h>
00054 #endif
00055 
00056 /*
00057  * Set optional memory guard bytes.
00058  */
00059 #ifdef NUTMEM_GUARD
00060 #ifndef NUTMEM_GUARD_PATTERN
00061 #define NUTMEM_GUARD_PATTERN   ((int)(0xDEADBEEF))
00062 #endif
00063 #define NUTMEM_GUARD_BYTES     sizeof(NUTMEM_GUARD_PATTERN)
00064 #else /* NUTMEM_GUARD */
00065 #define NUTMEM_GUARD_BYTES     0
00066 #endif /* NUTMEM_GUARD */
00067 
00069 #define NUT_HEAP_OVERHEAD   (sizeof(HEAPNODE) - sizeof(HEAPNODE *))
00070 
00072 #ifndef NUTMEM_HEAPNODE_MIN
00073 #define NUTMEM_HEAPNODE_MIN (sizeof(HEAPNODE) + (2 * NUTMEM_GUARD_BYTES))
00074 #endif
00075 
00079 HEAPNODE *heapFreeList;
00080 
00081 #ifdef NUTMEM_SPLIT_FAST
00082 
00085 HEAPNODE *heapFastMemFreeList;
00086 #endif
00087 
00088 #ifdef NUTDEBUG_HEAP
00089 
00092 static HEAPNODE *heapAllocList;
00093 #endif
00094 
00095 /*
00096  * Prepare the user region.
00097  *
00098  * Returns a pointer to the memory block's user region, after optionally
00099  * having added guard patterns at both ends.
00100  */
00101 static INLINE void *PrepareUserArea(HEAPNODE * node)
00102 {
00103     int *tp = (int *) (uintptr_t) &node->hn_next;
00104 #ifdef NUTMEM_GUARD
00105     size_t off = (node->hn_size - NUT_HEAP_OVERHEAD) / sizeof(int) - 2;
00106 
00107     *tp++ = NUTMEM_GUARD_PATTERN;
00108     *(tp + off) = NUTMEM_GUARD_PATTERN;
00109 #endif
00110     return tp;
00111 }
00112 
00113 /*
00114  * Validate the user region.
00115  *
00116  * If we have guarded user regions, then this routine will do a sanity
00117  * check. If the guards had been overridden, then -1 is returned.
00118  * However, if running in debug mode, then NUTPANIC is called instead.
00119  *
00120  * If the guards are still OK or if guard protection is not available,
00121  * then zero is returned.
00122  */
00123 #ifdef NUTDEBUG_HEAP
00124 static INLINE int DebugValidateUserArea(HEAPNODE * node, CONST char *file, int line)
00125 #else
00126 static INLINE int ValidateUserArea(HEAPNODE * node)
00127 #endif
00128 {
00129 #ifdef NUTMEM_GUARD
00130     size_t off = (node->hn_size - NUT_HEAP_OVERHEAD) / sizeof(int) - 1;
00131     int *tp = (int *) (uintptr_t) &node->hn_next;
00132 
00133 #ifdef NUTDEBUG_HEAP
00134     if (*tp != NUTMEM_GUARD_PATTERN) {
00135         NUTPANIC("%s:%d: Bad memory block at %p\n", file, line, tp + 1);
00136         return -1;
00137     }
00138     if (*(tp + off) != NUTMEM_GUARD_PATTERN) {
00139         NUTPANIC("%s:%d: Bad memory block at %p with %u bytes allocated in %s:%d\n", file, line, tp + 1, node->ht_size, node->ht_file, node->ht_line);
00140         return -1;
00141     }
00142 #else
00143     if (*tp != NUTMEM_GUARD_PATTERN || *(tp + off) != NUTMEM_GUARD_PATTERN) {
00144         return -1;
00145     }
00146 #endif
00147 #endif
00148     return 0;
00149 }
00150 
00151 #ifdef NUTDEBUG_HEAP
00152 /*
00153  * Remove a node from the allocation list.
00154  */
00155 static void DebugUnalloc(HEAPNODE * entry, CONST char *file, int line)
00156 {
00157     HEAPNODE *ht = heapAllocList;
00158     HEAPNODE **htp = &heapAllocList;
00159 
00160     while (ht && ht != entry) {
00161         htp = &ht->ht_next;
00162         ht = ht->ht_next;
00163     }
00164     if (ht) {
00165         *htp = entry->ht_next;
00166     } else {
00167         NUTPANIC("%s:%d: Memory block at %p never alloced\n", file, line, entry);
00168     }
00169 }
00170 #endif
00171 
00198 #ifdef NUTDEBUG_HEAP
00199 void *NutHeapDebugRootAlloc(HEAPNODE ** root, size_t size, CONST char *file, int line)
00200 #else
00201 void *NutHeapRootAlloc(HEAPNODE ** root, size_t size)
00202 #endif
00203 {
00204     HEAPNODE *node;
00205     HEAPNODE **npp;
00206     HEAPNODE *fit = NULL;
00207     HEAPNODE **fpp = NULL;
00208     size_t need;
00209 
00210     /* Determine the minimum size. Add optional guard and alignment bytes.
00211        ** Make sure that a HEAPNODE structure fits. */
00212     need = size + NUT_HEAP_OVERHEAD + 2 * NUTMEM_GUARD_BYTES;
00213     if (need < sizeof(HEAPNODE)) {
00214         need = sizeof(HEAPNODE);
00215     }
00216     need = NUTMEM_TOP_ALIGN(need);
00217 
00218     /*
00219      * Walk through the linked list of free nodes and find the best fit.
00220      */
00221     node = *root;
00222     npp = root;
00223     while (node) {
00224         /* Found a note that fits? */
00225         if (node->hn_size >= need) {
00226             /* Is it the first one we found or was the previous 
00227                ** one larger? */
00228             if (fit == NULL || fit->hn_size > node->hn_size) {
00229                 /* This is the best fit so far. */
00230                 fit = node;
00231                 fpp = npp;
00232                 /* Is this an exact match? */
00233                 if (node->hn_size == need) {
00234                     /* Exact match found. Stop searching. */
00235                     break;
00236                 }
00237             }
00238         }
00239         npp = &node->hn_next;
00240         node = node->hn_next;
00241     }
00242 
00243     if (fit) {
00244         /* Is remaining space of the node we found large enough for 
00245            another node? Honor the specified threshold. */
00246         if (fit->hn_size - need >= NUTMEM_HEAPNODE_MIN) {
00247             /* Split the node. */
00248             node = (HEAPNODE *) ((uintptr_t) fit + need);
00249             node->hn_size = fit->hn_size - need;
00250             node->hn_next = fit->hn_next;
00251             fit->hn_size = need;
00252             *fpp = node;
00253         } else {
00254             /* Use the full node. */
00255             *fpp = fit->hn_next;
00256         }
00257 #ifdef NUTDEBUG_HEAP
00258         /* Add debug information. */
00259         fit->ht_size = size;
00260         fit->ht_file = file;
00261         fit->ht_line = line;
00262         /* Add to allocation list. */
00263         fit->ht_next = heapAllocList;
00264         heapAllocList = fit;
00265 #endif
00266         fit = (HEAPNODE *) PrepareUserArea(fit);
00267     }
00268     return fit;
00269 }
00270 
00285 #ifdef NUTDEBUG_HEAP
00286 void *NutHeapDebugRootAllocClear(HEAPNODE ** root, size_t size, CONST char *file, int line)
00287 {
00288     void *ptr;
00289 
00290     if ((ptr = NutHeapDebugRootAlloc(root, size, file, line)) != 0)
00291         memset(ptr, 0, size);
00292 
00293     return ptr;
00294 }
00295 #else
00296 void *NutHeapRootAllocClear(HEAPNODE ** root, size_t size)
00297 {
00298     void *ptr;
00299 
00300     if ((ptr = NutHeapRootAlloc(root, size)) != 0)
00301         memset(ptr, 0, size);
00302 
00303     return ptr;
00304 }
00305 #endif
00306 
00325 #ifdef NUTDEBUG_HEAP
00326 int NutHeapDebugRootFree(HEAPNODE ** root, void *block, CONST char *file, int line)
00327 #else
00328 int NutHeapRootFree(HEAPNODE ** root, void *block)
00329 #endif
00330 {
00331     HEAPNODE *node;
00332     HEAPNODE **npp;
00333     HEAPNODE *fnode;
00334 
00335     /* IMHO, this should be removed. It adds additional code, which is
00336        useless for most applications. Those, who are interested, can
00337        easily do their own check. C99 declares this as a NUL op. */
00338     if (block == NULL) {
00339         return -3;
00340     }
00341 
00342     /* Revive our node pointer. */
00343     fnode = (HEAPNODE *) ((uintptr_t) block - (NUT_HEAP_OVERHEAD + NUTMEM_GUARD_BYTES));
00344 
00345 
00346 #ifdef NUTDEBUG_HEAP
00347     /* Sanity check. */
00348     if (DebugValidateUserArea(fnode, file, line)) {
00349         return -2;
00350     }
00351     /* Remove from allocation list. */
00352     if (file) {
00353         DebugUnalloc(fnode, file, line);
00354     }
00355 #else
00356     if (ValidateUserArea(fnode)) {
00357         return -2;
00358     }
00359 #endif
00360 
00361     /*
00362      * Walk through the linked list of free nodes and try
00363      * to link us in.
00364      */
00365     node = *root;
00366     npp = root;
00367     while (node) {
00368         /* If this node is directly in front of ours, merge it. */
00369         if ((uintptr_t) node + node->hn_size == (uintptr_t) fnode) {
00370             node->hn_size += fnode->hn_size;
00371 
00372             /*
00373              * If another free node is directly following us, merge it.
00374              */
00375             if (((uintptr_t) node + node->hn_size) == (uintptr_t) node->hn_next) {
00376                 node->hn_size += node->hn_next->hn_size;
00377                 node->hn_next = node->hn_next->hn_next;
00378             }
00379             break;
00380         }
00381 
00382         /*
00383          * If we walked past our address, link us to the list.
00384          */
00385         if ((uintptr_t) node > (uintptr_t) fnode) {
00386             *npp = fnode;
00387 
00388             /*
00389              * If a free node is following us, merge it.
00390              */
00391             if (((uintptr_t) fnode + fnode->hn_size) == (uintptr_t) node) {
00392                 fnode->hn_size += node->hn_size;
00393                 fnode->hn_next = node->hn_next;
00394             } else
00395                 fnode->hn_next = node;
00396             break;
00397         }
00398 
00399         /* If we are within a free node, somebody may have tried to free 
00400            a block twice. The panic below does not make much sense, because
00401            if we have NUTDEBUG_HEAP then we will also have NUTMEM_GUARD.
00402            In that case the guard will have been overridden by the link 
00403            pointer, which is detected by the ValidateUserArea() above. */
00404         if (((uintptr_t) node + node->hn_size) > (uintptr_t) fnode) {
00405 #ifdef NUTDEBUG_HEAP
00406             NUTPANIC("Trying to release free heap memory at %p in %s:%d\n", file, line);
00407 #endif
00408             return -1;
00409         }
00410 
00411         npp = &node->hn_next;
00412         node = node->hn_next;
00413     }
00414 
00415     /*
00416      * If no link was found, put us at the end of the list
00417      */
00418     if (node == NULL) {
00419         fnode->hn_next = node;
00420         *npp = fnode;
00421     }
00422     return 0;
00423 }
00424 
00435 void NutHeapRootAdd(HEAPNODE ** root, void *addr, size_t size)
00436 {
00437     HEAPNODE *node = (HEAPNODE *) NUTMEM_TOP_ALIGN((uintptr_t) addr);
00438 
00439     node->hn_size = NUTMEM_BOTTOM_ALIGN(size - ((uintptr_t) node - (uintptr_t) addr));
00440 #ifdef NUTDEBUG_HEAP
00441     NutHeapDebugRootFree(root, PrepareUserArea(node), NULL, 0);
00442 #else
00443     NutHeapRootFree(root, PrepareUserArea(node));
00444 #endif
00445 }
00446 
00452 size_t NutHeapRootAvailable(HEAPNODE ** root)
00453 {
00454     size_t rc = 0;
00455     HEAPNODE *node;
00456 
00457     /* Collect all free nodes. */
00458     for (node = *root; node; node = node->hn_next) {
00459         /* Reduce the size of each node by the required overhead. */
00460         rc += node->hn_size - NUT_HEAP_OVERHEAD;
00461     }
00462     return rc;
00463 }
00464 
00470 size_t NutHeapRootRegionAvailable(HEAPNODE ** root)
00471 {
00472     size_t rc = 0;
00473     HEAPNODE *node;
00474 
00475     /* Collect all free nodes. */
00476     for (node = *root; node; node = node->hn_next) {
00477         if (rc < node->hn_size) {
00478             rc = node->hn_size;
00479         }
00480     }
00481     /* Reduce the size by the required overhead. */
00482     return rc - NUT_HEAP_OVERHEAD;
00483 }
00484 
00498 #ifdef NUTDEBUG_HEAP
00499 void *NutHeapDebugRootRealloc(HEAPNODE ** root, void *block, size_t size, CONST char *file, int line)
00500 #else
00501 void *NutHeapRootRealloc(HEAPNODE ** root, void *block, size_t size)
00502 #endif
00503 {
00504     HEAPNODE *node;
00505     HEAPNODE **npp;
00506     HEAPNODE *fnode;
00507     void *newmem;
00508 
00509 #ifdef NUTDEBUG_HEAP
00510     /* With NULL pointer the call is equivalent to alloc. */
00511     if (block == NULL) {
00512         return NutHeapDebugRootAlloc(root, size, file, line);
00513     }
00514     /* With zero size the call is equivalent to free. */
00515     if (size == 0) {
00516         if (NutHeapDebugRootFree(root, block, file, line)) {
00517             return NULL;
00518         }
00519         return block;
00520     }
00521 
00522     /* Revive our node pointer. */
00523     fnode = (HEAPNODE *) ((uintptr_t) block - (NUT_HEAP_OVERHEAD + NUTMEM_GUARD_BYTES));
00524 
00525     /* Sanity check. */
00526     if (DebugValidateUserArea(fnode, file, line)) {
00527         return NULL;
00528     }
00529 #else
00530     if (block == NULL) {
00531         return NutHeapRootAlloc(root, size);
00532     }
00533     if (size == 0) {
00534         if (NutHeapRootFree(root, block)) {
00535             return NULL;
00536         }
00537         return block;
00538     }
00539     fnode = (HEAPNODE *) ((uintptr_t) block - (NUT_HEAP_OVERHEAD + NUTMEM_GUARD_BYTES));
00540     if (ValidateUserArea(fnode)) {
00541         return NULL;
00542     }
00543 #endif
00544 
00545     /* Determine the minimum size. Add optional guard and alignment bytes.
00546        Make sure that a HEAPNODE structure fits. */
00547     size += NUT_HEAP_OVERHEAD + NUTMEM_GUARD_BYTES * 2;
00548     if (size < sizeof(HEAPNODE)) {
00549         size = sizeof(HEAPNODE);
00550     }
00551     size = NUTMEM_TOP_ALIGN(size);
00552 
00553     /*
00554      * Expansion.
00555      */
00556     if (size > fnode->hn_size) {
00557         size_t size_miss = size - fnode->hn_size;
00558 
00559         /* Find the free node following next. */
00560         node = *root;
00561         npp = root;
00562         while (node && node < fnode) {
00563             npp = &node->hn_next;
00564             node = node->hn_next;
00565         }
00566 
00567         /* If we found a node and if this node is large enough and 
00568            if it directly follows without a gap, then use it. */
00569         if (node && node->hn_size >= size_miss &&       /* */
00570             (uintptr_t) fnode + fnode->hn_size == (uintptr_t) node) {
00571             /* Check if the following node is large enough to be split. */
00572             if (node->hn_size - size_miss >= NUTMEM_HEAPNODE_MIN) {
00573                 /* Adjust the allocated size. */
00574                 fnode->hn_size += size_miss;
00575                 /* Insert the remaining part into the free list. */
00576                 *npp = (HEAPNODE *) ((uintptr_t) node + size_miss);
00577                 /* Due to possible overlapping it is important to set
00578                    the pointer first, then the size. */
00579                 (*npp)->hn_next = node->hn_next;
00580                 (*npp)->hn_size = node->hn_size - size_miss;
00581                 PrepareUserArea(fnode);
00582             } else {
00583                 /* Adjust the allocated size. */
00584                 fnode->hn_size += node->hn_size;
00585                 PrepareUserArea(fnode);
00586                 /* Remove the merged node from the free list. */
00587                 *npp = node->hn_next;
00588             }
00589             /* Return the original pointer. */
00590             return block;
00591         }
00592 
00593         /* Relocate if no sufficiently large block follows. */
00594 #ifdef NUTDEBUG_HEAP
00595         newmem = NutHeapDebugRootAlloc(root, size, file, line);
00596 #else
00597         newmem = NutHeapRootAlloc(root, size);
00598 #endif
00599         if (newmem) {
00600             memcpy(newmem, block, 
00601                 fnode->hn_size - NUT_HEAP_OVERHEAD - 2 * NUTMEM_GUARD_BYTES);
00602 #ifdef NUTDEBUG_HEAP
00603             NutHeapDebugRootFree(root, block, file, line);
00604 #else
00605             NutHeapRootFree(root, block);
00606 #endif
00607         }
00608         return newmem;
00609     }
00610 
00611     /*
00612      * Reduction.
00613      */
00614     if (size < fnode->hn_size - NUTMEM_HEAPNODE_MIN) {
00615         /* Release the remaining part to the free list. */
00616         node = (HEAPNODE *) ((uintptr_t) fnode + size);
00617         node->hn_size = fnode->hn_size - size;
00618 #ifdef NUTDEBUG_HEAP
00619         NutHeapDebugRootFree(root, PrepareUserArea(node), NULL, 0);
00620 #else
00621         NutHeapRootFree(root, PrepareUserArea(node));
00622 #endif
00623         /* Adjust the allocated size. */
00624         fnode->hn_size = size;
00625         PrepareUserArea(fnode);
00626     }
00627     return block;
00628 }
00629 
00638 int NutHeapCheck(void)
00639 {
00640 #ifdef NUTDEBUG_HEAP
00641     HEAPNODE *node;
00642 
00643     for (node = heapAllocList; node; node = node->ht_next) {
00644         if (DebugValidateUserArea(node, __FILE__, __LINE__)) {
00645             return -1;
00646         }
00647     }
00648 #endif
00649     return 0;
00650 }
00651 
00652 #include <stdio.h>
00653 
00657 void NutHeapDump(void * stream)
00658 {
00659     HEAPNODE *node;
00660 
00661 #ifdef NUTMEM_SPLIT_FAST
00662     for (node = heapFastMemFreeList; node; node = node->hn_next) {
00663         fprintf(stream, "%p(%d)\n", node, (int) node->hn_size);
00664     }
00665 #endif
00666 
00667     for (node = heapFreeList; node; node = node->hn_next) {
00668         fprintf(stream, "%p(%d)\n", node, (int) node->hn_size);
00669     }
00670 
00671 #ifdef NUTDEBUG_HEAP
00672     for (node = heapAllocList; node; node = node->ht_next) {
00673         fprintf(stream, "%p(%u) %s:%d\n", node, (int) node->ht_size, node->ht_file, node->ht_line);
00674     }
00675 #endif
00676 }
00677