489 lines
10 KiB
C
489 lines
10 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
MyHeap.h
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Implementation of a (dumb and) fast heap allocator
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Hakki T. Bostanci (hakkib) 06-Apr-2000
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#ifndef MYHEAP_H
|
||
|
#define MYHEAP_H
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
#undef ALIGNMENT
|
||
|
#define ALIGNMENT 8
|
||
|
#endif //WIN32
|
||
|
|
||
|
#ifdef _WIN64
|
||
|
#undef ALIGNMENT
|
||
|
#define ALIGNMENT 16
|
||
|
#endif //WIN64
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
typedef enum { nAllocBlockSize = 64*1024 };
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
template <size_t nBucketSize = nAllocBlockSize>
|
||
|
class CMyHeap
|
||
|
{
|
||
|
public:
|
||
|
CMyHeap()
|
||
|
{
|
||
|
InitializeCriticalSection(&m_cs);
|
||
|
|
||
|
m_pBucket = 0;
|
||
|
m_pNextFree = 0;
|
||
|
}
|
||
|
|
||
|
~CMyHeap()
|
||
|
{
|
||
|
DumpAllocations();
|
||
|
FreeAllBuckets();
|
||
|
|
||
|
DeleteCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
void *allocate(size_t nSize, ULONG uFlags = 0)
|
||
|
{
|
||
|
int nAllocSize = AllocSize(nSize);
|
||
|
|
||
|
if (nAllocSize < 0)
|
||
|
{
|
||
|
if (uFlags & HEAP_GENERATE_EXCEPTIONS)
|
||
|
{
|
||
|
RaiseException(STATUS_NO_MEMORY, 0, 0, 0);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (!(uFlags & HEAP_NO_SERIALIZE))
|
||
|
{
|
||
|
EnterCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
// do we have enough buffer space?
|
||
|
|
||
|
if (m_pBucket == 0 ||
|
||
|
m_pNextFree + nAllocSize > (PBYTE) m_pBucket + nBucketSize)
|
||
|
{
|
||
|
// if we can manage the requested allocation size,
|
||
|
// try to allocate a new bucket
|
||
|
|
||
|
if (nAllocSize > nBucketSize || AllocNewBucket() == false)
|
||
|
{
|
||
|
// if we fail the allocation, return 0 or raise an exception
|
||
|
|
||
|
if (!(uFlags & HEAP_NO_SERIALIZE))
|
||
|
{
|
||
|
LeaveCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
if (uFlags & HEAP_GENERATE_EXCEPTIONS)
|
||
|
{
|
||
|
RaiseException(STATUS_NO_MEMORY, 0, 0, 0);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ok, we have enough space, allocate and initialize a block
|
||
|
|
||
|
ASSERT(m_pBucket->Contains(m_pNextFree));
|
||
|
|
||
|
CAllocation *pAllocation = (CAllocation *) m_pNextFree;
|
||
|
pAllocation->m_nRefCount = 1;
|
||
|
pAllocation->m_nDataSize = nSize;
|
||
|
|
||
|
m_pNextFree += nAllocSize;
|
||
|
m_pBucket->m_nAllocated += nAllocSize;
|
||
|
|
||
|
ASSERT(m_pBucket->m_nAllocated < nBucketSize);
|
||
|
|
||
|
// we are done
|
||
|
|
||
|
if (!(uFlags & HEAP_NO_SERIALIZE))
|
||
|
{
|
||
|
LeaveCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
if (uFlags & HEAP_ZERO_MEMORY)
|
||
|
{
|
||
|
// virtual alloc has already given us zero-init memory
|
||
|
}
|
||
|
|
||
|
return pAllocation->m_Data;
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
T *allocate_array(size_t nItems, ULONG uFlags = 0)
|
||
|
{
|
||
|
return (T *) g_MyHeap.allocate(nItems * sizeof(T), uFlags);
|
||
|
}
|
||
|
|
||
|
void AddRef(void *pMem)
|
||
|
{
|
||
|
if (pMem)
|
||
|
{
|
||
|
CAllocation *pAllocation = CONTAINING_RECORD(pMem, CAllocation, m_Data);
|
||
|
|
||
|
ASSERT(pAllocation->m_nRefCount != 0);
|
||
|
|
||
|
InterlockedIncrement(&pAllocation->m_nRefCount);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void deallocate(void *pMem, ULONG uFlags = 0)
|
||
|
{
|
||
|
if (!(uFlags & HEAP_NO_SERIALIZE))
|
||
|
{
|
||
|
EnterCriticalSection(&m_cs);
|
||
|
}
|
||
|
|
||
|
// find which bucket this allocation belongs to
|
||
|
|
||
|
CBucket *pBucket;
|
||
|
|
||
|
if (nBucketSize == nAllocBlockSize)
|
||
|
{
|
||
|
// if the bucket size is aligned with the VirtualAlloc block size,
|
||
|
// then simply zero the lower WORD to reach to the base address
|
||
|
|
||
|
pBucket = (CBucket *) ((UINT_PTR)pMem & ~(nAllocBlockSize-1));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// the general case: find the bucket by walking though each one
|
||
|
|
||
|
for (
|
||
|
pBucket = m_pBucket;
|
||
|
pBucket != 0 && !pBucket->Contains(pMem);
|
||
|
pBucket = pBucket->m_pNextBucket
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// if we have found the container bucket, release the allocation
|
||
|
|
||
|
if (pBucket)
|
||
|
{
|
||
|
CAllocation *pAllocation = CONTAINING_RECORD(pMem, CAllocation, m_Data);
|
||
|
|
||
|
pAllocation->m_nRefCount -= 1;
|
||
|
|
||
|
ASSERT(pAllocation->m_nRefCount >= 0);
|
||
|
|
||
|
// delete the allocation if the reference count is zero
|
||
|
|
||
|
if (pAllocation->m_nRefCount == 0)
|
||
|
{
|
||
|
pBucket->m_nAllocated -= AllocSize(pAllocation->m_nDataSize);
|
||
|
|
||
|
ASSERT(pBucket->m_nAllocated >= 0);
|
||
|
|
||
|
// if all the allocation units in this bucket are freed,
|
||
|
// free the bucket
|
||
|
|
||
|
if (pBucket->m_nAllocated == 0)
|
||
|
{
|
||
|
FreeBucket(pBucket);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(uFlags & HEAP_NO_SERIALIZE))
|
||
|
{
|
||
|
LeaveCriticalSection(&m_cs);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
size_t max_size() const
|
||
|
{
|
||
|
return nBucketSize;
|
||
|
}
|
||
|
|
||
|
bool DumpAllocations() const
|
||
|
{
|
||
|
bool bResult = true;
|
||
|
|
||
|
CBucket *pBucket = m_pBucket;
|
||
|
|
||
|
while (pBucket)
|
||
|
{
|
||
|
CAllocation *pAllocation = (CAllocation *) pBucket->m_Data;
|
||
|
|
||
|
while (pAllocation->m_nDataSize != 0)
|
||
|
{
|
||
|
if (pAllocation->m_nRefCount != 0)
|
||
|
{
|
||
|
OutputDebugStringF(
|
||
|
_T("0x%p, DataSize=%d, RefCount=%d; %s\n"),
|
||
|
pAllocation->m_Data,
|
||
|
pAllocation->m_nDataSize,
|
||
|
pAllocation->m_nRefCount,
|
||
|
pAllocation->m_Data
|
||
|
);
|
||
|
}
|
||
|
|
||
|
pAllocation = (CAllocation *)
|
||
|
((PBYTE)pAllocation + AllocSize(pAllocation->m_nDataSize));
|
||
|
}
|
||
|
|
||
|
pBucket = pBucket->m_pNextBucket;
|
||
|
}
|
||
|
|
||
|
return bResult;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
struct CBucket
|
||
|
{
|
||
|
CBucket *m_pNextBucket;
|
||
|
LONG m_nAllocated;
|
||
|
BYTE m_Data[ANYSIZE_ARRAY];
|
||
|
|
||
|
bool Contains(const void *pMem) const
|
||
|
{
|
||
|
return
|
||
|
(PBYTE)pMem >= (PBYTE)m_Data &&
|
||
|
(PBYTE)pMem < (PBYTE)this + nBucketSize;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct CAllocation
|
||
|
{
|
||
|
size_t m_nDataSize;
|
||
|
LONG m_nRefCount;
|
||
|
BYTE m_Data[ANYSIZE_ARRAY];
|
||
|
};
|
||
|
|
||
|
private:
|
||
|
static size_t AllocSize(int nDataSize)
|
||
|
{
|
||
|
return (nDataSize + FIELD_OFFSET(CAllocation, m_Data) + (ALIGNMENT-1)) & ~(ALIGNMENT-1);
|
||
|
}
|
||
|
|
||
|
bool AllocNewBucket()
|
||
|
{
|
||
|
CBucket *pBucket = (CBucket *) VirtualAlloc(
|
||
|
0,
|
||
|
nBucketSize,
|
||
|
MEM_COMMIT,
|
||
|
PAGE_READWRITE
|
||
|
);
|
||
|
|
||
|
if (pBucket)
|
||
|
{
|
||
|
pBucket->m_pNextBucket = m_pBucket;
|
||
|
|
||
|
m_pBucket = pBucket;
|
||
|
m_pNextFree = pBucket->m_Data;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void FreeBucket(CBucket *pThisBucket)
|
||
|
{
|
||
|
CBucket **ppBucket = &m_pBucket;
|
||
|
ASSERT(*ppBucket != 0);
|
||
|
|
||
|
while (*ppBucket != pThisBucket)
|
||
|
{
|
||
|
ppBucket = &((*ppBucket)->m_pNextBucket);
|
||
|
ASSERT(*ppBucket != 0);
|
||
|
}
|
||
|
|
||
|
*ppBucket = pThisBucket->m_pNextBucket;
|
||
|
|
||
|
VirtualFree(pThisBucket, 0, MEM_RELEASE);
|
||
|
}
|
||
|
|
||
|
void FreeAllBuckets()
|
||
|
{
|
||
|
CBucket *pBucket = m_pBucket;
|
||
|
|
||
|
while (pBucket)
|
||
|
{
|
||
|
CBucket *pThisBucket = pBucket;
|
||
|
pBucket = pBucket->m_pNextBucket;
|
||
|
VirtualFree(pThisBucket, 0, MEM_RELEASE);
|
||
|
}
|
||
|
|
||
|
m_pBucket = 0;
|
||
|
m_pNextFree = 0;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
CRITICAL_SECTION m_cs;
|
||
|
CBucket *m_pBucket;
|
||
|
PBYTE m_pNextFree;
|
||
|
};
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
extern CMyHeap<> g_MyHeap;
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
template <class T>
|
||
|
class CMyAlloc //: public allocator<T>
|
||
|
{
|
||
|
public:
|
||
|
typedef size_t size_type;
|
||
|
typedef ptrdiff_t difference_type;
|
||
|
typedef T *pointer;
|
||
|
typedef const T *const_pointer;
|
||
|
typedef T &reference;
|
||
|
typedef const T &const_reference;
|
||
|
typedef T value_type;
|
||
|
|
||
|
pointer allocate(size_type N, const void *)
|
||
|
{
|
||
|
return (pointer) g_MyHeap.allocate(N);
|
||
|
}
|
||
|
|
||
|
char *_Charalloc(size_type N)
|
||
|
{
|
||
|
return (char *) g_MyHeap.allocate(N);
|
||
|
}
|
||
|
|
||
|
void deallocate(void *P, size_type)
|
||
|
{
|
||
|
g_MyHeap.deallocate(P);
|
||
|
}
|
||
|
|
||
|
size_type max_size() const
|
||
|
{
|
||
|
return g_MyHeap.max_size();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template<class T, class U>
|
||
|
inline bool operator ==(const CMyAlloc<T>&, const CMyAlloc<U>&)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
template<class T, class U>
|
||
|
inline bool operator !=(const CMyAlloc<T>&, const CMyAlloc<U>&)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
|
||
|
class CMyStr
|
||
|
{
|
||
|
public:
|
||
|
CMyStr()
|
||
|
{
|
||
|
m_pStr = 0;
|
||
|
}
|
||
|
|
||
|
~CMyStr()
|
||
|
{
|
||
|
g_MyHeap.deallocate(m_pStr);
|
||
|
}
|
||
|
|
||
|
explicit CMyStr(int nLength)
|
||
|
{
|
||
|
m_pStr = (PTSTR) g_MyHeap.allocate(nLength * sizeof(TCHAR));
|
||
|
m_pStr[0] = _T('\0');
|
||
|
}
|
||
|
|
||
|
CMyStr(PCTSTR pStr)
|
||
|
{
|
||
|
DupStr(pStr);
|
||
|
}
|
||
|
|
||
|
CMyStr(const CMyStr &rhs)
|
||
|
{
|
||
|
g_MyHeap.AddRef(rhs.m_pStr);
|
||
|
m_pStr = rhs.m_pStr;
|
||
|
}
|
||
|
|
||
|
CMyStr &operator =(PCTSTR pStr)
|
||
|
{
|
||
|
g_MyHeap.deallocate(m_pStr);
|
||
|
DupStr(pStr);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
CMyStr &operator =(const CMyStr &rhs)
|
||
|
{
|
||
|
g_MyHeap.AddRef(rhs.m_pStr);
|
||
|
g_MyHeap.deallocate(m_pStr);
|
||
|
m_pStr = rhs.m_pStr;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
operator PTSTR()
|
||
|
{
|
||
|
return m_pStr;
|
||
|
}
|
||
|
|
||
|
operator PCTSTR() const
|
||
|
{
|
||
|
return m_pStr;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
void DupStr(PCTSTR pStr)
|
||
|
{
|
||
|
if (!pStr)
|
||
|
{
|
||
|
pStr = _T("");
|
||
|
}
|
||
|
|
||
|
m_pStr = (PTSTR) g_MyHeap.allocate((_tcslen(pStr) + 1) * sizeof(TCHAR));
|
||
|
|
||
|
if (m_pStr)
|
||
|
{
|
||
|
_tcscpy(m_pStr, pStr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
PTSTR m_pStr;
|
||
|
};
|
||
|
|
||
|
|
||
|
#endif //MYHEAP_H
|