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 }