mem.c

Go to the documentation of this file.
00001 /*##############################################################################
00002 
00003 nIP - nano IP stack
00004 
00005 File        : mem.c
00006 
00007 Description : NIP dynamic memory management unit
00008 
00009 Copyright notice:
00010 
00011 Copyright (C) 2007 -
00012 Andreas Dittrich, dittrich@informatik.hu-berlin.de
00013 Jon Kowal, kowal@informatik.hu-berlin.de
00014 
00015 This program is free software; you can redistribute it and/or
00016 modify it under the terms of the GNU General Public License
00017 as published by the Free Software Foundation; either version 2
00018 of the License, or (at your option) any later version.
00019 
00020 This program is distributed in the hope that it will be useful,
00021 but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00023 GNU General Public License for more details.
00024 
00025 You should have received a copy of the GNU General Public License
00026 along with this program; if not, write to the Free Software
00027 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00028 ##############################################################################*/
00029 
00030 #include "mem.h"
00031 #include "nip_error.h"
00032 #include "os_core.h"
00033 
00034 #if (NIP_MEM_ENABLE == 1)
00035 
00036 ///@todo debug-block.. remove when not needed
00037 nip_mem_block_t *debug_block;
00038 
00039 /** pool of dynamically allocatable memory */
00040 static uint8_t nip_mem_pool[ NIP_MEM_DYNAMIC_SIZE ];
00041 
00042 /** Initialize dynamic memory pool. All current data in the pool will be lost.
00043  */
00044 void nip_mem_init()
00045 {
00046         nip_mem_block_t *current = (nip_mem_block_t *)nip_mem_pool;
00047 
00048         current->id          = 1;
00049         current->res_length  = NIP_MEM_DYNAMIC_SIZE;
00050         current->min_length  = 0;
00051         current->used_length = 0;
00052 
00053         // initial memory block will be set to unused, that is, its memory
00054         // is available for allocation.
00055         current->flags       = NIP_MEM_FLGS_UNUSED;
00056 
00057 }
00058 
00059 /** free bytes from the memory block at the given address */
00060 void nip_mem_free_bytes_ex( nip_mem_block_t *block, nip_mem_size_t size )
00061 {
00062         /// \todo move all block-shrink-code here
00063 
00064 }
00065 
00066 /** find memory address of block */
00067 static nip_mem_block_t *nip_mem_locate_block( nip_mem_handle_t block_id )
00068 {
00069         nip_mem_block_t *block;
00070 
00071         // find block in pool
00072         for ( block = (nip_mem_block_t*) nip_mem_pool;
00073                   (uint8_t*)block < &nip_mem_pool[NIP_MEM_DYNAMIC_SIZE];
00074                   block = (nip_mem_block_t*)&((uint8_t*)block)[block->res_length])
00075         {
00076                 if ( block->id == block_id )
00077                 {
00078                         return block;
00079                 }
00080         }
00081 
00082         return NULL;
00083 }
00084 
00085 /** Free entire memory block.*/
00086 void nip_mem_free( nip_mem_handle_t block_id )
00087 {
00088         nip_mem_block_t *block;
00089 
00090         // find block in pool
00091         block = nip_mem_locate_block( block_id );
00092         if ( block != NULL )
00093         {
00094                 // found block; reset block flags
00095                 block->flags       = NIP_MEM_FLGS_UNUSED;
00096                 block->used_length = 0;
00097                 block->min_length  = 0;
00098                 ///\todo possibly defragment if previous or next block is also unused
00099                 nip_error = NIP_E_OK;
00100                 return;
00101         }
00102         nip_error = NIP_E_INVALID_BLOCK;
00103 }
00104 
00105 /** Make two blocks from one. First block will be resized to `'new size'' bytes
00106  * including header, the second block will receive remaining memory. The newly
00107  * created block will receive the given `'next_id''.
00108  * @warning: This function does _NOT_ do an sanity check. Make sure the given
00109  * parameters will work for the given block, before calling this function!!
00110  */
00111 static nip_mem_block_t *nip_mem_split( nip_mem_block_t *block, nip_mem_size_t new_size, nip_mem_handle_t next_id )
00112 {
00113         nip_mem_size_t next_size = block->res_length - new_size;
00114 
00115         // shrink block
00116         block->res_length = new_size;
00117 
00118         // create new unused block
00119         block = (nip_mem_block_t*)&(((uint8_t *)block)[block->res_length]);
00120         block->id          = next_id;
00121         block->res_length  = next_size;
00122         block->min_length  = 0;
00123         block->used_length = 0;
00124         block->flags = NIP_MEM_FLGS_UNUSED;
00125 
00126 #if NIP_MEM_DEBUG == 1
00127         if ( block->res_length == 0 )
00128         {
00129                 // DEBUG something went wrong. CHECK THIS!!!!!
00130                 return NIP_MEM_NULL;
00131         }
00132 #endif
00133         return block;
00134 }
00135 /** Search for unused memory in pool and collect at a specific position.
00136  *
00137  * The freed memory may be appended to an existing block (new_block_id ==
00138  * free_at_block_id) or a new block with the id new_block_id will be created.
00139  *
00140  * @todo There are many possible strategies to aquire needed memory. The current
00141  * approach is to take the required memory equally from all blocks. A better
00142  * strategy may be to take memory from blocks according to a which-block-would-
00143  * care-least algorithm.
00144  *
00145  * @param needed_size      number of bytes to look for
00146  * @param free_at_block_id block after which to position the freed memory
00147  * @param new_block_id     id of new block to create or the same value as
00148  *   free_at_block_id if freed memory is to be appended to the block specified
00149  *   in free_at_block_id.
00150  *
00151  * @return Pointer to modified memory block. On Error NULL will be returned
00152  * and an error code will be written to the global nip_error variable. Possible
00153  * errors are
00154  *  - NIP_E_OUT_OF_MEMORY if not enough memory could be freed, or
00155  *  - NIP_E_PARAM_FAILED if free_at_block_id==new_block_id but block was locked
00156  *    and can therefor not be extended.
00157  */
00158 nip_mem_block_t *nip_mem_defrag( nip_mem_size_t needed_size,
00159         nip_mem_handle_t free_at_block_id, nip_mem_handle_t new_block_id )
00160 {
00161         nip_mem_size_t     request_memory;    // memory needed to be freed
00162         nip_mem_size_t     free_available;    // amount of free available memory
00163         nip_mem_size_t     smallest_available;// smallest amount of free mem
00164         nip_mem_size_t     total_available;   // total free memory available
00165         nip_mem_size_t     freed_memory = 0;  // total memory freed so far
00166         nip_mem_size_t     unused_memory;// one block's reserved but unused memory
00167         nip_mem_size_t     mem_free = 0; // how much memory to free per block
00168         nip_mem_size_t     tmp_free;     // how much memory to free for a certain block
00169         nip_mem_size_t     mem_count;    // counter for mem move operation
00170         nip_mem_handle_t   block_count;
00171         nip_mem_block_t   *block;        // current block being worked with
00172         nip_mem_block_t   *first_block;  // first block to free memory at
00173         uint8_t           *insert_pos;   // position to insert memory at
00174         uint8_t            scan_pos;     /* 0x01: before block to add memory to,
00175                                             0x02: at block to add memory to
00176                                             0x03: after block to add memory to
00177                                             0x04: at end of memory */
00178         uint8_t            free_unused_block; // unused block is to be freed
00179         uint8_t            i = 0;
00180 
00181         request_memory = needed_size;
00182         // add size of block-header to request_memory if necesary
00183         if ( new_block_id != free_at_block_id )
00184                 request_memory += sizeof( nip_mem_block_t );
00185 
00186         // First run starts at beginning of memory pool.
00187         first_block = NULL;
00188 
00189         // position to free memory at yet unknown
00190         if ( free_at_block_id == NIP_MEM_NULL )
00191                 insert_pos = nip_mem_pool;
00192         else
00193                 insert_pos = NULL;
00194 
00195         do /* outer loop */
00196         {
00197                 scan_pos = 1;
00198                 // reset scanner position
00199                 if ( free_at_block_id == NIP_MEM_NULL )
00200                 {
00201                         scan_pos = 3;
00202                         block    = (nip_mem_block_t *)(nip_mem_pool + freed_memory);
00203                 }
00204                 if ( first_block == NULL || request_memory <= freed_memory )
00205                 {
00206                         // No start position has been set or enough memory has been freed.
00207                         // in the latter case we start at the beginning, as the scanner
00208                         // will just forward to the block where the freed memory has to be
00209                         // appended or a new block has to be formed.
00210                         block = (nip_mem_block_t *)nip_mem_pool;
00211                 }
00212                 else
00213                 {
00214                         block    = first_block;
00215                         if ( (uint8_t*)block >= insert_pos )
00216                                 scan_pos = 3;
00217                 }
00218 
00219                 // initialize values for memory analysis
00220                 block_count        = 0 ;
00221                 smallest_available = 0;
00222                 total_available    = 0;
00223                 free_available     = 0;
00224 
00225                 // reset start block for next run
00226                 first_block = NULL;
00227 
00228                 // start scanner
00229                 do /* inner loop (scanner) */
00230                 {
00231 
00232 #if NIP_MEM_DEBUG == 1
00233                         if ( (uint8_t *)block > &nip_mem_pool[ NIP_MEM_DYNAMIC_SIZE ]
00234                           || block == NULL
00235                           || block->res_length < 8 )
00236                         {
00237                                 // DEBUG: sanity check of block pointer
00238                                 return NULL;
00239                         }
00240 #endif
00241 
00242                         // reset variables
00243                         free_unused_block = 0;
00244 
00245                         // check if block is locked
00246                         if ( NIP_MEM_BLOCK_LOCKED( block ) )
00247                         {
00248                                 //=== block is locked ===//
00249 
00250                                 switch ( scan_pos )
00251                                 {
00252                                         case 1: // scanner is positioned before the block to add mem to.
00253                                                 // Locked block restarts scanner at that position because
00254                                                 // previously found memory cannot be moved to the requested
00255                                                 // position.
00256                                                 first_block = NULL;
00257                                                 total_available    = 0;
00258                                                 smallest_available = 0;
00259                                                 free_available     = 0;
00260                                                 break;
00261 
00262                                         case 3: // scanner is positioned after the block to add mem to.
00263                                                 // Locked block stops scanner at current position because any
00264                                                 // free memory beyond that block cannot be moved to the
00265                                                 // requested position.
00266                                                 scan_pos = 4;
00267                                                 break;
00268 
00269                                         default:
00270                                                 break;
00271                                 }
00272 
00273                                 // The block to add memory to must not be locked
00274                                 if ( block->id == free_at_block_id )
00275                                 {
00276                                         nip_error = NIP_E_PARAM_FAILED; // cancel function.
00277                                         return NULL;
00278                                 }
00279 
00280                         } else
00281                         {
00282                                 //=== block is not locked ===//
00283 
00284                                 // The unused_memory variable will only be used to signal
00285                                 // unused memory of blocks that are in use.
00286                                 unused_memory = 0;
00287                                 tmp_free = 0;
00288 
00289                                 // unused blocks may be freed entirely, if it needs to.
00290                                 if ( !NIP_MEM_BLOCK_USED( block )
00291                                   && request_memory > freed_memory
00292                                   && block->id != new_block_id
00293 //                                && (block->res_length < (request_memory-freed_memory+sizeof(nip_mem_block_t)))
00294                                         )
00295                                 {
00296 
00297 /*                                      // If the free_at_block_id-block is unused, it can be used
00298                                         // as new block. It will therefor be given the new_block_id
00299                                         // and its size will be deducted from the requested memory.
00300                                         if ( block->id == free_at_block_id )
00301                                         {
00302                                                 block->id = new_block_id;
00303                                                 free_at_block_id = new_block_id;
00304                                                 if ( request_memory > block->res_length - sizeof( nip_mem_block_t ) )
00305                                                         request_memory -= block->res_length;
00306                                                 else
00307                                                         request_memory = 0;
00308                                         }
00309                                         // If the current block is unused and the insert position for
00310                                         // freed memory has been determined already, the entire block
00311                                         // will be freed and moved to that position.
00312                                         else*/ if ( i==1  )
00313                                         {
00314                                                 tmp_free = request_memory - freed_memory;
00315                                                 /** @todo Free Blocks NEED NOT be splitted. If only parts of a
00316                                                  * block need to be freed, that should be done  using the mechanism
00317                                                  * that's being used for any other non-free block with unused
00318                                                  * memory (see further down this file). For further discussion
00319                                                  * see Bug #106. */
00320 
00321                                                 /** The following line is a workaround to Bug #106 */
00322                                                 if ( tmp_free < sizeof( nip_mem_block_t) )
00323                                                         tmp_free = sizeof( nip_mem_block_t );
00324 
00325                                                 // block larger than needed amount of memory?
00326                                                 if ( block->res_length > (tmp_free + sizeof( nip_mem_block_t )) )
00327                                                 {
00328 
00329                                                         // Split block, so only the needed amount of memory will
00330                                                         // be moved. The remaining memory will form a new block,
00331                                                         // given the ID of the current block, which will eventually
00332                                                         // be (re)moved.
00333                                                         nip_mem_split( block, tmp_free, block->id );
00334 
00335                                                 }
00336                                                 else
00337                                                         tmp_free = block->res_length;
00338 
00339                                                 free_unused_block = 1;
00340                                         }
00341                                         // If the current block is unused but no insert position for
00342                                         // freed memory has yet been determined, add the block's size
00343                                         // to the free_available memory counter for later use.
00344                                         else
00345                                         {
00346                                                 free_available += block->res_length;
00347                                         }
00348 
00349                                         // if the previous block has been blocked use this one as
00350                                         // starting point.
00351                                         /// @todo  Should this line be moved down to apply to all
00352                                         /// unused blocks? I would think so! (jon)
00353                                         if ( first_block == NULL )
00354                                                 first_block = block;
00355 
00356                                 } else
00357                                 if ( block->id != new_block_id )
00358                                 {
00359                                 /*DEBUG*/if (!NIP_MEM_BLOCK_USED( block ))
00360                                 /*DEBUG*/free_unused_block = 0;
00361                                         // check how much memory is unused in this block. Mem_free bytes
00362                                         // will be taken from that block. It's unused_memory will therefor
00363                                         // be reduced later.
00364                                         unused_memory = block->res_length - sizeof( nip_mem_block_t );
00365                                         if ( block->min_length < block->used_length)
00366                                                 unused_memory -= block->used_length;
00367                                         else
00368                                                 unused_memory -= block->min_length;
00369 
00370                                         // make sure we don't free more memory than is available
00371                                         if ( mem_free > unused_memory )
00372                                                 tmp_free = unused_memory;
00373                                         else
00374                                                 tmp_free = mem_free;
00375                                 }
00376                                 else
00377                                 {
00378                                         // don't free memory from the block we're collecting memory for
00379 //                                      unused_memory = 0;
00380                                 }
00381 
00382                                 // Free memory from block. Tmp_free will be 0 for the first run
00383                                 // of the scanner, so no memory will be freed during the first
00384                                 // run, as it's just used for memory analysis. (see below)
00385                                 /// @todo check if the following commented unused_memory condition
00386                                 /// makes any sense. It's been there and probably been cause of a
00387                                 /// bug but I'm not sure why I ever put it there, so there may be
00388                                 /// some sense to it.
00389                                 if ( tmp_free > 0 /*&& unused_memory > 0*/ )
00390                                 {
00391                                         block->res_length -= tmp_free;
00392                                         freed_memory      += tmp_free;
00393                                         if ( unused_memory > 0 )
00394                                                 unused_memory     -= tmp_free;
00395                                         switch ( scan_pos )
00396                                         {
00397                                                 case 1:
00398                                                         // Move-left following blocks by tmp_free-Bytes
00399                                                         mem_count = tmp_free + block->res_length;
00400 
00401                                                         for (
00402                                                                 ;
00403                                                                 &((uint8_t*)block)[mem_count] < insert_pos;
00404                                                                 mem_count++ )
00405                                                         {
00406                                                                 ((uint8_t*)block)[mem_count-tmp_free] = ((uint8_t*)block)[mem_count];
00407                                                         }
00408 
00409                                                         break;
00410                                                 case 3:
00411                                                         // Move-right current and previous blocks by tmp_free-Bytes
00412                                                         mem_count = (uint8_t*)block - insert_pos + block->res_length;
00413                                                         while ( mem_count > 0 )
00414                                                         {
00415                                                                 mem_count--;
00416                                                                 ((uint8_t*)insert_pos)[mem_count+tmp_free] = ((uint8_t*)insert_pos)[mem_count];
00417                                                         }
00418 
00419                                                         // Advance the block pointer by the amount of freed memory.
00420                                                         block = ( nip_mem_block_t *) ((uint8_t*) block + tmp_free);
00421                                                         break;
00422                                         }
00423                                 }
00424 
00425                                 // Analyse how much this block may still be shrinked.
00426                                 total_available += unused_memory;
00427 
00428                                 if ( unused_memory > 0 )
00429                                 {
00430                                         // if the previous block was locked, mark this as next block to
00431                                         // possibly free memory from.
00432                                         if ( first_block == NULL )
00433                                                 first_block = block;
00434 
00435                                         block_count++;
00436 
00437                                         if ( unused_memory < smallest_available || smallest_available == 0)
00438                                                 smallest_available = unused_memory;
00439 
00440                                 }
00441                         }
00442 
00443                         //=== the following will be done for all blocks, locked or not ===//
00444 
00445                         // is this the block to free memory at? Set insert position for
00446                         // freed memory.
00447                         if ( block->id == free_at_block_id )
00448                         {
00449                                 insert_pos = &(( uint8_t* )block)[block->res_length];
00450                         }
00451 
00452                         // Check if all requested memory has been collected. Continue to run
00453                         // up to free_at_block_id-Block because the block pointer will be
00454                         // needed later.
00455                         if ( request_memory <= freed_memory && block->id == free_at_block_id )
00456                                 scan_pos = 2;
00457                         else
00458                         if ( free_unused_block )
00459                         {
00460                                 // current block has been selected by free_unused_block already
00461                                 // TODO check if all is done
00462                         }
00463                         else
00464                         {
00465                                 // move to next block
00466                                 block = (nip_mem_block_t*)(((uint8_t*)block)+ block->res_length);
00467                                 if ( block == ( nip_mem_block_t *) insert_pos )
00468                                 {
00469                                         // skip freed memory
00470                                         block = ( nip_mem_block_t* )( insert_pos + freed_memory );
00471                                         scan_pos = 3;
00472                                 }
00473 
00474                                 #if NIP_MEM_DEBUG == 1
00475                                         if ( (uint8_t *)block > &nip_mem_pool[ NIP_MEM_DYNAMIC_SIZE ]
00476                                         || block == NULL
00477                                         || ((uint8_t *)block < &nip_mem_pool[ NIP_MEM_DYNAMIC_SIZE ] && block->res_length < 8) )
00478                                         {
00479                                                 // DEBUG: sanity check of block pointer
00480                                                 return NULL;
00481                                         }
00482                                 #endif
00483                         }
00484 
00485                         // End of memory pool reached?
00486                         if ( (uint8_t*)block == &nip_mem_pool[ NIP_MEM_DYNAMIC_SIZE ] )
00487                                 scan_pos = 4;
00488 
00489                         // reduce mem_free, if its not all needed
00490                         if ( request_memory < freed_memory + mem_free )
00491                                 mem_free = request_memory - freed_memory;
00492 
00493                         // if free memory is available skip the usage of non-free but unused memory
00494                         if ( free_available > 0 || request_memory <= freed_memory )
00495                                 mem_free = 0;
00496 
00497                 } while ( scan_pos != 2 && scan_pos != 4 ); /* inner loop (scanner) */
00498 
00499                 // Outer loop control:
00500                 // * After first run of scanner we know how much memory can be be freed
00501                 // * Subsequent runs of scanner free parts of available memory
00502                 // * Quit if first run did not end up with enough free memory or
00503                 // * Quit if enough memory has been freed. Create new block with
00504                 //   new_block_id if necessary.
00505                 // * Continue if more memory can and must be freed. Determine how much
00506                 //   memory should be freed per block ( mem_free variable )
00507 
00508                 if ( request_memory > total_available + free_available + freed_memory )
00509                 {
00510                         nip_error = NIP_E_OUT_OF_MEMORY;
00511                         return NULL;
00512                 }
00513 
00514                 // enough memory has been freed and the scanner is at correct position?
00515                 if ( request_memory <= freed_memory && scan_pos == 2 )
00516                 {
00517                         // create new block?
00518                         if ( new_block_id != free_at_block_id )
00519                         {
00520                                 block = ( nip_mem_block_t *) insert_pos;
00521                                 block->id = new_block_id;
00522                                 block->res_length = freed_memory;
00523                                 block->flags = NIP_MEM_FLGS_UNUSED;
00524                                 // default setting for minimum length is reserved length.
00525                                 block->min_length = freed_memory;
00526                                 block->used_length = 0;
00527                         }
00528                         else
00529                         {
00530                                 block->res_length += freed_memory;
00531                         }
00532 #if NIP_MEM_DEBUG == 1
00533                         /* DEBUG */ debug_block = (nip_mem_block_t*)&((uint8_t*)block)[block->res_length];
00534 #endif
00535                         nip_error = NIP_E_OK;
00536                         return block;
00537                 }
00538                 // more memory needs to be freed?
00539                 else if ( block_count > 0 && request_memory > freed_memory)
00540                 {
00541                         mem_free = ( request_memory - freed_memory ) / block_count;
00542 
00543                         // to ensure fair distribution of free memory, take same amounts of
00544                         // memory from all blocks.
00545                         if ( mem_free > smallest_available )
00546                                 mem_free = smallest_available;
00547 
00548                         if ( request_memory - freed_memory == 1 )
00549                                 mem_free = 1;
00550                 }
00551 
00552                 i = 1;
00553         } while (1); /* outer loop */
00554 
00555 }
00556 
00557 /** Set new minimum block length and resize block if necessary.
00558  * @return NIP_E_OK, NIP_E_INVALID_BLOCK, or NIP_E_OUT_OF_MEMORY. */
00559 nip_error_t nip_mem_set_min_length( nip_mem_handle_t block_id, nip_mem_size_t min_length )
00560 {
00561         nip_mem_block_t *block;
00562         nip_mem_size_t  block_user_size;
00563 
00564         // find block in pool
00565         block = nip_mem_locate_block( block_id );
00566         if ( block != NULL )
00567         {
00568                 // if min_length has to be reduced, well... reduce it. :)
00569                 // if min_length has to be increased, but does not exceed the reserved
00570                 // memory.. no problem.
00571 
00572                 // if block has to be increased, try to find memory for it.
00573                 block_user_size = block->res_length - sizeof( nip_mem_block_t );
00574                 if ( min_length > block_user_size )
00575                 {
00576                         block = nip_mem_defrag( min_length - block_user_size, block->id, block->id );
00577                         if ( block == NULL )
00578                                 return nip_error;
00579                 }
00580                 block->min_length = min_length;
00581         } else
00582                 return NIP_E_INVALID_BLOCK;
00583 
00584         return NIP_E_OK;
00585 
00586 }
00587 
00588 /** Allocate dynamic memory block. The amount of allocated memory may change
00589  * during a block's lifetime, but will always be at least min_length.
00590  *
00591  * @param size       number of bytes to initially allocate, if available.
00592  * @param min_length minimum number of bytes to always keep reserved for this
00593  *                   block
00594  * @param flags      memory block flags. NIP_MEM_FLG_USED will always be set.
00595  * @param res_length if not NULL, will be filled with number of bytes that have
00596  *                   actually been allocated for usage.
00597  *
00598  * @return memory block handle or NIP_MEM_NULL if min_length bytes of memory are
00599  *         not available for allocation.
00600  */
00601 
00602 nip_mem_handle_t nip_mem_alloc( nip_mem_size_t size, nip_mem_size_t min_length, nip_mem_flags_t flags, nip_mem_size_t *res_length)
00603 {
00604         nip_mem_block_t     *block;
00605         nip_mem_block_t     *next_block;
00606         nip_mem_block_t     *new_block;
00607         nip_mem_size_t       new_size;
00608         nip_mem_size_t       block_user_size;
00609         uint8_t              i;
00610         nip_mem_handle_t     new_block_id;
00611         nip_mem_handle_t     at_block_id;
00612         nip_error_t          result = NIP_E_OUT_OF_MEMORY;
00613 
00614 // TODO check lock or lock memory management
00615 
00616         // find free memory
00617         // perform a maximum of 4 outer loops:
00618         // 0. look for unused blocks of wanted length
00619         // 1. look for unused blocks6 of minimum length
00620         // 2. look for any unused blocks
00621         // 3. look for any spot with an unused block-id
00622         for ( i = 0; i < 4; i++ ) /* outer loop */
00623         {
00624                 at_block_id = 0;
00625 
00626                 for ( block = ( nip_mem_block_t *) nip_mem_pool; block != NULL; block = next_block)
00627                 {
00628                         if ( block->id > NIP_MEM_NULL )
00629                                 at_block_id  = block->id;
00630 
00631 #if NIP_MEM_DEBUG == 1
00632                         if ( block->res_length == 0 )
00633                         {
00634                                 // DEBUG something went wrong. CHECK THIS!!!!!
00635                                 return NIP_MEM_NULL;
00636                         }
00637 #endif
00638                         calc_user_size:
00639                         block_user_size = block->res_length - sizeof( nip_mem_block_t );
00640 
00641                         // determine next memory block
00642                         next_block = (nip_mem_block_t*)&(((uint8_t*)block)[block->res_length]);
00643                         if ( (uint8_t*)next_block == &nip_mem_pool[ NIP_MEM_DYNAMIC_SIZE ] )
00644                                 next_block = NULL;
00645 #if NIP_MEM_DEBUG == 1
00646                         if ( next_block != NULL && ( next_block->res_length == 0
00647                            || &((uint8_t*)next_block)[next_block->res_length]>&nip_mem_pool[ NIP_MEM_DYNAMIC_SIZE ]))
00648                         {
00649                                 // DEBUG something went wrong. CHECK THIS!!!!!
00650                                 return NIP_MEM_NULL;
00651                         }
00652 #endif
00653                         // merge neighboring empty blocks
00654                         if ( !NIP_MEM_BLOCK_USED( block ) && next_block != NULL && !NIP_MEM_BLOCK_USED( next_block ))
00655                         {
00656                                 block->res_length += next_block->res_length;
00657                                 goto calc_user_size;
00658                         }
00659 
00660                         if ( i==0 && !NIP_MEM_BLOCK_USED( block ) && block_user_size >= size )
00661                         {
00662                                 // Found free block. Check to reduce block size. Reducing block size
00663                                 // requires a second block to be created which will hold the remaining
00664                                 // memory. First of all check if there is space for a new block header.
00665                                 if ( block_user_size >= size + sizeof( nip_mem_block_t ) )
00666                                 {
00667 
00668                                         // Check if there is a free id available for new block.
00669                                         if (( next_block != NULL && (next_block->id > block->id+1 ))
00670                                         ||( next_block == NULL && block->id+1 < NIP_MEM_MAX_BLOCKS ))
00671                                         {
00672                                                 next_block = nip_mem_split( block, size + sizeof( nip_mem_block_t ), block->id+1 );
00673 #if NIP_MEM_DEBUG == 1
00674                                                 if ( block->res_length == 0 || next_block->res_length == 0
00675                                                 || &((uint8_t*)next_block)[next_block->res_length]>&nip_mem_pool[ NIP_MEM_DYNAMIC_SIZE ])
00676                                                 {
00677                                                         // DEBUG something went wrong. CHECK THIS!!!!!
00678                                                         return NIP_MEM_NULL;
00679                                                 }
00680 #endif
00681 
00682                                         }
00683                                         // else large block of memory than necessary will be reserved
00684                                         // because no free block-id is available to form a new unused
00685                                         // block.
00686                                         /** \todo Free memory could be moved to a position with available
00687                                          * block-id to form a new unused block. */
00688                                 }
00689 
00690                                 // Memory has been allocated -> positive result.
00691                                 result = NIP_E_OK;
00692 
00693                                 i = 10; /* exit outer loop */
00694                                 break;
00695                         }
00696                         else
00697                         if (( i==1 && !NIP_MEM_BLOCK_USED( block ) && block->id > NIP_MEM_NULL && block_user_size >= min_length && !NIP_MEM_BLOCK_LOCKED( block ) )
00698                          || ( i==2 && !NIP_MEM_BLOCK_USED( block ) && block->id > NIP_MEM_NULL && !NIP_MEM_BLOCK_LOCKED( block ))
00699                          || ( i==3 && next_block != NULL && at_block_id+1 < next_block->id)
00700                          || ( i==3 && next_block == NULL && at_block_id+1 < NIP_MEM_MAX_BLOCKS ))
00701                         {
00702                                 if ( i>2 && block == ( nip_mem_block_t *) nip_mem_pool && block->id > 1)
00703                                 {
00704                                         // create new first block in pool
00705                                         new_size     = size;
00706                                         new_block_id = 1;
00707                                         at_block_id  = 0;
00708                                 }
00709                                 if ( i>2 )
00710                                 {
00711                                         // create new block after current block
00712                                         new_size     = size;
00713                                         new_block_id = block->id+1;
00714                                 }
00715                                 else
00716                                 {
00717                                         // fill up current, unused block
00718                                         new_size     = size - block_user_size;
00719                                         new_block_id = block->id;
00720                                 }
00721 
00722                                 new_block = nip_mem_defrag( new_size, block->id, new_block_id);
00723 
00724                                 // if not enough memory for wanted size, try minimum size
00725                                 if ( new_block == NULL && nip_error == NIP_E_OUT_OF_MEMORY )
00726                                 {
00727                                         new_size  -= size - min_length;
00728                                         new_block  = nip_mem_defrag( new_size, block->id, new_block_id);
00729                                 }
00730 
00731                                 if ( new_block != NULL )
00732                                 {
00733                                         block = new_block;
00734                                         // init block
00735                                         result = NIP_E_OK;
00736                                         i = 10; /* exit outer loop */
00737                                         break;
00738                                 }
00739                         }
00740                 }
00741         }
00742 
00743         nip_error = result;
00744 
00745         if ( result == NIP_E_OK )
00746         {
00747                 block->min_length = min_length;
00748                 block->flags = flags | NIP_MEM_FLG_USED;
00749                 if ( res_length != NULL )
00750                         *res_length = block->res_length - sizeof( nip_mem_block_t );
00751                 return block->id;
00752         }
00753 
00754         // else
00755         return NIP_MEM_NULL;
00756 
00757 }
00758 
00759 /** Write data to memory block.
00760  *
00761  * The data will be appended after the last byte of data in the block. If the
00762  * block is too small to hold the data, attempts will be made to allocate the
00763  * needed memory. If that fails NIP_E_OUT_OF_MEMORY will be returned.
00764  *
00765  * @param block_id  id of block to write data to
00766  * @param buf       address of buffer to copy data from
00767  * @param size      number of bytes to copy from buffer
00768  *
00769  * If buf is NULL the block will still be increased by size bytes but initialized
00770  * with Zeros. That special case is usually being used to allocate memory to
00771  * be accessed directly, later. (see nip_mem_obtain_ptr() )
00772  *
00773  * @return NIP_E_OK or NIP_E_OUT_OF_MEMORY or NIP_E_INVALID_BLOCK
00774  */
00775 nip_error_t nip_mem_write( nip_mem_handle_t block_id, void *buf, nip_mem_size_t size )
00776 {
00777         nip_mem_block_t *block;
00778         nip_mem_size_t  unused_bytes;
00779 
00780         // find block in pool
00781         block = nip_mem_locate_block( block_id );
00782         if ( block != NULL )
00783         {
00784                 unused_bytes = block->res_length - block->used_length - sizeof( nip_mem_block_t );
00785                 // Make sure enough memory is available. If not, try to allocate.
00786                 if ( size > unused_bytes )
00787                 {
00788                         if ( (block = nip_mem_defrag( size - unused_bytes, block_id, block_id )) == NULL )
00789                         {
00790                                 return NIP_E_OUT_OF_MEMORY;
00791                         }
00792                 }
00793 
00794                 if ( buf != NULL )
00795                 {
00796                         // Copy data from buffer to block
00797                         nip_memcpy(
00798                                 &((uint8_t*)block)[ sizeof( nip_mem_block_t ) + block->used_length ],
00799                                 buf,
00800                                 size);
00801                 }
00802                 else
00803                 {
00804                         // no source buffer given -> initialize with 0
00805                         nip_memset(
00806                                 &((uint8_t*)block)[ sizeof( nip_mem_block_t ) + block->used_length ],
00807                                 0,
00808                                 size);
00809                 }
00810                 block->used_length += size;
00811                 return NIP_E_OK;
00812         }
00813         return NIP_E_INVALID_BLOCK;
00814 }
00815 
00816 /** Cut len bytes from the given location. The location has to be a managed block.
00817  */
00818 nip_error_t nip_mem_cut( nip_mem_ptr_t *location, nip_mem_size_t len )
00819 {
00820         uint8_t *l;
00821         nip_mem_size_t bufsize = nip_mem_buf_used( location->id);
00822 
00823         if ( len + (nip_mem_size_t)location->ptr > bufsize )
00824                 return NIP_E_PARAM_FAILED;
00825 
00826         l = nip_mem_ptr( location );
00827         if ( l == NULL ) return nip_error;
00828 
00829         nip_memmove( l, l+len, bufsize-len-(nip_mem_size_t)location->ptr );
00830 
00831         nip_mem_release_block( location->id );
00832 
00833         // resize block to new length
00834         nip_mem_set_used( location->id, bufsize - len );
00835 
00836         return NIP_E_OK;
00837 }
00838 
00839 /** Allocate memory at dst and copy data from src to dst. No data will be
00840  * overwritten at dst. All current data at dst will be moved by len bytes. After
00841  * successfully inserting the data, the dst-pointer will be placed at the end of
00842  * the inserted data, so it can be directly used for follow-up insertions.
00843  *
00844  * @param dst has to point to a managed memory block. If the offset component of
00845  * dst points beyond the size of the dst-block or is -1, the new data will be
00846  * Appended to the dst-block. So to simply append data to a block you can use
00847  * (nip_mem_size_t)-1 as offset.
00848  * @param src may either point to a managed memory block or some other buffer.
00849  * @param len amount of bytes to be copied from src to dst.
00850  *
00851  * @return NIP_E_OK on success or
00852  *  - NIP_E_OUT_OF_MEMORY if not enough memory could be allocated at dst,
00853  *  - NIP_E_BLOCK_LOCKED  if dst or source block were locked
00854  *  - NIP_E_INVALID_BLOCK or
00855  *  - NIP_E_PARAM_FAILED  if src or dst pointed to invalid address/block
00856  */
00857 nip_error_t nip_mem_insert( nip_mem_ptr_t *dst, nip_mem_ptr_t *src, nip_mem_size_t len )
00858 {
00859         nip_error_t    res;
00860         nip_mem_size_t dst_size;
00861         uint8_t        *dst_buf, *src_buf;
00862 
00863         if ( dst == NULL || src == NULL )
00864                 return NIP_E_PARAM_FAILED;
00865 
00866         dst_size = nip_mem_buf_used( dst->id );
00867 
00868         if ( (nip_mem_size_t)dst->ptr > dst_size )
00869                 dst->ptr = (void*)dst_size;
00870 
00871         // allocate len-Bytes at dst
00872         res = nip_mem_write( dst->id, NULL, len );
00873         if ( res != NIP_E_OK )
00874                 goto quit;
00875 
00876         // get pointers to source and destination buffers
00877         src_buf = nip_mem_ptr( src );
00878 
00879         if ( ( dst_buf = nip_mem_ptr( dst ) ) == NULL )
00880         {
00881                 res = nip_error;
00882                 goto release_src;
00883         }
00884 
00885         // move data at dst by len
00886         nip_memmove( dst_buf+len, dst_buf, dst_size - (nip_mem_size_t)dst->ptr );
00887 
00888         // copy data from source to dst, or if src_buf is NULL fill dst_buf with 0
00889         if ( src_buf != NULL )
00890                 nip_memcpy( dst_buf, src_buf, len );
00891         else
00892                 nip_memset( dst_buf, 0x00, len );
00893 
00894         // move dst pointer
00895         dst->ptr += len;
00896 
00897         nip_mem_release_ptr( dst );
00898 
00899         release_src:
00900                 nip_mem_release_ptr( src );
00901 
00902         quit:
00903                 return res;
00904 }
00905 
00906 /** Read data from block at a specific position.
00907  *
00908  * The data will be copied from a specific position in the block into the given
00909  * buffer. The data will not be removed from the block afterwards. If buf points
00910  * to NULL nothing will be copied, but the number of bytes that would've been
00911  * copied if buf had not been NULL will be returned.
00912  *
00913  * @return number of bytes actually copied or 0 on error. Possible errors are
00914  *  - NIP_E_EOF given position did not contain any data
00915  *  - NIP_E_INVALID_BLOCK given block_id was unknown
00916  */
00917 nip_mem_size_t nip_mem_read_at_pos( nip_mem_handle_t block_id, uint8_t *buf,
00918                                     nip_mem_size_t size, nip_mem_size_t pos)
00919 {
00920         nip_mem_block_t *block;
00921         nip_mem_size_t data_length;
00922 
00923         // find block in pool
00924         block = nip_mem_locate_block( block_id );
00925         if ( block == NULL )
00926         {
00927                 nip_error = NIP_E_INVALID_BLOCK;
00928                 return 0;
00929         }
00930 
00931         data_length = block->used_length;
00932         if ( pos >= data_length )
00933         {
00934                 nip_error = NIP_E_EOF;
00935                 return 0;
00936         }
00937 
00938         // check available data size
00939         if ( pos + size > data_length )
00940                 size = data_length - pos;
00941 
00942         // copy data to buffer
00943         if ( buf != NULL )
00944                 nip_memmove( buf, &((uint8_t*)block)[ sizeof( nip_mem_block_t ) + pos ], size );
00945 
00946         return size;
00947 }
00948 
00949 
00950 /** Read data from block.
00951  *
00952  * The data will be copied from the block into the given buffer. The read data
00953  * will be removed from the block. This operation will usually be used in
00954  * network buffers where data is just being collected and read once.
00955  *
00956  * If buf is NULL the requested amount of bytes will be read/deleted from the
00957  * buffer, but not copied anywhere.
00958  *
00959  * @param block_id  id of block to read data from
00960  * @param buf       pointer to buffer to copy data to
00961  * @param size      number of bytes to be copied
00962  *
00963  * @return number of bytes actually copied or 0 on error. Possible errors are
00964  *  - NIP_E_EOF given position did not contain any data
00965  *  - NIP_E_INVALID_BLOCK given block_id was unknown
00966  */
00967 nip_mem_size_t nip_mem_read( nip_mem_handle_t block_id, uint8_t *buf, nip_mem_size_t size )
00968 {
00969         nip_mem_block_t *block;
00970         nip_mem_size_t bytes_copied;
00971 
00972         // find block in pool
00973         block = nip_mem_locate_block( block_id );
00974         if ( block == NULL )
00975         {
00976                 nip_error = NIP_E_INVALID_BLOCK;
00977                 return 0;
00978         }
00979 
00980         bytes_copied = nip_mem_read_at_pos( block_id, buf, size, 0 );
00981 
00982         if ( bytes_copied > 0 )
00983         {
00984                 // remove bytes from block
00985                 block->used_length -= bytes_copied;
00986                 nip_memmove(
00987                         &((uint8_t*)block)[ sizeof( nip_mem_block_t ) ],
00988                         &((uint8_t*)block)[ sizeof( nip_mem_block_t ) + bytes_copied],
00989                         block->used_length
00990                                 );
00991         }
00992 
00993         return bytes_copied;
00994 }
00995 
00996 /** Move memory inbetween blocks by reading from the beginning of one block and
00997  * appending to the end of another block. Block sizes will be adjusted, if
00998  * necessary.
00999  *
01000  * In case not enough memory was available for the move operation,
01001  * NIP_E_OUT_OF_MEMORY will be written to nip_error and the number of bytes
01002  * which have successfully been moved be returned.
01003  *
01004  * @return number of bytes moved.
01005  */
01006 nip_mem_size_t nip_mem_move( nip_mem_handle_t dst_block, nip_mem_handle_t src_block, nip_mem_size_t size )
01007 {
01008         nip_mem_size_t  moved_size     = 0;
01009         nip_mem_size_t  available_size = size;
01010         nip_mem_block_t *block;
01011 
01012         do
01013         {
01014                 if ( available_size > size )
01015                         available_size = size;
01016 
01017                 // attempt to add available_size bytes to destination block
01018                 nip_error = nip_mem_write( dst_block, NULL, available_size );
01019 
01020                 if ( nip_error == NIP_E_OUT_OF_MEMORY )
01021                 {
01022                         if ( available_size == 1 )
01023                         {
01024                                 // no chance... tried smallest possible portion.
01025                                 break;
01026                         }
01027 
01028                         // try to allocate smaller size.
01029                         available_size /= 2;
01030                 }
01031                 else
01032                 if ( nip_error == NIP_E_OK )
01033                 {
01034                         // get pointer to destination memory block
01035                         block = nip_mem_locate_block( dst_block );
01036                         if ( block == NULL ) break;
01037 
01038                         // append data to block
01039                         nip_mem_read( src_block, &((uint8_t*)block)[sizeof(nip_mem_block_t)+block->used_length-available_size], available_size );
01040 
01041                         moved_size += available_size;
01042                         size       -= available_size;
01043                 }
01044                 else
01045                 {
01046                         // whatever happened.. quit.
01047                         break;
01048                 }
01049         } while ( size > 0 );
01050 
01051         return moved_size;
01052 }
01053 
01054 /** Obtain pointer on memory block data to perform direct operations on memory.
01055  *
01056  * The pointer is usually used for direct access to memory, such as using
01057  * structures. For simply reading from or writing to the block the functions
01058  * nip_mem_read() and nip_mem_write() SHOULD be used.
01059  *
01060  * The block will be locked for the time the pointer is handed out and will
01061  * therefor not be available for dynamic scaling or defragmentation, which will
01062  * in turn affect the entire memory management. MAKE SURE to obtain pointers for
01063  * as short a period as possible, to not impose those negative on the management
01064  * system any longer than needed.
01065  *
01066  * Use the nip_mem_release_block() function to unlock the block. The given pointer
01067  * MAY NOT be used after releasing it.
01068  *
01069  * @return Pointer to the block payload or NULL if the block_id was invalid or
01070  * the block has been locked by another process already. The nip_error variable
01071  * will be set to NIP_E_BLOCK_LOCKED, NIP_E_INVALID_BLOCK if NULL is returned.
01072  */
01073 void *nip_mem_obtain_ptr( nip_mem_handle_t block_id )
01074 {
01075         nip_mem_block_t *block;
01076 
01077         // find block in pool
01078         block = nip_mem_locate_block( block_id );
01079         if ( block == NULL || !NIP_MEM_BLOCK_USED( block ) )
01080         {
01081                 nip_error = NIP_E_INVALID_BLOCK;
01082                 return NULL;
01083         }
01084 
01085         if ( NIP_MEM_BLOCK_LOCKED( block ) )
01086         {
01087                 nip_error = NIP_E_BLOCK_LOCKED;
01088                 return NULL;
01089         }
01090 
01091         block->flags |= NIP_MEM_FLG_LOCKED;
01092 
01093         return ++block;
01094 }
01095 
01096 /** Unlocks memory block. Use this function after each nip_mem_obtain_ptr()
01097  * call.
01098  */
01099 void nip_mem_release_block( nip_mem_handle_t block_id )
01100 {
01101         nip_mem_block_t *block;
01102 
01103         // find block in pool
01104         block = nip_mem_locate_block( block_id );
01105         if ( block == NULL || !NIP_MEM_BLOCK_LOCKED( block ) )
01106         {
01107                 return;
01108         }
01109 
01110         block->flags &= ~NIP_MEM_FLG_LOCKED;
01111 }
01112 
01113 
01114 /** return pointer to location specified by p. Depending on the id attribute
01115  * of p the result will point to an offset of the block with that ID, or if
01116  * id is set to NIP_MEM_NULL, the ptr attribute will be simply returned.
01117  *
01118  * @note You should call nip_mem_release_ptr() after you're done working with
01119  * the pointer, to make sure no ressources will remain blocked indefinitely.
01120  */
01121 void *nip_mem_ptr( nip_mem_ptr_t *p )
01122 {
01123         uint8_t *res;
01124 
01125         if ( p == NULL )
01126                 return NULL;
01127 
01128         // no ID given? -> treat as regular pointer
01129         if ( p->id == NIP_MEM_NULL )
01130                 return p->ptr;
01131 
01132         // check offset, which MUST NOT exceed buffer size
01133         if ( (nip_mem_size_t)p->ptr >= nip_mem_buf_used( p->id ) )
01134                 return NULL;
01135 
01136         res = nip_mem_obtain_ptr( p->id );
01137         if ( res != NULL )
01138                 res = res + (nip_mem_size_t)p->ptr;
01139 
01140         return res;
01141 }
01142 
01143 /** Counterpart to nip_mem_ptr. Release previously obtained pointer and unlock
01144  * any ressources that may have been locked because of it.
01145  */
01146 void nip_mem_release_ptr( nip_mem_ptr_t *p )
01147 {
01148         if ( p != NULL )
01149         {
01150                 nip_mem_release_block( p->id );
01151         }
01152 }
01153 
01154 
01155 /** Return used buffer size. On error 0 will be returned but since 0 is also a
01156  * valid size you must check nip_error to ensure an error occured.
01157  */
01158 nip_mem_size_t nip_mem_buf_used( nip_mem_handle_t block_id )
01159 {
01160         nip_mem_block_t *block;
01161 
01162         // find block in pool
01163         block = nip_mem_locate_block( block_id );
01164         ///@todo remove debug-block line whenever not needed anymore. This is just used
01165         /// to debug the block's content outside the memory management functions.
01166         //debug_block = block;
01167         if ( block == NULL )
01168         {
01169                 nip_error = NIP_E_INVALID_BLOCK;
01170                 return 0;
01171         }
01172 
01173         return block->used_length;
01174 }
01175 
01176 /** Set Buffer used size to specific value.
01177  *
01178  * @warning Only do this if you're sure about the used buffer size as it may
01179  * lead to data loss or corrupted buffers if set to a wrong value.
01180  */
01181 void nip_mem_set_used( nip_mem_handle_t block_id, nip_mem_size_t new_size )
01182 {
01183         nip_mem_block_t *block;
01184 
01185         // find block in pool
01186         block = nip_mem_locate_block( block_id );
01187         if ( block == NULL )
01188         {
01189                 nip_error = NIP_E_INVALID_BLOCK;
01190         }
01191         else
01192                 block->used_length = new_size;
01193 }
01194 
01195 #endif /* NIP_MEM_ENABLE == 1 */

Generated on Thu Jul 10 01:09:29 2008 for NIP by  doxygen 1.5.5