"Fossies" - the Fresh Open Source Software Archive

Member "AutoHotkey_L-1.1.33.09/source/SimpleHeap.cpp" (8 May 2021, 7819 Bytes) of package /windows/misc/AutoHotkey_L-1.1.33.09.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "SimpleHeap.cpp" see the Fossies "Dox" file reference documentation.

    1 /*
    2 AutoHotkey
    3 
    4 Copyright 2003-2009 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 LPTSTR SimpleHeap::Malloc(LPTSTR 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 ensure 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 _T(""); // 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 = _tcslen(aBuf);
   36     LPTSTR new_buf;
   37     if (   !(new_buf = (LPTSTR)SimpleHeap::Malloc((aLength + 1) * sizeof(TCHAR)))   ) // +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     if (aLength)
   43         tmemcpy(new_buf, aBuf, aLength); // memcpy() typically benchmarks slightly faster than strcpy().
   44     //else only a terminator is needed.
   45     new_buf[aLength] = '\0'; // Terminate here for when aLength==0 and for the memcpy above so that caller's aBuf doesn't have to be terminated.
   46     return new_buf;
   47 }
   48 
   49 void* SimpleHeap::Malloc(size_t aSize)
   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 % sizeof(void *);
   78     size_t size_consumed = remainder ? aSize + (sizeof(void *) - 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 (void *)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 }