#include "pch.h"
#pragma hdrstop
#include "modlist.h"

BOOL
CModuleList::FDumpToString (
    OUT PSTR pszBuf,
    IN OUT ULONG* pcchBuf) const
{
    const_iterator iter;
    const CModule* pMod;
    ULONG cch;
    ULONG cchIn;
    BOOL fFirstTime;

    Assert (this);
    Assert (pcchBuf);

    cch = 0;
    cchIn = *pcchBuf;

    for (iter = begin(), fFirstTime = TRUE; iter != end(); iter++)
    {
        if (!fFirstTime)
        {
            cch += 2;
            if (pszBuf && (cch <= cchIn))
            {
                strcat (pszBuf, "->");
            }
        }
        else
        {
            fFirstTime = FALSE;
            if (pszBuf && (cch <= cchIn))
            {
                *pszBuf = 0;
            }
        }

        pMod = *iter;
        Assert (pMod);

        cch += strlen (pMod->m_pszFileName);
        if (pszBuf && (cch <= cchIn))
        {
            strcat (pszBuf, pMod->m_pszFileName);
        }
    }

    // If we ran out of room, erase the partial stuff we wrote.
    //
    if (pszBuf && cchIn && (cch > cchIn))
    {
        *pszBuf = 0;
    }

    *pcchBuf = cch;
    return cch <= cchIn;
}

BOOL
CModuleList::FIsSameModuleListAs (
    IN const CModuleList* pOtherList) const
{
    UINT unThisSize;
    UINT unOtherSize;
    UINT cb;

    Assert (this);
    Assert (pOtherList);

    unThisSize = this->size();
    unOtherSize = pOtherList->size();

    if ((0 == unThisSize) || (0 == unOtherSize) || (unThisSize != unOtherSize))
    {
        return FALSE;
    }

    // Sizes are non-zero and equal.  Compare the data.
    //
    cb = (BYTE*)(end()) - (BYTE*)(begin());
    Assert (cb == unThisSize * sizeof(CModule*));

    return (0 == memcmp (
                    (BYTE*)(this->begin()),
                    (BYTE*)(pOtherList->begin()),
                    cb));
}

VOID
CModuleList::GrowBufferIfNeeded ()
{
    if (m_Granularity && (size() == capacity()))
    {
        //fprintf(stderr, "growing module list buffer\n");

        __try
        {
            reserve (size() + m_Granularity);
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
            ;
        }
    }
}

HRESULT
CModuleList::HrInsertModule (
    IN const CModule* pMod,
    IN DWORD dwFlags /* INS_FLAGS */)
{
    HRESULT hr;

    Assert (this);
    Assert (pMod);
    Assert (dwFlags);
    Assert (dwFlags & (INS_ASSERT_IF_DUP | INS_IGNORE_IF_DUP));
    Assert (!(dwFlags & (INS_SORTED | INS_NON_SORTED)));
    Assert (dwFlags & (INS_APPEND | INS_INSERT));

    if (FLinearFindModuleByPointer (pMod))
    {
        Assert (dwFlags & INS_IGNORE_IF_DUP);
        return S_OK;
    }

    GrowBufferIfNeeded ();

    __try
    {
        iterator InsertPosition = begin();

        if (dwFlags & INS_APPEND)
        {
            InsertPosition = end();
        }

        insert (InsertPosition, (CModule*)pMod);
        hr = S_OK;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        hr = E_OUTOFMEMORY;
    }

    return hr;
}

HRESULT
CModuleList::HrInsertNewModule (
    IN PCSTR pszFileName,
    IN ULONG cbFileSize,
    IN DWORD dwFlags, /* INS_FLAGS */
    OUT CModule** ppMod)
{
    HRESULT hr;
    iterator InsertPosition = NULL;
    CModule* pMod;
    CHAR szLowerCaseFileName [MAX_PATH];

    Assert (dwFlags);
    Assert (dwFlags & (INS_ASSERT_IF_DUP | INS_IGNORE_IF_DUP));
    Assert (dwFlags & INS_SORTED);
    Assert (!(dwFlags & (INS_APPEND | INS_INSERT)));

    GrowBufferIfNeeded ();

    hr = S_OK;

    strcpy (szLowerCaseFileName, pszFileName);
    _strlwr (szLowerCaseFileName);

    pMod = PBinarySearchModuleByName (szLowerCaseFileName, &InsertPosition);

    if (!pMod)
    {
        Assert (!PLinearFindModuleByName (szLowerCaseFileName));

        hr = CModule::HrCreateInstance (szLowerCaseFileName, cbFileSize, &pMod);

        if (S_OK == hr)
        {
            if (dwFlags & INS_NON_SORTED)
            {
                InsertPosition = (dwFlags & INS_APPEND) ? end() : begin();
            }
            __try
            {
                Assert (InsertPosition);
                insert (InsertPosition, pMod);
                Assert (S_OK == hr);

                DbgVerifySorted();
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                hr = E_OUTOFMEMORY;
                delete pMod;
                pMod = NULL;
            }
        }
    }
    else
    {
        Assert (dwFlags & INS_IGNORE_IF_DUP);

        // Update the file size of the module if we didn't have it
        // when it was first created.
        //
        if (0 == pMod->m_cbFileSize)
        {
            pMod->m_cbFileSize = cbFileSize;
        }
    }

    *ppMod = pMod;

    return hr;
}

CModule*
CModuleList::PBinarySearchModuleByName (
    IN PCSTR pszFileName,
    OUT CModuleList::iterator* pInsertPosition OPTIONAL)
{
    // Find the module using a binary search.
    //
    if (size())
    {
        LONG Lo;
        LONG Hi;
        LONG Mid;
        INT Result;
        CModule* pScan;

        Lo = 0;
        Hi = size() - 1;

        while (Hi >= Lo)
        {
            Mid = (Lo + Hi) / 2;
            pScan = at(Mid);

            Result = strcmp (pszFileName, pScan->m_pszFileName);

            if (Result < 0)
            {
                Hi = Mid - 1;
            }
            else if (Result > 0)
            {
                Lo = Mid + 1;
            }
            else
            {
                return pScan;
            }
        }

        // If we make it to here, the module was not found.
        //
        if (pInsertPosition)
        {
            *pInsertPosition = begin() + Lo;
            Assert (*pInsertPosition >= begin());
            Assert (*pInsertPosition <= end());
        }
    }
    else if (pInsertPosition)
    {
        // Empty collection.  Insert position is at the beginning.
        //
        *pInsertPosition = begin();
    }

    return NULL;
}

CModule*
CModuleList::PLinearFindModuleByName (
    IN PCSTR pszFileName)
{
    const_iterator iter;
    CModule* pScan;

    for (iter = begin(); iter != end(); iter++)
    {
        pScan = *iter;
        Assert (pScan);

        if (0 == strcmp(pszFileName, pScan->m_pszFileName))
        {
            return pScan;
        }
    }
    return NULL;
}

CModule*
CModuleList::RemoveLastModule ()
{
    CModule* pMod = NULL;
    if (size() > 0)
    {
        pMod = back();
        AssertH(pMod);
        pop_back();
    }
    return pMod;
}

#if DBG
VOID
CModuleList::DbgVerifySorted ()
{
    const_iterator iter;
    CModule* pScan;
    CModule* pPrev = NULL;

    if (size() > 1)
    {
        for (pPrev = *begin(), iter = begin() + 1; iter != end(); iter++)
        {
            pScan = *iter;
            Assert (pScan);

            Assert (strcmp(pPrev->m_pszFileName, pScan->m_pszFileName) < 0);

            pPrev = pScan;
        }
    }
}
#endif