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