/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    memory.c

Abstract:

    This module provides all the memory management functions 

Author:

    Krishna Ganugapati (KrishnaG) 03-Feb-1994

Revision History:

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <imagehlp.h>
#include <stdio.h>
#include <stdlib.h>
#include "debug.h"
#include "memory.h"
#include "symhelp.h"

//#define WORD_ALIGN_DOWN(addr) ((LPBYTE)((DWORD)addr &= ~1))
#define DWORD_ALIGN_UP(size) ((size+3)&~3)

DWORD
MemSizeOri(
   LPVOID pMem
);

int
UnicodeToAnsiString(
    PCWSTR pszUnicode,
    PSTR pszAnsi
    )

/*++

Routine Description:

    Convert a Unicode string to ansi. If the same string is passed in to the
    result string pszAnsi, it will use the same blob of memory.

Arguments:

    pszUnicode - the unicode string to be converted to an ansi string
    pszAnsi  - the result ansi string

Return Value:

--*/

{
    PSTR  pszTemp = NULL;
    int   rc = 0;
    DWORD dwLength = 0;

    dwLength = wcslen(pszUnicode) + 1;

    //
    // Unfortunately, WideCharToMultiByte doesn't do conversion in place,
    // so allocate a temporary buffer, which we can then copy:
    //

    if(pszAnsi == (PSTR)pszUnicode) {
        pszTemp = (PSTR)MemAlloc_E(dwLength);
        if (!pszTemp) {
            return rc;
        }
        pszAnsi = pszTemp;
    }

    if(pszAnsi) {
        rc = WideCharToMultiByte( CP_ACP,
                                  0,
                                  pszUnicode,
                                  dwLength,
                                  pszAnsi,
                                  dwLength,
                                  NULL,
                                  NULL );
    }

    //
    // If szTemp is non-null, we must copy the resulting string
    // so that it looks as if we did it in place:
    //
    if( pszTemp && ( rc > 0 ) ) {
        pszAnsi = (PSTR)pszUnicode;
        strcpy( pszAnsi, pszTemp );
        MemFree( pszTemp );
    }

    return rc;
}

PSTR
AllocateAnsiString(
    PCWSTR  pszUnicodeString
    )

/*++

Routine Description:

    Allocate an Ansi string with a unicode string as input

Arguments:

    pszUnicodeString - the unicode string to be converted to an ansi string

Return Value:

--*/

{
    PSTR pszAnsiString = NULL;
    int rc = 0;

    ASSERT(pszUnicodeString);

    pszAnsiString = (PSTR)MemAlloc(wcslen(pszUnicodeString)+1);

    if (pszAnsiString) {
        rc = UnicodeToAnsiString(
                pszUnicodeString,
                pszAnsiString
                );
    }

    if (rc>0) {
        return pszAnsiString;
    }

    if (pszAnsiString) {
        MemFree(pszAnsiString);
    }

    return NULL;
}

int
AnsiToUnicodeString(
    PCSTR pszAnsi,
    PWSTR pszUnicode
    )

/*++

Routine Description:

    Convert an ansi string to unicode. An output string of enough size
    is expected to be passed in.

Arguments:

    pszUnicode - the unicode string to be converted to an ansi string
    pszAnsi  - the result ansi string

Return Value:

--*/

{
    int rc;
    DWORD dwLength = strlen(pszAnsi);

    rc = MultiByteToWideChar(CP_ACP,
                             MB_PRECOMPOSED,
                             pszAnsi,
                             dwLength + 1,
                             pszUnicode,
                             dwLength + 1);

    //
    // Ensure NULL termination.
    //
    pszUnicode[dwLength] = 0;

    return rc;
}


LPWSTR
AllocateUnicodeString(
    PCSTR  pszAnsiString
    )

/*++

Routine Description:

    Allocate a Unicode string with an ansi string as input

Arguments:

    pszAnsiString - the ansi string to be converted to a unicode string

Return Value:

--*/

{
    PWSTR  pszUnicodeString = NULL;
    int rc;

    ASSERT(pszAnsiString);

    pszUnicodeString = (PWSTR)MemAlloc(strlen(pszAnsiString)*sizeof(WCHAR) +
                                       sizeof(WCHAR));

    if (pszUnicodeString) {
        rc = AnsiToUnicodeString(
                pszAnsiString,
                pszUnicodeString
                );
    }

    if (rc>0) {
        return pszUnicodeString;
    }

    if (pszUnicodeString) {
        MemFree(pszUnicodeString);
    }
    return NULL;
}

PSTR MemAllocStr_E(
    PSTR pszIn
    ) 
{
    PSTR pszTemp;
    
    pszTemp = (PSTR)MemAlloc_E((strlen(pszIn)+1)*sizeof(char));
    
    if (pszTemp==NULL) {
        return NULL;
    }

    return strcpy(pszTemp, pszIn);

}

PWSTR MemAllocStrW_E(
    PWSTR pszIn
    ) 
{
    PWSTR pszTemp;
    
    pszTemp = (PWSTR)MemAlloc_E((wcslen(pszIn)+1)*sizeof(WCHAR));
    
    if (pszTemp==NULL) {
        return NULL;
    }

    return wcscpy(pszTemp, pszIn);

}


LPVOID MemAlloc_E(
    DWORD dwBytes
    ) 
{
    LPVOID pReturn = NULL;
    pReturn = MemAlloc(dwBytes);
    if (!pReturn) {
        RaiseException(LL_MEMORY_ERROR, 0, 0, NULL);    
    }
    return pReturn;
}

LPVOID MemRealloc_E(
            LPVOID IpMem, 
            DWORD dwBytes
            ) 
{
    DWORD dwSize;
    LPVOID pReturn = NULL;

    dwSize = MemSizeOri(IpMem);

    pReturn = MemRealloc(IpMem,dwSize,dwBytes);
    if (!pReturn) {
        RaiseException(LL_MEMORY_ERROR, 0, 0, NULL);    
    }
    return pReturn;

}   

#if DBG

DWORD dwMemoryLog = 1;

#define MAXDEPTH 10

typedef struct _MEMTAG {
    DWORD Tag ;
    DWORD Size ;
    PVOID pvBackTrace[MAXDEPTH+1];
    LPSTR pszSymbol[MAXDEPTH+1];
    DWORD uDepth;
    LIST_ENTRY List ;
} MEMTAG, *PMEMTAG ;

LIST_ENTRY       MemList ;
DWORD            MemCount ;
CRITICAL_SECTION MemCritSect ;

/*++

Routine Description:

    This function initializes the mem tracking code. Must be call
    during DLL load an ONLY during DLL load.

Arguments:

    None

Return Value:

    None.

--*/
VOID InitMem(
    VOID
)
{
    InitializeCriticalSection(&MemCritSect) ;
    InitializeListHead(&MemList) ;
    MemCount = 0 ;
}

/*++

Routine Description:

    This function asserts that the mem list is empty on exit.

Arguments:

    None

Return Value:

    None.

--*/
VOID AssertMemLeaks(
    VOID
)
{
    ASSERT(IsListEmpty(&MemList)) ;
}

#endif

LPVOID
MemAlloc(
    DWORD cb
)
/*++

Routine Description:

    This function will allocate local memory. It will possibly allocate extra
    memory and fill this with debugging information for the debugging version.

Arguments:

    cb - The amount of memory to allocate

Return Value:

    NON-NULL - A pointer to the allocated memory

    FALSE/NULL - The operation failed. Extended error status is available
    using GetLastError.

--*/
{
#if DBG
    DWORD cbNew ;
    PMEMTAG pMem ;
    DWORD i = 0;

    ULONG ulHash;

    //
    // adjust size for our tag and one spare dword at end
    // and allocate the memory
    //
    cb = DWORD_ALIGN_UP(cb);

    cbNew = cb + ( sizeof(MEMTAG) + sizeof(DWORD) );

    pMem=(PMEMTAG)LocalAlloc(LPTR, cbNew);

    if (!pMem) {

        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return 0;
    }

    //
    // fill in deadbeef at end and tag info.
    // and insert it into the MemList
    //

    *(LPDWORD)((LPBYTE)pMem+cbNew-sizeof(DWORD)) = 0xdeadbeef;
    pMem->Tag = 0xB00FB00F ;
    pMem->Size = cb ;


    //
    // Capture a backtrace at this spot for debugging.
    //

#if (defined(i386) && !defined(WIN95))

    pMem->uDepth = RtlCaptureStackBackTrace(
                            0,
                            MAXDEPTH,
                            pMem->pvBackTrace,
                            &ulHash
                            );



#else

    pMem->uDepth = 0;

#endif


    EnterCriticalSection(&MemCritSect) ;
    InsertHeadList(&MemList, &pMem->List) ;
    MemCount++ ;
    LeaveCriticalSection(&MemCritSect) ;

    //
    // skip past the mem tag
    //
    pMem++ ;
    return (LPVOID)(pMem);
#else
    return(LocalAlloc(LPTR, cb));
#endif

}

BOOL
MemFree(
   LPVOID pMem
)
{
#if DBG
    DWORD        cb;
    DWORD        cbNew = 0;
    PMEMTAG pNewMem ;
    LPDWORD      pRetAddr;
    DWORD i = 0;



    pNewMem = (PMEMTAG)pMem;
    pNewMem -- ;

    cb = pNewMem->Size;
    cbNew = cb + sizeof(DWORD) + sizeof(MEMTAG);

    //
    // check the trailing deadbeef and remove from list
    //

    if (*(LPDWORD)(((LPBYTE)pNewMem) + cbNew - sizeof(DWORD)) != 0xdeadbeef) {
        ERR(("Freeing memory not allocated by MemAlloc"));
        return FALSE;
    }

    EnterCriticalSection(&MemCritSect) ;
    RemoveEntryList(&pNewMem->List) ;
    MemCount-- ;
    LeaveCriticalSection(&MemCritSect) ;


    for (i = 0; i < pNewMem->uDepth; i++) {

        if (pNewMem->pszSymbol[i]) {
            LocalFree(pNewMem->pszSymbol[i]);
        }
    }


    //
    // Whack freed memory with known pattern
    //

    memset(pMem, 0x65, cb);
    return(LocalFree((LPVOID)pNewMem) == NULL);

#else

    return(LocalFree(pMem) == NULL);

#endif


}

DWORD
MemSize(
   LPVOID pMem
)
{
#if DBG
    DWORD        cb;
    DWORD        cbNew = 0;
    PMEMTAG      pNewMem ;
    LPDWORD      pRetAddr;
    DWORD i = 0;



    pNewMem = (PMEMTAG)pMem;
    pNewMem -- ;

    cb = pNewMem->Size;
    cbNew = cb + sizeof(DWORD) + sizeof(MEMTAG);

    if (*(LPDWORD)(((LPBYTE)pNewMem) + cbNew - sizeof(DWORD)) != 0xdeadbeef) {
        ERR(("Getting size not allocated by MemAlloc!"));
        return 0;
    }

    return((DWORD)LocalSize((LPVOID)pNewMem));
#else
    return((DWORD)LocalSize(pMem));
#endif
}

DWORD
MemSizeOri(
   LPVOID pMem
)
{
#if DBG
    DWORD        cb;
    PMEMTAG      pNewMem ;

    pNewMem = (PMEMTAG)pMem;
    pNewMem -- ;

    cb = pNewMem->Size;

    return((DWORD)cb);
#else
    return((DWORD)LocalSize(pMem));
#endif
}



LPVOID
MemRealloc(
   LPVOID pOldMem,
   DWORD cbOld,
   DWORD cbNew
)
{
    LPVOID pNewMem;

    pNewMem=MemAlloc(cbNew);

    if (pOldMem && pNewMem) {
        memcpy(pNewMem, pOldMem, min(cbNew, cbOld));
        MemFree(pOldMem);
    }

    return pNewMem;
}

LPSTR
MemAllocStr(
    LPSTR pStr
)
/*++

Routine Description:

    This function will allocate enough local memory to store the specified
    string, and copy that string to the allocated memory

Arguments:

    pStr - Pointer to the string that needs to be allocated and stored

Return Value:

    NON-NULL - A pointer to the allocated memory containing the string

    FALSE/NULL - The operation failed. Extended error status is available
    using GetLastError.

--*/
{
   LPSTR pMem;

   if (!pStr)
      return 0;

   if (pMem = (LPSTR)MemAlloc( strlen(pStr)*sizeof(CHAR) + sizeof(CHAR) ))
      strcpy(pMem, pStr);

   return pMem;
}

PWSTR
MemAllocStrW(
    PWSTR pStr
)
/*++

Routine Description:

    This function will allocate enough local memory to store the specified
    string, and copy that string to the allocated memory

Arguments:

    pStr - Pointer to the string that needs to be allocated and stored

Return Value:

    NON-NULL - A pointer to the allocated memory containing the string

    FALSE/NULL - The operation failed. Extended error status is available
    using GetLastError.

--*/
{
   PWSTR pMem;

   if (!pStr)
      return 0;

   if (pMem = (PWSTR)MemAlloc( wcslen(pStr)*sizeof(WCHAR) + sizeof(WCHAR) ))
      wcscpy(pMem, pStr);

   return pMem;
}

BOOL
MemReallocStr(
   LPSTR *ppStr,
   LPSTR pStr
)
{
   if (ppStr && (*ppStr)) {
        MemFree(*ppStr);
        *ppStr=MemAllocStr(pStr);

        return TRUE;
    }
    else {
        return FALSE;
    }
}


#if DBG
VOID
DumpMemoryTracker(
    VOID
    )
{
#ifndef _WIN64
   LIST_ENTRY* pEntry;
   MEMTAG*  pMem;
   BYTE*       pTemp;
   DWORD i = 0;
   CHAR szSymbolPath[MAX_PATH+1];
   DWORD dwCount = 0;

   pEntry   = MemList.Flink;

   if (!dwMemoryLog) {
      return;
   }


   if ( pEntry == &MemList ) {
       OutputDebugStringA( "No Memory leaks found\n" );
   }

   while( pEntry != &MemList )
   {
      CHAR szLeak[1024];

      pTemp = (BYTE*)pEntry;
      pTemp = pTemp - sizeof(DWORD) - sizeof(DWORD)
              - sizeof(DWORD) -
              (sizeof(CHAR*) + sizeof(LPVOID))*( MAXDEPTH +1);
      pMem  = (MEMTAG*)pTemp;

      sprintf(
        szLeak,
        "[oleds] Memory leak!!! Addresss = %.8x Size = %ld \n",
        pMem + 1,
        pMem->Size
        );
      OutputDebugStringA( szLeak );


     for (i = 0; i < pMem->uDepth; i++) {

         dwCount = TranslateAddress(
                     (ULONG)pMem->pvBackTrace[ i ],
                     szSymbolPath,
                     MAX_PATH
                     );
         szSymbolPath[dwCount] = '\0';
         sprintf(szLeak, "%s\n",szSymbolPath);
         OutputDebugStringA( szLeak);

     }

      pEntry   = pEntry->Flink;
   }
#endif
}
#endif

