Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / WINNT / talocale / tal_alloc.cpp
1
2 #ifndef DEBUG
3 #define DEBUG
4 #endif
5 #ifdef NO_DEBUG_ALLOC
6 #undef NO_DEBUG_ALLOC
7 #endif
8
9 #include <windows.h>
10 #include <commctrl.h>
11 #include <WINNT/tal_alloc.h>
12
13
14 /*
15  * PARAMETERS _________________________________________________________________
16  *
17  */
18
19 #define cDEFAULT_BUCKETS 128
20
21 #define dwSIG_AT_END     'Okay'
22
23 #define iINVALID         ((size_t)-1)
24
25 #define MIN_BUCKETS(_cObjects)     ((_cObjects) / 23)
26 #define TARGET_BUCKETS(_cObjects)  ((_cObjects) /  5)
27
28 #define TRACK_FREED
29
30 #define cmsecREFRESH   1000
31
32
33 /*
34  * DEFINITIONS ________________________________________________________________
35  *
36  */
37
38 typedef struct
39    {
40    PVOID aElements; // = EXPANDARRAYHEAP.aElements + 4;
41    // Followed by allocated space for aElements
42    } EXPANDARRAYHEAP, *LPEXPANDARRAYHEAP;
43
44 class ALLOCEXPANDARRAY
45    {
46    public:
47       ALLOCEXPANDARRAY (size_t cbElement, size_t cElementsPerHeap = 0);
48       ~ALLOCEXPANDARRAY (void);
49
50       PVOID GetAt (size_t iElement);
51       void SetAt (size_t iElement, PVOID pData);
52
53    private:
54       size_t m_cbElement;
55       size_t m_cElementsPerHeap;
56
57       size_t m_cHeaps;
58       LPEXPANDARRAYHEAP *m_aHeaps;
59    };
60
61 typedef struct
62    {
63    PVOID pData;
64    size_t cbData;
65    LPSTR pszExpr;
66    LPSTR pszFile;
67    DWORD dwLine;
68    DWORD dwTick;
69    DWORD dwEndSig;
70    BOOL fCPP;
71    size_t iNext;
72    size_t iPrev;
73    BOOL fFreed;
74    BOOL fInList;
75    BOOL fTared;
76    } MEMCHUNK, *PMEMCHUNK;
77
78 typedef struct
79    {
80    size_t iFirst;
81    } BUCKET, *PBUCKET;
82
83 typedef struct
84    {
85    LONG cAllocCpp;
86    LONG cAllocDyna;
87    LONG cAllocTotal;
88    LONG cAllocCppTared;
89    LONG cAllocDynaTared;
90    LONG cAllocTotalTared;
91    LONG cbAllocCpp;
92    LONG cbAllocDyna;
93    LONG cbAllocTotal;
94    LONG cbAllocCppTared;
95    LONG cbAllocDynaTared;
96    LONG cbAllocTotalTared;
97    } STATISTICS;
98
99 static struct l
100    {
101    CRITICAL_SECTION *pcs;
102    HWND hManager;
103    int idTimer;
104
105    ALLOCEXPANDARRAY *pHeap;
106    size_t cChunks;
107
108    BUCKET *aBuckets;
109    size_t cBuckets;
110
111    STATISTICS Stats;
112    } l;
113
114 static struct lr
115    {
116    RECT rManager;
117    LONG acxColumns[6];
118    int iColSort;
119    BOOL fSortRev;
120    } lr;
121
122 #define HASH(_dw,_cTable)  (((DWORD)(_dw) >> 4) % (_cTable))
123
124
125 /*
126  * REALLOC ____________________________________________________________________
127  *
128  */
129
130 #ifndef REALLOC
131 #define REALLOC(_a,_c,_r,_i) Alloc_ReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
132 BOOL Alloc_ReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
133 {
134    LPVOID pNew;
135    size_t cNew;
136
137    if (cReq <= *pcTarget)
138       return TRUE;
139
140    if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
141       return FALSE;
142
143    if ((pNew = (LPVOID)GlobalAlloc (GMEM_FIXED, cbElement * cNew)) == NULL)
144       return FALSE;
145    memset (pNew, 0x00, cbElement * cNew);
146
147    if (*pcTarget == 0)
148       memset (pNew, 0x00, cbElement * cNew);
149    else
150       {
151       memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
152       memset (&((char*)pNew)[ cbElement * (*pcTarget) ], 0x00, cbElement * (cNew - *pcTarget));
153       GlobalFree ((HGLOBAL)*ppTarget);
154       }
155
156    *ppTarget = pNew;
157    *pcTarget = cNew;
158    return TRUE;
159 }
160 #endif
161
162
163 /*
164  * USER INTERFACE ROUTINES ____________________________________________________
165  *
166  */
167
168 #define IDC_INITIALIZE           100
169 #define IDC_BOX_ALLOC            101
170 #define IDC_LABEL_CPP            102
171 #define IDC_LABEL_OTHER          103
172 #define IDC_LABEL_TARED          104
173 #define IDC_LABEL_TOTAL          106
174 #define IDC_LABEL_COUNT          107
175 #define IDC_LABEL_SIZE           108
176 #define IDC_LABEL_AVERAGE        109
177 #define IDC_VALUE_CPP_COUNT      110
178 #define IDC_VALUE_OTHER_COUNT    111
179 #define IDC_VALUE_TARED_COUNT    112
180 #define IDC_VALUE_TOTAL_COUNT    113
181 #define IDC_VALUE_CPP_SIZE       114
182 #define IDC_VALUE_OTHER_SIZE     115
183 #define IDC_VALUE_TARED_SIZE     116
184 #define IDC_VALUE_TOTAL_SIZE     117
185 #define IDC_VALUE_CPP_AVERAGE    118
186 #define IDC_VALUE_OTHER_AVERAGE  119
187 #define IDC_VALUE_TARED_AVERAGE  120
188 #define IDC_VALUE_TOTAL_AVERAGE  121
189 #define IDC_BOX_DETAILS          122
190 #define IDC_LIST                 123
191 #define IDC_LABEL                124
192 #define IDC_HIDE                 125
193 #define IDC_TARE                 126
194 #define IDC_RESET                127
195 #define IDC_REFRESH              128
196 #define IDC_LIST_ADD             129
197 #define IDC_LIST_REMOVE          130
198 #define IDC_LIST_REMOVEADD       131
199
200 #define cszTITLE        TEXT("Memory Manager")
201
202 typedef struct
203    {
204    RECT rBoxAlloc;
205    RECT rLabelCpp;
206    RECT rLabelOther;
207    RECT rLabelTared;
208    RECT rLabelTotal;
209    RECT rLabelCount;
210    RECT rLabelSize;
211    RECT rLabelAverage;
212    RECT rValueCppCount;
213    RECT rValueOtherCount;
214    RECT rValueTaredCount;
215    RECT rValueTotalCount;
216    RECT rValueCppSize;
217    RECT rValueOtherSize;
218    RECT rValueTaredSize;
219    RECT rValueTotalSize;
220    RECT rValueCppAverage;
221    RECT rValueOtherAverage;
222    RECT rValueTaredAverage;
223    RECT rValueTotalAverage;
224    RECT rBoxDetails;
225    RECT rList;
226    RECT rLabel;
227    RECT rHide;
228    RECT rTare;
229    RECT rReset;
230    RECT rClose;
231    } WINDOWSIZES;
232
233 #define cBORDER      7
234 #define cxBETWEEN    6
235 #define cyBETWEEN    6
236 #define cxBUTTONS   65
237 #define cyBUTTONS   22
238 #define cxLABELS    70
239 #define cxVALUES    70
240 #define cyLABELS    15
241
242
243 HWND MakeWindow (LPCTSTR pszClass, LPCTSTR pszTitle, DWORD dwStyle, RECT *prSource, HWND hParent, UINT idc, DWORD dwStyleEx = 0)
244 {
245    RECT rr = { 0, 0, 16, 16 };
246    if (prSource)
247       rr = *prSource;
248    HWND hWnd = CreateWindowEx (dwStyleEx, pszClass, pszTitle, dwStyle, rr.left, rr.top, rr.right - rr.left, rr.bottom - rr.top, hParent, (HMENU)idc, GetModuleHandle(0), 0);
249    if (IsWindow (hWnd))
250       SendMessage (hWnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 1);
251    return hWnd;
252 }
253
254 void SetWindowRect (HWND hWnd, RECT *pr)
255 {
256    SetWindowPos (hWnd, 0, pr->left, pr->top, pr->right - pr->left, pr->bottom - pr->top, SWP_NOZORDER | SWP_NOACTIVATE);
257 }
258
259 void FormatBytes (LPTSTR pszText, double lfValue)
260 {
261    if (lfValue < 0)
262       {
263       lfValue = 0-lfValue;
264       *pszText++ = TEXT('-');
265       }
266
267    if (lfValue >= 1048576)
268       {
269       LONG Value = (LONG)(lfValue / 1048576);
270       lfValue -= Value * 1048576;
271       LONG Frac = (LONG)(lfValue * 100 / 1048576);
272       wsprintf (pszText, TEXT("%ld.%ld MB"), Value, Frac);
273       }
274    else if (lfValue >= 3072)
275       {
276       LONG Value = (LONG)(lfValue / 1024);
277       lfValue -= Value * 1024;
278       LONG Frac = (LONG)(lfValue * 100 / 1024);
279       wsprintf (pszText, TEXT("%ld.%ld kb"), Value, Frac);
280       }
281    else // (lfValue < 3072)
282       {
283       wsprintf (pszText, TEXT("%ld b"), (LONG)lfValue);
284       }
285 }
286
287 void FormatTime (LPTSTR pszText, DWORD dwTick)
288 {
289    static FILETIME ftTickZero = { 0, 0 };
290    if (!ftTickZero.dwHighDateTime && !ftTickZero.dwLowDateTime)
291       {
292       // We need to find out what FILETIME corresponds with a tick
293       // count of zero. To do that, first find the FILETIME and tick
294       // count that represent *now* (in local time).
295       //
296       SYSTEMTIME stNow;
297       GetLocalTime (&stNow);
298       SystemTimeToFileTime (&stNow, &ftTickZero);
299
300       DWORD dwTickNow = GetTickCount();
301
302       // A FILETIME is a count-of-100ns-intervals, and a tick count is
303       // a count of 1ms-intervals.  So we need to subtract off a big
304       // number times our tick count.
305       //
306       if ((dwTickNow * 10000) < (dwTickNow))
307          ftTickZero.dwHighDateTime --;
308       dwTickNow *= 10000;   // convert to 100ns intervals
309       if (dwTickNow > ftTickZero.dwLowDateTime)
310          ftTickZero.dwHighDateTime --;
311       ftTickZero.dwLowDateTime -= dwTickNow; // unsigned, so it'll wrap OK
312       }
313
314    // Convert the given tick count into 100ns intervals, and add it
315    // to our ftTickZero.
316    //
317    FILETIME ftTick = ftTickZero;
318
319    if ((dwTick * 10000) < (dwTick))
320       ftTick.dwHighDateTime ++;
321    dwTick *= 10000;   // convert to 100ns intervals
322    if (dwTick > (DWORD)(0-ftTick.dwLowDateTime)) // too big to add?
323       ftTick.dwHighDateTime ++;
324    ftTick.dwLowDateTime += dwTick; // unsigned, so it'll wrap OK
325
326    // Convert to a SYSTEMTIME, and output the time component
327    //
328    SYSTEMTIME stTick;
329    FileTimeToSystemTime (&ftTick, &stTick);
330
331    wsprintf (pszText, TEXT("%ld:%02ld:%02ld.%02ld"),
332               (DWORD)stTick.wHour,
333               (DWORD)stTick.wMinute,
334               (DWORD)stTick.wSecond,
335               (DWORD)(stTick.wMilliseconds / 100));
336 }
337
338 void SetDlgItemBytes (HWND hDlg, int idc, double lfValue)
339 {
340    TCHAR szText[ 256 ];
341    FormatBytes (szText, lfValue);
342    SetDlgItemText (hDlg, idc, szText);
343 }
344
345
346 void MemMgr_ShowWarning (PMEMCHUNK pChunk, LPSTR pszFile, DWORD dwLine, LPTSTR pszDesc)
347 {
348    TCHAR szMessage[ 1024 ];
349    wsprintf (szMessage, TEXT("%s\n\n   Address: 0x%08lX (%s)\n   Allocated: %s line %ld\n   Freed: %s line %ld\n\nClick OK for memory details."), pszDesc, pChunk->pData, pChunk->pszExpr, pChunk->pszFile, pChunk->dwLine, pszFile, dwLine);
350    if (MessageBox (NULL, szMessage, cszTITLE, MB_ICONHAND | MB_OKCANCEL | MB_DEFBUTTON2) == IDOK)
351       {
352       // TODO: Show Details
353       }
354 }
355
356 void MIX (RECT *pr, RECT *pr1, RECT *pr2)
357 {
358    pr->left = pr2->left;
359    pr->top = pr1->top;
360    pr->right = pr2->right;
361    pr->bottom = pr1->bottom;
362 }
363
364 void MemMgr_GetWindowSizes (WINDOWSIZES *pSizes)
365 {
366    RECT rClient;
367    GetClientRect (l.hManager, &rClient);
368    InflateRect (&rClient, 0-cBORDER, 0-cBORDER);
369
370    pSizes->rLabelAverage.right = rClient.right -cxBETWEEN*2;
371    pSizes->rLabelAverage.top = rClient.top +8 +cyBETWEEN;
372    pSizes->rLabelAverage.left = pSizes->rLabelAverage.right -cxVALUES;
373    pSizes->rLabelAverage.bottom = pSizes->rLabelAverage.top +cyLABELS;
374
375    pSizes->rLabelSize = pSizes->rLabelAverage;
376    pSizes->rLabelSize.left -= cxBETWEEN + cxVALUES;
377    pSizes->rLabelSize.right -= cxBETWEEN + cxVALUES;
378
379    pSizes->rLabelCount = pSizes->rLabelSize;
380    pSizes->rLabelCount.left -= cxBETWEEN + cxVALUES;
381    pSizes->rLabelCount.right -= cxBETWEEN + cxVALUES;
382
383    pSizes->rLabelCpp.left = rClient.left +cxBETWEEN;
384    pSizes->rLabelCpp.top = pSizes->rLabelCount.bottom +cyBETWEEN;
385    pSizes->rLabelCpp.right = pSizes->rLabelCpp.left +cxLABELS;
386    pSizes->rLabelCpp.bottom = pSizes->rLabelCpp.top +cyLABELS;
387
388    pSizes->rLabelOther = pSizes->rLabelCpp;
389    pSizes->rLabelOther.top += cyBETWEEN + cyLABELS;
390    pSizes->rLabelOther.bottom += cyBETWEEN + cyLABELS;
391
392    pSizes->rLabelTotal = pSizes->rLabelOther;
393    pSizes->rLabelTotal.top += cyBETWEEN + cyLABELS;
394    pSizes->rLabelTotal.bottom += cyBETWEEN + cyLABELS;
395
396    pSizes->rLabelTared = pSizes->rLabelTotal;
397    pSizes->rLabelTared.top += cyBETWEEN + cyLABELS;
398    pSizes->rLabelTared.bottom += cyBETWEEN + cyLABELS;
399
400    pSizes->rBoxAlloc = rClient;
401    pSizes->rBoxAlloc.bottom = pSizes->rLabelTared.bottom +cyBETWEEN;
402
403    MIX (&pSizes->rValueCppCount, &pSizes->rLabelCpp, &pSizes->rLabelCount);
404    MIX (&pSizes->rValueOtherCount, &pSizes->rLabelOther, &pSizes->rLabelCount);
405    MIX (&pSizes->rValueTaredCount, &pSizes->rLabelTared, &pSizes->rLabelCount);
406    MIX (&pSizes->rValueTotalCount, &pSizes->rLabelTotal, &pSizes->rLabelCount);
407
408    MIX (&pSizes->rValueCppSize, &pSizes->rLabelCpp, &pSizes->rLabelSize);
409    MIX (&pSizes->rValueOtherSize, &pSizes->rLabelOther, &pSizes->rLabelSize);
410    MIX (&pSizes->rValueTaredSize, &pSizes->rLabelTared, &pSizes->rLabelSize);
411    MIX (&pSizes->rValueTotalSize, &pSizes->rLabelTotal, &pSizes->rLabelSize);
412
413    MIX (&pSizes->rValueCppAverage, &pSizes->rLabelCpp, &pSizes->rLabelAverage);
414    MIX (&pSizes->rValueOtherAverage, &pSizes->rLabelOther, &pSizes->rLabelAverage);
415    MIX (&pSizes->rValueTaredAverage, &pSizes->rLabelTared, &pSizes->rLabelAverage);
416    MIX (&pSizes->rValueTotalAverage, &pSizes->rLabelTotal, &pSizes->rLabelAverage);
417
418    pSizes->rBoxDetails = rClient;
419    pSizes->rBoxDetails.top = pSizes->rBoxAlloc.bottom +cyBETWEEN;
420    pSizes->rBoxDetails.bottom -= cyBUTTONS +cyBETWEEN;
421
422    pSizes->rList = pSizes->rBoxDetails;
423    pSizes->rList.top += 8;
424    InflateRect (&pSizes->rList, 0-cBORDER, 0-cBORDER);
425
426    pSizes->rClose = rClient;
427    pSizes->rClose.top = pSizes->rClose.bottom - cyBUTTONS;
428    pSizes->rClose.left = pSizes->rClose.right - cxBUTTONS;
429
430    pSizes->rReset = pSizes->rClose;
431    pSizes->rReset.right = pSizes->rClose.left - cxBETWEEN;
432    pSizes->rReset.left = pSizes->rReset.right - cxBUTTONS;
433
434    pSizes->rTare = pSizes->rClose;
435    pSizes->rTare.right = pSizes->rReset.left - cxBETWEEN;
436    pSizes->rTare.left = pSizes->rTare.right - cxBUTTONS;
437
438    pSizes->rLabel = pSizes->rTare;
439    pSizes->rLabel.right = pSizes->rTare.left - cxBETWEEN;
440    pSizes->rLabel.left = pSizes->rLabel.right - cxBUTTONS;
441
442    pSizes->rHide = pSizes->rLabel;
443    pSizes->rHide.right = pSizes->rLabel.left - cxBETWEEN;
444    pSizes->rHide.left = pSizes->rHide.right - cxBUTTONS;
445 }
446
447 void MemMgr_OnSize (void)
448 {
449    WINDOWSIZES Sizes;
450    MemMgr_GetWindowSizes (&Sizes);
451    SetWindowRect (GetDlgItem (l.hManager, IDC_BOX_ALLOC), &Sizes.rBoxAlloc);
452    SetWindowRect (GetDlgItem (l.hManager, IDC_LABEL_CPP), &Sizes.rLabelCpp);
453    SetWindowRect (GetDlgItem (l.hManager, IDC_LABEL_OTHER), &Sizes.rLabelOther);
454    SetWindowRect (GetDlgItem (l.hManager, IDC_LABEL_TARED), &Sizes.rLabelTared);
455    SetWindowRect (GetDlgItem (l.hManager, IDC_LABEL_TOTAL), &Sizes.rLabelTotal);
456    SetWindowRect (GetDlgItem (l.hManager, IDC_LABEL_COUNT), &Sizes.rLabelCount);
457    SetWindowRect (GetDlgItem (l.hManager, IDC_LABEL_SIZE), &Sizes.rLabelSize);
458    SetWindowRect (GetDlgItem (l.hManager, IDC_LABEL_AVERAGE), &Sizes.rLabelAverage);
459    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_CPP_COUNT), &Sizes.rValueCppCount);
460    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_OTHER_COUNT), &Sizes.rValueOtherCount);
461    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_TARED_COUNT), &Sizes.rValueTaredCount);
462    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_TOTAL_COUNT), &Sizes.rValueTotalCount);
463    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_CPP_SIZE), &Sizes.rValueCppSize);
464    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_OTHER_SIZE), &Sizes.rValueOtherSize);
465    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_TARED_SIZE), &Sizes.rValueTaredSize);
466    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_TOTAL_SIZE), &Sizes.rValueTotalSize);
467    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_CPP_AVERAGE), &Sizes.rValueCppAverage);
468    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_OTHER_AVERAGE), &Sizes.rValueOtherAverage);
469    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_TARED_AVERAGE), &Sizes.rValueTaredAverage);
470    SetWindowRect (GetDlgItem (l.hManager, IDC_VALUE_TOTAL_AVERAGE), &Sizes.rValueTotalAverage);
471    SetWindowRect (GetDlgItem (l.hManager, IDC_BOX_DETAILS), &Sizes.rBoxDetails);
472    SetWindowRect (GetDlgItem (l.hManager, IDC_LIST), &Sizes.rList);
473    SetWindowRect (GetDlgItem (l.hManager, IDC_LABEL), &Sizes.rLabel);
474    SetWindowRect (GetDlgItem (l.hManager, IDC_HIDE), &Sizes.rHide);
475    SetWindowRect (GetDlgItem (l.hManager, IDC_TARE), &Sizes.rTare);
476    SetWindowRect (GetDlgItem (l.hManager, IDC_RESET), &Sizes.rReset);
477    SetWindowRect (GetDlgItem (l.hManager, IDCANCEL), &Sizes.rClose);
478 }
479
480
481 void MemMgr_OnListRemove (PVOID pData)
482 {
483    HWND hList = GetDlgItem (l.hManager, IDC_LIST);
484
485    LV_FINDINFO Find;
486    Find.flags = LVFI_PARAM;
487    Find.lParam = (LPARAM)pData;
488
489    int iItem;
490    if ((iItem = ListView_FindItem (hList, 0, &Find)) == -1)
491       {
492       // The listview's find feature sucks; I've seen with my own little
493       // eyes that it may miss an item if it's the only one in a list.
494       // Look for ourselves.
495       //
496       int cItems = ListView_GetItemCount(hList);
497       for (iItem = 0; iItem < cItems; ++iItem)
498          {
499          LV_ITEM Item;
500          Item.iItem = iItem;
501          Item.mask = LVIF_PARAM;
502          ListView_GetItem (hList, &Item);
503          if (Item.lParam == (LPARAM)pData)
504             break;
505          }
506       }
507
508    if (iItem != -1)
509       {
510       ListView_DeleteItem (hList, iItem);
511       }
512 }
513
514
515 void MemMgr_RemoveFromList (PMEMCHUNK pChunk)
516 {
517    PostMessage (l.hManager, WM_COMMAND, IDC_LIST_REMOVE, (LPARAM)(pChunk->pData));
518    pChunk->fInList = FALSE;
519 }
520
521
522 LPTSTR MemMgr_GetItemKey (HWND hList, int iItem)
523 {
524    static TCHAR szText[ 256 ];
525    LPTSTR pszReturn = NULL;
526
527    switch (lr.iColSort)
528       {
529       case 0:
530       case 4:
531       case 5:
532          LV_ITEM Item;
533          Item.iItem = iItem;
534          Item.mask = LVIF_PARAM;
535          ListView_GetItem (hList, &Item);
536
537          EnterCriticalSection (l.pcs);
538
539          size_t iBucket;
540          iBucket = HASH(Item.lParam,l.cBuckets);
541
542          PMEMCHUNK pChunk;
543          pChunk = NULL;
544
545          size_t iChunk;
546          for (iChunk = l.aBuckets[iBucket].iFirst; iChunk != iINVALID; )
547             {
548             if ((pChunk = (PMEMCHUNK)l.pHeap->GetAt(iChunk)) == NULL)
549                break;
550             if (pChunk->pData == (PVOID)Item.lParam)
551                break;
552             if ((iChunk = pChunk->iNext) == iINVALID)
553                {
554                pChunk = NULL;
555                break;
556                }
557             }
558
559          if (pChunk)
560             {
561             if (lr.iColSort == 0)
562                pszReturn = (LPTSTR)pChunk->dwTick;
563             else if (lr.iColSort == 4)
564                pszReturn = (LPTSTR)pChunk->cbData;
565             else // (lr.iColSort == 5)
566                pszReturn = (LPTSTR)pChunk->pData;
567             }
568
569          LeaveCriticalSection (l.pcs);
570          break;
571
572       default:
573          ListView_GetItemText (hList, iItem, lr.iColSort, szText, 256);
574          pszReturn = szText;
575          break;
576       }
577
578    return pszReturn;
579 }
580
581
582 int MemMgr_CompareItems (LPTSTR pszKey1, LPTSTR pszKey2)
583 {
584    int dw;
585
586    switch (lr.iColSort)
587       {
588       case 0:
589       case 4:
590       case 5:
591          dw = (int)( (DWORD)pszKey1 - (DWORD)pszKey2 );
592          break;
593
594       default:
595          dw = lstrcmpi (pszKey1, pszKey2);
596          break;
597       }
598
599    return (lr.fSortRev) ? (0-dw) : dw;
600 }
601
602
603 int MemMgr_PickListInsertionPoint (HWND hList, LPTSTR pszKey)
604 {
605    int iItemLow  = 0;
606    int iItemHigh = ListView_GetItemCount (hList) -1;
607
608    while (iItemLow <= iItemHigh)
609       {
610       int iItemTest = iItemLow + (iItemHigh - iItemLow) / 2;
611
612       LPTSTR pszAlternate = MemMgr_GetItemKey (hList, iItemTest);
613
614       int iCompare = MemMgr_CompareItems (pszKey, pszAlternate);
615       if (iCompare <= 0)
616          {
617          if ((iItemHigh = iItemTest-1) < iItemLow)
618             return iItemHigh +1;
619          }
620       if (iCompare >= 0)
621          {
622          if ((iItemLow = iItemTest+1) > iItemHigh)
623             return iItemLow;
624          }
625       }
626
627    return 0;
628 }
629
630
631 void MemMgr_OnListAdd (PMEMCHUNK pCopy)
632 {
633    HWND hList = GetDlgItem (l.hManager, IDC_LIST);
634
635    TCHAR szTime[256];
636    FormatTime (szTime, pCopy->dwTick);
637
638    TCHAR szFlags[256];
639    LPTSTR pszFlags = szFlags;
640    *pszFlags++ = (pCopy->fCPP) ? TEXT('C') : TEXT(' ');
641    *pszFlags++ = TEXT(' ');
642    *pszFlags++ = (pCopy->fFreed) ? TEXT('F') : TEXT(' ');
643    *pszFlags++ = 0;
644
645    TCHAR szExpr[256];
646    lstrcpy (szExpr, (pCopy->pszExpr) ? pCopy->pszExpr : TEXT("unknown"));
647
648    LPTSTR pszFile = pCopy->pszFile;
649    for (LPTSTR psz = pCopy->pszFile; *psz; ++psz)
650       {
651       if ((*psz == TEXT(':')) || (*psz == TEXT('\\')))
652          pszFile = &psz[1];
653       }
654    TCHAR szLocation[256];
655    if (!pszFile || !pCopy->dwLine)
656       lstrcpy (szLocation, TEXT("unknown"));
657    else
658       wsprintf (szLocation, TEXT("%s, %ld"), pszFile, pCopy->dwLine);
659
660    TCHAR szBytes[256];
661    FormatBytes (szBytes, pCopy->cbData);
662
663    TCHAR szAddress[256];
664    wsprintf (szAddress, TEXT("0x%08lX"), pCopy->pData);
665
666    LPTSTR pszKey = NULL;
667    switch (lr.iColSort)
668       {
669       case 0:  pszKey = (LPTSTR)pCopy->dwTick;  break;
670       case 1:  pszKey = (LPTSTR)szFlags;        break;
671       case 2:  pszKey = (LPTSTR)szExpr;         break;
672       case 3:  pszKey = (LPTSTR)szLocation;     break;
673       case 4:  pszKey = (LPTSTR)pCopy->cbData;  break;
674       case 5:  pszKey = (LPTSTR)pCopy->pData;   break;
675       }
676
677    LV_ITEM Item;
678    memset (&Item, 0x00, sizeof(Item));
679    Item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
680    Item.iItem = MemMgr_PickListInsertionPoint (hList, pszKey);
681    Item.iSubItem = 0;
682    Item.cchTextMax = 256;
683    Item.lParam = (LPARAM)pCopy->pData;
684
685    Item.pszText = szTime;
686    DWORD iItem = ListView_InsertItem (hList, &Item);
687    ListView_SetItemText (hList, iItem, 1, szFlags);
688    ListView_SetItemText (hList, iItem, 2, szExpr);
689    ListView_SetItemText (hList, iItem, 3, szLocation);
690    ListView_SetItemText (hList, iItem, 4, szBytes);
691    ListView_SetItemText (hList, iItem, 5, szAddress);
692
693    delete pCopy;
694 }
695
696
697 void MemMgr_AddToList (PMEMCHUNK pChunk)
698 {
699    PMEMCHUNK pCopy = new MEMCHUNK;
700    memcpy (pCopy, pChunk, sizeof(MEMCHUNK));
701
702    if (pChunk->fInList)
703       PostMessage (l.hManager, WM_COMMAND, IDC_LIST_REMOVEADD, (LPARAM)pCopy);
704    else
705       PostMessage (l.hManager, WM_COMMAND, IDC_LIST_ADD, (LPARAM)pCopy);
706    pChunk->fInList = TRUE;
707 }
708
709
710 void MemMgr_RestoreSettings (void)
711 {
712    lr.rManager.left = 0;
713    lr.rManager.top = 0;
714    lr.rManager.right = 510;
715    lr.rManager.bottom = 440;
716    lr.acxColumns[0] = 70;
717    lr.acxColumns[1] = 40;
718    lr.acxColumns[2] = 100;
719    lr.acxColumns[3] = 110;
720    lr.acxColumns[4] = 50;
721    lr.acxColumns[5] = 80;
722    lr.iColSort = 4;
723    lr.fSortRev = TRUE;
724
725    HKEY hk;
726    if (RegOpenKey (HKEY_LOCAL_MACHINE, TEXT("Software\\Random\\MemMgr"), &hk) == 0)
727       {
728       DWORD dwType = REG_BINARY;
729       DWORD dwSize = sizeof(lr);
730       RegQueryValueEx (hk, TEXT("Settings"), 0, &dwType, (PBYTE)&lr, &dwSize);
731       RegCloseKey (hk);
732       }
733 }
734
735 void MemMgr_StoreSettings (void)
736 {
737    if (IsWindow (l.hManager))
738       {
739       GetWindowRect (l.hManager, &lr.rManager);
740
741       HWND hList = GetDlgItem (l.hManager, IDC_LIST);
742       if (IsWindow (hList))
743          {
744          for (size_t ii = 0; ii < 6; ++ii)
745             lr.acxColumns[ ii ] = ListView_GetColumnWidth (hList, ii);
746          }
747       }
748
749    HKEY hk;
750    if (RegCreateKey (HKEY_LOCAL_MACHINE, TEXT("Software\\Random\\MemMgr"), &hk) == 0)
751       {
752       RegSetValueEx (hk, TEXT("Settings"), 0, REG_BINARY, (PBYTE)&lr, sizeof(lr));
753       RegCloseKey (hk);
754       }
755 }
756
757 void MemMgr_OnInit (void)
758 {
759    DWORD dwFlags = WS_CHILD | WS_TABSTOP | WS_VISIBLE;
760    MakeWindow (TEXT("Button"), TEXT("Allocation Statistics"), dwFlags | BS_GROUPBOX, NULL, l.hManager, IDC_BOX_ALLOC);
761    MakeWindow (TEXT("Static"), TEXT("C++ Objects:"), dwFlags, NULL, l.hManager, IDC_LABEL_CPP);
762    MakeWindow (TEXT("Static"), TEXT("Dynamic:"), dwFlags, NULL, l.hManager, IDC_LABEL_OTHER);
763    MakeWindow (TEXT("Static"), TEXT("Combined:"), dwFlags, NULL, l.hManager, IDC_LABEL_TOTAL);
764    MakeWindow (TEXT("Static"), TEXT("Tared:"), dwFlags, NULL, l.hManager, IDC_LABEL_TARED);
765    MakeWindow (TEXT("Static"), TEXT("Count"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_LABEL_COUNT);
766    MakeWindow (TEXT("Static"), TEXT("Size"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_LABEL_SIZE);
767    MakeWindow (TEXT("Static"), TEXT("Average"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_LABEL_AVERAGE);
768    MakeWindow (TEXT("Static"), TEXT("0"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_CPP_COUNT);
769    MakeWindow (TEXT("Static"), TEXT("1"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_OTHER_COUNT);
770    MakeWindow (TEXT("Static"), TEXT("2"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_TARED_COUNT);
771    MakeWindow (TEXT("Static"), TEXT("3"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_TOTAL_COUNT);
772    MakeWindow (TEXT("Static"), TEXT("4"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_CPP_SIZE);
773    MakeWindow (TEXT("Static"), TEXT("5"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_OTHER_SIZE);
774    MakeWindow (TEXT("Static"), TEXT("6"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_TARED_SIZE);
775    MakeWindow (TEXT("Static"), TEXT("7"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_TOTAL_SIZE);
776    MakeWindow (TEXT("Static"), TEXT("8"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_CPP_AVERAGE);
777    MakeWindow (TEXT("Static"), TEXT("9"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_OTHER_AVERAGE);
778    MakeWindow (TEXT("Static"), TEXT("10"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_TARED_AVERAGE);
779    MakeWindow (TEXT("Static"), TEXT("11"), dwFlags | SS_CENTER, NULL, l.hManager, IDC_VALUE_TOTAL_AVERAGE);
780    MakeWindow (TEXT("Button"), TEXT("Details"), dwFlags | BS_GROUPBOX, NULL, l.hManager, IDC_BOX_DETAILS);
781    MakeWindow (WC_LISTVIEW, TEXT(""), dwFlags | LVS_REPORT | LVS_SINGLESEL, NULL, l.hManager, IDC_LIST, WS_EX_CLIENTEDGE);
782 // MakeWindow (TEXT("Button"), TEXT("Label"), dwFlags, NULL, l.hManager, IDC_LABEL);
783 // MakeWindow (TEXT("Button"), TEXT("Hidden"), dwFlags, NULL, l.hManager, IDC_HIDE);
784    MakeWindow (TEXT("Button"), TEXT("Tare"), dwFlags, NULL, l.hManager, IDC_TARE);
785    MakeWindow (TEXT("Button"), TEXT("Restore"), dwFlags, NULL, l.hManager, IDC_RESET);
786    MakeWindow (TEXT("Button"), TEXT("Close"), dwFlags, NULL, l.hManager, IDCANCEL);
787    MemMgr_OnSize();
788
789    HWND hList = GetDlgItem (l.hManager, IDC_LIST);
790
791    LV_COLUMN Col;
792    Col.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT | LVCF_SUBITEM;
793    Col.fmt = LVCFMT_LEFT;
794    Col.iSubItem = 0;
795    Col.cx = lr.acxColumns[0];
796    Col.pszText = TEXT("Time");
797    ListView_InsertColumn (hList, Col.iSubItem, &Col);
798
799    Col.cx = lr.acxColumns[1];
800    Col.pszText = TEXT("Flags");
801    Col.iSubItem++;
802    ListView_InsertColumn (hList, Col.iSubItem, &Col);
803
804    Col.cx = lr.acxColumns[2];
805    Col.pszText = TEXT("Expression");
806    Col.iSubItem++;
807    ListView_InsertColumn (hList, Col.iSubItem, &Col);
808
809    Col.cx = lr.acxColumns[3];
810    Col.pszText = TEXT("Location");
811    Col.iSubItem++;
812    ListView_InsertColumn (hList, Col.iSubItem, &Col);
813
814    Col.fmt = LVCFMT_RIGHT;
815    Col.cx = lr.acxColumns[4];
816    Col.pszText = TEXT("Size");
817    Col.iSubItem++;
818    ListView_InsertColumn (hList, Col.iSubItem, &Col);
819
820    Col.cx = lr.acxColumns[5];
821    Col.pszText = TEXT("Address");
822    Col.iSubItem++;
823    ListView_InsertColumn (hList, Col.iSubItem, &Col);
824
825    EnterCriticalSection (l.pcs);
826    SendMessage (GetDlgItem (l.hManager, IDC_LIST), WM_SETREDRAW, FALSE, 0);
827
828    for (size_t iChunk = 0; iChunk < l.cChunks; ++iChunk)
829       {
830       PMEMCHUNK pChunk;
831       if ((pChunk = (PMEMCHUNK)l.pHeap->GetAt(iChunk)) == NULL)
832          continue;
833
834       pChunk->fInList = FALSE;
835       if (!pChunk->fTared && !pChunk->fFreed)
836          MemMgr_AddToList (pChunk);
837       }
838
839    SendMessage (GetDlgItem (l.hManager, IDC_LIST), WM_SETREDRAW, TRUE, 0);
840    LeaveCriticalSection (l.pcs);
841
842    l.idTimer = SetTimer (l.hManager, 0, cmsecREFRESH, NULL);
843 }
844
845 void MemMgr_OnRefresh (void)
846 {
847    // Fill in the statistics at the top of the manager dialog
848    //
849    SetDlgItemInt (l.hManager, IDC_VALUE_CPP_COUNT, l.Stats.cAllocCpp, TRUE);
850    SetDlgItemInt (l.hManager, IDC_VALUE_OTHER_COUNT, l.Stats.cAllocDyna, TRUE);
851    SetDlgItemInt (l.hManager, IDC_VALUE_TARED_COUNT, l.Stats.cAllocTotalTared, TRUE);
852    SetDlgItemInt (l.hManager, IDC_VALUE_TOTAL_COUNT, l.Stats.cAllocTotal, TRUE);
853
854    SetDlgItemBytes (l.hManager, IDC_VALUE_CPP_SIZE, (double)l.Stats.cbAllocCpp);
855    SetDlgItemBytes (l.hManager, IDC_VALUE_OTHER_SIZE, (double)l.Stats.cbAllocDyna);
856    SetDlgItemBytes (l.hManager, IDC_VALUE_TARED_SIZE, (double)l.Stats.cbAllocTotalTared);
857    SetDlgItemBytes (l.hManager, IDC_VALUE_TOTAL_SIZE, (double)l.Stats.cbAllocTotal);
858
859    SetDlgItemBytes (l.hManager, IDC_VALUE_CPP_AVERAGE, (l.Stats.cAllocCpp) ? ((double)l.Stats.cbAllocCpp / l.Stats.cAllocCpp) : 0);
860    SetDlgItemBytes (l.hManager, IDC_VALUE_OTHER_AVERAGE, (l.Stats.cAllocDyna) ? ((double)l.Stats.cbAllocDyna / l.Stats.cAllocDyna) : 0);
861    SetDlgItemBytes (l.hManager, IDC_VALUE_TARED_AVERAGE, (l.Stats.cAllocTotalTared) ? ((double)l.Stats.cbAllocTotalTared / l.Stats.cAllocTotalTared) : 0);
862    SetDlgItemBytes (l.hManager, IDC_VALUE_TOTAL_AVERAGE, (l.Stats.cAllocTotal) ? ((double)l.Stats.cbAllocTotal / l.Stats.cAllocTotal) : 0);
863 }
864
865 void MemMgr_OnLabel (void)
866 {
867    // TODO
868 }
869
870 void MemMgr_OnHide (void)
871 {
872    // TODO
873 }
874
875 void MemMgr_OnReset (void)
876 {
877    l.Stats.cAllocCpp += l.Stats.cAllocCppTared;
878    l.Stats.cAllocDyna += l.Stats.cAllocDynaTared;
879    l.Stats.cAllocTotal += l.Stats.cAllocTotalTared;
880    l.Stats.cbAllocCpp += l.Stats.cbAllocCppTared;
881    l.Stats.cbAllocDyna += l.Stats.cbAllocDynaTared;
882    l.Stats.cbAllocTotal += l.Stats.cbAllocTotalTared;
883
884    l.Stats.cAllocCppTared = 0;
885    l.Stats.cAllocDynaTared = 0;
886    l.Stats.cAllocTotalTared = 0;
887    l.Stats.cbAllocCppTared = 0;
888    l.Stats.cbAllocDynaTared = 0;
889    l.Stats.cbAllocTotalTared = 0;
890
891    EnterCriticalSection (l.pcs);
892    SendMessage (GetDlgItem (l.hManager, IDC_LIST), WM_SETREDRAW, FALSE, 0);
893
894    ListView_DeleteAllItems (GetDlgItem (l.hManager, IDC_LIST));
895
896    for (size_t iChunk = 0; iChunk < l.cChunks; ++iChunk)
897       {
898       PMEMCHUNK pChunk;
899       if ((pChunk = (PMEMCHUNK)l.pHeap->GetAt(iChunk)) == NULL)
900          continue;
901
902       pChunk->fInList = FALSE;
903       pChunk->fTared = FALSE;
904       if (!pChunk->fFreed)
905          MemMgr_AddToList (pChunk);
906       }
907
908    SendMessage (GetDlgItem (l.hManager, IDC_LIST), WM_SETREDRAW, TRUE, 0);
909    LeaveCriticalSection (l.pcs);
910
911    MemMgr_OnRefresh();
912 }
913
914 void MemMgr_OnTare (void)
915 {
916    l.Stats.cAllocCppTared += l.Stats.cAllocCpp;
917    l.Stats.cAllocDynaTared += l.Stats.cAllocDyna;
918    l.Stats.cAllocTotalTared += l.Stats.cAllocTotal;
919    l.Stats.cbAllocCppTared += l.Stats.cbAllocCpp;
920    l.Stats.cbAllocDynaTared += l.Stats.cbAllocDyna;
921    l.Stats.cbAllocTotalTared += l.Stats.cbAllocTotal;
922
923    l.Stats.cAllocCpp = 0;
924    l.Stats.cAllocDyna = 0;
925    l.Stats.cAllocTotal = 0;
926    l.Stats.cbAllocCpp = 0;
927    l.Stats.cbAllocDyna = 0;
928    l.Stats.cbAllocTotal = 0;
929
930    EnterCriticalSection (l.pcs);
931    SendMessage (GetDlgItem (l.hManager, IDC_LIST), WM_SETREDRAW, FALSE, 0);
932
933    ListView_DeleteAllItems (GetDlgItem (l.hManager, IDC_LIST));
934
935    for (size_t iChunk = 0; iChunk < l.cChunks; ++iChunk)
936       {
937       PMEMCHUNK pChunk;
938       if ((pChunk = (PMEMCHUNK)l.pHeap->GetAt(iChunk)) == NULL)
939          continue;
940
941       pChunk->fInList = FALSE;
942       pChunk->fTared = TRUE;
943       }
944
945    SendMessage (GetDlgItem (l.hManager, IDC_LIST), WM_SETREDRAW, TRUE, 0);
946    LeaveCriticalSection (l.pcs);
947
948    MemMgr_OnRefresh();
949 }
950
951
952 void MemMgr_OnSort (void)
953 {
954    EnterCriticalSection (l.pcs);
955    SendMessage (GetDlgItem (l.hManager, IDC_LIST), WM_SETREDRAW, FALSE, 0);
956
957    ListView_DeleteAllItems (GetDlgItem (l.hManager, IDC_LIST));
958
959    for (size_t iChunk = 0; iChunk < l.cChunks; ++iChunk)
960       {
961       PMEMCHUNK pChunk;
962       if ((pChunk = (PMEMCHUNK)l.pHeap->GetAt(iChunk)) == NULL)
963          continue;
964
965       pChunk->fInList = FALSE;
966       if (!pChunk->fTared && !pChunk->fFreed)
967          MemMgr_AddToList (pChunk);
968       }
969
970    SendMessage (GetDlgItem (l.hManager, IDC_LIST), WM_SETREDRAW, TRUE, 0);
971    LeaveCriticalSection (l.pcs);
972 }
973
974
975 void MemMgr_OnPaint (void)
976 {
977    PAINTSTRUCT ps;
978    HDC hdc = BeginPaint (l.hManager, &ps);
979
980    static HBRUSH hbr = (HBRUSH)CreateSolidBrush (GetSysColor (COLOR_BTNFACE));
981    FillRect (hdc, &ps.rcPaint, hbr);
982
983    EndPaint (l.hManager, &ps);
984 }
985
986
987 void MemMgr_OnTimer (void)
988 {
989    if (GetWindowLong (l.hManager, GWL_USERDATA))
990       {
991       SetWindowLong (l.hManager, GWL_USERDATA, 0);
992       MemMgr_OnRefresh();
993       }
994 }
995
996
997 void MemMgr_OnDelayedRefresh (void)
998 {
999    SetWindowLong (l.hManager, GWL_USERDATA, 1);
1000 }
1001
1002
1003 BOOL CALLBACK MemMgr_DlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
1004 {
1005    switch (msg)
1006       {
1007       case WM_DESTROY:
1008          MemMgr_StoreSettings();
1009          l.hManager = NULL;
1010          KillTimer (hDlg, l.idTimer);
1011          l.idTimer = -1;
1012          break;
1013
1014       case WM_SIZE:
1015          MemMgr_OnSize();
1016          MemMgr_StoreSettings();
1017          break;
1018
1019       case WM_MOVE:
1020          MemMgr_StoreSettings();
1021          break;
1022
1023       case WM_GETMINMAXINFO:
1024          LPMINMAXINFO lpmmi;
1025          lpmmi = (LPMINMAXINFO)lp;
1026          lpmmi->ptMinTrackSize.x = cxBUTTONS*5 + cxBETWEEN*4 + cBORDER*2 + 8;
1027          lpmmi->ptMinTrackSize.y = 270;
1028          return FALSE;
1029
1030       case WM_PAINT:
1031          MemMgr_OnPaint();
1032          break;
1033
1034       case WM_NOTIFY:
1035          switch (((NMHDR *)lp)->code)
1036             {
1037             case LVN_COLUMNCLICK:
1038                if (((NM_LISTVIEW*)lp)->iSubItem == lr.iColSort)
1039                   lr.fSortRev = !lr.fSortRev;
1040                else
1041                   {
1042                   lr.iColSort = ((NM_LISTVIEW*)lp)->iSubItem;
1043                   lr.fSortRev = FALSE;
1044                   }
1045                MemMgr_OnSort();
1046                MemMgr_StoreSettings();
1047                break;
1048             }
1049          break;
1050
1051       case WM_TIMER:
1052          MemMgr_OnTimer();
1053          break;
1054
1055       case WM_COMMAND:
1056          switch (LOWORD(wp))
1057             {
1058             case IDCANCEL:
1059                DestroyWindow (hDlg);
1060                break;
1061
1062             case IDC_INITIALIZE:
1063                MemMgr_OnInit();
1064                MemMgr_OnRefresh();
1065                break;
1066
1067             case IDC_REFRESH:
1068                MemMgr_OnDelayedRefresh();
1069                break;
1070
1071             case IDC_LABEL:
1072                MemMgr_OnLabel();
1073                break;
1074
1075             case IDC_HIDE:
1076                MemMgr_OnHide();
1077                break;
1078
1079             case IDC_RESET:
1080                MemMgr_OnReset();
1081                break;
1082
1083             case IDC_TARE:
1084                MemMgr_OnTare();
1085                break;
1086
1087             case IDC_LIST_REMOVEADD:
1088                MemMgr_OnListRemove (((PMEMCHUNK)lp)->pData);
1089                MemMgr_OnListAdd ((PMEMCHUNK)lp);
1090                break;
1091
1092             case IDC_LIST_ADD:
1093                MemMgr_OnListAdd ((PMEMCHUNK)lp);
1094                break;
1095
1096             case IDC_LIST_REMOVE:
1097                MemMgr_OnListRemove ((PVOID)lp);
1098                break;
1099             }
1100          break;
1101       }
1102
1103    return DefWindowProc (hDlg, msg, wp, lp);
1104 }
1105
1106
1107 /*
1108  * MANAGEMENT ROUTINES ________________________________________________________
1109  *
1110  */
1111
1112 BOOL MemMgr_Initialize (void)
1113 {
1114    if (!l.pHeap)
1115       {
1116       l.pcs = new CRITICAL_SECTION;
1117       InitializeCriticalSection (l.pcs);
1118
1119       l.pHeap = new ALLOCEXPANDARRAY (sizeof(MEMCHUNK));
1120
1121       REALLOC (l.aBuckets, l.cBuckets, cDEFAULT_BUCKETS, 1);
1122       if (l.aBuckets)
1123          {
1124          for (size_t ii = 0; ii < l.cBuckets; ++ii)
1125             l.aBuckets[ii].iFirst = iINVALID;
1126          }
1127       }
1128
1129    return (l.pHeap) && (l.aBuckets);
1130 }
1131
1132
1133 void MemMgr_TrackAllocation (PVOID pData, size_t cb, LPSTR pszExpr, LPSTR pszFile, DWORD dwLine, BOOL fSig)
1134 {
1135    if (!pData)
1136       return;
1137    if (!MemMgr_Initialize())
1138       return;
1139
1140    EnterCriticalSection (l.pcs);
1141
1142    // Ensure our hash table will be large enough to handle the new
1143    // MEMCHUNK we're about to assign.
1144    //
1145    if (l.cBuckets < MIN_BUCKETS(l.cChunks+1))
1146       {
1147       // Too small! Rebuild the hash table.
1148       //
1149       REALLOC (l.aBuckets, l.cBuckets, TARGET_BUCKETS(l.cChunks+1), 1);
1150       if (l.aBuckets)
1151          {
1152          for (size_t ii = 0; ii < l.cBuckets; ++ii)
1153             l.aBuckets[ii].iFirst = iINVALID;
1154
1155          for (size_t iChunk = 0; iChunk < l.cChunks; ++iChunk)
1156             {
1157             PMEMCHUNK pChunk;
1158             if ((pChunk = (PMEMCHUNK)l.pHeap->GetAt(iChunk)) == NULL)
1159                continue;
1160
1161             size_t iBucket = HASH(pChunk->pData,l.cBuckets);
1162             if ((pChunk->iNext = l.aBuckets[iBucket].iFirst) != iINVALID)
1163                {
1164                PMEMCHUNK pNext;
1165                if ((pNext = (PMEMCHUNK)l.pHeap->GetAt(pChunk->iNext)) != NULL)
1166                   pNext->iPrev = iChunk;
1167                }
1168             l.aBuckets[iBucket].iFirst = iChunk;
1169             pChunk->iPrev = iINVALID;
1170             }
1171          }
1172       }
1173
1174    // If space was allocated for a trailing signature, write one
1175    //
1176    if (fSig)
1177       *(DWORD *)( (PBYTE)pData + cb ) = dwSIG_AT_END;
1178
1179    // Prepare a MEMCHUNK entry and shove it in our array
1180    //
1181    size_t iChunk = l.cChunks;
1182    size_t iBucket = HASH(pData,l.cBuckets);
1183    BOOL fLinkIn = TRUE;
1184
1185    MEMCHUNK Chunk;
1186    Chunk.pData = pData;
1187    Chunk.fFreed = FALSE;
1188    Chunk.cbData = cb;
1189    Chunk.pszExpr = pszExpr;
1190    Chunk.pszFile = pszFile;
1191    Chunk.fCPP = !fSig;
1192    Chunk.dwLine = dwLine;
1193    Chunk.dwTick = GetTickCount();
1194    Chunk.dwEndSig = (fSig) ? dwSIG_AT_END : 0;
1195    Chunk.fInList = FALSE;
1196    Chunk.fTared = FALSE;
1197
1198 #ifdef TRACK_FREED
1199    // Find the memchunk associated with this pData. That's what our
1200    // hash table is for.
1201    //
1202    for (iChunk = l.aBuckets[iBucket].iFirst; iChunk != iINVALID; )
1203       {
1204       PMEMCHUNK pChunk;
1205       if ((pChunk = (PMEMCHUNK)l.pHeap->GetAt(iChunk)) != NULL)
1206          {
1207          if (pChunk->pData == pData)
1208             {
1209             Chunk.iNext = pChunk->iNext;
1210             Chunk.iPrev = pChunk->iPrev;
1211             fLinkIn = FALSE;
1212             break;
1213             }
1214          }
1215       iChunk = (pChunk) ? pChunk->iNext : iINVALID;
1216       }
1217    if (iChunk == iINVALID)
1218       iChunk = l.cChunks;
1219 #endif
1220
1221    l.cChunks = max (l.cChunks, 1+iChunk);
1222
1223    if (fLinkIn)
1224       {
1225       if ((Chunk.iNext = l.aBuckets[iBucket].iFirst) != iINVALID)
1226          {
1227          PMEMCHUNK pNext;
1228          if ((pNext = (PMEMCHUNK)l.pHeap->GetAt(Chunk.iNext)) != NULL)
1229             pNext->iPrev = iChunk;
1230          }
1231       l.aBuckets[iBucket].iFirst = iChunk;
1232       Chunk.iPrev = iINVALID;
1233       }
1234
1235    if (IsWindow (l.hManager))
1236       MemMgr_AddToList (&Chunk);
1237
1238    l.pHeap->SetAt (iChunk, &Chunk);
1239
1240    if (Chunk.fCPP)
1241       l.Stats.cAllocCpp ++;
1242    else // (!Chunk.fCPP)
1243       l.Stats.cAllocDyna ++;
1244
1245    if (Chunk.fCPP)
1246       l.Stats.cbAllocCpp += Chunk.cbData;
1247    else // (!Chunk.fCPP)
1248       l.Stats.cbAllocDyna += Chunk.cbData;
1249
1250    l.Stats.cAllocTotal ++;
1251    l.Stats.cbAllocTotal += Chunk.cbData;
1252
1253    LeaveCriticalSection (l.pcs);
1254
1255    if (IsWindow (l.hManager))
1256       PostMessage (l.hManager, WM_COMMAND, IDC_REFRESH, 0);
1257 }
1258
1259
1260 BOOL MemMgr_TrackDestruction (PVOID pData, LPSTR pszFile, DWORD dwLine)
1261 {
1262    if (MemMgr_Initialize())
1263       {
1264       EnterCriticalSection (l.pcs);
1265
1266       // Find the memchunk associated with this pData. That's what our
1267       // hash table is for.
1268       //
1269       size_t iBucket = HASH(pData,l.cBuckets);
1270
1271       PMEMCHUNK pChunk = NULL;
1272       for (size_t iChunk = l.aBuckets[iBucket].iFirst; iChunk != iINVALID; )
1273          {
1274          if ((pChunk = (PMEMCHUNK)l.pHeap->GetAt(iChunk)) == NULL)
1275             break;
1276          if (pChunk->pData == pData)
1277             break;
1278          if ((iChunk = pChunk->iNext) == iINVALID)
1279             {
1280             pChunk = NULL;
1281             break;
1282             }
1283          }
1284
1285       if (!pChunk)
1286          {
1287          MEMCHUNK Sim;
1288          memset (&Sim, 0x00, sizeof(MEMCHUNK));
1289          Sim.pData = pData;
1290          Sim.pszExpr = "(unknown)";
1291          Sim.pszFile = "(unknown)";
1292          MemMgr_ShowWarning (&Sim, pszFile, dwLine, TEXT("An invalid memory address was freed."));
1293          }
1294       else if (pChunk->dwEndSig && (*(DWORD*)((PBYTE)pData + pChunk->cbData) != pChunk->dwEndSig))
1295          {
1296          MemMgr_ShowWarning (pChunk, pszFile, dwLine, TEXT("The trailing signature on a block of memory was overwritten."));
1297          }
1298       else if (pChunk->fFreed)
1299          {
1300          MemMgr_ShowWarning (pChunk, pszFile, dwLine, TEXT("A block of memory was freed more than once."));
1301          }
1302
1303       if (pChunk)
1304          {
1305          pChunk->fFreed = TRUE;
1306
1307          if (IsWindow (l.hManager))
1308             {
1309             if (pChunk->fInList)
1310                MemMgr_RemoveFromList (pChunk);
1311             }
1312
1313          if (pChunk->fCPP)
1314             l.Stats.cAllocCpp --;
1315          else // (!pChunk->fCPP)
1316             l.Stats.cAllocDyna --;
1317
1318          if (pChunk->fCPP)
1319             l.Stats.cbAllocCpp -= pChunk->cbData;
1320          else // (!pChunk->fCPP)
1321             l.Stats.cbAllocDyna -= pChunk->cbData;
1322
1323          l.Stats.cAllocTotal --;
1324          l.Stats.cbAllocTotal -= pChunk->cbData;
1325
1326 #ifndef TRACK_FREED
1327
1328          // Unlink this chunk from the hash table
1329          //
1330          if (pChunk->iNext != iINVALID)
1331             {
1332             PMEMCHUNK pNext;
1333             if ((pNext = (PMEMCHUNK)l.pHeap->GetAt(pChunk->iNext)) != NULL)
1334                pNext->iPrev = pChunk->iPrev;
1335             }
1336          if (pChunk->iPrev != iINVALID)
1337             {
1338             PMEMCHUNK pPrev;
1339             if ((pPrev = (PMEMCHUNK)l.pHeap->GetAt(pChunk->iPrev)) != NULL)
1340                pPrev->iNext = pChunk->iNext;
1341             }
1342          if (l.aBuckets[iBucket].iFirst == iChunk)
1343             l.aBuckets[iBucket].iFirst = pChunk->iNext;
1344
1345          // Empty this element in the expandarray. That means we need to
1346          // take the last element and shove it in here--so we'll have do
1347          // rework the hash table a little bit more.
1348          //
1349          if (iChunk != l.cChunks-1)
1350             {
1351             PMEMCHUNK pMove;
1352             if ((pMove = (PMEMCHUNK)l.pHeap->GetAt(l.cChunks-1)) != NULL)
1353                {
1354                size_t iBucket = HASH(pMove->pData,l.cBuckets);
1355
1356                if (pMove->iNext != iINVALID)
1357                   {
1358                   PMEMCHUNK pNext;
1359                   if ((pNext = (PMEMCHUNK)l.pHeap->GetAt(pMove->iNext)) != NULL)
1360                      pNext->iPrev = iChunk;
1361                   }
1362                if (pMove->iPrev != iINVALID)
1363                   {
1364                   PMEMCHUNK pPrev;
1365                   if ((pPrev = (PMEMCHUNK)l.pHeap->GetAt(pMove->iPrev)) != NULL)
1366                      pPrev->iNext = iChunk;
1367                   }
1368                if (l.aBuckets[iBucket].iFirst == (l.cChunks-1))
1369                   l.aBuckets[iBucket].iFirst = iChunk;
1370
1371                l.pHeap->SetAt (iChunk, pMove);
1372                memset (pMove, 0x00, sizeof(MEMCHUNK));
1373                }
1374             }
1375
1376          l.cChunks --;
1377
1378 #endif // TRACK_FREED
1379          }
1380
1381       LeaveCriticalSection (l.pcs);
1382       }
1383
1384    if (IsWindow (l.hManager))
1385       PostMessage (l.hManager, WM_COMMAND, IDC_REFRESH, 0);
1386    return TRUE;
1387 }
1388
1389
1390 /*
1391  * PUBLIC ROUTINES ____________________________________________________________
1392  *
1393  */
1394
1395 void MEMMGR_CALLCONV ShowMemoryManager (void)
1396 {
1397    if (MemMgr_Initialize())
1398       {
1399       if (!IsWindow (l.hManager))
1400          {
1401          MemMgr_RestoreSettings();
1402          l.hManager = MakeWindow (TEXT("Static"), cszTITLE, WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU, &lr.rManager, 0, 0);
1403          SetWindowLong (l.hManager, GWL_WNDPROC, (LONG)MemMgr_DlgProc);
1404          PostMessage (l.hManager, WM_COMMAND, IDC_INITIALIZE, 0);
1405          ShowWindow (l.hManager, SW_SHOW);
1406          }
1407       }
1408 }
1409
1410
1411 void MEMMGR_CALLCONV WhileMemoryManagerShowing (void)
1412 {
1413    if (MemMgr_Initialize())
1414       {
1415       while (IsWindow (l.hManager))
1416          {
1417          MSG msg;
1418          if (!GetMessage (&msg, NULL, 0, 0))
1419             break;
1420
1421          if (!IsDialogMessage (l.hManager, &msg))
1422             {
1423             TranslateMessage (&msg);
1424             DispatchMessage (&msg);
1425             }
1426          }
1427       }
1428 }
1429
1430
1431 BOOL MEMMGR_CALLCONV IsMemoryManagerMessage (MSG *pMsg)
1432 {
1433    if (IsWindow (l.hManager))
1434       {
1435       if (pMsg->hwnd == l.hManager)
1436          {
1437 //       if (IsDialogMessage (l.hManager, pMsg))
1438 //          return TRUE;
1439          }
1440       }
1441    else if ((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_F8))
1442       {
1443       ShowMemoryManager();
1444       return TRUE;
1445       }
1446    return FALSE;
1447 }
1448
1449
1450 PVOID MEMMGR_CALLCONV MemMgr_AllocateMemory (size_t cb, LPSTR pszExpr, LPSTR pszFile, DWORD dwLine)
1451 {
1452    PVOID pData = GlobalAlloc (GMEM_FIXED, cb + sizeof(DWORD));
1453    MemMgr_TrackAllocation (pData, cb, pszExpr, pszFile, dwLine, TRUE);
1454    return pData;
1455 }
1456
1457
1458 void MEMMGR_CALLCONV MemMgr_FreeMemory (PVOID pData, LPSTR pszFile, DWORD dwLine)
1459 {
1460    if (MemMgr_TrackDestruction (pData, pszFile, dwLine))
1461       {
1462       GlobalFree ((HGLOBAL)pData);
1463       }
1464 }
1465
1466
1467 PVOID MEMMGR_CALLCONV MemMgr_TrackNew (PVOID pData, size_t cb, LPSTR pszExpr, LPSTR pszFile, DWORD dwLine)
1468 {
1469    MemMgr_TrackAllocation (pData, cb, pszExpr, pszFile, dwLine, FALSE);
1470    return pData;
1471 }
1472
1473
1474 void MEMMGR_CALLCONV MemMgr_TrackDelete (PVOID pData, LPSTR pszFile, DWORD dwLine)
1475 {
1476    MemMgr_TrackDestruction (pData, pszFile, dwLine);
1477 }
1478
1479
1480 /*
1481  * EXPANDARRAY ________________________________________________________________
1482  *
1483  * We'll use an EXPANDARRAY to manage our heap of allocation information.
1484  * Technically I would prefer to use a full HASHLIST here, but I don't want
1485  * this source file to be dependent upon yet another file. So we'll kinda
1486  * write a special-purpose hash system.
1487  *
1488  */
1489
1490 #define cEXPANDARRAYHEAPELEMENTS  1024
1491 #define cREALLOC_EXPANDARRAYHEAPS   16
1492
1493 ALLOCEXPANDARRAY::ALLOCEXPANDARRAY (size_t cbElement, size_t cElementsPerHeap)
1494 {
1495    if ((m_cbElement = cbElement) == 0)
1496       m_cbElement = sizeof(DWORD);
1497    if ((m_cElementsPerHeap = cElementsPerHeap) == 0)
1498       m_cElementsPerHeap = cEXPANDARRAYHEAPELEMENTS;
1499
1500    m_cHeaps = 0;
1501    m_aHeaps = 0;
1502 }
1503
1504 ALLOCEXPANDARRAY::~ALLOCEXPANDARRAY (void)
1505 {
1506    if (m_aHeaps)
1507       {
1508       for (size_t ii = 0; ii < m_cHeaps; ++ii)
1509          GlobalFree ((HGLOBAL)(m_aHeaps[ ii ]));
1510       GlobalFree ((HGLOBAL)m_aHeaps);
1511       }
1512 }
1513
1514 PVOID ALLOCEXPANDARRAY::GetAt (size_t iElement)
1515 {
1516    size_t iHeap = iElement / m_cElementsPerHeap;
1517    size_t iIndex = iElement % m_cElementsPerHeap;
1518    if ((iHeap >= m_cHeaps) || (!m_aHeaps[iHeap]))
1519       return NULL;
1520    return (PVOID)&((PBYTE)m_aHeaps[ iHeap ]->aElements)[ iIndex * m_cbElement ];
1521 }
1522
1523 void ALLOCEXPANDARRAY::SetAt (size_t iElement, PVOID pData)
1524 {
1525    size_t iHeap = iElement / m_cElementsPerHeap;
1526    size_t iIndex = iElement % m_cElementsPerHeap;
1527
1528    if (!REALLOC (m_aHeaps, m_cHeaps, 1+iHeap, cREALLOC_EXPANDARRAYHEAPS))
1529       return;
1530
1531    if (!m_aHeaps[ iHeap ])
1532       {
1533       size_t cbHeap = sizeof(EXPANDARRAYHEAP) + (m_cElementsPerHeap * m_cbElement);
1534       if ((m_aHeaps[ iHeap ] = (LPEXPANDARRAYHEAP)GlobalAlloc (GMEM_FIXED, cbHeap)) == NULL)
1535          return;
1536       memset (m_aHeaps[ iHeap ], 0x00, cbHeap);
1537       m_aHeaps[ iHeap ]->aElements = ((PBYTE)m_aHeaps[ iHeap ]) + sizeof(EXPANDARRAYHEAP);
1538       }
1539
1540    if (!pData)
1541       memset (&((PBYTE)m_aHeaps[ iHeap ]->aElements)[ iIndex * m_cbElement ], 0x00, m_cbElement);
1542    else
1543       memcpy (&((PBYTE)m_aHeaps[ iHeap ]->aElements)[ iIndex * m_cbElement ], pData, m_cbElement);
1544 }
1545