/*----------------------------------------------------------------------------
** Name: memmanag.cpp
**
** Title: Memory Manager
**
** Purpose:
**  Create and manage the symbol table memory.  Included:
**      creating table
**      adding elements to table
**      growing table
**      closing table
**
** Status: PRELIMINARY | CODED | TESTED
**
** $Log:   S:/tbird/mt2_186/symbol/memmanag.cpv  $
** 
**    Rev 1.1   26 Feb 1997 11:42:58   Judy
** 
**    Rev 1.1   14 Jan 1997 15:30:58   Judy
** No change.
** 
**    Rev 1.0   14 Jun 1996 16:42:04   Judy
** Initial revision.
** 
**    Rev 1.6   21 Dec 1994 15:16:26   nghia
** Fixed Cooper's GPF bug, Symbol table failed to expand in holding 2M types.
** Revised Table class to check for new handle from memory reallocation to
** free the old block.  Added additional checks for table memory reallocation.
** 
**    Rev 1.5   25 Feb 1994 12:49:56   nghia
** Fixed PV 2.1 BUG: GlobalReAlloc() causes pointers to be invalid.
** - Removed GlobalUnlock() call before calling GlobalReAlloc() to reduce the
** chance that a new handle will be allocated.  However, this will not prevent
** Windows from allocate new memory, if the re-allocation is crossed the 64K
** boundary.
** 
**    Rev 1.4   10 Jul 1992 19:00:28   brucea
** Cleanup, no code changes
** 
**    Rev 1.3   15 Jun 1992 09:18:08   brucea
** Added: return SUCCESS to Table::InitOrExtendTable to fix compile warning
** 
**    Rev 1.2   02 Jan 1992 12:00:54   john
** Added check to make sure memory should be freed
** 
**    Rev 1.1   09 Dec 1991 09:27:50   john
** Added code to handle reinitializing the TABLE entries.  This was
** done to support removing all symbols from the symbol table.
** 
**    Rev 1.0   11 Sep 1991 09:08:56   brucea
** Initial revision.
**
** $Header:   S:/tbird/mt2_186/symbol/memmanag.cpv   1.1   26 Feb 1997 11:42:58   Judy  $
**
** Copyright (C) 1991 Microtek International.  All rights reserved.
**
**--------------------------------------------------------------------------*/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/

#ifndef _MEMMANAG_
#include "memmanag.h"
#endif
                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/

//------------------------------------------------------------------------
// Table::InitOrExtendTable
//
// Purpose:
//    Initialize table if tableSize = 0 else
//    extend the table by <expandSize>.
//
// Input parameters:
//    allocFlag:
//       0 if memory does not need to be zeroed upon initialization
//       GMEM_ZEROINIT if memory needs to be zeroed upon initialization
//
// Output parameters: None
//
// Error:
//    Reports ER_SYM_OUT_OF_MEMORY if memory cannot be Alloc'ed or ReAlloc'ed
//------------------------------------------------------------------------
RETCODE Table::InitOrExtendTable(WORD zeroInitFlag) {

   HANDLE tempTableHandle;
   U32 actualSize, memAvailable = 0L;
   U32 trialSize;    // latest incremental size to try

   if (!tableSize) {  // table has not been Alloc'ed yet
      if (!(tableHandle = GlobalAlloc(GMEM_MOVEABLE | zeroInitFlag,
                                      initialSize))) {
         // not enough memory found; compact then check again
         memAvailable = GlobalCompact(initialSize);
         if (memAvailable < initialSize) {
            return ER_SYM_OUT_OF_MEMORY;
         } else {
            if (!(tableHandle = GlobalAlloc(GMEM_MOVEABLE | zeroInitFlag,
                                            initialSize)))
               return ER_SYM_OUT_OF_MEMORY;
         }
      }
      // memory found; fall thru
      actualSize = GlobalSize(tableHandle);
      
      if ((hpTableObject = (HPU8)GlobalLock(tableHandle)) != 0L) {
         //successful initial allocation
         // actual size may be larger than requested size, but we only want
         // the size on an even binary boundary
         tableSize = initialSize;
         return SUCCESS;
      }
   } else {  // table has been previously Alloc'ed; need to ReAlloc
      trialSize = expandSize;
    
#ifdef DEBUG
      if (expandSize < MIN_EXPAND_SIZE) return ER_EXPAND_SIZE_ERROR;
#endif

	   if (!(tempTableHandle = GlobalReAlloc(tableHandle,
                                            (tableSize + trialSize),
                                            GMEM_MOVEABLE | zeroInitFlag))) {
         // GlobalReAlloc failed; compact and try again
         memAvailable = GlobalCompact(tableSize + trialSize);
         // Try remove all disacardable segments the reallocate again
         if (memAvailable < (tableSize + trialSize))
            GlobalCompact(0L);
         
         for (;;)  {
            GlobalCompact(tableSize + trialSize);
            if ((tempTableHandle = GlobalReAlloc(tableHandle,
                  (tableSize + trialSize), GMEM_MOVEABLE | zeroInitFlag)) != 0)
               // get out of loop if the ReAlloc worked
               { break; };
            // reduce requested size in half and try again
            if ((trialSize >>= 1) < 256)
               return ER_SYM_OUT_OF_MEMORY;
         }         
      }
   
      // 12/19/94 - Nghia
      // Check for the current size of the allocated memory handle
      // to make sure that the newly allocated table have enough memory
      actualSize = GlobalSize(tempTableHandle);
      if (actualSize < (tableSize + trialSize))
         return ER_SYM_OUT_OF_MEMORY;

      // save the actual expandSize
      expandSize = trialSize;
      
      // memory found - make sure that old memory block is deallocated
      if (tempTableHandle != tableHandle) {
         // Changed the old table attribute to DISCARDABLE
         GlobalReAlloc(tableHandle, 0L, GMEM_MODIFY | GMEM_DISCARDABLE);
         // Unlock the old table
         WORD lockCount = (GlobalFlags(tableHandle) & GMEM_LOCKCOUNT);
         while (lockCount--) GlobalUnlock(tableHandle);
         GlobalFree(tableHandle);  // free the old table
      }

      // restore table memory handle and other information
      tableHandle = tempTableHandle;  

      // restore huge pointer
      if ((hpTableObject = (HPU8)GlobalLock(tableHandle)) == 0L) 
         return ER_SYM_OUT_OF_MEMORY;  // GlobalLock failed

      // successful lock; update total memory size
      tableSize = (actualSize & 0xFFFFFF00L);
      // even though actual size may be larger than requested size,
      // want to maintain binary boundary of memory so that data
      // elements don't cross 64K boundary
      return SUCCESS;
      
   }  // end of else part of if (!tableSize)
   return SUCCESS;
};  // end of Table::InitOrExtendTable


//------------------------------------------------------------------------
// Table::ObliterateTable
//
// Purpose: release this table memory with GlobalFree
//------------------------------------------------------------------------
VOID Table::ObliterateTable(VOID) {
   WORD lockCount = 0;
   // call the routines to free memory only if the tableHandle is non-zero ;
   if (tableHandle) {
      lockCount = (GlobalFlags(tableHandle) & GMEM_LOCKCOUNT);
      while (lockCount--) GlobalUnlock(tableHandle);
      GlobalFree(tableHandle);
   }
   tableHandle = 0;
   tableSize = 0L;
   initialSize = INITIAL_TABLE_SIZE;
   expandSize =  MIN_EXPAND_SIZE;
}

/******************************** E O F *************************************/