#if !defined(__LD_MEMORY_H)
#define __LD_MEMORY_H
namespace Memory {
/// \mainpage Memory Library Documentation
///
/// \section copyright Copyright Information
///
/// Memory Library
///
/// Copyright (C) 2004. Jeff Schiller
///
/// \section revhist Revision History
///
///
///
/// | Version | Notes |
/// | 0.1 | Initial version, contains TPrivateHeap and TObjectPool |
///
///
/// \section license License Stuff
///
/// This library is free software; you can redistribute it and/or
/// modify it under the terms of the GNU Lesser General Public
/// License as published by the Free Software Foundation; either
/// version 2.1 of the License, or (at your option) any later version.
///
/// This library is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
/// Lesser General Public License for more details.
///
/// You should have received a copy of the GNU Lesser General Public
/// License along with this library; if not, write to the Free Software
/// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
///
/// \section Introduction
///
/// This module includes a few classes to manage memory:
/// \link Memory::TPrivateHeap TPrivateHeap \endlink and
/// \link Memory::TObjectPool TObjectPool \endlink.
/// They attempt to limit heap fragmentation by pre-allocating pages of
/// bytes and managing the memory underneath.
/// This class allocates objects of type T in a private heap to avoid heap
/// fragmentation. The private heap only grows, it never shrinks until
/// the allocator is destroyed. The private heap only holds objects of
/// type T. Some optimal number for N (the size of a heap "page")
/// should be chosen to strike a balance between an oversized heap and
/// a heap that must allocate pages frequently.
///
/// This class saves time by pre-allocating the raw memory for objects of
/// type T and then tracking available "slots" in this memory for new objects.
/// It is effective for holding objects which don't allocate heap memory
/// themselves.
///
/// Users call Alloc() to get a pointer to the raw memory for an object of
/// type T. Users must then call placement new on this address to initialize
/// the object. When the object is no longer needed, users must explicitly
/// call the destructor and then call Free() to mark the memory as free to
/// use again by the PrivateHeap.
///
/// Upon construction, this object allocates one "page" worth of raw bytes,
/// which is N * sizeof(T) bytes. The raw pointer to this "page" is stored
/// internally so that it can be deallocated upon destruction of the heap.
///
/// Each page is broken up into pointers to T objects, and each pointer is
/// stored in the heap as a deque, which will initially contains N pointers
/// to objects of type T. The allocator internally keeps track of where the
/// next available (free) pointer is in the heap. Upon construction of the
/// heap, the "next free pointer" is set to the top of the 1st page.
///
/// When alloc is called, if we're not at the bottom of the heap, then return
/// the next free pointer in the deque and bumps down to the next free pointer
/// on the heap. If we ARE at the end of the heap, then the heap allocates
/// another page of bytes, stores the raw pointer to the next page internally
/// and returns the first pointer in the new page.
///
/// When free is called, if the next free pointer is not at the beginning
/// of the heap, then the next free pointer is bumped up the heap and the
/// deleted pointer value overwrites the existing value at that position in the
/// heap. If the next free pointer is at the top of the heap (i.e. first
/// pointer in the first page), then NOTHING HAPPENS. Eventually, support
/// for a PrivateHeapEmptyException being thrown may be added to this method.
///
/// Upon destruction of the allocator, all pages of bytes are deleted. This
/// means that if the heap is destroyed there are no resource leaks from the
/// objects created from this heap. You must be careful to only destroy the
/// private heap once all objects that have pointers into it are also
/// destroyed.
template<
// type of items to be stored in the heap
typename T,
// number of pointers to store in each "page" of memory
unsigned int N = 100
>
class TPrivateHeap {
protected:
class PrivateHeapEmptyException {};
typedef unsigned char Byte;
// This vector holds the pointers to the tops of each memory "page"
std::vector m_vRawBytes;
// This deque holds the pointers to all the objects of type T
std::deque m_Heap; // deque where front/begin is where we start
// and if we reach end(), we have to allocate
// more pages to the end
// This iterator tracks the next free object pointer
typename std::deque::iterator m_sNextFreePtr;
// -------------------------------------------------------------------
// -------------------------------------------------------------------
void allocate_page(unsigned int num_pages = 1)
{
Byte* ptrPage = 0;
T* ptrNode = 0;
// ---------------------------------------------------------------
// ---------------------------------------------------------------
for(unsigned int page = 0; page < num_pages; ++page) {
// allocate the raw bytes
ptrPage = new Byte[ sizeof(T) * N ];
m_vRawBytes.push_back(ptrPage);
// -----------------------------------------------------------
// now save the node pointers to the bottom of the deque
// -----------------------------------------------------------
for(int node = 0; node < N; ++node) {
ptrNode = (T*)(ptrPage + (node * sizeof(T)));
m_Heap.push_back(ptrNode);
}
// -----------------------------------------------------------
}
// ---------------------------------------------------------------
}
// -------------------------------------------------------------------
// void log_private_heap();
public:
// -------------------------------------------------------------------
/// This constructs a private heap with (initially) one page of memory
/// allocated.
TPrivateHeap()
{
allocate_page();
m_sNextFreePtr = m_Heap.begin();
// log_private_heap();
}
// -------------------------------------------------------------------
// -------------------------------------------------------------------
/// WARNING! Before deleting, this destructor checks that the
/// next free pointer is at the top of the heap (in other words, that
/// all objects "allocated" and then constructed in this heap were then
/// destructed and then "freed" prior to the heap destructor being called).
///
/// In other words, if a resource leak is detected in the heap, a
/// debug assertion is tripped.
~TPrivateHeap()
{
assert(m_sNextFreePtr == m_Heap.begin() &&
"Error! Private heap is being destroyed but objects were not \
correctly destroyed and freed");
// delete all the raw bytes...
for(std::vector::iterator pit = m_vRawBytes.begin(); pit != m_vRawBytes.end(); ++pit) {
if(NULL != *pit) {
delete[] *pit;
*pit = 0;
}
}
m_vRawBytes.clear();
}
// -------------------------------------------------------------------
// -------------------------------------------------------------------
/// Allocates the next available T object in the heap and returns a pointer
/// to this uninitialized object. WARNING! You must still run a constructor
/// on the pointer to initialize your object.
///
/// Note: This method will cause a new page to be allocated in the heap
/// if the returned pointer was at the bottom of the last page.
T* Alloc()
{
T* ptrNode = *m_sNextFreePtr;
++m_sNextFreePtr;
// if m_sNextFreePtr now points past the bottom of the deque,
// allocate a new page and re-position the deque iterator (marker)
if(m_Heap.end() == m_sNextFreePtr) {
// back up our deque marker by one, temporarily
--m_sNextFreePtr;
// allocate a new page to the end of our deque
allocate_page();
// reposition the stack marker on the newly available pointer
++m_sNextFreePtr;
}
// log_private_heap();
return ptrNode;
}
// -------------------------------------------------------------------
// -------------------------------------------------------------------
/// De-allocates the object T that pointer pn points to and frees space
/// in the heap. WARNING! You should always explicitly run the destructor
/// on the object prior to calling Free().
///
/// TO DO: Need to put in debug assertions about the pointer to be freed and
/// ensure that it is within the proper range (I'm thinking store top and
/// bottom addresses of the heap to make this check as quick as possible).
///
/// WARNING! Never Free an object that was not allocated from this Heap. This
/// is the equivalent of deleting a pointer that was allocated using malloc and
/// could produce disastrous results.
///
/// WARNING! Never Free the same object twice!!! This is the equivalent of
/// deleting a pointer twice. The safest thing to do after Freeing a pointer
/// is to NULL that pointer.
void Free(T* pn)
{
// the returned pointer goes back in our deque at the point one
// above the deque pointer (unless we're at the beginning of the deque)
if(m_Heap.begin() == m_sNextFreePtr) {
// throw 1;
}
else {
--m_sNextFreePtr;
*m_sNextFreePtr = pn;
}
// log_private_heap();
}
// -------------------------------------------------------------------
// I DON'T THINK "Clear" IS A GOOD METHOD. THE ONLY PROPER WAY TO CLEAN
// UP ALL SPACE IN THE HEAP IS TO DESTROY EACH OBJECT IN THE HEAP AND
// THEN FREE THE MEMORY. THIS METHOD IS VERY PRONE TO RESOURCE LEAKS
// FOR ANYTHING EXCEPT OBJECTS THAT DON'T ALLOCATE HEAP MEMORY.
/*
// -------------------------------------------------------------------
// frees all space in the heap
// -------------------------------------------------------------------
void Clear()
{
std::deque::iterator pit = m_Heap.begin();
for(std::vector::iterator bit = m_vRawBytes.begin();
bit != m_vRawBytes.end(); ++bit) {
// for each page of bytes, re-assign the heap's pointers
Byte* ptrPage = *bit;
for(int ptrNum = 0; ptrNum < N; ++ptrNum) {
*pit = (T*)(ptrPage + (ptrNum * sizeof(T)));
++pit;
}
}
m_sNextFreePtr = m_Heap.begin();
}
// -------------------------------------------------------------------
*/
}; // end template class TPrivateHeap
// -------------------------------------------------------------------
/// This class uses the lower-level TPrivateHeap to allocate the memory
/// for objects of type T and default constructs them before returning
/// the pointer to the caller in New(). The Delete() calls the destructor
/// and then frees the memory in the private heap.
///
/// It is essentially a wrapper around TPrivateHeap that ensure the
/// default constructor and destructor are called during alloc and free
/// operations. If you need to call a different (non-default) constructor
/// on your created objects or do not want to call constructors on your
/// your objects, use TPrivateHeap directly.
template<
// type of objects to be stored in the private heap
typename T,
// number of objects to store in each "page" of memory
unsigned int N = 100
>
class TObjectPool
{
private:
TPrivateHeap m_heap;
public:
/// This method allocates memory on the private heap and
/// calls placement new on the memory (invoking the default
/// constructor)
T* New()
{
// get the pointer and run placement new constructor on it
return new (m_heap.Alloc()) T;
}
/// This method calls the destructor on the object and then
/// frees the memory on the private heap. If this method is
/// called with a NULL pointer, no operation is performed.
void Delete(T* pObj)
{
if(pObj) {
// explicitly call the destructor
pObj->~T();
// free the memory in the private heap
m_heap.Free(pObj);
}
}
};
}; // namespace Memory
#endif // __LD_MEMORY_H