aboutsummaryrefslogtreecommitdiff
path: root/include/EASTL/fixed_allocator.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/EASTL/fixed_allocator.h')
-rw-r--r--include/EASTL/fixed_allocator.h455
1 files changed, 455 insertions, 0 deletions
diff --git a/include/EASTL/fixed_allocator.h b/include/EASTL/fixed_allocator.h
new file mode 100644
index 0000000..488eae4
--- /dev/null
+++ b/include/EASTL/fixed_allocator.h
@@ -0,0 +1,455 @@
+/////////////////////////////////////////////////////////////////////////////
+// Copyright (c) Electronic Arts Inc. All rights reserved.
+/////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// 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>
+
+EA_DISABLE_ALL_VC_WARNINGS();
+
+#include <new>
+
+EA_RESTORE_ALL_VC_WARNINGS();
+
+EA_DISABLE_VC_WARNING(4275); // non dll-interface class used as base for DLL-interface classkey 'identifier'
+
+#if defined(EA_PRAGMA_ONCE_SUPPORTED)
+ #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
+#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<char*>(mpNext) + n);
+
+ #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)
+ , mpPoolBegin(nullptr)
+ , mpPoolEnd(nullptr)
+ , mnNodeSize(0)
+ {
+ }
+
+
+ /// 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)
+ , mpPoolBegin(nullptr)
+ , mpPoolEnd(nullptr)
+ , mnNodeSize(0)
+ {
+ }
+
+
+ /// 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
+ {
+ // If there's no free node in the free list, just
+ // allocate another from the reserved memory area
+
+ if (mpNext != mpCapacity)
+ {
+ p = mpNext;
+ mpNext = reinterpret_cast<Link*>(reinterpret_cast<char*>(mpNext) + mnNodeSize);
+ }
+ 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
+
+
+EA_RESTORE_VC_WARNING();
+
+#endif // Header include guard