home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 242 / Issue 242 - April 2008 - DPCS0408DVD.ISO / Open Source / AutoHotKey / Source / AutoHotkey104705_source.exe / source / SimpleHeap.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-01-01  |  7.6 KB  |  165 lines

  1. /*
  2. AutoHotkey
  3.  
  4. Copyright 2003-2007 Chris Mallett (support@autohotkey.com)
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15. */
  16.  
  17. #include "stdafx.h" // pre-compiled headers
  18. #include "SimpleHeap.h"
  19. #include "globaldata.h" // for g_script, so that errors can be centrally reported here.
  20.  
  21. // Static member data:
  22. SimpleHeap *SimpleHeap::sFirst = NULL;
  23. SimpleHeap *SimpleHeap::sLast  = NULL;
  24. char *SimpleHeap::sMostRecentlyAllocated = NULL;
  25. UINT SimpleHeap::sBlockCount = 0;
  26.  
  27. char *SimpleHeap::Malloc(char *aBuf, size_t aLength)
  28. // v1.0.44.14: Added aLength to improve performance in cases where callers already know the length.
  29. // If aLength is at its default of -1, the length will be calculated here.
  30. // Caller must ensture that aBuf isn't NULL.
  31. {
  32.     if (!aBuf || !*aBuf) // aBuf is checked for NULL because it's not worth avoiding it for such a low-level, frequently-called function.
  33.         return ""; // Return the constant empty string to the caller (not aBuf itself since that might be volatile).
  34.     if (aLength == -1) // Caller wanted us to calculate it.  Compare directly to -1 since aLength is unsigned.
  35.         aLength = strlen(aBuf);
  36.     char *new_buf;
  37.     if (   !(new_buf = SimpleHeap::Malloc(aLength + 1))   ) // +1 for the zero terminator.
  38.     {
  39.         g_script.ScriptError(ERR_OUTOFMEM, aBuf);
  40.         return NULL; // Callers may rely on NULL vs. "" being returned in the event of failure.
  41.     }
  42.     memcpy(new_buf, aBuf, aLength + 1); // memcpy() typically benchmarks slightly faster than strcpy().
  43.     return new_buf;
  44. }
  45.  
  46.  
  47.  
  48. char *SimpleHeap::Malloc(size_t aSize)
  49. // Seems okay to return char* for convenience, since that's the type most often used.
  50. // This could be made more memory efficient by searching old blocks for sufficient
  51. // free space to handle <size> prior to creating a new block.  But the whole point
  52. // of this class is that it's only called to allocate relatively small objects,
  53. // such as the lines of text in a script file.  The length of such lines is typically
  54. // around 80, and only rarely would exceed 1000.  Trying to find memory in old blocks
  55. // seems like a bad trade-off compared to the performance impact of traversing a
  56. // potentially large linked list or maintaining and traversing an array of
  57. // "under-utilized" blocks.
  58. {
  59.     if (aSize < 1 || aSize > BLOCK_SIZE)
  60.         return NULL;
  61.     if (!sFirst) // We need at least one block to do anything, so create it.
  62.         if (   !(sFirst = CreateBlock())   )
  63.             return NULL;
  64.     if (aSize > sLast->mSpaceAvailable)
  65.         if (   !(sLast->mNextBlock = CreateBlock())   )
  66.             return NULL;
  67.     sMostRecentlyAllocated = sLast->mFreeMarker; // THIS IS NOW THE NEWLY ALLOCATED BLOCK FOR THE CALLER, which is 32-bit aligned because the previous call to this function (i.e. the logic below) set it up that way.
  68.     // v1.0.40.04: Set up the NEXT chunk to be aligned on a 32-bit boundary (the first chunk in each block
  69.     // should always be aligned since the block's address came from malloc()).  On average, this change
  70.     // "wastes" only 1.5 bytes per chunk. In a 200 KB script of typical contents, this change requires less
  71.     // than 8 KB of additional memory (as shown by temporarily making BLOCK_SIZE a smaller value such as 8 KB
  72.     // for a more accurate measurement).  That cost seems well worth the following benefits:
  73.     // 1) Solves failure of API functions like GetRawInputDeviceList() when passed a non-aligned address.
  74.     // 2) May solve other obscure issues (past and future), which improves sanity due to not chasing bugs
  75.     //    for hours on end that were caused solely by non-alignment.
  76.     // 3) May slightly improve performance since aligned data is easier for the CPU to access and cache.
  77.     size_t remainder = aSize % 4;
  78.     size_t size_consumed = remainder ? aSize + (4 - remainder) : aSize;
  79.     // v1.0.45: The following can't happen when BLOCK_SIZE is a multiple of 4, so it's commented out:
  80.     //if (size_consumed > sLast->mSpaceAvailable) // For maintainability, don't allow mFreeMarker to go out of bounds or
  81.     //    size_consumed = sLast->mSpaceAvailable; // mSpaceAvailable to go negative (which it can't due to be unsigned).
  82.     sLast->mFreeMarker += size_consumed;
  83.     sLast->mSpaceAvailable -= size_consumed;
  84.     return sMostRecentlyAllocated;
  85. }
  86.  
  87.  
  88.  
  89. void SimpleHeap::Delete(void *aPtr)
  90. // If aPtr is the most recently allocated area of memory by SimpleHeap, this will reclaim that
  91. // memory.  Otherwise, the caller should realize that the memory cannot be reclaimed (i.e. potential
  92. // memory leak unless caller handles things right).
  93. {
  94.     if (aPtr != sMostRecentlyAllocated || !sMostRecentlyAllocated)
  95.         return;
  96.     size_t sMostRecentlyAllocated_size = sLast->mFreeMarker - sMostRecentlyAllocated;
  97.     sLast->mFreeMarker -= sMostRecentlyAllocated_size;
  98.     sLast->mSpaceAvailable += sMostRecentlyAllocated_size;
  99.     sMostRecentlyAllocated = NULL; // i.e. no support for anything other than a one-time delete of an item just added.
  100. }
  101.  
  102.  
  103.  
  104. // Commented out because not currently used:
  105. //void SimpleHeap::DeleteAll()
  106. //// See Hotkey::AllDestructAndExit for comments about why this isn't actually called.
  107. //{
  108. //    SimpleHeap *next, *curr;
  109. //    for (curr = sFirst; curr != NULL;)
  110. //    {
  111. //        next = curr->mNextBlock;  // Save this member's value prior to deleting the object.
  112. //        delete curr;
  113. //        curr = next;
  114. //    }
  115. //}
  116.  
  117.  
  118.  
  119. SimpleHeap *SimpleHeap::CreateBlock()
  120. // Added for v1.0.40.04 to try to solve the fact that some functions such as GetRawInputDeviceList()
  121. // will sometimes fail if passed memory from SimpleHeap. Although this change didn't actually solve
  122. // the issue (it turned out to be a 32-bit alignment issue), using malloc() appears to save memory
  123. // (compared to using "new" on a class that contains a large buffer such as "char mBlock[BLOCK_SIZE]").
  124. // In a 200 KB script, it saves 8 KB of VM Size as shown by Task Manager.
  125. {
  126.     SimpleHeap *block;
  127.     if (   !(block = new SimpleHeap)   )
  128.         return NULL;
  129.     // The new block's mFreeMarker starts off pointing to the first byte in the new block:
  130.     if (   !(block->mBlock = block->mFreeMarker = (char *)malloc(BLOCK_SIZE))   )
  131.     {
  132.         delete block;
  133.         return NULL;
  134.     }
  135.     // Since above didn't return, block was successfully created:
  136.     block->mSpaceAvailable = BLOCK_SIZE;
  137.     sLast = block;  // Constructing a new block always results in it becoming the current block.
  138.     ++sBlockCount;
  139.     return block;
  140. }
  141.  
  142.  
  143.  
  144. SimpleHeap::SimpleHeap()  // Construct a new block.  Caller is responsible for initializing other members.
  145.     : mNextBlock(NULL)
  146. {
  147. }
  148.  
  149.  
  150. SimpleHeap::~SimpleHeap()
  151. // This destructor is currently never called because all instances of the object are created
  152. // with "new", yet none are ever destroyed with "delete".  As an alternative to this behavior
  153. // the delete method should recursively delete mNextBlock, if it's non-NULL, prior to
  154. // returning.  It seems unnecessary to do this, however, since the whole idea behind this
  155. // class is that it's a simple implementation of one-time, persistent memory allocation.
  156. // It's not intended to permit deallocation and subsequent reclamation of freed fragments
  157. // within the collection of blocks.  When the program exits, all memory dynamically
  158. // allocated by the constructor and any other methods that call "new" will be reclaimed
  159. // by the OS.  UPDATE: This is now called by static method DeleteAll().
  160. {
  161.     if (mBlock) // v1.0.40.04
  162.         free(mBlock);
  163.     return;
  164. }
  165.