diff --git a/Makefile.am b/Makefile.am index e21b97d0..6050a553 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,6 +19,7 @@ UTIL_INCLUDE = \ matrix.hpp \ nocopy.hpp \ point.hpp \ + pool.hpp \ range.hpp \ region.hpp \ signal.hpp \ @@ -37,6 +38,7 @@ UTIL_FILES = \ maths.cpp \ matrix.cpp \ point.cpp \ + pool.cpp \ range.cpp \ region.cpp \ signal.cpp \ diff --git a/pool.cpp b/pool.cpp new file mode 100644 index 00000000..faaeb10b --- /dev/null +++ b/pool.cpp @@ -0,0 +1,25 @@ +/* + * This file is part of libgim. + * + * libgim is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * libgim 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with libgim. If not, see . + * + * Copyright 2010 Danny Robson + */ + + +#include "pool.hpp" + + +// Explicitly instance a possibly useful specialisation so that we can more easily catch linker errors. +template class pool; diff --git a/pool.hpp b/pool.hpp new file mode 100644 index 00000000..491b80f2 --- /dev/null +++ b/pool.hpp @@ -0,0 +1,99 @@ +/* + * This file is part of libgim. + * + * libgim is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * libgim 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with libgim. If not, see . + * + * Copyright 2010 Danny Robson + */ + +#ifndef __UTIL_POOL_HPP +#define __UTIL_POOL_HPP + +#include "nocopy.hpp" + + +template +class pool : public nocopy { + protected: + union node { + char _data[sizeof (T)]; + node *_chain; + }; + + node *m_head; + node *m_next; + unsigned int m_capacity; + + public: + pool (unsigned int _capacity): + m_capacity (_capacity) + { + static_assert (sizeof (T) >= sizeof (uintptr_t), + "pool's chained block system requires that T be at least pointer sized"); + + m_head = (node *)operator new (sizeof (T) * m_capacity); + m_next = m_head; + + for (unsigned int i = 0; i < m_capacity - 1; ++i) + m_next[i]._chain = &m_next[i + 1]; + m_next[m_capacity - 1]._chain = NULL; + } + + + ~pool () { + check (m_next != NULL); + + unsigned int doomed_count = 0; + for (node *cursor = m_next; cursor != NULL; cursor = cursor->_chain) + ++doomed_count; + + check_eq (doomed_count, m_capacity); + operator delete (m_head); + } + + + unsigned int capacity (void) const + { return m_capacity; } + + + template + T* acquire (Args&... args) { + if (!m_next) + throw std::bad_alloc (); + + node *newnext = m_next->_chain; + T *data = (T*)&m_next->_data; + + try { + new (data) T (args...); + } catch (...) { + m_next->_chain = newnext; + throw; + } + + m_next = newnext; + return data; + } + + + void release (T *data) { + node *newnode = (node *)data; + + newnode->_chain = m_next; + m_next = newnode; + } +}; + + +#endif // __UTIL_POOL_HPP diff --git a/test/.gitignore b/test/.gitignore index a82d7fb0..82caddcb 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -5,6 +5,7 @@ /json-check /maths /matrix +/pool /range /signal /version diff --git a/test/Makefile.am b/test/Makefile.am index efccdd5b..f9b97c77 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -14,6 +14,7 @@ TEST_BIN = \ ip \ maths \ matrix \ + pool \ range \ signal \ version @@ -44,6 +45,9 @@ maths_SOURCES = maths.cpp matrix_LDADD = $(builddir)/../libutil.la matrix_SOURCES = matrix.cpp +pool_LDADD = $(builddir)/../libutil.la +pool_SOURCES = pool.cpp + range_LDADD = $(builddir)/../libutil.la range_SOURCES = range.cpp diff --git a/test/pool.cpp b/test/pool.cpp new file mode 100644 index 00000000..ab9ed577 --- /dev/null +++ b/test/pool.cpp @@ -0,0 +1,86 @@ + +#include "debug.hpp" +#include "pool.hpp" + +#include +#include +#include + +using namespace std; + + +void +check_single (void) { + // Ensure a single element doesn't break the circular linked list + pool single(1); + single.release (single.acquire ()); +} + + +void +check_unique_ptr (void) { + pool uintpool (1025); + set uintset; + + // Take all pointers out, checking they are unique, then replace for destruction. + for (unsigned int i = 0; i < uintpool.capacity (); ++i) { + bool success = uintset.insert (uintpool.acquire ()).second; + check_hard (success); + } + + for (auto i = uintset.begin (); i != uintset.end (); ++i) + uintpool.release (*i); + uintset.clear (); + + // Do the above one more time to ensure that releasing works right + for (unsigned int i = 0; i < uintpool.capacity (); ++i) { + bool success = uintset.insert (uintpool.acquire ()).second; + check_hard (success); + } + + for (auto i = uintset.begin (); i != uintset.end (); ++i) + uintpool.release (*i); +} + + +void +check_keep_value (void) { + // Ensure that items keep their values. + pool uintpool(256); + std::vector uintvector; + uintvector.reserve(uintpool.capacity ()); + + // Give every item a unique value + for (unsigned int i = 0; i < uintpool.capacity (); ++i) { + uint64_t *uint = uintpool.acquire (); + *uint = i; + + uintvector.push_back(uint); + } + check (uintvector.size () == uintpool.capacity ()); + + // Ensure they're all still present + vector present(uintpool.capacity (), false); + for (auto i = uintvector.begin (); i != uintvector.end (); ++i) { + check_hard (**i < uintpool.capacity ()); + check_hard (present[**i] != true); + + present[**i] = true; + } + + // All must have been marked as present... + check_hard (find (present.begin (), present.end (), false) == present.end ()); + + // Release all back into the pool for destruction + for (auto i = uintvector.begin (); i != uintvector.end (); ++i) + uintpool.release (*i); + uintvector.clear (); +} + + +int +main (int argc, char **argv) { + check_single (); + check_unique_ptr (); + check_keep_value (); +}