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