#include <malloc.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include "THeapSumInfo.h"


//---------------------------------------
int GetHeapSumInfo (THeapSumInfo* Dst)
//---------------------------------------
{
    int r;

    #ifdef __BORLANDC__
        /* ENG. Problem with Borland Turbo C++ Explorer 2005.
        Solution found by analizing disassemling code.

        RUS. Проблемы под Borland Turbo C++ Explorer 2005,
        если до переменная _HEAPINFO HI содержится целое,
        значение котрого больше 0x100000. В этом случае
        функция _heapwalk сразу возвращает код _HEAPEND,
        как будто куча пуста.
            Поэтому под Borland вводится массив из двух
        структур _HEAPINFO, первая обнуляется, вторая
        передаётся в _heapwalk. */
        static _HEAPINFO HIArr[2];
        #define HI HIArr[1]
    #else
        // Нормальная работа с локальным объектом
        // _HEAPINFO под компиляторами Microsoft и Mingw.
        _HEAPINFO HI;
    #endif

    // Обнуляем все поля
    memset (Dst, 0, sizeof (*Dst));

    #ifdef __BORLANDC__
        memset (HIArr, 0, sizeof (_HEAPINFO));
    #endif

    HI._pentry = NULL;

    do
    {
        r = _heapwalk (&HI);

        if (r == _HEAPEND) break;
        if (r != _HEAPOK)  return 0;

        Dst->BlocksTotal++;

        if (HI._useflag)
        {
            Dst->BlocksUsed++;

            if (Dst->MaxUsed < HI._size)
                Dst->MaxUsed = HI._size;

            Dst->UsedSum += HI._size;
        }
        else
        {
            Dst->BlocksFree++;
            if (Dst->MaxFree < HI._size)
                Dst->MaxFree = HI._size;

            Dst->FreeSum += HI._size;
        }
    }
    while (1);

    Dst->TotalSum = Dst->FreeSum + Dst->UsedSum;
    return 1;

    #ifdef __BORLANDC__
        #undef HI
    #endif

} // ~GetHeapSumInfo



//------------------------------------------------------------
void GetHeapSumInfoDifference (THeapSumInfo* Info1,
     THeapSumInfo* Info2, THeapSumInfoDifference *InfoDiff)
//------------------------------------------------------------
{
    #define CALC_DIFF(field) \
        InfoDiff->field = Info2->field - Info1->field;

    CALC_DIFF (BlocksTotal);
    CALC_DIFF (BlocksUsed);
    CALC_DIFF (BlocksFree);
    CALC_DIFF (MaxFree);
    CALC_DIFF (MaxUsed);
    CALC_DIFF (FreeSum);
    CALC_DIFF (UsedSum);
    CALC_DIFF (TotalSum);

    #undef CALC_DIFF

} // ~GetHeapSumInfoDifference



//------------------------------------------------------------
static void TCommonHeapInfoStruct_to_cstring (
     THeapSumInfo *Info, char* Dst, const char* StartStr,
     const char* FieldSeparatorStr, const char* EndStr,
     const THeapSumInfoFieldPresenceFlag *PresenceFlagPtr,
     char SignedConversion)
//------------------------------------------------------------
{
    #ifdef _MSC_FULL_VER
        #pragma warning (disable: 4996)
        // Откючение предупреждения Микрософт
        // об использовании старых функций
        // без префикса "_".
    #endif

    #ifdef _M_X64
        typedef __int64              signed_type;
        typedef unsigned __int64     unsigned_type;

        #define SIGNED_TO_STR        _i64toa
        #define UNSIGNED_TO_STR      _ui64toa
    #else
        typedef int                  signed_type;
        typedef unsigned int         unsigned_type;

        #define SIGNED_TO_STR        ltoa

        #ifdef __BORLANDC__
            #define UNSIGNED_TO_STR  ultoa
        #else
            #define UNSIGNED_TO_STR  _ultoa
        #endif
    #endif

    char Buf[64];
    char PrevFieldPresent = 0;

    static const char
        *DefaultSeparator = "\r\n";

    static THeapSumInfoFieldPresenceFlag
        DefaultPresenceFlag = {1,1,1,1,1,1,1,1};

    if (StartStr)   strcpy (Dst, StartStr);
    else *Dst = 0;

    if (!FieldSeparatorStr) FieldSeparatorStr = DefaultSeparator;
    if (!EndStr)            EndStr            = DefaultSeparator;
    if (!PresenceFlagPtr)   PresenceFlagPtr   = &DefaultPresenceFlag;

    #define ADD_IF_REQUIRED(Field) \
        if (PresenceFlagPtr->Field) \
        { \
            INSERT_SEPARATOR_IF_REQUIRED; \
            strcat (Dst, #Field" = "); \
            if (SignedConversion) \
                    SIGNED_TO_STR ((  signed_type)Info->Field, Buf, 10); \
            else  UNSIGNED_TO_STR ((unsigned_type)Info->Field, Buf, 10); \
            strcat (Dst, Buf); \
            PrevFieldPresent = 1; \
        }

    #define INSERT_SEPARATOR_IF_REQUIRED

    ADD_IF_REQUIRED (BlocksTotal)

    #undef  INSERT_SEPARATOR_IF_REQUIRED

    #define INSERT_SEPARATOR_IF_REQUIRED \
        if (PrevFieldPresent) strcat (Dst, FieldSeparatorStr);

    ADD_IF_REQUIRED (BlocksUsed)
    ADD_IF_REQUIRED (BlocksFree)
    ADD_IF_REQUIRED (MaxFree)
    ADD_IF_REQUIRED (MaxUsed)
    ADD_IF_REQUIRED (FreeSum)
    ADD_IF_REQUIRED (UsedSum)
    ADD_IF_REQUIRED (TotalSum)

    strcat (Dst, EndStr);

    #undef SIGNED_TO_STR
    #undef UNSIGNED_TO_STR
    #undef INSERT_SEPARATOR_IF_REQUIRED
    #undef ADD_IF_REQUIRED

} // ~TCommonHeapInfoStruct_to_cstring



//-----------------------------------------------------------
void THeapSumInfo_to_cstring (
     THeapSumInfo *Info, char* Dst, const char* StartStr,
     const char* FieldSeparatorStr, const char* EndStr,
     const THeapSumInfoFieldPresenceFlag *PresenceFlagPtr)
//-----------------------------------------------------------
{
    TCommonHeapInfoStruct_to_cstring (Info, Dst, StartStr,
        FieldSeparatorStr, EndStr, PresenceFlagPtr, 0);
}



//--------------------------------------------------------------------
void THeapSumInfoDifference_to_cstring (
     THeapSumInfoDifference *Info, char* Dst, const char* StartStr,
     const char* FieldSeparatorStr, const char* EndStr,
     const THeapSumInfoFieldPresenceFlag *PresenceFlagPtr)
//--------------------------------------------------------------------
{
    TCommonHeapInfoStruct_to_cstring ((THeapSumInfo*)Info,
        Dst, StartStr, FieldSeparatorStr, EndStr, PresenceFlagPtr, 1);
}



//-----------------------------------------------------------------
void SaveHeapSumInfoAndDifference (
     THeapSumInfo *Info1, THeapSumInfo *Info2,
     THeapSumInfoDifference *InfoDiff,
     const char* FileName)
//-----------------------------------------------------------------
{
    #define SAVE_HeapInfoStruct(Info) \
        THeapSumInfo_to_cstring (Info, Buf, "["#Info"]\r\n", 0,0,0); \
        fprintf (f, "%s\n", Buf);

    #define SAVE_HeapInfoStructDiff(Info) \
        THeapSumInfoDifference_to_cstring (Info, Buf, "["#Info"]\r\n", 0,0,0); \
        fprintf (f, Buf);

    char Buf [512];
    FILE *f = fopen (FileName, "wt");
    if (!f) return;

    SAVE_HeapInfoStruct     (Info1);
    SAVE_HeapInfoStruct     (Info2);
    SAVE_HeapInfoStructDiff (InfoDiff);

    fclose (f);

    #undef SAVE_HeapInfoStruct
    #undef SAVE_HeapInfoStructDiff

} // ~SaveHeapSumInfoAndDifference


