diff --git a/Fw/CMakeLists.txt b/Fw/CMakeLists.txt index cd065719bc1..cd33a106ce8 100644 --- a/Fw/CMakeLists.txt +++ b/Fw/CMakeLists.txt @@ -4,6 +4,7 @@ set(FPRIME_FRAMEWORK_MODULES Fw_Prm Fw_Cmd Fw_Log Fw_Tlm Fw_Fpy Fw_Com Fw_Time F add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Buffer/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Cmd/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Com/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/DataStructures/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Dp/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Fpy/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Interfaces/") diff --git a/Fw/DataStructures/Array.hpp b/Fw/DataStructures/Array.hpp new file mode 100644 index 00000000000..a1fc99a2150 --- /dev/null +++ b/Fw/DataStructures/Array.hpp @@ -0,0 +1,160 @@ +// ====================================================================== +// \file Array.hpp +// \author bocchino +// \brief A statically-sized, bounds checked array +// ====================================================================== + +#ifndef Fw_Array_HPP +#define Fw_Array_HPP + +#include + +#include "Fw/DataStructures/ExternalArray.hpp" +#include "Fw/FPrimeBasicTypes.hpp" +#include "Fw/Types/Assert.hpp" + +namespace Fw { + +template +class Array final { + // ---------------------------------------------------------------------- + // Static assertions + // ---------------------------------------------------------------------- + + static_assert(std::is_default_constructible::value, "T must be default constructible"); + static_assert(S > 0, "array size must be greater than zero"); + + public: + // ---------------------------------------------------------------------- + // Types + // ---------------------------------------------------------------------- + + //! The type of the elements array + using Elements = T[S]; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + Array() = default; + + //! Initializer list constructor + Array(const std::initializer_list& il //!< The initializer list + ) { + *this = il; + } + + //! Primitive array constructor + Array(const Elements& elements //!< The array elements + ) { + *this = elements; + } + + //! Single-element constructor + explicit Array(const T& element //!< The element + ) { + *this = element; + } + + //! Copy constructor + Array(const Array& a //!< The array to copy + ) { + *this = a; + } + + //! Destructor + ~Array() = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Subscript operator + //! \return The element at index i + T& operator[](FwSizeType i //!< The subscript index + ) { + FW_ASSERT(i < S, static_cast(i)); + return this->m_elements[i]; + } + + //! Const subscript operator + //! \return The element at index i + const T& operator[](FwSizeType i //!< The subscript index + ) const { + FW_ASSERT(i < S, static_cast(i)); + return this->m_elements[i]; + } + + //! operator= (initializer list) + //! \return *this + Array& operator=(const std::initializer_list& il //!< The initializer list + ) { + // Since we are required to use C++11, this has to be a runtime check + // In C++14, it can be a static check + FW_ASSERT(il.size() == S, static_cast(il.size()), static_cast(S)); + FwSizeType i = 0; + for (const auto& e : il) { + FW_ASSERT(i < S, static_cast(i), static_cast(S)); + this->m_elements[i] = e; + i++; + } + return *this; + } + + //! operator= (primitive array) + //! \return *this + Array& operator=(const Elements& elements //!< The array elements + ) { + for (FwSizeType i = 0; i < S; i++) { + this->m_elements[i] = elements[i]; + } + return *this; + } + + //! operator= (single element) + Array& operator=(const T& element //!< The element + ) { + for (FwSizeType i = 0; i < S; i++) { + this->m_elements[i] = element; + } + return *this; + } + + //! operator= (copy assignment) + //! \return *this + Array& operator=(const Array& a) { + if (&a != this) { + for (FwSizeType i = 0; i < S; i++) { + this->m_elements[i] = a.m_elements[i]; + } + } + return *this; + } + + //! Get a mutable reference to the elements + //! \return A mutable reference to the elements + Elements& getElements() { return this->m_elements; } + + //! Get a const reference to the elements + //! \return A const reference to the elements + const Elements& getElements() const { return this->m_elements; } + + //! Convert this array to an ExternalArray + // \return The ExternalArray + ExternalArray asExternalArray() { return ExternalArray(this->m_elements, S); } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The array elements + Elements m_elements = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/ArrayMap.hpp b/Fw/DataStructures/ArrayMap.hpp new file mode 100644 index 00000000000..e5f1c9addc3 --- /dev/null +++ b/Fw/DataStructures/ArrayMap.hpp @@ -0,0 +1,125 @@ +// ====================================================================== +// \file ArrayMap.hpp +// \author bocchino +// \brief An array-based map with internal storage +// ====================================================================== + +#ifndef Fw_ArrayMap_HPP +#define Fw_ArrayMap_HPP + +#include "Fw/DataStructures/ExternalArrayMap.hpp" + +namespace Fw { + +template +class ArrayMap final : public MapBase { + // ---------------------------------------------------------------------- + // Static assertions + // ---------------------------------------------------------------------- + + static_assert(C > 0, "capacity must be greater than zero"); + + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class ArrayMapTester; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a const iterator + using ConstIterator = MapConstIterator; + + //! The type of an implementation entry + using Entry = SetOrMapImplEntry; + + //! The type of the implementation entries + using Entries = Entry[C]; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ArrayMap() : MapBase(), m_extMap(m_entries, C) {} + + //! Copy constructor + ArrayMap(const ArrayMap& map) : MapBase(), m_extMap(m_entries, C) { *this = map; } + + //! Destructor + ~ArrayMap() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + ArrayMap& operator=(const ArrayMap& map) { + this->m_extMap.copyDataFrom(map); + return *this; + } + + //! Get the begin iterator + //! \return The iterator + ConstIterator begin() const override { return this->m_extMap.begin(); } + + //! Clear the map + void clear() override { this->m_extMap.clear(); } + + //! Get the end iterator + //! \return The iterator + ConstIterator end() const override { return this->m_extMap.end(); } + + //! Find a value associated with a key in the map + //! \return SUCCESS if the item was found + Success find(const K& key, //!< The key + V& value //!< The value + ) const override { + return this->m_extMap.find(key, value); + } + + //! Get the capacity of the map (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_extMap.getCapacity(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const override { return this->m_extMap.getSize(); } + + //! Insert a (key, value) pair in the map + //! \return SUCCESS if there is room in the map + Success insert(const K& key, //!< The key + const V& value //!< The value + ) override { + return this->m_extMap.insert(key, value); + } + + //! Remove a (key, value) pair from the map + //! \return SUCCESS if the key was there + Success remove(const K& key, //!< The key + V& value //!< The value + ) override { + return this->m_extMap.remove(key, value); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The external map implementation + ExternalArrayMap m_extMap = {}; + + //! The array providing the backing memory for m_extMap + Entries m_entries = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/ArraySet.hpp b/Fw/DataStructures/ArraySet.hpp new file mode 100644 index 00000000000..0a2afc7a491 --- /dev/null +++ b/Fw/DataStructures/ArraySet.hpp @@ -0,0 +1,122 @@ +// ====================================================================== +// \file ArraySet.hpp +// \author bocchino +// \brief An array-based set with internal storage +// ====================================================================== + +#ifndef Fw_ArraySet_HPP +#define Fw_ArraySet_HPP + +#include "Fw/DataStructures/ExternalArraySet.hpp" + +namespace Fw { + +template +class ArraySet final : public SetBase { + // ---------------------------------------------------------------------- + // Static assertions + // ---------------------------------------------------------------------- + + static_assert(C > 0, "capacity must be greater than zero"); + + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class ArraySetTester; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a const iterator + using ConstIterator = SetConstIterator; + + //! The type of an implementation entry + using Entry = SetOrMapImplEntry; + + //! The type of the implementation entries + using Entries = Entry[C]; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ArraySet() : SetBase(), m_extSet(m_entries, C) {} + + //! Copy constructor + ArraySet(const ArraySet& set) : SetBase(), m_extSet(m_entries, C) { *this = set; } + + //! Destructor + ~ArraySet() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + ArraySet& operator=(const ArraySet& set) { + this->m_extSet.copyDataFrom(set); + return *this; + } + + //! Get the begin iterator + //! \return The iterator + ConstIterator begin() const override { return this->m_extSet.begin(); } + + //! Clear the set + void clear() override { this->m_extSet.clear(); } + + //! Get the end iterator + //! \return The iterator + ConstIterator end() const override { return this->m_extSet.end(); } + + //! Find an element in the set + //! \return SUCCESS if the element was found + Success find(const T& element //!< The element + ) const override { + return this->m_extSet.find(element); + } + + //! Get the capacity of the set (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_extSet.getCapacity(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const override { return this->m_extSet.getSize(); } + + //! Insert an element in the set + //! \return SUCCESS if there is room in the set + Success insert(const T& element //!< The element + ) override { + return this->m_extSet.insert(element); + } + + //! Remove an element from the set + //! \return SUCCESS if the key was there + Success remove(const T& element //!< The element + ) override { + return this->m_extSet.remove(element); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The external set implementation + ExternalArraySet m_extSet = {}; + + //! The array providing the backing memory for m_extSet + Entries m_entries = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/ArraySetOrMapImpl.hpp b/Fw/DataStructures/ArraySetOrMapImpl.hpp new file mode 100644 index 00000000000..2c42f937737 --- /dev/null +++ b/Fw/DataStructures/ArraySetOrMapImpl.hpp @@ -0,0 +1,289 @@ +// ====================================================================== +// \title ArraySetOrMapImpl +// \author bocchino +// \brief An array-based implementation of a set or map +// ====================================================================== + +#ifndef Fw_ArraySetOrMapImpl_HPP +#define Fw_ArraySetOrMapImpl_HPP + +#include "Fw/DataStructures/ExternalArray.hpp" +#include "Fw/DataStructures/SetOrMapImplConstIterator.hpp" +#include "Fw/DataStructures/SetOrMapImplEntry.hpp" +#include "Fw/Types/Assert.hpp" +#include "Fw/Types/SuccessEnumAc.hpp" + +namespace Fw { + +template +class ArraySetOrMapImpl final { + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class ArraySetOrMapImplTester; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of an entry in the set or map + using Entry = SetOrMapImplEntry; + + //! Const iterator + class ConstIterator final : public SetOrMapImplConstIterator { + public: + using ImplKind = typename SetOrMapImplConstIterator::ImplKind; + + public: + //! Default constructor + ConstIterator() {} + + //! Constructor providing the implementation + ConstIterator(const ArraySetOrMapImpl& impl) : SetOrMapImplConstIterator(), m_impl(&impl) {} + + //! Copy constructor + ConstIterator(const ConstIterator& it) + : SetOrMapImplConstIterator(), m_impl(it.m_impl), m_index(it.m_index) {} + + //! Destructor + ~ConstIterator() override = default; + + public: + //! Copy assignment operator + ConstIterator& operator=(const ConstIterator& it) { + this->m_impl = it.m_impl; + this->m_index = it.m_index; + return *this; + } + + //! Equality comparison operator + bool compareEqual(const ConstIterator& it) const { + bool result = false; + if ((this->m_impl == nullptr) && (it.m_impl == nullptr)) { + result = true; + } else if (this->m_impl == it.m_impl) { + result |= (this->m_index == it.m_index); + result |= (!this->isInRange() and !it.isInRange()); + } + return result; + } + + //! Return the impl kind + //! \return The impl kind + ImplKind implKind() const override { return ImplKind::ARRAY; } + + //! Get the set or map impl entry pointed to by this iterator + //! \return The set or map impl entry + const Entry& getEntry() const override { + FW_ASSERT(this->m_impl != nullptr); + FW_ASSERT(this->isInRange(), static_cast(this->m_index), + static_cast(this->m_impl->m_size)); + return this->m_impl->m_entries[this->m_index]; + } + + //! Increment operator + void increment() override { + if (this->isInRange()) { + this->m_index++; + } + } + + //! Check whether the iterator is in range + bool isInRange() const override { + FW_ASSERT(this->m_impl != nullptr); + return this->m_index < this->m_impl->m_size; + } + + //! Set the iterator to the end value + void setToEnd() { + FW_ASSERT(this->m_impl != nullptr); + this->m_index = this->m_impl->m_size; + } + + private: + //! The implementation over which to iterate + const ArraySetOrMapImpl* m_impl = nullptr; + + //! The current iteration index + FwSizeType m_index = 0; + }; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ArraySetOrMapImpl() = default; + + //! Constructor providing typed backing storage. + //! entries must point to at least capacity elements of type Entry. + ArraySetOrMapImpl(Entry* entries, //!< The entries + FwSizeType capacity //!< The capacity + ) { + this->setStorage(entries, capacity); + } + + //! Constructor providing untyped backing storage. + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + ArraySetOrMapImpl(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->setStorage(data, capacity); + } + + //! Copy constructor + ArraySetOrMapImpl(const ArraySetOrMapImpl& impl) { *this = impl; } + + //! Destructor + ~ArraySetOrMapImpl() = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + ArraySetOrMapImpl& operator=(const ArraySetOrMapImpl& impl) { + if (&impl != this) { + m_entries = impl.m_entries; + m_size = impl.m_size; + } + return *this; + } + + //! Get the begin iterator + ConstIterator begin() const { return ConstIterator(*this); } + + //! Clear the set or map + void clear() { this->m_size = 0; } + + //! Get the end iterator + ConstIterator end() const { + auto it = begin(); + it.setToEnd(); + return it; + } + + //! Find a value associated with a key in the map or an element in a set + //! \return SUCCESS if the item was found + Success find(const KE& keyOrElement, //!< The key or element + VN& valueOrNil //!< The value or Nil + ) const { + auto status = Success::FAILURE; + for (FwSizeType i = 0; i < this->m_size; i++) { + const auto& e = this->m_entries[i]; + if (e.getKey() == keyOrElement) { + valueOrNil = e.getValue(); + status = Success::SUCCESS; + break; + } + } + return status; + } + + //! Get the capacity of the set or map (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const { return this->m_entries.getSize(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const { return this->m_size; } + + //! Insert an element in the set or a (key, value) pair in the map + //! \return SUCCESS if there is room in the set or map + Success insert(const KE& keyOrElement, //!< The key or element + const VN& valueOrNil //!< The value or Nil + ) { + auto status = Success::FAILURE; + for (FwSizeType i = 0; i < this->m_size; i++) { + auto& e = this->m_entries[i]; + if (e.getKey() == keyOrElement) { + e.setValueOrNil(valueOrNil); + status = Success::SUCCESS; + break; + } + } + if ((status == Success::FAILURE) && (this->m_size < this->getCapacity())) { + this->m_entries[this->m_size] = Entry(keyOrElement, valueOrNil); + this->m_size++; + status = Success::SUCCESS; + } + return status; + } + + //! Remove an element from the set or a (key, value) pair from the map + //! \return SUCCESS if the key or element was there + Success remove(const KE& keyOrElement, //!< The key or element + VN& valueOrNil //!< The value or Nil + ) { + auto status = Success::FAILURE; + for (FwSizeType i = 0; i < this->m_size; i++) { + if (this->m_entries[i].getKey() == keyOrElement) { + valueOrNil = this->m_entries[i].getValue(); + if (i < this->m_size - 1) { + this->m_entries[i] = this->m_entries[this->m_size - 1]; + } + this->m_size--; + status = Success::SUCCESS; + break; + } + } + return status; + } + + //! Set the backing storage (typed data) + //! entries must point to at least capacity elements of type Entry. + void setStorage(Entry* entries, //!< The entries + FwSizeType capacity //!< The capacity + ) { + this->m_entries.setStorage(entries, capacity); + this->clear(); + } + + //! Set the backing storage (untyped data) + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + void setStorage(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->m_entries.setStorage(data, capacity); + this->clear(); + } + + public: + // ---------------------------------------------------------------------- + // Public static functions + // ---------------------------------------------------------------------- + + //! Get the alignment of the storage for an ArraySetOrMapImpl + //! \return The alignment + static constexpr U8 getByteArrayAlignment() { return ExternalArray::getByteArrayAlignment(); } + + //! Get the size of the storage for an ExternalArray of the specified capacity, + //! as a byte array + //! \return The byte array size + static constexpr FwSizeType getByteArraySize(FwSizeType capacity //!< The capacity + ) { + return ExternalArray::getByteArraySize(capacity); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The array for storing the set or map entries + ExternalArray m_entries = {}; + + //! The number of entries in the set or map + FwSizeType m_size = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/CMakeLists.txt b/Fw/DataStructures/CMakeLists.txt new file mode 100644 index 00000000000..2c408d1ba90 --- /dev/null +++ b/Fw/DataStructures/CMakeLists.txt @@ -0,0 +1,37 @@ +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/DataStructures.cpp" +) + +register_fprime_module() + +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ArrayMapTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ArraySetOrMapImplTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ArraySetTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ArrayTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/CircularIndexTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/DataStructuresTestMain.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalArrayMapTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalArraySetTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalArrayTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalFifoQueueTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalStackTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/FifoQueueTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/ArraySetOrMapImplTestRules.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/ArraySetOrMapImplTestScenarios.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/FifoQueueTestRules.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/FifoQueueTestScenarios.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/MapTestRules.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/MapTestScenarios.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/SetTestRules.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/SetTestScenarios.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/StackTestRules.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/StackTestScenarios.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/StackTest.cpp" +) + +set(UT_MOD_DEPS + STest +) + +register_fprime_ut() diff --git a/Fw/DataStructures/CircularIndex.hpp b/Fw/DataStructures/CircularIndex.hpp new file mode 100644 index 00000000000..83f93d24595 --- /dev/null +++ b/Fw/DataStructures/CircularIndex.hpp @@ -0,0 +1,114 @@ +// ====================================================================== +// \file CircularIndex.hpp +// \author bocchino +// \brief An index value that wraps around modulo an integer +// ====================================================================== + +#ifndef Fw_CircularIndex_HPP +#define Fw_CircularIndex_HPP + +#include "Fw/FPrimeBasicTypes.hpp" +#include "Fw/Types/Assert.hpp" + +namespace Fw { + +class CircularIndex final { + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + CircularIndex() : m_value(0), m_modulus(1) {} + + //! Constructor with specified members + explicit CircularIndex(FwSizeType modulus, //!< The modulus + FwSizeType value = 0 //!< The initial value + ) + : m_modulus(modulus) { + FW_ASSERT(modulus > 0); + this->setValue(value); + } + + //! Copy constructor + CircularIndex(const CircularIndex& ci) { *this = ci; } + + //! Destructor + ~CircularIndex() = default; + + public: + // ---------------------------------------------------------------------- + // Public functions + // ---------------------------------------------------------------------- + + //! operator= + CircularIndex& operator=(const CircularIndex& ci) { + if (this != &ci) { + this->m_value = ci.m_value; + this->m_modulus = ci.m_modulus; + } + return *this; + } + + //! Get the index value + //! \return The index value + FwSizeType getValue() const { + FW_ASSERT(this->m_value < this->m_modulus); + return this->m_value; + } + + //! Set the index value + void setValue(FwSizeType value //!< The index value + ) { + FW_ASSERT(this->m_modulus > 0); + this->m_value = value % this->m_modulus; + } + + //! Get the modulus + FwSizeType getModulus() const { + FW_ASSERT(this->m_value < this->m_modulus); + return this->m_modulus; + } + + //! Set the modulus + void setModulus(FwSizeType modulus //!< The modulus value + ) { + this->m_modulus = modulus; + this->setValue(this->m_value); + } + + //! Increment the index value + //! \return The new value + FwSizeType increment(FwSizeType amount = 1 //!< The amount by which to increment + ) { + FW_ASSERT(this->m_modulus > 0); + const FwSizeType offset = amount % m_modulus; + this->setValue(this->m_value + offset); + return this->m_value; + } + + //! Decrement the index value + //! \return The new value + FwSizeType decrement(FwSizeType amount = 1 //!< The amount by which to decrement + ) { + FW_ASSERT(this->m_modulus > 0); + const FwSizeType offset = amount % this->m_modulus; + this->setValue(this->m_value + this->m_modulus - offset); + return this->m_value; + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The index value + FwSizeType m_value; + + //! The modulus + FwSizeType m_modulus; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/DataStructures.cpp b/Fw/DataStructures/DataStructures.cpp new file mode 100644 index 00000000000..3215aa7acb1 --- /dev/null +++ b/Fw/DataStructures/DataStructures.cpp @@ -0,0 +1 @@ +// Empty file to keep the F Prime build system happy diff --git a/Fw/DataStructures/ExternalArray.hpp b/Fw/DataStructures/ExternalArray.hpp new file mode 100644 index 00000000000..89febae1b59 --- /dev/null +++ b/Fw/DataStructures/ExternalArray.hpp @@ -0,0 +1,175 @@ +// ====================================================================== +// \file ExternalArray.hpp +// \author bocchino +// \brief A bounds-checked array with external memory +// ====================================================================== + +#ifndef Fw_ExternalArray_HPP +#define Fw_ExternalArray_HPP + +#include +#include +#include + +#include "Fw/FPrimeBasicTypes.hpp" +#include "Fw/Types/Assert.hpp" +#include "Fw/Types/ByteArray.hpp" + +namespace Fw { + +template +class ExternalArray final { + // ---------------------------------------------------------------------- + // Static assertions + // ---------------------------------------------------------------------- + + static_assert(std::is_assignable::value, "T must be assignable to T&"); + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ExternalArray() {} + + //! Constructor providing typed backing storage. + //! elements must point to at least size elements of type T. + ExternalArray(T* elements, //!< The elements + FwSizeType size //!< The array size + ) + : m_elements(elements), m_size(size) {} + + //! Constructor providing untyped backing storage. + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(size) bytes. + ExternalArray(ByteArray data, //!< The data + FwSizeType size //!< The array size + ) { + this->setStorage(data, size); + } + + //! Copy constructor + ExternalArray(const ExternalArray& a) : m_elements(a.m_elements), m_size(a.m_size) {} + + //! Destructor + ~ExternalArray() = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Subscript operator + //! \return The element at index i + T& operator[](const FwSizeType i //!< The subscript index + ) { + FW_ASSERT(this->m_elements != nullptr); + FW_ASSERT(i < this->m_size, static_cast(i)); + return this->m_elements[i]; + } + + //! Const subscript operator + //! \return The element at index i + const T& operator[](const FwSizeType i //!< The subscript index + ) const { + FW_ASSERT(this->m_elements != nullptr); + FW_ASSERT(i < this->m_size, static_cast(i)); + return this->m_elements[i]; + } + + //! Copy assignment operator + //! \return *this + ExternalArray& operator=(const ExternalArray& a) { + if (&a != this) { + this->m_elements = a.m_elements; + this->m_size = a.m_size; + } + return *this; + } + + //! Copy the data from a + void copyDataFrom(const ExternalArray& a) { + const FwSizeType size = FW_MIN(this->m_size, a.m_size); + for (FwSizeType i = 0; i < size; i++) { + (*this)[i] = a[i]; + } + } + + //! Get a mutable pointer to the elements + //! \return A mutable pointer to the elements + T* getElements() { return this->m_elements; } + + //! Get a const pointer to the elements + //! \return A const pointer to the elements + const T* getElements() const { return this->m_elements; } + + //! Get the size + //! \return The size + FwSizeType getSize() const { return this->m_size; } + + //! Set the backing storage (typed data) + void setStorage(T* elements, //!< The array elements + FwSizeType size //!< The size + ) { + this->m_elements = elements; + this->m_size = size; + } + + //! Set the backing storage (untyped data) + //! Data must be aligned for T and must contain at least getByteArraySize(size) bytes. + void setStorage(ByteArray data, //!< The data + FwSizeType size //!< The array size + ) { + // Check that data.bytes is not null + FW_ASSERT(data.bytes != nullptr); + // Check that data.bytes is properly aligned + FW_ASSERT(reinterpret_cast(data.bytes) % alignof(T) == 0); + // Check that data.size is large enough to hold the array + FW_ASSERT(size * sizeof(T) <= data.size); + // Initialize the array members + this->m_elements = reinterpret_cast(data.bytes); + // Construct the array members in place + // This step ensures that each array element holds a valid object + // into which we can assign data + for (FwSizeType i = 0; i < size; i++) { + // This code trips an alignment check in clang-tidy + // However the alignment has been checked by FW_ASSERT above + (void)new (&this->m_elements[i]) T(); // NOLINT + } + // Set the size + this->m_size = size; + } + + public: + // ---------------------------------------------------------------------- + // Public static functions + // ---------------------------------------------------------------------- + + //! Get the alignment of the storage for an ExternalArray + //! \return The alignment + static constexpr U8 getByteArrayAlignment() { return alignof(T); } + + //! Get the size of the storage for an ExternalArray of the specified size, + //! as a byte array + //! \return The byte array size + static constexpr FwSizeType getByteArraySize(FwSizeType size //!< The size + ) { + return size * sizeof(T); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The array elements + T* m_elements = nullptr; + + //! The size + FwSizeType m_size = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/ExternalArrayMap.hpp b/Fw/DataStructures/ExternalArrayMap.hpp new file mode 100644 index 00000000000..6f99df95b4f --- /dev/null +++ b/Fw/DataStructures/ExternalArrayMap.hpp @@ -0,0 +1,170 @@ +// ====================================================================== +// \file ExternalArrayMap.hpp +// \author bocchino +// \brief An array-based map with external storage +// ====================================================================== + +#ifndef Fw_ExternalArrayMap_HPP +#define Fw_ExternalArrayMap_HPP + +#include "Fw/DataStructures/ArraySetOrMapImpl.hpp" +#include "Fw/DataStructures/MapBase.hpp" +#include "Fw/Types/Assert.hpp" + +namespace Fw { + +template +class ExternalArrayMap final : public MapBase { + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class ExternalArrayMapTester; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a const iterator + using ConstIterator = MapConstIterator; + + //! The type of a map entry + using Entry = SetOrMapImplEntry; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ExternalArrayMap() = default; + + //! Constructor providing typed backing storage. + //! entries must point to at least capacity elements of type Entry. + ExternalArrayMap(Entry* entries, //!< The entries + FwSizeType capacity //!< The capacity + ) + : MapBase() { + this->setStorage(entries, capacity); + } + + //! Constructor providing untyped backing storage. + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + ExternalArrayMap(ByteArray data, //!< The data, + FwSizeType capacity //!< The capacity + ) + : MapBase() { + this->setStorage(data, capacity); + } + + //! Copy constructor + ExternalArrayMap(const ExternalArrayMap& map) : MapBase() { *this = map; } + + //! Destructor + ~ExternalArrayMap() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + ExternalArrayMap& operator=(const ExternalArrayMap& map) { + if (&map != this) { + this->m_impl = map.m_impl; + } + return *this; + } + + //! Get the begin iterator + //! \return The iterator + ConstIterator begin() const override { return ConstIterator(this->m_impl.begin()); } + + //! Clear the map + void clear() override { this->m_impl.clear(); } + + //! Get the end iterator + //! \return The iterator + ConstIterator end() const override { return ConstIterator(this->m_impl.end()); } + + //! Find a value associated with a key in the map + //! \return SUCCESS if the item was found + Success find(const K& key, //!< The key + V& value //!< The value + ) const override { + return this->m_impl.find(key, value); + } + + //! Get the capacity of the map (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_impl.getCapacity(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const override { return this->m_impl.getSize(); } + + //! Insert a (key, value) pair in the map + //! \return SUCCESS if there is room in the map + Success insert(const K& key, //!< The key + const V& value //!< The value + ) override { + return this->m_impl.insert(key, value); + } + + //! Remove a (key, value) pair from the map + //! \return SUCCESS if the key was there + Success remove(const K& key, //!< The key + V& value //!< The value + ) override { + return this->m_impl.remove(key, value); + } + + //! Set the backing storage (typed data) + //! entries must point to at least capacity elements of type Entry. + void setStorage(Entry* entries, //!< The entries + FwSizeType capacity //!< The capacity + ) { + this->m_impl.setStorage(entries, capacity); + } + + //! Set the backing storage (untyped data) + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + void setStorage(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->m_impl.setStorage(data, capacity); + } + + public: + // ---------------------------------------------------------------------- + // Public static functions + // ---------------------------------------------------------------------- + + //! Get the alignment of the storage for an ArraySetOrMapImpl + //! \return The alignment + static constexpr U8 getByteArrayAlignment() { return ArraySetOrMapImpl::getByteArrayAlignment(); } + + //! Get the size of the storage for an ExternalArray of the specified capacity, + //! as a byte array + //! \return The byte array size + static constexpr FwSizeType getByteArraySize(FwSizeType capacity //!< The capacity + ) { + return ArraySetOrMapImpl::getByteArraySize(capacity); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The map implementation + ArraySetOrMapImpl m_impl = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/ExternalArraySet.hpp b/Fw/DataStructures/ExternalArraySet.hpp new file mode 100644 index 00000000000..c36b67741db --- /dev/null +++ b/Fw/DataStructures/ExternalArraySet.hpp @@ -0,0 +1,170 @@ +// ====================================================================== +// \file ExternalArraySet.hpp +// \author bocchino +// \brief An array-based set with external storage +// ====================================================================== + +#ifndef Fw_ExternalArraySet_HPP +#define Fw_ExternalArraySet_HPP + +#include "Fw/DataStructures/ArraySetOrMapImpl.hpp" +#include "Fw/DataStructures/Nil.hpp" +#include "Fw/DataStructures/SetBase.hpp" +#include "Fw/Types/Assert.hpp" + +namespace Fw { + +template +class ExternalArraySet final : public SetBase { + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class ExternalArraySetTester; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a const iterator + using ConstIterator = SetConstIterator; + + //! The type of a set entry + using Entry = SetOrMapImplEntry; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ExternalArraySet() = default; + + //! Constructor providing typed backing storage. + //! entries must point to at least capacity elements of type ImplEntry. + ExternalArraySet(Entry* entries, //!< The entries + FwSizeType capacity //!< The capacity + ) + : SetBase() { + this->setStorage(entries, capacity); + } + + //! Constructor providing untyped backing storage. + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + ExternalArraySet(ByteArray data, //!< The data, + FwSizeType capacity //!< The capacity + ) + : SetBase() { + this->setStorage(data, capacity); + } + + //! Copy constructor + ExternalArraySet(const ExternalArraySet& set) : SetBase() { *this = set; } + + //! Destructor + ~ExternalArraySet() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + ExternalArraySet& operator=(const ExternalArraySet& set) { + if (&set != this) { + this->m_impl = set.m_impl; + } + return *this; + } + + //! Get the begin iterator + //! \return The iterator + ConstIterator begin() const override { return ConstIterator(this->m_impl.begin()); } + + //! Clear the set + void clear() override { this->m_impl.clear(); } + + //! Get the end iterator + //! \return The iterator + ConstIterator end() const override { return ConstIterator(this->m_impl.end()); } + + //! Find a value associated with an element in the set + //! \return SUCCESS if the item was found + Success find(const T& element //!< The element + ) const override { + Nil nil = {}; + return this->m_impl.find(element, nil); + } + + //! Get the capacity of the set (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_impl.getCapacity(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const override { return this->m_impl.getSize(); } + + //! Insert an element in the set + //! \return SUCCESS if there is room in the set + Success insert(const T& element //!< The element + ) override { + return this->m_impl.insert(element, Nil()); + } + + //! Remove an element from the set + //! \return SUCCESS if the element was there + Success remove(const T& element //!< The element + ) override { + Nil nil = {}; + return this->m_impl.remove(element, nil); + } + + //! Set the backing storage (typed data) + //! entries must point to at least capacity elements of type ImplEntry. + void setStorage(Entry* entries, //!< The entries + FwSizeType capacity //!< The capacity + ) { + this->m_impl.setStorage(entries, capacity); + } + + //! Set the backing storage (untyped data) + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + void setStorage(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->m_impl.setStorage(data, capacity); + } + + public: + // ---------------------------------------------------------------------- + // Public static functions + // ---------------------------------------------------------------------- + + //! Get the alignment of the storage for an ArraySetOrMapImpl + //! \return The alignment + static constexpr U8 getByteArrayAlignment() { return ArraySetOrMapImpl::getByteArrayAlignment(); } + + //! Get the size of the storage for an ExternalArray of the specified capacity, + //! as a byte array + //! \return The byte array size + static constexpr FwSizeType getByteArraySize(FwSizeType capacity //!< The capacity + ) { + return ArraySetOrMapImpl::getByteArraySize(capacity); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The set implementation + ArraySetOrMapImpl m_impl = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/ExternalFifoQueue.hpp b/Fw/DataStructures/ExternalFifoQueue.hpp new file mode 100644 index 00000000000..74938f82482 --- /dev/null +++ b/Fw/DataStructures/ExternalFifoQueue.hpp @@ -0,0 +1,190 @@ +// ====================================================================== +// \file ExternalFifoQueue.hpp +// \author bocchino +// \brief A FIFO queue with external storage +// ====================================================================== + +#ifndef Fw_ExternalFifoQueue_HPP +#define Fw_ExternalFifoQueue_HPP + +#include "Fw/DataStructures/CircularIndex.hpp" +#include "Fw/DataStructures/ExternalArray.hpp" +#include "Fw/DataStructures/FifoQueueBase.hpp" +#include "Fw/Types/ByteArray.hpp" + +namespace Fw { + +template +class ExternalFifoQueue final : public FifoQueueBase { + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class ExternalFifoQueueTester; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ExternalFifoQueue() = default; + + //! Constructor providing typed backing storage + ExternalFifoQueue(T* items, //!< The items + FwSizeType capacity //!< The capacity + ) + : FifoQueueBase() { + this->setStorage(items, capacity); + } + + //! Constructor providing untyped backing storage + ExternalFifoQueue(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) + : FifoQueueBase() { + this->setStorage(data, capacity); + } + + //! Copy constructor + ExternalFifoQueue(const ExternalFifoQueue& queue) : FifoQueueBase() { *this = queue; } + + //! Destructor + ~ExternalFifoQueue() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + ExternalFifoQueue& operator=(const ExternalFifoQueue& queue) { + if (&queue != this) { + this->m_items = queue.m_items; + this->m_enqueueIndex = queue.m_enqueueIndex; + this->m_dequeueIndex = queue.m_dequeueIndex; + this->m_size = queue.m_size; + } + return *this; + } + + //! Clear the queue + void clear() override { + this->m_enqueueIndex.setValue(0); + this->m_dequeueIndex.setValue(0); + this->m_size = 0; + } + + //! Set the storage (typed data) + void setStorage(T* items, //!< The items + FwSizeType capacity //!< The capacity + ) { + this->m_items.setStorage(items, capacity); + if (capacity > 0) { + this->m_enqueueIndex.setModulus(capacity); + this->m_dequeueIndex.setModulus(capacity); + } + this->clear(); + } + + //! Set the storage (untyped data) + void setStorage(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->m_items.setStorage(data, capacity); + if (capacity > 0) { + this->m_enqueueIndex.setModulus(capacity); + this->m_dequeueIndex.setModulus(capacity); + } + this->clear(); + } + + //! Enqueue an element (push on the right) + //! \return SUCCESS if element enqueued + Success enqueue(const T& e //!< The element (output) + ) override { + auto status = Success::FAILURE; + if (this->m_size < this->getCapacity()) { + const auto i = this->m_enqueueIndex.getValue(); + this->m_items[i] = e; + (void)this->m_enqueueIndex.increment(); + this->m_size++; + status = Success::SUCCESS; + } + return status; + } + + //! Get an item at an index. + //! Indices go from left to right in the queue. + //! Fails an assertion if the index is out of range. + //! \return The item + const T& at(FwSizeType index //!< The index + ) const override { + FW_ASSERT(index < this->m_size, static_cast(index), + static_cast(this->m_size)); + auto ci = this->m_dequeueIndex; + const auto i = ci.increment(index); + return this->m_items[i]; + } + + //! Dequeue an element (remove from the left) + //! \return SUCCESS if element dequeued + Success dequeue(T& e //!< The element (output) + ) override { + auto status = Success::FAILURE; + if (this->m_size > 0) { + e = this->at(0); + (void)this->m_dequeueIndex.increment(); + this->m_size--; + status = Success::SUCCESS; + } + return status; + } + + //! Get the size (number of items stored in the queue) + //! \return The size + FwSizeType getSize() const override { return this->m_size; } + + //! Get the capacity (maximum number of items stored in the queue) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_items.getSize(); } + + public: + // ---------------------------------------------------------------------- + // Public static functions + // ---------------------------------------------------------------------- + + //! Get the alignment of the storage for an ExternalFifoQueue + //! \return The alignment + static constexpr U8 getByteArrayAlignment() { return ExternalArray::getByteArrayAlignment(); } + + //! Get the size of the storage for an ExternalFifoQueue of the specified + //! capacity, as a byte array + //! \return The byte array size + static constexpr FwSizeType getByteArraySize(FwSizeType capacity //!< The capacity + ) { + return ExternalArray::getByteArraySize(capacity); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The array for storing the queue items + ExternalArray m_items = {}; + + //! The enqueue index + CircularIndex m_enqueueIndex = {}; + + //! The dequeue index + CircularIndex m_dequeueIndex = {}; + + //! The number of items on the queue + FwSizeType m_size = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/ExternalStack.hpp b/Fw/DataStructures/ExternalStack.hpp new file mode 100644 index 00000000000..fbad3fa35cb --- /dev/null +++ b/Fw/DataStructures/ExternalStack.hpp @@ -0,0 +1,165 @@ +// ====================================================================== +// \file ExternalStack.hpp +// \author bocchino +// \brief A stack with external storage +// ====================================================================== + +#ifndef Fw_ExternalStack_HPP +#define Fw_ExternalStack_HPP + +#include "Fw/DataStructures/ExternalArray.hpp" +#include "Fw/DataStructures/StackBase.hpp" +#include "Fw/Types/ByteArray.hpp" + +namespace Fw { + +template +class ExternalStack final : public StackBase { + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class ExternalStackTester; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ExternalStack() = default; + + //! Constructor providing typed backing storage + ExternalStack(T* items, //!< The items + FwSizeType capacity //!< The capacity + ) + : StackBase() { + this->setStorage(items, capacity); + } + + //! Constructor providing untyped backing storage + ExternalStack(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) + : StackBase() { + this->setStorage(data, capacity); + } + + //! Copy constructor + ExternalStack(const ExternalStack& stack) : StackBase() { *this = stack; } + + //! Destructor + ~ExternalStack() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + ExternalStack& operator=(const ExternalStack& stack) { + if (&stack != this) { + this->m_items = stack.m_items; + this->m_size = stack.m_size; + } + return *this; + } + + //! Clear the stack + void clear() override { this->m_size = 0; } + + //! Set the storage (typed data) + void setStorage(T* items, //!< The items + FwSizeType capacity //!< The capacity + ) { + this->m_items.setStorage(items, capacity); + this->clear(); + } + + //! Set the storage (untyped data) + void setStorage(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->m_items.setStorage(data, capacity); + this->clear(); + } + + //! Push an element (push on the right) + //! \return SUCCESS if element pushed + Success push(const T& e //!< The element (output) + ) override { + auto status = Success::FAILURE; + if (this->m_size < this->getCapacity()) { + this->m_items[this->m_size] = e; + this->m_size++; + status = Success::SUCCESS; + } + return status; + } + + //! Get an item at an index. + //! Index 0 is the rightmost (latest) element in the stack. + //! Increasing indices go from right to left. + //! Fails an assertion if the index is out of range. + //! \return The item + const T& at(FwSizeType index //!< The index + ) const override { + FW_ASSERT(index < this->m_size, static_cast(index), + static_cast(this->m_size)); + return this->m_items[this->m_size - 1 - index]; + } + + //! Pop an element (remove from the right) + //! \return SUCCESS if element popped + Success pop(T& e //!< The element (output) + ) override { + auto status = Success::FAILURE; + if (this->m_size > 0) { + e = this->at(0); + this->m_size--; + status = Success::SUCCESS; + } + return status; + } + + //! Get the size (number of items stored in the stack) + //! \return The size + FwSizeType getSize() const override { return this->m_size; } + + //! Get the capacity (maximum number of items stored in the stack) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_items.getSize(); } + + public: + // ---------------------------------------------------------------------- + // Public static functions + // ---------------------------------------------------------------------- + + //! Get the alignment of the storage for an ExternalStack + //! \return The alignment + static constexpr U8 getByteArrayAlignment() { return ExternalArray::getByteArrayAlignment(); } + + //! Get the size of the storage for an ExternalStack of the specified + //! capacity, as a byte array + //! \return The byte array size + static constexpr FwSizeType getByteArraySize(FwSizeType capacity //!< The capacity + ) { + return ExternalArray::getByteArraySize(capacity); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The array for storing the stack items + ExternalArray m_items = {}; + + //! The number of items on the stack + FwSizeType m_size = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/FifoQueue.hpp b/Fw/DataStructures/FifoQueue.hpp new file mode 100644 index 00000000000..050c09d366e --- /dev/null +++ b/Fw/DataStructures/FifoQueue.hpp @@ -0,0 +1,104 @@ +// ====================================================================== +// \file FifoQueue.hpp +// \author bocchino +// \brief A FIFO queue with internal storage +// ====================================================================== + +#ifndef Fw_FifoQueue_HPP +#define Fw_FifoQueue_HPP + +#include "Fw/DataStructures/Array.hpp" +#include "Fw/DataStructures/ExternalFifoQueue.hpp" + +namespace Fw { + +template +class FifoQueue final : public FifoQueueBase { + // ---------------------------------------------------------------------- + // Static assertions + // ---------------------------------------------------------------------- + + static_assert(std::is_default_constructible::value, "T must be default constructible"); + static_assert(C > 0, "capacity must be greater than zero"); + + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class FifoQueueTester; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + FifoQueue() : FifoQueueBase(), m_extQueue(m_items, C) {} + + //! Copy constructor + FifoQueue(const FifoQueue& queue) : FifoQueueBase(), m_extQueue(m_items, C) { *this = queue; } + + //! Destructor + ~FifoQueue() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + FifoQueue& operator=(const FifoQueue& queue) { + this->m_extQueue.copyDataFrom(queue); + return *this; + } + + //! Clear the queue + void clear() override { this->m_extQueue.clear(); } + + //! Enqueue an item (push on the right) + //! \return SUCCESS if item enqueued + Success enqueue(const T& e //!< The item (output) + ) override { + return this->m_extQueue.enqueue(e); + } + + //! Dequeue an item (remove from the left) + //! \return SUCCESS if item dequeued + Success dequeue(T& e //!< The item (output) + ) override { + return this->m_extQueue.dequeue(e); + } + + //! Get the size (number of items stored in the queue) + //! \return The size + FwSizeType getSize() const override { return this->m_extQueue.getSize(); } + + //! Get the capacity (maximum number of items stored in the queue) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_extQueue.getCapacity(); } + + //! Get an item at an index. + //! Indices go from left to right in the queue. + //! Fails an assertion if the index is out of range. + //! \return The item + const T& at(FwSizeType index //!< The index + ) const override { + return this->m_extQueue.at(index); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The external queue implementation + ExternalFifoQueue m_extQueue = {}; + + //! The array providing the backing memory for m_extQueue + T m_items[C] = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/FifoQueueBase.hpp b/Fw/DataStructures/FifoQueueBase.hpp new file mode 100644 index 00000000000..8fc94743dd6 --- /dev/null +++ b/Fw/DataStructures/FifoQueueBase.hpp @@ -0,0 +1,101 @@ +// ====================================================================== +// \title FifoQueueBase +// \author bocchino +// \brief An abstract base class template for a FIFO queue +// ====================================================================== + +#ifndef Fw_FifoQueueBase_HPP +#define Fw_FifoQueueBase_HPP + +#include "Fw/DataStructures/SizedContainer.hpp" +#include "Fw/Types/Assert.hpp" +#include "Fw/Types/SuccessEnumAc.hpp" + +namespace Fw { + +template +class FifoQueueBase : public SizedContainer { + private: + // ---------------------------------------------------------------------- + // Private constructors + // ---------------------------------------------------------------------- + + //! Copy constructor deleted in the base class + //! Behavior depends on the implementation + FifoQueueBase(const FifoQueueBase&) = delete; + + protected: + // ---------------------------------------------------------------------- + // Protected constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + FifoQueueBase() : SizedContainer() {} + + //! Destructor + virtual ~FifoQueueBase() = default; + + private: + // ---------------------------------------------------------------------- + // Private member functions + // ---------------------------------------------------------------------- + + //! operator= deleted in the base class + //! Behavior depends on the implementation + //! We avoid virtual user-defined operators + FifoQueueBase& operator=(const FifoQueueBase&) = delete; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Get an item at an index. + //! Indices go from left to right in the queue. + //! Fails an assertion if the index is out of range. + //! \return The item + virtual const T& at(FwSizeType index //!< The index + ) const = 0; + + //! Copy data from another queue + void copyDataFrom(const FifoQueueBase& queue //!< The queue + ) { + if (&queue != this) { + this->clear(); + const FwSizeType size = FW_MIN(queue.getSize(), this->getCapacity()); + for (FwSizeType i = 0; i < size; i++) { + const auto& e = queue.at(i); + const auto status = this->enqueue(e); + FW_ASSERT(status == Fw::Success::SUCCESS, static_cast(status)); + } + } + } + + //! Enqueue an item (add to the right) + //! \return SUCCESS if item enqueued + virtual Success enqueue(const T& e //!< The item (output) + ) = 0; + + //! Peek an item at an index + //! Indices go from left to right in the range [0, size) + //! \return SUCCESS if item exists + Success peek(T& e, //!< The item (output) + FwSizeType index = 0 //!< The index (input) + ) const { + auto status = Success::FAILURE; + if (index < this->getSize()) { + e = this->at(index); + status = Success::SUCCESS; + } + return status; + } + + //! Dequeue an item (remove from the left) + //! \return SUCCESS if item dequeued + virtual Success dequeue(T& e //!< The item (output) + ) = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/MapBase.hpp b/Fw/DataStructures/MapBase.hpp new file mode 100644 index 00000000000..b2a0fbd0a54 --- /dev/null +++ b/Fw/DataStructures/MapBase.hpp @@ -0,0 +1,100 @@ +// ====================================================================== +// \title MapBase +// \author bocchino +// \brief An abstract base class template for a map +// ====================================================================== + +#ifndef Fw_MapBase_HPP +#define Fw_MapBase_HPP + +#include "Fw/DataStructures/MapConstIterator.hpp" +#include "Fw/DataStructures/SizedContainer.hpp" +#include "Fw/Types/Assert.hpp" +#include "Fw/Types/SuccessEnumAc.hpp" + +namespace Fw { + +template +class MapBase : public SizedContainer { + private: + // ---------------------------------------------------------------------- + // Deleted elements + // ---------------------------------------------------------------------- + + //! Copy constructor deleted in the base class + //! Behavior depends on the implementation + MapBase(const MapBase&) = delete; + + //! operator= deleted in the base class + //! Behavior depends on the implementation + //! We avoid virtual user-defined operators + MapBase& operator=(const MapBase&) = delete; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a map const iterator + using ConstIterator = MapConstIterator; + + protected: + // ---------------------------------------------------------------------- + // Protected constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + MapBase() : SizedContainer() {} + + //! Destructor + virtual ~MapBase() = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Get the begin value of the iterator + //! \return The iterator + virtual ConstIterator begin() const = 0; + + //! Copy data from another map + void copyDataFrom(const MapBase& map) { + if (&map != this) { + this->clear(); + const FwSizeType size = FW_MIN(map.getSize(), this->getCapacity()); + auto it = map.begin(); + for (FwSizeType i = 0; i < size; i++) { + const auto status = this->insert(it->getKey(), it->getValue()); + FW_ASSERT(status == Success::SUCCESS, static_cast(status)); + it++; + } + } + } + + //! Get the end value of the iterator + //! \return The iterator + virtual ConstIterator end() const = 0; + + //! Find the value associated with a key in the map + //! SUCCESS if the item was found + virtual Success find(const K& key, //!< The key (input) + V& value //!< The value (output) + ) const = 0; + + //! Insert a (key, value) pair in the map + //! \return SUCCESS if there is room in the map + virtual Success insert(const K& key, //!< The key + const V& value //!< The value + ) = 0; + //! Remove a (key, value) pair from the map + //! Store the value into the value parameter if the key was there + //! \return SUCCESS if the key was there + virtual Success remove(const K& key, //!< The key (input) + V& value //!< The value (output) + ) = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/MapConstIterator.hpp b/Fw/DataStructures/MapConstIterator.hpp new file mode 100644 index 00000000000..f9a1cc84b77 --- /dev/null +++ b/Fw/DataStructures/MapConstIterator.hpp @@ -0,0 +1,162 @@ +// ====================================================================== +// \title MapConstIterator +// \author bocchino +// \brief An abstract class template representing a const iterator for a map +// ====================================================================== + +#ifndef Fw_MapConstIterator_HPP +#define Fw_MapConstIterator_HPP + +#include + +#include "Fw/DataStructures/ArraySetOrMapImpl.hpp" +#include "Fw/DataStructures/MapEntryBase.hpp" +#include "Fw/FPrimeBasicTypes.hpp" + +namespace Fw { + +template +class MapConstIterator { + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of an array iterator + using ArrayIterator = typename ArraySetOrMapImpl::ConstIterator; + using EntryBase = MapEntryBase; + + private: + // ---------------------------------------------------------------------- + // Private types + // ---------------------------------------------------------------------- + + //! The type of an implementation kind + using ImplKind = typename SetOrMapImplConstIterator::ImplKind; + + //! The type of an implementation + union Impl { + //! Default constructor + Impl() {} + //! Array constructor + Impl(const ArrayIterator& it) : array(it) {} + //! An array iterator + ArrayIterator array; + // TODO: Add red-black tree implementation + // ! Destructor + ~Impl() {} + }; + + public: + // ---------------------------------------------------------------------- + // Constructors and destructors + // ---------------------------------------------------------------------- + + //! Constructor providing an array implementation + MapConstIterator(const ArrayIterator& it) : m_impl(it), m_implIterator(&m_impl.array) {} + + //! Copy constructor + MapConstIterator(const MapConstIterator& it) : m_impl(), m_implIterator() { + const auto implKind = it.getImplIterator().implKind(); + switch (implKind) { + case ImplKind::ARRAY: + this->m_implIterator = new (&this->m_impl.array) ArrayIterator(it.m_impl.array); + break; + case ImplKind::RED_BLACK_TREE: + // TODO + break; + default: + FW_ASSERT(0, static_cast(implKind)); + break; + } + } + + //! Destructor + ~MapConstIterator() {} + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Copy assignment operator + MapConstIterator& operator=(const MapConstIterator&) = default; + + //! Equality comparison operator + bool operator==(const MapConstIterator& it) { + bool result = false; + const auto implKind1 = this->getImplIterator().implKind(); + const auto implKind2 = it.getImplIterator().implKind(); + if (implKind1 == implKind2) { + switch (implKind1) { + case ImplKind::ARRAY: + result = this->m_impl.array.compareEqual(it.m_impl.array); + break; + case ImplKind::RED_BLACK_TREE: + // TODO + break; + default: + FW_ASSERT(0, static_cast(implKind1)); + break; + } + } + return result; + } + + //! Inequality comparison operator + bool operator!=(const MapConstIterator& it) { return !(*this == it); }; + + //! Prefix increment + MapConstIterator& operator++() { + this->getImplIterator().increment(); + return *this; + } + + //! Postfix increment + MapConstIterator operator++(int) { + MapConstIterator tmp = *this; + ++(*this); + return tmp; + } + + //! Check whether the iterator is in range + bool isInRange() const { return this->getImplIterator().isInRange(); } + + //! Dereference + const EntryBase& operator*() const { return this->getImplIterator().getEntry(); } + + //! Pointer + const EntryBase* operator->() const { return &this->getImplIterator().getEntry(); } + + private: + // ---------------------------------------------------------------------- + // Private helper functions + // ---------------------------------------------------------------------- + + //! Assert and get the impl iterator + SetOrMapImplConstIterator& getImplIterator() { + FW_ASSERT(this->m_implIterator != nullptr); + return *this->m_implIterator; + } + + //! Assert and get the impl iterator (const) + const SetOrMapImplConstIterator& getImplIterator() const { + FW_ASSERT(this->m_implIterator != nullptr); + return *this->m_implIterator; + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The implementation + Impl m_impl; + + //! The impl iterator + SetOrMapImplConstIterator* m_implIterator = nullptr; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/MapEntryBase.hpp b/Fw/DataStructures/MapEntryBase.hpp new file mode 100644 index 00000000000..c270b88e129 --- /dev/null +++ b/Fw/DataStructures/MapEntryBase.hpp @@ -0,0 +1,57 @@ +// ====================================================================== +// \title MapEntryBase +// \author bocchino +// \brief An abstract base class representing an entry in a map +// ====================================================================== + +#ifndef Fw_MapEntryBase_HPP +#define Fw_MapEntryBase_HPP + +#include "Fw/FPrimeBasicTypes.hpp" + +namespace Fw { + +template +class MapEntryBase { + private: + // ---------------------------------------------------------------------- + // Deleted elements + // ---------------------------------------------------------------------- + + //! Copy constructor deleted in the base class + //! Behavior depends on the implementation + MapEntryBase(const MapEntryBase&) = delete; + + //! operator= deleted in the base class + //! Behavior depends on the implementation + //! We avoid virtual user-defined operators + MapEntryBase& operator=(const MapEntryBase&) = delete; + + protected: + // ---------------------------------------------------------------------- + // Protected constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + MapEntryBase() = default; + + //! Destructor + virtual ~MapEntryBase() = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Get the key associated with this entry + //! \return The key + virtual const K& getKey() const = 0; + + //! Get the value associated with this entry + //! \return The value + virtual const V& getValue() const = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/Nil.hpp b/Fw/DataStructures/Nil.hpp new file mode 100644 index 00000000000..c62b2191662 --- /dev/null +++ b/Fw/DataStructures/Nil.hpp @@ -0,0 +1,16 @@ +// ====================================================================== +// \file Nil.hpp +// \author bocchino +// \brief The Nil type +// ====================================================================== + +#ifndef Fw_Nil_HPP +#define Fw_Nil_HPP + +namespace Fw { + +struct Nil {}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/SetBase.hpp b/Fw/DataStructures/SetBase.hpp new file mode 100644 index 00000000000..8097abbd8a3 --- /dev/null +++ b/Fw/DataStructures/SetBase.hpp @@ -0,0 +1,97 @@ +// ====================================================================== +// \title SetBase +// \author bocchino +// \brief An abstract base class template for a set +// ====================================================================== + +#ifndef Fw_SetBase_HPP +#define Fw_SetBase_HPP + +#include "Fw/DataStructures/SetConstIterator.hpp" +#include "Fw/DataStructures/SizedContainer.hpp" +#include "Fw/Types/Assert.hpp" +#include "Fw/Types/SuccessEnumAc.hpp" + +namespace Fw { + +template +class SetBase : public SizedContainer { + private: + // ---------------------------------------------------------------------- + // Deleted elements + // ---------------------------------------------------------------------- + + //! Copy constructor deleted in the base class + //! Behavior depends on the implementation + SetBase(const SetBase&) = delete; + + //! operator= deleted in the base class + //! Behavior depends on the implementation + //! We avoid virtual user-defined operators + SetBase& operator=(const SetBase&) = delete; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a set const iterator + using ConstIterator = SetConstIterator; + + protected: + // ---------------------------------------------------------------------- + // Protected constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + SetBase() : SizedContainer() {} + + //! Destructor + virtual ~SetBase() = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Get the begin iterator + //! \return The iterator + virtual ConstIterator begin() const = 0; + + //! Get the end iterator + //! \return The iterator + virtual ConstIterator end() const = 0; + + //! Copy data from another set + void copyDataFrom(const SetBase& set) { + if (&set != this) { + this->clear(); + const FwSizeType size = FW_MIN(set.getSize(), this->getCapacity()); + auto it = set.begin(); + for (FwSizeType i = 0; i < size; i++) { + const auto status = this->insert(*it); + FW_ASSERT(status == Success::SUCCESS, static_cast(status)); + it++; + } + } + } + + //! Find an element in a set + //! SUCCESS if the item was found + virtual Success find(const T& element //!< The element + ) const = 0; + + //! Insert an element in the set + //! \return SUCCESS if there is room in the set + virtual Success insert(const T& element //!< The element + ) = 0; + + //! Remove an element from the set + //! \return SUCCESS if the element was there + virtual Success remove(const T& element //!< The element + ) = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/SetConstIterator.hpp b/Fw/DataStructures/SetConstIterator.hpp new file mode 100644 index 00000000000..8af5ff73ef2 --- /dev/null +++ b/Fw/DataStructures/SetConstIterator.hpp @@ -0,0 +1,161 @@ +// ====================================================================== +// \title SetConstIterator +// \author bocchino +// \brief An abstract class template representing a const iterator for a set +// ====================================================================== + +#ifndef Fw_SetConstIterator_HPP +#define Fw_SetConstIterator_HPP + +#include + +#include "Fw/DataStructures/ArraySetOrMapImpl.hpp" +#include "Fw/DataStructures/Nil.hpp" +#include "Fw/FPrimeBasicTypes.hpp" + +namespace Fw { + +template +class SetConstIterator { + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of an array iterator + using ArrayIterator = typename ArraySetOrMapImpl::ConstIterator; + + private: + // ---------------------------------------------------------------------- + // Private types + // ---------------------------------------------------------------------- + + //! The type of an implementation kind + using ImplKind = typename SetOrMapImplConstIterator::ImplKind; + + //! The type of an implementation + union Impl { + //! Default constructor + Impl() {} + //! Array constructor + Impl(const ArrayIterator& it) : array(it) {} + //! An array iterator + ArrayIterator array; + // TODO: Add red-black tree implementation + // ! Destructor + ~Impl() {} + }; + + public: + // ---------------------------------------------------------------------- + // Constructors and destructors + // ---------------------------------------------------------------------- + + //! Constructor providing an array implementation + SetConstIterator(const ArrayIterator& it) : m_impl(it), m_implIterator(&m_impl.array) {} + + //! Copy constructor + SetConstIterator(const SetConstIterator& it) : m_impl(), m_implIterator() { + const auto implKind = it.getImplIterator().implKind(); + switch (implKind) { + case ImplKind::ARRAY: + this->m_implIterator = new (&this->m_impl.array) ArrayIterator(it.m_impl.array); + break; + case ImplKind::RED_BLACK_TREE: + // TODO + break; + default: + FW_ASSERT(0, static_cast(implKind)); + break; + } + } + + //! Destructor + ~SetConstIterator() {} + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Copy assignment operator + SetConstIterator& operator=(const SetConstIterator&) = default; + + //! Equality comparison operator + bool operator==(const SetConstIterator& it) { + bool result = false; + const auto implKind1 = this->getImplIterator().implKind(); + const auto implKind2 = it.getImplIterator().implKind(); + if (implKind1 == implKind2) { + switch (implKind1) { + case ImplKind::ARRAY: + result = this->m_impl.array.compareEqual(it.m_impl.array); + break; + case ImplKind::RED_BLACK_TREE: + // TODO + break; + default: + FW_ASSERT(0, static_cast(implKind1)); + break; + } + } + return result; + } + + //! Inequality comparison operator + bool operator!=(const SetConstIterator& it) { return !(*this == it); }; + + //! Prefix increment + SetConstIterator& operator++() { + this->getImplIterator().increment(); + return *this; + } + + //! Postfix increment + SetConstIterator operator++(int) { + SetConstIterator tmp = *this; + ++(*this); + return tmp; + } + + //! Check whether the iterator is in range + bool isInRange() const { return this->getImplIterator().isInRange(); } + + //! Dereference + const T& operator*() const { return this->getImplIterator().getEntry().getKeyOrElement(); } + + //! Pointer + const T* operator->() const { return &this->getImplIterator().getEntry().getKeyOrElement(); } + + private: + // ---------------------------------------------------------------------- + // Private helper functions + // ---------------------------------------------------------------------- + + //! Assert and get the impl iterator + SetOrMapImplConstIterator& getImplIterator() { + FW_ASSERT(this->m_implIterator != nullptr); + return *this->m_implIterator; + } + + //! Assert and get the impl iterator (const) + const SetOrMapImplConstIterator& getImplIterator() const { + FW_ASSERT(this->m_implIterator != nullptr); + return *this->m_implIterator; + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The implementation + Impl m_impl; + + //! The impl iterator + SetOrMapImplConstIterator* m_implIterator = nullptr; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/SetOrMapImplConstIterator.hpp b/Fw/DataStructures/SetOrMapImplConstIterator.hpp new file mode 100644 index 00000000000..de4b19a32ca --- /dev/null +++ b/Fw/DataStructures/SetOrMapImplConstIterator.hpp @@ -0,0 +1,70 @@ +// ====================================================================== +// \title SetOrMapImplConstIterator +// \author bocchino +// \brief A class template representing a const iterator for a set or map implementation +// ====================================================================== + +#ifndef Fw_SetOrMapImplConstIterator_HPP +#define Fw_SetOrMapImplConstIterator_HPP + +#include "Fw/DataStructures/SetOrMapImplEntry.hpp" +#include "Fw/FPrimeBasicTypes.hpp" + +namespace Fw { + +template +class SetOrMapImplConstIterator { + private: + // ---------------------------------------------------------------------- + // Deleted elements + // ---------------------------------------------------------------------- + + //! Copy constructor + SetOrMapImplConstIterator(const SetOrMapImplConstIterator& it) = delete; + + //! Copy assignment operator + SetOrMapImplConstIterator& operator=(const SetOrMapImplConstIterator&) = delete; + + public: + // ---------------------------------------------------------------------- + // Types + // ---------------------------------------------------------------------- + + //! The kind of a const iterator implementation + enum class ImplKind { ARRAY, RED_BLACK_TREE }; + + public: + // ---------------------------------------------------------------------- + // Constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + SetOrMapImplConstIterator() = default; + + //! Destructor + virtual ~SetOrMapImplConstIterator() = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Return the impl kind + //! \return The impl kind + virtual ImplKind implKind() const = 0; + + //! Increment the iterator + virtual void increment() = 0; + + //! Check whether the iterator is in range + //! \return True if the iterator is in range + virtual bool isInRange() const = 0; + + //! Get the set or map impl entry pointed to by this iterator + //! \return The set or map impl entry + virtual const SetOrMapImplEntry& getEntry() const = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/SetOrMapImplEntry.hpp b/Fw/DataStructures/SetOrMapImplEntry.hpp new file mode 100644 index 00000000000..12661481c07 --- /dev/null +++ b/Fw/DataStructures/SetOrMapImplEntry.hpp @@ -0,0 +1,104 @@ +// ====================================================================== +// \title SetOrMapImplEntry +// \author bocchino +// \brief A class template representing an entry for a set or map implementation +// ====================================================================== + +#ifndef Fw_SetOrMapImplEntry_HPP +#define Fw_SetOrMapImplEntry_HPP + +#include "Fw/DataStructures/MapEntryBase.hpp" +#include "Fw/FPrimeBasicTypes.hpp" + +namespace Fw { + +template +class SetOrMapImplEntry final : public MapEntryBase { + // ---------------------------------------------------------------------- + // Static assertions + // ---------------------------------------------------------------------- + + static_assert(std::is_default_constructible::value, "key must be default constructible"); + static_assert(std::is_assignable::value, "key or element must be assignable"); + static_assert(std::is_default_constructible::value, "value must be default constructible"); + static_assert(std::is_assignable::value, "value must be assignable"); + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + SetOrMapImplEntry() : MapEntryBase() {} + + //! Constructor providing members + SetOrMapImplEntry(const KE& keyOrElement, //!< The key or element + const VN& valueOrNil //!< The value or Nil + ) + : MapEntryBase(), m_keyOrElement(keyOrElement), m_valueOrNil(valueOrNil) {} + + //! Copy constructor + SetOrMapImplEntry(const SetOrMapImplEntry& entry) : MapEntryBase() { *this = entry; } + + //! Destructor + ~SetOrMapImplEntry() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + SetOrMapImplEntry& operator=(const SetOrMapImplEntry& entry) { + if (this != &entry) { + this->m_keyOrElement = entry.m_keyOrElement; + this->m_valueOrNil = entry.m_valueOrNil; + } + return *this; + } + + //! Get the key or element associated with this entry + //! \return The key or element + const KE& getKeyOrElement() const { return this->m_keyOrElement; } + + //! Get the value or nil associated with this entry + //! \return The value or nil + const VN& getValueOrNil() const { return this->m_valueOrNil; } + + //! Set the key or element + void setKeyOrElement(const KE& keyOrElement //!< The key or element + ) { + this->m_keyOrElement = keyOrElement; + } + + //! Set the value or Nil + void setValueOrNil(const VN& valueOrNil) { this->m_valueOrNil = valueOrNil; } + + public: + // ---------------------------------------------------------------------- + // MapEntryBase implementation + // ---------------------------------------------------------------------- + + //! Get the key associated with this entry + //! \return The key + const KE& getKey() const override { return this->m_keyOrElement; } + + //! Get the value associated with this entry + //! \return The value + const VN& getValue() const override { return this->m_valueOrNil; } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The map key or set element + KE m_keyOrElement = {}; + + //! The value or nil + VN m_valueOrNil = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/SizedContainer.hpp b/Fw/DataStructures/SizedContainer.hpp new file mode 100644 index 00000000000..977e1fc5d1f --- /dev/null +++ b/Fw/DataStructures/SizedContainer.hpp @@ -0,0 +1,72 @@ +// ====================================================================== +// \title SizedContainer +// \author bocchino +// \brief An abstract base class representing a sized container +// ====================================================================== + +#ifndef Fw_SizedContainer_HPP +#define Fw_SizedContainer_HPP + +#include "Fw/FPrimeBasicTypes.hpp" + +namespace Fw { + +class SizedContainer { + private: + // ---------------------------------------------------------------------- + // Private constructors + // ---------------------------------------------------------------------- + + //! Copy constructor deleted in the base class + //! Behavior depends on the implementation + SizedContainer(const SizedContainer&) = delete; + + protected: + // ---------------------------------------------------------------------- + // Protected constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + SizedContainer() {} + + //! Destructor + virtual ~SizedContainer() = default; + + private: + // ---------------------------------------------------------------------- + // Private member functions + // ---------------------------------------------------------------------- + + //! operator= deleted in the base class + //! Behavior depends on the implementation + //! We avoid virtual user-defined operators + SizedContainer& operator=(const SizedContainer&) = delete; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Clear the container + virtual void clear() = 0; + + //! Get the size (number of items stored in the container) + //! \return The size + virtual FwSizeType getSize() const = 0; + + //! Get the capacity (maximum number of items storable in the container) + //! \return The capacity + virtual FwSizeType getCapacity() const = 0; + + //! Check whether the container is empty + //! \return True if the container is empty + bool isEmpty() const { return this->getSize() == 0; } + + //! Check whether the container is full + //! \return True if the container is full + bool isFull() const { return this->getSize() >= this->getCapacity(); } +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/Stack.hpp b/Fw/DataStructures/Stack.hpp new file mode 100644 index 00000000000..8e55b53b32e --- /dev/null +++ b/Fw/DataStructures/Stack.hpp @@ -0,0 +1,105 @@ +// ====================================================================== +// \file Stack.hpp +// \author bocchino +// \brief A stack with internal storage +// ====================================================================== + +#ifndef Fw_Stack_HPP +#define Fw_Stack_HPP + +#include "Fw/DataStructures/Array.hpp" +#include "Fw/DataStructures/ExternalStack.hpp" + +namespace Fw { + +template +class Stack final : public StackBase { + // ---------------------------------------------------------------------- + // Static assertions + // ---------------------------------------------------------------------- + + static_assert(std::is_default_constructible::value, "T must be default constructible"); + static_assert(C > 0, "capacity must be greater than zero"); + + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class StackTester; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + Stack() : StackBase(), m_extStack(m_items, C) {} + + //! Copy constructor + Stack(const Stack& stack) : StackBase(), m_extStack(m_items, C) { *this = stack; } + + //! Destructor + ~Stack() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + Stack& operator=(const Stack& stack) { + this->m_extStack.copyDataFrom(stack); + return *this; + } + + //! Clear the stack + void clear() override { this->m_extStack.clear(); } + + //! Push an item (push on the right) + //! \return SUCCESS if item pushed + Success push(const T& e //!< The item (output) + ) override { + return this->m_extStack.push(e); + } + + //! Pop an item (remove from the right) + //! \return SUCCESS if item popped + Success pop(T& e //!< The item (output) + ) override { + return this->m_extStack.pop(e); + } + + //! Get the size (number of items stored in the stack) + //! \return The size + FwSizeType getSize() const override { return this->m_extStack.getSize(); } + + //! Get the capacity (maximum number of items stored in the stack) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_extStack.getCapacity(); } + + //! Get an item at an index. + //! Index 0 is the rightmost (latest) element in the stack. + //! Increasing indices go from right to left. + //! Fails an assertion if the index is out of range. + //! \return The item + const T& at(FwSizeType index //!< The index + ) const override { + return this->m_extStack.at(index); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The external stack implementation + ExternalStack m_extStack = {}; + + //! The array providing the backing memory for m_extStack + T m_items[C] = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/StackBase.hpp b/Fw/DataStructures/StackBase.hpp new file mode 100644 index 00000000000..435a0ebfc13 --- /dev/null +++ b/Fw/DataStructures/StackBase.hpp @@ -0,0 +1,102 @@ +// ====================================================================== +// \title StackBase +// \author bocchino +// \brief An abstract base class template for a stack +// ====================================================================== + +#ifndef Fw_StackBase_HPP +#define Fw_StackBase_HPP + +#include "Fw/DataStructures/SizedContainer.hpp" +#include "Fw/Types/Assert.hpp" +#include "Fw/Types/SuccessEnumAc.hpp" + +namespace Fw { + +template +class StackBase : public SizedContainer { + private: + // ---------------------------------------------------------------------- + // Private constructors + // ---------------------------------------------------------------------- + + //! Copy constructor deleted in the base class + //! Behavior depends on the implementation + StackBase(const StackBase&) = delete; + + protected: + // ---------------------------------------------------------------------- + // Protected constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + StackBase() : SizedContainer() {} + + //! Destructor + virtual ~StackBase() = default; + + private: + // ---------------------------------------------------------------------- + // Private member functions + // ---------------------------------------------------------------------- + + //! operator= deleted in the base class + //! Behavior depends on the implementation + //! We avoid virtual user-defined operators + StackBase& operator=(const StackBase&) = delete; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! Get an item at an index. + //! Index 0 is the rightmost (latest) element in the stack. + //! Increasing indices go from right to left. + //! Fails an assertion if the index is out of range. + //! \return The item + virtual const T& at(FwSizeType index //!< The index + ) const = 0; + + //! Copy data from another stack + void copyDataFrom(const StackBase& stack //!< The stack + ) { + if (&stack != this) { + this->clear(); + const FwSizeType size = FW_MIN(stack.getSize(), this->getCapacity()); + for (FwSizeType i = 0; i < size; i++) { + const auto& e = stack.at(size - 1 - i); + const auto status = this->push(e); + FW_ASSERT(status == Fw::Success::SUCCESS, static_cast(status)); + } + } + } + + //! Push an item (add to the right) + //! \return SUCCESS if item pushed + virtual Success push(const T& e //!< The item (output) + ) = 0; + + //! Peek an item at an index + //! Indices go from left to right in the range [0, size) + //! \return SUCCESS if item exists + Success peek(T& e, //!< The item (output) + FwSizeType index = 0 //!< The index (input) + ) const { + auto status = Success::FAILURE; + if (index < this->getSize()) { + e = this->at(index); + status = Success::SUCCESS; + } + return status; + } + + //! Pop an item (remove from the right) + //! \return SUCCESS if item popped + virtual Success pop(T& e //!< The item (output) + ) = 0; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/docs/Array.md b/Fw/DataStructures/docs/Array.md new file mode 100644 index 00000000000..361051ed1f8 --- /dev/null +++ b/Fw/DataStructures/docs/Array.md @@ -0,0 +1,253 @@ +# 1.2. Array + +`Array` is a `final` class template defined in +[`Fw/DataStructures`](sdd.md). +It represents an array with internal storage. +It maintains the backing memory _M_ as a member variable. + +## 1. Template Parameters + +`Array` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an array element| +|`FwSizeType`|`S`|The array size in elements| + +`Array` statically asserts the following: + +* `T` is default constructible. +* `S > 0`. + +## 2. Types + +`Array` defines the type `Elements`. +It is an alias of `T[S]`. + +## 3. Private Member Variables + +`Array` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_elements`|`Elements`|The array elements|C++ default initialization| + +## 4. Public Constructors and Destructors + +### 4.1. Zero-Argument Constructor + +```c++ +Array() +``` + +Defined as `= default`. + +_Example:_ +```c++ +Array a; +``` + +### 4.2. Initializer List Constructor + +```c++ +Array(const std::initializer_list& il) +``` + +1. Assert that `il.size() == S`. + +1. Initialize `m_elements` from `il`. + +_Examples:_ +```c++ +Array a({ 1, 2, 3 }); +``` + +### 4.3. Primitive Array Constructor + +```c++ +Array(const Elements& elements) +``` + +1. Statically assert that `S1 == S`. + +1. Set `*this = elements`. + +_Example:_ +```c++ +U32 elements[3] = { 1, 2, 3 }; +Array a(elements); +``` + +### 4.4. Single-Element Constructor + +```c++ +explicit Array(const T& element) +``` + +Initialize each element of `m_elements` with `element`. + +_Example:_ +```c++ +// Explicit call to constructor in variable declaration +Array a(1); +// Explicit call to constructor in assignment +a = Array(2); +``` + +### 4.5. Copy Constructor + +```c++ +Array(const Array& a) +``` + +Initialize the elements of `m_elements` with the +elements of `a.m_elements`. + +_Example:_ +```c++ +// Call the single-item constructor +Array a1(3); +// Call the copy constructor +Array a2(a1); +``` + +### 4.6. Destructor + +```c++ +~Array() +``` + +Defined as `= default`. + +## 5. Public Member Functions + +### 5.1. operator[] + +```c++ +T& operator[](FwSizeType i) +const T& operator[](FwSizeType i) const +``` + +1. Assert that `i < S`. + +1. Return `m_elements[i]`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +Array a; +// Constant access +ASSERT_EQ(a[0], 0); +// Mutable access +a[0]++; +ASSERT_EQ(a[0], 1); +// Out-of-bounds access +ASSERT_DEATH(a[size], "Assert"); +``` + +### 5.2. operator= (Initializer List) + +```c++ +Array& operator=(const std::initializer_list& il) +``` + +1. Assert that `il.size() == S`. + +1. Copy each element of `il` into `m_elements`. + +1. Return `*this`. + +_Example:_ +```c++ +Array a; +a = { 1, 2, 3 }; +``` + +### 5.3. operator= (Primitive Array) + +```c++ +Array& operator=(const Elements& elements) +``` + +1. Copy each element of `elements` into `m_elements`. + +1. Return `*this`. + +_Example:_ +```c++ +U32 elements[3] = { 1, 2, 3 }; +Array a; +a = elements; +``` + +### 5.4. operator= (Single Element) + +```c++ +Array& operator=(const T& element) +``` + +1. Copy `element` into each element of `m_elements`. + +1. Return `*this`. + +_Example:_ +```c++ +Array a; +a = 5; +``` + +### 5.5. operator= (Copy Assignment) + +```c++ +Array& operator=(const Array& a) +``` + +1. If `&a != this`, overwrite each element of `m_elements` with the +corresponding element of `a`. + +1. Return `*this`. + +_Example:_ +```c++ +Array a1(1); +Array a2(2); +a1 = a2; +``` + +### 5.6. getElements + +```c++ +Elements& getElements() +const Elements& getElements() const +``` + +Return `m_elements`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +Array a; +// Mutable reference +auto& elements1 = a.getElements(); +ASSERT_EQ(elements1[0], 0); +elements1[0] = 1; +// Constant reference +const auto& elements2 = a.getElements(); +ASSERT_EQ(elements2[0], 1); +``` + +### 5.7. asExternalArray + +```c++ +ExternalArray asExternalArray() +``` + +Return [`ExternalArray(m_elements, S)`](ExternalArray.md#Public-Constructors-and-Destructors) + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +Array a = { 1, 2, 3 }; +ExternalArray ea = a.asExternalArray(); +ASSERT_EQ(ea[0], 1); +``` diff --git a/Fw/DataStructures/docs/ArrayMap.md b/Fw/DataStructures/docs/ArrayMap.md new file mode 100644 index 00000000000..8e66a0ea29f --- /dev/null +++ b/Fw/DataStructures/docs/ArrayMap.md @@ -0,0 +1,300 @@ +# ArrayMap + +`ArrayMap` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an array-based map with internal storage. + +## 1. Template Parameters + +`ArrayMap` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`K`|The type of a key in the map| +|`typename`|`V`|The type of a value in the map| +|`FwSizeType`|`C`|The capacity, i.e., the maximum number of keys that the map can store| + +`ArrayMap` statically asserts that `C > 0`. + +## 2. Base Class + +`ArrayMap` is publicly derived from +[`MapBase`](MapBase.md). + + +## 3. Public Types + +`ArrayMap` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`MapConstIterator`](MapConstIterator.md)| +|`Entry`|Alias of [`SetOrMapImplEntry`](SetOrMapImplEntry.md)| +|`Entries`|Alias of `Entry[C]`| + +## 4. Private Member Variables + +`ArrayMap` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_extMap`|[`ExternalArrayMap`](ExternalArrayMap.md)|The external map implementation|C++ default initialization| +|`m_entries`|`Entries`|The array providing the backing memory for `m_extMap`|C++ default initialization| + +```mermaid +classDiagram + ArrayMap *-- ExternalArrayMap +``` + +## 5. Public Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +ArrayMap() +``` + +Initialize `m_extMap` with `ExternalArrayMap(m_entries, C)`. + +_Example:_ +```c++ +ArrayMap map; +``` + +### 5.2. Copy Constructor + +```c++ +ArrayMap(const ArrayMap& map) +``` + +1. Initialize `m_extMap` with `ExternalArrayMap(m_entries, C)`. + +1. Set `*this = map`. + +_Example:_ +```c++ +using Map = ArrayMap; +Map m1; +// Insert an item +const U16 key = 0; +const U32 value = 42; +const auto status = m1.insert(key, value); +ASSERT_EQ(status, Success::SUCCESS); +// Call the copy constructor +Map m2(m1); +ASSERT_EQ(m2.getSize(), 1); +``` + +### 5.3. Destructor + +```c++ +~ArrayMap() override +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. operator= + +```c++ +ArrayMap& operator=(const ArrayMap& map) +``` + +Return `m_extMap.copyDataFrom(map)`. + +_Example:_ +```c++ +using Map = ArrayMap; +Map m1; +// Insert an item +U16 key = 0; +U32 value = 42; +auto status = m1.insert(key, value); +ASSERT_EQ(status, Success::SUCCESS); +// Call the default constructor +Map m2; +ASSERT_EQ(m2.getSize(), 0); +// Call the copy assignment operator +m2 = m1; +ASSERT_EQ(m2.getSize(), 1); +value = 0; +status = m2.find(key, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 42); +``` + +### 6.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `m_extMap.begin()`. + +_Example:_ +```c++ +using Map = ArrayMap; +Map map; +// Insert an entry in the map +const auto status = map.insert(0, 1); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a map const iterator object +auto it = map.begin(); +// Use the iterator to access the underlying map const entry +const key = it->getKey(); +const value = it->getValue(); +ASSERT_EQ(key, 0); +ASSERT_EQ(value, 1); +``` + +### 6.3. clear + +```c++ +void clear() override +``` + +Call `m_extMap.clear()`. + +_Example:_ +```c++ +using Map = ArrayMap; +Map map; +const auto status = map.insert(0, 3); +ASSERT_EQ(map.getSize(), 1); +map.clear(); +ASSERT_EQ(map.getSize(), 0); +``` + +### 6.4. end + +```c++ +ConstIterator end() const +``` + +Return `m_extMap.end()`. + +_Example:_ +```c++ +using Map = ArrayMap; +// Call the constructor providing backing storage +Map map; +// Insert an entry in the map +auto status = map.insert(0, 1); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a map const iterator object +auto iter = map.begin(); +// Check that iter is not at the end +ASSERT_NE(iter, map.end()); +// Increment iter +it++; +// Check that iter is at the end +ASSERT_EQ(iter, map.end()); +``` + +### 6.5. find + +```c++ +Success find(const K& key, V& value) override +``` + +Return `m_extMap.find(key, value)`. + +_Example:_ +```c++ +using Map = ArrayMap; +Map map; +U32 value = 0; +auto status = map.find(0, value); +ASSERT_EQ(status, Success::FAILURE); +status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +status = map.find(0, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 1); +``` + +### 6.6. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_extMap.getCapacity()`. + +_Example:_ +```c++ +using Map = ArrayMap; +Map map; +ASSERT_EQ(map.getCapacity(), 10); +``` + +### 6.8. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_extMap.getSize()`. + +_Example:_ +```c++ +using Map = ArrayMap; +Map map; +auto size = map.getSize(); +ASSERT_EQ(size, 0); +const auto status = map.insert(0, 3); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.9. insert + +```c++ +Success insert(const K& key, const V& value) override +``` + +Return `m_extMap.insert(key, value)`. + +_Example:_ +```c++ +using Map = ArrayMap; +Map map; +auto size = map.getSize(); +ASSERT_EQ(size, 0); +const auto status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.10. remove + +```c++ +Success remove(const K& key, V& value) override +``` + +Return `m_extMap.remove(key, value)`. + +_Example:_ +```c++ +using Map = ArrayMap; +Map map; +auto size = map.getSize(); +ASSERT_EQ(size, 0); +auto status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +// Key does not exist +U32 value = 0; +status = map.remove(10, value); +ASSERT_EQ(status, Success::FAILURE); +ASSERT_EQ(size, 1); +// Key exists +status = map.remove(0, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(size, 0); +ASSERT_EQ(value, 1); +``` diff --git a/Fw/DataStructures/docs/ArraySet.md b/Fw/DataStructures/docs/ArraySet.md new file mode 100644 index 00000000000..43d11feec40 --- /dev/null +++ b/Fw/DataStructures/docs/ArraySet.md @@ -0,0 +1,291 @@ +# ArraySet + +`ArraySet` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an array-based set with internal storage. + +## 1. Template Parameters + +`ArraySet` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an element in the set| +|`FwSizeType`|`C`|The capacity, i.e., the maximum number of elements that the set can store| + +`ArraySet` statically asserts that `C > 0`. + +## 2. Base Class + +`ArraySet` is publicly derived from +[`SetBase`](SetBase.md). + + +## 3. Public Types + +`ArraySet` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`SetConstIterator`](SetConstIterator.md)| +|`Entry`|Alias of [`SetOrMapImplEntry`](SetOrMapImplEntry.md)| + +The type `Nil` is defined [in this file](Nil.md). + +## 4. Private Member Variables + +`ArraySet` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_extSet`|[`ExternalArraySet`](ExternalArraySet.md)|The external set implementation|C++ default initialization| +|`m_entries`|`Entry[C]`|The array providing the backing memory for `m_extSet`|C++ default initialization| + +The type `Entry` is defined [in this section](ArraySet.md#Public-Types). + +```mermaid +classDiagram + ArraySet *-- ExternalArraySet +``` + +## 5. Public Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +ArraySet() +``` + +Initialize `m_extSet` with `ExternalArraySet(m_entries, C)`. + +_Example:_ +```c++ +ArraySet set; +``` + +### 5.2. Copy Constructor + +```c++ +ArraySet(const ArraySet& set) +``` + +1. Initialize `m_extSet` with `ExternalArraySet(m_entries, C)`. + +2. Set `*this = set`. + +_Example:_ +```c++ +using Set = ArraySet; +Set s1; +// Insert an item +const U32 element = 42; +const auto status = s1.insert(element); +ASSERT_EQ(status, Success::SUCCESS); +// Call the copy constructor +Set s2; +ASSERT_EQ(s2.getSize(), 1); +``` + +### 5.3. Destructor + +```c++ +~ArraySet() override +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. operator= + +```c++ +ArraySet& operator=(const ArraySet& set) +``` + +Return `m_extSet.copyDataFrom(set)`. + +_Example:_ +```c++ +using Set = ArraySet; +Set s1; +// Insert an item +U32 element = 42; +const auto status = s1.insert(element); +ASSERT_EQ(status, Success::SUCCESS); +// Call the default constructor +Set s2; +ASSERT_EQ(s2.getSize(), 0); +// Call the copy assignment operator +s2 = s1; +ASSERT_EQ(s2.getSize(), 1); +status = s2.find(element); +ASSERT_EQ(status, Success::SUCCESS); +``` + +### 6.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `m_extSet.begin()`. + +_Example:_ +```c++ +using Set = ArraySet; +Set set; +// Insert an element in the set +const auto status = map.insert(42); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a set const iterator object +auto it = set.begin(); +// Use the iterator to access the underlying map const entry +ASSERT_EQ(*it, 42); +``` + +### 6.3. clear + +```c++ +void clear() override +``` + +Call `m_extSet.clear()`. + +_Example:_ +```c++ +using Set = ArraySet; +Set set; +const auto status = set.insert(42); +ASSERT_EQ(set.getSize(), 1); +set.clear(); +ASSERT_EQ(set.getSize(), 0); +``` + +### 6.4. end + +```c++ +ConstIterator end() const +``` + +Return `m_extSet.end()`. + +_Example:_ +```c++ +using Set = ArraySet; +// Call the constructor providing backing storage +Set set; +// Insert an element in the set +auto status = set.insert(42); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a set const iterator object +auto iter = set.begin(); +// Check that iter is not at the end +ASSERT_NE(iter, set.end()); +// Increment iter +it++; +// Check that iter is at the end +ASSERT_EQ(iter, set.end()); +``` + +### 6.5. find + +```c++ +Success find(const K& element, V& value) override +``` + +Return `m_extSet.find(element)`. + +_Example:_ +```c++ +using Set = ArraySet; +Set set; +auto status = set.find(42); +ASSERT_EQ(status, Success::FAILURE); +status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +status = set.find(42); +ASSERT_EQ(status, Success::SUCCESS); +``` + +### 6.6. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_extSet.getCapacity()`. + +_Example:_ +```c++ +using Set = ArraySet; +Set set; +ASSERT_EQ(set.getCapacity(), 10); +``` + +### 6.7. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_extSet.getSize()`. + +_Example:_ +```c++ +using Set = ArraySet; +Set set; +auto size = set.getSize(); +ASSERT_EQ(size, 0); +const auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.8. insert + +```c++ +Success insert(const T& element) override +``` + +Return `m_extSet.insert(element)`. + +_Example:_ +```c++ +using Set = ArraySet; +Set set; +auto size = set.getSize(); +ASSERT_EQ(size, 0); +const auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.9. remove + +```c++ +Success remove(const T& element) override +``` + +Return `m_extSet.remove(element)`. + +_Example:_ +```c++ +using Set = ArraySet; +Set set; +auto size = set.getSize(); +ASSERT_EQ(size, 0); +auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +// Element does not exist +status = set.remove(0); +ASSERT_EQ(status, Success::FAILURE); +ASSERT_EQ(size, 1); +// Element exists +status = set.remove(42); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(size, 0); +``` diff --git a/Fw/DataStructures/docs/ArraySetOrMapImpl.md b/Fw/DataStructures/docs/ArraySetOrMapImpl.md new file mode 100644 index 00000000000..8f68850e1c4 --- /dev/null +++ b/Fw/DataStructures/docs/ArraySetOrMapImpl.md @@ -0,0 +1,286 @@ +# ArraySetOrMapImpl + +`ArraySetOrMapImpl` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an array-based implementation of a set or map. +Internally it maintains an [`ExternalArray`](ExternalArray.md) for +storing the entries in the set or map. + +## 1. Template Parameters + +`ArraySetOrMapImpl` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`KE`|The type of a key in a map or the element of a set| +|`typename`|`VN`|The type of a value in a map or Nil for set| + + +## 2. Public Types + + +### 2.1. Type Aliases + +`ArraySetOrMapImpl` defines the following type aliases: + +|Name|Definition| +|----|----------| +|`Entry`|Alias for [`SetOrMapImplEntry`](SetOrMapImplEntry.md)| + +### 2.2. ConstIterator + +`ConstIterator` is a public inner class of `ArraySetOrMapImpl`. +It provides non-modifying iteration over the elements of an `ArraySetOrMapImpl` +instance. +It is a base class of [`SetOrMapImplConstIterator`](SetOrMapImplConstIterator.md). + +## 3. Private Member Variables + +`ArraySetOrMapImpl` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_entries`|[`ExternalArray`](ExternalArray.md)|The array for storing the set or map entries|C++ default initialization| +|`m_size`|`FwSizeType`|The number of entries in the set or map|0| + +```mermaid +classDiagram + ArraySetOrMapImpl *-- ExternalArray + ExternalArray *-- "1..*" Entry +``` + +## 4. Public Constructors and Destructors + +### 4.1. Zero-Argument Constructor + +```c++ +ArraySetOrMapImpl() +``` + +Initialize each member variable with its default value. + +### 4.2. Constructor Providing Typed Backing Storage + +```c++ +ArraySetOrMapImpl(Entry* entries, FwSizeType capacity) +``` + +Call `setStorage(entries, capacity)`. + +### 4.3. Constructor Providing Untyped Backing Storage + +```c++ +ArraySetOrMapImpl(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(size)`](#getByteArraySize) bytes. + +Call `setStorage(data, capacity)`. + +### 4.4. Copy Constructor + +```c++ +ArraySetOrMapImpl(const ArraySetOrMapImpl& map) +``` + +Set `*this = map`. + +### 4.5. Destructor + +```c++ +~ArraySetOrMapImpl() +``` + +Defined as `= default`. + +## 5. Public Member Functions + +### 5.1. operator= + +```c++ +ArraySetOrMapImpl& operator=(const ArraySetOrMapImpl& impl) +``` + +1. If `&impl != this` + + 1. Set `m_entries = impl.m_entries`. + + 1. Set `m_size = impl.m_size`. + +1. Return `*this`. + +### 5.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `ConstIterator(*this)`. + +### 5.3. clear + +```c++ +void clear() +``` + +Set `m_size = 0`. + +### 5.4. end + +```c++ +ConstIterator end() const +``` + +1. Set `it = begin()`. + +1. Call `it.setToEnd()`. + +1. Return `it`. + +### 5.5. find + +```c++ +Success find(const KE& keyOrElement, VN& valueOrNil) const +``` + +1. Set `status = Success::FAILURE`. + +1. For `i` in `[0, m_size)` + + 1. Let `const auto& e = m_entries[i]`. + + 1. If `e.getKey() == keyOrElement` + + 1. Set `valueOrNil = e.getValue()`. + + 1. Set `status = Success::SUCCESS`. + + 1. Break out of the loop. + +1. Return `status`. + +### 5.6. getCapacity + +```c++ +FwSizeType getCapacity() const +``` + +Return `m_entries.getSize()`. + +### 5.8. getSize + +```c++ +FwSizeType getSize() +``` + +Return `m_size`. + +### 5.9. insert + +```c++ +Success insert(const KE& keyOrElement, const VN& valueOrNil) +``` + +1. Set `status = Success::FAILURE`. + +1. For `i` in `[0, m_size)` + + 1. Let `auto& e = m_entries[i]`. + + 1. If `e.getKey() == keyOrElement` + + 1. Call `e.setValue(valueOrNil)`. + + 1. Set `status = Success::SUCCESS`. + + 1. Break out of the loop + +1. If `(status == Success::FAILURE) && (m_size < getCapacity())` + + 1. Set `m_entries[m_size] = Entry(keyOrElement, valueOrNil)`. + + 1. If `m_size > 0` then + call `m_entries[m_size - 1].setNextEntry(&m_entries[m_size])`. + + 1. Increment `m_size`. + + 1. Set `status = Success::SUCCESS`. + +1. Return `status`. + +### 5.10. remove + +```c++ +Success remove(const KE& keyOrElement, VN& valueOrNil) +``` + +1. Set `status = Success::FAILURE`. + +1. For `i` in `[0, m_size)` + + 1. If `m_entries[i].getKey() == keyOrElement` + + 1. Set `valueOrNil = m_entries[i].getValue()`. + + 1. If `i < m_size - 1` then + + 1. `m_entries[i] = m_entries[m_size - 1]`. + + 1. Call `m_entries[i].setNextEntry(&m_entries[i + 1])`. + + 1. Otherwise call `m_entries[i].setNextEntry(nullptr)`. + + 1. Decrement `size`. + + 1. Set `status = Success::SUCCESS`. + + 1. Break out of the loop. + +1. Return `status`. + +### 5.11. setStorage (Typed Data) + +```c++ +void setStorage(Entry* entries, FwSizeType capacity) +``` + +1. Call `m_entries.setStorage(entries, capacity)`. + +1. Call `clear()`. + +### 5.12. setStorage (Untyped Data) + +```c++ +void setStorage(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(size)`](#getByteArraySize) bytes. + +1. Call `m_entries.setStorage(data, capacity)`. + +1. Call `clear()`. + +## 6. Public Static Functions + + +### 6.1. getByteArrayAlignment + +```c++ +static constexpr U8 getByteArrayAlignment() +``` + +Return `ExternalArray::getByteArrayAlignment()`. + + +### 6.2. getByteArraySize + +```c++ +static constexpr FwSizeType getByteArraySize(FwSizeType capacity) +``` + +Return `ExternalArray::getByteArraySize(capacity)`. diff --git a/Fw/DataStructures/docs/CircularIndex.md b/Fw/DataStructures/docs/CircularIndex.md new file mode 100644 index 00000000000..13afeaad127 --- /dev/null +++ b/Fw/DataStructures/docs/CircularIndex.md @@ -0,0 +1,138 @@ +# CircularIndex + +`CircularIndex` is a `final` class defined in +[`Fw/DataStructures`](sdd.md). +It represents an index value that +wraps around modulo an integer. + +## 1. Private Member Variables + +`CircularIndex` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_value`|`FwSizeType`|The index value|0| +|`m_modulus`|`FwSizeType`|The modulus|1| + +## 2. Public Constructors and Destructors + +### 2.1. Zero-Argument Constructor + +```c++ +CircularIndex() +``` + +Use default initialization of members. + +### 2.2. Constructor with Specified Members + +```c++ +explicit CircularIndex(FwSizeType modulus, FwSizeType value = 0) +``` + +1. Assert `modulus > 0`. + +1. Set `m_modulus = modulus`. + +1. Call `setValue(value)`. + +### 2.3. Copy Constructor + +```c++ +CircularIndex(const CircularIndex& ci) +``` + +Set `*this = ci`. + +### 2.4. Destructor + +```c++ +~CircularIndex() +``` + +Defined as `= default`. + +## 3. Public Member Functions + +### 3.1. operator= + +```c++ +CircularIndex& operator=(const CircularIndex& ci) +``` + +1. If `this != &ci` + + 1. Set `m_value = ci.m_value`. + + 1. Set `m_modulus = ci.m_modulus`. + +1. Return `*this`. + +### 3.2. getValue + +```c++ +FwSizeType getValue() const +``` + +1. Assert `m_value < m_modulus`. + +1. Return `m_value`. + +### 3.3. setValue + +```c++ +void setValue(FwSizeType value) +``` + +1. Assert `m_modulus > 0`. + +2. Set `m_value = m_value % m_modulus`. + +### 3.4. getModulus + +```c++ +FwSizeType CircularIndex::getModulus() const +``` + +1. Assert `m_value < m_modulus`. + +1. Return `m_modulus`. + +### 3.5. setModulus + +```c++ +void setModulus(FwSizeType modulus) +``` + +1. Set `m_modulus = modulus`. + +2. Call `setValue(m_value)`. + +### 3.6. increment + +```c++ +FwSizeType increment(FwSizeType amount = 1) +``` + +1. Assert `m_modulus > 0`. + +1. Set `offset = amount % m_modulus`. + +1. Call `setValue(m_value + offset)`. + +1. Return `m_value`. + +### 3.7. decrement + +```c++ +FwSizeType decrement(FwSizeType amount = 1) +``` + +1. Assert `m_modulus > 0`. + +1. Set `offset = amount % m_modulus`. + +1. Call `setValue(m_value + m_modulus - offset)`. + +1. Return `m_value`. + diff --git a/Fw/DataStructures/docs/ExternalArray.md b/Fw/DataStructures/docs/ExternalArray.md new file mode 100644 index 00000000000..0147d3888c8 --- /dev/null +++ b/Fw/DataStructures/docs/ExternalArray.md @@ -0,0 +1,304 @@ +# ExternalArray + +`ExternalArray` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an +[array](sdd.md#1-arrays) with external storage. +It stores a pointer to the backing memory _M_. + +## 1. Template Parameters + +`ExternalArray` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an array element| + +`Array` statically asserts that `T` is assignable to `T&`. + +## 2. Private Member Variables + +`ExternalArray` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_elements`|`T*`|Pointer to backing memory or `nullptr`|`nullptr`| +|`m_size`|`FwSizeType`|Stores the size (number of elements) of the array|0| + + +## 3. Public Constructors and Destructors + +### 3.1. Zero-Argument Constructor + +```c++ +ExternalArray() +``` + +Initialize the member variables with their default values. + +_Example:_ +```c++ +ExternalArray a; +``` + +### 3.2. Constructor Providing Typed Backing Storage + +```c++ +ExternalArray(T* elements, FwSizeType size) +``` + +`elements` must point to a primitive array of at least `size` +elements of type `T`. + +Call `setStorage(elements, size)`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +U32 elements[size]; +ExternalArray a(elements, size); +``` + +### 3.3. Constructor Providing Untyped Backing Storage + +```c++ +ExternalArray(ByteArray data, FwSizeType size) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#51-getbytearrayalignment) and must +contain at least [`getByteArraySize(size)`](#52-getbytearraysize) bytes. + +Call `setStorage(data, size)`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +constexpr U8 alignment = ExternalArray::byteArrayAlignment(); +constexpr FwSizeType byteArraySize = ExternalArray::getByteArraySize(size); +alignas(alignment) U8 bytes[byteArraySize]; +ExternalArray a(ByteArray(&bytes[0], sizeof bytes), size); +``` + +### 3.4. Copy Constructor + +```c++ +ExternalArray(const ExternalArray& a) +``` + +Set `m_elements = a.m_elements` and `m_size = a.m_size`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +U32 elements[size]; +// Call the constructor providing backing storage +ExternalArray a1(elements, size); +// Call the copy constructor +ExternalArray a2(a1); +``` + +### 3.5. Destructor + +```c++ +~ExternalArray() +``` + +Defined as `= default`. + +## 4. Public Member Functions + +### 4.1. operator[] + +```c++ +T& operator[](FwSizeType i) +const T& operator[](FwSizeType i) const +``` + +1. Assert that `m_elements != nullptr`. + +1. Assert that `i < m_size`. + +1. Return `m_elements[i]`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +U32 elements[size] = {}; +ExternalArray a(elements, size); +// Constant access +ASSERT_EQ(a[0], 0); +// Mutable access +a[0]++; +ASSERT_EQ(a[0], 1); +// Out-of-bounds access +ASSERT_DEATH(a[size], "Assert"); +``` + +### 4.2. operator= + +```c++ +ExternalArray& operator=(const ExternalArray& a) +``` + +1. If `&a == this` then do nothing. + +1. Otherwise set `m_elements = a.m_elements` and `m_size = a.m_size`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +U32 elements[size]; +ExternalArray a1(elements, size); +ExternalArray a2; +a2 = a1; +``` + +### 4.3. copyDataFrom + +```c++ +void copyDataFrom(const ExternalArray& a) +``` + +1. If `&a != this` + + 1. Let `size` be the minimum of `m_size` and `a.m_size` + + 1. For each `i` in [0, `size`), set `m_elements[i] = a.m_elements[i]` + +_Example:_ +```c++ +constexpr FwSizeType size = 10; +U32 elements1[size]; +ExternalArray a1(elements, size); +for (FwSizeType i = 0; i < size; i++) { + a1[i] = i; +} +U32 elements2[size]; +ExternalArray a2(elements, size); +a2.copyDataFrom(a1); +for (FwSizeType i = 0; i < size; i++) { + ASSERT_EQ(a2[i], a1[i]); +} +``` + +### 4.4. getElements + +```c++ +T* getElements() +const T* getElements() const +``` + +Return `m_elements`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +U32 elements[size]; +ExternalArray a(elements, size); +// Mutable pointer +auto& elements1 = a.getElements(); +ASSERT_EQ(elements1[0], 0); +elements1[0] = 1; +// Constant pointer +const auto& elements2 = a.getElements(); +ASSERT_EQ(elements2[0], 1); +``` + +### 4.5. getSize + +```c++ +FwSizeType getSize() +``` + +Return `m_size`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +U32 elements[size]; +ExternalArray a(elements, size); +const auto size1 = a.getSize(); +ASSERT_EQ(size1, size); +``` + +### 4.6. setStorage (Typed Data) + +```c++ +void setStorage(T* elements, FwSizeType size) +``` + +`elements` must point to a primitive array of at least `size` +elements of type `T`. + +Set `m_elements = elements` and `m_size = size`. + +_Example:_ +```c++ +ExternalArray a; +constexpr FwSizeType size = 3; +U32 elements[size]; +a.setStorage(elements, size); +``` + +### 4.7. setStorage (Untyped Data) + +```c++ +void setStorage(ByteArray data, FwSizeType size) +``` + +`data` must be correctly aligned according to [`getByteArrayAlignment`](#51-getbytearrayalignment) +and must contain at least [`getByteArraySize(size)`](#52-getbytearraysize) bytes. + +1. Assert that `data.bytes != nullptr`. + +1. Assert that `data.bytes` is correctly aligned for type `T`. + +1. Assert that `size * sizeof(T) <= data.size`. + +1. Initialize `m_elements` with `data.bytes`. + +1. Construct the objects pointed to by `m_elements` in place. + +1. Initialize `m_size` with `size`. + +_Example:_ +```c++ +constexpr FwSizeType size = 3; +constexpr U8 alignment = ExternalArray::byteArrayAlignment(); +constexpr FwSizeType byteArraySize = ExternalArray::getByteArraySize(size); +alignas(alignment) U8 bytes[byteArraySize]; +ExternalArray a; +a.setStorage(ByteArray(&bytes[0], sizeof bytes), size); +``` + +## 5. Public Static Functions + +### 5.1. getByteArrayAlignment + +```c++ +static constexpr U8 getByteArrayAlignment() +``` + +Return `alignof(T)`. + +_Example:_ +```c++ +const U8 byteArrayAlignment = ExternalArray::getByteArrayAlignment(size); +ASSERT_EQ(byteArrayAlignment, alignof(U32)); +``` + +### 5.2. getByteArraySize + +```c++ +static constexpr FwSizeType getByteArraySize(FwSizeType size) +``` + +Return `size * sizeof(T)`. + +_Example:_ +```c++ +const FwSizeType size = 10; +const FwSizeType byteArraySize = ExternalArray::getByteArraySize(size); +ASSERT_EQ(byteArraySize, 10 * sizeof(U32)); +``` diff --git a/Fw/DataStructures/docs/ExternalArrayMap.md b/Fw/DataStructures/docs/ExternalArrayMap.md new file mode 100644 index 00000000000..48cdb32431a --- /dev/null +++ b/Fw/DataStructures/docs/ExternalArrayMap.md @@ -0,0 +1,428 @@ +# ExternalArrayMap + +`ExternalArrayMap` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an array-based map with external storage. +Internally it maintains an [`ArraySetOrMapImpl`](ArraySetOrMapImpl.md) +as the map implementation. + +## 1. Template Parameters + +`ExternalArrayMap` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`K`|The type of a key in the map| +|`typename`|`V`|The type of a value in the map| + +## 2. Base Class + +`ExternalArrayMap` is publicly derived from +[`MapBase`](MapBase.md). + + +## 3. Public Types + +`ExternalArrayMap` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`MapConstIterator`](MapConstIterator.md)| +|`Entry`|Alias of [`SetOrMapImplEntry`](SetOrMapImplEntry.md)| + +## 4. Private Member Variables + +`ExternalArrayMap` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_impl`|[`ArraySetOrMapImpl`](ArraySetOrMapImpl.md)|The map implementation|C++ default initialization| + +```mermaid +classDiagram + ExternalArrayMap *-- ArraySetOrMapImpl +``` + +## 5. Public Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +ExternalArrayMap() +``` + +Initialize each member variable with its default value. + +_Example:_ +```c++ +ExternalArrayMap map; +``` + +### 5.2. Constructor Providing Typed Backing Storage + +```c++ +ExternalArrayMap(Entry* entries, FwSizeType capacity) +``` + +`entries` must point to a primitive array of at least `capacity` +elements of type [`Entry`](ExternalArrayMap.md#Public-Types). + +Call `setStorage(entries, capacity)`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +``` + +### 5.3. Constructor Providing Untyped Backing Storage + +```c++ +ExternalArrayMap(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +Call `setStorage(data, capacity)`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = Map::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = Map::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +Map map(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +### 5.4. Copy Constructor + +```c++ +ExternalArrayMap(const ExternalArrayMap& map) +``` + +Set `*this = map`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 3; +Map::Entry entries[capacity]; +// Call the constructor providing backing storage +Map m1(entries, capacity); +// Insert an item +const U16 key = 0; +const U32 value = 42; +const auto status = m1.insert(key, value); +ASSERT_EQ(status, Success::SUCCESS); +// Call the copy constructor +Map m2(m1); +ASSERT_EQ(m2.getSize(), 1); +``` + +### 5.5. Destructor + +```c++ +~ExternalArrayMap() override +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. operator= + +```c++ +ExternalArrayMap& operator=(const ExternalArrayMap& map) +``` + +1. If `&map != this` + + 1. Set `m_impl = map.m_impl`. + +1. Return `*this`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 3; +Map::Entry entries[capacity]; +// Call the constructor providing backing storage +Map m1(entries, capacity); +// Insert an item +U16 key = 0; +U32 value = 42; +const auto status = m1.insert(key, value); +ASSERT_EQ(status, Success::SUCCESS); +// Call the default constructor +ExternalArrayMap m2; +ASSERT_EQ(m2.getSize(), 0); +// Call the copy assignment operator +m2 = m1; +ASSERT_EQ(m2.getSize(), 1); +value = 0; +status = m2.find(key, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 42); +``` + +### 6.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `m_impl.begin()`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +// Call the constructor providing backing storage +Map map(entries, capacity); +// Insert an entry in the map +const auto status = map.insert(0, 1); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a map const iterator object +auto it = map.begin(); +// Use the iterator to access the underlying map const entry +const auto key = it->getKey(); +const auto value = it->getValue(); +ASSERT_EQ(key, 0); +ASSERT_EQ(value, 1); +``` + +### 6.3. clear + +```c++ +void clear() override +``` + +Call `m_impl.clear()`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +const auto status = map.insert(0, 3); +ASSERT_EQ(map.getSize(), 1); +map.clear(); +ASSERT_EQ(map.getSize(), 0); +``` + +### 6.4. end + +```c++ +ConstIterator end() const +``` + +Return `m_impl.end()`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +// Call the constructor providing backing storage +Map map(entries, capacity); +// Insert an entry in the map +auto status = map.insert(0, 1); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a map const iterator object +auto iter = map.begin(); +// Check that iter is not at the end +ASSERT_NE(iter, map.end()); +// Increment iter +it++; +// Check that iter is at the end +ASSERT_EQ(iter, map.end()); +``` + +### 6.5. find + +```c++ +Success find(const K& key, V& value) override +``` + +Return `m_impl.find(key, value)`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +U32 value = 0; +auto status = map.find(0, value); +ASSERT_EQ(status, Success::FAILURE); +status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +status = map.find(0, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 1); +``` + +### 6.6. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_impl.getCapacity()`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +ASSERT_EQ(map.getCapacity(), capacity); +``` + +### 6.7. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_impl.getSize()`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +auto size = map.getSize(); +ASSERT_EQ(size, 0); +const auto status = map.insert(0, 3); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.8. insert + +```c++ +Success insert(const K& key, const V& value) override +``` + +Return `m_impl.insert(key, value)`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +auto size = map.getSize(); +ASSERT_EQ(size, 0); +const auto status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.9. remove + +```c++ +Success remove(const K& key, V& value) override +``` + +Return `m_impl.remove(key, value)`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +auto size = map.getSize(); +ASSERT_EQ(size, 0); +auto status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +// Key does not exist +U32 value = 0; +status = map.remove(10, value); +ASSERT_EQ(status, Success::FAILURE); +ASSERT_EQ(size, 1); +// Key exists +status = map.remove(0, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(size, 0); +ASSERT_EQ(value, 1); +``` + +### 6.10. setStorage (Typed Data) + +```c++ +void setStorage(Entry* entries, FwSizeType capacity) +``` + +`entries` must point to a primitive array of at least `capacity` +elements of type `Entry`. +The type `Entry` is defined [in this section](ExternalArrayMap.md#Public-Types). + +Call `m_impl.setStorage(entries, capacity)`. + +_Example:_ +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +Map map; +Map::Entry entries[capacity]; +map.setStorage(entries, capacity); +``` + +### 6.11. setStorage (Untyped Data) + +```c++ +void setStorage(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +1. Call `m_entries.setStorage(data, capacity)`. + +1. Call `clear()`. + +```c++ +using Map = ExternalArrayMap; +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = Map::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = Map::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +Map map; +map.setStorage(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +## 7. Public Static Functions + + +### 7.1. getByteArrayAlignment + +```c++ +static constexpr U8 getByteArrayAlignment() +``` + +Return `ArraySetOrMapImpl::getByteArrayAlignment()`. + + +### 7.2. getByteArraySize + +```c++ +static constexpr FwSizeType getByteArraySize(FwSizeType capacity) +``` + +Return `ArraySetOrMapImpl::getByteArraySize(capacity)`. diff --git a/Fw/DataStructures/docs/ExternalArraySet.md b/Fw/DataStructures/docs/ExternalArraySet.md new file mode 100644 index 00000000000..f6747db8099 --- /dev/null +++ b/Fw/DataStructures/docs/ExternalArraySet.md @@ -0,0 +1,421 @@ +# ExternalArraySet + +`ExternalArraySet` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an array-based set with external storage. +Internally it maintains an [`ArraySetOrMapImpl`](ArraySetOrMapImpl.md) +as the set implementation. + +## 1. Template Parameters + +`ExternalArraySet` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an element in the set| + +## 2. Base Class + +`ExternalArraySet` is publicly derived from +[`SetBase`](SetBase.md). + + +## 3. Public Types + +`ExternalArraySet` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`MapConstIterator`](MapConstIterator.md)| +|`Entry`|Alias of [`SetOrMapImplEntry`](SetOrMapImplEntry.md)| + +The type `Nil` is defined [in this file](Nil.md). + +## 4. Private Member Variables + +`ExternalArraySet` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_impl`|[`ArraySetOrMapImpl`](ArraySetOrMapImpl.md)|The set implementation|C++ default initialization| + +The type `Nil` is defined [in this file](Nil.md). + +```mermaid +classDiagram + ExternalArraySet *-- ArraySetOrMapImpl +``` + +## 5. Public Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +ExternalArraySet() +``` + +Initialize each member variable with its default value. + +_Example:_ +```c++ +ExternalArraySet set; +``` + +### 5.2. Constructor Providing Typed Backing Storage + +```c++ +ExternalArraySet(Entry* entries, FwSizeType capacity) +``` + +`entries` must point to a primitive array of at least `capacity` +elements of type `Entry`. +The type `Entry` is defined [in this section](ExternalArraySet.md#Public-Types). + +Call `setStorage(entries, capacity)`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +``` + +### 5.3. Constructor Providing Untyped Backing Storage + +```c++ +ExternalArraySet(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +Call `setStorage(data, capacity)`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = Set::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = Set::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +ExternalArraySet set(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +### 5.4. Copy Constructor + +```c++ +ExternalArraySet(const ExternalArraySet& set) +``` + +Set `*this = set`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 3; +Set::Entry entries[capacity]; +// Call the constructor providing backing storage +Set m1(entries, capacity); +// Insert an item +const auto status = m1.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +// Call the copy constructor +Set m2(m1); +ASSERT_EQ(m2.getSize(), 1); +``` + +### 5.5. Destructor + +```c++ +~ExternalArraySet() override +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. operator= + +```c++ +ExternalArraySet& operator=(const ExternalArraySet& set) +``` + +1. If `&set != this` + + 1. Set `m_impl = set.m_impl`. + +1. Return `*this`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 3; +Set::Entry entries[capacity]; +// Call the constructor providing backing storage +Set m1(entries, capacity); +// Insert an item +const auto status = m1.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +// Call the default constructor +Set m2; +ASSERT_EQ(m2.getSize(), 0); +// Call the copy assignment operator +m2 = m1; +ASSERT_EQ(m2.getSize(), 1); +``` + +### 6.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `m_impl.begin()`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +// Call the constructor providing backing storage +Set set(entries, capacity); +// Insert an entry in the set +const auto status = set.insert(42); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a set const iterator object +auto it = set.begin(); +// Use the iterator to access the element +ASSERT_EQ(*it, 42); +``` + +### 6.3. clear + +```c++ +void clear() override +``` + +Call `m_impl.clear()`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +const auto status = set.insert(42); +ASSERT_EQ(set.getSize(), 1); +set.clear(); +ASSERT_EQ(set.getSize(), 0); +``` + +### 6.4. end + +```c++ +ConstIterator end() const +``` + +Return `m_impl.end()`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +// Call the constructor providing backing storage +Set set(entries, capacity); +// Insert an entry in the set +auto status = set.insert(42); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a set const iterator object +auto iter = set.begin(); +// Check that iter is not at the end +ASSERT_NE(iter, set.end()); +// Increment iter +it++; +// Check that iter is at the end +ASSERT_EQ(iter, set.end()); +``` + +### 6.5. find + +```c++ +Success find(const T& element) override +``` + +1. Set `Nil nil = {}`. + +1. Return `m_impl.find(key, nil)`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +auto status = set.find(42); +ASSERT_EQ(status, Success::FAILURE); +status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +status = set.find(42); +ASSERT_EQ(status, Success::SUCCESS); +``` + +### 6.6. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_impl.getCapacity()`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +ASSERT_EQ(set.getCapacity(), capacity); +``` + +### 6.8. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_impl.getSize()`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +auto size = set.getSize(); +ASSERT_EQ(size, 0); +const auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.9. insert + +```c++ +Success insert(const T& element) override +``` + +Return `m_impl.insert(key, Nil())`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +auto size = set.getSize(); +ASSERT_EQ(size, 0); +const auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.10. remove + +```c++ +Success remove(const T& element) override +``` + +1. Set `Nil nil = {}`. + +1. Return `m_impl.remove(key, nil)`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +auto size = set.getSize(); +ASSERT_EQ(size, 0); +auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +// Element does not exist +status = set.remove(0); +ASSERT_EQ(status, Success::FAILURE); +ASSERT_EQ(size, 1); +// Element exists +status = set.remove(42); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(size, 0); +``` + +### 6.11. setStorage (Typed Data) + +```c++ +void setStorage(Entry* entries, FwSizeType capacity) +``` + +`entries` must point to a primitive array of at least `capacity` +elements of type `Entry`. +The type `Entry` is defined [in this section](ExternalArraySet.md#Public-Types). + +Call `m_impl.setStorage(entries, capacity)`. + +_Example:_ +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +Set set; +Set::Entry entries[capacity]; +set.setStorage(entries, capacity); +``` + +### 6.12. setStorage (Untyped Data) + +```c++ +void setStorage(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +1. Call `m_entries.setStorage(data, capacity)`. + +1. Call `clear()`. + +```c++ +using Set = ExternalArraySet; +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = Set::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = Set::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +Set set; +set.setStorage(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +## 7. Public Static Functions + + +### 7.1. getByteArrayAlignment + +```c++ +static constexpr U8 getByteArrayAlignment() +``` + +Return `ArraySetOrMapImpl::getByteArrayAlignment()`. + + +### 7.2. getByteArraySize + +```c++ +static constexpr FwSizeType getByteArraySize(FwSizeType capacity) +``` + +Return `ArraySetOrMapImpl::getByteArraySize(capacity)`. diff --git a/Fw/DataStructures/docs/ExternalFifoQueue.md b/Fw/DataStructures/docs/ExternalFifoQueue.md new file mode 100644 index 00000000000..86e377db616 --- /dev/null +++ b/Fw/DataStructures/docs/ExternalFifoQueue.md @@ -0,0 +1,391 @@ +# ExternalFifoQueue + +`ExternalFifoQueue` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents a FIFO queue with external storage. +Internally it maintains an [`ExternalArray`](ExternalArray.md) for +storing the items on the queue. + +## 1. Template Parameters + +`ExternalFifoQueue` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an item on the queue| + +## 2. Base Class + +`ExternalFifoQueue` is publicly derived from +[`FifoQueueBase`](FifoQueueBase.md). + +## 3. Private Member Variables + +`ExternalFifoQueue` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_items`|[`ExternalArray`](ExternalArray.md)|The array for storing the queue items|C++ default initialization| +|`m_enqueueIndex`|[`CircularIndex`](CircularIndex.md)|The enqueue index|`CircularIndex(m_items.size(), 0)`| +|`m_dequeueIndex`|[`CircularIndex`](CircularIndex.md)|The dequeue index|`CircularIndex(m_items.size(), 0)`| +|`m_size`|`FwSizeType`|The number of items on the queue|0| + +```mermaid +classDiagram + FifoQueue *-- ExternalArray + FifoQueue *-- CircularIndex + FifoQueue *-- CircularIndex +``` + +## 4. Public Constructors and Destructors + +### 4.1. Zero-Argument Constructor + +```c++ +ExternalFifoQueue() +``` + +Initialize each member variable with its default value. + +_Example:_ +```c++ +ExternalFifoQueue queue; +``` + +### 4.2. Constructor Providing Typed Backing Storage + +```c++ +ExternalFifoQueue(T* items, FwSizeType capacity) +``` + +`items` must point to a primitive array of at least `capacity` +items of type `T`. + +1. Call `setStorage(items, capacity)`. + +1. Initialize the other member variables with their default values. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +U32 items[capacity]; +ExternalFifoQueue queue(items, capacity); +``` + +### 4.3. Constructor Providing Untyped Backing Storage + +```c++ +ExternalFifoQueue(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +1. Call `setStorage(data, capacity)`. + +1. Initialize the other member variables with their default values. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = ExternalFifoQueue::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = ExternalFifoQueue::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +ExternalFifoQueue queue(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +### 4.4. Copy Constructor + +```c++ +ExternalFifoQueue(const ExternalFifoQueue& queue) +``` + +Set `*this = queue`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +// Call the constructor providing backing storage +ExternalFifoQueue q1(items, capacity); +// Enqueue an item +U32 value = 42; +(void) q1.enqueue(value); +// Call the copy constructor +ExternalFifoQueue q2(q1); +ASSERT_EQ(q2.getSize(), 1); +``` + +### 4.5. Destructor + +```c++ +~ExternalFifoQueue() override +``` + +Defined as `= default`. + +## 5. Public Member Functions + +### 5.1. operator= + +```c++ +ExternalFifoQueue& operator=(const ExternalFifoQueue& queue) +``` + +1. If `&queue != this` + + 1. Set `m_items = queue.m_items`. + + 1. Set `m_enqueueIndex = queue.m_enqueueIndex`. + + 1. Set `m_dequeueIndex = queue.m_dequeueIndex`. + + 1. Set `m_size = queue.m_size`. + +1. Return `*this`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +// Call the constructor providing backing storage +ExternalFifoQueue q1(items, capacity); +// Enqueue an item +U32 value = 42; +(void) q1.enqueue(value); +// Call the default constructor +ExternalFifoQueue q2; +ASSERT_EQ(q2.getSize(), 0); +// Call the copy assignment operator +q2 = q1; +ASSERT_EQ(q2.getSize(), 1); +``` + +### 5.2. clear + +```c++ +void clear() override +``` + +1. Call `m_enqueueIndex.setValue(0)`. + +1. Call `m_dequeueIndex.setValue(0)`. + +1. Set `m_size = 0`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +U32 items[capacity]; +ExternalFifoQueue queue(items, capacity); +const auto status = queue.enqueue(3); +ASSERT_EQ(queue.getSize(), 1); +queue.clear(); +ASSERT_EQ(queue.getSize(), 0); +``` + +### 5.3. setStorage (Typed Data) + +```c++ +void setStorage(T* items, FwSizeType capacity) +``` + +`items` must point to a primitive array of at least `capacity` +items of type `T`. + +1. Call `m_items.setStorage(items, capacity)`. + +1. If `capacity > 0` + + 1. Call `this->m_enqueueIndex.setModulus(capacity)`. + + 1. Call `this->m_dequeueIndex.setModulus(capacity)`. + +1. Call `this->clear()`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +ExternalFifoQueue queue; +U32 items[capacity]; +queue.setStorage(items, capacity); +``` + +### 5.4. setStorage (Untyped Data) + +```c++ +void setStorage(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +1. Call `m_items.setStorage(data, capacity)`. + +1. If `capacity > 0` + + 1. Call `this->m_enqueueIndex.setModulus(capacity)`. + + 1. Call `this->m_dequeueIndex.setModulus(capacity)`. + +1. Call `this->clear()`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = ExternalFifoQueue::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = ExternalFifoQueue::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +ExternalFifoQueue queue; +queue.setStorage(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +### 5.5. enqueue + +```c++ +Success enqueue(const T& e) override +``` + +1. Set `status = Success::FAILURE`. + +1. If `m_size < getCapacity()` then + + 1. Set `i = m_enqueueIndex.getValue()`. + + 1. Set `m_items[i] = e`. + + 1. Call `m_enqueueIndex.increment()`. + + 1. Increment `m_size`. + +1. Return `status`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +ExternalFifoQueue queue(items, capacity); +ASSERT_EQ(queue.getSize(), 0); +auto status = queue.enqueue(42); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(queue.getSize(), 1); +``` + +### 5.6. at + +```c++ +const T& at(FwSizeType index) const override +``` + +1. Assert `index < m_size`. + +1. Set `ci = m_dequeueIndex`. + +1. Set `i = ci.increment(index)`. + +1. Return `m_items[i]`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +ExternalFifoQueue queue(items, capacity); +const auto status = queue.enqueue(3); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(queue.at(0), 3); +ASSERT_DEATH(queue.at(1), "Assert"); +``` + +### 5.7. dequeue + +```c++ +Success dequeue(T& e) override +``` + +1. Set `status = Success::FAILURE`. + +1. If `m_size > 0` then + + 1. Set `i = m_dequeueIndex.getValue()`. + + 1. Set `e = m_items[i]`. + + 1. Call `m_dequeueIndex.increment()`. + + 1. Decrement `m_size`. + +1. Return `status`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +ExternalFifoQueue queue(items, capacity); +U32 val; +auto status = queue.dequeue(val); +ASSERT_EQ(status, Success::FAILURE); +status = queue.enqueue(42); +ASSERT_EQ(status, Success::SUCCESS); +status = queue.dequeue(val); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(val, 42); +``` + +### 5.8. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_size`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +U32 items[capacity]; +ExternalFifoQueue queue(items, capacity); +auto size = queue.getSize(); +ASSERT_EQ(size, 0); +const auto status = queue.enqueue(3); +ASSERT_EQ(status, Success::SUCCESS); +size = queue.getSize(); +ASSERT_EQ(size, 1); +``` + +### 5.9. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_items.getSize()`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +U32 items[capacity]; +ExternalFifoQueue queue(items, capacity); +ASSERT_EQ(queue.getCapacity(), capacity); +``` + +## 6. Public Static Functions + + +### 6.1. getByteArrayAlignment + +```c++ +static constexpr U8 getByteArrayAlignment() +``` + +Return `ExternalArray::getByteArrayAlignment()`. + + +### 6.2. getByteArraySize + +```c++ +static constexpr FwSizeType getByteArraySize(FwSizeType capacity) +``` + +Return `ExternalArray::getByteArraySize(capacity)`. diff --git a/Fw/DataStructures/docs/ExternalRedBlackTreeMap.md b/Fw/DataStructures/docs/ExternalRedBlackTreeMap.md new file mode 100644 index 00000000000..52e6a22c9a8 --- /dev/null +++ b/Fw/DataStructures/docs/ExternalRedBlackTreeMap.md @@ -0,0 +1,4 @@ +# ExternalRedBlackTreeMap + +TODO + diff --git a/Fw/DataStructures/docs/ExternalRedBlackTreeSet.md b/Fw/DataStructures/docs/ExternalRedBlackTreeSet.md new file mode 100644 index 00000000000..eaf1581452a --- /dev/null +++ b/Fw/DataStructures/docs/ExternalRedBlackTreeSet.md @@ -0,0 +1,4 @@ +# RedBlackTreeSet + +TODO + diff --git a/Fw/DataStructures/docs/ExternalStack.md b/Fw/DataStructures/docs/ExternalStack.md new file mode 100644 index 00000000000..98533e29994 --- /dev/null +++ b/Fw/DataStructures/docs/ExternalStack.md @@ -0,0 +1,355 @@ +# ExternalStack + +`ExternalStack` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents a stack with external storage. +Internally it maintains an [`ExternalArray`](ExternalArray.md) for +storing the items on the stack. + +## 1. Template Parameters + +`ExternalStack` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an item on the stack| + +## 2. Base Class + +`ExternalStack` is publicly derived from +[`StackBase`](StackBase.md). + +## 3. Private Member Variables + +`ExternalStack` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_items`|[`ExternalArray`](ExternalArray.md)|The array for storing the stack items|C++ default initialization| +|`m_size`|`FwSizeType`|The number of items on the stack|0| + +```mermaid +classDiagram + Stack *-- ExternalArray +``` + +## 4. Public Constructors and Destructors + +### 4.1. Zero-Argument Constructor + +```c++ +ExternalStack() +``` + +Initialize each member variable with its default value. + +_Example:_ +```c++ +ExternalStack stack; +``` + +### 4.2. Constructor Providing Typed Backing Storage + +```c++ +ExternalStack(T* items, FwSizeType capacity) +``` + +`items` must point to a primitive array of at least `capacity` +items of type `T`. + +1. Call `setStorage(items, capacity)`. + +1. Initialize the other member variables with their default values. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +U32 items[capacity]; +ExternalStack stack(items, capacity); +``` + +### 4.3. Constructor Providing Untyped Backing Storage + +```c++ +ExternalStack(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +1. Call `setStorage(data, capacity)`. + +1. Initialize the other member variables with their default values. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = ExternalStack::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = ExternalStack::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +ExternalStack stack(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +### 4.4. Copy Constructor + +```c++ +ExternalStack(const ExternalStack& stack) +``` + +Set `*this = stack`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +// Call the constructor providing backing storage +ExternalStack q1(items, capacity); +// Push an item +U32 value = 42; +(void) q1.push(value); +// Call the copy constructor +ExternalStack q2(q1); +ASSERT_EQ(q2.getSize(), 1); +``` + +### 4.5. Destructor + +```c++ +~ExternalStack() override +``` + +Defined as `= default`. + +## 5. Public Member Functions + +### 5.1. operator= + +```c++ +ExternalStack& operator=(const ExternalStack& stack) +``` + +1. If `&stack != this` + + 1. Set `m_items = stack.m_items`. + + 1. Set `m_size = stack.m_size`. + +1. Return `*this`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +// Call the constructor providing backing storage +ExternalStack q1(items, capacity); +// Push an item +U32 value = 42; +(void) q1.push(value); +// Call the default constructor +ExternalStack q2; +ASSERT_EQ(q2.getSize(), 0); +// Call the copy assignment operator +q2 = q1; +ASSERT_EQ(q2.getSize(), 1); +``` + +### 5.2. clear + +```c++ +void clear() override +``` + +Set `m_size = 0`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +U32 items[capacity]; +ExternalStack stack(items, capacity); +const auto status = stack.push(3); +ASSERT_EQ(stack.getSize(), 1); +stack.clear(); +ASSERT_EQ(stack.getSize(), 0); +``` + +### 5.3. setStorage (Typed Data) + +```c++ +void setStorage(T* items, FwSizeType capacity) +``` + +`items` must point to a primitive array of at least `capacity` +items of type `T`. + +1. Call `m_items.setStorage(items, capacity)`. + +1. Call `this->clear()`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +ExternalStack stack; +U32 items[capacity]; +stack.setStorage(items, capacity); +``` + +### 5.4. setStorage (Untyped Data) + +```c++ +void setStorage(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +1. Call `m_items.setStorage(data, capacity)`. + +1. Call `this->clear()`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = ExternalStack::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = ExternalStack::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +ExternalStack stack; +stack.setStorage(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +### 5.6. at + +```c++ +const T& at(FwSizeType index) const override +``` + +1. Assert `index < m_size`. + +1. Return `m_items[m_size - 1 - index]`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +ExternalStack stack(items, capacity); +const auto status = stack.push(3); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(stack.at(0), 3); +ASSERT_DEATH(stack.at(1), "Assert"); +``` + +### 5.8. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_size`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +U32 items[capacity]; +ExternalStack stack(items, capacity); +auto size = stack.getSize(); +ASSERT_EQ(size, 0); +const auto status = stack.push(3); +ASSERT_EQ(status, Success::SUCCESS); +size = stack.getSize(); +ASSERT_EQ(size, 1); +``` + +### 5.9. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_items.getSize()`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 10; +U32 items[capacity]; +ExternalStack stack(items, capacity); +ASSERT_EQ(stack.getCapacity(), capacity); +``` + +### 5.7. pop + +```c++ +Success pop(T& e) override +``` + +1. Set `status = Success::FAILURE`. + +1. If `m_size > 0` then + + 1. Decrement `m_size`. + + 1. Set `e = m_items[m_size]`. + +1. Return `status`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +ExternalStack stack(items, capacity); +U32 val; +auto status = stack.pop(val); +ASSERT_EQ(status, Success::FAILURE); +status = stack.push(42); +ASSERT_EQ(status, Success::SUCCESS); +status = stack.pop(val); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(val, 42); +``` + +### 5.5. push + +```c++ +Success push(const T& e) override +``` + +1. Set `status = Success::FAILURE`. + +1. If `m_size < getCapacity()` then + + 1. Set `m_items[m_size] = e`. + + 1. Increment `m_size`. + +1. Return `status`. + +_Example:_ +```c++ +constexpr FwSizeType capacity = 3; +U32 items[capacity]; +ExternalStack stack(items, capacity); +ASSERT_EQ(stack.getSize(), 0); +auto status = stack.push(42); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(stack.getSize(), 1); +``` + +## 6. Public Static Functions + + +### 6.1. getByteArrayAlignment + +```c++ +static constexpr U8 getByteArrayAlignment() +``` + +Return `ExternalArray::getByteArrayAlignment()`. + + +### 6.2. getByteArraySize + +```c++ +static constexpr FwSizeType getByteArraySize(FwSizeType capacity) +``` + +Return `ExternalArray::getByteArraySize(capacity)`. diff --git a/Fw/DataStructures/docs/FifoQueue.md b/Fw/DataStructures/docs/FifoQueue.md new file mode 100644 index 00000000000..a8878db8209 --- /dev/null +++ b/Fw/DataStructures/docs/FifoQueue.md @@ -0,0 +1,173 @@ +# FifoQueue + +`FifoQueue` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents a FIFO queue with internal storage. + +## 1. Template Parameters + +`FifoQueue` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of a queue item| +|`FwSizeType`|`C`|The queue capacity in items| + +`FifoQueue` statically asserts the following: + +* `T` is default constructible. +* `C > 0`. + +## 2. Base Class + +`FifoQueue` is publicly derived from +[`FifoQueueBase`](FifoQueueBase.md). + +## 3. Private Member Variables + +`FifoQueue` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_extQueue`|`ExternalFifoQueue`|The external queue implementation|C++ default initialization| +|`m_items`|`T[C]`|The array providing the backing memory for `m_extQueue`|C++ default initialization| + +```mermaid +classDiagram + FifoQueue *-- ExternalFifoQueue +``` + +## 4. Public Constructors and Destructors + +### 4.1. Zero-Argument Constructor + +```c++ +FifoQueue() +``` + +Initialize `m_extQueue` with `ExternalFifoQueue(m_items, C)`. + +_Example:_ +```c++ +FifoQueue queue; +``` + +### 4.2. Copy Constructor + +```c++ +FifoQueue(const FifoQueue& queue) +``` + +1. Initialize `m_extQueue` with `ExternalFifoQueue(m_items, C)`. + +1. Set `*this = queue`. + +_Example:_ +```c++ +FifoQueue q1; +auto status = q1.enqueue(3); +ASSERT_EQ(status, Success::SUCCESS); +FifoQueue q2(q1); +ASSERT_EQ(q2.size(), 1); +U32 value = 0; +status = q2.dequeue(value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 3); +``` + +### 4.3. Destructor + +```c++ +~FifoQueue() override +``` + +Defined as `= default`. + +## 5. Public Member Functions + +### 5.1. operator= + +```c++ +FifoQueue& operator=(const FifoQueue& queue) +``` + +Return `m_extQueue.copyDataFrom(queue)`. + +_Example:_ +```c++ +FifoQueue q1; +auto status = q1.enqueue(3); +ASSERT_EQ(status, Success::SUCCESS); +FifoQueue q2; +ASSERT_EQ(q2.size(), 0); +q2 = q1; +ASSERT_EQ(q2.size(), 1); +U32 value = 0; +status = q2.dequeue(value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 3); +``` + +### 5.2. clear + +```c++ +void clear() override +``` + +Call `m_extQueue.clear()`. + +### 5.3. enqueue + +```c++ +Success enqueue(const T& e) override +``` + +Return `m_extQueue.enqueue(e)`. + +### 5.4. at + +```c++ +const T& at(FwSizeType index) const override +``` + +Return `m_extQueue.at(index)`. + +### 5.5. dequeue + +```c++ +Success dequeue(T& e) override +``` + +Return `m_extQueue.dequeue(e)`. + +### 5.6. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_extQueue.getSize()`. + +### 5.7. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_extQueue.getCapacity()`. + +## 6. Public Static Functions + +### 6.1. getStaticCapacity + +```c++ +static constexpr FwSizeType getStaticCapacity() +``` + +Return the static capacity `C`. + +_Example:_ +```c++ +const auto capacity = FifoQueue::getStaticCapacity(); +ASSERT_EQ(capacity, 3); +``` diff --git a/Fw/DataStructures/docs/FifoQueueBase.md b/Fw/DataStructures/docs/FifoQueueBase.md new file mode 100644 index 00000000000..4053f6b1811 --- /dev/null +++ b/Fw/DataStructures/docs/FifoQueueBase.md @@ -0,0 +1,210 @@ +# FifoQueueBase + +`FifoQueueBase` is a class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an abstract base class for a FIFO queue. + +## 1. Template Parameters + +`FifoQueueBase` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an item on the queue| + +## 2. Base Class + +`FifoQueueBase` is publicly derived from [`SizedContainer`](SizedContainer.md). + +## 3. Private Constructors + +### 3.1. Copy Constructor + +```c++ +FifoQueueBase(const FifoQueueBase& queue) +``` + +Defined as `= delete`. + +## 4. Protected Constructors and Destructors + +### 4.1. Zero-Argument Constructor + +```c++ +FifoQueueBase() +``` + +Use default initialization of members. + +### 4.2. Destructor + +```c++ +virtual ~FifoQueueBase() +``` + +Defined as `= default`. + +## 5. Private Member Functions + +### 5.1. operator= + +```c++ +FifoQueueBase& operator=(const FifoQueueBase&) +``` + +Defined as `= delete`. + +## 6. Public Member Functions + +### 6.1. at + +```c++ +virtual const T& at(FwSizeType index) const = 0 +``` + +Return the item at the specified index. +Index 0 is the leftmost (earliest) element in the queue. +Increasing indices go from left to right. +Fails an assertion if the index is out of range. + +_Example:_ +```c++ +void f(FifoQueueBase& queue) { + queue.clear(); + const auto status = queue.enqueue(3); + ASSERT_EQ(status, Success::SUCCESS); + const auto status = queue.enqueue(4); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(queue.at(0), 3); + ASSERT_EQ(queue.at(1), 4); + ASSERT_DEATH(queue.at(2), "Assert"); +} +``` + +### 6.2. copyDataFrom + +```c++ +void copyDataFrom(const FifoQueueBase& queue) +``` + +1. If `&queue != this` then + + 1. Call `clear()`. + + 1. Let `size` be the minimum of `queue.getSize()` and `getCapacity()`. + + 1. For `i` in [0, `size`) + + 1. Set `e = at(i)`. + + 1. Set `status = enqueue(e)`. + + 1. Assert `status == Success::SUCCESS`. + + +_Example:_ +```c++ +void f(FifoQueueBase& q1, FifoQueueBase& q2) { + q1.clear(); + // Enqueue an item + U32 value = 42; + (void) q1.enqueue(value); + q2.clear(); + ASSERT_EQ(q2.getSize(), 0); + q2.copyDataFrom(q1); + ASSERT_EQ(q2.getSize(), 1); +} +``` + +### 6.3. dequeue + +```c++ +virtual Success dequeue(T& e) = 0 +``` + +1. Set `status = Success::FAILURE`. + +1. If `size > 0` + + 1. Remove the leftmost item from the queue and store it into `e`. + + 1. Set `status = Success::SUCCESS`. + +1. Return `status`. + +_Example:_ +```c++ +void f(FifoQueueBase& queue) { + queue.clear(); + U32 val = 0; + auto status = queue.dequeue(val); + ASSERT_EQ(status, Success::FAILURE); + status = queue.enqueue(3); + ASSERT_EQ(status, Success::SUCCESS); + status = queue.dequeue(val); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(val, 3); +} +``` + +### 6.4. enqueue + +```c++ +virtual Success enqueue(const T& e) = 0 +``` + +1. Set `status = Success::FAILURE`. + +1. If there is room on the queue for a new item, then + + 1. Add `e` to the right of the queue. + + 1. Set `status = Success::SUCCESS`. + +1. Return `status`. + +_Example:_ +```c++ +void f(FifoQueueBase& queue) { + queue.clear(); + const auto status = queue.enqueue(3); + ASSERT_EQ(status, Success::SUCCESS); +} +``` + +### 6.5. peek + +```c++ +Success peek(T& e, FwSizeType index = 0) const +``` + +1. Set `status = Success::FAILURE`. + +1. If `index < getSize()` + + 1. Set `e = at(index)`. + + 1. Set `status = Success::SUCCESS`. + +1. Return `status`. + +_Example:_ +```c++ +void f(FifoQueueBase& queue) { + queue.clear(); + U32 value = 0; + auto status = queue.peek(value); + ASSERT_EQ(status, Success::FAILURE); + status = queue.enqueue(3); + status = queue.peek(value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, 3); + status = queue.peek(value, 1); + ASSERT_EQ(status, Success::FAILURE); + status = queue.enqueue(4); + status = queue.peek(value, 1); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, 4); +} +``` + diff --git a/Fw/DataStructures/docs/MapBase.md b/Fw/DataStructures/docs/MapBase.md new file mode 100644 index 00000000000..a627399c90c --- /dev/null +++ b/Fw/DataStructures/docs/MapBase.md @@ -0,0 +1,238 @@ +# MapBase + +`MapBase` is an abstract class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an abstract base class for a map. + +## 1. Template Parameters + +`MapBase` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`K`|The type of a key in the map| +|`typename`|`V`|The type of a value in the map| + +## 2. Base Class + +`MapBase` is publicly derived from [`SizedContainer`](SizedContainer.md). + +## 3. Deleted Elements + +The following elements are private and are defined `= delete`: + +1. The copy constructor. + ```c++ + MapBase(const MapBase& map) + ``` + +1. The assignment operator. + ```c++ + MapBase& operator=(const MapBase&) + ``` + +## 4. Public Types + +`MapBase` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`MapConstIterator`](MapConstIterator.md)| + +## 5. Protected Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +MapBase() +``` + +Use default initialization of members. + +### 5.2. Destructor + +```c++ +virtual ~MapBase() +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. begin + +```c++ +virtual ConstIterator begin() const = 0. +``` + +Return the begin value of the iterator for the implementation. + +_Example:_ +```c++ +void f(MapBase& map) { + map.clear(); + // Insert an entry in the map + const auto status = map.insert(0, 1); + ASSERT_EQ(status, Fw::Success::SUCCESS); + // Get a map const iterator object + auto it = map.begin(); + // Use the iterator to access the underlying map const entry + const auto key = it->getKey(); + const auto value = it->getValue(); + ASSERT_EQ(key, 0); + ASSERT_EQ(value, 1); +} +``` + +### 6.2. copyDataFrom + +```c++ +void copyDataFrom(const MapBase& map) +``` + +1. If `&map != this` then + + 1. Call `clear()`. + + 1. Let `size` be the minimum of `map.getSize()` and `getCapacity()`. + + 1. Set `it = map.begin()`. + + 1. For `i` in [0, `size`) + + 1. Let `status = insert(it->getKey(), it->getValue())`. + + 1. Assert `status == Success::SUCCESS`. + + 1. Call `it++`. + +_Example:_ +```c++ +void f(MapBase& m1, MapBase& m2) { + m1.clear(); + // Insert an entry + const U16 key = 0 + const U32 value = 42; + const auto status = m1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + m2.clear(); + ASSERT_EQ(m2.getSize(), 0); + m2.copyDataFrom(q1); + ASSERT_EQ(m2.getSize(), 1); +} +``` + +### 6.3. end + +```c++ +virtual ConstIterator end() const = 0 +``` + +Return the end value of the iterator for the implementation. + +_Example:_ +```c++ +void f(MapBase& map) { + map.clear(); + // Insert an entry in the map + auto status = map.insert(0, 1); + ASSERT_EQ(status, Fw::Success::SUCCESS); + // Get a map const iterator object + auto iter = map.begin(); + // Check that iter is not at the end + ASSERT_NE(iter, map.end()); + // Increment iter + it++; + // Check that iter is at the end + ASSERT_EQ(iter, map.end()); +} +``` + +### 6.4. find + +```c++ +virtual Success find(const K& key, V& value) const = 0 +``` + +1. If an entry `e` with value `key` exists in the map, +then set `value = e.getValue()` and return `SUCCESS`. + +1. Otherwise return `FAILURE`. + +_Example:_ +```c++ +void f(const MapBase& map) { + map.clear(); + U32 value = 0; + auto status = map.find(0, value); + ASSERT_EQ(status, Success::FAILURE); + status = map.insert(0, 1); + ASSERT_EQ(status, Success::SUCCESS); + status = map.find(0, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, 1); +} +``` + +### 6.5. insert + +```c++ +virtual Success insert(const K& key, const V& value) = 0 +``` + +1. If an entry `e` exists with the specified key, then update the + value in `e` and return `SUCCESS`. + +1. Otherwise if there is room in the map, then add a new entry `e` with the +specified key-value pair and return `SUCCESS`. + +1. Otherwise return `FAILURE`. + +_Example:_ +```c++ +void f(MapBase& map) { + map.clear(); + auto size = map.getSize(); + ASSERT_EQ(size, 0); + const auto status = map.insert(0, 1); + ASSERT_EQ(status, Success::SUCCESS); + size = map.getSize(); + ASSERT_EQ(size, 1); +} +``` + +### 6.6. remove + +```c++ +virtual Success remove(const K& key, V& value) = 0 +``` + +1. If an entry `e` exists with key `key`, then +store the value of `e` into `value`, +remove `e` from the map, and return `SUCCESS`. + +1. Otherwise return `FAILURE`. + +_Example:_ +```c++ +void f(MapBase& map) { + map.clear(); + auto size = map.getSize(); + ASSERT_EQ(size, 0); + auto status = map.insert(0, 1); + ASSERT_EQ(status, Success::SUCCESS); + size = map.getSize(); + ASSERT_EQ(size, 1); + // Key does not exist + U32 value = 0; + status = map.remove(10, value); + ASSERT_EQ(status, Success::FAILURE); + ASSERT_EQ(size, 1); + // Key exists + status = map.remove(0, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(size, 0); + ASSERT_EQ(value, 1); +} +``` + diff --git a/Fw/DataStructures/docs/MapConstIterator.md b/Fw/DataStructures/docs/MapConstIterator.md new file mode 100644 index 00000000000..a1536810301 --- /dev/null +++ b/Fw/DataStructures/docs/MapConstIterator.md @@ -0,0 +1,104 @@ +# MapConstIterator + +`MapConstIterator` is a class for performing immutable iteration over a map. +The iteration order is not specified. + +## 1. Template Parameters + +`MapConstIterator` has the following template parameters: + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`K`|The type of a key in the map| +|`typename`|`V`|The type of a value in the map| + +## 2. Public Types + +`MapConstIterator` defines the following public types: + +|Name|Definition| +|----|----------| +|`EntryBase`|Alias of [`MapEntryBase`](MapEntryBase.md)| + +## 3. Constructors and Destructors + +`MapConstIterator` provides the following constructors and destructors: + +1. One constructor for each map implementation. + The map implementations use these constructors to provide iterators. + +1. A copy constructor. + +1. A destructor. + +## 4. Public Member Functions + +`MapConstIterator` provides the following member functions. + +### 4.1. operator= + +Defined as `= default`. + +### 4.2. operator== + +```c++ +bool operator==(const MapConstIterator& it) +``` + +Compare two `MapConstIterator` instances for equality. + +1. If the implementations differ, then return `false`. + +1. Otherwise check whether the implementations have equal values. + +### 4.3. operator != + +```c++ +bool operator!=(const MapConstIterator& it) +``` + +Return the negation of `operator=`. + +### 4.4. operator++ + +```c++ +MapConstIterator& operator++() +MapConstIterator& operator++(int) +``` + +Increment the iterator. + +### 4.5. isInRange() + +```c++ +bool isInRange() const +``` + +Check whether the iterator is in range. +It is a runtime error to attempt to access a map entry through an iterator +for which `isInRange` evaluates to `false`. +In this case an assertion failure will occur. + +### 4.6. operator* + +```c++ +const EntryBase& operator*() const +``` + +Return a `const` reference to the `EntryBase` object +pointed to by the iterator. +If the iterator is not in range for the map, an assertion failure will occur. +It is not recommended to use this operation +after updating the map that the iterator points to. + +### 4.7. operator-> + +```c++ +const EntryBase* operator->() const +``` + +Return a pointer to the `const EntryBase` object +pointed to by the iterator. +If the iterator is not in range for the map, an assertion failure will occur. +It is not recommended to use this operation +after updating the map that the iterator points to. diff --git a/Fw/DataStructures/docs/MapEntryBase.md b/Fw/DataStructures/docs/MapEntryBase.md new file mode 100644 index 00000000000..63b4d806e0f --- /dev/null +++ b/Fw/DataStructures/docs/MapEntryBase.md @@ -0,0 +1,43 @@ +# MapEntryBase + +`MapEntryBase` is an abstract class template +defined in [`Fw/DataStructures`](sdd.md). +It provides the user-facing interface for a map entry. + +## 1. Template Parameters + +`MapEntryBase` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`K`|The type of a key in the map| +|`typename`|`V`|The type of a value in the map| + +## 2. Deleted elements + +Because this is an abstract base class, +the copy constructor and copy assignment operator are deleted. + +## 3. Protected Constructors and Destructors + +`MapEntryBase` provides a protected zero-argument constructor +and a protected virtual destructor. +It uses the default implementations. + +## 4. Public Member Functions + +### 4.1. getKey + +```c++ +virtual const K& getKey() const = 0 +``` + +Return a `const` reference to the key stored in the entry. + +### 4.2. getValue + +```c++ +virtual const V& getValue() const = 0 +``` + +Return a `const` reference to the value stored in the entry. diff --git a/Fw/DataStructures/docs/Nil.md b/Fw/DataStructures/docs/Nil.md new file mode 100644 index 00000000000..1600881b9ed --- /dev/null +++ b/Fw/DataStructures/docs/Nil.md @@ -0,0 +1,6 @@ +# Nil + +`Nil` is an empty type. +It is a placeholder that is used as the value type +when a [`SetOrMapImplEntry`](SetOrMapImplEntry.md) +is used as a set iterator. diff --git a/Fw/DataStructures/docs/RedBlackTreeMap.md b/Fw/DataStructures/docs/RedBlackTreeMap.md new file mode 100644 index 00000000000..47b9088b3e3 --- /dev/null +++ b/Fw/DataStructures/docs/RedBlackTreeMap.md @@ -0,0 +1,4 @@ +# RedBlackTreeMap + +TODO + diff --git a/Fw/DataStructures/docs/RedBlackTreeSet.md b/Fw/DataStructures/docs/RedBlackTreeSet.md new file mode 100644 index 00000000000..e0c1c939f2e --- /dev/null +++ b/Fw/DataStructures/docs/RedBlackTreeSet.md @@ -0,0 +1,4 @@ +# ExternalRedBlackTreeSet + +TODO + diff --git a/Fw/DataStructures/docs/SetBase.md b/Fw/DataStructures/docs/SetBase.md new file mode 100644 index 00000000000..9d1aa38989a --- /dev/null +++ b/Fw/DataStructures/docs/SetBase.md @@ -0,0 +1,227 @@ +# SetBase + +`SetBase` is an abstract class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an abstract base class for a set. + +## 1. Template Parameters + +`SetBase` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an element in the set| + +## 2. Base Class + +`SetBase` is publicly derived from [`SizedContainer`](SizedContainer.md). + +## 3. Deleted Elements + +The following elements are private and are defined `= delete`: + +1. The copy constructor. + ```c++ + SetBase(const SetBase& map) + ``` + +1. The assignment operator. + ```c++ + SetBase& operator=(const SetBase&) + ``` + +## 4. Public Types + +`SetBase` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`SetConstIterator`](SetConstIterator.md)| + +## 5. Protected Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +SetBase() +``` + +Use default initialization of members. + +### 5.2. Destructor + +```c++ +virtual ~SetBase() +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. begin + +```c++ +virtual ConstIterator begin() const = 0. +``` + +Return the begin value of the iterator for the implementation. + +_Example:_ +```c++ +void f(SetBase& set) { + set.clear(); + // Insert an element in the set + const auto status = set.insert(42); + ASSERT_EQ(status, Fw::Success::SUCCESS); + // Get a set const iterator object + auto it = set.begin(); + // Use the iterator to access the element + ASSERT_EQ(*it, 42); +} +``` + +### 6.2. copyDataFrom + +```c++ +void copyDataFrom(const SetBase& set) +``` + +1. If `&set != this` then + + 1. Call `clear()`. + + 1. Let `size` be the minimum of `set.getSize()` and `getCapacity()`. + + 1. Set `it = map.begin()`. + + 1. For `i` in [0, `size`) + + 1. Let `status = insert(*it)`. + + 1. Assert `status == Success::SUCCESS`. + + 1. Call `it++`. + +_Example:_ +```c++ +void f(SetBase& s1, SetBase& s2) { + s1.clear(); + // Insert an entry + const auto status = s1.insert(42); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(s1.getSize(), 1); + s2.clear(); + ASSERT_EQ(s2.getSize(), 0); + s2.copyDataFrom(q1); + ASSERT_EQ(s2.getSize(), 1); +} +``` + +### 6.3. end + +```c++ +virtual ConstIterator end() const = 0 +``` + +Return the end value of the iterator for the implementation. + +_Example:_ +```c++ +void f(SetBase& set) { + set.clear(); + // Insert an element in the set + auto status = set.insert(42); + ASSERT_EQ(status, Fw::Success::SUCCESS); + // Get a set const iterator object + auto iter = set.begin(); + // Check that iter is not at the end + ASSERT_NE(iter, set.end()); + // Increment iter + it++; + // Check that iter is at the end + ASSERT_EQ(iter, set.end()); +} +``` + +### 6.4. find + +```c++ +virtual Success find(const T& element) = 0 +``` + +1. If an entry `e` with element `element` exists in the set, +then return `SUCCESS`. + +1. Otherwise return `FAILURE`. + +_Example:_ +```c++ +void f(const SetBase& set) { + set.clear(); + auto status = set.find(42); + ASSERT_EQ(status, Success::FAILURE); + status = set.insert(42); + ASSERT_EQ(status, Success::SUCCESS); + status = set.find(42); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, 1); +} +``` + +### 6.5. insert + +```c++ +virtual Success insert(const T& element) = 0 +``` + +1. If an entry `e` exists with the specified element, then return `SUCCESS`. + +1. Otherwise if there is room in the set, then add a new entry `e` with the + specified element and return `SUCCESS`. + +1. Otherwise return `FAILURE`. + +_Example:_ +```c++ +void f(SetBase& set) { + set.clear(); + auto size = set.getSize(); + ASSERT_EQ(size, 0); + const auto status = set.insert(0, 1); + ASSERT_EQ(status, Success::SUCCESS); + size = set.getSize(); + ASSERT_EQ(size, 1); +} +``` + +### 6.6. remove + +```c++ +virtual Success remove(const T& element) = 0 +``` + +1. If an entry `e` exists with element `element`, then +remove `e` from the set, and return `SUCCESS`. + +1. Otherwise return `FAILURE`. + +_Example:_ +```c++ +void f(SetBase& set) { + set.clear(); + auto size = set.getSize(); + ASSERT_EQ(size, 0); + auto status = set.insert(0); + ASSERT_EQ(status, Success::SUCCESS); + size = set.getSize(); + ASSERT_EQ(size, 1); + // Element does not exist + status = set.remove(42); + ASSERT_EQ(status, Success::FAILURE); + ASSERT_EQ(size, 1); + // Key exists + status = set.remove(0); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(size, 0); +} +``` diff --git a/Fw/DataStructures/docs/SetConstIterator.md b/Fw/DataStructures/docs/SetConstIterator.md new file mode 100644 index 00000000000..87f8f7ac9a9 --- /dev/null +++ b/Fw/DataStructures/docs/SetConstIterator.md @@ -0,0 +1,95 @@ +# SetConstIterator + +`SetConstIterator` is a class for performing immutable iteration over a set. +The iteration order is not specified. + +## 1. Template Parameters + +`SetConstIterator` has the following template parameters: + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an element in the set| + +## 2. Constructors and Destructors + +`SetConstIterator` provides the following constructors and destructors: + +1. One constructor for each set implementation. + The set implementations use these constructors to provide iterators. + +1. A copy constructor. + +1. A destructor. + +## 3. Public Member Functions + +`SetConstIterator` provides the following member functions. + +### 3.1. operator= + +Defined as `= default`. + +### 3.2. operator== + +```c++ +bool operator==(const SetConstIterator& it) +``` + +Compare two `SetConstIterator` instances for equality. + +1. If the implementations differ, then return `false`. + +1. Otherwise check whether the implementations have equal values. + +### 3.3. operator != + +```c++ +bool operator!=(const SetConstIterator& it) +``` + +Return the negation of `operator=`. + +### 3.4. operator++ + +```c++ +SetConstIterator& operator++() +SetConstIterator& operator++(int) +``` + +Increment the iterator. + +### 3.5. isInRange() + +```c++ +bool isInRange() const +``` + +Check whether the iterator is in range. +It is a runtime error to attempt to access a set element through an iterator +for which `isInRange` evaluates to `false`. +In this case an assertion failure will occur. + +### 3.6. operator* + +```c++ +const EntryBase& operator*() const +``` + +Return a `const` reference to the `T` element +pointed to by the iterator. +If the iterator is not in range for the map, an assertion failure will occur. +It is not recommended to use this operation +after updating the set that the iterator points to. + +### 3.7. operator-> + +```c++ +const EntryBase* operator->() const +``` + +Return a pointer to the `const T` element +pointed to by the iterator. +If the iterator is not in range for the map, an assertion failure will occur. +It is not recommended to use this operation +after updating the set that the iterator points to. diff --git a/Fw/DataStructures/docs/SetEntry.md b/Fw/DataStructures/docs/SetEntry.md new file mode 100644 index 00000000000..8a3192af5f6 --- /dev/null +++ b/Fw/DataStructures/docs/SetEntry.md @@ -0,0 +1,70 @@ +# SetEntry + +`SetEntry` is an abstract class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an iterator for a set. + +## 1. Template Parameters + +`SetEntry` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an element in the set| + +## 2. Private Constructors and Destructors + +### 2.1. Copy Constructor + +```c++ +SetEntry(const SetEntry& set) +``` + +Defined as `= delete`. + +## 3. Protected Constructors and Destructors + +### 3.1. Zero-Argument Constructor + +```c++ +SetEntry() +``` + +Use default initialization of members. + +### 3.2. Destructor + +```c++ +virtual ~SetEntry() +``` + +Defined as `= default`. + +## 4. Private Member Functions + +### 4.1. operator= + +```c++ +SetEntry& operator=(const SetEntry& setEntry) +``` + +Defined as `= delete`. + +## 5. Public Member Functions + +### 5.1. getElement + +```c++ +virtual const T& getElement() const = 0 +``` + +Return a reference to the set element stored in the iterator. + +### 5.2. getNextSetEntry + +```c++ +virtual const SetEntry* getNextSetEntry() = 0 +``` + +Return a pointer to the next iterator for the set, or `nullptr` if +there is none. diff --git a/Fw/DataStructures/docs/SetOrMapImplConstIterator.md b/Fw/DataStructures/docs/SetOrMapImplConstIterator.md new file mode 100644 index 00000000000..1b2e207af57 --- /dev/null +++ b/Fw/DataStructures/docs/SetOrMapImplConstIterator.md @@ -0,0 +1,6 @@ +# SetOrMapImplConstIterator + +`SetOrMapImplConstIterator` is an abstract base class. +It specifies an interface for constant iterators over +set or map implementations. +Here is the [header file for the class](../SetOrMapImplConstIterator.hpp). diff --git a/Fw/DataStructures/docs/SetOrMapImplEntry.md b/Fw/DataStructures/docs/SetOrMapImplEntry.md new file mode 100644 index 00000000000..cbb1274230a --- /dev/null +++ b/Fw/DataStructures/docs/SetOrMapImplEntry.md @@ -0,0 +1,132 @@ +# SetOrMapImplEntry + +`SetOrMapImplEntry` is a final class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an iterator for a set or a map implementation. + +## 1. Template Parameters + +`SetOrMapImplEntry` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`KE`|The type of a key in a map or the element of a set| +|`typename`|`VN`|The type of a value in a map or [`Nil`](Nil.md) in a set| + +`SetOrMapImplEntry` statically asserts the following: + +* `KE` is default constructible. +* `KE` is assignable to `KE&`. +* `VN` is default constructible. +* `VN` is assignable to `VN&`. + +## 2. Base Class + +`SetOrMapImplEntry` is publicly derived from the following +templates: + +1. [`MapEntryBase`](MapEntryBase.md). + +```mermaid +classDiagram + MapEntryBase <|-- SetOrMapImplEntry +``` + +## 3. Private Member Variables + +`SetOrMapImplEntry` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_keyOrElement`|`KE`|The map key or set element|C++ default initialization| +|`m_valueOrNil`|`VN`|The value or [`Nil`](Nil.md)|C++ default initialization| + +## 4. Public Constructors and Destructors + +### 4.1. Zero-Argument Constructor + +```c++ +SetOrMapImplEntry() +``` + +Use default initialization of members. + +### 4.2. Constructor Providing Members + +```c++ +SetOrMapImplEntry(const KE& keyOrElement, const VN& valueOrNil) +``` + +1. Set `m_keyOrElement = keyOrElement`. + +2. Set `m_valueOrNil = valueOrNil`. + +### 4.3. Copy Constructor + +```c++ +SetOrMapImplEntry(const SetOrMapImplEntry& iterator) +``` + +Set `*this = iterator`. + +### 4.4. Destructor + +```c++ +~SetOrMapImplEntry() override +``` + +Defined as `= default`. + +## 5. Public Member Functions + +### 5.1. operator= + +```c++ +SetOrMapImplEntry& operator=(const SetOrMapImplEntry& iterator) +``` + +1. If `this != &iterator` + + 1. Set `m_keyOrElement = iterator.keyOrElement`. + + 1. Set `m_valueOrNil = iterator.valueOrNil`. + +### 5.3. getElement + +```c++ +const KE& getElement() const +``` + +Return a reference to `m_keyOrElement`. + +### 5.2. getKey + +```c++ +const KE& getKey() const override +``` + +Return a reference to `m_keyOrElement`. + +### 5.3. getValue + +```c++ +const VN& getValue() const override +``` + +Return a reference to `m_valueOrNil`. + +### 5.6. setKeyOrElement + +```c++ +void setKeyOrElement(const KE& keyOrElement) const +``` + +Set `m_keyOrElement = keyOrElement`. + +### 5.7. setValueOrNil + +```c++ +void setValueOrNil(const VN& valueOrNil) const +``` + +Set `m_valueOrNil = valueOrNil`. diff --git a/Fw/DataStructures/docs/SizedContainer.md b/Fw/DataStructures/docs/SizedContainer.md new file mode 100644 index 00000000000..0087b997d52 --- /dev/null +++ b/Fw/DataStructures/docs/SizedContainer.md @@ -0,0 +1,134 @@ +# SizedContainer + +`SizedContainer` is a class +defined in [`Fw/DataStructures`](sdd.md). +It is an abstract class representing a sized container. + +## 1. Private Constructors + +### 1.1. Copy Constructor + +```c++ +SizedContainer(const SizedContainer& c) +``` + +Defined as `= delete`. + +## 2. Protected Constructors and Destructors + +### 2.1. Zero-Argument Constructor + +```c++ +SizedContainer() +``` + +Use default initialization of members. + +### 2.2. Destructor + +```c++ +virtual ~SizedContainer() +``` + +Defined as `= default`. + +## 3. Private Member Functions + +### 3.1. operator= + +```c++ +SizedContainer& operator=(const SizedContainer&) +``` + +Defined as `= delete`. + +## 4. Public Member Functions + +### 4.1. clear + +```c++ +virtual void clear() = 0 +``` + +Clear the container. + +_Example:_ +```c++ +void f(SizedContainer& c) { + c.clear(); + ASSERT_EQ(c.getSize(), 0); +} +``` + +### 4.2. getCapacity + +```c++ +virtual FwSizeType getCapacity() const = 0 +``` + +Return the current capacity. + +_Example:_ +```c++ +void f(const SizedContainer& c) { + const auto size = c.getSize(); + const auto capacity = c.getCapacity(); + ASSERT_LE(size, capacity); +} +``` + +### 4.3. getSize + +```c++ +virtual FwSizeType getSize() const = 0 +``` + +Return the current size. + +_Example:_ +```c++ +void f(const SizedContainer& c) { + c.clear(); + auto size = c.getSize(); + ASSERT_EQ(size, 0); +} +``` + +### 4.4. isEmpty + +```c++ +bool isEmpty() const +``` + +Return `true` if the container is empty. + +_Example:_ +```c++ +void f(const SizedContainer& c) { + if (c.size() == 0) { + ASSERT_TRUE(c.isEmpty()); + } else { + ASSERT_FALSE(c.isEmpty()); + } +} +``` + +### 4.5. isFull + +```c++ +bool isFull() const +``` + +Return `true` if the container is full. + +_Example:_ +```c++ +void f(const SizedContainer& c) { + if (c.size() >= c.capacity()) { + ASSERT_TRUE(c.isFull()); + } else { + ASSERT_FALSE(c.isFull()); + } +} +``` + diff --git a/Fw/DataStructures/docs/Stack.md b/Fw/DataStructures/docs/Stack.md new file mode 100644 index 00000000000..735274870e7 --- /dev/null +++ b/Fw/DataStructures/docs/Stack.md @@ -0,0 +1,171 @@ +# Stack + +`Stack` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents a stack with internal storage. + +## 1. Template Parameters + +`Stack` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of a stack item| +|`FwSizeType`|`C`|The stack capacity in items| + +`Stack` statically asserts the following: + +* `T` is default constructible. +* `C > 0`. + +## 2. Base Class + +`Stack` is publicly derived from +[`StackBase`](StackBase.md). + +## 3. Private Member Variables + +`Stack` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_extStack`|`ExternalStack`|The external stack implementation|C++ default initialization| +|`m_items`|`T[C]`|The array providing the backing memory for `m_extStack`|C++ default initialization| + +```mermaid +classDiagram + Stack *-- ExternalStack +``` + +## 4. Public Constructors and Destructors + +### 4.1. Zero-Argument Constructor + +```c++ +Stack() +``` + +Initialize `m_extStack` with `ExternalStack(m_items, C)`. + +_Example:_ +```c++ +Stack stack; +``` + +### 4.2. Copy Constructor + +```c++ +Stack(const Stack& stack) +``` + +Set `*this = stack`. + +_Example:_ +```c++ +Stack q1; +auto status = q1.push(3); +ASSERT_EQ(status, Success::SUCCESS); +Stack q2(q1); +ASSERT_EQ(q2.size(), 1); +U32 value = 0; +status = q2.pop(value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 3); +``` + +### 4.3. Destructor + +```c++ +~Stack() override +``` + +Defined as `= default`. + +## 5. Public Member Functions + +### 5.1. operator= + +```c++ +Stack& operator=(const Stack& stack) +``` + +Call `m_extStack.copyDataFrom(stack)`. + +_Example:_ +```c++ +Stack q1; +auto status = q1.push(3); +ASSERT_EQ(status, Success::SUCCESS); +Stack q2; +ASSERT_EQ(q2.size(), 0); +q2 = q1; +ASSERT_EQ(q2.size(), 1); +U32 value = 0; +status = q2.pop(value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 3); +``` + +### 5.2. clear + +```c++ +void clear() override +``` + +Call `m_extStack.clear()`. + +### 5.3. push + +```c++ +Success push(const T& e) override +``` + +Return `m_extStack.push(e)`. + +### 5.4. at + +```c++ +const T& at(FwSizeType index) const override +``` + +Return `m_extStack.at(index)`. + +### 5.5. pop + +```c++ +Success pop(T& e) override +``` + +Return `m_extStack.pop(e)`. + +### 5.6. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_extStack.getSize()`. + +### 5.7. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_extStack.getCapacity()`. + +## 6. Public Static Functions + +### 6.1. getStaticCapacity + +```c++ +static constexpr FwSizeType getStaticCapacity() +``` + +Return the static capacity `C`. + +_Example:_ +```c++ +const auto capacity = Stack::getStaticCapacity(); +ASSERT_EQ(capacity, 3); +``` diff --git a/Fw/DataStructures/docs/StackBase.md b/Fw/DataStructures/docs/StackBase.md new file mode 100644 index 00000000000..8448c892486 --- /dev/null +++ b/Fw/DataStructures/docs/StackBase.md @@ -0,0 +1,212 @@ +# StackBase + +`StackBase` is a class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an abstract base class for a stack. + +## 1. Template Parameters + +`StackBase` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an item on the stack| + +## 2. Base Class + +`StackBase` is publicly derived from [`SizedContainer`](SizedContainer.md). + +## 3. Private Constructors + +### 3.1. Copy Constructor + +```c++ +StackBase(const StackBase& stack) +``` + +Defined as `= delete`. + +## 4. Protected Constructors and Destructors + +### 4.1. Zero-Argument Constructor + +```c++ +StackBase() +``` + +Use default initialization of members. + +### 4.2. Destructor + +```c++ +virtual ~StackBase() +``` + +Defined as `= default`. + +## 5. Private Member Functions + +### 5.1. operator= + +```c++ +StackBase& operator=(const StackBase&) +``` + +Defined as `= delete`. + +## 6. Public Member Functions + +### 6.1. at + +```c++ +virtual const T& at(FwSizeType index) const = 0 +``` + +Return the item at the specified index. +Index 0 is the rightmost (latest) element in the stack. +Increasing indices go from right to left. +Fails an assertion if the index is out of range. + +_Example:_ +```c++ +void f(StackBase& stack) { + stack.clear(); + auto status = stack.push(3); + ASSERT_EQ(status, Success::SUCCESS); + status = stack.push(4); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(stack.at(0), 4); + ASSERT_EQ(stack.at(1), 3); + ASSERT_DEATH(stack.at(2), "Assert"); +} +``` + +### 6.2. copyDataFrom + +```c++ +void copyDataFrom(const StackBase& stack) +``` + +1. If `&stack != this` then + + 1. Call `clear()`. + + 1. Let `size` be the minimum of `stack.getSize()` and `getCapacity()`. + + 1. For `i` in [0, `size`) + + 1. Set `e = at(size - 1 - i)`. + + 1. Set `status = push(e)`. + + 1. Assert `status == Success::SUCCESS`. + + +_Example:_ +```c++ +void f(StackBase& q1, StackBase& q2) { + q1.clear(); + // Push an item + U32 value = 42; + (void) q1.push(value); + q2.clear(); + ASSERT_EQ(q2.getSize(), 0); + q2.copyDataFrom(q1); + ASSERT_EQ(q2.getSize(), 1); +} +``` + +### 6.3. peek + +```c++ +Success peek(T& e, FwSizeType index = 0) const +``` + +1. Set `status = Success::FAILURE`. + +1. If `index < getSize()` + + 1. Set `e = at(index)`. + + 1. Set `status = Success::SUCCESS`. + +1. Return `status`. + +_Example:_ +```c++ +void f(StackBase& stack) { + stack.clear(); + U32 value = 0; + auto status = stack.peek(value); + ASSERT_EQ(status, Success::FAILURE); + status = stack.push(3); + status = stack.peek(value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, 3); + status = stack.peek(value, 1); + ASSERT_EQ(status, Success::FAILURE); + status = stack.push(4); + status = stack.peek(value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, 4); + status = stack.peek(value, 1); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, 3); +} +``` + +### 6.4. pop + +```c++ +virtual Success pop(T& e) = 0 +``` + +1. Set `status = Success::FAILURE`. + +1. If `size > 0` + + 1. Remove the rightmost item from the stack and store it into `e`. + + 1. Set `status = Success::SUCCESS`. + +1. Return `status`. + +_Example:_ +```c++ +void f(StackBase& stack) { + stack.clear(); + U32 val = 0; + auto status = stack.pop(val); + ASSERT_EQ(status, Success::FAILURE); + status = stack.push(3); + ASSERT_EQ(status, Success::SUCCESS); + status = stack.pop(val); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(val, 3); +} +``` + +### 6.5. push + +```c++ +virtual Success push(const T& e) = 0 +``` + +1. Set `status = Success::FAILURE`. + +1. If there is room on the stack for a new item, then + + 1. Add `e` to the right of the stack. + + 1. Set `status = Success::SUCCESS`. + +1. Return `status`. + +_Example:_ +```c++ +void f(StackBase& stack) { + stack.clear(); + const auto status = stack.push(3); + ASSERT_EQ(status, Success::SUCCESS); +} +``` diff --git a/Fw/DataStructures/docs/sdd.md b/Fw/DataStructures/docs/sdd.md new file mode 100644 index 00000000000..5973453b78a --- /dev/null +++ b/Fw/DataStructures/docs/sdd.md @@ -0,0 +1,161 @@ +# Fw/DataStructures: Basic Data Structures + +`Fw/DataStructures` contains a library of basic data structures. +All the definitions in this directory are in the +namespace `Fw`. + +The data structures defined here use the following concepts: + +* **size:** The number of elements currently stored in a data structure. + +* **capacity:** The maximum number of elements stored in a data structure. + +For a fixed-size array, the size and the capacity are the same. +For other data structures, the size and the capacity are not +in general the same. +For example, a map has a capacity _C_ and a size _S_ between 0 +and _C_. + +The data structures in this directory are **sequential data structures**, +i.e., they do not support direct concurrent access by multiple threads. +To use these data structures in a multithreaded context, you need +to provide separate concurrency control. +The most common way to do this is to make the data +structure a member of an active or queued component and to use +the component queue to guard the access to the structure. + +## 1. Arrays + +An **array** _A_ stores _S_ elements for _S > 0_ at indices +0, 1, ..., _S_ - 1. +The elements are stored in **backing memory** _M_. +An array provides bounds-checked access to the array elements +stored in _M_. + +`Fw/DataStructures` provides the following array templates: + +|Name|Description| +|----|-----------| +|[`Array`](Array.md)|A bounds-checked array with internal memory for storing the array elements| +|[`ExternalArray`](ExternalArray.md)|A bounds-checked array with external memory for storing the array elements| + +## 2. Stacks + +A **stack** is a data structure that provides push and pop +operations in last-in-first-out (LIFO) order. + +### 2.1. Templates + +`Fw/DataStructures` provides the following stack templates: + +|Name|Description| +|----|-----------| +|[`ExternalStack`](ExternalStack.md)|A stack with external memory for storing the stack items| +|[`Stack`](Stack.md)|A stack with internal memory for storing the stack items| +|[`StackBase`](StackBase.md)|The abstract base class for a stack| + +### 2.2. Class Diagram + +```mermaid +classDiagram + SizedContainer <|-- StackBase + StackBase <|-- ExternalStack + StackBase <|-- Stack +``` + +`StackBase` is a derived class of [`SizedContainer`](SizedContainer.md), +which represents a generic container with a capacity and a size. + +## 3. FIFO Queues + +A **FIFO queue** is a data structure that +provides enqueue and dequeue operations in +first-in-first-out (FIFO) order. + +### 3.1. Templates + +`Fw/DataStructures` provides the following FIFO queue templates: + +|Name|Description| +|----|-----------| +|[`ExternalFifoQueue`](ExternalFifoQueue.md)|A FIFO queue with external memory for storing the queue items| +|[`FifoQueue`](FifoQueue.md)|A FIFO queue with internal memory for storing the queue items| +|[`FifoQueueBase`](FifoQueueBase.md)|The abstract base class for a FIFO queue| + +The queue implementations use a template called +[`CircularIndex`](CircularIndex.md) +for representing a **circular index**, i.e., an index that wraps around modulo +an integer. +You can use this template to represent any circular index. + +### 3.2. Class Diagram + +```mermaid +classDiagram + SizedContainer <|-- FifoQueueBase + FifoQueueBase <|-- ExternalFifoQueue + FifoQueueBase <|-- FifoQueue +``` + +`FifoQueueBase` is a derived class of [`SizedContainer`](SizedContainer.md), +which represents a generic container with a capacity and a size. + +## 4. Maps + +A **map** is a data structure that associates keys to values. +It provides insert, remove, and find operations. + +### 4.1. Templates + +`Fw/DataStructures` provides the following map templates: + +|Name|Description| +|----|-----------| +|[`ArrayMap`](ArrayMap.md)|An array-based map with internal memory for storing the array| +|[`ExternalArrayMap`](ExternalArrayMap.md)|An array-based map with external memory for storing the array| +|[`ExternalRedBlackTreeMap`](ExternalRedBlackTreeMap.md)|A red-black tree with external memory for storing the tree| +|[`MapBase`](MapBase.md)|The abstract base class for a map| +|[`RedBlackTreeMap`](RedBlackTreeMap.md)|A red-black tree with internal memory for storing the tree| + +### 4.2. Class Diagram + +```mermaid +classDiagram + SizedContainer <|-- MapBase + MapBase <|-- ArrayMap + MapBase <|-- ExternalArrayMap + MapBase <|-- ExternalRedBlackTreeMap + MapBase <|-- RedBlackTreeMap +``` + +`MapBase` is a derived class of [`SizedContainer`](SizedContainer.md), +which represents a generic container with a capacity and a size. + +## 5. Sets + +A **set** is a data structure that contains elements. +It provides insert, remove, and find operations. + +### 5.1. Templates + +|Name|Description| +|----|-----------| +|[`ArraySet`](ArraySet.md)|An array-based set with internal memory for storing the array| +|[`ExternalArraySet`](ExternalArraySet.md)|An array-based set with external memory for storing the array| +|[`ExternalRedBlackTreeSet`](ExternalRedBlackTreeSet.md)|A red-black tree with external memory for storing the tree| +|[`RedBlackTreeSet`](RedBlackTreeSet.md)|A red-black tree with internal memory for storing the tree| +|[`SetBase`](SetBase.md)|The abstract base class for a set| + +### 5.2. Class Diagram + +```mermaid +classDiagram + SizedContainer <|-- SetBase + SetBase <|-- ArraySet + SetBase <|-- ExternalArraySet + SetBase <|-- ExternalRedBlackTreeSet + SetBase <|-- RedBlackTreeSet +``` + +`SetBase` is a derived class of [`SizedContainer`](SizedContainer.md), +which represents a generic container with a capacity and a size. diff --git a/Fw/DataStructures/test/ut/ArrayMapTest.cpp b/Fw/DataStructures/test/ut/ArrayMapTest.cpp new file mode 100644 index 00000000000..e4514e3e1d2 --- /dev/null +++ b/Fw/DataStructures/test/ut/ArrayMapTest.cpp @@ -0,0 +1,150 @@ +// ====================================================================== +// \title ArrayMapTest.cpp +// \author bocchino +// \brief cpp file for ArrayMap tests +// ====================================================================== + +#include "Fw/DataStructures/ArrayMap.hpp" +#include "STest/STest/Pick/Pick.hpp" + +#include "Fw/DataStructures/ArrayMap.hpp" +#include "Fw/DataStructures/test/ut/ArraySetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/MapTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/MapTestScenarios.hpp" + +namespace Fw { + +template +class ArrayMapTester { + public: + ArrayMapTester(const ArrayMap& map) : m_map(map) {} + + const ExternalArrayMap getExtMap() const { return this->m_map.extMap; } + + const typename ArrayMap::Entries& getEntries() const { return this->m_map.m_entries; } + + private: + const ArrayMap& m_map; +}; + +namespace MapTest { + +using Entry = SetOrMapImplEntry; +using Map = ArrayMap; +using MapTester = ArrayMapTester; +using ImplTester = ArraySetOrMapImplTester; + +TEST(ArrayMap, ZeroArgConstructor) { + Map map; + ASSERT_EQ(map.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(map.getSize(), 0); +} + +TEST(ArrayMap, CopyConstructor) { + Map m1; + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = m1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + Map m2(m1); + ASSERT_EQ(m2.getSize(), 1); +} + +TEST(ArrayMap, CopyAssignmentOperator) { + Map m1; + // Insert an item + const State::KeyType key = 0; + State::ValueType value = 42; + auto status = m1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + Map m2; + ASSERT_EQ(m2.getSize(), 0); + // Call the copy assignment operator + m2 = m1; + ASSERT_EQ(m2.getSize(), 1); + value = 0; + status = m2.find(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, 42); +} + +TEST(ArrayMap, CopyDataFrom) { + constexpr FwSizeType maxSize = State::capacity; + constexpr FwSizeType smallSize = maxSize / 2; + Map m1; + // size1 < capacity2 + { + Map m2; + State::testCopyDataFrom(m1, smallSize, m2); + } + // size1 == capacity2 + { + Map m2; + State::testCopyDataFrom(m1, maxSize, m2); + } + // size1 > capacity2 + { + ArrayMap m2; + State::testCopyDataFrom(m1, maxSize, m2); + } +} + +TEST(ArrayMapScenarios, Clear) { + Map map; + State state(map); + Scenarios::clear(state); +} + +TEST(ArrayMapScenarios, Find) { + Map map; + State state(map); + Scenarios::find(state); +} + +TEST(ArrayMapScenarios, FindExisting) { + Map map; + State state(map); + Scenarios::findExisting(state); +} + +TEST(ArrayMapScenarios, InsertExisting) { + Map map; + State state(map); + Scenarios::insertExisting(state); +} + +TEST(ArrayMapScenarios, InsertFull) { + Map map; + State state(map); + Scenarios::insertFull(state); +} + +TEST(ArrayMapScenarios, InsertNotFull) { + Map map; + State state(map); + Scenarios::insertNotFull(state); +} + +TEST(ArrayMapScenarios, Remove) { + Map map; + State state(map); + Scenarios::remove(state); +} + +TEST(ArrayMapScenarios, RemoveExisting) { + Map map; + State state(map); + Scenarios::removeExisting(state); +} + +TEST(ArrayMapScenarios, Random) { + Map map; + State state(map); + Scenarios::random(Fw::String("ArrayMapRandom"), state, 1000); +} + +} // namespace MapTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/ArraySetOrMapImplTest.cpp b/Fw/DataStructures/test/ut/ArraySetOrMapImplTest.cpp new file mode 100644 index 00000000000..e7f3daa5a63 --- /dev/null +++ b/Fw/DataStructures/test/ut/ArraySetOrMapImplTest.cpp @@ -0,0 +1,175 @@ +// ====================================================================== +// \title ArraySetOrMapImplTest.cpp +// \author bocchino +// \brief cpp file for ArraySetOrMapImpl tests +// ====================================================================== + +#include + +#include "Fw/DataStructures/ArraySetOrMapImpl.hpp" +#include "STest/STest/Pick/Pick.hpp" + +#include "Fw/DataStructures/test/ut/ArraySetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestScenarios.hpp" + +namespace Fw { + +namespace ArraySetOrMapImplTest { + +TEST(ArraySetOrMapImpl, ZeroArgConstructor) { + State::Impl impl; + ASSERT_EQ(impl.getCapacity(), 0); + ASSERT_EQ(impl.getSize(), 0); +} + +TEST(ArraySetOrMapImpl, TypedStorageConstructor) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State::Tester tester(impl); + ASSERT_EQ(tester.getEntries().getElements(), entries); + ASSERT_EQ(impl.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(impl.getSize(), 0); +} + +TEST(ArraySetOrMapImpl, UntypedStorageConstructor) { + constexpr auto alignment = State::Impl::getByteArrayAlignment(); + constexpr auto byteArraySize = State::Impl::getByteArraySize(State::capacity); + alignas(alignment) U8 bytes[byteArraySize]; + State::Impl impl(ByteArray(&bytes[0], sizeof bytes), State::capacity); + State::Tester tester(impl); + ASSERT_EQ(tester.getEntries().getElements(), reinterpret_cast(bytes)); + ASSERT_EQ(impl.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(impl.getSize(), 0); +} + +TEST(ArraySetOrMapImpl, CopyConstructor) { + State::Entry entries[State::capacity]; + // Call the constructor providing backing storage + State::Impl impl1(entries, State::capacity); + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = impl1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + State::Impl impl2(impl1); + State::Tester tester1(impl1); + State::Tester tester2(impl2); + ASSERT_EQ(tester2.getEntries().getElements(), entries); + ASSERT_EQ(tester2.getEntries().getSize(), FwSizeType(State::capacity)); + ASSERT_EQ(impl2.getSize(), 1); +} + +TEST(ArraySetOrMapImpl, CopyAssignmentOperator) { + State::Entry entries[State::capacity]; + // Call the constructor providing backing storage + State::Impl impl1(entries, State::capacity); + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = impl1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + State::Impl impl2; + ASSERT_EQ(impl2.getSize(), 0); + // Call the copy assignment operator + impl2 = impl1; + ASSERT_EQ(impl2.getSize(), 1); +} + +TEST(ArraySetOrMapImpl, IteratorConstruction) { + State::Impl impl; + State::Impl::ConstIterator it(impl); +} + +TEST(ArraySetOrMapImpl, IteratorComparison) { + // Test comparison in default case + State::Impl::ConstIterator it1; + State::Impl::ConstIterator it2; + ASSERT_TRUE(it1.compareEqual(it2)); +} + +TEST(ArraySetOrMapImplScenarios, Clear) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State state(impl); + Rules::insertNotFull.apply(state); + ASSERT_EQ(state.impl.getSize(), 1); + Rules::clear.apply(state); + ASSERT_EQ(state.impl.getSize(), 0); +} + +TEST(ArraySetOrMapImplScenarios, Find) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State state(impl); + Rules::find.apply(state); + state.useStoredKey = true; + Rules::insertNotFull.apply(state); + Rules::find.apply(state); +} + +TEST(ArraySetOrMapImplScenarios, FindExisting) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State state(impl); + Rules::insertNotFull.apply(state); + Rules::findExisting.apply(state); +} + +TEST(ArraySetOrMapImplScenarios, InsertExisting) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State state(impl); + Rules::insertNotFull.apply(state); + Rules::insertExisting.apply(state); +} + +TEST(ArraySetOrMapImplScenarios, InsertFull) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State state(impl); + state.useStoredKey = true; + for (FwSizeType i = 0; i < State::capacity; i++) { + state.storedKey = static_cast(i); + Rules::insertNotFull.apply(state); + } + state.useStoredKey = false; + Rules::insertFull.apply(state); +} + +TEST(ArraySetOrMapImplScenarios, InsertNotFull) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State state(impl); + Rules::insertNotFull.apply(state); +} + +TEST(ArraySetOrMapImplScenarios, Remove) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State state(impl); + state.useStoredKey = true; + Rules::insertNotFull.apply(state); + Rules::remove.apply(state); + Rules::remove.apply(state); +} + +TEST(ArraySetOrMapImplScenarios, RemoveExisting) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State state(impl); + Rules::insertNotFull.apply(state); + Rules::removeExisting.apply(state); +} + +TEST(ArraySetOrMapImplScenarios, Random) { + State::Entry entries[State::capacity]; + State::Impl impl(entries, State::capacity); + State state(impl); + Scenarios::random(Fw::String("ArraySetOrMapImplRandom"), state, 1000); +} + +} // namespace ArraySetOrMapImplTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/ArraySetOrMapImplTester.hpp b/Fw/DataStructures/test/ut/ArraySetOrMapImplTester.hpp new file mode 100644 index 00000000000..fd5b4726e47 --- /dev/null +++ b/Fw/DataStructures/test/ut/ArraySetOrMapImplTester.hpp @@ -0,0 +1,32 @@ +// ====================================================================== +// \title ArraySetOrMapImplTester.hpp +// \author bocchino +// \brief Class template for access to ArraySetOrMapImpl members +// ====================================================================== + +#ifndef ArraySetOrMapImplTester_HPP +#define ArraySetOrMapImplTester_HPP + +#include + +#include "Fw/DataStructures/ArraySetOrMapImpl.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +template +class ArraySetOrMapImplTester { + public: + using Entry = SetOrMapImplEntry; + + ArraySetOrMapImplTester(const ArraySetOrMapImpl& impl) : m_impl(impl) {} + + const ExternalArray& getEntries() const { return this->m_impl.m_entries; } + + private: + const ArraySetOrMapImpl& m_impl; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/ArraySetTest.cpp b/Fw/DataStructures/test/ut/ArraySetTest.cpp new file mode 100644 index 00000000000..7e6b0f29f8d --- /dev/null +++ b/Fw/DataStructures/test/ut/ArraySetTest.cpp @@ -0,0 +1,146 @@ +// ====================================================================== +// \title ArraySetTest.cpp +// \author bocchino +// \brief cpp file for ArraySet tests +// ====================================================================== + +#include "Fw/DataStructures/ArraySet.hpp" +#include "STest/STest/Pick/Pick.hpp" + +#include "Fw/DataStructures/ArraySet.hpp" +#include "Fw/DataStructures/test/ut/ArraySetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/SetTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/SetTestScenarios.hpp" + +namespace Fw { + +template +class ArraySetTester { + public: + ArraySetTester(const ArraySet& set) : m_set(set) {} + + const ExternalArraySet getExtSet() const { return this->m_set.extSet; } + + const typename ArraySet::Entries& getEntries() const { return this->m_set.m_entries; } + + private: + const ArraySet& m_set; +}; + +namespace SetTest { + +using Entry = SetOrMapImplEntry; +using Set = ArraySet; +using SetTester = ArraySetTester; +using ImplTester = ArraySetOrMapImplTester; + +TEST(ArraySet, ZeroArgConstructor) { + Set set; + ASSERT_EQ(set.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(set.getSize(), 0); +} + +TEST(ArraySet, CopyConstructor) { + Set s1; + // Insert an item + const State::ElementType e = 42; + const auto status = s1.insert(e); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + Set s2(s1); + ASSERT_EQ(s2.getSize(), 1); +} + +TEST(ArraySet, CopyAssignmentOperator) { + Set s1; + // Insert an item + const State::ElementType e = 42; + auto status = s1.insert(e); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + Set s2; + ASSERT_EQ(s2.getSize(), 0); + // Call the copy assignment operator + s2 = s1; + ASSERT_EQ(s2.getSize(), 1); + status = s2.find(e); + ASSERT_EQ(status, Success::SUCCESS); +} + +TEST(ArraySet, CopyDataFrom) { + constexpr FwSizeType maxSize = State::capacity; + constexpr FwSizeType smallSize = maxSize / 2; + Set s1; + // size1 < capacity2 + { + Set s2; + State::testCopyDataFrom(s1, smallSize, s2); + } + // size1 == capacity2 + { + Set s2; + State::testCopyDataFrom(s1, maxSize, s2); + } + // size1 > capacity2 + { + ArraySet s2; + State::testCopyDataFrom(s1, maxSize, s2); + } +} + +TEST(ArraySetScenarios, Clear) { + Set set; + State state(set); + Scenarios::clear(state); +} + +TEST(ArraySetScenarios, Find) { + Set set; + State state(set); + Scenarios::find(state); +} + +TEST(ArraySetScenarios, FindExisting) { + Set set; + State state(set); + Scenarios::findExisting(state); +} + +TEST(ArraySetScenarios, InsertExisting) { + Set set; + State state(set); + Scenarios::insertExisting(state); +} + +TEST(ArraySetScenarios, InsertFull) { + Set set; + State state(set); + Scenarios::insertFull(state); +} + +TEST(ArraySetScenarios, InsertNotFull) { + Set set; + State state(set); + Scenarios::insertNotFull(state); +} + +TEST(ArraySetScenarios, Remove) { + Set set; + State state(set); + Scenarios::remove(state); +} + +TEST(ArraySetScenarios, RemoveExisting) { + Set set; + State state(set); + Scenarios::removeExisting(state); +} + +TEST(ArraySetScenarios, Random) { + Set set; + State state(set); + Scenarios::random(Fw::String("ArraySetRandom"), state, 1000); +} + +} // namespace SetTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/ArrayTest.cpp b/Fw/DataStructures/test/ut/ArrayTest.cpp new file mode 100644 index 00000000000..fad97f1bf4f --- /dev/null +++ b/Fw/DataStructures/test/ut/ArrayTest.cpp @@ -0,0 +1,111 @@ +// ====================================================================== +// \title ArrayTest.cpp +// \author bocchino +// \brief cpp file for Array tests +// ====================================================================== + +#include + +#include "Fw/DataStructures/Array.hpp" + +namespace Fw { + +TEST(Array, ZeroArgConstructor) { + Array a; + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(a[i], 0U); + } +} + +TEST(Array, InitializerListConstructor) { + // Explicit call to constructor + Array a({1, 2, 3}); + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(a[i], i + 1); + } + // Implicit call to constructor in assignment + Array b = {1, 2, 3}; + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(b[i], i + 1); + } +} + +TEST(Array, RawArrayConstructor) { + // Explicit call to constructor + U32 elements[3] = {1, 2, 3}; + Array a(elements); + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(a[i], i + 1); + } + // Implicit call to constructor in assignment + Array b = elements; + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(b[i], i + 1); + } +} + +TEST(Array, SingleElementConstructor) { + // Explicit call to constructor in variable declaration + Array a(1); + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(a[i], 1); + } + // Explicit call to constructor in assignment + Array b = Array(2); + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(b[i], 2); + } +} + +TEST(Array, CopyConstructor) { + // Call the single-item constructor + Array a1(10); + // Call the copy constructor + Array a2(a1); + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(a2[i], 10); + } +} + +TEST(Array, Subscript) { + Array a = {0, 1, 2}; + // Constant access + ASSERT_EQ(a[1], 1); + // Mutable access + a[1]++; + ASSERT_EQ(a[1], 2); + // Out-of-bounds access + ASSERT_DEATH(a[3], "Assert"); +} + +TEST(Array, CopyAssignmentOperator) { + Array a1(1); + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(a1[i], 1); + } + Array a2(2); + auto& a = (a1 = a2); + for (FwSizeType i = 0; i < 3; i++) { + ASSERT_EQ(a1[i], 2); + } + ASSERT_EQ(&a, &a1); +} + +TEST(Array, GetElements) { + Array a; + // Mutable reference + auto& elements1 = a.getElements(); + ASSERT_EQ(elements1[0], 0); + elements1[0] = 1; + // Constant reference + const auto& elements2 = a.getElements(); + ASSERT_EQ(elements2[0], 1); +} + +TEST(Array, AsExternalArray) { + Array a = {1, 2, 3}; + ExternalArray ea = a.asExternalArray(); + ASSERT_EQ(ea[0], 1); +} + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/CircularIndexTest.cpp b/Fw/DataStructures/test/ut/CircularIndexTest.cpp new file mode 100644 index 00000000000..0b6db6717bb --- /dev/null +++ b/Fw/DataStructures/test/ut/CircularIndexTest.cpp @@ -0,0 +1,133 @@ +// ====================================================================== +// \title CircularIndexTest.cpp +// \author bocchino +// \brief Tests for CircularIndex types +// ====================================================================== + +#include +#include + +#include "Fw/DataStructures/CircularIndex.hpp" +#include "STest/Pick/Pick.hpp" + +namespace Fw { + +namespace { + +FwSizeType pickValue() { + return static_cast(STest::Pick::any()); +} + +FwSizeType pickModulus() { + return static_cast(STest::Pick::lowerUpper(1, std::numeric_limits::max())); +} + +} // namespace + +TEST(CircularIndex, ZeroArgConstructor) { + CircularIndex ci; + ASSERT_EQ(ci.getValue(), 0); + ASSERT_EQ(ci.getModulus(), 1); +} + +TEST(CircularIndex, SpecifiedMemberConstructor) { + const FwSizeType modulus = pickModulus(); + const FwSizeType value = pickValue(); + CircularIndex ci(modulus, value); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_EQ(ci.getValue(), value % modulus); +} + +TEST(CircularIndex, CopyConstructor) { + const FwSizeType modulus = pickModulus(); + const FwSizeType value = pickValue(); + CircularIndex ci1(modulus, value); + CircularIndex ci2(ci1); + ASSERT_EQ(ci1.getModulus(), ci2.getModulus()); + ASSERT_EQ(ci1.getValue(), ci2.getValue()); +} + +TEST(CircularIndex, CopyAssignment) { + const FwSizeType modulus = pickModulus(); + const FwSizeType value = pickValue(); + CircularIndex ci1(modulus, value); + CircularIndex ci2; + ci2 = ci1; + ASSERT_EQ(ci1.getModulus(), ci2.getModulus()); + ASSERT_EQ(ci1.getValue(), ci2.getValue()); +} + +TEST(CircularIndex, SetValue) { + const FwSizeType modulus = pickModulus(); + const FwSizeType value = pickValue(); + CircularIndex ci(modulus); + ASSERT_EQ(ci.getValue(), 0); + ASSERT_EQ(ci.getModulus(), modulus); + ci.setValue(value); + ASSERT_EQ(ci.getValue(), value % modulus); +} + +TEST(CircularIndex, SetModulus) { + const FwSizeType value = pickValue(); + const FwSizeType modulus = pickModulus(); + CircularIndex ci(modulus, value); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_EQ(ci.getValue(), value % modulus); + ci.setModulus(modulus); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_EQ(ci.getValue(), value % modulus); +} + +TEST(CircularIndex, IncrementOne) { + const FwSizeType value = pickValue(); + const FwSizeType modulus = pickModulus(); + CircularIndex ci(modulus, value); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_EQ(ci.getValue(), value % modulus); + ci.increment(); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_EQ(ci.getValue(), (value + 1) % modulus); +} + +TEST(CircularIndex, IncrementRandom) { + const FwSizeType value = pickValue(); + const FwSizeType modulus = pickModulus(); + const FwSizeType amount = pickValue(); + const FwSizeType offset = amount % modulus; + CircularIndex ci(modulus, value); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_EQ(ci.getValue(), value % modulus); + ci.increment(amount); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_GT(modulus, 0); + ASSERT_EQ(ci.getValue(), (value + offset) % modulus); +} + +TEST(CircularIndex, DecrementOne) { + const FwSizeType value = pickValue(); + const FwSizeType modulus = pickModulus(); + CircularIndex ci(modulus, value); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_EQ(ci.getValue(), value % modulus); + ci.decrement(); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_GT(modulus, 0); + ASSERT_GE(value + modulus, 1); + ASSERT_EQ(ci.getValue(), ((value + modulus) - 1) % modulus); +} + +TEST(CircularIndex, DecrementRandom) { + const FwSizeType value = pickValue(); + const FwSizeType modulus = pickModulus(); + const FwSizeType amount = pickValue(); + const FwSizeType offset = amount % modulus; + CircularIndex ci(modulus, value); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_EQ(ci.getValue(), value % modulus); + ci.decrement(amount); + ASSERT_EQ(ci.getModulus(), modulus); + ASSERT_GT(modulus, 0); + ASSERT_GE(value + modulus, offset); + ASSERT_EQ(ci.getValue(), (value + modulus - offset) % modulus); +} +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/DataStructuresTestMain.cpp b/Fw/DataStructures/test/ut/DataStructuresTestMain.cpp new file mode 100644 index 00000000000..17b1785f3f8 --- /dev/null +++ b/Fw/DataStructures/test/ut/DataStructuresTestMain.cpp @@ -0,0 +1,15 @@ +// ====================================================================== +// \title DataStructuresTestMain.cpp +// \author bocchino +// \brief cpp file for DataStructures tests +// ====================================================================== + +#include + +#include "STest/Random/Random.hpp" + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + STest::Random::seed(); + return RUN_ALL_TESTS(); +} diff --git a/Fw/DataStructures/test/ut/ExternalArrayMapTest.cpp b/Fw/DataStructures/test/ut/ExternalArrayMapTest.cpp new file mode 100644 index 00000000000..0d2112e4629 --- /dev/null +++ b/Fw/DataStructures/test/ut/ExternalArrayMapTest.cpp @@ -0,0 +1,185 @@ +// ====================================================================== +// \title ExternalArrayMapTest.cpp +// \author bocchino +// \brief cpp file for ExternalArrayMap tests +// ====================================================================== + +#include "Fw/DataStructures/ExternalArrayMap.hpp" +#include "Fw/DataStructures/test/ut/ArraySetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/MapTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/MapTestScenarios.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +template +class ExternalArrayMapTester { + public: + ExternalArrayMapTester(const ExternalArrayMap& map) : m_map(map) {} + + const ArraySetOrMapImpl& getImpl() const { return this->m_map.m_impl; } + + private: + const ExternalArrayMap& m_map; +}; + +namespace MapTest { + +using Entry = SetOrMapImplEntry; +using Map = ExternalArrayMap; +using MapTester = ExternalArrayMapTester; +using ImplTester = ArraySetOrMapImplTester; + +TEST(ExternalArrayMap, ZeroArgConstructor) { + Map map; + ASSERT_EQ(map.getCapacity(), 0); + ASSERT_EQ(map.getSize(), 0); +} + +TEST(ExternalArrayMap, TypedStorageConstructor) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + MapTester mapTester(map); + ImplTester implTester(mapTester.getImpl()); + ASSERT_EQ(implTester.getEntries().getElements(), entries); + ASSERT_EQ(map.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(map.getSize(), 0); +} + +TEST(ExternalArrayMap, UntypedStorageConstructor) { + constexpr auto alignment = Map::getByteArrayAlignment(); + constexpr auto byteArraySize = Map::getByteArraySize(State::capacity); + alignas(alignment) U8 bytes[byteArraySize]; + Map map(ByteArray(&bytes[0], sizeof bytes), State::capacity); + MapTester mapTester(map); + ImplTester implTester(mapTester.getImpl()); + ASSERT_EQ(implTester.getEntries().getElements(), reinterpret_cast(bytes)); + ASSERT_EQ(map.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(map.getSize(), 0); +} + +TEST(ExternalArrayMap, CopyConstructor) { + Entry entries[State::capacity]; + // Call the constructor providing backing storage + Map map1(entries, State::capacity); + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = map1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + Map map2(map1); + MapTester mapTester1(map1); + ImplTester implTester1(mapTester1.getImpl()); + MapTester mapTester2(map2); + ImplTester implTester2(mapTester2.getImpl()); + ASSERT_EQ(implTester2.getEntries().getElements(), entries); + ASSERT_EQ(implTester2.getEntries().getSize(), FwSizeType(State::capacity)); + ASSERT_EQ(map2.getSize(), 1); +} + +TEST(ExternalArrayMap, CopyAssignmentOperator) { + Entry entries[State::capacity]; + // Call the constructor providing backing storage + Map map1(entries, State::capacity); + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = map1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + Map map2; + ASSERT_EQ(map2.getSize(), 0); + // Call the copy assignment operator + map2 = map1; + ASSERT_EQ(map2.getSize(), 1); +} + +TEST(ExternalArrayMap, CopyDataFrom) { + constexpr FwSizeType maxSize = 10; + constexpr FwSizeType smallSize = maxSize / 2; + Entry entries1[maxSize]; + Entry entries2[maxSize]; + Map m1(entries1, maxSize); + // size1 < capacity2 + { + Map m2(entries2, maxSize); + State::testCopyDataFrom(m1, smallSize, m2); + } + // size1 == capacity2 + { + Map m2(entries2, maxSize); + State::testCopyDataFrom(m1, maxSize, m2); + } + // size1 > capacity2 + { + Map m2(entries2, smallSize); + State::testCopyDataFrom(m1, maxSize, m2); + } +} + +TEST(ExternalArrayMapScenarios, Clear) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + State state(map); + Scenarios::clear(state); +} + +TEST(ExternalArrayMapScenarios, Find) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + State state(map); + Scenarios::find(state); +} + +TEST(ExternalArrayMapScenarios, FindExisting) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + State state(map); + Scenarios::findExisting(state); +} + +TEST(ExternalArrayMapScenarios, InsertExisting) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + State state(map); + Scenarios::insertExisting(state); +} + +TEST(ExternalArrayMapScenarios, InsertFull) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + State state(map); + Scenarios::insertFull(state); +} + +TEST(ExternalArrayMapScenarios, InsertNotFull) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + State state(map); + Scenarios::insertNotFull(state); +} + +TEST(ExternalArrayMapScenarios, Remove) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + State state(map); + Scenarios::remove(state); +} + +TEST(ExternalArrayMapScenarios, RemoveExisting) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + State state(map); + Scenarios::removeExisting(state); +} + +TEST(ExternalArrayMapScenarios, Random) { + Entry entries[State::capacity]; + Map map(entries, State::capacity); + State state(map); + Scenarios::random(Fw::String("ExternalArrayMapRandom"), state, 1000); +} + +} // namespace MapTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/ExternalArraySetTest.cpp b/Fw/DataStructures/test/ut/ExternalArraySetTest.cpp new file mode 100644 index 00000000000..e5e794d50ba --- /dev/null +++ b/Fw/DataStructures/test/ut/ExternalArraySetTest.cpp @@ -0,0 +1,185 @@ +// ====================================================================== +// \title ExternalArraySetTest.cpp +// \author bocchino +// \brief cpp file for ExternalArraySet tests +// ====================================================================== + +#include "Fw/DataStructures/ExternalArraySet.hpp" +#include "STest/STest/Pick/Pick.hpp" + +#include "Fw/DataStructures/ExternalArraySet.hpp" +#include "Fw/DataStructures/test/ut/ArraySetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/SetTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/SetTestScenarios.hpp" + +namespace Fw { + +template +class ExternalArraySetTester { + public: + ExternalArraySetTester(const ExternalArraySet& set) : m_set(set) {} + + const ArraySetOrMapImpl& getImpl() const { return this->m_set.m_impl; } + + private: + const ExternalArraySet& m_set; +}; + +namespace SetTest { + +using Entry = SetOrMapImplEntry; +using Set = ExternalArraySet; +using SetTester = ExternalArraySetTester; +using ImplTester = ArraySetOrMapImplTester; + +TEST(ExternalArraySet, ZeroArgConstructor) { + Set set; + ASSERT_EQ(set.getCapacity(), 0); + ASSERT_EQ(set.getSize(), 0); +} + +TEST(ExternalArraySet, TypedStorageConstructor) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + SetTester setTester(set); + ImplTester implTester(setTester.getImpl()); + ASSERT_EQ(implTester.getEntries().getElements(), entries); + ASSERT_EQ(set.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(set.getSize(), 0); +} + +TEST(ExternalArraySet, UntypedStorageConstructor) { + constexpr auto alignment = Set::getByteArrayAlignment(); + constexpr auto byteArraySize = Set::getByteArraySize(State::capacity); + alignas(alignment) U8 bytes[byteArraySize]; + Set set(ByteArray(&bytes[0], sizeof bytes), State::capacity); + SetTester setTester(set); + ImplTester implTester(setTester.getImpl()); + ASSERT_EQ(implTester.getEntries().getElements(), reinterpret_cast(bytes)); + ASSERT_EQ(set.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(set.getSize(), 0); +} + +TEST(ExternalArraySet, CopyConstructor) { + Entry entries[State::capacity]; + // Call the constructor providing backing storage + Set set1(entries, State::capacity); + // Insert an item + const State::ElementType e = 42; + const auto status = set1.insert(e); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + Set set2(set1); + SetTester setTester1(set1); + ImplTester implTester1(setTester1.getImpl()); + SetTester setTester2(set2); + ImplTester implTester2(setTester2.getImpl()); + ASSERT_EQ(implTester2.getEntries().getElements(), entries); + ASSERT_EQ(implTester2.getEntries().getSize(), FwSizeType(State::capacity)); + ASSERT_EQ(set2.getSize(), 1); +} + +TEST(ExternalArraySet, CopyAssignmentOperator) { + Entry entries[State::capacity]; + // Call the constructor providing backing storage + Set set1(entries, State::capacity); + // Insert an item + const State::ElementType e = 42; + const auto status = set1.insert(e); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + Set set2; + ASSERT_EQ(set2.getSize(), 0); + // Call the copy assignment operator + set2 = set1; + ASSERT_EQ(set2.getSize(), 1); +} + +TEST(ExternalArraySet, CopyDataFrom) { + constexpr FwSizeType maxSize = 10; + constexpr FwSizeType smallSize = maxSize / 2; + Entry entries1[maxSize]; + Entry entries2[maxSize]; + Set s1(entries1, maxSize); + // size1 < capacity2 + { + Set s2(entries2, maxSize); + State::testCopyDataFrom(s1, smallSize, s2); + } + // size1 == size2 + { + Set s2(entries2, maxSize); + State::testCopyDataFrom(s1, maxSize, s2); + } + // size1 > size2 + { + Set s2(entries2, smallSize); + State::testCopyDataFrom(s1, maxSize, s2); + } +} + +TEST(ExternalArraySetScenarios, Clear) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + State state(set); + Scenarios::clear(state); +} + +TEST(ExternalArraySetScenarios, Find) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + State state(set); + Scenarios::find(state); +} + +TEST(ExternalArraySetScenarios, FindExisting) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + State state(set); + Scenarios::findExisting(state); +} + +TEST(ExternalArraySetScenarios, InsertExisting) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + State state(set); + Scenarios::insertExisting(state); +} + +TEST(ExternalArraySetScenarios, InsertFull) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + State state(set); + Scenarios::insertFull(state); +} + +TEST(ExternalArraySetScenarios, InsertNotFull) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + State state(set); + Scenarios::insertNotFull(state); +} + +TEST(ExternalArraySetScenarios, Remove) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + State state(set); + Scenarios::remove(state); +} + +TEST(ExternalArraySetScenarios, RemoveExisting) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + State state(set); + Scenarios::removeExisting(state); +} + +TEST(ExternalArraySetScenarios, Random) { + Entry entries[State::capacity]; + Set set(entries, State::capacity); + State state(set); + Scenarios::random(Fw::String("ExternalArraySetRandom"), state, 1000); +} + +} // namespace SetTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/ExternalArrayTest.cpp b/Fw/DataStructures/test/ut/ExternalArrayTest.cpp new file mode 100644 index 00000000000..b240e9c130c --- /dev/null +++ b/Fw/DataStructures/test/ut/ExternalArrayTest.cpp @@ -0,0 +1,146 @@ +// ====================================================================== +// \title ExternalArrayTest.cpp +// \author bocchino +// \brief cpp file for ExternalArray tests +// ====================================================================== + +#include + +#include "Fw/Buffer/Buffer.hpp" +#include "Fw/DataStructures/ExternalArray.hpp" + +namespace Fw { + +TEST(ExternalArray, ZeroArgConstructor) { + ExternalArray a; + ASSERT_EQ(a.getElements(), nullptr); + ASSERT_EQ(a.getSize(), 0); +} + +TEST(ExternalArray, StorageConstructorTyped) { + constexpr FwSizeType size = 3; + U32 elements[size]; + ExternalArray a(elements, size); + ASSERT_EQ(a.getElements(), elements); + ASSERT_EQ(a.getSize(), size); +} + +TEST(ExternalArray, StorageConstructorUntyped) { + constexpr FwSizeType size = 3; + constexpr U8 alignment = ExternalArray::getByteArrayAlignment(); + constexpr FwSizeType byteArraySize = ExternalArray::getByteArraySize(size); + alignas(alignment) U8 bytes[byteArraySize]; + ExternalArray a(ByteArray(&bytes[0], sizeof bytes), size); + ASSERT_EQ(a.getElements(), reinterpret_cast(&bytes[0])); + ASSERT_EQ(a.getSize(), size); + a[0] = Fw::Buffer(); +} + +TEST(ExternalArray, CopyConstructor) { + constexpr FwSizeType size = 3; + U32 elements[size]; + // Call the constructor providing backing storage + ExternalArray a1(elements, size); + // Call the copy constructor + ExternalArray a2(a1); + ASSERT_EQ(a2.getElements(), elements); + ASSERT_EQ(a2.getSize(), size); +} + +TEST(ExternalArray, CopyAssignment) { + constexpr FwSizeType size = 3; + U32 elements[size]; + // Call the constructor providing backing storage + ExternalArray a1(elements, size); + // Call the copy assignment operator + ExternalArray a2; + a2 = a1; + ASSERT_EQ(a1.getElements(), a2.getElements()); + ASSERT_EQ(a1.getSize(), a2.getSize()); +} + +namespace { + +void testCopyDataFrom(ExternalArray a1, ExternalArray a2) { + const FwSizeType size1 = a1.getSize(); + for (FwSizeType i = 0; i < size1; i++) { + a1[i] = static_cast(i); + } + const FwSizeType size2 = a2.getSize(); + for (FwSizeType i = 0; i < size2; i++) { + a2[i] = 0; + } + a2.copyDataFrom(a1); + const FwSizeType size = FW_MIN(size1, size2); + for (FwSizeType i = 0; i < size; i++) { + ASSERT_EQ(a2[i], a1[i]); + } +} + +} // namespace + +TEST(ExternalArray, CopyDataFrom) { + constexpr FwSizeType maxSize = 10; + constexpr FwSizeType smallSize = maxSize / 2; + U32 elements1[maxSize]; + U32 elements2[maxSize]; + // size1 < size2 + testCopyDataFrom(ExternalArray(elements1, smallSize), ExternalArray(elements2, maxSize)); + // size1 == size2 + testCopyDataFrom(ExternalArray(elements1, maxSize), ExternalArray(elements2, maxSize)); + // size1 > size2 + testCopyDataFrom(ExternalArray(elements1, maxSize), ExternalArray(elements2, smallSize)); +} + +TEST(ExternalArray, Subscript) { + constexpr FwSizeType size = 10; + U32 elements[size] = {}; + ExternalArray a(elements, size); + // Constant access + ASSERT_EQ(a[0], 0); + // Mutable access + a[0]++; + ASSERT_EQ(a[0], 1); + // Out-of-bounds access + ASSERT_DEATH(a[size], "Assert"); +} + +TEST(ExternalArray, SetStorageTyped) { + constexpr FwSizeType size = 10; + U32 elements[size]; + ExternalArray a; + a.setStorage(elements, size); + ASSERT_EQ(a.getElements(), elements); + ASSERT_EQ(a.getSize(), size); +} + +TEST(ExternalArray, SetStorageUntypedOK) { + constexpr FwSizeType size = 10; + constexpr U8 alignment = ExternalArray::getByteArrayAlignment(); + constexpr FwSizeType byteArraySize = ExternalArray::getByteArraySize(size); + alignas(alignment) U8 bytes[byteArraySize]; + ExternalArray a; + a.setStorage(ByteArray(&bytes[0], sizeof bytes), size); + ASSERT_EQ(a.getElements(), reinterpret_cast(bytes)); + ASSERT_EQ(a.getSize(), size); +} + +TEST(ExternalArray, SetStorageUntypedBadSize) { + constexpr FwSizeType size = 10; + constexpr U8 alignment = ExternalArray::getByteArrayAlignment(); + constexpr FwSizeType byteArraySize = ExternalArray::getByteArraySize(size); + alignas(alignment) U8 bytes[byteArraySize]; + ExternalArray a; + ASSERT_DEATH(a.setStorage(ByteArray(&bytes[0], sizeof bytes), size + 1), "Assert"); +} + +TEST(ExternalArray, SetStorageUntypedBadAlignment) { + constexpr FwSizeType size = 10; + constexpr U8 alignment = ExternalArray::getByteArrayAlignment(); + constexpr FwSizeType byteArraySize = ExternalArray::getByteArraySize(size); + alignas(alignment) U8 bytes[byteArraySize]; + ExternalArray a; + ASSERT_DEATH(a.setStorage(ByteArray(&bytes[1], sizeof bytes), size), "Assert"); +} + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/ExternalFifoQueueTest.cpp b/Fw/DataStructures/test/ut/ExternalFifoQueueTest.cpp new file mode 100644 index 00000000000..c35f6ab439b --- /dev/null +++ b/Fw/DataStructures/test/ut/ExternalFifoQueueTest.cpp @@ -0,0 +1,175 @@ +// ====================================================================== +// \title ExternalFifoQueueTest.cpp +// \author bocchino +// \brief cpp file for ExternalFifoQueue tests +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/FifoQueueTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/FifoQueueTestScenarios.hpp" + +namespace Fw { + +template +class ExternalFifoQueueTester { + public: + ExternalFifoQueueTester(const ExternalFifoQueue& queue) : m_queue(queue) {} + + const ExternalArray& getItems() const { return this->m_queue.m_items; } + + const CircularIndex& getEnqueueIndex() const { return this->m_queue.m_enqueueIndex; } + + const CircularIndex& getDequeueIndex() const { return this->m_queue.m_dequeueIndex; } + + private: + const ExternalFifoQueue& m_queue; +}; + +namespace FifoQueueTest { + +using Queue = ExternalFifoQueue; +using QueueTester = ExternalFifoQueueTester; + +TEST(ExternalFifoQueue, ZeroArgConstructor) { + Queue queue; + ASSERT_EQ(queue.getCapacity(), 0); + ASSERT_EQ(queue.getSize(), 0); +} + +TEST(ExternalFifoQueue, TypedStorageConstructor) { + State::ItemType items[State::capacity]; + Queue queue(items, State::capacity); + QueueTester tester(queue); + ASSERT_EQ(tester.getItems().getElements(), items); + ASSERT_EQ(queue.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(queue.getSize(), 0); +} + +TEST(ExternalFifoQueue, UntypedStorageConstructor) { + constexpr U8 alignment = Queue::getByteArrayAlignment(); + constexpr FwSizeType byteArraySize = Queue::getByteArraySize(State::capacity); + alignas(alignment) U8 bytes[byteArraySize]; + Queue queue(ByteArray(&bytes[0], sizeof bytes), State::capacity); + QueueTester tester(queue); + ASSERT_EQ(tester.getItems().getElements(), reinterpret_cast(bytes)); + ASSERT_EQ(queue.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(queue.getSize(), 0); +} + +TEST(ExternalFifoQueue, CopyConstructor) { + State::ItemType items[State::capacity]; + // Call the constructor providing backing storage + Queue q1(items, State::capacity); + // Enqueue an item + State::ItemType item = State::getRandomItem(); + const auto status = q1.enqueue(item); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + Queue q2(q1); + QueueTester tester1(q1); + QueueTester tester2(q2); + ASSERT_EQ(tester2.getItems().getElements(), items); + ASSERT_EQ(tester2.getItems().getSize(), FwSizeType(State::capacity)); + ASSERT_EQ(tester2.getEnqueueIndex().getValue(), 1); + ASSERT_EQ(tester2.getDequeueIndex().getValue(), 0); + ASSERT_EQ(q2.getSize(), 1); +} + +TEST(ExternalFifoQueue, CopyAssignmentOperator) { + State::ItemType items[State::capacity]; + // Call the constructor providing backing storage + Queue q1(items, State::capacity); + // Enqueue an item + const State::ItemType item = State::getRandomItem(); + const auto status = q1.enqueue(item); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + Queue q2; + ASSERT_EQ(q2.getSize(), 0); + // Call the copy assignment operator + q2 = q1; + ASSERT_EQ(q2.getSize(), 1); +} + +TEST(ExternalFifoQueue, CopyDataFrom) { + constexpr FwSizeType maxSize = State::capacity; + constexpr FwSizeType smallSize = maxSize / 2; + State::ItemType items1[maxSize]; + State::ItemType items2[maxSize]; + Queue q1(items1, maxSize); + // size1 < capacity2 + { + Queue q2(items2, maxSize); + State::testCopyDataFrom(q1, smallSize, q2); + } + // size1 == capacity2 + { + Queue q2(items2, maxSize); + State::testCopyDataFrom(q1, maxSize, q2); + } + // size1 > capacity2 + { + Queue q2(items2, smallSize); + State::testCopyDataFrom(q1, maxSize, q2); + } +} + +TEST(ExternalFifoQueueScenarios, At) { + State::ItemType items[State::capacity]; + State::ExternalQueue queue(items, State::capacity); + State state(queue); + Scenarios::enqueueOK(state); +} + +TEST(ExternalFifoQueueScenarios, Clear) { + State::ItemType items[State::capacity]; + State::ExternalQueue queue(items, State::capacity); + State state(queue); + Scenarios::clear(state); +} + +TEST(ExternalFifoQueueScenarios, DequeueEmpty) { + State::ItemType items[State::capacity]; + State::ExternalQueue queue(items, State::capacity); + State state(queue); + Scenarios::dequeueEmpty(state); +} + +TEST(ExternalFifoQueueScenarios, DequeueOK) { + State::ItemType items[State::capacity]; + State::ExternalQueue queue(items, State::capacity); + State state(queue); + Scenarios::dequeueOK(state); +} + +TEST(ExternalFifoQueueScenarios, EnqueueFull) { + State::ItemType items[State::capacity]; + State::ExternalQueue queue(items, State::capacity); + State state(queue); + Scenarios::enqueueFull(state); +} + +TEST(ExternalFifoQueueScenarios, EnqueueOK) { + State::ItemType items[State::capacity]; + State::ExternalQueue queue(items, State::capacity); + State state(queue); + Rules::enqueueOK.apply(state); +} + +TEST(ExternalFifoQueueTestScenarios, Peek) { + State::ItemType items[State::capacity]; + State::ExternalQueue queue(items, State::capacity); + State state(queue); + Scenarios::enqueueOK(state); + Scenarios::enqueueOK(state); + Scenarios::peek(state); +} + +TEST(ExternalFifoQueueScenarios, Random) { + State::ItemType items[State::capacity]; + State::ExternalQueue queue(items, State::capacity); + State state(queue); + Scenarios::random(Fw::String("ExternalFifoQueueRandom"), state, 1000); +} + +} // namespace FifoQueueTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/ExternalStackTest.cpp b/Fw/DataStructures/test/ut/ExternalStackTest.cpp new file mode 100644 index 00000000000..8666ca92e18 --- /dev/null +++ b/Fw/DataStructures/test/ut/ExternalStackTest.cpp @@ -0,0 +1,168 @@ +// ====================================================================== +// \title ExternalStackTest.cpp +// \author bocchino +// \brief cpp file for ExternalStack tests +// ====================================================================== + +#include "Fw/DataStructures/ExternalStack.hpp" +#include "Fw/DataStructures/test/ut/STest/StackTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/StackTestScenarios.hpp" + +namespace Fw { + +template +class ExternalStackTester { + public: + ExternalStackTester(const ExternalStack& stack) : m_stack(stack) {} + + const ExternalArray getItems() const { return this->m_stack.m_items; } + + private: + const ExternalStack& m_stack; +}; + +namespace StackTest { + +using TestStack = ExternalStack; +using StackTester = ExternalStackTester; + +TEST(ExternalStack, ZeroArgConstructor) { + TestStack stack; + ASSERT_EQ(stack.getCapacity(), 0); + ASSERT_EQ(stack.getSize(), 0); +} + +TEST(ExternalStack, TypedStorageConstructor) { + State::ItemType items[State::capacity]; + TestStack stack(items, State::capacity); + StackTester tester(stack); + ASSERT_EQ(tester.getItems().getElements(), items); + ASSERT_EQ(stack.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(stack.getSize(), 0); +} + +TEST(ExternalStack, UntypedStorageConstructor) { + constexpr U8 alignment = TestStack::getByteArrayAlignment(); + constexpr FwSizeType byteArraySize = TestStack::getByteArraySize(State::capacity); + alignas(alignment) U8 bytes[byteArraySize]; + TestStack stack(ByteArray(&bytes[0], sizeof bytes), State::capacity); + StackTester tester(stack); + ASSERT_EQ(tester.getItems().getElements(), reinterpret_cast(bytes)); + ASSERT_EQ(stack.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(stack.getSize(), 0); +} + +TEST(ExternalStack, CopyConstructor) { + State::ItemType items[State::capacity]; + // Call the constructor providing backing storage + TestStack s1(items, State::capacity); + // Push an item + const auto item = State::getRandomItem(); + const auto status = s1.push(item); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + TestStack s2(s1); + StackTester tester1(s1); + StackTester tester2(s2); + ASSERT_EQ(tester2.getItems().getElements(), items); + ASSERT_EQ(tester2.getItems().getSize(), FwSizeType(State::capacity)); + ASSERT_EQ(s2.getSize(), 1); +} + +TEST(ExternalStack, CopyAssignmentOperator) { + State::ItemType items[State::capacity]; + // Call the constructor providing backing storage + TestStack s1(items, State::capacity); + // Push an item + const auto item = State::getRandomItem(); + const auto status = s1.push(item); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + TestStack s2; + ASSERT_EQ(s2.getSize(), 0); + // Call the copy assignment operator + s2 = s1; + ASSERT_EQ(s2.getSize(), 1); +} + +TEST(ExternalStack, CopyDataFrom) { + constexpr FwSizeType maxSize = State::capacity; + constexpr FwSizeType smallSize = maxSize / 2; + State::ItemType items1[maxSize]; + State::ItemType items2[maxSize]; + TestStack s1(items1, maxSize); + // size1 < capacity2 + { + TestStack s2(items2, maxSize); + State::testCopyDataFrom(s1, smallSize, s2); + } + // size1 == capacity2 + { + TestStack s2(items2, maxSize); + State::testCopyDataFrom(s1, maxSize, s2); + } + // size1 > capacity2 + { + ExternalStack s2(items2, smallSize); + State::testCopyDataFrom(s1, maxSize, s2); + } +} + +TEST(ExternalStackScenarios, At) { + State::ItemType items[State::capacity]; + TestStack stack(items, State::capacity); + State state(stack); + Scenarios::at(state); +} + +TEST(ExternalStackScenarios, Clear) { + State::ItemType items[State::capacity]; + TestStack stack(items, State::capacity); + State state(stack); + Scenarios::clear(state); +} + +TEST(ExternalStackScenarios, Peek) { + State::ItemType items[State::capacity]; + TestStack stack(items, State::capacity); + State state(stack); + Scenarios::peek(state); +} + +TEST(ExternalStackScenarios, PopEmpty) { + State::ItemType items[State::capacity]; + TestStack stack(items, State::capacity); + State state(stack); + Rules::popEmpty.apply(state); +} + +TEST(ExternalStackScenarios, PopOK) { + State::ItemType items[State::capacity]; + TestStack stack(items, State::capacity); + State state(stack); + Scenarios::popOK(state); +} + +TEST(ExternalStackScenarios, PushFull) { + State::ItemType items[State::capacity]; + TestStack stack(items, State::capacity); + State state(stack); + Scenarios::pushFull(state); +} + +TEST(ExternalStackScenarios, PushOK) { + State::ItemType items[State::capacity]; + TestStack stack(items, State::capacity); + State state(stack); + Scenarios::pushOK(state); +} + +TEST(ExternalStackScenarios, Random) { + State::ItemType items[State::capacity]; + TestStack stack(items, State::capacity); + State state(stack); + Scenarios::random(Fw::String("ExternalStackRandom"), state, 1000); +} + +} // namespace StackTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/FifoQueueTest.cpp b/Fw/DataStructures/test/ut/FifoQueueTest.cpp new file mode 100644 index 00000000000..f98c2f8832d --- /dev/null +++ b/Fw/DataStructures/test/ut/FifoQueueTest.cpp @@ -0,0 +1,136 @@ +// ====================================================================== +// \title FifoQueueTest.cpp +// \author bocchino +// \brief cpp file for FifoQueue tests +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/FifoQueueTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/FifoQueueTestScenarios.hpp" + +namespace Fw { + +template +class FifoQueueTester { + public: + FifoQueueTester(const FifoQueue& queue) : m_queue(queue) {} + + const ExternalFifoQueue getExtQueue() const { return this->m_queue.extQueue; } + + const typename Array::Elements& getItems() const { return this->m_queue.m_items; } + + private: + const FifoQueue& m_queue; +}; + +namespace FifoQueueTest { + +using Queue = FifoQueue; +using QueueTester = FifoQueueTester; + +TEST(FifoQueue, ZeroArgConstructor) { + Queue queue; + ASSERT_EQ(queue.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(queue.getSize(), 0); +} + +TEST(FifoQueue, CopyConstructor) { + // Construct q1 + Queue q1; + // Enqueue an item + const auto item = State::getRandomItem(); + const auto status = q1.enqueue(item); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(q1.getSize(), 1); + // Use the copy constructor to construct q2 + Queue q2(q1); + ASSERT_EQ(q2.getSize(), 1); +} + +TEST(FifoQueue, CopyAssignmentOperator) { + // Call the constructor providing backing storage + Queue q1; + // Enqueue an item + const auto item = State::getRandomItem(); + (void)q1.enqueue(item); + // Call the default constructor + Queue q2; + ASSERT_EQ(q2.getSize(), 0); + // Call the copy assignment operator + q2 = q1; + ASSERT_EQ(q2.getSize(), 1); +} + +TEST(FifoQueue, CopyDataFrom) { + constexpr FwSizeType maxSize = State::capacity; + constexpr FwSizeType smallSize = maxSize / 2; + Queue q1; + // size1 < capacity2 + { + Queue q2; + State::testCopyDataFrom(q1, smallSize, q2); + } + // size1 == capacity2 + { + Queue q2; + State::testCopyDataFrom(q1, maxSize, q2); + } + // size1 > capacity2 + { + FifoQueue q2; + State::testCopyDataFrom(q1, maxSize, q2); + } +} + +TEST(FifoQueueTestScenarios, At) { + State::Queue queue; + State state(queue); + Scenarios::at(state); +} + +TEST(FifoQueueTestScenarios, Clear) { + State::Queue queue; + State state(queue); + Scenarios::clear(state); +} + +TEST(FifoQueueTestScenarios, DequeueEmpty) { + State::Queue queue; + State state(queue); + Scenarios::dequeueEmpty(state); +} + +TEST(FifoQueueTestScenarios, DequeueOK) { + State::Queue queue; + State state(queue); + Scenarios::dequeueOK(state); +} + +TEST(FifoQueueTestScenarios, EnqueueFull) { + State::Queue queue; + State state(queue); + Scenarios::enqueueFull(state); +} + +TEST(FifoQueueTestScenarios, EnqueueOK) { + State::Queue queue; + State state(queue); + Scenarios::enqueueOK(state); +} + +TEST(FifoQueueTestScenarios, Peek) { + State::Queue queue; + State state(queue); + Scenarios::enqueueOK(state); + Scenarios::enqueueOK(state); + Scenarios::peek(state); +} + +TEST(FifoQueueScenarios, Random) { + State::Queue queue; + State state(queue); + Scenarios::random(Fw::String("FifoQueueRandom"), state, 1000); +} + +} // namespace FifoQueueTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestRules.cpp b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestRules.cpp new file mode 100644 index 00000000000..088b68e62b3 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestRules.cpp @@ -0,0 +1,35 @@ +// ====================================================================== +// \title ArraySetOrMapImplTestRules.cpp +// \author Rob Bocchino +// \brief cpp file for ArraySetOrMapImpl test rules +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestRules.hpp" + +namespace Fw { + +namespace ArraySetOrMapImplTest { + +namespace Rules { + +Clear clear; + +Find find; + +FindExisting findExisting; + +InsertExisting insertExisting; + +InsertFull insertFull; + +InsertNotFull insertNotFull; + +Remove remove; + +RemoveExisting removeExisting; + +} // namespace Rules + +} // namespace ArraySetOrMapImplTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestRules.hpp b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestRules.hpp new file mode 100644 index 00000000000..609bfb2612d --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestRules.hpp @@ -0,0 +1,189 @@ +// ====================================================================== +// \title ArraySetOrMapImplTestRules.hpp +// \author bocchino +// \brief hpp file for ArraySetOrMapImpl test rules +// ====================================================================== + +#ifndef ArraySetOrMapImplTestRules_HPP +#define ArraySetOrMapImplTestRules_HPP + +#include + +#include "Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestState.hpp" +#include "STest/STest/Pick/Pick.hpp" +#include "STest/STest/Rule/Rule.hpp" + +namespace Fw { + +namespace ArraySetOrMapImplTest { + +using Rule = STest::Rule; + +namespace Rules { + +struct Clear : public Rule { + Clear() : Rule("Clear") {} + bool precondition(const State& state) { return state.impl.getSize() > 0; } + void action(State& state) { + state.impl.clear(); + ASSERT_EQ(state.impl.getSize(), 0); + state.modelMap.clear(); + } +}; + +struct Find : public Rule { + Find() : Rule("Find") {} + bool precondition(const State& state) { return true; } + void action(State& state) { + const auto key = state.getKey(); + State::ValueType value = 0; + const auto status = state.impl.find(key, value); + if (state.modelMapContains(key)) { + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, state.modelMap[key]); + } else { + ASSERT_EQ(status, Success::FAILURE); + } + } +}; + +struct FindExisting : public Rule { + FindExisting() : Rule("FindExisting") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) > 0; } + void action(State& state) { + const auto size = state.impl.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.impl.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it.increment(); + } + ASSERT_TRUE(it.isInRange()); + const auto key = it.getEntry().getKeyOrElement(); + const auto expectedValue = state.modelMap[key]; + State::ValueType value = 0; + const auto status = state.impl.find(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, expectedValue); + } +}; + +struct InsertExisting : public Rule { + InsertExisting() : Rule("InsertExisting") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) > 0; } + void action(State& state) { + const auto size = state.impl.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.impl.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it.increment(); + } + ASSERT_TRUE(it.isInRange()); + const auto key = it.getEntry().getKeyOrElement(); + const auto value = state.getValue(); + const auto status = state.impl.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + state.modelMap[key] = value; + ASSERT_EQ(state.impl.getSize(), size); + } +}; + +struct InsertFull : public Rule { + InsertFull() : Rule("InsertFull") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) >= State::capacity; } + void action(State& state) { + const auto key = state.getKey(); + const auto value = state.getValue(); + const auto size = state.impl.getSize(); + const auto expectedStatus = state.modelMapContains(key) ? Success::SUCCESS : Success::FAILURE; + const auto status = state.impl.insert(key, value); + ASSERT_EQ(status, expectedStatus); + ASSERT_EQ(state.impl.getSize(), size); + } +}; + +struct InsertNotFull : public Rule { + InsertNotFull() : Rule("InsertNotFull") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) < State::capacity; } + void action(State& state) { + const auto key = state.getKey(); + const auto value = state.getValue(); + const auto size = state.impl.getSize(); + const auto expectedSize = state.modelMapContains(key) ? size : size + 1; + const auto status = state.impl.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(state.impl.getSize(), expectedSize); + state.modelMap[key] = value; + } +}; + +struct Remove : public Rule { + Remove() : Rule("Remove") {} + bool precondition(const State& state) { return true; } + void action(State& state) { + const auto size = state.impl.getSize(); + ASSERT_EQ(size, state.modelMap.size()); + const auto key = state.getKey(); + State::ValueType value = 0; + const auto status = state.impl.remove(key, value); + if (state.modelMap.count(key) != 0) { + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, state.modelMap[key]); + ASSERT_EQ(state.impl.getSize(), size - 1); + } else { + ASSERT_EQ(status, Success::FAILURE); + ASSERT_EQ(state.impl.getSize(), size); + } + (void)state.modelMap.erase(key); + ASSERT_EQ(state.impl.getSize(), state.modelMap.size()); + } +}; + +struct RemoveExisting : public Rule { + RemoveExisting() : Rule("RemoveExisting") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) > 0; } + void action(State& state) { + const auto size = state.impl.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.impl.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it.increment(); + } + ASSERT_TRUE(it.isInRange()); + const auto key = it.getEntry().getKeyOrElement(); + const auto expectedValue = state.modelMap[key]; + State::ValueType value = 0; + const auto status = state.impl.remove(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, expectedValue); + const auto n = state.modelMap.erase(key); + ASSERT_EQ(n, 1); + ASSERT_EQ(state.impl.getSize(), state.modelMap.size()); + } +}; + +extern Clear clear; + +extern Find find; + +extern FindExisting findExisting; + +extern InsertExisting insertExisting; + +extern InsertFull insertFull; + +extern InsertNotFull insertNotFull; + +extern Remove remove; + +extern RemoveExisting removeExisting; + +} // namespace Rules + +} // namespace ArraySetOrMapImplTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestScenarios.cpp b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestScenarios.cpp new file mode 100644 index 00000000000..e06ce310071 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestScenarios.cpp @@ -0,0 +1,32 @@ +// ====================================================================== +// \title ArraySetOrMapImplTestScenarios.cpp +// \author Rob Bocchino +// \brief ArraySetOrMapImpl test scenarios +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestScenarios.hpp" +#include "Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestRules.hpp" +#include "STest/Scenario/BoundedScenario.hpp" +#include "STest/Scenario/RandomScenario.hpp" + +namespace Fw { + +namespace ArraySetOrMapImplTest { + +namespace Scenarios { + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps) { + Rule* rules[] = {&Rules::clear, &Rules::find, &Rules::findExisting, &Rules::insertExisting, + &Rules::insertFull, &Rules::insertNotFull, &Rules::remove, &Rules::removeExisting}; + STest::RandomScenario scenario("RandomScenario", rules, + sizeof(rules) / sizeof(STest::RandomScenario*)); + STest::BoundedScenario boundedScenario(name.toChar(), scenario, maxNumSteps); + const U32 numSteps = boundedScenario.run(state); + printf("Ran %u steps.\n", numSteps); +} + +} // namespace Scenarios + +} // namespace ArraySetOrMapImplTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestScenarios.hpp b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestScenarios.hpp new file mode 100644 index 00000000000..311684d5b16 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestScenarios.hpp @@ -0,0 +1,26 @@ +// ====================================================================== +// \title ArraySetOrMapImplTestScenarios.hpp +// \author Rob Bocchino +// \brief ArraySetOrMapImpl test scenarios +// ====================================================================== + +#ifndef ArraySetOrMapImplTestScenarios_HPP +#define ArraySetOrMapImplTestScenarios_HPP + +#include "Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestState.hpp" + +namespace Fw { + +namespace ArraySetOrMapImplTest { + +namespace Scenarios { + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps); + +} // namespace Scenarios + +} // namespace ArraySetOrMapImplTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestState.hpp b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestState.hpp new file mode 100644 index 00000000000..10131e3f9f7 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/ArraySetOrMapImplTestState.hpp @@ -0,0 +1,61 @@ +// ====================================================================== +// \title ArraySetOrMapImplTestState.hpp +// \author bocchino +// \brief hpp file for ArraySetOrMapImpl test state +// ====================================================================== + +#ifndef ArraySetOrMapImplTestState_HPP +#define ArraySetOrMapImplTestState_HPP + +#include + +#include "Fw/DataStructures/ArraySetOrMapImpl.hpp" +#include "Fw/DataStructures/test/ut/ArraySetOrMapImplTester.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +namespace ArraySetOrMapImplTest { + +struct State { + //! The key type + using KeyType = U16; + //! The value type + using ValueType = U32; + //! The array set or map capacity + static constexpr FwSizeType capacity = 1024; + //! The Impl type + using Impl = ArraySetOrMapImpl; + //! The Tester type + using Tester = ArraySetOrMapImplTester; + //! The entry type + using Entry = SetOrMapImplEntry; + //! Constructor + State(Impl& a_impl) : impl(a_impl), tester(a_impl) {} + //! The array set or map under test + Impl& impl; + //! The tester + Tester tester; + //! The map for modeling correct behavior + std::map modelMap; + //! Whether to use the stored key + bool useStoredKey = false; + //! The stored key + KeyType storedKey = 0; + //! Whether to use the stored value + bool useStoredValue = false; + //! The stored value + ValueType storedValue = 0; + //! Get a key + KeyType getKey() const { return useStoredKey ? storedKey : static_cast(STest::Pick::any()); } + //! Get a value + ValueType getValue() const { return useStoredValue ? storedValue : static_cast(STest::Pick::any()); } + //! Check whether the model map contains the specified key + bool modelMapContains(KeyType key) const { return modelMap.count(key) != 0; } +}; + +} // namespace ArraySetOrMapImplTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/FifoQueueTestRules.cpp b/Fw/DataStructures/test/ut/STest/FifoQueueTestRules.cpp new file mode 100644 index 00000000000..ca8213a5090 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/FifoQueueTestRules.cpp @@ -0,0 +1,33 @@ +// ====================================================================== +// \title FifoQueueTestRules.cpp +// \author Rob Bocchino +// \brief cpp file for FifoQueue test rules +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/FifoQueueTestRules.hpp" + +namespace Fw { + +namespace FifoQueueTest { + +namespace Rules { + +At at; + +Clear clear; + +DequeueEmpty dequeueEmpty; + +DequeueOK dequeueOK; + +EnqueueFull enqueueFull; + +EnqueueOK enqueueOK; + +Peek peek; + +} // namespace Rules + +} // namespace FifoQueueTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/FifoQueueTestRules.hpp b/Fw/DataStructures/test/ut/STest/FifoQueueTestRules.hpp new file mode 100644 index 00000000000..507d75f2f61 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/FifoQueueTestRules.hpp @@ -0,0 +1,118 @@ +// ====================================================================== +// \title FifoQueueTestRules.hpp +// \author bocchino +// \brief hpp file for FIFO queue test rules +// ====================================================================== + +#ifndef FifoQueueTestRules_HPP +#define FifoQueueTestRules_HPP + +#include "Fw/DataStructures/test/ut/STest/FifoQueueTestState.hpp" +#include "STest/STest/Pick/Pick.hpp" +#include "STest/STest/Rule/Rule.hpp" + +namespace Fw { + +namespace FifoQueueTest { + +using Rule = STest::Rule; + +namespace Rules { + +struct At : public Rule { + At() : Rule("At") {} + bool precondition(const State& state) { return !state.queue.isEmpty(); } + void action(State& state) { + const auto index = STest::Pick::startLength(0, static_cast(state.queue.getSize())); + ASSERT_EQ(state.queue.at(index), state.modelQueue.at(index)); + } +}; + +struct Clear : public Rule { + Clear() : Rule("Clear") {} + bool precondition(const State& state) { return !state.queue.isEmpty(); } + void action(State& state) { + state.queue.clear(); + ASSERT_EQ(state.queue.getSize(), 0); + state.modelQueue.clear(); + } +}; + +struct DequeueEmpty : public Rule { + DequeueEmpty() : Rule("DequeueEmpty") {} + bool precondition(const State& state) { return state.queue.isEmpty(); } + void action(State& state) { + State::ItemType item = 0; + const auto status = state.queue.dequeue(item); + ASSERT_EQ(status, Success::FAILURE); + } +}; + +struct DequeueOK : public Rule { + DequeueOK() : Rule("DequeueOK") {} + bool precondition(const State& state) { return !state.queue.isEmpty(); } + void action(State& state) { + State::ItemType item = 0; + const auto status = state.queue.dequeue(item); + ASSERT_EQ(status, Success::SUCCESS); + const auto expectedItem = state.modelQueue.at(0); + ASSERT_EQ(item, expectedItem); + state.modelQueue.pop_front(); + ASSERT_EQ(state.queue.getSize(), state.modelQueue.size()); + } +}; + +struct EnqueueFull : public Rule { + EnqueueFull() : Rule("EnqueueFull") {} + bool precondition(const State& state) { return state.queue.isFull(); } + void action(State& state) { + const auto item = State::getRandomItem(); + const auto status = state.queue.enqueue(item); + ASSERT_EQ(status, Success::FAILURE); + } +}; + +struct EnqueueOK : public Rule { + EnqueueOK() : Rule("EnqueueOK") {} + bool precondition(const State& state) { return !state.queue.isFull(); } + void action(State& state) { + const auto item = State::getRandomItem(); + const auto status = state.queue.enqueue(item); + ASSERT_EQ(status, Success::SUCCESS); + state.modelQueue.push_back(item); + } +}; + +struct Peek : public Rule { + Peek() : Rule("Peek") {} + bool precondition(const State& state) { return !state.queue.isEmpty(); } + void action(State& state) { + const auto index = STest::Pick::startLength(0, static_cast(state.queue.getSize())); + State::ItemType item = 0; + const auto status = state.queue.peek(item, index); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(item, state.modelQueue.at(index)); + } +}; + +extern At at; + +extern Clear clear; + +extern DequeueEmpty dequeueEmpty; + +extern DequeueOK dequeueOK; + +extern EnqueueFull enqueueFull; + +extern EnqueueOK enqueueOK; + +extern Peek peek; + +} // namespace Rules + +} // namespace FifoQueueTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/FifoQueueTestScenarios.cpp b/Fw/DataStructures/test/ut/STest/FifoQueueTestScenarios.cpp new file mode 100644 index 00000000000..b42cd4a3548 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/FifoQueueTestScenarios.cpp @@ -0,0 +1,68 @@ +// ====================================================================== +// \title FifoQueueTestScenarios.cpp +// \author Rob Bocchino +// \brief FifoQueue test scenarios +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/FifoQueueTestScenarios.hpp" +#include "Fw/DataStructures/test/ut/STest/FifoQueueTestRules.hpp" +#include "STest/Scenario/BoundedScenario.hpp" +#include "STest/Scenario/RandomScenario.hpp" + +namespace Fw { + +namespace FifoQueueTest { + +namespace Scenarios { + +void at(State& state) { + Rules::enqueueOK.apply(state); + Rules::at.apply(state); +} + +void clear(State& state) { + Rules::enqueueOK.apply(state); + ASSERT_EQ(state.queue.getSize(), 1); + Rules::clear.apply(state); + ASSERT_EQ(state.queue.getSize(), 0); +} + +void dequeueEmpty(State& state) { + Rules::dequeueEmpty.apply(state); +} + +void dequeueOK(State& state) { + Rules::enqueueOK.apply(state); + Rules::dequeueOK.apply(state); +} + +void enqueueFull(State& state) { + for (FwSizeType i = 0; i < State::capacity; i++) { + Rules::enqueueOK.apply(state); + } + Rules::enqueueFull.apply(state); +} + +void enqueueOK(State& state) { + Rules::enqueueOK.apply(state); +} + +void peek(State& state) { + Rules::peek.apply(state); +} + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps) { + Rule* rules[] = {&Rules::at, &Rules::clear, &Rules::dequeueEmpty, &Rules::dequeueOK, + &Rules::enqueueFull, &Rules::enqueueOK, &Rules::peek}; + STest::RandomScenario scenario("RandomScenario", rules, + sizeof(rules) / sizeof(STest::RandomScenario*)); + STest::BoundedScenario boundedScenario(name.toChar(), scenario, maxNumSteps); + const U32 numSteps = boundedScenario.run(state); + printf("Ran %u steps.\n", numSteps); +} + +} // namespace Scenarios + +} // namespace FifoQueueTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/FifoQueueTestScenarios.hpp b/Fw/DataStructures/test/ut/STest/FifoQueueTestScenarios.hpp new file mode 100644 index 00000000000..bb89fbb1811 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/FifoQueueTestScenarios.hpp @@ -0,0 +1,40 @@ +// ====================================================================== +// \title FifoQueueTestScenarios.hpp +// \author Rob Bocchino +// \brief FifoQueue test scenarios +// ====================================================================== + +#ifndef FifoQueueTestScenarios_HPP +#define FifoQueueTestScenarios_HPP + +#include "Fw/DataStructures/test/ut/STest/FifoQueueTestState.hpp" + +namespace Fw { + +namespace FifoQueueTest { + +namespace Scenarios { + +void at(State& state); + +void clear(State& state); + +void dequeueEmpty(State& state); + +void dequeueOK(State& state); + +void enqueueFull(State& state); + +void enqueueOK(State& state); + +void peek(State& state); + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps); + +} // namespace Scenarios + +} // namespace FifoQueueTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/FifoQueueTestState.hpp b/Fw/DataStructures/test/ut/STest/FifoQueueTestState.hpp new file mode 100644 index 00000000000..382a0d4ed9b --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/FifoQueueTestState.hpp @@ -0,0 +1,64 @@ +// ====================================================================== +// \title FifoQueueTestState.hpp +// \author bocchino +// \brief hpp file for FIFO queue test state +// ====================================================================== + +#ifndef FifoQueueTestState_HPP +#define FifoQueueTestState_HPP + +#include +#include + +#include "Fw/DataStructures/FifoQueue.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +namespace FifoQueueTest { + +struct State { + //! The queue item type + using ItemType = U32; + //! The queue capacity + static constexpr FwSizeType capacity = 1024; + //! The Queue type + using Queue = FifoQueue; + //! The ExternalQueue type + using ExternalQueue = ExternalFifoQueue; + //! The QueueBase type + using QueueBase = FifoQueueBase; + //! Constructor + State(QueueBase& a_queue) : queue(a_queue) {} + //! The queue under test + QueueBase& queue; + //! The queue for modeling correct behavior + std::deque modelQueue; + //! Get a random item + static ItemType getRandomItem() { return STest::Pick::any(); } + //! Test copy data from + static void testCopyDataFrom(QueueBase& q1, FwSizeType size1, QueueBase& q2) { + q1.clear(); + for (FwSizeType i = 0; i < size1; i++) { + const auto status = q1.enqueue(static_cast(i)); + ASSERT_EQ(status, Success::SUCCESS); + } + q2.copyDataFrom(q1); + const auto capacity2 = q2.getCapacity(); + const FwSizeType size = FW_MIN(size1, capacity2); + for (FwSizeType i = 0; i < size; i++) { + U32 val1 = 0; + auto status = q1.peek(val1, i); + ASSERT_EQ(status, Success::SUCCESS); + U32 val2 = 1; + status = q2.peek(val2, i); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(val1, val2); + } + } +}; + +} // namespace FifoQueueTest + +} // namespace Fw +#endif diff --git a/Fw/DataStructures/test/ut/STest/MapTestRules.cpp b/Fw/DataStructures/test/ut/STest/MapTestRules.cpp new file mode 100644 index 00000000000..41179ffb8bf --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/MapTestRules.cpp @@ -0,0 +1,35 @@ +// ====================================================================== +// \title MapTestRules.cpp +// \author Rob Bocchino +// \brief cpp file for ArrayMap test rules +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/MapTestRules.hpp" + +namespace Fw { + +namespace MapTest { + +namespace Rules { + +Clear clear; + +Find find; + +FindExisting findExisting; + +InsertExisting insertExisting; + +InsertFull insertFull; + +InsertNotFull insertNotFull; + +Remove remove; + +RemoveExisting removeExisting; + +} // namespace Rules + +} // namespace MapTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/MapTestRules.hpp b/Fw/DataStructures/test/ut/STest/MapTestRules.hpp new file mode 100644 index 00000000000..6d8f48330b0 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/MapTestRules.hpp @@ -0,0 +1,181 @@ +// ====================================================================== +// \title MapTestRules.hpp +// \author bocchino +// \brief hpp file for map test rules +// ====================================================================== + +#ifndef MapTestRules_HPP +#define MapTestRules_HPP + +#include "Fw/DataStructures/test/ut/STest/MapTestState.hpp" +#include "STest/STest/Pick/Pick.hpp" +#include "STest/STest/Rule/Rule.hpp" + +namespace Fw { + +namespace MapTest { + +using Rule = STest::Rule; + +namespace Rules { + +struct Clear : public Rule { + Clear() : Rule("Clear") {} + bool precondition(const State& state) { return !state.map.isEmpty(); } + void action(State& state) { + state.map.clear(); + ASSERT_EQ(state.map.getSize(), 0); + state.modelMap.clear(); + } +}; + +struct Find : public Rule { + Find() : Rule("Find") {} + bool precondition(const State& state) { return true; } + void action(State& state) { + const auto key = state.getKey(); + State::ValueType value = 0; + const auto status = state.map.find(key, value); + if (state.modelMapContains(key)) { + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, state.modelMap[key]); + } else { + ASSERT_EQ(status, Success::FAILURE); + } + } +}; + +struct FindExisting : public Rule { + FindExisting() : Rule("FindExisting") {} + bool precondition(const State& state) { return !state.map.isEmpty(); } + void action(State& state) { + for (auto& entry : state.map) { + const auto key = entry.getKey(); + const auto expectedValue = state.modelMap[key]; + State::ValueType value = 0; + const auto status = state.map.find(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, expectedValue); + } + } +}; + +struct InsertExisting : public Rule { + InsertExisting() : Rule("InsertExisting") {} + bool precondition(const State& state) { return !state.map.isEmpty(); } + void action(State& state) { + const auto size = state.map.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.map.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it++; + } + ASSERT_TRUE(it.isInRange()); + const auto key = it->getKey(); + const auto value = state.getValue(); + const auto status = state.map.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + state.modelMap[key] = value; + ASSERT_EQ(state.map.getSize(), size); + } +}; + +struct InsertFull : public Rule { + InsertFull() : Rule("InsertFull") {} + bool precondition(const State& state) { return state.map.isFull(); } + void action(State& state) { + const auto key = state.getKey(); + const auto value = state.getValue(); + const auto size = state.map.getSize(); + const auto expectedStatus = state.modelMapContains(key) ? Success::SUCCESS : Success::FAILURE; + const auto status = state.map.insert(key, value); + ASSERT_EQ(status, expectedStatus); + ASSERT_EQ(state.map.getSize(), size); + } +}; + +struct InsertNotFull : public Rule { + InsertNotFull() : Rule("InsertNotFull") {} + bool precondition(const State& state) { return !state.map.isFull(); } + void action(State& state) { + const auto key = state.getKey(); + const auto value = state.getValue(); + const auto size = state.map.getSize(); + const auto expectedSize = state.modelMapContains(key) ? size : size + 1; + const auto status = state.map.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(state.map.getSize(), expectedSize); + state.modelMap[key] = value; + } +}; + +struct Remove : public Rule { + Remove() : Rule("Remove") {} + bool precondition(const State& state) { return true; } + void action(State& state) { + const auto size = state.map.getSize(); + ASSERT_EQ(size, state.modelMap.size()); + const auto key = state.getKey(); + State::ValueType value = 0; + const auto status = state.map.remove(key, value); + if (state.modelMapContains(key)) { + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, state.modelMap[key]); + ASSERT_EQ(state.map.getSize(), size - 1); + } else { + ASSERT_EQ(status, Success::FAILURE); + ASSERT_EQ(state.map.getSize(), size); + } + (void)state.modelMap.erase(key); + ASSERT_EQ(state.map.getSize(), state.modelMap.size()); + } +}; + +struct RemoveExisting : public Rule { + RemoveExisting() : Rule("RemoveExisting") {} + bool precondition(const State& state) { return !state.map.isEmpty(); } + void action(State& state) { + const auto size = state.map.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.map.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it++; + } + ASSERT_TRUE(it.isInRange()); + const auto key = (*it).getKey(); + const auto expectedValue = state.modelMap[key]; + State::ValueType value = 0; + const auto status = state.map.remove(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, expectedValue); + const auto n = state.modelMap.erase(key); + ASSERT_EQ(n, 1); + ASSERT_EQ(state.map.getSize(), state.modelMap.size()); + } +}; + +extern Clear clear; + +extern Find find; + +extern FindExisting findExisting; + +extern InsertExisting insertExisting; + +extern InsertFull insertFull; + +extern InsertNotFull insertNotFull; + +extern Remove remove; + +extern RemoveExisting removeExisting; + +} // namespace Rules + +} // namespace MapTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/MapTestScenarios.cpp b/Fw/DataStructures/test/ut/STest/MapTestScenarios.cpp new file mode 100644 index 00000000000..75fdd7c3861 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/MapTestScenarios.cpp @@ -0,0 +1,82 @@ +// ====================================================================== +// \title MapTestScenarios.cpp +// \author Rob Bocchino +// \brief Map test scenarios +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/MapTestScenarios.hpp" +#include "Fw/DataStructures/test/ut/STest/MapTestRules.hpp" +#include "STest/Scenario/BoundedScenario.hpp" +#include "STest/Scenario/RandomScenario.hpp" + +namespace Fw { + +namespace MapTest { + +namespace Scenarios { + +void clear(State& state) { + Rules::insertNotFull.apply(state); + ASSERT_EQ(state.map.getSize(), 1); + Rules::clear.apply(state); + ASSERT_EQ(state.map.getSize(), 0); +} + +void find(State& state) { + Rules::find.apply(state); + state.useStoredKey = true; + Rules::insertNotFull.apply(state); + Rules::find.apply(state); +} + +void findExisting(State& state) { + Rules::insertNotFull.apply(state); + Rules::findExisting.apply(state); +} + +void insertExisting(State& state) { + Rules::insertNotFull.apply(state); + Rules::insertExisting.apply(state); +} + +void insertFull(State& state) { + state.useStoredKey = true; + for (FwSizeType i = 0; i < State::capacity; i++) { + state.storedKey = static_cast(i); + Rules::insertNotFull.apply(state); + } + state.useStoredKey = false; + Rules::insertFull.apply(state); +} + +void insertNotFull(State& state) { + Rules::insertNotFull.apply(state); +} + +void remove(State& state) { + state.useStoredKey = true; + Rules::insertNotFull.apply(state); + Rules::remove.apply(state); + Rules::remove.apply(state); +} + +void removeExisting(State& state) { + Rules::insertNotFull.apply(state); + Rules::removeExisting.apply(state); +} + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps) { + Rule* rules[] = {&Rules::clear, &Rules::find, &Rules::findExisting, &Rules::insertExisting, + &Rules::insertFull, &Rules::insertNotFull, &Rules::remove, &Rules::removeExisting}; + STest::RandomScenario scenario("RandomScenario", rules, + sizeof(rules) / sizeof(STest::RandomScenario*)); + STest::BoundedScenario boundedScenario(name.toChar(), scenario, maxNumSteps); + const U32 numSteps = boundedScenario.run(state); + printf("Ran %u steps.\n", numSteps); +} + +} // namespace Scenarios + +} // namespace MapTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/MapTestScenarios.hpp b/Fw/DataStructures/test/ut/STest/MapTestScenarios.hpp new file mode 100644 index 00000000000..09b7c08850b --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/MapTestScenarios.hpp @@ -0,0 +1,42 @@ +// ====================================================================== +// \title MapTestScenarios.hpp +// \author Rob Bocchino +// \brief Map test scenarios +// ====================================================================== + +#ifndef MapTestScenarios_HPP +#define MapTestScenarios_HPP + +#include "Fw/DataStructures/test/ut/STest/MapTestState.hpp" + +namespace Fw { + +namespace MapTest { + +namespace Scenarios { + +void clear(State& state); + +void find(State& state); + +void findExisting(State& state); + +void insertExisting(State& state); + +void insertFull(State& state); + +void insertNotFull(State& state); + +void remove(State& state); + +void removeExisting(State& state); + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps); + +} // namespace Scenarios + +} // namespace MapTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/MapTestState.hpp b/Fw/DataStructures/test/ut/STest/MapTestState.hpp new file mode 100644 index 00000000000..26c65919743 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/MapTestState.hpp @@ -0,0 +1,73 @@ +// ====================================================================== +// \title MapTestState.hpp +// \author bocchino +// \brief hpp file for map test state +// ====================================================================== + +#ifndef MapTestState_HPP +#define MapTestState_HPP + +#include +#include + +#include "Fw/DataStructures/MapBase.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +namespace MapTest { + +struct State { + //! The key type + using KeyType = U16; + //! The value type + using ValueType = U32; + //! The map capacity + static constexpr FwSizeType capacity = 1024; + //! The MapBase type + using MapBaseType = MapBase; + //! Constructor + State(MapBaseType& a_map) : map(a_map) {} + //! The map under test + MapBaseType& map; + //! The map for modeling correct behavior + std::map modelMap; + //! Whether to use the stored key + bool useStoredKey = false; + //! The stored key + KeyType storedKey = 0; + //! Whether to use the stored value + bool useStoredValue = false; + //! The stored value + ValueType storedValue = 0; + //! Get a key + KeyType getKey() const { return useStoredKey ? storedKey : static_cast(STest::Pick::any()); } + //! Get a value + ValueType getValue() const { return useStoredValue ? storedValue : static_cast(STest::Pick::any()); } + //! Check whether the model map contains the specified key + bool modelMapContains(KeyType key) const { return modelMap.count(key) != 0; } + //! Test copy data from + static void testCopyDataFrom(MapBaseType& m1, FwSizeType size1, MapBaseType& m2) { + m1.clear(); + for (FwSizeType i = 0; i < size1; i++) { + const auto status = m1.insert(static_cast(i), static_cast(i)); + ASSERT_EQ(status, Success::SUCCESS); + } + m2.copyDataFrom(m1); + const auto capacity2 = m2.getCapacity(); + const FwSizeType size = FW_MIN(size1, capacity2); + for (FwSizeType i = 0; i < size; i++) { + State::KeyType key = static_cast(i); + U32 val = 0; + const auto status = m2.find(key, val); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(val, static_cast(i)); + } + } +}; + +} // namespace MapTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/SetTestRules.cpp b/Fw/DataStructures/test/ut/STest/SetTestRules.cpp new file mode 100644 index 00000000000..ff4ade40a73 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/SetTestRules.cpp @@ -0,0 +1,35 @@ +// ====================================================================== +// \title SetTestRules.cpp +// \author Rob Bocchino +// \brief cpp file for ArraySet test rules +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/SetTestRules.hpp" + +namespace Fw { + +namespace SetTest { + +namespace Rules { + +Clear clear; + +Find find; + +FindExisting findExisting; + +InsertExisting insertExisting; + +InsertFull insertFull; + +InsertNotFull insertNotFull; + +Remove remove; + +RemoveExisting removeExisting; + +} // namespace Rules + +} // namespace SetTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/SetTestRules.hpp b/Fw/DataStructures/test/ut/STest/SetTestRules.hpp new file mode 100644 index 00000000000..dc2b5636556 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/SetTestRules.hpp @@ -0,0 +1,175 @@ +// ====================================================================== +// \title SetTestRules.hpp +// \author bocchino +// \brief hpp file for set test rules +// ====================================================================== + +#ifndef SetTestRules_HPP +#define SetTestRules_HPP + +#include "Fw/DataStructures/test/ut/STest/SetTestState.hpp" +#include "STest/STest/Pick/Pick.hpp" +#include "STest/STest/Rule/Rule.hpp" + +namespace Fw { + +namespace SetTest { + +using Rule = STest::Rule; + +namespace Rules { + +struct Clear : public Rule { + Clear() : Rule("Clear") {} + bool precondition(const State& state) { return !state.set.isEmpty(); } + void action(State& state) { + state.set.clear(); + ASSERT_EQ(state.set.getSize(), 0); + state.modelSet.clear(); + } +}; + +struct Find : public Rule { + Find() : Rule("Find") {} + bool precondition(const State& state) { return true; } + void action(State& state) { + const auto e = state.getElement(); + const auto status = state.set.find(e); + const auto expectedStatus = state.modelSetContains(e) ? Success::SUCCESS : Success::FAILURE; + ASSERT_EQ(status, expectedStatus); + } +}; + +struct FindExisting : public Rule { + FindExisting() : Rule("FindExisting") {} + bool precondition(const State& state) { return !state.set.isEmpty(); } + void action(State& state) { + // Check that sizes match + const auto size = state.set.getSize(); + const auto modelSize = state.modelSet.size(); + ASSERT_EQ(size, modelSize); + // Check that all elements of set are in modelSet + { + for (auto& e : state.set) { + ASSERT_TRUE(state.modelSetContains(e)); + } + } + // Check that all elements of modelSet are in set + { + for (auto& e : state.modelSet) { + const auto status = state.set.find(e); + ASSERT_EQ(status, Success::SUCCESS); + } + } + } +}; + +struct InsertExisting : public Rule { + InsertExisting() : Rule("InsertExisting") {} + bool precondition(const State& state) { return !state.set.isEmpty(); } + void action(State& state) { + const auto size = state.set.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.set.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it++; + } + ASSERT_TRUE(it.isInRange()); + const auto status = state.set.insert(*it); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(state.set.getSize(), size); + } +}; + +struct InsertFull : public Rule { + InsertFull() : Rule("InsertFull") {} + bool precondition(const State& state) { return state.set.isFull(); } + void action(State& state) { + const auto e = state.getElement(); + const auto size = state.set.getSize(); + const auto expectedStatus = state.modelSetContains(e) ? Success::SUCCESS : Success::FAILURE; + const auto status = state.set.insert(e); + ASSERT_EQ(status, expectedStatus); + ASSERT_EQ(state.set.getSize(), size); + } +}; + +struct InsertNotFull : public Rule { + InsertNotFull() : Rule("InsertNotFull") {} + bool precondition(const State& state) { return !state.set.isFull(); } + void action(State& state) { + const auto e = state.getElement(); + const auto size = state.set.getSize(); + const auto expectedSize = state.modelSetContains(e) ? size : size + 1; + const auto status = state.set.insert(e); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(state.set.getSize(), expectedSize); + state.modelSet.insert(e); + } +}; + +struct Remove : public Rule { + Remove() : Rule("Remove") {} + bool precondition(const State& state) { return true; } + void action(State& state) { + const auto size = state.set.getSize(); + ASSERT_EQ(size, state.modelSet.size()); + const auto e = state.getElement(); + const auto status = state.set.remove(e); + if (state.modelSetContains(e)) { + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(state.set.getSize(), size - 1); + } else { + ASSERT_EQ(status, Success::FAILURE); + ASSERT_EQ(state.set.getSize(), size); + } + (void)state.modelSet.erase(e); + ASSERT_EQ(state.set.getSize(), state.modelSet.size()); + } +}; + +struct RemoveExisting : public Rule { + RemoveExisting() : Rule("RemoveExisting") {} + bool precondition(const State& state) { return !state.set.isEmpty(); } + void action(State& state) { + const auto size = state.set.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.set.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it++; + } + ASSERT_TRUE(it.isInRange()); + const auto e = *it; + const auto status = state.set.remove(e); + ASSERT_EQ(status, Success::SUCCESS); + const auto n = state.modelSet.erase(e); + ASSERT_EQ(n, 1); + ASSERT_EQ(state.set.getSize(), state.modelSet.size()); + } +}; + +extern Clear clear; + +extern Find find; + +extern FindExisting findExisting; + +extern InsertExisting insertExisting; + +extern InsertFull insertFull; + +extern InsertNotFull insertNotFull; + +extern Remove remove; + +extern RemoveExisting removeExisting; + +} // namespace Rules + +} // namespace SetTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/SetTestScenarios.cpp b/Fw/DataStructures/test/ut/STest/SetTestScenarios.cpp new file mode 100644 index 00000000000..53f3d02aa3a --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/SetTestScenarios.cpp @@ -0,0 +1,82 @@ +// ====================================================================== +// \title SetTestScenarios.cpp +// \author Rob Bocchino +// \brief Set test scenarios +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/SetTestScenarios.hpp" +#include "Fw/DataStructures/test/ut/STest/SetTestRules.hpp" +#include "STest/Scenario/BoundedScenario.hpp" +#include "STest/Scenario/RandomScenario.hpp" + +namespace Fw { + +namespace SetTest { + +namespace Scenarios { + +void clear(State& state) { + Rules::insertNotFull.apply(state); + ASSERT_EQ(state.set.getSize(), 1); + Rules::clear.apply(state); + ASSERT_EQ(state.set.getSize(), 0); +} + +void find(State& state) { + Rules::find.apply(state); + state.useStoredElement = true; + Rules::insertNotFull.apply(state); + Rules::find.apply(state); +} + +void findExisting(State& state) { + Rules::insertNotFull.apply(state); + Rules::findExisting.apply(state); +} + +void insertExisting(State& state) { + Rules::insertNotFull.apply(state); + Rules::insertExisting.apply(state); +} + +void insertFull(State& state) { + state.useStoredElement = true; + for (FwSizeType i = 0; i < State::capacity; i++) { + state.storedElement = static_cast(i); + Rules::insertNotFull.apply(state); + } + state.useStoredElement = false; + Rules::insertFull.apply(state); +} + +void insertNotFull(State& state) { + Rules::insertNotFull.apply(state); +} + +void remove(State& state) { + state.useStoredElement = true; + Rules::insertNotFull.apply(state); + Rules::remove.apply(state); + Rules::remove.apply(state); +} + +void removeExisting(State& state) { + Rules::insertNotFull.apply(state); + Rules::removeExisting.apply(state); +} + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps) { + Rule* rules[] = {&Rules::clear, &Rules::find, &Rules::findExisting, &Rules::insertExisting, + &Rules::insertFull, &Rules::insertNotFull, &Rules::remove, &Rules::removeExisting}; + STest::RandomScenario scenario("RandomScenario", rules, + sizeof(rules) / sizeof(STest::RandomScenario*)); + STest::BoundedScenario boundedScenario(name.toChar(), scenario, maxNumSteps); + const U32 numSteps = boundedScenario.run(state); + printf("Ran %u steps.\n", numSteps); +} + +} // namespace Scenarios + +} // namespace SetTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/SetTestScenarios.hpp b/Fw/DataStructures/test/ut/STest/SetTestScenarios.hpp new file mode 100644 index 00000000000..2f44e8b16d4 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/SetTestScenarios.hpp @@ -0,0 +1,42 @@ +// ====================================================================== +// \title SetTestScenarios.hpp +// \author Rob Bocchino +// \brief Set test scenarios +// ====================================================================== + +#ifndef SetTestScenarios_HPP +#define SetTestScenarios_HPP + +#include "Fw/DataStructures/test/ut/STest/SetTestState.hpp" + +namespace Fw { + +namespace SetTest { + +namespace Scenarios { + +void clear(State& state); + +void find(State& state); + +void findExisting(State& state); + +void insertExisting(State& state); + +void insertFull(State& state); + +void insertNotFull(State& state); + +void remove(State& state); + +void removeExisting(State& state); + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps); + +} // namespace Scenarios + +} // namespace SetTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/SetTestState.hpp b/Fw/DataStructures/test/ut/STest/SetTestState.hpp new file mode 100644 index 00000000000..8ba0574794b --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/SetTestState.hpp @@ -0,0 +1,66 @@ +// ====================================================================== +// \title SetTestState.hpp +// \author bocchino +// \brief hpp file for set test state +// ====================================================================== + +#ifndef SetTestState_HPP +#define SetTestState_HPP + +#include + +#include + +#include "Fw/DataStructures/SetBase.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +namespace SetTest { + +struct State { + //! The element type + using ElementType = U32; + //! The set capacity + static constexpr FwSizeType capacity = 1024; + //! The SetBase type + using SetBaseType = SetBase; + //! Constructor + State(SetBaseType& a_set) : set(a_set) {} + //! The set under test + SetBaseType& set; + //! The set for modeling correct behavior + std::set modelSet; + //! Whether to use the stored element + bool useStoredElement = false; + //! The stored element + ElementType storedElement = 0; + //! Get an element + ElementType getElement() const { + return useStoredElement ? storedElement : static_cast(STest::Pick::any()); + } + //! Check whether the model set contains the specified element + bool modelSetContains(ElementType e) const { return modelSet.count(e) != 0; } + //! Test copy data from + static void testCopyDataFrom(SetBaseType& m1, FwSizeType size1, SetBaseType& m2) { + m1.clear(); + for (FwSizeType i = 0; i < size1; i++) { + const auto status = m1.insert(static_cast(i)); + ASSERT_EQ(status, Success::SUCCESS); + } + m2.copyDataFrom(m1); + const auto capacity2 = m2.getCapacity(); + const FwSizeType size = FW_MIN(size1, capacity2); + for (FwSizeType i = 0; i < size; i++) { + const auto e = static_cast(i); + const auto status = m2.find(e); + ASSERT_EQ(status, Success::SUCCESS); + } + } +}; + +} // namespace SetTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/StackTestRules.cpp b/Fw/DataStructures/test/ut/STest/StackTestRules.cpp new file mode 100644 index 00000000000..d8a59e4109a --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/StackTestRules.cpp @@ -0,0 +1,33 @@ +// ====================================================================== +// \title StackTestRules.cpp +// \author Rob Bocchino +// \brief cpp file for Stack test rules +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/StackTestRules.hpp" + +namespace Fw { + +namespace StackTest { + +namespace Rules { + +At at; + +Clear clear; + +Peek peek; + +PopEmpty popEmpty; + +PopOK popOK; + +PushFull pushFull; + +PushOK pushOK; + +} // namespace Rules + +} // namespace StackTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/StackTestRules.hpp b/Fw/DataStructures/test/ut/STest/StackTestRules.hpp new file mode 100644 index 00000000000..a3cb99bb9f6 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/StackTestRules.hpp @@ -0,0 +1,121 @@ +// ====================================================================== +// \title StackTestRules.hpp +// \author bocchino +// \brief hpp file for Stack test rules +// ====================================================================== + +#ifndef StackTestRules_HPP +#define StackTestRules_HPP + +#include "Fw/DataStructures/test/ut/STest/StackTestState.hpp" +#include "STest/STest/Pick/Pick.hpp" +#include "STest/STest/Rule/Rule.hpp" + +namespace Fw { + +namespace StackTest { + +using Rule = STest::Rule; + +namespace Rules { + +struct At : public Rule { + At() : Rule("At") {} + bool precondition(const State& state) { return !state.stack.isEmpty(); } + void action(State& state) { + const auto size = state.stack.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + ASSERT_EQ(state.stack.at(index), state.modelStack.at(size - 1 - index)); + } +}; + +struct Clear : public Rule { + Clear() : Rule("Clear") {} + bool precondition(const State& state) { return !state.stack.isEmpty(); } + void action(State& state) { + state.stack.clear(); + ASSERT_EQ(state.stack.getSize(), 0); + state.modelStack.clear(); + } +}; + +struct Peek : public Rule { + Peek() : Rule("Peek") {} + bool precondition(const State& state) { return !state.stack.isEmpty(); } + void action(State& state) { + const auto size = state.stack.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + State::ItemType item = 0; + const auto status = state.stack.peek(item, index); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(item, state.modelStack.at(size - 1 - index)); + } +}; + +struct PopEmpty : public Rule { + PopEmpty() : Rule("PopEmpty") {} + bool precondition(const State& state) { return state.stack.isEmpty(); } + void action(State& state) { + U32 value = 0; + const auto status = state.stack.pop(value); + ASSERT_EQ(status, Success::FAILURE); + } +}; + +struct PopOK : public Rule { + PopOK() : Rule("PopOK") {} + bool precondition(const State& state) { return !state.stack.isEmpty(); } + void action(State& state) { + const auto size = state.stack.getSize(); + U32 value = 0; + const auto status = state.stack.pop(value); + ASSERT_EQ(status, Success::SUCCESS); + const auto expectedValue = state.modelStack.at(size - 1); + ASSERT_EQ(value, expectedValue); + state.modelStack.pop_back(); + ASSERT_EQ(state.stack.getSize(), state.modelStack.size()); + } +}; + +struct PushFull : public Rule { + PushFull() : Rule("PushFull") {} + bool precondition(const State& state) { return state.stack.isFull(); } + void action(State& state) { + const auto item = State::getRandomItem(); + const auto status = state.stack.push(item); + ASSERT_EQ(status, Success::FAILURE); + } +}; + +struct PushOK : public Rule { + PushOK() : Rule("PushOK") {} + bool precondition(const State& state) { return !state.stack.isFull(); } + void action(State& state) { + const U32 value = STest::Pick::any(); + const auto status = state.stack.push(value); + ASSERT_EQ(status, Success::SUCCESS); + state.modelStack.push_back(value); + } +}; + +extern At at; + +extern Clear clear; + +extern Peek peek; + +extern PopEmpty popEmpty; + +extern PopOK popOK; + +extern PushFull pushFull; + +extern PushOK pushOK; + +} // namespace Rules + +} // namespace StackTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/StackTestScenarios.cpp b/Fw/DataStructures/test/ut/STest/StackTestScenarios.cpp new file mode 100644 index 00000000000..93e7de38dbc --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/StackTestScenarios.cpp @@ -0,0 +1,70 @@ +// ====================================================================== +// \title StackTestScenarios.cpp +// \author Rob Bocchino +// \brief Stack test scenarios +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/StackTestScenarios.hpp" +#include "Fw/DataStructures/test/ut/STest/StackTestRules.hpp" +#include "STest/Scenario/BoundedScenario.hpp" +#include "STest/Scenario/RandomScenario.hpp" + +namespace Fw { + +namespace StackTest { + +namespace Scenarios { + +void at(State& state) { + Rules::pushOK.apply(state); + for (FwSizeType i = 0; i < State::capacity; i++) { + Rules::at.apply(state); + } +} + +void clear(State& state) { + Rules::pushOK.apply(state); + Rules::clear.apply(state); +} + +void peek(State& state) { + Rules::pushOK.apply(state); + Rules::pushOK.apply(state); + Rules::peek.apply(state); +} + +void popEmpty(State& state) { + Rules::popEmpty.apply(state); +} + +void popOK(State& state) { + Rules::pushOK.apply(state); + Rules::popOK.apply(state); +} + +void pushFull(State& state) { + for (FwSizeType i = 0; i < State::capacity; i++) { + Rules::pushOK.apply(state); + } + Rules::pushFull.apply(state); +} + +void pushOK(State& state) { + Rules::pushOK.apply(state); +} + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps) { + Rule* rules[] = {&Rules::pushOK, &Rules::pushFull, &Rules::at, &Rules::peek, + &Rules::popOK, &Rules::popEmpty, &Rules::clear}; + STest::RandomScenario scenario("RandomScenario", rules, + sizeof(rules) / sizeof(STest::RandomScenario*)); + STest::BoundedScenario boundedScenario(name.toChar(), scenario, maxNumSteps); + const U32 numSteps = boundedScenario.run(state); + printf("Ran %u steps.\n", numSteps); +} + +} // namespace Scenarios + +} // namespace StackTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/StackTestScenarios.hpp b/Fw/DataStructures/test/ut/STest/StackTestScenarios.hpp new file mode 100644 index 00000000000..578916ba3d2 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/StackTestScenarios.hpp @@ -0,0 +1,40 @@ +// ====================================================================== +// \title StackTestScenarios.hpp +// \author Rob Bocchino +// \brief Stack test scenarios +// ====================================================================== + +#ifndef StackTestScenarios_HPP +#define StackTestScenarios_HPP + +#include "Fw/DataStructures/test/ut/STest/StackTestState.hpp" + +namespace Fw { + +namespace StackTest { + +namespace Scenarios { + +void at(State& state); + +void clear(State& state); + +void peek(State& state); + +void popEmpty(State& state); + +void popOK(State& state); + +void pushFull(State& state); + +void pushOK(State& state); + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps); + +} // namespace Scenarios + +} // namespace StackTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/StackTestState.hpp b/Fw/DataStructures/test/ut/STest/StackTestState.hpp new file mode 100644 index 00000000000..4c8b30b07b0 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/StackTestState.hpp @@ -0,0 +1,55 @@ +// ====================================================================== +// \title StackTestState.hpp +// \author bocchino +// \brief hpp file for Stack test state +// ====================================================================== + +#ifndef StackTestState_HPP +#define StackTestState_HPP + +#include +#include + +#include "Fw/DataStructures/StackBase.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +namespace StackTest { + +struct State { + //! The stack item type + using ItemType = U32; + //! The stack capacity + static constexpr FwSizeType capacity = 1024; + //! The StackBase type + using StackBaseType = StackBase; + //! Constructor + State(StackBaseType& a_stack) : stack(a_stack) {} + //! The stack under test + StackBaseType& stack; + //! The stack for modeling correct behavior + std::vector modelStack; + //! Get a random item + static ItemType getRandomItem() { return STest::Pick::any(); } + //! Test copy data from + static void testCopyDataFrom(StackBaseType& s1, FwSizeType size1, StackBaseType& s2) { + s1.clear(); + for (FwSizeType i = 0; i < size1; i++) { + const auto status = s1.push(static_cast(i)); + ASSERT_EQ(status, Success::SUCCESS); + } + s2.copyDataFrom(s1); + const auto capacity2 = s2.getCapacity(); + const FwSizeType size = FW_MIN(size1, capacity2); + for (FwSizeType i = 0; i < size; i++) { + ASSERT_EQ(s1.at(i), s2.at(i)); + } + } +}; + +} // namespace StackTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/StackTest.cpp b/Fw/DataStructures/test/ut/StackTest.cpp new file mode 100644 index 00000000000..28c8dcc2e6f --- /dev/null +++ b/Fw/DataStructures/test/ut/StackTest.cpp @@ -0,0 +1,136 @@ +// ====================================================================== +// \title StackTest.cpp +// \author bocchino +// \brief cpp file for Stack tests +// ====================================================================== + +#include "Fw/DataStructures/Stack.hpp" +#include "Fw/DataStructures/test/ut/STest/StackTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/StackTestScenarios.hpp" + +namespace Fw { + +template +class StackTester { + public: + StackTester(const Stack& stack) : m_stack(stack) {} + + const ExternalStack getExtStack() const { return this->m_stack.extStack; } + + const typename Array::Elements& getItems() const { return this->m_stack.m_items; } + + private: + const Stack& m_stack; +}; + +namespace StackTest { + +using TestStack = Stack; +using StackTester = StackTester; + +TEST(Stack, ZeroArgConstructor) { + TestStack stack; + ASSERT_EQ(stack.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(stack.getSize(), 0); +} + +TEST(Stack, CopyConstructor) { + // Construct s1 + TestStack s1; + // Push an item + const auto item = State::getRandomItem(); + const auto status = s1.push(item); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(s1.getSize(), 1); + // Use the copy constructor to construct s2 + TestStack s2(s1); + ASSERT_EQ(s2.getSize(), 1); +} + +TEST(Stack, CopyAssignmentOperator) { + // Call the constructor providing backing storage + TestStack s1; + // Push an item + const auto item = State::getRandomItem(); + const auto status = s1.push(item); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + TestStack s2; + ASSERT_EQ(s2.getSize(), 0); + // Call the copy assignment operator + s2 = s1; + ASSERT_EQ(s2.getSize(), 1); +} + +TEST(Stack, CopyDataFrom) { + constexpr FwSizeType maxSize = State::capacity; + constexpr FwSizeType smallSize = maxSize / 2; + TestStack s1; + // size1 < capacity2 + { + TestStack s2; + State::testCopyDataFrom(s1, smallSize, s2); + } + // size1 == capacity2 + { + TestStack s2; + State::testCopyDataFrom(s1, maxSize, s2); + } + // size1 > capacity2 + { + Stack s2; + State::testCopyDataFrom(s1, maxSize, s2); + } +} + +TEST(StackScenarios, At) { + TestStack stack; + State state(stack); + Scenarios::at(state); +} + +TEST(StackScenarios, Clear) { + TestStack stack; + State state(stack); + Scenarios::clear(state); +} + +TEST(StackScenarios, Peek) { + TestStack stack; + State state(stack); + Scenarios::peek(state); +} + +TEST(StackScenarios, PopEmpty) { + TestStack stack; + State state(stack); + Scenarios::popEmpty(state); +} + +TEST(StackScenarios, PopOK) { + TestStack stack; + State state(stack); + Scenarios::popOK(state); +} + +TEST(StackScenarios, PushFull) { + TestStack stack; + State state(stack); + Scenarios::pushFull(state); +} + +TEST(StackScenarios, PushOK) { + TestStack stack; + State state(stack); + Scenarios::pushOK(state); +} + +TEST(StackScenarios, Random) { + TestStack stack; + State state(stack); + Scenarios::random(Fw::String("StackRandom"), state, 1000); +} + +} // namespace StackTest + +} // namespace Fw