From 2895327752663b887ff9b693805909d4891614d9 Mon Sep 17 00:00:00 2001 From: Christos Choutouridis Date: Mon, 4 Oct 2021 21:15:38 +0300 Subject: [PATCH] DEV: add deque (based on ring buffer) to utl --- include/utl/container/deque.h | 225 ++++++++++++ include/utl/container/range.h | 64 ++++ include/utl/container/ring_iterator.h | 453 +++++++++++++++++++++++++ test/tests/deque.cpp | 469 ++++++++++++++++++++++++++ test/tests/ring_iterator.cpp | 424 +++++++++++++++++++++++ 5 files changed, 1635 insertions(+) create mode 100644 include/utl/container/deque.h create mode 100644 include/utl/container/range.h create mode 100644 include/utl/container/ring_iterator.h create mode 100644 test/tests/deque.cpp create mode 100644 test/tests/ring_iterator.cpp diff --git a/include/utl/container/deque.h b/include/utl/container/deque.h new file mode 100644 index 0000000..0682dfd --- /dev/null +++ b/include/utl/container/deque.h @@ -0,0 +1,225 @@ +/*! + * \file container/deque.h + * \brief + * A statically allocated deque based on a ring buffer. + * + * \copyright Copyright (C) 2021 Christos Choutouridis + * + *
License
+ * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *
+ */ + +#ifndef utl_container_deque_h__ +#define utl_container_deque_h__ + +#include +#include +#include +#include +#include + +namespace utl { + +/*! + * \class deque + * \brief + * A statically allocated deque based on a ring buffer + * + * The deque uses two ring_iterators one for the front and one for the rear. The iterators + * are pointing to the next available spot, not on the last inserted spot. This way at the + * initialization the iterators wont "pretend" to point to a valid item . + * + * We use a ring buffer of size \c N+1. We start the front iterator at the last location of the buffer + * and the rear on the first. This way when the queue is full the iterators are pointing to the same location. + * + * \tparam Data_t The char-like queued item type. Usually \c char + * \tparam N The size of deque + * \tparam SemiAtomic True for semi-atomic operation. In that case the \c ring_iterator is also atomic. + * \note + * SemiAtomic means it is safe to access different ends from different threads. For example one thread can + * push only from front and another can pop from back to implement a queue. + */ +template +class deque { + public: + // meta-identity type + using type = deque; + using buffer_t = std::array; // We need N+1 spaces ring buffer for N spaces deque + using iterator_t = ring_iterator; + using range_t = range; + + // STL + using value_type = Data_t; + using reference = Data_t&; + using const_reference = const Data_t&; + using pointer = Data_t*; + using const_pointer = const Data_t*; + using iterator = iterator_t; + using const_iterator = const iterator_t; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + //! \name Constructor / Destructor + //! @{ + public: + //! Default constructor + constexpr deque () noexcept : + data_{}, + f{data_.data(), N}, + r{data_.data()} { + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); + } + + //! fill contructor + constexpr deque(const Data_t& value) noexcept { + data_.fill(value); + f = iterator(data_.data(), N); + r = iterator(data_.data(), N); + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); + } + + //! Initializer list contructor + template + constexpr deque(It&& ...it) noexcept : + data_{{std::forward(it)...}}, + f(data_.data(), N), + r(data_.data(), sizeof...(It)) { + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); + } + + deque(const deque&) = delete; //!< No copies + deque& operator= (const deque&) = delete; //!< No copy assignments + ~deque () = default; //!< default destructor + //! @} + + //! \name Iterators + //! @{ + public: + constexpr iterator begin() noexcept { iterator ret = f; return ++ret; } + constexpr const_iterator begin() const noexcept { iterator ret = f; return ++ret; } + constexpr const_iterator cbegin() const noexcept { iterator ret = f; return ++ret; } + + constexpr iterator end() noexcept { return r; } + constexpr const_iterator end() const noexcept { return r; } + constexpr const_iterator cend() const noexcept { return r; } + + constexpr reverse_iterator rbegin() noexcept { return r; } + constexpr const_reverse_iterator rbegin() const noexcept { return r; } + constexpr const_reverse_iterator crbegin() const noexcept { return r; } + + constexpr reverse_iterator rend() noexcept { reverse_iterator ret = f; return ++ret; } + constexpr const_reverse_iterator rend() const noexcept { reverse_iterator ret = f; return ++ret; } + constexpr const_reverse_iterator crend() const noexcept { reverse_iterator ret = f; return ++ret; } + //! @} + + //! \name Capacity + //! @{ + public: + //! \return The size of the deque. The items currently in queue. + constexpr size_t size() noexcept { + return r - (f +1); + } + constexpr size_t size() const noexcept { + return r - (f +1); + } + //! \return The maximum size of the deque. The items the queue can hold. + constexpr size_t max_size() noexcept { return N; } + //! \return The capacity of the deque. The items the queue can hold. + constexpr size_t capacity() noexcept { return N; } + //! \return True if the deque is empty + constexpr bool empty() noexcept { return size() == 0 ? true : false; } + //! \return True if the deque is full + constexpr bool full() noexcept { return size() == N ? true : false; } + + //! @} + + //! \name Member access + //! @{ + public: + //! \brief Clears-empty the deque and return it to init state, without + //! really deleting the contents. + constexpr void clear() noexcept { + f = iterator_t(data_.data(), N); + r = iterator_t(data_.data()); + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); + } + //! \brief Push an item in the front of the deque + //! \param it The item to push + constexpr void push_front (const Data_t& it) noexcept { + if (full()) return; + *f = it; + --f; // keep this separate for thread safety + } + //! \brief Push an item in the back of the deque + //! \param it The item to push + constexpr void push_back (const Data_t& it) noexcept { + if (full()) return; + *r = it; + ++r; // keep this separate for thread safety + } + //! \brief Extract an item from the front of the deque and remove it from the deque + //! \param it The item to push + constexpr Data_t pop_front () noexcept { + if (empty()) return Data_t{}; + return *++f; + } + //! \brief Extract an item from the back of the deque and remove it from the deque + //! \param it The item to push + constexpr Data_t pop_back () noexcept { + if (empty()) return Data_t{}; + return *--r; + } + + //! \brief Get a reference to the item in the front of the deque without extracting it. + //! \return Reference to the item + constexpr Data_t& front() noexcept { iterator_t it = f; return *++it; } + constexpr const Data_t& front() const noexcept { iterator_t it = f; return *++it; } + + //! \brief Get a reference to the item in the front of the deque without extracting it. + //! \return Reference to the item + constexpr Data_t& back() noexcept { iterator_t it = r; return *--it; } + constexpr const Data_t& back() const noexcept { iterator_t it = r; return *--it; } + + //! \brief Get a pointer to the begin of the items on the deque + //! \return + constexpr Data_t* data() noexcept { return &front(); } + constexpr const Data_t* data() const noexcept { return &front(); } + + //! \brief Get a range for the data in queue + //! \return A begin-end iterator pair struct + constexpr range_t contents () noexcept { iterator_t b = f; return {++b, r}; } + constexpr const range_t contents () const noexcept { iterator_t b = f; return {++b, r}; } + + //! @} + private: + buffer_t data_{}; //!< The statically allocated buffer + iterator_t f{data_.data(), N}; //!< A ring iterator for the front (points to the next available location) + iterator_t r{data_.data()}; //!< A ring iterator for the rear (points to the next available location). +}; + +} // namespace utl + +#endif /* utl_container_deque_h__ */ diff --git a/include/utl/container/range.h b/include/utl/container/range.h new file mode 100644 index 0000000..15f5f69 --- /dev/null +++ b/include/utl/container/range.h @@ -0,0 +1,64 @@ +/*! + * \file container/range.h + * \brief + * A plain definition of a range struct with agregate initialization + * and begin-end pairs. + * + * \copyright Copyright (C) 2021 Christos Choutouridis + * + *
License
+ * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *
+ */ + +#ifndef utl_container_range_h__ +#define utl_container_range_h__ + +#include + +namespace utl { + +/*! + * \brief + * A plain definition of a range struct with begin-end pairs. + * + * \tparam Iter_t The iterator type of the range + */ +template +struct range { + Iter_t b{}, e{}; + +// range () = default; +// range (const Iter_t& first, const Iter_t& last) noexcept : +// b(first), e(last) { } +// range (Iter_t first, Iter_t last) noexcept : +// b(first), e(last) { } + + Iter_t begin() { return b; } + const Iter_t begin() const { return b; } + const Iter_t cbegin() const { return b; } + Iter_t end() { return e; } + const Iter_t end() const { return e; } + const Iter_t cend() const { return e; } +}; + +} // namespace utl; +#endif /* utl_container_range_h__ */ diff --git a/include/utl/container/ring_iterator.h b/include/utl/container/ring_iterator.h new file mode 100644 index 0000000..2f0e1a2 --- /dev/null +++ b/include/utl/container/ring_iterator.h @@ -0,0 +1,453 @@ +/*! + * \file container/ring_iterator.h + * \brief + * A ring/circular iterator. + * + * \copyright Copyright (C) 2021 Christos Choutouridis + * + *
License
+ * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *
+ */ + +#ifndef utl_container_ring_iterator_h__ +#define utl_container_ring_iterator_h__ + +#include + +#include +#include +#include + +namespace utl { + +template +class ring_iterator { + //! \name STL iterator traits "forwarding" + //! @{ + protected: + using traits_type = std::iterator_traits; + + public: + using iterator_type = Iter_t; + using iterator_category = typename traits_type::iterator_category; + using value_type = typename traits_type::value_type; + using difference_type = typename traits_type::difference_type; + using reference = typename traits_type::reference; + using pointer = typename traits_type::pointer; + //! @} + + + //! \name Constructor / Destructor + //! @{ + public: + constexpr ring_iterator(const Iter_t base =nullptr) noexcept : + base_(base), iter_(base) { } + + constexpr ring_iterator(const Iter_t base, size_t elem) noexcept : + base_(base), iter_(base + elem) { } + + constexpr ring_iterator(const ring_iterator& it) noexcept : + base_(it.base_), iter_(it.iter_) { } + + constexpr ring_iterator& operator= (const ring_iterator& it) noexcept { + base_ = it.base_; + iter_ = it.iter_; + return *this; + } + //! @} + + //! \name Forward iterator requirements + //! @{ + public: + constexpr reference operator*() const noexcept { + return *iter_; + } + + constexpr pointer operator->() const noexcept { + return iter_; + } + + constexpr ring_iterator& operator++() noexcept { + if (static_cast(++iter_ - base_) >= N) + iter_ = base_; + return *this; + } + + constexpr ring_iterator operator++(int) noexcept { + ring_iterator it = *this; + if (static_cast(++iter_ - base_) >= N) + iter_ = base_; + return it; + } + //! @} + + //! \name Bidirectional iterator requirements + //! @{ + public: + constexpr ring_iterator& operator--() noexcept { + if (--iter_ < base_) + iter_ = base_ + N -1; + return *this; + } + constexpr ring_iterator operator--(int) noexcept { + ring_iterator it = *this; + if (--iter_ < base_) + iter_ = base_ + N -1; + return it; + } + //! @} + + //! \name Random access iterator requirements + //! @{ + constexpr reference operator[](difference_type n) const noexcept { + difference_type k = iter_ - base_; // ptrdiff from base_ + return (static_cast(k + n) < N) ? + base_[k + n] : // on range + base_[k + n - N]; // out of range, loop + } + constexpr ring_iterator& operator+=(difference_type n) noexcept { + difference_type k = iter_ - base_; // ptrdiff from base_ + iter_ += (static_cast(k + n) < N) ? + n : // on range + n - N; // out of range, loop + return *this; + } + + constexpr ring_iterator operator+(difference_type n) const noexcept { + difference_type k = iter_ - base_; // ptrdiff from base_ + return (static_cast(k + n) < N) ? + ring_iterator(base_, k + n) : // on range + ring_iterator(base_, k + n - N); // out of range, loop + } + constexpr ring_iterator& operator-=(difference_type n) noexcept { + difference_type k = iter_ - base_; // ptrdiff from base_ + iter_ -= ((k - n) < 0)? + n - N: // out of range, loop + n; // on range + return *this; + } + constexpr ring_iterator operator-(difference_type n) const noexcept { + difference_type k = iter_ - base_; // ptrdiff from base_ + return ((k - n) < 0) ? + ring_iterator(base_, k - n + N) : // out of range, loop + ring_iterator(base_, k - n); // on range + } + //! @} + + //! \name Data members and access + //! @{ + constexpr const Iter_t& base() const noexcept { + return base_; + } + constexpr const Iter_t& iter() const noexcept { + return iter_; + } + constexpr size_t size() noexcept { + return N; + } + + constexpr operator Iter_t() noexcept { return iter_; } + constexpr operator const Iter_t() const noexcept { return iter_; } + + protected: + Iter_t base_; + Iter_t iter_; + //! @} +}; + +// Forward iterator requirements +template +inline bool operator==(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() == rhs.iter(); +} + +template +inline bool operator!=(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() != rhs.iter(); +} + +// Random access iterator requirements +template +inline bool operator<(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() < rhs.iter(); +} + +template +inline bool operator<=(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() <= rhs.iter(); +} + +template +inline bool operator>(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() > rhs.iter(); +} + +template +inline bool operator>=(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() >= rhs.iter(); +} + +template +inline auto operator-(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept +-> decltype(lhs.iter() - rhs.iter()) { + auto diff = lhs.iter() - rhs.iter(); + return diff < 0 ? + diff + N : // loop + diff; // no loop +} + +template +inline ring_iterator operator+(std::ptrdiff_t lhs, const ring_iterator& rhs) +noexcept { + ring_iterator it(rhs.iter()); + return it += lhs; +} + + + + + + +template +class ring_iterator { + //! \name STL iterator traits "forwarding" + //! @{ + protected: + using traits_type = std::iterator_traits; + + public: + using iterator_type = Iter_t; + using iterator_category = typename traits_type::iterator_category; + using value_type = typename traits_type::value_type; + using difference_type = typename traits_type::difference_type; + using reference = typename traits_type::reference; + using pointer = typename traits_type::pointer; + //! @} + + + //! \name Constructor / Destructor + //! @{ + public: + constexpr ring_iterator(const Iter_t base =nullptr) noexcept : + base_(base), iter_(base) { } + + constexpr ring_iterator(const Iter_t base, size_t elem) noexcept : + base_(base), iter_(base + elem) { } + + constexpr ring_iterator(const ring_iterator& it) noexcept : + base_(it.base_) { + iter_ = it.iter_.load(std::memory_order_acquire); + } + + constexpr ring_iterator& operator= (const ring_iterator& it) noexcept { + base_ = it.base_; + iter_ = it.iter_.load(std::memory_order_acquire); + return *this; + } + //! @} + + //! \name Forward iterator requirements + //! @{ + public: + constexpr reference operator*() const noexcept { + return *iter_.load(std::memory_order_acquire); + } + + constexpr pointer operator->() const noexcept { + return iter_.load(std::memory_order_acquire); + } + + constexpr ring_iterator& operator++() noexcept { + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + if (static_cast(++itnew - base_) >= N) + itnew = base_; + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acq_rel)); + return *this; + } + constexpr ring_iterator operator++(int) noexcept { + ring_iterator ret = *this; + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + if (static_cast(++itnew - base_) >= N) + itnew = base_; + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acq_rel)); + return ret; + } + //! @} + + //! \name Bidirectional iterator requirements + //! @{ + public: + constexpr ring_iterator& operator--() noexcept { + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + if (--itnew < base_) + itnew = base_ + N -1; + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acq_rel)); + return *this; + } + constexpr ring_iterator operator--(int) noexcept { + ring_iterator ret = *this; + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + if (--itnew < base_) + itnew = base_ + N -1; + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acq_rel)); + return ret; + } + //! @} + + //! \name Random access iterator requirements + //! @{ + constexpr reference operator[](difference_type n) const noexcept { + difference_type k = iter_.load(std::memory_order_acquire) - base_; // ptrdiff from base_ + return (static_cast(k + n) < N) ? + base_[k + n] : // on range + base_[k + n - N]; // out of range, loop + } + constexpr ring_iterator& operator+=(difference_type n) noexcept { + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + difference_type k = it - base_; // ptrdiff from base_ + itnew += (static_cast(k + n) < N) ? + n : // on range + n - N; // out of range, loop + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acquire)); + return *this; + } + constexpr ring_iterator operator+(difference_type n) const noexcept { + difference_type k = iter_.load(std::memory_order_acquire) - base_; // ptrdiff from base_ + return (static_cast(k + n) < N) ? + ring_iterator(base_, k + n) : // on range + ring_iterator(base_, k + n - N); // out of range, loop + } + constexpr ring_iterator& operator-=(difference_type n) noexcept { + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + difference_type k = it - base_; // ptrdiff from base_ + itnew -= ((k - n) < 0)? + n - N: // out of range, loop + n; // on range + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acquire)); + return *this; + } + constexpr ring_iterator operator-(difference_type n) const noexcept { + difference_type k = iter_.load(std::memory_order_acquire) - base_; // ptrdiff from base_ + return ((k - n) < 0) ? + ring_iterator(base_, k - n + N) : // out of range, loop + ring_iterator(base_, k - n); // on range + } + //! @} + + //! \name Data members and access + //! @{ + constexpr const Iter_t& base() const noexcept { + return base_; + } + constexpr const Iter_t iter() const noexcept { + return iter_.load(std::memory_order_acquire); + } + constexpr size_t size() noexcept { + return N; + } + + constexpr operator Iter_t() noexcept { return iter_.load(std::memory_order_acquire); } + constexpr operator const Iter_t() const noexcept { return iter_.load(std::memory_order_acquire); } + + protected: + Iter_t base_; + std::atomic iter_; + //! @} +}; + +// Forward iterator requirements +template +inline bool operator==(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() == rhs.iter(); +} + +template +inline bool operator!=(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() != rhs.iter(); +} + +// Random access iterator requirements +template +inline bool operator<(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() < rhs.iter(); +} + +template +inline bool operator<=(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() <= rhs.iter(); +} + +template +inline bool operator>(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() > rhs.iter(); +} + +template +inline bool operator>=(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept { + return lhs.iter() >= rhs.iter(); +} + +template +inline auto operator-(const ring_iterator& lhs, const ring_iterator& rhs) +noexcept +-> decltype(lhs.iter() - rhs.iter()) { + auto diff = lhs.iter() - rhs.iter(); + return diff < 0 ? + diff + N : // loop + diff; // no loop +} + +template +inline ring_iterator operator+(std::ptrdiff_t lhs, const ring_iterator& rhs) +noexcept { + ring_iterator it(rhs.iter()); + return it += lhs; +} + +} //namespace utl; + +#endif /* utl_container_ring_iterator_h__ */ diff --git a/test/tests/deque.cpp b/test/tests/deque.cpp new file mode 100644 index 0000000..9f5d459 --- /dev/null +++ b/test/tests/deque.cpp @@ -0,0 +1,469 @@ +/*! + * \file deque.cpp + * \brief + * Unit tests for deque + * + * \copyright Copyright (C) 2020 Christos Choutouridis + * + *
License
+ * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *
+ * + */ +#include +//#include +#include + +#include +#include +#include +#ifndef WIN_TRHEADS +#include +#include +#else +#include +#include +#endif + +namespace Tdeque { + using namespace utl; + +// template +// struct is_span : std::false_type {}; +// +// template +// struct is_span> : std::true_type {}; + + template + struct is_std_array : std::false_type {}; + + template + struct is_std_array> : std::true_type {}; + + template + struct has_size_and_data : std::false_type {}; + + template + struct has_size_and_data().size()), + decltype(std::declval().data())>> + : std::true_type {}; + + // Concept + TEST(Tdeque, concept) { + using deque_t = deque; + + EXPECT_EQ ( std::is_default_constructible::value, true); + EXPECT_EQ ( std::is_nothrow_default_constructible::value, true); + EXPECT_EQ (!std::is_copy_constructible::value, true); + EXPECT_EQ (!std::is_copy_assignable::value, true); + +// EXPECT_EQ (true, !is_span::value); + EXPECT_EQ (true, !is_std_array::value); + EXPECT_EQ (true, !std::is_array::value); + EXPECT_EQ (true, has_size_and_data::value); + } + // Test construction + TEST(Tdeque, contruct) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + deque q3{1, 2, 3, 4, 5}; + + EXPECT_EQ (8UL, q1.capacity()); + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (8UL, q2.capacity()); + EXPECT_EQ (8UL, q2.size()); + EXPECT_EQ (8UL, q3.capacity()); + EXPECT_EQ (5UL, q3.size()); + } + + // simple push-pop functionality + TEST(Tdeque, push_pop) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + q1.push_front(1); + q1.push_front(2); + EXPECT_EQ (1, q1.pop_back()); + EXPECT_EQ (2, q1.pop_back()); + + q1.push_back(1); + q1.push_back(2); + EXPECT_EQ (1, q1.pop_front()); + EXPECT_EQ (2, q1.pop_front()); + + q1.push_front(2); + q1.push_back(3); + q1.push_front(1); + q1.push_back(4); + + for (int i=1 ; i<= 4 ; ++i) + EXPECT_EQ ((int)i, q1.pop_front()); + } + + // front-back + TEST(Tdeque, front_back) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + q1.push_front(2); + q1.push_front(1); + q1.push_back(3); + q1.push_back(4); + + EXPECT_EQ (1, q1.front()); + EXPECT_EQ (4, q1.back()); + EXPECT_EQ (1, q2.front()); + EXPECT_EQ (8, q2.back()); + } + + // capacity + TEST(Tdeque, capacity) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + q1.push_back(1); + q1.clear(); + EXPECT_EQ (true, q1.empty()); + EXPECT_EQ (true, q2.full()); + + EXPECT_EQ (8UL, q1.capacity()); + EXPECT_EQ (8UL, q2.capacity()); + + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (8UL, q2.size()); + + q1.push_back(2); + EXPECT_EQ (1UL, q1.size()); + q1.push_front(1); + EXPECT_EQ (2UL, q1.size()); + + q1.pop_back(); + EXPECT_EQ (1UL, q1.size()); + q1.pop_front(); + EXPECT_EQ (0UL, q1.size()); + } + + // push-pop limits + TEST (Tdeque, push_pop_limits) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + EXPECT_EQ (int{}, q1.pop_back()); + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (true, q1.empty()); + EXPECT_EQ (false, q1.full()); + + EXPECT_EQ (int{}, q1.pop_front()); + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (true, q1.empty()); + EXPECT_EQ (false, q1.full()); + + q2.push_front(0); + EXPECT_EQ (1, q2.front()); + EXPECT_EQ (8, q2.back()); + EXPECT_EQ (8UL, q2.size()); + EXPECT_EQ (false, q2.empty()); + EXPECT_EQ (true, q2.full()); + + q2.push_back(9); + EXPECT_EQ (1, q2.front()); + EXPECT_EQ (8, q2.back()); + EXPECT_EQ (8UL, q2.size()); + EXPECT_EQ (false, q2.empty()); + EXPECT_EQ (true, q2.full()); + } + + // iterators + TEST (Tdeque, iterators) { + deque q1{1, 2, 3, 4, 5, 6, 7, 8}; + int check_it=1; + + EXPECT_EQ (q1.begin().base(), q1.end().base()); + EXPECT_NE (q1.begin().iter(), q1.end().iter()); + EXPECT_EQ (1, *q1.begin()); + EXPECT_EQ (true, (q1.begin() == ++q1.end())); // loop edge iterators + + for (auto it = q1.begin() ; it != q1.end() ; ++it) + EXPECT_EQ(*it, check_it++); + EXPECT_EQ(9, check_it); // run through all + + EXPECT_EQ (1, q1.front()); // queue stays intact + EXPECT_EQ (8, q1.back()); + EXPECT_EQ (8UL, q1.size()); + EXPECT_EQ (false, q1.empty()); + EXPECT_EQ (true, q1.full()); + + q1.pop_front(); + q1.pop_back(); + + check_it=2; + for (auto& it : q1) + EXPECT_EQ(it, check_it++); + EXPECT_EQ(8, check_it); // run through all + + EXPECT_EQ (2, q1.front()); // queue stays intact + EXPECT_EQ (7, q1.back()); + EXPECT_EQ (6UL, q1.size()); + EXPECT_EQ (false, q1.empty()); + EXPECT_EQ (false, q1.full()); + + deque q2; + q2.push_front(2); + q2.push_front(1); + q2.push_back(3); + q2.push_back(4); + q2.push_back(5); + check_it =1; + for (auto& it : q2) + EXPECT_EQ(it, check_it++); + EXPECT_EQ(6, check_it); // run through all + + } + TEST (Tdeque, range) { + deque q1{1, 2, 3, 4, 5, 6, 7, 8}; + int check_it=1; + + for (auto& it : q1.contents()) + EXPECT_EQ(it, check_it++); + + EXPECT_EQ(9, check_it); // run through all + } + + + // Concept + TEST(Tdeque, concept_atomic) { + using deque_t = deque; + +// EXPECT_EQ (true, !is_span::value); + EXPECT_EQ (true, !is_std_array::value); + EXPECT_EQ (true, !std::is_array::value); + EXPECT_EQ (true, has_size_and_data::value); + } + // Test construction + TEST(Tdeque, contruct_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + deque q3{1, 2, 3, 4, 5}; + + EXPECT_EQ (8UL, q1.capacity()); + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (8UL, q2.capacity()); + EXPECT_EQ (8UL, q2.size()); + EXPECT_EQ (8UL, q3.capacity()); + EXPECT_EQ (5UL, q3.size()); + } + + // simple push-pop functionality + TEST(Tdeque, push_pop_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + q1.push_front(1); + q1.push_front(2); + EXPECT_EQ (1, q1.pop_back()); + EXPECT_EQ (2, q1.pop_back()); + + q1.push_back(1); + q1.push_back(2); + EXPECT_EQ (1, q1.pop_front()); + EXPECT_EQ (2, q1.pop_front()); + + q1.push_front(2); + q1.push_back(3); + q1.push_front(1); + q1.push_back(4); + + for (int i=1 ; i<= 4 ; ++i) + EXPECT_EQ ((int)i, q1.pop_front()); + } + + // front-back + TEST(Tdeque, front_back_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + q1.push_front(2); + q1.push_front(1); + q1.push_back(3); + q1.push_back(4); + + EXPECT_EQ (1, q1.front()); + EXPECT_EQ (4, q1.back()); + EXPECT_EQ (1, q2.front()); + EXPECT_EQ (8, q2.back()); + } + + // capacity + TEST(Tdeque, capacity_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + q1.push_back(1); + q1.clear(); + EXPECT_EQ (true, q1.empty()); + EXPECT_EQ (true, q2.full()); + + EXPECT_EQ (8UL, q1.capacity()); + EXPECT_EQ (8UL, q2.capacity()); + + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (8UL, q2.size()); + + q1.push_back(2); + EXPECT_EQ (1UL, q1.size()); + q1.push_front(1); + EXPECT_EQ (2UL, q1.size()); + + q1.pop_back(); + EXPECT_EQ (1UL, q1.size()); + q1.pop_front(); + EXPECT_EQ (0UL, q1.size()); + } + + // push-pop limits + TEST (Tdeque, push_pop_limits_atomic) { + deque q1; + deque q2{1, 2, 3, 4, 5, 6, 7, 8}; + + EXPECT_EQ (int{}, q1.pop_back()); + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (true, q1.empty()); + EXPECT_EQ (false, q1.full()); + + EXPECT_EQ (int{}, q1.pop_front()); + EXPECT_EQ (0UL, q1.size()); + EXPECT_EQ (true, q1.empty()); + EXPECT_EQ (false, q1.full()); + + q2.push_front(0); + EXPECT_EQ (1, q2.front()); + EXPECT_EQ (8, q2.back()); + EXPECT_EQ (8UL, q2.size()); + EXPECT_EQ (false, q2.empty()); + EXPECT_EQ (true, q2.full()); + + q2.push_back(9); + EXPECT_EQ (1, q2.front()); + EXPECT_EQ (8, q2.back()); + EXPECT_EQ (8UL, q2.size()); + EXPECT_EQ (false, q2.empty()); + EXPECT_EQ (true, q2.full()); + } + + // iterators + TEST (Tdeque, iterators_atomic) { + deque q1{1, 2, 3, 4, 5, 6, 7, 8}; + int check_it=1; + + EXPECT_EQ (q1.begin().base(), q1.end().base()); + EXPECT_NE (q1.begin().iter(), q1.end().iter()); + EXPECT_EQ (1, *q1.begin()); + EXPECT_EQ (true, (q1.begin() == ++q1.end())); // loop edge iterators + + for (auto it = q1.begin() ; it != q1.end() ; ++it) + EXPECT_EQ(*it, check_it++); + EXPECT_EQ(9, check_it); // run through all + + EXPECT_EQ (1, q1.front()); // queue stays intact + EXPECT_EQ (8, q1.back()); + EXPECT_EQ (8UL, q1.size()); + EXPECT_EQ (false, q1.empty()); + EXPECT_EQ (true, q1.full()); + + q1.pop_front(); + q1.pop_back(); + + check_it=2; + for (auto& it : q1) + EXPECT_EQ(it, check_it++); + EXPECT_EQ(8, check_it); // run through all + + EXPECT_EQ (2, q1.front()); // queue stays intact + EXPECT_EQ (7, q1.back()); + EXPECT_EQ (6UL, q1.size()); + EXPECT_EQ (false, q1.empty()); + EXPECT_EQ (false, q1.full()); + + deque q2; + q2.push_front(2); + q2.push_front(1); + q2.push_back(3); + q2.push_back(4); + q2.push_back(5); + check_it =1; + for (auto& it : q2) + EXPECT_EQ(it, check_it++); + EXPECT_EQ(6, check_it); // run through all + + } + TEST (Tdeque, range_atomic) { + deque q1{1, 2, 3, 4, 5, 6, 7, 8}; + int check_it=1; + + for (auto& it : q1.contents()) + EXPECT_EQ(it, check_it++); + + EXPECT_EQ(9, check_it); // run through all + } + + TEST(Tdeque, race) { + constexpr size_t N = 1000000; + deque q; + int result[N]; + + auto push_front = [&](){ + for (size_t i=1 ; i<=N ; ++i) q.push_front(i); + }; + auto push_back = [&](){ + for (size_t i=1 ; i<=N ; ++i) q.push_back(i); + }; + auto pop_front = [&](){ + for (size_t i=0 ; i + * + *
License
+ * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *
+ * + */ +#include +#include + +#include +#include + +namespace Tring_iterator { + using namespace utl; + + // Test construction + TEST(Tring_iterator, construct) { + int A[10]; + + //default constructor + ring_iterator i1; + EXPECT_EQ(nullptr, i1.base()); + EXPECT_EQ(nullptr, i1.iter()); + EXPECT_EQ(10UL, i1.size()); + + // implementation specific (you can remove it freely) + EXPECT_EQ(2*sizeof(int*), sizeof(i1)); + + // basic + ring_iterator i2(A); + EXPECT_EQ(A, i2.base()); + EXPECT_EQ(A, i2.iter()); + EXPECT_EQ(10UL, i2.size()); + + // basic from assignment + ring_iterator i3 = A; + EXPECT_EQ(A, i3.base()); + EXPECT_EQ(A, i3.iter()); + EXPECT_EQ(10UL, i3.size()); + + // basic with offset + ring_iterator i4(A, 5); + EXPECT_EQ(A, i4.base()); + EXPECT_EQ(&A[5], i4.iter()); + EXPECT_EQ(10UL, i4.size()); + + // copy (Legacy iterator) + auto i5 = i2; + EXPECT_EQ(A, i5.base()); + EXPECT_EQ(A, i5.iter()); + EXPECT_EQ(10UL, i5.size()); + + // arbitrary type + struct TT { int a,b,c; }; + std::array t; + ring_iterator it(t.data(), 2); + EXPECT_EQ(t.begin(), it.base()); + EXPECT_EQ(&t[2], it.iter()); + EXPECT_EQ(10UL, it.size()); + } + + // Legacy iterator + TEST(Tring_iterator, LegacyIterator) { + + EXPECT_EQ(true, (std::is_same::value_type>::value)); + EXPECT_EQ(true, (std::is_same::difference_type>::value)); + EXPECT_EQ(true, (std::is_same::reference>::value)); + EXPECT_EQ(true, (std::is_same::pointer>::value)); + EXPECT_EQ(true, (std::is_same::iterator_category>::value)); + + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator i1(A); + + // copy constructible/assignable + auto i2 = i1; + EXPECT_EQ(A, i2.base()); + EXPECT_EQ(A, i2.iter()); + EXPECT_EQ(10UL, i2.size()); + + // dereferenceable - incrementable + ring_iterator i3(A); + EXPECT_EQ(true, (std::is_reference::value)); + EXPECT_EQ(true, (std::is_same&, decltype(++i3)>::value)); + EXPECT_EQ(true, (std::is_reference::value)); + + // more practical + ring_iterator i4(A); + ring_iterator i5(A, 9); + EXPECT_EQ(A[0], *i4); + EXPECT_EQ(&A[1], (++i4).iter()); + // check loop + EXPECT_EQ(A[9], *i5); + EXPECT_EQ(&A[0], (++i5).iter()); + } + + // Legacy input iterator + TEST(Tring_iterator, LegacyInputIterator) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator i1(A), i2(A), i3(A, 1); + + struct T { int m; }; + T B[5] { {0}, {1}, {2}, {3}, {4}}; + ring_iterator it(B); + + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_samem)>::value)); + EXPECT_EQ (true, (std::is_same&, decltype(++i1)>::value)); + EXPECT_EQ (true, (std::is_same::value)); + + // more practical + EXPECT_EQ (true, i1 == i2); + EXPECT_EQ (true, i1 != i3); + EXPECT_EQ (0, *i1); + EXPECT_EQ (0, it->m); + EXPECT_EQ (true, (++i1 == i3)); + EXPECT_EQ (1, *i1++); + EXPECT_EQ (2, *i1); + } + + // Legacy input iterator + TEST(Tring_iterator, LegacyOutputIterator) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it(A); + + EXPECT_EQ (true, (std::is_assignable::value)); + EXPECT_EQ (true, (std::is_assignable::value)); + + // more practical + *it = 42; + EXPECT_EQ (42, A[0]); + *it++ = 7; + EXPECT_EQ (7, A[0]); + EXPECT_EQ (&A[1], it.iter()); + } + + // Legacy forward iterator + TEST(Tring_iterator, LegacyForwardIterator) + { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it(A); + + EXPECT_EQ (0, *it++); + EXPECT_EQ (1, *it); + } + + // Legacy bidirectional iterator + TEST(Tring_iterator, LegacyBidirectionalIterator) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it(A); + + EXPECT_EQ (true, (std::is_same&, decltype(--it)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(it--)>::value)); + EXPECT_EQ (true, (std::is_same::value)); + + // more practical + ring_iterator i1(A), i2(A, 9); + EXPECT_EQ (9, *i2--); // check loop also + EXPECT_EQ (8, *i2); + EXPECT_EQ (0, *i1--); // check loop also + EXPECT_EQ (9, *i1); + } + + // Legacy random access iterator + TEST(Tring_iterator, LegacyRandomAccessIterator) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it1(A), it2(A, 7); + + EXPECT_EQ (true, (std::is_same&, decltype(it1 += 7)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(it1 + 7)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(7 + it1)>::value)); + + EXPECT_EQ (true, (std::is_same&, decltype(it1 -= 7)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(it1 - 7)>::value)); + + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same::value)); + + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same it2)>::value)); + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same= it2)>::value)); + + // more practical + ring_iterator i1(A), i2(A); + i1 += 7; + EXPECT_EQ (7, *i1); + i1 -= 7; + EXPECT_EQ (0, *i1); + i1 += 11; + EXPECT_EQ (1, *i1); + i1 -= 2; + EXPECT_EQ (9, *i1); + + EXPECT_EQ (7, *(i2+7)); + EXPECT_EQ (7, *(7+i2)); + EXPECT_EQ (1, *(i2+11)); + EXPECT_EQ (1, *(11+i2)); + EXPECT_EQ (7, *(i1-2)); + EXPECT_EQ (8, *(i2-2)); + + EXPECT_EQ (9, (i1 - i2)); + EXPECT_EQ (1, (i2 - i1)); // loop + + } + + // Test construction atomic + TEST(Tring_iterator, construct_atomic) { + int A[10]; + + //default constructor + ring_iterator i1; + EXPECT_EQ(nullptr, i1.base()); + EXPECT_EQ(nullptr, i1.iter()); + EXPECT_EQ(10UL, i1.size()); + + // implementation specific (you can remove it freely) + EXPECT_EQ(2*sizeof(int*), sizeof(i1)); + + // basic + ring_iterator i2(A); + EXPECT_EQ(A, i2.base()); + EXPECT_EQ(A, i2.iter()); + EXPECT_EQ(10UL, i2.size()); + + // basic from assignment + ring_iterator i3 = A; + EXPECT_EQ(A, i3.base()); + EXPECT_EQ(A, i3.iter()); + EXPECT_EQ(10UL, i3.size()); + + // basic with offset + ring_iterator i4(A, 5); + EXPECT_EQ(A, i4.base()); + EXPECT_EQ(&A[5], i4.iter()); + EXPECT_EQ(10UL, i4.size()); + + // copy (Legacy iterator) + auto i5 = i2; + EXPECT_EQ(A, i5.base()); + EXPECT_EQ(A, i5.iter()); + EXPECT_EQ(10UL, i5.size()); + + // arbitrary type + struct TT { int a,b,c; }; + std::array t; + ring_iterator it(t.data(), 2); + EXPECT_EQ(t.begin(), it.base()); + EXPECT_EQ(&t[2], it.iter()); + EXPECT_EQ(10UL, it.size()); + } + + // Legacy iterator atomic + TEST(Tring_iterator, LegacyIterator_atomic) { + + EXPECT_EQ(true, (std::is_same::value_type>::value)); + EXPECT_EQ(true, (std::is_same::difference_type>::value)); + EXPECT_EQ(true, (std::is_same::reference>::value)); + EXPECT_EQ(true, (std::is_same::pointer>::value)); + EXPECT_EQ(true, (std::is_same::iterator_category>::value)); + + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator i1(A); + + // copy constructible/assignable + auto i2 = i1; + EXPECT_EQ(A, i2.base()); + EXPECT_EQ(A, i2.iter()); + EXPECT_EQ(10UL, i2.size()); + + // dereferenceable - incrementable + ring_iterator i3(A); + EXPECT_EQ(true, (std::is_reference::value)); + EXPECT_EQ(true, (std::is_same&, decltype(++i3)>::value)); + EXPECT_EQ(true, (std::is_reference::value)); + + // more practical + ring_iterator i4(A); + ring_iterator i5(A, 9); + EXPECT_EQ(A[0], *i4); + EXPECT_EQ(&A[1], (++i4).iter()); + // check loop + EXPECT_EQ(A[9], *i5); + EXPECT_EQ(&A[0], (++i5).iter()); + } + + // Legacy input iterator atomic + TEST(Tring_iterator, LegacyInputIterator_atomic) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator i1(A), i2(A), i3(A, 1); + + struct T { int m; }; + T B[5] { {0}, {1}, {2}, {3}, {4}}; + ring_iterator it(B); + + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_samem)>::value)); + EXPECT_EQ (true, (std::is_same&, decltype(++i1)>::value)); + EXPECT_EQ (true, (std::is_same::value)); + + // more practical + EXPECT_EQ (true, i1 == i2); + EXPECT_EQ (true, i1 != i3); + EXPECT_EQ (0, *i1); + EXPECT_EQ (0, it->m); + EXPECT_EQ (true, (++i1 == i3)); + EXPECT_EQ (1, *i1++); + EXPECT_EQ (2, *i1); + } + + // Legacy input iterator atomic + TEST(Tring_iterator, LegacyOutputIterator_atomic) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it(A); + + EXPECT_EQ (true, (std::is_assignable::value)); + EXPECT_EQ (true, (std::is_assignable::value)); + + // more practical + *it = 42; + EXPECT_EQ (42, A[0]); + *it++ = 7; + EXPECT_EQ (7, A[0]); + EXPECT_EQ (&A[1], it.iter()); + } + + // Legacy forward iterator atomic + TEST(Tring_iterator, LegacyForwardIterator_atomic) + { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it(A); + + EXPECT_EQ (0, *it++); + EXPECT_EQ (1, *it); + } + + // Legacy bidirectional iterator atomic + TEST(Tring_iterator, LegacyBidirectionalIterator_atomic) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it(A); + + EXPECT_EQ (true, (std::is_same&, decltype(--it)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(it--)>::value)); + EXPECT_EQ (true, (std::is_same::value)); + + // more practical + ring_iterator i1(A), i2(A, 9); + EXPECT_EQ (9, *i2--); // check loop also + EXPECT_EQ (8, *i2); + EXPECT_EQ (0, *i1--); // check loop also + EXPECT_EQ (9, *i1); + } + + // Legacy random access iterator atomic + TEST(Tring_iterator, LegacyRandomAccessIterator_atomic) { + int A[10] {0, 1, 2, 3, 4, 5, 6, 7, 8 , 9}; + ring_iterator it1(A), it2(A, 7); + + EXPECT_EQ (true, (std::is_same&, decltype(it1 += 7)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(it1 + 7)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(7 + it1)>::value)); + + EXPECT_EQ (true, (std::is_same&, decltype(it1 -= 7)>::value)); + EXPECT_EQ (true, (std::is_same, decltype(it1 - 7)>::value)); + + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same::value)); + + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same it2)>::value)); + EXPECT_EQ (true, (std::is_same::value)); + EXPECT_EQ (true, (std::is_same= it2)>::value)); + + // more practical + ring_iterator i1(A), i2(A); + i1 += 7; + EXPECT_EQ (7, *i1); + i1 -= 7; + EXPECT_EQ (0, *i1); + i1 += 11; + EXPECT_EQ (1, *i1); + i1 -= 2; + EXPECT_EQ (9, *i1); + + EXPECT_EQ (7, *(i2+7)); + EXPECT_EQ (7, *(7+i2)); + EXPECT_EQ (1, *(i2+11)); + EXPECT_EQ (1, *(11+i2)); + EXPECT_EQ (7, *(i1-2)); + EXPECT_EQ (8, *(i2-2)); + + EXPECT_EQ (9, (i1 - i2)); + EXPECT_EQ (1, (i2 - i1)); // loop + + } + +}