aboutsummaryrefslogtreecommitdiff
path: root/examples/cxx/hybrid/allocator/arena.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'examples/cxx/hybrid/allocator/arena.cxx')
-rw-r--r--examples/cxx/hybrid/allocator/arena.cxx167
1 files changed, 167 insertions, 0 deletions
diff --git a/examples/cxx/hybrid/allocator/arena.cxx b/examples/cxx/hybrid/allocator/arena.cxx
new file mode 100644
index 0000000..facb852
--- /dev/null
+++ b/examples/cxx/hybrid/allocator/arena.cxx
@@ -0,0 +1,167 @@
+// file : examples/cxx/hybrid/allocator/arena.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : not copyrighted - public domain
+
+#include <stdio.h>
+#include <string.h> // memcpy
+
+#include "arena.hxx"
+
+arena::
+arena (void* memory, size_t size)
+ : alloc_count_ (0),
+ realloc_count_ (0),
+ free_count_ (0),
+ cur_allocated_ (0),
+ max_allocated_ (0)
+{
+ head_ = static_cast<block*> (memory);
+ head_->cap = size - sizeof (block);
+ head_->next = 0;
+ head_->used = false;
+}
+
+void* arena::
+allocate (size_t size)
+{
+ block* b = head_;
+
+ while (b != 0)
+ {
+ if (!b->used)
+ {
+ if (b->cap >= size)
+ {
+ // See if it makes sense to fragment this block.
+ //
+ if (b->cap - size >= sizeof (block) * 2)
+ {
+ block* b1 = reinterpret_cast<block*> (
+ reinterpret_cast<char*> (b) + sizeof (block) + size);
+
+ b1->cap = b->cap - size - sizeof (block);
+ b1->next = b->next;
+ b1->used = false;
+
+ b->next = b1;
+ b->cap = size;
+ }
+
+ b->used = true;
+ b->size = size;
+
+ alloc_count_++;
+ cur_allocated_ += size;
+
+ if (cur_allocated_ > max_allocated_)
+ max_allocated_ = cur_allocated_;
+
+ return b + 1;
+ }
+
+ // This block is not big enough. See if we can merge it
+ // with the next block.
+ //
+ if (b->next != 0 && !b->next->used)
+ {
+ block* b1 = b->next;
+ b->cap += b1->cap + sizeof (block);
+ b->next = b1->next;
+
+ // Try the merged block again.
+ //
+ continue;
+ }
+ }
+
+ // This block is either in use or not big enough. Continue
+ // searching.
+ //
+ b = b->next;
+ }
+
+ return 0;
+}
+
+void* arena::
+reallocate (void* p, size_t size)
+{
+ // If the passed pointer is NULL, reallocate is equivalent to
+ // allocate.
+ //
+ if (!p)
+ return allocate (size);
+
+ block* b = static_cast<block*> (p) - 1;
+
+ // If the passed size is NULL, reallocate is equivalent to free.
+ //
+ if (size == 0)
+ {
+ free (p);
+ return 0;
+ }
+
+ // If this block is not large enough to satisfy the request, try to
+ // merge it with the next block(s).
+ //
+ while (b->cap < size && b->next != 0 && !b->next->used)
+ {
+ block* b1 = b->next;
+ b->cap += b1->cap + sizeof (block);
+ b->next = b1->next;
+ }
+
+ // If this block is now large enough then we can reuse the same
+ // memory region.
+ //
+ if (b->cap >= size)
+ {
+ realloc_count_++;
+ cur_allocated_ += size - b->size;
+
+ if (cur_allocated_ > max_allocated_)
+ max_allocated_ = cur_allocated_;
+
+ b->size = size;
+ return p;
+ }
+
+ // Otherwise allocate a new block and copy the data over.
+ //
+ void* r = allocate (size);
+
+ if (r)
+ {
+ memcpy (r, p, b->size);
+ free (p);
+ }
+
+ return r;
+}
+
+void arena::
+free (void* p)
+{
+ if (p)
+ {
+ block* b = static_cast<block*> (p) - 1;
+
+ cur_allocated_ -= b->size;
+ free_count_++;
+
+ b->used = false;
+ }
+}
+
+void arena::
+print_statistics ()
+{
+ printf ("\n");
+ printf ("allocations: %lu\n", alloc_count_);
+ printf ("reallocations: %lu\n", realloc_count_);
+ printf ("deallocations: %lu\n", free_count_);
+ printf ("currently in use: %lu bytes\n", cur_allocated_);
+ printf ("maximum in use: %lu bytes\n", max_allocated_);
+ printf ("\n");
+}