468 lines
16 KiB
C++
468 lines
16 KiB
C++
/*
|
|
Copyright (C) 2009-2010 Electronic Arts, Inc. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of
|
|
its contributors may be used to endorse or promote products derived
|
|
from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// EASTL/fixed_allocator.h
|
|
//
|
|
// Copyright (c) 2005, Electronic Arts. All rights reserved.
|
|
// Written and maintained by Paul Pedriana.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// This file implements the following
|
|
// fixed_allocator
|
|
// fixed_allocator_with_overflow
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#ifndef EASTL_FIXED_ALLOCATOR_H
|
|
#define EASTL_FIXED_ALLOCATOR_H
|
|
|
|
|
|
#include <EASTL/internal/config.h>
|
|
#include <EASTL/internal/fixed_pool.h>
|
|
#include <EASTL/functional.h>
|
|
#include <EASTL/memory.h>
|
|
#include <EASTL/allocator.h>
|
|
#include <EASTL/type_traits.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push, 0)
|
|
#include <new>
|
|
#pragma warning(pop)
|
|
#else
|
|
#include <new>
|
|
#endif
|
|
|
|
|
|
namespace eastl
|
|
{
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// fixed_allocator
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
/// fixed_allocator
|
|
///
|
|
/// Implements an allocator which allocates a single fixed size where
|
|
/// the size, alignment, and memory used for the pool is defined at
|
|
/// runtime by the user. This is different from fixed containers
|
|
/// such as fixed_list whereby the size and alignment are determined
|
|
/// at compile time and the memory is directly built into the container's
|
|
/// member data.
|
|
///
|
|
/// If the pool's memory is exhausted or was never initialized, the
|
|
/// allocate function returns NULL. Consider the fixed_allocator_with_overflow
|
|
/// class as an alternative in order to deal with this situation.
|
|
///
|
|
/// This class requires the user to call container.get_allocator().init()
|
|
/// after constructing the container. There currently isn't a way to
|
|
/// construct the container with the initialization parameters, though
|
|
/// with some effort such a thing could probably be made possible.
|
|
/// It's not as simple as it might first seem, due to the non-copyable
|
|
/// nature of fixed allocators. A side effect of this limitation is that
|
|
/// you cannot copy-construct a container using fixed_allocators.
|
|
///
|
|
/// Another side-effect is that you cannot swap two containers using
|
|
/// a fixed_allocator, as a swap requires temporary memory allocated by
|
|
/// an equivalent allocator, and such a thing cannot be done implicitly.
|
|
/// A workaround for the swap limitation is that you can implement your
|
|
/// own swap whereby you provide an explicitly created temporary object.
|
|
///
|
|
/// Note: Be careful to set the allocator's node size to the size of the
|
|
/// container node and not the size of the contained object. Note that the
|
|
/// example code below uses IntListNode.
|
|
///
|
|
/// Example usage:
|
|
/// typedef eastl::list<int, fixed_allocator> IntList;
|
|
/// typedef IntList::node_type IntListNode;
|
|
///
|
|
/// IntListNode buffer[200];
|
|
/// IntList intList;
|
|
/// intList.get_allocator().init(buffer, sizeof(buffer), sizeof(IntListNode), __alignof(IntListNode));
|
|
///
|
|
class EASTL_API fixed_allocator : public fixed_pool_base
|
|
{
|
|
public:
|
|
/// fixed_allocator
|
|
///
|
|
/// Default constructor. The user usually will need to call init() after
|
|
/// constructing via this constructor.
|
|
///
|
|
fixed_allocator(const char* /*pName*/ = EASTL_FIXED_POOL_DEFAULT_NAME)
|
|
: fixed_pool_base(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
/// fixed_allocator
|
|
///
|
|
/// Copy constructor. The user usually will need to call init() after
|
|
/// constructing via this constructor. By their nature, fixed-allocators
|
|
/// cannot be copied in any useful way, as by their nature the user
|
|
/// must manually initialize them.
|
|
///
|
|
fixed_allocator(const fixed_allocator&)
|
|
: fixed_pool_base(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
/// operator=
|
|
///
|
|
/// By their nature, fixed-allocators cannot be copied in any
|
|
/// useful way, as by their nature the user must manually
|
|
/// initialize them.
|
|
///
|
|
fixed_allocator& operator=(const fixed_allocator&)
|
|
{
|
|
return *this;
|
|
}
|
|
|
|
|
|
// init
|
|
//
|
|
// No init here, as the base class version is sufficient.
|
|
//
|
|
//void init(void* pMemory, size_t memorySize, size_t nodeSize,
|
|
// size_t alignment, size_t alignmentOffset = 0);
|
|
|
|
|
|
/// allocate
|
|
///
|
|
/// Allocates a new object of the size specified upon class initialization.
|
|
/// Returns NULL if there is no more memory.
|
|
///
|
|
void* allocate(size_t /*n*/, int /*flags*/ = 0)
|
|
{
|
|
// To consider: Verify that 'n' is what the user initialized us with.
|
|
|
|
Link* pLink = mpHead;
|
|
|
|
if(pLink) // If we have space...
|
|
{
|
|
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
|
|
if(++mnCurrentSize > mnPeakSize)
|
|
mnPeakSize = mnCurrentSize;
|
|
#endif
|
|
|
|
mpHead = pLink->mpNext;
|
|
return pLink;
|
|
}
|
|
else
|
|
{
|
|
// If there's no free node in the free list, just
|
|
// allocate another from the reserved memory area
|
|
|
|
if(mpNext != mpCapacity)
|
|
{
|
|
pLink = mpNext;
|
|
|
|
mpNext = reinterpret_cast<Link*>(reinterpret_cast<char8_t*>(mpNext) + mnNodeSize);
|
|
|
|
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
|
|
if(++mnCurrentSize > mnPeakSize)
|
|
mnPeakSize = mnCurrentSize;
|
|
#endif
|
|
|
|
return pLink;
|
|
}
|
|
|
|
// EASTL_ASSERT(false); To consider: enable this assert. However, we intentionally disable it because this isn't necessarily an assertable error.
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/// allocate
|
|
///
|
|
void* allocate(size_t n, size_t /*alignment*/, size_t /*offset*/, int flags = 0)
|
|
{
|
|
return allocate(n, flags);
|
|
}
|
|
|
|
|
|
/// deallocate
|
|
///
|
|
/// Frees the given object which was allocated by allocate().
|
|
/// If the given node was not allocated by allocate() then the behaviour
|
|
/// is undefined.
|
|
///
|
|
void deallocate(void* p, size_t)
|
|
{
|
|
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
|
|
--mnCurrentSize;
|
|
#endif
|
|
|
|
((Link*)p)->mpNext = mpHead;
|
|
mpHead = ((Link*)p);
|
|
}
|
|
|
|
|
|
using fixed_pool_base::can_allocate;
|
|
|
|
|
|
const char* get_name() const
|
|
{
|
|
return EASTL_FIXED_POOL_DEFAULT_NAME;
|
|
}
|
|
|
|
|
|
void set_name(const char*)
|
|
{
|
|
// Nothing to do. We don't allocate memory.
|
|
}
|
|
|
|
}; // fixed_allocator
|
|
|
|
bool operator==(const fixed_allocator& a, const fixed_allocator& b);
|
|
bool operator!=(const fixed_allocator& a, const fixed_allocator& b);
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// fixed_allocator_with_overflow
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
/// fixed_allocator_with_overflow
|
|
///
|
|
/// Implements an allocator which allocates a single fixed size where
|
|
/// the size, alignment, and memory used for the pool is defined at
|
|
/// runtime by the user. This is different from fixed containers
|
|
/// such as fixed_list whereby the size and alignment are determined
|
|
/// at compile time and the memory is directly built into the container's
|
|
/// member data.
|
|
///
|
|
/// Note: Be careful to set the allocator's node size to the size of the
|
|
/// container node and not the size of the contained object. Note that the
|
|
/// example code below uses IntListNode.
|
|
///
|
|
/// This class requires the user to call container.get_allocator().init()
|
|
/// after constructing the container. There currently isn't a way to
|
|
/// construct the container with the initialization parameters, though
|
|
/// with some effort such a thing could probably be made possible.
|
|
/// It's not as simple as it might first seem, due to the non-copyable
|
|
/// nature of fixed allocators. A side effect of this limitation is that
|
|
/// you cannot copy-construct a container using fixed_allocators.
|
|
///
|
|
/// Another side-effect is that you cannot swap two containers using
|
|
/// a fixed_allocator, as a swap requires temporary memory allocated by
|
|
/// an equivalent allocator, and such a thing cannot be done implicitly.
|
|
/// A workaround for the swap limitation is that you can implement your
|
|
/// own swap whereby you provide an explicitly created temporary object.
|
|
///
|
|
/// Example usage:
|
|
/// typedef eastl::list<int, fixed_allocator_with_overflow> IntList;
|
|
/// typedef IntList::node_type IntListNode;
|
|
///
|
|
/// IntListNode buffer[200];
|
|
/// IntList intList;
|
|
/// intList.get_allocator().init(buffer, sizeof(buffer), sizeof(IntListNode), __alignof(IntListNode));
|
|
///
|
|
class EASTL_API fixed_allocator_with_overflow : public fixed_pool_base
|
|
{
|
|
public:
|
|
/// fixed_allocator_with_overflow
|
|
///
|
|
/// Default constructor. The user usually will need to call init() after
|
|
/// constructing via this constructor.
|
|
///
|
|
fixed_allocator_with_overflow(const char* pName = EASTL_FIXED_POOL_DEFAULT_NAME)
|
|
: fixed_pool_base(NULL),
|
|
mOverflowAllocator(pName)
|
|
{
|
|
}
|
|
|
|
|
|
/// fixed_allocator_with_overflow
|
|
///
|
|
/// Copy constructor. The user usually will need to call init() after
|
|
/// constructing via this constructor. By their nature, fixed-allocators
|
|
/// cannot be copied in any useful way, as by their nature the user
|
|
/// must manually initialize them.
|
|
///
|
|
fixed_allocator_with_overflow(const fixed_allocator_with_overflow&)
|
|
: fixed_pool_base(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
/// operator=
|
|
///
|
|
/// By their nature, fixed-allocators cannot be copied in any
|
|
/// useful way, as by their nature the user must manually
|
|
/// initialize them.
|
|
///
|
|
fixed_allocator_with_overflow& operator=(const fixed_allocator_with_overflow& x)
|
|
{
|
|
#if EASTL_ALLOCATOR_COPY_ENABLED
|
|
mOverflowAllocator = x.mOverflowAllocator;
|
|
#else
|
|
(void)x;
|
|
#endif
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
/// init
|
|
///
|
|
void init(void* pMemory, size_t memorySize, size_t nodeSize,
|
|
size_t alignment, size_t alignmentOffset = 0)
|
|
{
|
|
fixed_pool_base::init(pMemory, memorySize, nodeSize, alignment, alignmentOffset);
|
|
|
|
mpPoolBegin = pMemory;
|
|
mpPoolEnd = (void*)((uintptr_t)pMemory + memorySize);
|
|
mnNodeSize = (eastl_size_t)nodeSize;
|
|
}
|
|
|
|
|
|
/// allocate
|
|
///
|
|
/// Allocates a new object of the size specified upon class initialization.
|
|
/// Returns NULL if there is no more memory.
|
|
///
|
|
void* allocate(size_t /*n*/, int /*flags*/ = 0)
|
|
{
|
|
// To consider: Verify that 'n' is what the user initialized us with.
|
|
|
|
void* p;
|
|
|
|
if(mpHead) // If we have space...
|
|
{
|
|
p = mpHead;
|
|
mpHead = mpHead->mpNext;
|
|
}
|
|
else
|
|
p = mOverflowAllocator.allocate(mnNodeSize);
|
|
|
|
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
|
|
if(p && (++mnCurrentSize > mnPeakSize))
|
|
mnPeakSize = mnCurrentSize;
|
|
#endif
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
/// allocate
|
|
///
|
|
void* allocate(size_t n, size_t /*alignment*/, size_t /*offset*/, int flags = 0)
|
|
{
|
|
return allocate(n, flags);
|
|
}
|
|
|
|
|
|
/// deallocate
|
|
///
|
|
/// Frees the given object which was allocated by allocate().
|
|
/// If the given node was not allocated by allocate() then the behaviour
|
|
/// is undefined.
|
|
///
|
|
void deallocate(void* p, size_t)
|
|
{
|
|
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
|
|
--mnCurrentSize;
|
|
#endif
|
|
|
|
if((p >= mpPoolBegin) && (p < mpPoolEnd))
|
|
{
|
|
((Link*)p)->mpNext = mpHead;
|
|
mpHead = ((Link*)p);
|
|
}
|
|
else
|
|
mOverflowAllocator.deallocate(p, (size_t)mnNodeSize);
|
|
}
|
|
|
|
|
|
using fixed_pool_base::can_allocate;
|
|
|
|
|
|
const char* get_name() const
|
|
{
|
|
return mOverflowAllocator.get_name();
|
|
}
|
|
|
|
|
|
void set_name(const char* pName)
|
|
{
|
|
mOverflowAllocator.set_name(pName);
|
|
}
|
|
|
|
protected:
|
|
EASTLAllocatorType mOverflowAllocator; // To consider: Allow the user to define the type of this, presumably via a template parameter.
|
|
void* mpPoolBegin; // To consider: We have these member variables and ideally we shouldn't need them. The problem is that
|
|
void* mpPoolEnd; // the information about the pool buffer and object size is stored in the owning container
|
|
eastl_size_t mnNodeSize; // and we can't have access to it without increasing the amount of code we need and by templating
|
|
// more code. It may turn out that simply storing data here is smaller in the end.
|
|
}; // fixed_allocator_with_overflow // Granted, this class is usually used for debugging purposes, but perhaps there is an elegant solution.
|
|
|
|
bool operator==(const fixed_allocator_with_overflow& a, const fixed_allocator_with_overflow& b);
|
|
bool operator!=(const fixed_allocator_with_overflow& a, const fixed_allocator_with_overflow& b);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// global operators
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
inline bool operator==(const fixed_allocator&, const fixed_allocator&)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
inline bool operator!=(const fixed_allocator&, const fixed_allocator&)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
inline bool operator==(const fixed_allocator_with_overflow&, const fixed_allocator_with_overflow&)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
inline bool operator!=(const fixed_allocator_with_overflow&, const fixed_allocator_with_overflow&)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
} // namespace eastl
|
|
|
|
|
|
|
|
|
|
#endif // Header include guard
|
|
|
|
|
|
|