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