memblk.cpp

Go to the documentation of this file.
00001 // $Id: memblk.cpp 1467 2006-07-18 17:00:07Z gerry $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 
00099 // class to handle large memory blocks
00100 
00101 /*
00102 */
00103 
00104 #include "camtypes.h"
00105 
00106 #include <errno.h>
00107 
00108 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 //#include "andy.h"
00110 //#include "simon.h"                // for _R(IDS_CONTINUE)
00111 //#include "memblk.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 #include "camelot.h"
00114 
00115 #ifndef WIN32
00116 #define MAX_BLOCK   0xFFFCL     // 64k-4
00117 #endif
00118 
00119 #if USE_VM_BLOCKS
00120 UINT32 MemoryBlock::PageSize;               // page size used in Virtual Mem API
00121 #endif
00122 
00123 /*********************************************************************************************
00124 
00125 >   class MemoryBlock
00126 
00127     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00128     Created:    3/3/94
00129     Purpose:    Some items start small and get large. The MemoryBlock class encapsulates
00130                 memory management for objects that generally need to be fixed in memory
00131                 but need to grow, and are generally large. Items that use the class
00132                 include the handles table and the relocatable heap.
00133         
00134 *********************************************************************************************/
00135 
00136 /*********************************************************************************************
00137 
00138 >   MemoryBlock::MemoryBlock()
00139 
00140     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00141     Created:    3/3/94
00142     Inputs:     -
00143     Returns:    -
00144     Purpose:    Constructor. Initialises some fields to zero. Does not do any allocations.
00145         
00146 *********************************************************************************************/
00147 
00148 MemoryBlock::MemoryBlock()
00149 {
00150 #if USE_STD_ALLOC
00151 #elif USE_VM_BLOCKS
00152     MemBase = NULL;
00153 #else
00154     MemHandle = 0;
00155 #endif
00156 }
00157 
00158 /*********************************************************************************************
00159 
00160 >   MemoryBlock::~MemoryBlock()
00161 
00162     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00163     Created:    3/3/94
00164     Inputs:     -
00165     Returns:    -
00166     Purpose:    Destructor. Doesn't do anything really, though might do a DeInit one day.
00167     SeeAlso:    MemoryBlock::DeInit
00168         
00169 *********************************************************************************************/
00170 
00171 MemoryBlock::~MemoryBlock()
00172 {
00173 }
00174 
00175 /*********************************************************************************************
00176 
00177 >   LPVOID MemoryBlock::Init( INT32 InitSize, BOOL bAutoZero, MemoryBlockType Type )
00178 
00179     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00180     Created:    3/3/94
00181     Inputs:     InitSize    initial size of block required
00182                 bAutoZero   TRUE if new memory required to be zeroed
00183                 Type        one of MEMORYBLOCK_xxx constants. Determines maximum size of
00184                             block.
00185     Returns:    Pointer to memory if worked, NULL if failed to initialise.
00186     Purpose:    Sets up block for subsequent allocations.
00187         
00188 *********************************************************************************************/
00189 
00190 LPVOID MemoryBlock::Init( INT32 InitSize, BOOL bAutoZero, MemoryBlockType Type )
00191 {
00192 #if USE_STD_ALLOC
00193 
00194     m_RoundedSize = GetRoundedSize( InitSize );
00195 
00196     // now physically grab the starting size
00197     LPVOID              pNewPtr;
00198     if( bAutoZero )
00199     {
00200         pNewPtr = CCMalloc( m_RoundedSize );
00201         memset(pNewPtr, 0, m_RoundedSize);
00202     }
00203     else
00204     {
00205         pNewPtr = CCMalloc( m_RoundedSize );
00206     }
00207         
00208     if( pNewPtr == NULL )
00209     {
00210         TRACEUSER( "Andy", wxT("malloc() failed because %lx\n"), errno );
00211         return NULL;
00212     }
00213 
00214     TRACEUSER( "Andy", wxT("malloc OK at %p\n"), pNewPtr );
00215     m_pMemBlk = pNewPtr;
00216     return pNewPtr;
00217 
00218 #elif USE_VM_BLOCKS
00219 
00220     // first time we are called get the page size
00221     if (PageSize==0L)
00222     {
00223         SYSTEM_INFO SysInfo;
00224         GetSystemInfo( &SysInfo );
00225         PageSize = SysInfo.dwPageSize;
00226     }
00227 
00228     // Note that the Virtual API always gives us zeroed memory whether we want it or not
00229 
00230 
00231     // calculate a suitable maximum size for the type of block
00232     INT32 LargestSize;
00233 
00234     switch (Type)
00235     {
00236         case MEMORYBLOCK_HANDLETABLE:
00237             LargestSize = 0x01000000;               // 16M of handles is enough
00238             break;
00239         case MEMORYBLOCK_RELOCHEAP:
00240             LargestSize = 0x10000000;               // 256M of reloc heap
00241             break;
00242         case MEMORYBLOCK_SCREENBUFFER:
00243             LargestSize = 0x01000000;               // 1024x768x32bits x4 for good measure
00244             break;
00245         default:
00246             ENSURE( FALSE, "Bad type in MemoryBlock::Init");
00247             return NULL;
00248     }
00249 
00250     // firstly reserve a gratuitously large chunk of address space
00251 
00252     MemBase = VirtualAlloc( NULL, LargestSize, MEM_RESERVE, PAGE_NOACCESS );
00253     if (MemBase==NULL)
00254     {
00255         TRACEUSER( "Andy", wxT("VirtualAlloc(reserve) failed because %lx\n"), GetLastError() );
00256         return NULL;
00257     }
00258 
00259     RoundedSize = GetRoundedSize( InitSize );
00260 
00261     // now physically grab the starting size
00262     LPVOID NewPtr = VirtualAlloc( MemBase, RoundedSize, MEM_COMMIT, PAGE_READWRITE );
00263     if (NewPtr==NULL)
00264     {
00265         TRACEUSER( "Andy", wxT("VirtualAlloc(commit) failed because %lx\n"), GetLastError() );
00266         VirtualFree(MemBase, 0L, MEM_RELEASE );
00267         MemBase = NULL;
00268         return NULL;
00269     }
00270 
00271     TRACEUSER( "Andy", wxT("VirtualAlloced OK at %lx from %lx\n"), NewPtr, MemBase );
00272 
00273     return NewPtr;
00274 #else
00275 
00276     #ifndef WIN32
00277     if (InitSize > MAX_BLOCK)
00278         return NULL;
00279     #endif
00280 
00281     AutoZero = bAutoZero;                                           // remember flag in struct
00282 
00283     UINT32 Flags = GMEM_MOVEABLE;
00284     if (AutoZero)
00285         Flags |= GMEM_ZEROINIT;
00286 
00287     MemHandle = GlobalAlloc( Flags, InitSize );
00288 TRACE( wxT("GlobalAlloc %u %lx returned %x\n"), Flags, InitSize, (INT32)MemHandle);
00289     if (MemHandle==NULL)
00290         return NULL;
00291 
00292     LPVOID Ptr = GlobalLock( MemHandle );
00293     if (Ptr == NULL)
00294     {
00295         GlobalFree( MemHandle );
00296         MemHandle = 0;
00297         return NULL;
00298     }
00299 
00300     return Ptr;
00301 #endif
00302 }
00303 
00304 /*********************************************************************************************
00305 
00306 >   void MemoryBlock::DeInit()
00307 
00308     Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00309     Created:    3/3/94
00310     Inputs:     -
00311     Returns:    -
00312     Purpose:    Frees up any allocations on this memory block. Should always be called
00313                 during exit for a tidy cleanup.
00314         
00315 *********************************************************************************************/
00316 
00317 void MemoryBlock::DeInit()
00318 {
00319 #if USE_STD_ALLOC
00320     if( NULL != m_pMemBlk )
00321     {
00322         CCFree(  m_pMemBlk );
00323         m_pMemBlk = NULL;
00324     }
00325 #elif USE_VM_BLOCKS
00326     if (MemBase)
00327     {
00328         BOOL Result = VirtualFree(MemBase, 0L, MEM_RELEASE);
00329         MemBase = NULL;
00330         ENSURE( Result, "VirtualFree failed");
00331     }
00332 #else
00333     if (MemHandle)
00334     {
00335         GlobalUnlock( MemHandle );
00336         GlobalFree( MemHandle );
00337         MemHandle = 0;
00338     }
00339 #endif
00340 }
00341 
00342 /*********************************************************************************************
00343 
00344     static BOOL DoGrowGlobalHandle( HGLOBAL *pHandle, UINT32 OldSize, UINT32 NewSize, UINT32 MoreFlags, LPVOID *Pointer)
00345 
00346         Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00347         Created:    24/2/94
00348         Inputs:     pHandle     pointer to global handle whose block we want bigger, might be
00349                                 updated upon return
00350                     OldSize     size it is at the moment
00351                     NewSize     desired size of new block
00352                     MoreFlags   extra flags to pass to GlobalReAlloc
00353                     Pointer     where new pointer is to be stored
00354         Returns:    TRUE if worked, FALSE if failed
00355         Purpose:    Low-level private primitve for MemoryBlock::Grow. Actually does the
00356                     work. Function does not exist if using VM_BLOCKs.
00357         SeeAlso:    MemoryBlock::Grow
00358         
00359 *********************************************************************************************/
00360 
00361 #if !USE_VM_BLOCKS && !USE_STD_ALLOC
00362 
00363 static BOOL DoGrowGlobalHandle( HGLOBAL *pHandle, UINT32 OldSize, UINT32 NewSize, UINT32 MoreFlags, LPVOID *Pointer)
00364 {
00365     if (GlobalUnlock( *pHandle ))
00366     {
00367         // if didn't unlock then we won't be able to realloc, so better stop now
00368         TRACE( wxT("GlobalUnlock failed in GrowGlobalHandle\n") );
00369         return FALSE;
00370     }
00371 
00372     // get Windows to grab us a new bigger block. Important to use GMEM_MOVEABLE as we want
00373     // our block to move if possible to satisfy the request
00374     HGLOBAL hNewHandle = GlobalReAlloc( *pHandle, NewSize, GMEM_MOVEABLE | MoreFlags );
00375 
00376     if (hNewHandle)
00377     {
00378         ADDR NewPtr = (ADDR)GlobalLock( hNewHandle );
00379         if (NewPtr == NULL)
00380         {
00381             // careful here as we really do want to get our old data back - at the moment
00382             // all we have is a handle to an unlocked block of memory. To try and recover the
00383             // situation we'll try setting it back to its old size
00384             hNewHandle = GlobalReAlloc( hNewHandle, OldSize, GMEM_MOVEABLE );
00385             if (hNewHandle)
00386             {
00387                 NewPtr = (ADDR)GlobalLock( hNewHandle );
00388                 if (NewPtr)
00389                 {
00390                     // OK, got old data back but might be in different place so tidy up
00391                     // then exit gracefully
00392                     *Pointer = NewPtr;
00393                     *pHandle = hNewHandle;
00394                     TRACE( wxT("WARNING: Made hasty exit from GrowGlobalHandle\n") );
00395                     return FALSE;                                   // indicate failure
00396                 }
00397             }
00398             // this is very catastrophic - we cannot get our old data back, so
00399             // we will communicate this to the user. The program is likely to
00400             // crash and burn after this
00401             TRACE( wxT("DANGER: Catastrophic failure in GrowGlobalHandle\n") );
00402             if (InformSeriousError( _R(IDE_BAD_GROWHEAP), _R(IDS_ABORT), _R(IDS_CONTINUE) ) == 1)
00403                 AfxAbort();
00404 
00405             return FALSE;                                   // although system is v. unstable
00406         }
00407 
00408         TRACEUSER( "Andy", wxT("Handle %x changed to %x (%lx-%lx) (%ld-%ld) %x\n"),
00409             (INT32)*pHandle, (INT32)hNewHandle,
00410             (INT32)*Pointer, (INT32)NewPtr,
00411             (INT32)OldSize, (INT32)NewSize,
00412             (INT32)MoreFlags
00413         );
00414 
00415         // we've got our new block, update the callers variables
00416         *Pointer = NewPtr;
00417         *pHandle = hNewHandle;
00418         return TRUE;
00419     }
00420 
00421     // couldn't reallocate it, the old one will still be there though
00422     TRACE( wxT("GlobalReAlloc failed in GrowGlobalHandle\n") );
00423     TRACEUSER( "Andy", wxT("Failed to grow handle %x from %ld.%ld at %lx\n"),
00424                 (INT32)*pHandle,
00425                 (INT32)OldSize,(INT32)NewSize,
00426                 (INT32)*Pointer
00427             );
00428 
00429     return FALSE;
00430 
00431 }
00432 #endif
00433 
00434 
00435 /*********************************************************************************************
00436 
00437 >   BOOL MemoryBlock::Grow( UINT32 OldSize, UINT32 NewSize, LPVOID *Pointer )
00438 
00439         Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00440         Created:    24/2/94
00441         Inputs:     OldSize     size it is at the moment
00442                     NewSize     desired size of new block
00443                     Pointer     where new pointer is to be stored
00444         Returns:    TRUE if worked, FALSE if failed
00445         Purpose:    Like GlobalReAlloc in a way, but a safer version that does all it can
00446                     to ensure existing data doesn't get thrown away. Handles unlocking,
00447                     reallocing and relocking the result. Even if the return value is
00448                     FALSE, the data might still have been moved - see if *Pointer has changed
00449                     if this is important to you.
00450         SeeAlso:    DoGrowGlobalHeap;MemoryBlock::Shrink
00451         
00452 *********************************************************************************************/
00453 
00454 BOOL MemoryBlock::Grow( UINT32 OldSize, UINT32 NewSize, LPVOID *Pointer )
00455 {
00456 #if USE_STD_ALLOC
00457     NewSize = GetRoundedSize( NewSize );                // round new size up
00458     if( NewSize == m_RoundedSize )
00459         return TRUE;                                    // if same physical size
00460 
00461     LPVOID              NewPtr = CCRealloc( *Pointer, NewSize );
00462     if (NewPtr==NULL)
00463     {
00464         TRACEUSER( "Andy", wxT("CCRealloc growing from %ld to %ld failed\n"), OldSize, NewSize);
00465         return FALSE;
00466     }
00467     *Pointer = m_pMemBlk = NewPtr;
00468     m_RoundedSize = NewSize;
00469     return TRUE;
00470     
00471 #elif USE_VM_BLOCKS
00472     NewSize = GetRoundedSize( NewSize );                // round new size up
00473     if (NewSize == RoundedSize)
00474         return TRUE;                                    // if same physical size
00475 
00476     LPVOID NewPtr = VirtualAlloc( MemBase, NewSize, MEM_COMMIT, PAGE_READWRITE );
00477     if (NewPtr==NULL)
00478     {
00479         TRACEUSER( "Andy", wxT("VirtualAlloc growing from %ld to %ld failed\n"), OldSize, NewSize);
00480         return FALSE;
00481     }
00482     *Pointer = NewPtr;
00483     RoundedSize = NewSize;
00484     return TRUE;
00485 
00486 #else
00487     const UINT32 Flags = AutoZero ? GMEM_ZEROINIT : 0;
00488 
00489     #ifndef WIN32
00490     if (NewSize > MAX_BLOCK)
00491         return FALSE;
00492     #endif
00493 
00494     return DoGrowGlobalHandle( &MemHandle, OldSize, NewSize, Flags, Pointer );
00495 #endif
00496 }
00497 
00498 /*********************************************************************************************
00499 
00500 >   BOOL MemoryBlock::Shrink( UINT32 OldSize, UINT32 NewSize, LPVOID *Pointer )
00501 
00502         Author:     Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com>
00503         Created:    3/3/94
00504         Inputs:     OldSize     size it is at the moment
00505                     NewSize     desired size of new block, smaller than OldSize
00506                     Pointer     where new pointer is to be stored
00507         Returns:    TRUE if worked, FALSE if failed
00508         Purpose:    Reduces the current allocation, without throwing away data. Normally
00509                     this should always work, as giving memory back shouldn't ever fail,
00510                     but a FALSE return means we couldn't. If a FALSE return is combined
00511                     with a NULL Pointer return, it means an extremely bad thing occurred
00512                     and the original data has been lost.
00513         SeeAlso:    MemoryBlock::Grow
00514         
00515 *********************************************************************************************/
00516 
00517 BOOL MemoryBlock::Shrink( UINT32 OldSize, UINT32 NewSize, LPVOID *Pointer )
00518 {
00519 #if USE_STD_ALLOC
00520     
00521     OldSize = GetRoundedSize( OldSize );
00522     NewSize = GetRoundedSize( NewSize );
00523     
00524     if (OldSize == NewSize)
00525         return TRUE;                                        // no change required
00526 
00527     if (OldSize < NewSize)
00528     {
00529         ENSURE(FALSE, "Cannot Shrink backwards");
00530         return FALSE;
00531     }                                   // if same physical size
00532 
00533     LPVOID              NewPtr = CCRealloc( *Pointer, NewSize );
00534     if (NewPtr==NULL)
00535     {
00536         TRACEUSER( "Andy", wxT("CCRealloc growing from %ld to %ld failed\n"), OldSize, NewSize);
00537         return FALSE;
00538     }
00539     *Pointer = m_pMemBlk = NewPtr;
00540     m_RoundedSize = NewSize;
00541     return TRUE;
00542     
00543 #elif USE_VM_BLOCKS
00544 
00545     // we do a shrink by freeing up spare blocks
00546 
00547     OldSize = GetRoundedSize( OldSize );
00548     NewSize = GetRoundedSize( NewSize );
00549 
00550     if (OldSize == NewSize)
00551         return TRUE;                                        // no change required
00552 
00553     if (OldSize < NewSize)
00554     {
00555         ENSURE(FALSE, "Cannot Shrink backwards");
00556         return FALSE;
00557     }
00558 
00559     // lets free up the unwanted bits now
00560     BOOL Result = VirtualFree( ((LPBYTE)MemBase)+NewSize, OldSize-NewSize, MEM_DECOMMIT );
00561 
00562     if (Result==FALSE)
00563     {
00564         TRACEUSER( "Andy", wxT("VirtualFree Shrink from %ld to %ld failed\n"), OldSize, NewSize);
00565         return FALSE;
00566     }
00567 
00568     RoundedSize = NewSize;
00569     return TRUE;
00570 
00571 #else
00572     // the data will be left alone
00573     ENSURE( FALSE, "Cannot shrink memory blocks");
00574     return FALSE;
00575 
00576 #endif
00577 }

Generated on Sat Nov 10 03:48:39 2007 for Camelot by  doxygen 1.4.4