diff --git a/include/etl/ipool.h b/include/etl/ipool.h index 94292774..141cdc7e 100644 --- a/include/etl/ipool.h +++ b/include/etl/ipool.h @@ -34,6 +34,7 @@ SOFTWARE. #include "platform.h" #include "error_handler.h" #include "exception.h" +#include "iterator.h" #include "static_assert.h" #include "utility.h" #include "memory.h" @@ -104,6 +105,189 @@ namespace etl typedef size_t size_type; + private: + + //*************************************************************************** + /// Iterator helper functions + //*************************************************************************** + + const char* buffer_end() const + { + return p_buffer + Item_Size * items_initialised; + } + + //*************************************************************************** + /// Optimization: a candidate for being in free list, but no guarantee + /// if false, cannot be part of free list, and therefore is allocated + /// if true, possibly in free list, but still needs to be checked via free list + //*************************************************************************** + bool is_pointing_into_pool_or_end_or_nullptr(const char *address) const + { + return address == ETL_NULLPTR || (p_buffer <= address && address <= buffer_end()); + } + + //*************************************************************************** + /// Iterate free list to confirm specified address is included or not + //*************************************************************************** + bool is_in_free_list(const char* address) const + { + const char* i = p_next; + while (i != ETL_NULLPTR) + { + if (address == i) + { + return true; + } + i = *reinterpret_cast(i); + } + return false; + } + + public: + + template + class ipool_iterator + { + public: + + friend class ipool; + + typedef typename etl::conditional::type value_type; + typedef typename etl::conditional::type reference; + typedef typename etl::conditional::type pointer; + typedef ptrdiff_t difference_type; + typedef ETL_OR_STD::forward_iterator_tag iterator_category; + typedef typename etl::conditional::type void_type; + typedef typename etl::conditional::type pool_type; + typedef typename etl::conditional::type pointer_type; + + ipool_iterator(const ipool_iterator& other) + : p_current(other.p_current) + , p_pool(other.p_pool) + { + find_allocated(); + } + + ipool_iterator& operator ++() + { + p_current = p_current + p_pool->Item_Size; + find_allocated(); + return *this; + } + + ipool_iterator operator ++(int) + { + ipool_iterator temp(*this); + p_current = p_current + p_pool->Item_Size; + find_allocated(); + return temp; + } + + ipool_iterator& operator =(const ipool_iterator& other) + { + p_current = other.p_current; + p_pool = other.p_pool; + return *this; + } + + void_type operator *() const + { + return p_current; + } + + template + T& get() const + { + return *reinterpret_cast(p_current); + } + + friend bool operator == (const ipool_iterator& lhs, const ipool_iterator& rhs) + { + return lhs.p_current == rhs.p_current; + } + + friend bool operator != (const ipool_iterator& lhs, const ipool_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + + //*************************************************************************** + /// find allocated item by increasing p_current, starting at p_current + /// leave p_current at buffer_end() if no further allocated item found + //*************************************************************************** + void find_allocated() + { + while (p_current < p_pool->buffer_end()) + { + value_type value = *reinterpret_cast(p_current); + if (!p_pool->is_pointing_into_pool_or_end_or_nullptr(value)) + { + return; + } + if (!p_pool->is_in_free_list(p_current)) + { + return; + } + p_current += p_pool->Item_Size; + } + } + + ipool_iterator(value_type p, pool_type* pool_) + : p_current(p) + , p_pool(pool_) + { + find_allocated(); + } + + value_type p_current; + pool_type* p_pool; + }; + + template + friend class ipool_iterator; + + typedef ipool_iterator iterator; + + class const_iterator : public ipool_iterator + { + public: + const_iterator(const ipool_iterator& other) : ipool_iterator(other) {} + const_iterator(const ipool_iterator& other) : ipool_iterator(other.p_current, other.p_pool) {} + const_iterator(value_type p, pool_type* pool_) : ipool_iterator(p, pool_) {} + }; + + iterator begin() + { + return iterator(p_buffer, this); + } + + iterator end() + { + return iterator(p_buffer + Item_Size * items_initialised, this); + } + + const_iterator begin() const + { + return const_iterator(p_buffer, this); + } + + const_iterator end() const + { + return const_iterator(p_buffer + Item_Size * items_initialised, this); + } + + const_iterator cbegin() const + { + return const_iterator(p_buffer, this); + } + + const_iterator cend() const + { + return const_iterator(p_buffer + Item_Size * items_initialised, this); + } + //************************************************************************* /// Allocate storage for an object from the pool. /// If asserts or exceptions are enabled and there are no more free items an @@ -337,6 +521,7 @@ namespace etl } private: + static ETL_CONSTEXPR uintptr_t invalid_item_ptr = 1; //************************************************************************* /// Allocate an item from the pool. @@ -371,6 +556,11 @@ namespace etl // No more left! p_next = ETL_NULLPTR; } + + // invalid pointer, outside pool + // needs to be different from ETL_NULLPTR since ETL_NULLPTR is used + // as list endmarker + *reinterpret_cast(p_value) = invalid_item_ptr; } else { diff --git a/test/test_pool.cpp b/test/test_pool.cpp index 2866ab1d..c74425aa 100644 --- a/test/test_pool.cpp +++ b/test/test_pool.cpp @@ -34,6 +34,7 @@ SOFTWARE. #include #include +#include "etl/unaligned_type.h" #include "etl/pool.h" #include "etl/largest.h" @@ -484,4 +485,254 @@ namespace CHECK_EQUAL(3, memPool.available()); CHECK_EQUAL(0, memPool.size()); } + + //************************************************************************* + TEST(test_iterators) + { + etl::pool pool0; + + etl::ipool::iterator begin = pool0.begin(); + etl::ipool::iterator end = pool0.end(); + + CHECK(begin == end); + CHECK(!(begin != end)); + CHECK_EQUAL(etl::distance(begin, end), 0); + + int* a = pool0.allocate(); + *a = 1; + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 1); + + int* b = pool0.allocate(); + *b = 2; + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 2); + + // post increment + for (auto i = pool0.begin(); i != pool0.end(); i++) + { + CHECK(*reinterpret_cast(*i) == 1 || *reinterpret_cast(*i) == 2); + } + + // pre increment + for (auto i = pool0.begin(); i != pool0.end(); ++i) + { + CHECK(*reinterpret_cast(*i) == 1 || *reinterpret_cast(*i) == 2); + } + + int* c = pool0.allocate(); + *c = 3; + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 3); + + pool0.release(b); + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 2); + + int* d = pool0.allocate(); + *d = 4; + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 3); + + b = pool0.allocate(); + *b = 2; + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 4); + + // post increment + for (auto i = pool0.begin(); i != pool0.end(); i++) + { + CHECK(*reinterpret_cast(*i) >= 1 && *reinterpret_cast(*i) <= 4); + } + + // pre increment + for (auto i = pool0.begin(); i != pool0.end(); ++i) + { + CHECK(*reinterpret_cast(*i) >= 1 && *reinterpret_cast(*i) <= 4); + } + + pool0.release(b); + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 3); + + pool0.release(a); + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 2); + + pool0.release(d); + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 1); + + auto& v = begin.get(); + v = 1; + + pool0.release(c); + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin == end); + CHECK(!(begin != end)); + CHECK_EQUAL(etl::distance(begin, end), 0); + } + + //************************************************************************* + TEST(test_const_iterators) + { + etl::pool pool0; + + etl::ipool::const_iterator begin = pool0.begin(); + etl::ipool::const_iterator end = pool0.end(); + + CHECK(begin == end); + CHECK(!(begin != end)); + CHECK_EQUAL(etl::distance(begin, end), 0); + + int* a = pool0.allocate(); + *a = 1; + + begin = pool0.cbegin(); + end = pool0.cend(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 1); + + int* b = pool0.allocate(); + *b = 2; + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 2); + + // post increment + for (auto i = pool0.begin(); i != pool0.end(); i++) + { + CHECK(*reinterpret_cast(*i) == 1 || *reinterpret_cast(*i) == 2); + } + + // pre increment + for (auto i = pool0.begin(); i != pool0.end(); ++i) + { + CHECK(*reinterpret_cast(*i) == 1 || *reinterpret_cast(*i) == 2); + } + + int* c = pool0.allocate(); + *c = 3; + + begin = pool0.cbegin(); + end = pool0.cend(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 3); + + pool0.release(b); + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 2); + + int* d = pool0.allocate(); + *d = 4; + + begin = pool0.cbegin(); + end = pool0.cend(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 3); + + b = pool0.allocate(); + *b = 2; + + begin = pool0.cbegin(); + end = pool0.cend(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 4); + + // post increment + for (auto i = pool0.begin(); i != pool0.end(); i++) + { + CHECK(*reinterpret_cast(*i) >= 1 && *reinterpret_cast(*i) <= 4); + } + + // pre increment + for (auto i = pool0.cbegin(); i != pool0.cend(); ++i) + { + CHECK(*reinterpret_cast(*i) >= 1 && *reinterpret_cast(*i) <= 4); + } + + pool0.release(b); + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 3); + + pool0.release(a); + + begin = pool0.cbegin(); + end = pool0.cend(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 2); + + pool0.release(d); + + begin = pool0.begin(); + end = pool0.end(); + CHECK(begin != end); + CHECK(!(begin == end)); + CHECK_EQUAL(etl::distance(begin, end), 1); + + const auto& v = begin.get(); + CHECK(v != 0); + + pool0.release(c); + + begin = pool0.cbegin(); + end = pool0.cend(); + CHECK(begin == end); + CHECK(!(begin != end)); + CHECK_EQUAL(etl::distance(begin, end), 0); + } }