/***
*chsyheap.cpp - RTC support
*
*       Copyright (c) 1998-2001, Microsoft Corporation. All rights reserved.
*
*
*Revision History:
*       07-28-98  JWM   Module incorporated into CRTs (from KFrei)
*       05-11-99  KBF   Error if RTC support define not enabled
*       05-25-99  KBF   Renamed - _RTC_SimpleHeap instead of CheesyHeap
*       05-26-99  KBF   Removed RTCl and RTCv, added _RTC_ADVMEM stuff
*
****/

#ifndef _RTC
#error  RunTime Check support not enabled!
#endif

#include "rtcpriv.h"

#ifdef _RTC_ADVMEM

// This is my 'Cheesy Heap' implementation...

/* Here are the sizes that I need:

BinaryNode              3 DWORDS - use heap4
BinaryTree              1 DWORD  - use heap2
Container               2 DWORDS - use heap2
BreakPoint              2 DWORDS - use heap2
HashTable<HeapBlock>    2 DWORDS - use heap2
HeapBlock               6 DWORDS - use heap8

Container[] - short term...
CallSite[]  - permanent
HeapBlock[] - permanent

*/

_RTC_SimpleHeap *_RTC_heap2 = 0;
_RTC_SimpleHeap *_RTC_heap4 = 0;
_RTC_SimpleHeap *_RTC_heap8 = 0;

void *
_RTC_SimpleHeap::operator new(unsigned) throw()
{
    void *res = VirtualAlloc(NULL, ALLOC_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#ifdef _RTC_SHADOW
    if (shadow)
        _RTC_MSCommitRange((memptr)res, ALLOC_SIZE, IDX_STATE_ILLEGAL);
#endif
    return res;
}    

void 
_RTC_SimpleHeap::operator delete(void *addr) throw()
{
    VirtualFree(addr, 0, MEM_RELEASE);
#ifdef _RTC_SHADOW
    if (shadow)
        _RTC_MSDecommitRange((memptr)addr, ALLOC_SIZE);
#endif
}

_RTC_SimpleHeap::_RTC_SimpleHeap(unsigned blockSize)  throw()
{
    // Flag it as the only item in the heap
    head.next = 0;
    head.inf.top.nxtFree = 0;

    // Align the block size
    head.inf.top.wordSize = 8;
    blockSize = (blockSize - 1) >> 3;
    
    while (blockSize) {
        blockSize >>= 1;
        head.inf.top.wordSize <<= 1;
    }

    // Build up the free-list
    head.free = (FreeList*)(((unsigned)&head) + 
                           ((head.inf.top.wordSize < sizeof(HeapNode)) ?
                                sizeof(HeapNode) :
                                head.inf.top.wordSize));
    FreeList *t = head.free;
    while (((unsigned)t) + head.inf.top.wordSize < ((unsigned)&head) + ALLOC_SIZE)
    {
        t->next = (FreeList*)(((unsigned)t) + head.inf.top.wordSize);
        t = t->next;
    }
    t->next = 0;
}

_RTC_SimpleHeap::~_RTC_SimpleHeap() throw()
{
    // Free all sections that we have allocated
    HeapNode *n, *c = head.next;
    while(c) {
        n = c->next;
        _RTC_SimpleHeap::operator delete(c);
        c = n;
    }
    // the 'head' page will be handled by delete
}

void *
_RTC_SimpleHeap::alloc() throw()
{
    void *res;

    // If there's a free item, remove it from the list
    // And decrement the free count for it's parent page
    
    if (head.free) 
    {
        // There's a free block on the first page
        res = head.free;
        head.free = head.free->next;

        // Since it's on the top page, there's no free-count to update,
        // And it ain't on no stinkin' free-list
        
    } else if (head.inf.top.nxtFree)
    {
        // There's a free block on some page
        HeapNode *n = head.inf.top.nxtFree;
        
        res = n->free;
        n->free = n->free->next;
        n->inf.nontop.freeCount--;

        if (!n->free)
        {
            // This page is now full, so it must be removed from the freelist
            for (n = head.next; n && !n->free; n = n->next) {}
            // Now the nxtFree pointer is either null (indicating a full heap)
            // or it's pointing to a page that has free nodes
            head.inf.top.nxtFree = n;
        }
        
    } else 
    {
        // No pages have any free blocks
        // Get a new page, and add it to the list
        HeapNode *n = (HeapNode *)_RTC_SimpleHeap::operator new(0);
        
        // Count the number of free nodes
        n->inf.nontop.freeCount = 
            (ALLOC_SIZE - sizeof(HeapNode)) / head.inf.top.wordSize - 1;
   
        res = (void *)(((unsigned)n) + 
                        ((head.inf.top.wordSize < sizeof(HeapNode)) ?
                            sizeof(HeapNode) :
                            head.inf.top.wordSize));
        
        // Build the free-list for this node
        FreeList *f;
        for (f = n->free = (FreeList*)(((unsigned)res) + head.inf.top.wordSize);
             ((unsigned)f) + head.inf.top.wordSize < ((unsigned)n) + ALLOC_SIZE;
             f = f->next)
            f->next = (FreeList*)(((unsigned)f) + head.inf.top.wordSize);
        
        f->next = 0;
             
        // Stick it in the page list
        n->next = head.next;
        n->inf.nontop.prev = &head;
        head.next = n;
        
        // Flag this as a page with free stuff on it...
        head.inf.top.nxtFree = n;
    }
    return res;
}

void
_RTC_SimpleHeap::free(void *addr) throw()
{
    // Get the heap node for this address
    HeapNode *n = (HeapNode *)(((unsigned)addr) & ~(ALLOC_SIZE - 1));

    // Stick this sucker back in the free list
    FreeList *f = (FreeList *)addr;
    f->next = n->free;
    n->free = f;

    if (n == &head)
        // If this is in the head node, just return...
        return;
    
    if (++n->inf.nontop.freeCount == 
        (ALLOC_SIZE - sizeof(HeapNode)) / head.inf.top.wordSize)
    {
        // This page is free
        if (head.inf.top.freePage)
        {
            // There's already another free page, go ahead and free this one
            
            // (there's always a previous node)
            n->inf.nontop.prev->next = n->next;
            if (n->next)
                n->next->inf.nontop.prev = n->inf.nontop.prev;
            _RTC_SimpleHeap::operator delete(n);
                
            if (head.inf.top.nxtFree == n)
            {   
                // This was the free page
                // find a page with some free nodes on it...
                for (n = head.next; !n->free; n = n->next) {}
                // ASSERT(n)
                // If n is null, we're in some serious trouble...
                head.inf.top.nxtFree = n;
            }
            // If it wasn't the free page, we're just fine...
        } else
        { 
            // flag the freePages to say we have a 100% free page
            head.inf.top.freePage = true;

            if (head.inf.top.nxtFree == n)
            {
                // If this is the free page,
                // try to find another page with some free nodes
                HeapNode *t;
                for (t = head.next; t && (!t->free || t == n) ; t = t->next) {}

                // if there was a different page with some nodes, pick it
                head.inf.top.nxtFree = t ? t : n;
            }
        }
    } else
        // This page isn't empty, so just set it as the next free
        head.inf.top.nxtFree = n;
}

#endif // _RTC_ADVMEM