From c477d481336e7de28d462efd68c6d581313bfac5 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sun, 13 Nov 2016 16:22:21 +0000 Subject: [PATCH] New intrusive stack --- src/intrusive_stack.h | 120 +++++++++++++--- test/test_intrusive_stack.cpp | 248 ++++++++++++++++++++++++++++++++++ 2 files changed, 349 insertions(+), 19 deletions(-) create mode 100644 test/test_intrusive_stack.cpp diff --git a/src/intrusive_stack.h b/src/intrusive_stack.h index 025dac89..d7c43c24 100644 --- a/src/intrusive_stack.h +++ b/src/intrusive_stack.h @@ -34,11 +34,40 @@ SOFTWARE. #include #include "type_traits.h" -#include "parameter_type.h" #include "error_handler.h" +#include "intrusive_links.h" +#include "private/counter_type.h" namespace etl { + //*************************************************************************** + /// Exception base for intrusive stack + ///\ingroup intrusive_stack + //*************************************************************************** + class intrusive_stack_exception : public etl::exception + { + public: + + intrusive_stack_exception(string_type what, string_type file_name, numeric_type line_number) + : exception(what, file_name, line_number) + { + } + }; + + //*************************************************************************** + /// intrusive_stack empty exception. + ///\ingroup intrusive_stack + //*************************************************************************** + class intrusive_stack_empty : public intrusive_stack_exception + { + public: + + intrusive_stack_empty(string_type file_name, numeric_type line_number) + : intrusive_stack_exception(ETL_ERROR_TEXT("intrusive_stack:empty", ETL_FILE"A"), file_name, line_number) + { + } + }; + //*************************************************************************** ///\ingroup stack /// An intrusive stack. Stores elements derived from etl::forward_link @@ -46,7 +75,7 @@ namespace etl /// \tparam TValue The type of value that the stack holds. /// \tparam TLink The link type that the value is derived from. //*************************************************************************** - template > + template class intrusive_stack { public: @@ -62,15 +91,20 @@ namespace etl typedef const value_type& const_reference; typedef size_t size_type; - public: + enum + { + // The count option is based on the type of link. + COUNT_OPTION = ((TLink::OPTION == etl::link_option::AUTO) || (TLink::OPTION == etl::link_option::CHECKED)) ? + etl::count_option::SLOW_COUNT : + etl::count_option::FAST_COUNT + }; //************************************************************************* /// Constructor //************************************************************************* intrusive_stack() - : p_top(&base) + : p_top(nullptr) { - } //************************************************************************* @@ -80,7 +114,7 @@ namespace etl //************************************************************************* reference top() { - return *p_top; + return *static_cast(p_top); } //************************************************************************* @@ -89,8 +123,29 @@ namespace etl //************************************************************************* void push(link_type& value) { - etl::link(p_top, value); + if (p_top != nullptr) + { + etl::link(value, p_top); + } + p_top = &value; + + ++current_size; + } + + //************************************************************************* + /// Removes the oldest item from the top of the stack. + /// Undefined behaviour if the stack is already empty. + //************************************************************************* + void pop() + { +#if defined(ETL_CHECK_PUSH_POP) + ETL_ASSERT(!empty(), ETL_ERROR(intrusive_stack_empty)); +#endif + link_type* p_next = p_top->etl_next; + p_top->clear(); + p_top = p_next; + --current_size; } //************************************************************************* @@ -107,31 +162,58 @@ namespace etl //************************************************************************* void clear() { - unlink(base, *p_top); + while (!empty()) + { + pop(); + } + current_size = 0; } //************************************************************************* - /// Removes the oldest item from the top of the stack. - /// Does nothing if the stack is already empty. + /// Checks if the stack is in the empty state. //************************************************************************* - void pop() + bool empty() const { -#if defined(ETL_CHECK_PUSH_POP) - ETL_ASSERT(p_top != &base, ETL_ERROR(intrusive_stack_empty)); -#endif - link_type* p_previous = p_top->etl_previous; - unlink(p_top); - p_top = p_previous; + return p_top == nullptr; + } + + //************************************************************************* + /// Returns the number of elements. + //************************************************************************* + size_t size() const + { + if (COUNT_OPTION == etl::count_option::SLOW_COUNT) + { + size_t count = 0; + + if (p_top != nullptr) + { + link_type* p_link = p_top; + + while (p_link != nullptr) + { + ++count; + p_link = p_link->etl_next; + } + } + + return count; + } + else + { + return current_size.get_count(); + } } private: // Disable copy construction and assignment. intrusive_stack(const intrusive_stack&); - intrusive_stack& operator = (const intrusive_stack& rhs) + intrusive_stack& operator = (const intrusive_stack& rhs); - link_type base; // The base of the stack. link_type* p_top; // The current top of the stack. + + etl::counter_type current_size; ///< Counts the number of elements in the list. }; } diff --git a/test/test_intrusive_stack.cpp b/test/test_intrusive_stack.cpp new file mode 100644 index 00000000..cec894da --- /dev/null +++ b/test/test_intrusive_stack.cpp @@ -0,0 +1,248 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2016 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 + +#include "../src/intrusive_stack.h" +#include "../src/intrusive_links.h" + +#include + +namespace +{ + enum + { + DEFAULT, + AUTO, + CHECKED + }; + + etl::forward_link link; + + typedef etl::forward_link default_link; + typedef etl::forward_link checked_link; + + struct Data : public default_link, public checked_link + { + Data(int i) + : i(i) + { + + } + + int i; + }; + + bool operator ==(const Data& lhs, const Data& rhs) + { + return lhs.i == rhs.i; + } + + std::ostream& operator << (std::ostream& os, const Data& data) + { + os << data.i; + return os; + } + + std::vector data = + { + Data(1), Data(2), Data(3), Data(4), Data(5), Data(6), Data(7), Data(8) + }; + + SUITE(test_intrusive_stack) + { + //************************************************************************* + TEST(test_constructor) + { + etl::intrusive_stack stackD; + etl::intrusive_stack stackC; + + CHECK(stackD.empty()); + CHECK(stackC.empty()); + + CHECK_EQUAL(0, stackD.size()); + CHECK_EQUAL(0, stackC.size()); + } + + //************************************************************************* + TEST(test_empty) + { + etl::intrusive_stack stackD; + etl::intrusive_stack stackC; + + Data data1(1); + Data data2(2); + + CHECK(stackD.empty()); + CHECK(stackC.empty()); + + stackD.push(data1); + stackC.push(data2); + + CHECK(!stackD.empty()); + CHECK(!stackC.empty()); + + data1.checked_link::clear(); + data2.checked_link::clear(); + } + + //************************************************************************* + TEST(test_size) + { + etl::intrusive_stack stackD; + etl::intrusive_stack stackC; + + Data data1(1); + Data data2(2); + Data data3(3); + + stackD.push(data1); + stackD.push(data2); + stackD.push(data3); + + stackC.push(data1); + stackC.push(data2); + + CHECK_EQUAL(3U, stackD.size()); + CHECK_EQUAL(2U, stackC.size()); + + data1.checked_link::clear(); + data2.checked_link::clear(); + } + + //************************************************************************* + TEST(test_clear) + { + etl::intrusive_stack stackD; + etl::intrusive_stack stackC; + + Data data1(1); + Data data2(2); + Data data3(3); + + stackD.push(data1); + stackD.push(data2); + stackD.push(data3); + + stackC.push(data1); + stackC.push(data2); + + stackD.clear(); + stackC.clear(); + + CHECK(stackD.empty()); + CHECK(stackC.empty()); + } + + //************************************************************************* + TEST(test_push) + { + etl::intrusive_stack stackD; + etl::intrusive_stack stackC; + + Data data1(1); + Data data2(2); + Data data3(3); + + stackD.push(data1); + CHECK_EQUAL(stackD.top(), data1); + + stackD.push(data2); + CHECK_EQUAL(stackD.top(), data2); + + stackD.push(data3); + CHECK_EQUAL(stackD.top(), data3); + + stackC.push(data1); + CHECK_EQUAL(stackC.top(), data1); + + stackC.push(data2); + CHECK_EQUAL(stackC.top(), data2); + + data1.checked_link::clear(); + data2.checked_link::clear(); + } + + + //************************************************************************* + TEST(test_pop) + { + etl::intrusive_stack stackD; + etl::intrusive_stack stackC; + + Data data1(1); + Data data2(2); + Data data3(3); + + stackD.push(data1); + stackD.push(data2); + stackD.push(data3); + + stackC.push(data1); + stackC.push(data2); + + CHECK_EQUAL(stackD.top(), data3); + stackD.pop(); + CHECK_EQUAL(stackD.top(), data2); + stackD.pop(); + CHECK_EQUAL(stackD.top(), data1); + stackD.pop(); + CHECK(stackD.empty()); + + CHECK_EQUAL(stackC.top(), data2); + stackC.pop(); + CHECK_EQUAL(stackC.top(), data1); + stackC.pop(); + CHECK(stackC.empty()); + + data1.checked_link::clear(); + data2.checked_link::clear(); + } + + //************************************************************************* + TEST(test_top_const) + { + etl::intrusive_stack stackD; + const etl::intrusive_stack& stackDR = stackD; + + Data data1(1); + Data data2(2); + Data data3(3); + + stackD.push(data1); + stackD.push(data2); + stackD.push(data3); + + CHECK_EQUAL(stackD.top(), stackDR.top()); + stackD.pop(); + CHECK_EQUAL(stackD.top(), stackDR.top()); + stackD.pop(); + CHECK_EQUAL(stackD.top(), stackDR.top()); + } + }; +}