diff --git a/src/pool.h b/src/pool.h index 9d302307..4186a889 100644 --- a/src/pool.h +++ b/src/pool.h @@ -35,11 +35,17 @@ SOFTWARE. #include "array.h" #include "container.h" #include "integral_limits.h" -#include "ipool.h" +#include "platform.h" +#include "nullptr.h" +#include "alignment.h" +#include "error_handler.h" #include #include +#undef ETL_FILE +#define ETL_FILE "11" + //***************************************************************************** ///\defgroup pool pool /// A fixed capacity pool. @@ -48,12 +54,262 @@ SOFTWARE. namespace etl { + //*************************************************************************** + /// The base class for pool exceptions. + ///\ingroup pool + //*************************************************************************** + class pool_exception : public exception + { + public: + + pool_exception(string_type what, string_type file_name, numeric_type line_number) + : exception(what, file_name, line_number) + {} + }; + + //*************************************************************************** + /// The exception thrown when the pool has no more free items. + ///\ingroup pool + //*************************************************************************** + class pool_no_allocation : public pool_exception + { + public: + + explicit pool_no_allocation(string_type file_name, numeric_type line_number) + : pool_exception(ETL_ERROR_TEXT("pool:allocation", ETL_FILE"A"), file_name, line_number) + {} + }; + + //*************************************************************************** + /// The exception thrown when an object is released which does not belong to the pool. + ///\ingroup pool + //*************************************************************************** + class pool_object_not_in_pool : public pool_exception + { + public: + + pool_object_not_in_pool(string_type file_name, numeric_type line_number) + : pool_exception(ETL_ERROR_TEXT("pool:notinpool", ETL_FILE"B"), file_name, line_number) + {} + }; + + //*************************************************************************** + ///\ingroup pool + //*************************************************************************** + class ipool + { + public: + + typedef size_t size_type; + + //************************************************************************* + /// Allocate an object from the pool. + /// Uses the default constructor. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a nullptr is returned. + //************************************************************************* + template + T* allocate() + { + return reinterpret_cast(allocate_item()); + } + + //************************************************************************* + /// Release an object in the pool. + /// If asserts or exceptions are enabled and the object does not belong to this + /// pool then an etl::pool_object_not_in_pool is thrown. + /// \param p_object A pointer to the object to be released. + //************************************************************************* + void release(const void* p_object) + { + release_item((char*)p_object); + } + + //************************************************************************* + /// Release all objects in the pool. + //************************************************************************* + void release_all() + { + items_allocated = 0; + items_initialised = 0; + p_next = p_buffer; + } + + //************************************************************************* + /// Check to see if the object belongs to the pool. + /// \param p_object A pointer to the object to be checked. + /// \return true<\b> if it does, otherwise false + //************************************************************************* + //template + bool is_in_pool(const void* p_object) const + { + return is_item_in_pool((const char*)p_object); + } + + //************************************************************************* + /// Returns the maximum number of items in the pool. + //************************************************************************* + size_t max_items() const + { + return MAX_ITEMS; + } + + //************************************************************************* + /// Returns the number of free items in the pool. + //************************************************************************* + size_t available() const + { + return MAX_ITEMS - items_allocated; + } + + //************************************************************************* + /// Returns the number of allocated items in the pool. + //************************************************************************* + size_t size() const + { + return items_allocated; + } + + //************************************************************************* + /// Checks to see if there are no allocated items in the pool. + /// \return true if there are none allocated. + //************************************************************************* + bool empty() const + { + return items_allocated == 0; + } + + //************************************************************************* + /// Checks to see if there are no free items in the pool. + /// \return true if there are none free. + //************************************************************************* + bool full() const + { + return items_allocated == MAX_ITEMS; + } + + protected: + + //************************************************************************* + /// Constructor + //************************************************************************* + ipool(char* p_buffer_, uint32_t item_size, uint32_t max_items) + : p_buffer(p_buffer_), + p_next(p_buffer_), + items_allocated(0), + items_initialised(0), + ITEM_SIZE(item_size), + MAX_ITEMS(max_items) + { + } + + private: + + //************************************************************************* + /// Allocate an item from the pool. + //************************************************************************* + char* allocate_item() + { + char* p_value = nullptr; + + // Any free space left? + if (items_allocated < MAX_ITEMS) + { + // Initialise another one if necessary. + if (items_initialised < MAX_ITEMS) + { + uintptr_t p = reinterpret_cast(p_buffer + (items_initialised * ITEM_SIZE)); + *reinterpret_cast(p) = p + ITEM_SIZE; + ++items_initialised; + } + + // Get the address of new allocated item. + p_value = p_next; + + ++items_allocated; + if (items_allocated != MAX_ITEMS) + { + // Set up the pointer to the next free item + p_next = *reinterpret_cast(p_next); + } + else + { + // No more left! + p_next = nullptr; + } + } + else + { + ETL_ASSERT(false, ETL_ERROR(etl::pool_no_allocation)); + } + + return p_value; + } + + //************************************************************************* + /// Release an item back to the pool. + //************************************************************************* + void release_item(char* p_value) + { + // Does it belong to us? + ETL_ASSERT(is_item_in_pool(p_value), ETL_ERROR(pool_object_not_in_pool)); + + if (p_next != nullptr) + { + // Point it to the current free item. + *(uintptr_t*)p_value = reinterpret_cast(p_next); + } + else + { + // This is the only free item. + *((uintptr_t*)p_value) = 0; + } + + p_next = p_value; + + --items_allocated; + } + + //************************************************************************* + /// Check if the item belongs to this pool. + //************************************************************************* + bool is_item_in_pool(const char* p) const + { + // Within the range of the buffer? + intptr_t distance = p - p_buffer; + bool is_within_range = (distance >= 0) && (distance <= intptr_t((ITEM_SIZE * MAX_ITEMS) - ITEM_SIZE)); + + // Modulus and division can be slow on some architectures, so only do this in debug. +#if defined(ETL_DEBUG) + // Is the address on a valid object boundary? + bool is_valid_address = ((distance % ITEM_SIZE) == 0); +#else + bool is_valid_address = true; +#endif + + return is_within_range && is_valid_address; + } + + // Disable copy construction and assignment. + ipool(const ipool&); + ipool& operator =(const ipool&); + + char* p_buffer; + char* p_next; + + uint32_t items_allocated; ///< The number of items allocated. + uint32_t items_initialised; ///< The number of items initialised. + + const uint32_t ITEM_SIZE; ///< The size of allocated items. + const uint32_t MAX_ITEMS; ///< The maximum number of objects that can be allocated. + }; + //************************************************************************* /// A templated pool implementation that uses a fixed size pool. ///\ingroup pool //************************************************************************* template - class pool : public ipool + class pool : public etl::ipool { public: @@ -63,7 +319,7 @@ namespace etl /// Constructor //************************************************************************* pool() - : ipool(reinterpret_cast(&buffer[0]), ELEMENT_SIZE, SIZE) + : etl::ipool(reinterpret_cast(&buffer[0]), ELEMENT_SIZE, SIZE) { } @@ -93,5 +349,7 @@ namespace etl }; } +#undef ETL_FILE + #endif