//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1994.
//
//  File:       smalloc.cxx
//
//  Contents:   Shared memory heap implementation
//
//  Classes:    
//
//  Functions:  
//
//  History:    29-Mar-94       PhilipLa        Created
//              05-Feb-95   KentCe      Use Win95 Shared Heap.
//              10-May-95   KentCe      Defer Heap Destruction to the last
//                                      process detach.
//
//----------------------------------------------------------------------------

#include <dfhead.cxx>
#pragma hdrstop

#include <smalloc.hxx>
#include <dfdeb.hxx>


#ifdef NEWPROPS
#define FULLIMPL
#endif

//
//  Take advantage of unique Win95 support of a shared heap.
//
#if defined(_CHICAGO_)

#define HEAP_SHARED 0x04000000          // Secret feature of Win95 only.

//
//  Locate the following in a shared data segment.
//
#pragma data_seg(".sdata")

HANDLE gs_hSharedHeap      = NULL;      // hSharedHeap Handle for Win95.
DFLUID gs_dfluid           = LUID_BASE; // shared docfile global LUID

#pragma data_seg()

#define PRINTSTATS

#else // defined(_CHICAGO_)

#ifdef MULTIHEAP
DFLUID gs_dfluid           = LUID_BASE; // task memory heap support
INT    gs_iSharedHeaps     = 0;
#endif
#define DLL

#define DEB_STATS 0x00010000
#define DEB_PRINT 0x00020000

#ifdef DLL

#define PERCENT(a,b,c) (int)((((double)a + (double)b) / (double)c) * 100.0)

#define PRINTSTATS \
        memDebugOut((DEB_STATS,  \
                     "Total size: %lu, Space:  Free: %lu, Alloced: %lu"\
                     "  Blocks:  Free: %lu, Alloced: %lu"\
                     "  Efficiency: %.2f%%\n",\
                     _cbSize,\
                     GetHeader()->_ulFreeBytes,\
                     GetHeader()->_ulAllocedBytes,\
                     GetHeader()->_ulFreeBlocks,\
                     GetHeader()->GetAllocedBlocks(),\
                     PERCENT(GetHeader()->_ulFreeBytes,\
                             GetHeader()->_ulAllocedBytes, _cbSize)));
                     
#else
#define PRINTSTATS \
        printf( \
                "Total size: %lu, Free space: %lu, Alloced space: %lu"\
                "  Efficiency: %.2f%%\n",\
                _cbSize,\
                GetHeader()->_ulFreeBytes,\
                GetHeader()->_ulAllocedBytes,\
                ((double)(GetHeader()->_ulFreeBytes +\
                        GetHeader()->_ulAllocedBytes) / \
                (double)_cbSize) * (double)100);
#endif


#if DBG == 1
inline BOOL IsAligned(void *pv)
{
    return !((ULONG_PTR)pv & 7);
}
#else
#define IsAligned(x) TRUE
#endif


#define SHAREDMEMBASE NULL

#endif // !defined(_CHICAGO_)

//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::Init, public
//
//  Synopsis:   Initialize heap for use
//
//  Arguments:  [pszName] -- Name of shared memory heap to use
//
//  Returns:    Appropriate status code
//
//  History:    29-Mar-94       PhilipLa        Created
//              05-Feb-95       KentCe          Use Win95 Shared Heap.
//
//  Remarks:    Review the class destructor if you change this code.
//
//----------------------------------------------------------------------------

#if !defined(MULTIHEAP)
SCODE CSmAllocator::Init(LPWSTR pszName)
#else
SCODE CSmAllocator::Init(ULONG ulHeapName, BOOL fUnmarshal)
#endif
{
    SCODE sc = S_OK;

#if !defined(MULTIHEAP)
    // Initialize the mutex
    sc = _dmtx.Init(TEXT("DocfileAllocatorMutex"));
    if (FAILED(sc))
    {
        return sc;
    }

    sc = _dmtx.Take(DFM_TIMEOUT);
    if (FAILED(sc))
    {
        return sc;
    }
#endif

#if defined(_CHICAGO_)
    //
    //  Create a new shared heap if this is the first time thru.
    //
    if (gs_hSharedHeap == NULL)
    {
        gs_hSharedHeap = HeapCreate(HEAP_SHARED, 0, 0);
#if DBG == 1
        ModifyResLimit(DBRQ_HEAPS, 1);
#endif        
    }

    //
    //  We keep a copy of the shared heap as a flag so the destructor logic
    //  does the right thing.
    //
    //
    m_hSharedHeap = gs_hSharedHeap;

#else
    CSharedMemoryBlock *psmb = NULL;
#ifdef MULTIHEAP
    _cbSize = 0;
    if (!fUnmarshal && g_pteb == NtCurrentTeb())  // only for main thread
    {
        if (g_ulHeapName != 0)  // the global shared memory block is active
        {
            _psmb = &g_smb;                             // needed for GetHeader
            _pbBase = (BYTE *)(_psmb->GetBase());       // needed for GetHeader
            if (_pbBase != NULL && GetHeader()->GetAllocedBlocks() == 0)
            {                                           // its' empty reuse it
                psmb = _psmb;
                _ulHeapName = g_ulHeapName;
                memDebugOut ((DEB_ITRACE, "Out CSmAllocator::Init "
                              " reuse %x\n", g_ulHeapName));
                return sc;
            }
        }
        else
        {
            psmb = _psmb = &g_smb;             // initialize g_smb
        }
    }

    if (psmb == NULL)
    {
        psmb = _psmb = new CSharedMemoryBlock ();
        if (psmb == NULL)
            return STG_E_INSUFFICIENTMEMORY;
    }

    WCHAR pszName[DOCFILE_SM_NAMELEN];
    wsprintf(pszName, L"DfSharedHeap%X", ulHeapName);
#else
    psmb = &_smb;
#endif

#if WIN32 == 100 || WIN32 > 200
    CGlobalSecurity         gs;
    if (SUCCEEDED(sc = gs.Init(TRUE)))
    {
#else
    LPSECURITY_ATTRIBUTES gs = NULL;
#endif

    //  the SMB needs a few bytes for its own header. If we request
    //  a page sized allocation, those few header bytes will cause an
    //  extra page to be allocated, so to prevent that we subtract off
    //  the header space from our requests.

    sc = psmb->Init(pszName,
                   DOCFILE_SM_SIZE - psmb->GetHdrSize(),        // reserve size
                   INITIALHEAPSIZE - psmb->GetHdrSize(),        // commit size
                   SHAREDMEMBASE,               // base address
                   (SECURITY_DESCRIPTOR *)gs,               // security descriptor
                   TRUE);                       // create if doesn't exist

    // Always pass in TRUE for "fOKToCreate", since passing FALSE
    // will open an existing mapping in read-only mode, but we need read-write

#if WIN32 == 100 || WIN32 > 200
    }
#endif

    if (SUCCEEDED(sc))
    {
#if DBG == 1
        ModifyResLimit(DBRQ_HEAPS, 1);
#endif                
        _cbSize = psmb->GetSize();
        _pbBase = (BYTE *)(psmb->GetBase());
#ifdef MULTIHEAP
        gs_iSharedHeaps++;
        _ulHeapName = ulHeapName;
#endif
        
            if (psmb->Created())
        {
            if (fUnmarshal)  // do not allow creates for unmarshals
            {
                Uninit();
                memErr (EH_Err, STG_E_INVALIDFUNCTION);
            }
            else
            {
                Reset();
            }
        }
        
#ifdef MULTIHEAP
        if (psmb == &g_smb)
            g_ulHeapName = ulHeapName;         // store global heap name
#endif
        PRINTSTATS;
    }
#endif
    
#if defined(MULTIHEAP)
    memDebugOut ((DEB_ITRACE, "Out CSmAllocator::Init sc=%x %x\n",
                  sc, ulHeapName));
#else
    _dmtx.Release();
    memDebugOut ((DEB_ITRACE, "Out CSmAllocator::Init sc=%x\n",
                  sc));
#endif

EH_Err:
    return sc;
}


//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::QueryInterface, public
//
//  Synopsis:   Standard QI
//
//  Arguments:  [iid] - Interface ID
//              [ppvObj] - Object return
//
//  Returns:    Appropriate status code
//
//  History:    29-Mar-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CSmAllocator::QueryInterface(REFIID iid, void **ppvObj)
{
    SCODE sc = S_OK;
    
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::QueryInterface:%p()\n", this));

    if (IsEqualIID(iid, IID_IMalloc) || IsEqualIID(iid, IID_IUnknown))
    {
        *ppvObj = (IMalloc *) this;
        CSmAllocator::AddRef();
    }
    else
        sc = E_NOINTERFACE;
        
    memDebugOut((DEB_ITRACE, "Out CSmAllocator::QueryInterface\n"));

    return sc;
}


//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::AddRef, public
//
//  Synopsis:   Add reference
//
//  History:    29-Mar-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CSmAllocator::AddRef(void)
{
#ifdef MULTIHEAP
    return ++_cRefs;
#else
    return 1;
#endif
}


//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::Release, public
//
//  Synopsis:   Release
//
//  History:    29-Mar-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CSmAllocator::Release(void)
{
#ifdef MULTIHEAP
    ULONG cRefs = --_cRefs;
    if (cRefs <= 0 && this != &g_ErrorSmAllocator)
        delete this;
    return cRefs;
#else
    return 0;
#endif
}

#if !defined(_CHICAGO_)

//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::FindBlock, private
//
//  Synopsis:   Find an appropriately sized block in the heap.
//
//  Arguments:  [cb] -- Size of block required
//
//  Returns:    Pointer to block, NULL on failure
//
//  History:    29-Mar-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

CBlockHeader * CSmAllocator::FindBlock(SIZE_T cb, CBlockHeader **ppbhPrev)
{
    CBlockHeader *pbhCurrent = GetAddress(GetHeader()->GetFirstFree());
    *ppbhPrev = NULL;
    
    while (pbhCurrent != NULL)
    {
        memAssert(IsAligned(pbhCurrent));
        
        if ((pbhCurrent->GetSize() >= cb) && (pbhCurrent->IsFree()))
        {
            memAssert(pbhCurrent->GetSize() < _cbSize);  //MULTIHEAP
            memAssert((BYTE *)pbhCurrent >= _pbBase && 
                      (BYTE *)pbhCurrent < _pbBase + _cbSize);  // MULTIHEAP
            break;
        }
        else
        {
            memAssert (pbhCurrent->GetNext() <= _cbSize);   // MULITHEAP
            *ppbhPrev = pbhCurrent;
            pbhCurrent = GetAddress(pbhCurrent->GetNext());
        }
    }
    return pbhCurrent;
}


//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::Reset, private
//
//  Synopsis:   Reset the heap to its original empty state.
//
//  Returns:    Appropriate status code
//
//  History:    04-Apr-94       PhilipLa        Created
//
//  Notes:
//
//----------------------------------------------------------------------------

inline SCODE CSmAllocator::Reset(void)
{
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::Reset:%p()\n", this));

    CBlockHeader *pbh = (CBlockHeader *)
        (_pbBase + sizeof(CHeapHeader));
            
    memAssert(IsAligned(pbh));
    pbh->SetFree();
    pbh->SetSize(_cbSize - sizeof(CHeapHeader));
    pbh->SetNext(0);

    memAssert((BYTE *)pbh + pbh->GetSize() == _pbBase + _cbSize);
    GetHeader()->SetFirstFree(GetOffset(pbh));
    GetHeader()->SetCompacted();
    GetHeader()->ResetAllocedBlocks();
    GetHeader()->ResetLuid();

#if DBG == 1
    GetHeader()->_ulAllocedBytes = 0;
    GetHeader()->_ulFreeBytes =
        pbh->GetSize() - sizeof(CBlockPreHeader);
    GetHeader()->_ulFreeBlocks = 1;
#endif
        
    memDebugOut((DEB_ITRACE, "Out CSmAllocator::Reset\n"));

    return S_OK;
}

#endif // !defined(_CHICAGO_)


//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::Alloc, public
//
//  Synopsis:   Allocate memory
//
//  Arguments:  [cb] -- Number of bytes to allocate
//
//  Returns:    Pointer to block, NULL if failure
//
//  History:    29-Mar-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(void *) CSmAllocator::Alloc (
        SIZE_T cb )
{
    void *pv = NULL;
#if DBG == 1
    SIZE_T cbSize = cb;
#endif
    
#if !defined(_CHICAGO_)
    CBlockHeader *pbh = NULL;
    CBlockHeader *pbhPrev = NULL;
    SCODE sc;
#endif

    memDebugOut((DEB_ITRACE, "In  CSmAllocator::Alloc:%p(%lu)\n", this, cb));

#if defined(_CHICAGO_)

    pv = HeapAlloc(m_hSharedHeap, 0, cb);

#else // !defined(_CHICAGO_)
#if !defined(MULTIHEAP)
    CLockDfMutex lckdmtx(_dmtx);
#endif
#ifdef MULTIHEAP
    if (_pbBase == NULL)
    {
        memAssert (g_pTaskAllocator != NULL);
        return g_pTaskAllocator->Alloc (cb);
    }
#endif

    Sync();

    //The block must be at least large enough to hold the standard
    //  header (size and free bit) and a pointer to the next block.
    if (cb < sizeof(CBlockHeader) - sizeof(CBlockPreHeader))
    {
        cb = sizeof(CBlockHeader) - sizeof(CBlockPreHeader);
    }
    
    cb = cb + sizeof(CBlockPreHeader);

    //Make cb 8 byte aligned.
    if (cb & 7)
    {
        cb += (8 - (cb & 7));
    }

    memAssert((cb >= CBLOCKMIN) && "Undersized block requested.");
    pbh = FindBlock(cb, &pbhPrev);

    if (pbh == NULL)
    {
        if (!(GetHeader()->IsCompacted()))
        {
            //Do a heap merge and try to allocate again.
            CSmAllocator::HeapMinimize();
            pbh = FindBlock(cb, &pbhPrev);
        }
        
        if (pbh == NULL)
        {
#ifdef MULTIHEAP
            CSharedMemoryBlock *psmb = _psmb;
#else
            CSharedMemoryBlock *psmb = &_smb;
#endif
#if DBG == 1
            ULONG ulOldSize = psmb->GetSize();
#endif
            sc = (ULONG)psmb->Commit(_cbSize + (ULONG)max(cb, MINHEAPGROWTH));
            if (SUCCEEDED(sc))
            {
                //Attach newly committed space to free list.
                CBlockHeader *pbhNew = (CBlockHeader *)
                    (_pbBase + _cbSize);

                _cbSize = psmb->GetSize();

                memAssert((pbhPrev == NULL) || (pbhPrev->GetNext() == 0));
                memAssert(_cbSize > ulOldSize);

                if (pbhPrev != NULL)
                {
                    pbhPrev->SetNext(GetOffset(pbhNew));
                }
                else
                {
                    GetHeader()->SetFirstFree(GetOffset(pbhNew));
                }

                pbhNew->SetNext(0);
                pbhNew->SetSize(max(cb, MINHEAPGROWTH));
                pbhNew->SetFree();

                
                memAssert((BYTE *)pbhNew + pbhNew->GetSize() ==
                          _pbBase + _cbSize);
                
#if DBG == 1
                GetHeader()->_ulFreeBytes +=
                    pbhNew->GetSize() - sizeof(CBlockPreHeader);
                GetHeader()->_ulFreeBlocks += 1;
#endif
                
                pbh = pbhNew;
            }
#if DBG == 1
            else
            {
                memDebugOut((DEB_ERROR, "Can't grow shared memory\n"));
                PrintAllocatedBlocks();
            }
#endif            
        }
    }

    if (pbh != NULL)
    {
        //Allocate the found block.
        if ((pbh->GetSize() > cb) &&
            (pbh->GetSize() - cb > CBLOCKMIN))
        {
            //Split an existing block.  No free list update required.
            
            CBlockHeader *pbhNew =
                (CBlockHeader *)((BYTE *)pbh + (pbh->GetSize() - cb));

            pbhNew->SetSize(cb);
            pbhNew->ResetFree();
            pbhNew->SetNext(0);
            
            pbh->SetSize(pbh->GetSize() - cb);
#if DBG == 1
            cbSize = cb;
            GetHeader()->_ulAllocedBytes += (cb - sizeof(CBlockPreHeader));
            //The number of available free bytes decreases by the number
            //  of bytes allocated
            GetHeader()->_ulFreeBytes -= cb;
#endif
            memAssert(IsAligned(pbhNew));
            memAssert(IsAligned(pbh));
            
            pbh = pbhNew;
        }
        else
        {
            //Use an entire block.  Update free list appropriately.
            memAssert(IsAligned(pbh));
            pbh->ResetFree();
            if (pbhPrev != NULL)
            {
                pbhPrev->SetNext(pbh->GetNext());
            }
            else
            {
                GetHeader()->SetFirstFree(pbh->GetNext());
            }
#if DBG == 1
            cbSize = pbh->GetSize() - sizeof(CBlockPreHeader);
            GetHeader()->_ulAllocedBytes += cbSize;
            GetHeader()->_ulFreeBytes -= cbSize;
            GetHeader()->_ulFreeBlocks--;
#endif
            pbh->SetNext(0);
        }
    }

    if (pbh != NULL)
    {
        pv = (BYTE *)pbh + sizeof(CBlockPreHeader);
        GetHeader()->IncrementAllocedBlocks();
    }
#endif // !defined(_CHICAGO_)

    memDebugOut((DEB_ITRACE, "Out CSmAllocator::Alloc=> %p\n", pv));

#if !defined(_CHICAGO_)
    memAssert(IsAligned(pv));
#endif // !defined(_CHICAGO_)

    PRINTSTATS;
    
#if DBG == 1 
    if (pv == NULL)
    {
#if defined(_CHICAGO_)
        memDebugOut((DEB_ERROR,
                     "Failed allocation of %lu bytes.\n",
                     cb));
#else  // !defined(_CHICAGO_)
        memDebugOut((DEB_ERROR,
                     "Failed allocation of %lu bytes.  Heap size is %lu\n",
                     cb,
                     _cbSize));
#endif // !defined(_CHICAGO_)
    }
    else
    {
        //Allocated some bytes.  Record this for leak tracking.
        ModifyResLimit(DBRQ_MEMORY_ALLOCATED, (LONG)pbh->GetSize());
    }
#endif
    
    return pv;
}


//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::Realloc, public
//
//  Synopsis:   Resize the block given
//
//  Arguments:  [pv] -- Pointer to block to realloc
//              [cb] -- New size for block
//
//  Returns:    Pointer to new block, NULL if failure
//
//  History:    29-Mar-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(void *) CSmAllocator::Realloc(
        void *pv,
        SIZE_T cb )
{
    void *pvNew = NULL;
#ifdef FULLIMPL
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::Realloc:%p()\n", this));

#if defined(_CHICAGO_)

    pvNew = HeapReAlloc(m_hSharedHeap, 0, pv, cb);

#else
#if !defined(MULTIHEAP)
    CLockDfMutex lckdmtx(_dmtx);
#endif
#ifdef MULTIHEAP
    if (_pbBase == NULL)
    {
        memAssert (g_pTaskAllocator != NULL);
        return g_pTaskAllocator->Realloc (pv, cb);
    }
#endif

    if ((pv != NULL) && (cb == 0))
    {
        CSmAllocator::Free(pv);
        return NULL;
    }

    pvNew = CSmAllocator::Alloc(cb);
    if (pvNew != NULL && pv != NULL)
    {
        //Copy contents
        memcpy(pvNew, pv, min(cb, CSmAllocator::GetSize(pv)));
        CSmAllocator::Free(pv);
    }
#endif
    
    memDebugOut((DEB_ITRACE, "Out CSmAllocator::Realloc\n"));
#endif
    PRINTSTATS;
    
    return pvNew;
}

//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::DoFree, private
//
//  Synopsis:   Free a memory block
//
//  Arguments:  [pv] -- Pointer to block to free
//
//  Returns:    void
//
//  History:    26-Jul-95       SusiA   Created
//
//----------------------------------------------------------------------------
inline void CSmAllocator::DoFree(void *pv)
{
#ifdef MULTIHEAP
    if (_pbBase == NULL)
    {
        memAssert (g_pTaskAllocator != NULL);
        g_pTaskAllocator->Free (pv);
        return;
    }
#endif
   memDebugOut((DEB_ITRACE, "In  CSmAllocator::DoFree:%p(%p)\n", this, pv));
#if DBG == 1
   SSIZE_T cbSize = 0;
#endif   

#if defined(_CHICAGO_)

    if (pv != NULL)
    {
#if DBG == 1
        cbSize = HeapSize(m_hSharedHeap, 0, pv);
#endif        
        HeapFree(m_hSharedHeap, 0, pv);
    }

#else

    Sync();
    
    if (pv != NULL)
    {
        CBlockHeader *pbh = (CBlockHeader *)
            ((BYTE *)pv - sizeof(CBlockPreHeader));
#ifdef MULTIHEAP
        SIZE_T ulSize = pbh->GetSize();  // temporary to hold size for debug
#if DBG == 1
        cbSize = ulSize;
#endif        
#endif

        memAssert(IsAligned(pbh));
        memAssert((BYTE*)pbh >= _pbBase && 
                  (BYTE*)pbh < _pbBase + _cbSize);      // MULTIHEAP
        pbh->SetFree();
        pbh->SetNext(GetHeader()->GetFirstFree());

        GetHeader()->SetFirstFree(GetOffset(pbh));
        GetHeader()->ResetCompacted();
        if (GetHeader()->DecrementAllocedBlocks() == 0)
        {
#ifdef MULTIHEAP
            Uninit();
#else
            
            Reset();
#endif
        }
        
#if DBG == 1
        else
        {
            GetHeader()->_ulAllocedBytes -=
                (pbh->GetSize() - sizeof(CBlockPreHeader));
            memAssert (GetHeader()->_ulAllocedBytes <= _cbSize);
            GetHeader()->_ulFreeBytes +=
                (pbh->GetSize() - sizeof(CBlockPreHeader));
            GetHeader()->_ulFreeBlocks++;
        }
#endif
#ifdef MULTIHEAP
        memDebugOut((DEB_ITRACE, "Out CSmAllocator::DoFree.  Freed %lu\n",
                     ulSize));  // don't access shared memory
#else
        memDebugOut((DEB_ITRACE, "Out CSmAllocator::DoFree.  Freed %lu\n",
                     pbh->GetSize()));
#endif
    }
#endif
#if !defined(MULTIHEAP)
    // the shared heap may have been unmapped, mustn't read it now
    PRINTSTATS;
#endif
#if DBG == 1
   //Freed some bytes, so record that.
   ModifyResLimit(DBRQ_MEMORY_ALLOCATED, (LONG)-cbSize);
#endif   
   
}
//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::Free, public
//
//  Synopsis:   Free a memory block
//
//  Arguments:  [pv] -- Pointer to block to free
//
//  Returns:    void
//
//  History:    29-Mar-94       PhilipLa        Created
//              26-Jul-95       SusiA           Moved bulk of work to DoFree to
//                                              share code between Free and
//                                              FreeNoMutex
//
//----------------------------------------------------------------------------

STDMETHODIMP_(void) CSmAllocator::Free(void *pv)
{
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::Free:%p(%p)\n", this, pv));
    
#if !defined(_CHICAGO_)
#if !defined(MULTIHEAP)
    CLockDfMutex lckdmtx(_dmtx);
#endif
#endif
    DoFree(pv);

}
//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::FreeNoMutex, public
//
//  Synopsis:   Free a memory block without first aquiring the mutex.
//              This function is equivalent to Free above, except that is does 
//              not attempt to first aquire the mutex.  It should be used OLNY 
//              when the calling function guarantees to already have the mutex.         
//    
//
//  Arguments:  [pv] -- Pointer to block to free
//
//  Returns:    void
//
//  History:    19-Jul-95       SusiA           Created
//
//----------------------------------------------------------------------------

void CSmAllocator::FreeNoMutex(void *pv)
{
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::FreeNoMutex:%p(%p)\n", this, pv));
    
#if !defined(_CHICAGO_)
#if !defined(MULTIHEAP)
   //ensure we already have the mutex
    memAssert(_dmtx.HaveMutex());
#endif
#endif
    DoFree(pv);

}


//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::GetSize, public
//
//  Synopsis:   Return the size of the given block
//
//  Arguments:  [pv] -- Block to get size of
//
//  Returns:    Size of block pointer to by pv
//
//  History:    29-Mar-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(SIZE_T) CSmAllocator::GetSize(void * pv)
{
#if !defined(_CHICAGO_)
#if !defined(MULTIHEAP)
    CLockDfMutex lckdmtx(_dmtx);
#endif
#endif
#ifdef MULTIHEAP
    if (_pbBase == NULL)
    {
        memAssert (g_pTaskAllocator != NULL);
        return g_pTaskAllocator->GetSize (pv);
    }
#endif
    
    Sync();
    
    SIZE_T ulSize = (SIZE_T)-1;
#ifdef FULLIMPL
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::GetSize:%p()\n", this));
    if (pv != NULL)
    {
#if defined(_CHICAGO_)
        ulSize = HeapSize(m_hSharedHeap, 0, pv);
#else
        CBlockHeader *pbh;
        pbh = (CBlockHeader *)((BYTE *)pv - sizeof(CBlockPreHeader));
        
        ulSize = pbh->GetSize() - sizeof(CBlockPreHeader);
#endif
    }
        
    memDebugOut((DEB_ITRACE, "Out CSmAllocator::GetSize\n"));
#endif
    return ulSize;
}

//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::DidAlloc, public
//
//  Synopsis:   Return '1' if this heap allocated pointer at pv
//
//  Arguments:  [pv] -- Pointer to block
//
//  Returns:    '1' == This heap allocated block.
//              '0' == This heap did not allocate block.
//              '-1' == Could not determine if this heap allocated block.
//
//  History:    29-Mar-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(int) CSmAllocator::DidAlloc(void FAR * pv)
{
#ifdef MULTIHEAP
    if (_pbBase == NULL)
    {
        memAssert (g_pTaskAllocator != NULL);
        return g_pTaskAllocator->DidAlloc (pv);
    }
#endif
    int i = -1;
#ifdef FULLIMPL
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::DidAlloc:%p()\n", this));
#if defined(_CHICAGO_)
    if (HeapValidate(m_hSharedHeap, 0, pv))
    {
       i = 1;
    }
    else
    {
       i = 0;
    }
#else  // !defined(_CHICAGO_)
#if !defined(MULTIHEAP)
    CLockDfMutex lckdmtx(_dmtx);
#endif

    i = ((BYTE *)pv >= _pbBase) && ((BYTE *)pv <= (_pbBase + _cbSize));
#endif // !defined(_CHICAGO_)
    memDebugOut((DEB_ITRACE, "Out CSmAllocator::DidAlloc\n"));
#endif
    return i;
}

//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::HeapMinimize, public
//
//  Synopsis:   Minimize the heap
//
//  Arguments:  None.
//
//  Returns:    void.
//
//  History:    29-Mar-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

STDMETHODIMP_(void) CSmAllocator::HeapMinimize(void)
{
#if !defined(_CHICAGO_)
#if !defined(MULTIHEAP)
    CLockDfMutex lckdmtx(_dmtx);
#endif
#endif
#ifdef MULTIHEAP
    if (_pbBase == NULL)
    {
        memAssert (g_pTaskAllocator != NULL);
        g_pTaskAllocator->HeapMinimize ();
        return;
    }
#endif
    
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::HeapMinimize:%p()\n", this));

    PRINTSTATS;
    
#if defined(_CHICAGO_)

    HeapCompact(m_hSharedHeap, 0);

#else  // !defined(_CHICAGO_)

    CBlockHeader *pbhCurrent;
    CBlockHeader *pbhLast = NULL;
    BYTE *pbEnd = _pbBase + _cbSize;

#if DBG == 1
    PrintFreeBlocks();
    GetHeader()->_ulAllocedBytes = 0;
    GetHeader()->_ulFreeBytes = 0;
    GetHeader()->_ulFreeBlocks = 0;
#endif
    
    pbhCurrent = (CBlockHeader *)(_pbBase + sizeof(CHeapHeader));

    while ((BYTE *)pbhCurrent < pbEnd)
    {
        memAssert(IsAligned(pbhCurrent));
        memAssert((pbhCurrent->GetSize() != 0) &&
                  "Zero size block found.");
        if (pbhCurrent->IsFree())
        {
            //Check last block.  If adjacent, merge them.  If not,
            //  update pbhNext.
            
            if (pbhLast == NULL)
            {
                GetHeader()->SetFirstFree(GetOffset(pbhCurrent));
#if DBG == 1
                GetHeader()->_ulFreeBlocks = 1;
#endif
            }
            else
            {
                if (pbhLast->GetSize() + GetOffset(pbhLast) ==
                    GetOffset(pbhCurrent))
                {
                    //Merge the blocks.
                    pbhLast->SetSize(pbhLast->GetSize() +
                                     pbhCurrent->GetSize());
                    pbhCurrent = pbhLast;
                }
                else
                {
#if DBG == 1
                    GetHeader()->_ulFreeBytes +=
                        (pbhLast->GetSize() - sizeof(CBlockPreHeader));
                    GetHeader()->_ulFreeBlocks++;
#endif
                    pbhLast->SetNext(GetOffset(pbhCurrent));
                }
            }
            pbhLast = pbhCurrent;
        }
#if DBG == 1
        else
        {
            GetHeader()->_ulAllocedBytes +=
                (pbhCurrent->GetSize() - sizeof(CBlockPreHeader));
        }
#endif
        //Move to next block.
        pbhCurrent =
            (CBlockHeader *)((BYTE *)pbhCurrent + pbhCurrent->GetSize());
    }

    if (pbhLast != NULL)
    {
                    
#if DBG == 1
        GetHeader()->_ulFreeBytes +=
            (pbhLast->GetSize() - sizeof(CBlockPreHeader));
#endif
        pbhLast->SetNext(0);
    }
    else
    {
        GetHeader()->SetFirstFree(0);
    }

    GetHeader()->SetCompacted();
#if DBG == 1
    PrintFreeBlocks();
#endif

#endif // !defined(_CHICAGO_)
    
    memDebugOut((DEB_ITRACE, "Out CSmAllocator::HeapMinimize\n"));
    
    PRINTSTATS;
}

#if !defined(_CHICAGO_)
#if DBG == 1
//+---------------------------------------------------------------------------
//
//  Member:     CSmAllocator::PrintFreeBlocks, private
//
//  Synopsis:   Debug code to print sizes of free blocks
//
//  History:    25-Apr-94       PhilipLa        Created
//
//----------------------------------------------------------------------------

void CSmAllocator::PrintFreeBlocks(void)
{
    CBlockHeader *pbhCurrent = GetAddress(GetHeader()->GetFirstFree());

    memDebugOut((DEB_PRINT, "There are %lu total free blocks\n",
                 GetHeader()->_ulFreeBlocks));
    
    while (pbhCurrent != NULL)
    {
        memDebugOut((DEB_PRINT, "Free block %p has size %lu\n", pbhCurrent,
                     pbhCurrent->GetSize()));
        pbhCurrent = GetAddress(pbhCurrent->GetNext());
    }
}
#endif

#ifdef MULTIHEAP
#if DBG == 1
//+---------------------------------------------------------------------------
//
//  Member: CSmAllocator::PrintAllocatedBlocks, private
//
//  Synopsis:   Debug code to find allocated block(s) that leaked
//
//  History:    25-Nov-95   HenryLee    Created
//
//----------------------------------------------------------------------------
void CSmAllocator::PrintAllocatedBlocks(void)
{
    CBlockHeader *pbhCurrent;
    CBlockHeader *pbhLast = NULL;
    BYTE *pbEnd = _pbBase + _cbSize;
    ULONG *pul;

    if (_psmb != NULL)
    {
        pbhCurrent = (CBlockHeader *)(_pbBase + sizeof(CHeapHeader));

        while ((BYTE *)pbhCurrent < pbEnd)
        {
            memAssert(IsAligned(pbhCurrent));
            memAssert((pbhCurrent->GetSize() != 0) && "Zero size block found.");
            if (!pbhCurrent->IsFree())
            {
                pul = (ULONG *)((BYTE *)pbhCurrent + sizeof(CBlockPreHeader));
                memDebugOut((DEB_ERROR, "Allocated Block %p %8x %8x (size %lu)\n",
                             pul, *pul, *(pul+1), pbhCurrent->GetSize()));
                
            }
            else
            {
                pul = (ULONG *)pbhCurrent;
                memDebugOut((DEB_ERROR, "Free      Block %p %8x %8x (size %lu)\n",
                             pul, *pul, *(pul+1), pbhCurrent->GetSize()));
            }
            pbhCurrent =
                (CBlockHeader *)((BYTE *)pbhCurrent + pbhCurrent->GetSize());
        }
    }
}
#endif // DBG == 1

//+---------------------------------------------------------------------------
//
//  Member: CSmAllocator::SetState
//
//  Synopsis:   replace thread local state by PerContext state
//
//  History:    20-Nov-95  Henrylee    Created
//
//----------------------------------------------------------------------------
void CSmAllocator::SetState (CSharedMemoryBlock *psmb, BYTE * pbBase,
                             ULONG ulHeapName, CPerContext ** ppcPrev,
                             CPerContext *ppcOwner)
{
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::SetState(%p, %p, %lx, %p, %p, %p) (this == %p)\n", psmb, pbBase, ulHeapName, ppcPrev, ppcOwner, _ppcOwner, this));

    _psmb = psmb;
    _pbBase = pbBase;
    _cbSize = (_psmb) ? _psmb->GetSize() : 0;
    _ulHeapName = ulHeapName;
    DFBASEPTR = _pbBase;

    if (ppcPrev != NULL)
        *ppcPrev = _ppcOwner;
    _ppcOwner = ppcOwner;

//    memAssert (g_smAllocator.GetBase() == DFBASEPTR);
    memDebugOut((DEB_ITRACE, "Out CSmAllocator::SetState()\n"));
}

//+---------------------------------------------------------------------------
//
//  Member: CSmAllocator::GetState
//
//  Synopsis:   retrive thread local allocator state into percontext
//
//  History:    20-Nov-95  Henrylee    Created
//
//----------------------------------------------------------------------------
void CSmAllocator::GetState (CSharedMemoryBlock **ppsmb,
                             BYTE ** ppbBase, ULONG *pulHeapName)
{
    *ppsmb = _psmb;
    *ppbBase = _pbBase;
    *pulHeapName = _ulHeapName;
}

//+---------------------------------------------------------------------------
//
//  Member: CSmAllocator::Uninit
//
//  Synopsis:   unmap the shared memory region
//
//  History:    20-Nov-95  Henrylee    Created
//
//----------------------------------------------------------------------------
SCODE CSmAllocator::Uninit ()
{
    memDebugOut((DEB_ITRACE, "In  CSmAllocator::Uninit\n"));
    if (_psmb != NULL)
    {
        if (_psmb != &g_smb)
        {
            // This is last block in the heap, so we can close the heap
            // now.  There must be no shared heap accesses after this.
            delete _psmb;
#if DBG == 1
            ModifyResLimit(DBRQ_HEAPS, -1);
#endif            
            gs_iSharedHeaps--;
        }
        else
        {
            if (GetHeader()->GetAllocedBlocks() == 0)
                Reset();           // for g_smb
        }
        _psmb = NULL;
    }
    _pbBase = NULL;
    memDebugOut((DEB_ITRACE, "Out CSmAllocator::Uninit %x\n", _ulHeapName));

    _ulHeapName = 0;

    return S_OK;
}

#endif // MULTIHEAP

#endif // !defined(_CHICAGO_)