From 494059fd6b7218d9d0bd60dfbf2157b305930d97 Mon Sep 17 00:00:00 2001 From: Daniel B <23653928+daniel-brosche@users.noreply.github.com> Date: Fri, 1 Oct 2021 19:46:52 +0200 Subject: [PATCH 1/2] Feature/pool ext variant pool ext (#433) * added threads dependency to meson build because of pthread linkage problems (gcc version Debian 8.3.0-6) * initial version of generic_pool_ext, pool_ext and variant_pool_ext (extended unit tests) * format code * fix test_call_if_and_not_valid_returning_void by moving SetupFixture into namespace (to ensure correct test setup) Co-authored-by: Daniel B --- include/etl/generic_pool.h | 133 ++++++ include/etl/pool.h | 117 +++++ include/etl/variant_pool.h | 126 ++++++ meson.build | 6 +- test/CMakeLists.txt | 2 + test/test_delegate.cpp | 24 +- test/test_pool_external_buffer.cpp | 496 +++++++++++++++++++++ test/test_variant_pool_external_buffer.cpp | 319 +++++++++++++ 8 files changed, 1210 insertions(+), 13 deletions(-) create mode 100644 test/test_pool_external_buffer.cpp create mode 100644 test/test_variant_pool_external_buffer.cpp diff --git a/include/etl/generic_pool.h b/include/etl/generic_pool.h index 9979e425..bda98e74 100644 --- a/include/etl/generic_pool.h +++ b/include/etl/generic_pool.h @@ -194,7 +194,140 @@ namespace etl generic_pool& operator =(const generic_pool&); }; + template + class generic_pool_ext : public etl::ipool { + private: + // The pool element. + union element_internal { + char* next; ///< Pointer to the next free element. + char value[VTypeSize]; ///< Storage for value type. + typename etl::type_with_alignment::type dummy; ///< Dummy item to get correct alignment. + }; + static const size_t ELEMENT_INTERNAL_SIZE = sizeof(element_internal); + + public: + static ETL_CONSTANT size_t ALIGNMENT = VAlignment; + static ETL_CONSTANT size_t TYPE_SIZE = VTypeSize; + + using element = typename etl::aligned_storage::value>::type; + + //************************************************************************* + /// Constructor + //************************************************************************* + generic_pool_ext(element* buffer, size_t size) : etl::ipool(reinterpret_cast(&buffer[0]), ELEMENT_INTERNAL_SIZE, size) {} + + //************************************************************************* + /// Allocate an object from the pool. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + /// Static asserts if the specified type is too large for the pool. + //************************************************************************* + template + U* allocate() + { + ETL_STATIC_ASSERT(etl::alignment_of::value <= VAlignment, "Type has incompatible alignment"); + ETL_STATIC_ASSERT(sizeof(U) <= VTypeSize, "Type too large for pool"); + return ipool::allocate(); + } + +#if ETL_CPP11_NOT_SUPPORTED || ETL_POOL_CPP03_CODE || ETL_USING_STLPORT + //************************************************************************* + /// Allocate storage for an object from the pool and create with default. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + U* create() + { + ETL_STATIC_ASSERT(etl::alignment_of::value <= VAlignment, "Type has incompatible alignment"); + ETL_STATIC_ASSERT(sizeof(U) <= VTypeSize, "Type too large for pool"); + return ipool::create(); + } + + //************************************************************************* + /// Allocate storage for an object from the pool and create with 1 parameter. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + U* create(const T1& value1) + { + ETL_STATIC_ASSERT(etl::alignment_of::value <= VAlignment, "Type has incompatible alignment"); + ETL_STATIC_ASSERT(sizeof(U) <= VTypeSize, "Type too large for pool"); + return ipool::create(value1); + } + + //************************************************************************* + /// Allocate storage for an object from the pool and create with 2 parameters. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + U* create(const T1& value1, const T2& value2) + { + ETL_STATIC_ASSERT(etl::alignment_of::value <= VAlignment, "Type has incompatible alignment"); + ETL_STATIC_ASSERT(sizeof(U) <= VTypeSize, "Type too large for pool"); + return ipool::create(value1, value2); + } + + //************************************************************************* + /// Allocate storage for an object from the pool and create with 3 parameters. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + U* create(const T1& value1, const T2& value2, const T3& value3) + { + ETL_STATIC_ASSERT(etl::alignment_of::value <= VAlignment, "Type has incompatible alignment"); + ETL_STATIC_ASSERT(sizeof(U) <= VTypeSize, "Type too large for pool"); + return ipool::create(value1, value2, value3); + } + + //************************************************************************* + /// Allocate storage for an object from the pool and create with 4 parameters. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + U* create(const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + ETL_STATIC_ASSERT(etl::alignment_of::value <= VAlignment, "Type has incompatible alignment"); + ETL_STATIC_ASSERT(sizeof(U) <= VTypeSize, "Type too large for pool"); + return ipool::create(value1, value2, value3, value4); + } +#else + //************************************************************************* + /// Emplace with variadic constructor parameters. + //************************************************************************* + template + U* create(Args&&... args) + { + ETL_STATIC_ASSERT(etl::alignment_of::value <= VAlignment, "Type has incompatible alignment"); + ETL_STATIC_ASSERT(sizeof(U) <= VTypeSize, "Type too large for pool"); + return ipool::create(etl::forward(args)...); + } +#endif + + //************************************************************************* + /// Destroys the object. + /// Undefined behaviour if the pool does not contain a 'U'. + /// \param p_object A pointer to the object to be destroyed. + //************************************************************************* + template + void destroy(const U* const p_object) + { + ETL_STATIC_ASSERT(etl::alignment_of::value <= VAlignment, "Type has incompatible alignment"); + ETL_STATIC_ASSERT(sizeof(U) <= VTypeSize, "Type too large for pool"); + p_object->~U(); + ipool::release(p_object); + } + + private: + // Should not be copied. + generic_pool_ext(const generic_pool_ext&); + generic_pool_ext& operator=(const generic_pool_ext&); + }; } #endif diff --git a/include/etl/pool.h b/include/etl/pool.h index 87cc4e94..a8a867a0 100644 --- a/include/etl/pool.h +++ b/include/etl/pool.h @@ -178,6 +178,123 @@ namespace etl pool(const pool&) ETL_DELETE; pool& operator =(const pool&) ETL_DELETE; }; + + template + class pool_ext : public etl::generic_pool_ext::value> { + private: + typedef etl::generic_pool_ext::value> base_t; + + public: + using base_t::ALIGNMENT; + using base_t::TYPE_SIZE; + + //************************************************************************* + /// Constructor + //************************************************************************* + pool_ext(typename base_t::element* buffer, size_t size) : base_t(buffer, size) {} + + //************************************************************************* + /// 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 null pointer is returned. + /// Static asserts if the specified type is too large for the pool. + //************************************************************************* + T* allocate() { return base_t::template allocate(); } + +#if ETL_CPP11_NOT_SUPPORTED || ETL_POOL_CPP03_CODE || ETL_USING_STLPORT + //************************************************************************* + /// Allocate storage for an object from the pool and create with default. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + T* create() { return base_t::template create(); } + + //************************************************************************* + /// Allocate storage for an object from the pool and create with 1 parameter. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + T* create(const T1& value1) + { + return base_t::template create(value1); + } + + //************************************************************************* + /// Allocate storage for an object from the pool and create with 2 parameters. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + T* create(const T1& value1, const T2& value2) + { + return base_t::template create(value1, value2); + } + + //************************************************************************* + /// Allocate storage for an object from the pool and create with 3 parameters. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + T* create(const T1& value1, const T2& value2, const T3& value3) + { + return base_t::template create(value1, value2, value3); + } + + //************************************************************************* + /// Allocate storage for an object from the pool and create with 4 parameters. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + T* create(const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + return base_t::template create(value1, value2, value3, value4); + } +#else + //************************************************************************* + /// Allocate storage for an object from the pool and create with variadic parameters. + /// If asserts or exceptions are enabled and there are no more free items an + /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. + //************************************************************************* + template + T* create(Args&&... args) + { + return base_t::template create(etl::forward(args)...); + } +#endif + + //************************************************************************* + /// Releases the object. + /// Undefined behaviour if the pool does not contain a 'U' object derived from 'U'. + /// \param p_object A pointer to the object to be destroyed. + //************************************************************************* + template + void release(const U* const p_object) + { + ETL_STATIC_ASSERT((etl::is_same::value || etl::is_base_of::value), "Pool does not contain this type"); + base_t::release(p_object); + } + + //************************************************************************* + /// Destroys the object. + /// Undefined behaviour if the pool does not contain a 'U' object derived from 'U'. + /// \param p_object A pointer to the object to be destroyed. + //************************************************************************* + template + void destroy(const U* const p_object) + { + ETL_STATIC_ASSERT((etl::is_base_of::value), "Pool does not contain this type"); + base_t::destroy(p_object); + } + + private: + // Should not be copied. + pool_ext(const pool_ext&) ETL_DELETE; + pool_ext& operator=(const pool_ext&) ETL_DELETE; + }; } #endif diff --git a/include/etl/variant_pool.h b/include/etl/variant_pool.h index 4d448673..6a7c3de8 100644 --- a/include/etl/variant_pool.h +++ b/include/etl/variant_pool.h @@ -206,6 +206,132 @@ namespace etl variant_pool(const variant_pool&) ETL_DELETE; variant_pool& operator =(const variant_pool&) ETL_DELETE; }; + + //*************************************************************************** + template + class variant_pool_ext + : public etl::generic_pool_ext::size, + etl::largest::alignment> { + public: + typedef etl::generic_pool_ext::size, + etl::largest::alignment> + base_t; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + variant_pool_ext(typename base_t::element* buffer, size_t size) : base_t(buffer, size) {} + +#if ETL_CPP11_NOT_SUPPORTED || ETL_USING_STLPORT + //************************************************************************* + /// Creates the object. Default constructor. + //************************************************************************* + template + T* create() + { + ETL_STATIC_ASSERT((etl::is_one_of::value), "Unsupported type"); + + return base_t::template create(); + } + + //************************************************************************* + /// Creates the object. One parameter constructor. + //************************************************************************* + template + T* create(const TP1& p1) + { + ETL_STATIC_ASSERT((etl::is_one_of::value), "Unsupported type"); + + return base_t::template create(p1); + } + + //************************************************************************* + /// Creates the object. Two parameter constructor. + //************************************************************************* + template + T* create(const TP1& p1, const TP2& p2) + { + ETL_STATIC_ASSERT((etl::is_one_of::value), "Unsupported type"); + + return base_t::template create(p1, p2); + } + + //************************************************************************* + /// Creates the object. Three parameter constructor. + //************************************************************************* + template + T* create(const TP1& p1, const TP2& p2, const TP3& p3) + { + ETL_STATIC_ASSERT((etl::is_one_of::value), "Unsupported type"); + + return base_t::template create(p1, p2, p3); + } + + //************************************************************************* + /// Creates the object. Four parameter constructor. + //************************************************************************* + template + T* create(const TP1& p1, const TP2& p2, const TP3& p3, const TP4& p4) + { + ETL_STATIC_ASSERT((etl::is_one_of::value), "Unsupported type"); + + return base_t::template create(p1, p2, p3, p4); + } +#else + //************************************************************************* + /// Creates the object from a type. Variadic parameter constructor. + //************************************************************************* + template + T* create(Args&&... args) + { + ETL_STATIC_ASSERT((etl::is_one_of::value), "Unsupported type"); + + return base_t::template create(args...); + } +#endif + + //************************************************************************* + /// Destroys the object. + //************************************************************************* + template + void destroy(const T* const p) + { + ETL_STATIC_ASSERT( + (etl::is_one_of::value || etl::is_base_of::value || + etl::is_base_of::value || etl::is_base_of::value || etl::is_base_of::value || etl::is_base_of::value || + etl::is_base_of::value || etl::is_base_of::value || etl::is_base_of::value || etl::is_base_of::value || + etl::is_base_of::value || etl::is_base_of::value || etl::is_base_of::value || etl::is_base_of::value || + etl::is_base_of::value || etl::is_base_of::value || etl::is_base_of::value), + "Invalid type"); + + base_t::destroy(p); + } + + //************************************************************************* + /// Returns the maximum number of items in the variant_pool. + //************************************************************************* + // redundant also declared in ipool::max_size() + size_t max_size() const { return base_t::max_size(); } + + private: + variant_pool_ext(const variant_pool_ext&) ETL_DELETE; + variant_pool_ext& operator=(const variant_pool_ext&) ETL_DELETE; + }; } #endif diff --git a/meson.build b/meson.build index 4a1fe78c..39a1033c 100644 --- a/meson.build +++ b/meson.build @@ -85,6 +85,7 @@ etl_test_sources = files( 'test/test_parameter_type.cpp', 'test/test_parity_checksum.cpp', 'test/test_pool.cpp', + 'test/test_pool_external_buffer.cpp', 'test/test_priority_queue.cpp', 'test/test_queue.cpp', 'test/test_queue_memory_model_small.cpp', @@ -114,6 +115,7 @@ etl_test_sources = files( 'test/test_variant_legacy.cpp', 'test/test_variant_variadic.cpp', 'test/test_variant_pool.cpp', + 'test/test_variant_pool_external_buffer.cpp', 'test/test_variant_variadic.cpp', 'test/test_vector.cpp', 'test/test_vector_non_trivial.cpp', @@ -179,6 +181,8 @@ unittestcpp_dep = dependency('UnitTest++', disabler: true, ) +threads_dep = dependency('threads') + etl_unit_tests = executable('etl_unit_tests', include_directories: [ include_directories('test'), @@ -187,7 +191,7 @@ etl_unit_tests = executable('etl_unit_tests', include_directories('include') ], sources: etl_test_sources, - dependencies: unittestcpp_dep, + dependencies: [unittestcpp_dep, threads_dep], cpp_args: [ '-fexceptions', '-DENABLE_ETL_UNIT_TESTS', diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f8aab040..7acc9421 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -185,6 +185,7 @@ set(TEST_SOURCE_FILES test_parity_checksum.cpp test_pearson.cpp test_pool.cpp + test_pool_external_buffer.cpp test_priority_queue.cpp test_quantize.cpp test_queue.cpp @@ -257,6 +258,7 @@ set(TEST_SOURCE_FILES test_variant_legacy.cpp test_variant_variadic.cpp test_variant_pool.cpp + test_variant_pool_external_buffer.cpp test_vector.cpp test_vector_external_buffer.cpp test_vector_non_trivial.cpp diff --git a/test/test_delegate.cpp b/test/test_delegate.cpp index 02bdb882..9a2a6023 100644 --- a/test/test_delegate.cpp +++ b/test/test_delegate.cpp @@ -222,20 +222,20 @@ namespace const Test const_test_static; } -//***************************************************************************** -// Initialises the test results. -//***************************************************************************** -struct SetupFixture -{ - SetupFixture() - { - function_called = false; - parameter_correct = false; - } -}; - namespace { + //***************************************************************************** + // Initialises the test results. + //***************************************************************************** + struct SetupFixture + { + SetupFixture() + { + function_called = false; + parameter_correct = false; + } + }; + SUITE(test_delegate) { //************************************************************************* diff --git a/test/test_pool_external_buffer.cpp b/test/test_pool_external_buffer.cpp new file mode 100644 index 00000000..c2298c29 --- /dev/null +++ b/test/test_pool_external_buffer.cpp @@ -0,0 +1,496 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2014 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "data.h" + +#include +#include +#include + +#include "etl/pool.h" +#include "etl/largest.h" + +#if defined(ETL_COMPILER_GCC) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif + +typedef TestDataDC Test_Data; +typedef TestDataNDC Test_Data2; + +namespace +{ + struct D0 + { + D0() + { + } + }; + + struct D1 + { + D1(const std::string& a_) + : a(a_) + { + } + + std::string a; + }; + + struct D2 + { + D2(const std::string& a_, const std::string& b_) + : a(a_), + b(b_) + { + } + + std::string a; + std::string b; + }; + + struct D3 + { + D3(const std::string& a_, const std::string& b_, const std::string& c_) + : a(a_), + b(b_), + c(c_) + { + } + + std::string a; + std::string b; + std::string c; + }; + + struct D4 + { + D4(const std::string& a_, const std::string& b_, const std::string& c_, const std::string& d_) + : a(a_), + b(b_), + c(c_), + d(d_) + { + } + + std::string a; + std::string b; + std::string c; + std::string d; + }; + + bool operator == (const D0&, const D0&) + { + return true; + } + + bool operator == (const D1& lhs, const D1& rhs) + { + return (lhs.a == rhs.a); + } + + bool operator == (const D2& lhs, const D2& rhs) + { + return (lhs.a == rhs.a) && (lhs.b == rhs.b); + } + + bool operator == (const D3& lhs, const D3& rhs) + { + return (lhs.a == rhs.a) && (lhs.b == rhs.b) && (lhs.c == rhs.c); + } + + bool operator == (const D4& lhs, const D4& rhs) + { + return (lhs.a == rhs.a) && (lhs.b == rhs.b) && (lhs.c == rhs.c) && (lhs.d == rhs.d); + } + + std::ostream& operator <<(std::ostream& os, const D0&) + { + return os; + } + + std::ostream& operator <<(std::ostream& os, const D1& d) + { + os << d.a; + return os; + } + + std::ostream& operator <<(std::ostream& os, const D2& d) + { + os << d.a << " " << d.b; + return os; + } + + std::ostream& operator <<(std::ostream& os, const D3& d) + { + os << d.a << " " << d.b << " " << d.c; + return os; + } + + std::ostream& operator <<(std::ostream& os, const D4& d) + { + os << d.a << " " << d.b << " " << d.c << " " << d.d; + return os; + } + + SUITE(test_pool_external_buffer) + { + //************************************************************************* + TEST(test_allocate) + { + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + Test_Data* p1 = nullptr; + Test_Data* p2 = nullptr; + Test_Data* p3 = nullptr; + Test_Data* p4 = nullptr; + + CHECK_NO_THROW(p1 = pool.allocate()); + CHECK_NO_THROW(p2 = pool.allocate()); + CHECK_NO_THROW(p3 = pool.allocate()); + CHECK_NO_THROW(p4 = pool.allocate()); + + CHECK(p1 != p2); + CHECK(p1 != p3); + CHECK(p1 != p4); + CHECK(p2 != p3); + CHECK(p2 != p4); + CHECK(p3 != p4); + + CHECK_THROW(pool.allocate(), etl::pool_no_allocation); + } + + //************************************************************************* + TEST(test_release) + { + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + Test_Data* p1 = pool.allocate(); + Test_Data* p2 = pool.allocate(); + Test_Data* p3 = pool.allocate(); + Test_Data* p4 = pool.allocate(); + + CHECK_NO_THROW(pool.release(p2)); + CHECK_NO_THROW(pool.release(p3)); + CHECK_NO_THROW(pool.release(p1)); + CHECK_NO_THROW(pool.release(p4)); + + CHECK_EQUAL(4U, pool.available()); + + Test_Data not_in_pool; + + CHECK_THROW(pool.release(¬_in_pool), etl::pool_object_not_in_pool); + } + + //************************************************************************* + TEST(test_allocate_release) + { + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + Test_Data* p1 = pool.allocate(); + Test_Data* p2 = pool.allocate(); + Test_Data* p3 = pool.allocate(); + Test_Data* p4 = pool.allocate(); + + // Allocated p1, p2, p3, p4 + + CHECK_EQUAL(0U, pool.available()); + + CHECK_NO_THROW(pool.release(p2)); + CHECK_NO_THROW(pool.release(p3)); + + // Allocated p1, p4 + + CHECK_EQUAL(2U, pool.available()); + + Test_Data* p5 = pool.allocate(); + Test_Data* p6 = pool.allocate(); + + // Allocated p1, p4, p5, p6 + + CHECK_EQUAL(0U, pool.available()); + + CHECK(p5 != p1); + CHECK(p5 != p4); + + CHECK(p6 != p1); + CHECK(p6 != p4); + + CHECK_NO_THROW(pool.release(p5)); + + // Allocated p1, p4, p6 + + CHECK_EQUAL(1U, pool.available()); + + Test_Data* p7 = pool.allocate(); + + // Allocated p1, p4, p6, p7 + + CHECK(p7 != p1); + CHECK(p7 != p4); + CHECK(p7 != p6); + + CHECK(pool.full()); + } + + //************************************************************************* + TEST(test_available) + { + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + CHECK_EQUAL(4U, pool.available()); + + Test_Data* p; + + p = pool.allocate(); + CHECK_EQUAL(3U, pool.available()); + + p = pool.allocate(); + CHECK_EQUAL(2U, pool.available()); + + p = pool.allocate(); + CHECK_EQUAL(1U, pool.available()); + + p = pool.allocate(); + CHECK_EQUAL(0U, pool.available()); + } + + //************************************************************************* + TEST(test_max_size) + { + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + CHECK(pool.max_size() == 4U); + } + + //************************************************************************* + TEST(test_size) + { + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + CHECK_EQUAL(0U, pool.size()); + + Test_Data* p; + + p = pool.allocate(); + CHECK_EQUAL(1U, pool.size()); + + p = pool.allocate(); + CHECK_EQUAL(2U, pool.size()); + + p = pool.allocate(); + CHECK_EQUAL(3U, pool.size()); + + p = pool.allocate(); + CHECK_EQUAL(4U, pool.size()); + } + + //************************************************************************* + TEST(test_empty_full) + { + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + CHECK(pool.empty()); + CHECK(!pool.full()); + + Test_Data* p; + + p = pool.allocate(); + CHECK(!pool.empty()); + CHECK(!pool.full()); + + p = pool.allocate(); + CHECK(!pool.empty()); + CHECK(!pool.full()); + + p = pool.allocate(); + CHECK(!pool.empty()); + CHECK(!pool.full()); + + p = pool.allocate(); + CHECK(!pool.empty()); + CHECK(pool.full()); + } + + //************************************************************************* + TEST(test_is_in_pool) + { + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + Test_Data not_in_pool; + + Test_Data* p1 = pool.allocate(); + + CHECK(pool.is_in_pool(p1)); + CHECK(!pool.is_in_pool(¬_in_pool)); + } + + //************************************************************************* + TEST(test_type_error) + { + struct Test + { + uint64_t a; + uint64_t b; + }; + + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + etl::ipool& ip = pool; + + CHECK_THROW(ip.allocate(), etl::pool_element_size); + } + + //************************************************************************* + TEST(test_create_destroy) + { + const size_t SIZE = 4; + + auto* buffer0 = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool0(buffer0, SIZE); + + auto* buffer1 = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool1(buffer1, SIZE); + + auto* buffer2 = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool2(buffer2, SIZE); + + auto* buffer3 = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool3(buffer3, SIZE); + + auto* buffer4 = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool4(buffer4, SIZE); + + + D0* p0 = pool0.create(); + D1* p1 = pool1.create("1"); + D2* p2 = pool2.create("1", "2"); + D3* p3 = pool3.create("1", "2", "3"); + D4* p4 = pool4.create("1", "2", "3", "4"); + + CHECK_EQUAL(pool0.max_size() - 1, pool0.available()); + CHECK_EQUAL(1U, pool0.size()); + + CHECK_EQUAL(pool1.max_size() - 1, pool1.available()); + CHECK_EQUAL(1U, pool1.size()); + + CHECK_EQUAL(pool2.max_size() - 1, pool2.available()); + CHECK_EQUAL(1U, pool2.size()); + + CHECK_EQUAL(pool3.max_size() - 1, pool3.available()); + CHECK_EQUAL(1U, pool3.size()); + + CHECK_EQUAL(pool4.max_size() - 1, pool4.available()); + CHECK_EQUAL(1U, pool4.size()); + + CHECK_EQUAL(D0(), *p0); + CHECK_EQUAL(D1("1"), *p1); + CHECK_EQUAL(D2("1", "2"), *p2); + CHECK_EQUAL(D3("1", "2", "3"), *p3); + CHECK_EQUAL(D4("1", "2", "3", "4"), *p4); + + pool0.destroy(p0); + pool1.destroy(p1); + pool2.destroy(p2); + pool3.destroy(p3); + pool4.destroy(p4); + + CHECK_EQUAL(pool0.max_size(), pool0.available()); + CHECK_EQUAL(0U, pool0.size()); + + CHECK_EQUAL(pool1.max_size(), pool1.available()); + CHECK_EQUAL(0U, pool1.size()); + + CHECK_EQUAL(pool2.max_size(), pool2.available()); + CHECK_EQUAL(0U, pool2.size()); + + CHECK_EQUAL(pool3.max_size(), pool3.available()); + CHECK_EQUAL(0U, pool3.size()); + + CHECK_EQUAL(pool4.max_size(), pool4.available()); + CHECK_EQUAL(0U, pool4.size()); + } + + //************************************************************************* + TEST(test_allocate_release_non_class) + { + const size_t SIZE = 4; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext pool(buffer, SIZE); + + int* i = pool.allocate(); + pool.release(i); + } + + //************************************************************************* + TEST(test_issue_406_pool_of_c_array) + { + using elem_type = uint8_t[10]; + + const size_t SIZE = 3; + auto* buffer = new etl::pool_ext::element[SIZE]; + etl::pool_ext memPool(buffer, SIZE); + + CHECK_EQUAL(3, memPool.available()); + CHECK_EQUAL(0, memPool.size()); + + elem_type* memory = memPool.allocate(); + + CHECK_EQUAL(2, memPool.available()); + CHECK_EQUAL(1, memPool.size()); + + memPool.release(memory); + + CHECK_EQUAL(3, memPool.available()); + CHECK_EQUAL(0, memPool.size()); + } +} +} +#if defined(ETL_COMPILER_GCC) + #pragma GCC diagnostic pop +#endif diff --git a/test/test_variant_pool_external_buffer.cpp b/test/test_variant_pool_external_buffer.cpp new file mode 100644 index 00000000..740ee356 --- /dev/null +++ b/test/test_variant_pool_external_buffer.cpp @@ -0,0 +1,319 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/variant_pool.h" + +#include +#include + +namespace +{ + bool destructor; + + //*********************************** + struct Base + { + Base() + { + destructor = false; + } + + virtual ~Base() + { + } + + virtual void Set() = 0; + }; + + //*********************************** + struct Derived1 : public Base + { + int i; + + Derived1() + : i(0) + { + } + + ~Derived1() + { + destructor = true; + } + + void Set() + { + i = 1; + } + }; + + //*********************************** + struct Derived2 : public Base + { + double d; + + Derived2() + : d(0.0) + { + } + + ~Derived2() + { + destructor = true; + } + + void Set() + { + d = 1.2; + } + }; + + //*********************************** + struct Derived3 : public Base + { + std::string s; + + Derived3() + : s("constructed") + { + } + + Derived3(const char* p1) + : s("constructed") + { + s.append(p1); + } + + Derived3(const char* p1, const std::string& p2) + : s("constructed") + { + s.append(p1); + s.append(p2); + } + + Derived3(const char* p1, const std::string& p2, const char* p3) + : s("constructed") + { + s.append(p1); + s.append(p2); + s.append(p3); + } + + Derived3(const char* p1, const std::string& p2, const char* p3, const std::string& p4) + : s("constructed") + { + s.append(p1); + s.append(p2); + s.append(p3); + s.append(p4); + } + + ~Derived3() + { + destructor = true; + } + + void Set() + { + s = "set"; + } + }; + + //*********************************** + struct NonDerived + { + NonDerived() + : s("constructed") + { + } + + ~NonDerived() + { + destructor = true; + } + + void Set() + { + s = "set"; + } + + std::string s; + }; + + const size_t SIZE = 5; + + // Notice that the type declaration order is not important. + typedef etl::variant_pool_ext Factory; + + SUITE(test_variant_pool_external_buffer) + { + //************************************************************************* + TEST(test_sizes) + { + auto* buffer = new Factory::element[SIZE]; + Factory variant_pool(buffer, SIZE); + + size_t ms = variant_pool.max_size(); + CHECK_EQUAL(SIZE, ms); + CHECK_EQUAL(SIZE, variant_pool.available()); + CHECK_EQUAL(0U, variant_pool.size()); + CHECK(variant_pool.empty()); + CHECK(!variant_pool.full()); + + variant_pool.create(); + CHECK_EQUAL(SIZE - 1U, variant_pool.available()); + CHECK_EQUAL(1U, variant_pool.size()); + CHECK(!variant_pool.empty()); + CHECK(!variant_pool.full()); + + variant_pool.create(); + variant_pool.create(); + variant_pool.create(); + variant_pool.create(); + CHECK_EQUAL(0U, variant_pool.available()); + CHECK_EQUAL(SIZE, variant_pool.size()); + CHECK(!variant_pool.empty()); + CHECK(variant_pool.full()); + + CHECK_THROW(variant_pool.create(), etl::pool_no_allocation); + } + + //************************************************************************* + TEST(test_create_release) + { + auto* buffer = new Factory::element[SIZE]; + Factory variant_pool(buffer, SIZE); + + Base* p; + + // Derived 1 + p = variant_pool.create(); + Derived1* pd1 = static_cast(p); + CHECK_EQUAL(0, pd1->i); + p->Set(); + CHECK_EQUAL(1, pd1->i); + variant_pool.destroy(p); + CHECK(destructor); + + // Derived 2 + destructor = false; + p = variant_pool.create(); + Derived2* pd2 = static_cast(p); + CHECK_EQUAL(0.0, pd2->d); + p->Set(); + CHECK_EQUAL(1.2, pd2->d); + variant_pool.destroy(p); + CHECK(destructor); + + // Derived 3 + destructor = false; + p = variant_pool.create(); + Derived3* pd3 = static_cast(p); + CHECK_EQUAL("constructed", pd3->s); + p->Set(); + CHECK_EQUAL("set", pd3->s); + variant_pool.destroy(p); + CHECK(destructor); + + // Non Derived + destructor = false; + NonDerived* pnd = variant_pool.create(); + CHECK_EQUAL("constructed", pnd->s); + pnd->Set(); + CHECK_EQUAL("set", pnd->s); + variant_pool.destroy(pnd); + CHECK(destructor); + + // Integral + int* pi = variant_pool.create(); + variant_pool.destroy(pi); + } + + //************************************************************************* + TEST(test_create_release_const) + { + auto* buffer = new Factory::element[SIZE]; + Factory variant_pool(buffer, SIZE); + + const Derived1& d = *variant_pool.create(); + + CHECK_EQUAL(0, d.i); + variant_pool.destroy(&d); + CHECK(destructor); + + } + + //************************************************************************* + TEST(test_create_emplace) + { + auto* buffer = new Factory::element[SIZE]; + Factory variant_pool(buffer, SIZE); + + Base* p; + Derived3* pd3; + + p = variant_pool.create("1"); + pd3 = static_cast(p); + CHECK_EQUAL("constructed1", pd3->s); + variant_pool.destroy(p); + + p = variant_pool.create("1", "2"); + pd3 = static_cast(p); + CHECK_EQUAL("constructed12", pd3->s); + variant_pool.destroy(p); + + p = variant_pool.create("1", "2", "3"); + pd3 = static_cast(p); + CHECK_EQUAL("constructed123", pd3->s); + variant_pool.destroy(p); + + p = variant_pool.create("1", "2", "3", "4"); + pd3 = static_cast(p); + CHECK_EQUAL("constructed1234", pd3->s); + variant_pool.destroy(p); + } + + //************************************************************************* + TEST(test_did_not_create) + { + auto* buffer1 = new Factory::element[SIZE]; + Factory variant_pool1(buffer1, SIZE); + auto* buffer2 = new Factory::element[SIZE]; + Factory variant_pool2(buffer2, SIZE); + + Base* p; + + p = variant_pool1.create(); + CHECK_NO_THROW(variant_pool1.destroy(p)); + + p = variant_pool2.create(); + CHECK_THROW(variant_pool1.destroy(p), etl::pool_object_not_in_pool); + } + }; +} From aaa00ca61016f4ced7bbdf0e363eccbd30a0e4f3 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 3 Oct 2021 11:50:40 +0100 Subject: [PATCH 2/2] Added pool_ext & generic_pool_ext --- .gitignore | 1 + include/etl/generic_pool.h | 26 ++++++++++++++++++-------- include/etl/pool.h | 23 +++++++++++++++++++---- test/vs2019/etl.vcxproj | 1 + test/vs2019/etl.vcxproj.filters | 3 +++ 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 72c021e0..da3224a9 100644 --- a/.gitignore +++ b/.gitignore @@ -325,3 +325,4 @@ test/vs2019/Test2 test/vs2019/Debug MSVC - Force C++03 test/vs2019/Debug LLVM - No STL test/vs2019/Debug - No STL +test/build-make diff --git a/include/etl/generic_pool.h b/include/etl/generic_pool.h index bda98e74..cd4a6f97 100644 --- a/include/etl/generic_pool.h +++ b/include/etl/generic_pool.h @@ -190,15 +190,22 @@ namespace etl static ETL_CONSTANT uint32_t Element_Size = sizeof(Element); // Should not be copied. - generic_pool(const generic_pool&); - generic_pool& operator =(const generic_pool&); + generic_pool(const generic_pool&) ETL_DELETE; + generic_pool& operator =(const generic_pool&) ETL_DELETE; }; + //************************************************************************* + /// A templated abstract pool implementation that uses a fixed size pool. + /// The storage for the pool is supplied externally. + ///\ingroup pool + //************************************************************************* template - class generic_pool_ext : public etl::ipool { + class generic_pool_ext : public etl::ipool + { private: // The pool element. - union element_internal { + union element_internal + { char* next; ///< Pointer to the next free element. char value[VTypeSize]; ///< Storage for value type. typename etl::type_with_alignment::type dummy; ///< Dummy item to get correct alignment. @@ -210,12 +217,15 @@ namespace etl static ETL_CONSTANT size_t ALIGNMENT = VAlignment; static ETL_CONSTANT size_t TYPE_SIZE = VTypeSize; - using element = typename etl::aligned_storage::value>::type; + typedef typename etl::aligned_storage::value>::type element; //************************************************************************* /// Constructor //************************************************************************* - generic_pool_ext(element* buffer, size_t size) : etl::ipool(reinterpret_cast(&buffer[0]), ELEMENT_INTERNAL_SIZE, size) {} + generic_pool_ext(element* buffer, size_t size) + : etl::ipool(reinterpret_cast(&buffer[0]), ELEMENT_INTERNAL_SIZE, size) + { + } //************************************************************************* /// Allocate an object from the pool. @@ -325,8 +335,8 @@ namespace etl private: // Should not be copied. - generic_pool_ext(const generic_pool_ext&); - generic_pool_ext& operator=(const generic_pool_ext&); + generic_pool_ext(const generic_pool_ext&) ETL_DELETE; + generic_pool_ext& operator=(const generic_pool_ext&) ETL_DELETE; }; } diff --git a/include/etl/pool.h b/include/etl/pool.h index a8a867a0..8d5f5a1a 100644 --- a/include/etl/pool.h +++ b/include/etl/pool.h @@ -179,8 +179,14 @@ namespace etl pool& operator =(const pool&) ETL_DELETE; }; + //************************************************************************* + /// A templated pool implementation that uses a fixed size pool. + /// The storage for the pool is supplied externally. + ///\ingroup pool + //************************************************************************* template - class pool_ext : public etl::generic_pool_ext::value> { + class pool_ext : public etl::generic_pool_ext::value> + { private: typedef etl::generic_pool_ext::value> base_t; @@ -191,7 +197,10 @@ namespace etl //************************************************************************* /// Constructor //************************************************************************* - pool_ext(typename base_t::element* buffer, size_t size) : base_t(buffer, size) {} + pool_ext(typename base_t::element* buffer, size_t size) + : base_t(buffer, size) + { + } //************************************************************************* /// Allocate an object from the pool. @@ -200,7 +209,10 @@ namespace etl /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. /// Static asserts if the specified type is too large for the pool. //************************************************************************* - T* allocate() { return base_t::template allocate(); } + T* allocate() + { + return base_t::template allocate(); + } #if ETL_CPP11_NOT_SUPPORTED || ETL_POOL_CPP03_CODE || ETL_USING_STLPORT //************************************************************************* @@ -208,7 +220,10 @@ namespace etl /// If asserts or exceptions are enabled and there are no more free items an /// etl::pool_no_allocation if thrown, otherwise a null pointer is returned. //************************************************************************* - T* create() { return base_t::template create(); } + T* create() + { + return base_t::template create(); + } //************************************************************************* /// Allocate storage for an object from the pool and create with 1 parameter. diff --git a/test/vs2019/etl.vcxproj b/test/vs2019/etl.vcxproj index 89e16065..fa1202df 100644 --- a/test/vs2019/etl.vcxproj +++ b/test/vs2019/etl.vcxproj @@ -8276,6 +8276,7 @@ + diff --git a/test/vs2019/etl.vcxproj.filters b/test/vs2019/etl.vcxproj.filters index 120b521b..4acb0b3f 100644 --- a/test/vs2019/etl.vcxproj.filters +++ b/test/vs2019/etl.vcxproj.filters @@ -3017,6 +3017,9 @@ Source Files + + Source Files +