/*++ 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 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 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 CMyAlloc //: public allocator { 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 inline bool operator ==(const CMyAlloc&, const CMyAlloc&) { return true; } template inline bool operator !=(const CMyAlloc&, const CMyAlloc&) { 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