summaryrefslogtreecommitdiff
path: root/src/utilcode/memorypool.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/utilcode/memorypool.cpp')
-rw-r--r--src/utilcode/memorypool.cpp317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/utilcode/memorypool.cpp b/src/utilcode/memorypool.cpp
new file mode 100644
index 0000000000..1db8291e6a
--- /dev/null
+++ b/src/utilcode/memorypool.cpp
@@ -0,0 +1,317 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "stdafx.h"
+
+#include "memorypool.h"
+#include "ex.h"
+
+size_t MemoryPool::GetSize()
+{
+ LIMITED_METHOD_CONTRACT;
+ size_t retval=0;
+
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ retval+=(BYTE*)block->elementsEnd-(BYTE*)block->elements;
+ block = block->next;
+ }
+ return retval;
+}
+
+#ifndef DACCESS_COMPILE
+
+BOOL MemoryPool::AddBlock(SIZE_T elementCount)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return FALSE;);
+ } CONTRACTL_END;
+
+ //
+ // Check for arithmetic overflow
+ //
+ S_SIZE_T cbBlockSize = S_SIZE_T(elementCount) * S_SIZE_T(m_elementSize);
+ S_SIZE_T cbAllocSize = S_SIZE_T(sizeof(Block)) + cbBlockSize;
+ if (cbBlockSize.IsOverflow() || cbAllocSize.IsOverflow())
+ return FALSE;
+
+ //
+ // Allocate the new block.
+ //
+
+ Block *block = (Block *) new (nothrow) BYTE [cbAllocSize.Value()];
+
+ if (block == NULL)
+ return FALSE;
+
+ //
+ // Chain all elements together for the free list
+ //
+
+ _ASSERTE(m_freeList == NULL);
+ Element **prev = &m_freeList;
+
+ Element *e = block->elements;
+ Element *eEnd = (Element *) ((BYTE*) block->elements + elementCount*m_elementSize);
+ while (e < eEnd)
+ {
+ *prev = e;
+ prev = &e->next;
+#if _DEBUG
+ DeadBeef(e);
+#endif
+ e = (Element*) ((BYTE*)e + m_elementSize);
+ }
+
+ *prev = NULL;
+
+ //
+ // Initialize the other block fields & link the block into the block list
+ //
+
+ block->elementsEnd = e;
+ block->next = m_blocks;
+ m_blocks = block;
+
+ return TRUE;
+}
+
+void MemoryPool::DeadBeef(Element *element)
+{
+#if _DEBUG
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ int *i = &element->deadBeef;
+ int *iEnd = (int*) ((BYTE*)element + m_elementSize);
+ while (i < iEnd)
+ *i++ = (int) 0xdeadbeef;
+#else
+ LIMITED_METHOD_CONTRACT;
+#endif
+}
+
+MemoryPool::MemoryPool(SIZE_T elementSize, SIZE_T initGrowth, SIZE_T initCount)
+ : m_elementSize(elementSize),
+ m_growCount(initGrowth),
+ m_blocks(NULL),
+ m_freeList(NULL)
+{
+ CONTRACTL {
+ if (initCount) THROWS; else NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(elementSize >= sizeof(Element));
+ _ASSERTE((elementSize & ((sizeof(PVOID)-1))) == 0);
+
+ if (initCount > 0)
+ AddBlock(initCount);
+}
+
+MemoryPool::~MemoryPool()
+{
+ LIMITED_METHOD_CONTRACT;
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ Block *next = block->next;
+ delete [] (BYTE*) block;
+ block = next;
+ }
+}
+
+BOOL MemoryPool::IsElement(void *element)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ if (element >= block->elements &&
+ element < block->elementsEnd )
+ {
+ return ((BYTE *)element - (BYTE*)block->elements) % m_elementSize == 0;
+ }
+ block = block->next;
+ }
+
+ return FALSE;
+}
+
+BOOL MemoryPool::IsAllocatedElement(void *element)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ if (!IsElement(element))
+ return FALSE;
+
+ //
+ // Now, make sure the element isn't
+ // in the free list.
+ //
+
+#if _DEBUG
+ //
+ // In a debug build, all objects on the free list
+ // will be marked with deadbeef. This means that
+ // if the object is not deadbeef, it's not on the
+ // free list.
+ //
+ // This check will give us decent performance in
+ // a debug build for FreeElement, since we
+ // always expect to return TRUE in that case.
+ //
+
+ if (((Element*)element)->deadBeef != (int) 0xdeadBeef)
+ return TRUE;
+#endif
+
+ Element *f = m_freeList;
+ while (f != NULL)
+ {
+ if (f == element)
+ return FALSE;
+ f = f->next;
+ }
+
+#if _DEBUG
+ //
+ // We should never get here in a debug build, because
+ // all free elements should be deadbeefed.
+ //
+ _ASSERTE(!"Unreachable");
+#endif
+
+ return TRUE;
+}
+
+void *MemoryPool::AllocateElement()
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ void *element = AllocateElementNoThrow();
+ if (element == NULL)
+ ThrowOutOfMemory();
+
+ return element;
+}
+
+void *MemoryPool::AllocateElementNoThrow()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT( return FALSE; );
+ } CONTRACTL_END;
+
+ void *element = m_freeList;
+
+ if (element == NULL)
+ {
+ if (!AddBlock(m_growCount))
+ return NULL;
+
+ m_growCount *= 2;
+ element = m_freeList;
+ }
+
+ // if we come there means that addblock succeeded and m_freelist isn't null anymore
+ PREFIX_ASSUME(m_freeList!= NULL);
+ m_freeList = m_freeList->next;
+
+ return element;
+}
+
+void MemoryPool::FreeElement(void *element)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+#if _DEBUG // don't want to do this assert in a non-debug build; it is expensive
+ _ASSERTE(IsAllocatedElement(element));
+#endif
+
+ Element *e = (Element *) element;
+
+#if _DEBUG
+ DeadBeef(e);
+#endif
+
+ e->next = m_freeList;
+ m_freeList = e;
+}
+
+void MemoryPool::FreeAllElements()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ Block *next = block->next;
+ delete [] block;
+ block = next;
+ }
+
+ m_freeList = NULL;
+ m_blocks = NULL;
+}
+
+
+
+MemoryPool::Iterator::Iterator(MemoryPool *pool)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ //
+ // Warning! This only works if you haven't freed
+ // any elements.
+ //
+
+ m_next = pool->m_blocks;
+ m_e = NULL;
+ m_eEnd = NULL;
+ m_end = (BYTE*) pool->m_freeList;
+ m_size = pool->m_elementSize;
+}
+
+BOOL MemoryPool::Iterator::Next()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_e == m_eEnd
+ || (m_e == m_end && m_end != NULL))
+ {
+ if (m_next == NULL)
+ return FALSE;
+ m_e = (BYTE*) m_next->elements;
+ m_eEnd = (BYTE*) m_next->elementsEnd;
+ m_next = m_next->next;
+ if (m_e == m_end)
+ return FALSE;
+ }
+
+ m_e += m_size;
+
+ return TRUE;
+}
+
+#endif