diff options
-rw-r--r-- | include/jau/cow_darray.hpp | 101 | ||||
-rw-r--r-- | include/jau/cow_iterator.hpp | 339 | ||||
-rw-r--r-- | include/jau/cow_vector.hpp | 42 | ||||
-rw-r--r-- | test/test_cow_darray_01.cpp | 34 | ||||
-rw-r--r-- | test/test_cow_darray_perf01.cpp | 50 | ||||
-rw-r--r-- | test/test_cow_iterator_01.cpp | 125 | ||||
-rw-r--r-- | test/test_hashset_perf01.cpp | 47 |
7 files changed, 457 insertions, 281 deletions
diff --git a/include/jau/cow_darray.hpp b/include/jau/cow_darray.hpp index e9926af..44582da 100644 --- a/include/jau/cow_darray.hpp +++ b/include/jau/cow_darray.hpp @@ -93,16 +93,29 @@ namespace jau { * jau::cow_darray::get_write_mutex(), jau::cow_darray::copy_store() and jau::cow_darray::set_store().<br> * See example in jau::cow_darray::set_store() * </p> - * + * <p> + * To allow data-race free operations using iterators from a potentially mutated CoW, + * only one cow_darray::begin() const_iterator or iterator should be retrieved from this CoW + * and all further operations shall use its + * jau::cow_ro_iterator::size(), jau::cow_ro_iterator::begin() and jau::cow_ro_iterator::end() + * - or its respective variant from jau::cow_rw_iterator. * </p> * See also: * <pre> * - Sequentially Consistent (SC) ordering or SC-DRF (data race free) <https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering> * - std::memory_order <https://en.cppreference.com/w/cpp/atomic/memory_order> * </pre> + * @see jau::cow_darray::cbegin() * @see jau::cow_ro_iterator + * @see jau::cow_ro_iterator::size() + * @see jau::cow_ro_iterator::begin() + * @see jau::cow_ro_iterator::end() * @see jau::for_each_fidelity + * @see jau::cow_darray::begin() * @see jau::cow_rw_iterator + * @see jau::cow_rw_iterator::size() + * @see jau::cow_rw_iterator::begin() + * @see jau::cow_rw_iterator::end() */ template <typename Value_type, typename Alloc_type = callocator<Value_type>, typename Size_type = jau::nsize_t> class cow_darray @@ -125,6 +138,8 @@ namespace jau { typedef darray<value_type, allocator_type, size_type> storage_t; typedef std::shared_ptr<storage_t> storage_ref_t; + typedef cow_darray<value_type, allocator_type, size_type> cow_container_t; + /** * Immutable, read-only const_iterator, lock-free, * holding the current shared store reference until destruction. @@ -138,10 +153,13 @@ namespace jau { * Also see jau::for_each_fidelity to iterate through in this good faith fashion. * </p> * @see jau::cow_ro_iterator + * @see jau::cow_ro_iterator::size() + * @see jau::cow_ro_iterator::begin() + * @see jau::cow_ro_iterator::end() * @see jau::for_each_fidelity * @see jau::cow_rw_iterator */ - typedef cow_ro_iterator<storage_t, storage_ref_t, cow_darray> const_iterator; + typedef cow_ro_iterator<storage_t, storage_ref_t, cow_container_t> const_iterator; /** * Mutable, read-write iterator, holding the write-lock and a store copy until destruction. @@ -154,11 +172,13 @@ namespace jau { * consider using jau::cow_rw_iterator if elements won't get mutated * or any changes can be discarded. * </p> - * @see jau::cow_ro_iterator - * @see jau::for_each_fidelity * @see jau::cow_rw_iterator + * @see jau::cow_rw_iterator::size() + * @see jau::cow_rw_iterator::begin() + * @see jau::cow_rw_iterator::end() + * @see jau::cow_ro_iterator */ - typedef cow_rw_iterator<storage_t, storage_ref_t, cow_darray> iterator; + typedef cow_rw_iterator<storage_t, storage_ref_t, cow_container_t> iterator; // typedef std::reverse_iterator<iterator> reverse_iterator; // typedef std::reverse_iterator<const_iterator> const_reverse_iterator; @@ -410,86 +430,49 @@ namespace jau { // const_iterator, non mutable, read-only - /** - * Returns a jau::cow_darray::const_iterator of type jau::cow_ro_iterator. - * <p> - * This method variant is only chosen if the container is const. - * </p> - * @return jau::cow_darray::const_iterator of type jau::cow_ro_iterator - * @see jau::cow_ro_iterator - * @see jau::for_each_fidelity - */ - constexpr const_iterator begin() const noexcept { - return const_iterator(get_snapshot(), store_ref->cbegin()); - } + // Removed for clarity: "constexpr const_iterator begin() const noexcept" /** - * Returns a jau::cow_darray::const_iterator of type jau::cow_ro_iterator. + * Returns an jau::cow_ro_iterator to the first element of this CoW storage. * <p> * This method is the preferred choice if the use case allows, * read remarks in jau::cow_ro_iterator. * </p> - * @return jau::cow_darray::const_iterator of type jau::cow_ro_iterator - * @see jau::cow_ro_iterator - * @see jau::for_each_fidelity - */ - constexpr const_iterator cbegin() const noexcept { - return const_iterator(get_snapshot(), store_ref->cbegin()); - } - - /** - * Returns a jau::cow_darray::const_iterator of type jau::cow_ro_iterator. - * <p> - * This method variant is only chosen if the container is const. - * </p> - * @return jau::cow_darray::const_iterator of type jau::cow_ro_iterator - * @see jau::cow_ro_iterator - * @see jau::for_each_fidelity - */ - constexpr const_iterator end() const noexcept { - return const_iterator(get_snapshot(), store_ref->cend()); - } - - /** - * Returns a jau::cow_darray::const_iterator of type jau::cow_ro_iterator. * <p> - * This method is the preferred choice if the use case allows, - * read remarks in jau::cow_ro_iterator. + * Use jau::cow_ro_iterator::end() on this returned const_iterator + * to retrieve the end const_iterator in a data-race free fashion. * </p> * @return jau::cow_darray::const_iterator of type jau::cow_ro_iterator * @see jau::cow_ro_iterator + * @see jau::cow_ro_iterator::size() + * @see jau::cow_ro_iterator::begin() + * @see jau::cow_ro_iterator::end() * @see jau::for_each_fidelity */ - constexpr const_iterator cend() const noexcept { - return const_iterator(get_snapshot(), store_ref->cend()); + constexpr const_iterator cbegin() const noexcept { + return const_iterator(get_snapshot(), store_ref->cbegin()); } // iterator, mutable, read-write /** - * Returns a jau::cow_darray::iterator of type jau::cow_rw_iterator. + * Returns an jau::cow_rw_iterator to the first element of this CoW storage. * <p> * Acquiring this mutable iterator has considerable costs attached, * read remarks in jau::cow_rw_iterator. * </p> - * @return jau::cow_darray::iterator of type jau::cow_rw_iterator - * @see jau::cow_rw_iterator - */ - constexpr iterator begin() noexcept { - return iterator(*this, [](storage_ref_t& new_store) -> typename storage_t::iterator { return new_store->begin(); } ); - } - - /** - * Returns a jau::cow_darray::iterator of type jau::cow_rw_iterator. * <p> - * Acquiring this mutable iterator has considerable costs attached, - * read remarks in jau::cow_rw_iterator. + * Use jau::cow_rw_iterator::end() on this returned iterator + * to retrieve the end iterator in a data-race free fashion. * </p> * @return jau::cow_darray::iterator of type jau::cow_rw_iterator * @see jau::cow_rw_iterator + * @see jau::cow_rw_iterator::size() + * @see jau::cow_rw_iterator::begin() + * @see jau::cow_rw_iterator::end() */ - constexpr iterator end() noexcept { - return iterator(*this, [](storage_ref_t& new_store) -> typename storage_t::iterator { return new_store->end(); } ); + constexpr iterator begin() noexcept { + return iterator(*this, [](storage_ref_t& new_store) -> typename storage_t::iterator { return new_store->begin(); } ); } // read access diff --git a/include/jau/cow_iterator.hpp b/include/jau/cow_iterator.hpp index 23e5a64..bc5e9a8 100644 --- a/include/jau/cow_iterator.hpp +++ b/include/jau/cow_iterator.hpp @@ -44,13 +44,11 @@ namespace jau { template <typename Storage_type, typename Storage_ref_type, typename CoW_container> class cow_rw_iterator; + /**************************************************************************************** + ****************************************************************************************/ + /** * iterator_type -> std::string conversion - * <p> - * TODO: Test whether 'base()' method actually exists, - * however - this is good enough for stl iterator_type - * having implemented the inner native iterator (a pointer). - * </p> * @tparam iterator_type the iterator type * @param iter the iterator * @param if iterator_type is a class @@ -59,9 +57,9 @@ namespace jau { template< class iterator_type > std::string to_string(const iterator_type & iter, typename std::enable_if< - std::is_class<iterator_type>::value - >::type* = 0 - ) { + !std::is_pointer<iterator_type>::value + >::type* = 0 ) + { return aptrHexString( (void*) ( iter.base() ) ); } @@ -75,16 +73,21 @@ namespace jau { template< class iterator_type > std::string to_string(const iterator_type & iter, typename std::enable_if< - !std::is_class<iterator_type>::value - >::type* = 0 - ) { + std::is_pointer<iterator_type>::value + >::type* = 0 ) + { return aptrHexString((void*)iter); } + /**************************************************************************************** + ****************************************************************************************/ + /** * Implementation of a Copy-On-Write (CoW) read-write iterator for mutable value_type.<br> - * Instance holds the 'shared value_type reference', Storage_ref_type, - * of the current CoW storage until destruction. + * Instance holds a copy of the CoW's value_type storage until destruction. + * <p> + * Implementation complies with Type Traits iterator_category 'random_access_iterator_tag' + * </p> * <p> * This iterator wraps the native iterator of type 'iterator_type' * and manages the CoW related resource lifecycle. @@ -98,35 +101,57 @@ namespace jau { * consider using jau::cow_rw_iterator if elements won't get mutated * or any changes can be discarded. * </p> + * <p> + * To allow data-race free operations on this iterator's data copy from a potentially mutated CoW, + * only one begin iterator should be retrieved from CoW and all further operations shall use + * jau::cow_rw_iterator::size(), jau::cow_rw_iterator::begin() and jau::cow_rw_iterator::end(). + * </p> + * @see jau::cow_rw_iterator::size() + * @see jau::cow_rw_iterator::begin() + * @see jau::cow_rw_iterator::end() + * @see jau::for_each_fidelity * @see jau::cow_darray */ template <typename Storage_type, typename Storage_ref_type, typename CoW_container> class cow_rw_iterator { friend cow_ro_iterator<Storage_type, Storage_ref_type, CoW_container>; + template<typename, typename, typename> friend class cow_darray; + template<typename, typename> friend class cow_vector; public: + typedef Storage_type storage_t; + typedef Storage_ref_type storage_ref_t; + typedef CoW_container cow_container_t; + /** Actual iterator type of the contained native iterator, probably a simple pointer. */ - typedef typename Storage_type::iterator iterator_type; + typedef typename storage_t::iterator iterator_type; private: typedef std::iterator_traits<iterator_type> sub_traits_t; - CoW_container& cow_parent_; + cow_container_t& cow_parent_; std::lock_guard<std::recursive_mutex> lock_; - Storage_ref_type store_ref_; - iterator_type iterator_; + storage_ref_t store_ref_; + iterator_type iterator_; + iterator_type iterator_begin; + + constexpr explicit cow_rw_iterator(cow_container_t& cow_parent, const storage_ref_t& store, iterator_type iter) noexcept + : cow_parent_(cow_parent), lock_(cow_parent.get_write_mutex()), store_ref_(store), + iterator_(iter), iterator_begin(iter) {} + + constexpr cow_rw_iterator(cow_container_t& cow_parent, iterator_type (*get_begin)(storage_ref_t&)) + : cow_parent_(cow_parent), lock_(cow_parent.get_write_mutex()), + store_ref_(cow_parent.copy_store()), iterator_(get_begin(store_ref_)), iterator_begin(iterator_) {} - constexpr explicit cow_rw_iterator(CoW_container& cow_parent, const Storage_ref_type& store, iterator_type iter) noexcept - : cow_parent_(cow_parent), lock_(cow_parent.get_write_mutex()), store_ref_(store), iterator_(iter) {} public: typedef typename sub_traits_t::iterator_category iterator_category; // random_access_iterator_tag - typedef typename Storage_type::size_type size_type; // using our template overload Size_type - typedef typename Storage_type::difference_type difference_type; // derived from our Size_type - // typedef typename Storage_type::value_type value_type; // OK - // typedef typename Storage_type::reference reference; // - // typedef typename Storage_type::pointer pointer; // + typedef typename storage_t::size_type size_type; // using our template overload Size_type + typedef typename storage_t::difference_type difference_type; // derived from our Size_type + // typedef typename storage_t::value_type value_type; // OK + // typedef typename storage_t::reference reference; // + // typedef typename storage_t::pointer pointer; // typedef typename sub_traits_t::value_type value_type; // OK typedef typename sub_traits_t::reference reference; // 'value_type &' typedef typename sub_traits_t::pointer pointer; // 'value_type *' @@ -137,16 +162,6 @@ namespace jau { public: - constexpr cow_rw_iterator(CoW_container& cow_parent, iterator_type (*get_iterator)(Storage_ref_type&)) - : cow_parent_(cow_parent), lock_(cow_parent.get_write_mutex()), - store_ref_(cow_parent.copy_store()), iterator_(get_iterator(store_ref_)) {} - -#if 0 - constexpr cow_rw_iterator(CoW_container& cow_parent, iterator_type iter) - : cow_parent_(cow_parent), lock_(cow_parent.get_write_mutex()), - store_ref_(cow_parent.copy_store()), iterator_(iter) {} -#endif - #if __cplusplus > 201703L constexpr ~cow_rw_iterator() noexcept #else @@ -159,7 +174,7 @@ namespace jau { // C++ named requirements: LegacyIterator: CopyConstructible constexpr cow_rw_iterator(const cow_rw_iterator& o) noexcept : cow_parent_(o.cow_parent_), lock_(cow_parent_.get_write_mutex()), - store_ref_(o.store_ref_), iterator_(o.iterator_) {} + store_ref_(o.store_ref_), iterator_(o.iterator_), iterator_begin(o.iterator_begin) {} // C++ named requirements: LegacyIterator: CopyAssignable constexpr cow_rw_iterator& operator=(const cow_rw_iterator& o) noexcept { @@ -167,16 +182,18 @@ namespace jau { lock_ = cow_parent_.get_write_mutex(); store_ref_ = o.store_ref_; iterator_ = o.iterator_; + iterator_begin = o.iterator_begin; return *this; } // C++ named requirements: LegacyIterator: MoveConstructable constexpr cow_rw_iterator(cow_rw_iterator && o) noexcept : cow_parent_(std::move(o.cow_parent_)), lock_(cow_parent_.get_write_mutex()), - store_ref_(std::move(o.store_ref_)), iterator_(std::move(o.iterator_)) { + store_ref_(std::move(o.store_ref_)), iterator_(std::move(o.iterator_)), iterator_begin(std::move(o.iterator_begin)) { o.lock_ = nullptr; // ??? o.store_ref_ = nullptr; // o.iterator_ = nullptr; + // o.iterator_begin = nullptr; } // C++ named requirements: LegacyIterator: MoveAssignable @@ -185,8 +202,10 @@ namespace jau { lock_ = cow_parent_.get_write_mutex(); store_ref_ = std::move(o.store_ref_); iterator_ = std::move(o.iterator_); + iterator_begin = std::move(o.iterator_begin); o.store_ref_ = nullptr; // o.iterator_ = nullptr; + // o.iterator_begin = nullptr; return *this; } @@ -196,9 +215,45 @@ namespace jau { // std::swap( lock_, o.lock_); // lock stays in each std::swap( store_ref_, o.store_ref_); std::swap( iterator_, o.iterator_); + std::swap( iterator_begin, o.iterator_begin); } /** + * Return the size of the underlying value_type store. + * <p> + * This is an addition API entry, allowing data-race free arithmetic on + * this iterator's data snapshot from a potentially mutated CoW. + * </p> + * @see begin() + * @see end() + */ + constexpr size_type size() const noexcept { return store_ref_->size(); } + + /** + * Returns a new iterator pointing to the first element, aka begin. + * <p> + * This is an addition API entry, allowing data-race free operations on + * this iterator's data snapshot from a potentially mutated CoW. + * </p> + * @see size() + * @see end() + */ + constexpr cow_rw_iterator begin() const noexcept + { return cow_rw_iterator( cow_parent_, store_ref_, iterator_begin ); } + + /** + * Returns a new iterator pointing to the <i>element following the last element</i>, aka end.<br> + * <p> + * This is an addition API entry, allowing data-race free operations on + * this iterator's data snapshot from a potentially mutated CoW. + * </p> + * @see size() + * @see begin() + */ + constexpr cow_rw_iterator end() const noexcept + { return cow_rw_iterator( cow_parent_, store_ref_, iterator_begin + store_ref_->size() ); } + + /** * Returns a copy of the underlying storage iterator. */ constexpr iterator_type base() const noexcept { return iterator_; }; @@ -324,10 +379,12 @@ namespace jau { /** * Implementation of a Copy-On-Write (CoW) read-only iterator for immutable value_type.<br> - * Instance holds the 'shared value_type reference', Storage_ref_type, - * of the current CoW storage until destruction. + * Instance holds a 'shared value_type' snapshot of the current CoW storage until destruction. + * <p> + * Implementation complies with Type Traits iterator_category 'random_access_iterator_tag' + * </p> * <p> - * This iterator simply wraps the native iterator of type 'iterator_type' + * Implementation simply wraps the native iterator of type 'iterator_type' * and manages the CoW related resource lifecycle. * </p> * <p> @@ -337,31 +394,47 @@ namespace jau { * Also see jau::for_each_fidelity to iterate through in this good faith fashion. * </p> * <p> - * Implementation complies with iterator_category 'random_access_iterator_tag' + * To allow data-race free operations on this iterator's data snapshot from a potentially mutated CoW, + * only one begin iterator should be retrieved from CoW and all further operations shall use + * jau::cow_ro_iterator::size(), jau::cow_ro_iterator::begin() and jau::cow_ro_iterator::end(). * </p> + * @see jau::cow_ro_iterator::size() + * @see jau::cow_ro_iterator::begin() + * @see jau::cow_ro_iterator::end() * @see jau::for_each_fidelity * @see jau::cow_darray */ template <typename Storage_type, typename Storage_ref_type, typename CoW_container> class cow_ro_iterator { + template<typename, typename, typename> friend class cow_darray; + template<typename, typename> friend class cow_vector; + public: + typedef Storage_type storage_t; + typedef Storage_ref_type storage_ref_t; + typedef CoW_container cow_container_t; + /** Actual const iterator type of the contained native iterator, probably a simple pointer. */ - typedef typename Storage_type::const_iterator iterator_type; + typedef typename storage_t::const_iterator iterator_type; private: typedef std::iterator_traits<iterator_type> sub_traits_t; - Storage_ref_type store_ref_; - iterator_type iterator_; + storage_ref_t store_ref_; + iterator_type iterator_; + iterator_type iterator_begin; + + constexpr cow_ro_iterator(storage_ref_t store, iterator_type begin) noexcept + : store_ref_(store), iterator_(begin), iterator_begin(begin) { } public: typedef typename sub_traits_t::iterator_category iterator_category; // random_access_iterator_tag - typedef typename Storage_type::size_type size_type; // using our template overload Size_type - typedef typename Storage_type::difference_type difference_type; // derived from our Size_type - // typedef typename Storage_type::value_type value_type; // OK - // typedef typename Storage_type::reference reference; // Storage_type is not 'const' - // typedef typename Storage_type::pointer pointer; // Storage_type is not 'const' + typedef typename storage_t::size_type size_type; // using our template overload Size_type + typedef typename storage_t::difference_type difference_type; // derived from our Size_type + // typedef typename storage_t::value_type value_type; // OK + // typedef typename storage_t::reference reference; // storage_t is not 'const' + // typedef typename storage_t::pointer pointer; // storage_t is not 'const' typedef typename sub_traits_t::value_type value_type; // OK typedef typename sub_traits_t::reference reference; // 'const value_type &' typedef typename sub_traits_t::pointer pointer; // 'const value_type *' @@ -372,10 +445,7 @@ namespace jau { public: constexpr cow_ro_iterator() noexcept - : store_ref_(nullptr), iterator_() { } - - constexpr cow_ro_iterator(Storage_ref_type store, iterator_type iter) noexcept - : store_ref_(store), iterator_(iter) { } + : store_ref_(nullptr), iterator_(), iterator_begin() { } /** * Conversion constructor: cow_rw_iterator -> cow_ro_iterator @@ -384,39 +454,44 @@ namespace jau { * using a temporary cow_rw_iterator instance involving storage copy etc. * </p> */ - constexpr explicit cow_ro_iterator(const cow_rw_iterator<Storage_type, Storage_ref_type, CoW_container>& o) noexcept - : store_ref_(o.store_ref_), iterator_(o.iterator_) {} + constexpr explicit cow_ro_iterator(const cow_rw_iterator<storage_t, storage_ref_t, cow_container_t>& o) noexcept + : store_ref_(o.store_ref_), iterator_(o.iterator_), iterator_begin(o.iterator_begin) {} // C++ named requirements: LegacyIterator: CopyConstructible constexpr cow_ro_iterator(const cow_ro_iterator& o) noexcept - : store_ref_(o.store_ref_), iterator_(o.iterator_) {} + : store_ref_(o.store_ref_), iterator_(o.iterator_), iterator_begin(o.iterator_begin) {} // C++ named requirements: LegacyIterator: CopyAssignable constexpr cow_ro_iterator& operator=(const cow_ro_iterator& o) noexcept { store_ref_ = o.store_ref_; iterator_ = o.iterator_; + iterator_begin = o.iterator_begin; return *this; } - constexpr cow_ro_iterator& operator=(const cow_rw_iterator<Storage_type, Storage_ref_type, CoW_container>& o) noexcept { + constexpr cow_ro_iterator& operator=(const cow_rw_iterator<storage_t, storage_ref_t, cow_container_t>& o) noexcept { store_ref_ = o.store_ref_; iterator_ = o.iterator_; + iterator_begin = o.iterator_begin; return *this; } // C++ named requirements: LegacyIterator: MoveConstructable constexpr cow_ro_iterator(cow_ro_iterator && o) noexcept - : store_ref_(std::move(o.store_ref_)), iterator_(std::move(o.iterator_)) { + : store_ref_(std::move(o.store_ref_)), iterator_(std::move(o.iterator_)), iterator_begin(std::move(o.iterator_begin)) { o.store_ref_ = nullptr; // o.iterator_ = nullptr; + // o.iterator_begin = nullptr; } // C++ named requirements: LegacyIterator: MoveAssignable constexpr cow_ro_iterator& operator=(cow_ro_iterator&& o) noexcept { store_ref_ = std::move(o.store_ref_); iterator_ = std::move(o.iterator_); + iterator_begin = std::move(o.iterator_begin); o.store_ref_ = nullptr; // o.iterator_ = nullptr; + // o.iterator_begin = nullptr; return *this; } @@ -424,10 +499,49 @@ namespace jau { void swap(cow_ro_iterator& o) noexcept { std::swap( store_ref_, o.store_ref_); std::swap( iterator_, o.iterator_); + std::swap( iterator_begin, o.iterator_begin); } /** + * Return the size of the underlying value_type store. + * <p> + * This is an addition API entry, allowing data-race free arithmetic on + * this iterator's data snapshot from a potentially mutated CoW. + * </p> + * @see begin() + * @see end() + */ + constexpr size_type size() const noexcept { return store_ref_->size(); } + + /** + * Returns a new const_iterator pointing to the first element, aka begin. + * <p> + * This is an addition API entry, allowing data-race free operations on + * this iterator's data snapshot from a potentially mutated CoW. + * </p> + * @see size() + * @see end() + */ + constexpr cow_ro_iterator begin() const noexcept + { return cow_ro_iterator( store_ref_, iterator_begin ); } + + /** + * Returns a new const_iterator pointing to the <i>element following the last element</i>, aka end.<br> + * <p> + * This is an addition API entry, allowing data-race free operations on + * this iterator's data snapshot from a potentially mutated CoW. + * </p> + * @see size() + * @see begin() + */ + constexpr cow_ro_iterator end() const noexcept + { return cow_ro_iterator( store_ref_, iterator_begin + store_ref_->size() ); } + + /** * Returns a copy of the underlying storage const_iterator. + * <p> + * This is an addition API entry, inspired by the STL std::normal_iterator. + * </p> */ constexpr iterator_type base() const noexcept { return iterator_; }; @@ -448,7 +562,7 @@ namespace jau { : ( iterator_ < rhs.iterator_ ? -1 : 1); } - constexpr int compare(const cow_rw_iterator<Storage_type, Storage_ref_type, CoW_container>& rhs) const noexcept { + constexpr int compare(const cow_rw_iterator<storage_t, storage_ref_t, cow_container_t>& rhs) const noexcept { return store_ref_ == rhs.store_ref_ && iterator_ == rhs.iterator_ ? 0 : ( iterator_ < rhs.iterator_ ? -1 : 1); } @@ -533,7 +647,7 @@ namespace jau { constexpr difference_type operator-(const cow_ro_iterator& rhs) const noexcept { return iterator_ - rhs.iterator_; } - constexpr difference_type distance(const cow_rw_iterator<Storage_type, Storage_ref_type, CoW_container>& rhs) const noexcept + constexpr difference_type distance(const cow_rw_iterator<storage_t, storage_ref_t, cow_container_t>& rhs) const noexcept { return iterator_ - rhs.iterator_; } __constexpr_cxx20_ std::string toString() const noexcept { @@ -546,6 +660,9 @@ namespace jau { #endif }; + /**************************************************************************************** + ****************************************************************************************/ + template <typename Storage_type, typename Storage_ref_type, typename CoW_container> std::ostream & operator << (std::ostream &out, const cow_rw_iterator<Storage_type, Storage_ref_type, CoW_container> &c) { out << c.toString(); @@ -558,6 +675,9 @@ namespace jau { return out; } + /**************************************************************************************** + ****************************************************************************************/ + template <typename Storage_type, typename Storage_ref_type, typename CoW_container> constexpr bool operator==(const cow_ro_iterator<Storage_type, Storage_ref_type, CoW_container>& lhs, const cow_rw_iterator<Storage_type, Storage_ref_type, CoW_container>& rhs) noexcept @@ -630,6 +750,107 @@ namespace jau { const cow_ro_iterator<Storage_type, Storage_ref_type, CoW_container>& rhs) noexcept { return rhs.distance(lhs) * -1; } + /**************************************************************************************** + ****************************************************************************************/ + + /** + * <code>template< class T > is_cow_type<T>::value</code> compile-time Type Trait, + * determining whether the given template class is a CoW type, e.g. jau::cow_darray, + * jau::cow_vector or any of their iterator. + */ + template< class, class = void > + struct is_cow_type : std::false_type { }; + + /** + * <code>template< class T > is_cow_type<T>::value</code> compile-time Type Trait, + * determining whether the given template class is a CoW type, e.g. jau::cow_darray, + * jau::cow_vector or any of their iterator. + */ + template< class T > + struct is_cow_type<T, std::void_t<typename T::cow_container_t>> : std::true_type { }; + + template<class T> + const typename T::value_type * find_const(T& data, typename T::value_type const & elem, + std::enable_if_t< is_cow_type<T>::value, bool> = true ) noexcept + { + typename T::const_iterator begin = data.cbegin(); + typename T::const_iterator end = begin.end(); + auto it = jau::find( begin, end, elem); + if( it != end ) { + return &(*it); + } + return nullptr; + } + template<class T> + const typename T::value_type * find_const(T& data, typename T::value_type const & elem, + std::enable_if_t< !is_cow_type<T>::value, bool> = true ) noexcept + { + typename T::const_iterator end = data.cend(); + auto it = jau::find( data.cbegin(), end, elem); + if( it != end ) { + return &(*it); + } + return nullptr; + } + + /**************************************************************************************** + ****************************************************************************************/ + + template<class T, class UnaryFunction> + constexpr UnaryFunction for_each_const(T& data, UnaryFunction f, + std::enable_if_t< is_cow_type<T>::value, bool> = true ) noexcept + { + typename T::const_iterator first = data.cbegin(); + typename T::const_iterator last = first.end(); + for (; first != last; ++first) { + f(*first); + } + return f; // implicit move since C++11 + } + template<class T, class UnaryFunction> + constexpr UnaryFunction for_each_const(T& data, UnaryFunction f, + std::enable_if_t< !is_cow_type<T>::value, bool> = true ) noexcept + { + typename T::const_iterator first = data.cbegin(); + typename T::const_iterator last = data.cend(); + for (; first != last; ++first) { + f(*first); + } + return f; // implicit move since C++11 + } + + /**************************************************************************************** + ****************************************************************************************/ + + /** + * See jau::for_each_fidelity() + */ + template<class T, class UnaryFunction> + constexpr UnaryFunction for_each_fidelity(T& data, UnaryFunction f, + std::enable_if_t< is_cow_type<T>::value, bool> = true ) noexcept + { + typename T::const_iterator first = data.cbegin(); + typename T::const_iterator last = first.end(); + for (; first != last; ++first) { + f( *const_cast<typename T::value_type*>( & (*first) ) ); + } + return f; // implicit move since C++11 + } + /** + * See jau::for_each_fidelity() + */ + template<class T, class UnaryFunction> + constexpr UnaryFunction for_each_fidelity(T& data, UnaryFunction f, + std::enable_if_t< !is_cow_type<T>::value, bool> = true ) noexcept + { + typename T::const_iterator first = data.cbegin(); + typename T::const_iterator last = data.cend(); + for (; first != last; ++first) { + f( *const_cast<typename T::value_type*>( & (*first) ) ); + } + return f; // implicit move since C++11 + } + } /* namespace jau */ diff --git a/include/jau/cow_vector.hpp b/include/jau/cow_vector.hpp index 5223f82..365d776 100644 --- a/include/jau/cow_vector.hpp +++ b/include/jau/cow_vector.hpp @@ -117,23 +117,19 @@ namespace jau { typedef std::vector<value_type, allocator_type> storage_t; typedef std::shared_ptr<storage_t> storage_ref_t; + typedef cow_vector<value_type, allocator_type> cow_container_t; + /** - * Immutable, read-only const_iterator, lock-free, - * holding the current shared store reference until destruction. - * <p> - * Using jau::cow_vector::get_snapshot() at construction. - * </p> + * @see jau::cow_darray::const_iterator + * @see jau::cow_ro_iterator */ - typedef cow_ro_iterator<storage_t, storage_ref_t, cow_vector> const_iterator; + typedef cow_ro_iterator<storage_t, storage_ref_t, cow_container_t> const_iterator; /** - * Mutable, read-write iterator, holding the write-lock and a store copy until destruction. - * <p> - * Using jau::cow_vector::get_write_mutex(), jau::cow_vector::copy_store() at construction<br> - * and jau::cow_vector::set_store() at destruction. - * </p> + * @see jau::cow_darray::iterator + * @see jau::cow_rw_iterator */ - typedef cow_rw_iterator<storage_t, storage_ref_t, cow_vector> iterator; + typedef cow_rw_iterator<storage_t, storage_ref_t, cow_container_t> iterator; private: static constexpr size_type DIFF_MAX = std::numeric_limits<difference_type>::max(); @@ -278,32 +274,24 @@ namespace jau { // const_iterator, non mutable, read-only - constexpr const_iterator begin() const noexcept { - return const_iterator(get_snapshot(), store_ref->cbegin()); - } + // Removed for clarity: "constexpr const_iterator begin() const noexcept" + /** + * See description in jau::cow_darray::cbegin() + */ constexpr const_iterator cbegin() const noexcept { return const_iterator(get_snapshot(), store_ref->cbegin()); } - constexpr const_iterator end() const noexcept { - return const_iterator(get_snapshot(), store_ref->cend()); - } - - constexpr const_iterator cend() const noexcept { - return const_iterator(get_snapshot(), store_ref->cend()); - } - // iterator, mutable, read-write + /** + * See description in jau::cow_darray::begin() + */ constexpr iterator begin() noexcept { return iterator(*this, [](storage_ref_t& new_store) -> typename storage_t::iterator { return new_store->begin(); } ); } - constexpr iterator end() noexcept { - return iterator(*this, [](storage_ref_t& new_store) -> typename storage_t::iterator { return new_store->end(); } ); - } - // read access allocator_type get_allocator() const noexcept { diff --git a/test/test_cow_darray_01.cpp b/test/test_cow_darray_01.cpp index 7b7f87a..1ae6fb8 100644 --- a/test/test_cow_darray_01.cpp +++ b/test/test_cow_darray_01.cpp @@ -35,6 +35,7 @@ #include "test_datatype01.hpp" +#include <jau/basic_algos.hpp> #include <jau/basic_types.hpp> #include <jau/darray.hpp> #include <jau/cow_darray.hpp> @@ -72,17 +73,6 @@ DataType01 * findDataSet01_idx(T& data, DataType01 const & elem) noexcept { } return nullptr; } -template<class T> -const DataType01 * findDataSet01_itr(T& data, DataType01 const & elem) noexcept { - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - if( elem == *iter ) { - return &(*iter); - } - } - return nullptr; -} template<class T> static void test_00_list_idx(T& data, const bool show) { @@ -98,18 +88,20 @@ static void test_00_list_idx(T& data, const bool show) { } } template<class T> -static void test_00_list_itr(T& data, const bool show) { +static int test_00_list_itr(T& data, const bool show) { Addr48Bit a0(start_addr); - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(std::size_t i = 0; iter != end && a0.next(); ++iter, ++i) { - const DataType01 & e = *iter; - e.nop(); + int some_number = 0, i=0; // add some validated work, avoiding any 'optimization away' + jau::for_each_const(data, [&some_number, &a0, &i, show](const DataType01 & e) { + some_number += e.nop(); if( show ) { - printf("data[%zu]: %s\n", i, e.toString().c_str()); + printf("data[%d]: %s\n", i, e.toString().c_str()); } + REQUIRE( a0.next() ); REQUIRE(e.address == a0); - } + ++i; + } ); + REQUIRE(some_number > 0); + return some_number; } template<class T> @@ -136,7 +128,7 @@ static void test_00_seq_find_itr(T& data) { for(; i<size && a0.next(); i++) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01 *found = findDataSet01_itr(data, elem); + const DataType01 *found = jau::find_const(data, elem); if( nullptr != found ) { fi++; found->nop(); @@ -191,7 +183,7 @@ static void test_00_seq_fill_unique_itr(T& data, const std::size_t size) { for(; i<size && a0.next(); i++) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01* exist = findDataSet01_itr(data, elem); + const DataType01* exist = jau::find_const(data, elem); if( nullptr == exist ) { data.push_back( std::move(elem) ); fi++; diff --git a/test/test_cow_darray_perf01.cpp b/test/test_cow_darray_perf01.cpp index c4c519b..70e3a47 100644 --- a/test/test_cow_darray_perf01.cpp +++ b/test/test_cow_darray_perf01.cpp @@ -52,9 +52,6 @@ using namespace jau; static uint8_t start_addr_b[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static Addr48Bit start_addr(start_addr_b); -// #define USE_STD_ITER_ALGO 1 -#define USE_JAU_ITER_ALGO 1 - /**************************************************************************************** ****************************************************************************************/ @@ -70,34 +67,6 @@ DataType01 * findDataSet01_idx(T& data, DataType01 const & elem) noexcept { return nullptr; } -template<class T> -const DataType01 * findDataSet01_itr(T& data, DataType01 const & elem) noexcept { -#if defined(USE_STD_ITER_ALGO) - // much slower, approx 3x over 1000 * 1000, why? - typename T::const_iterator end = data.cend(); - auto it = std::find( data.cbegin(), end, elem); - if( it != end ) { - return &(*it); - } -#elif defined (USE_JAU_ITER_ALGO) - // same logic, much faster - typename T::const_iterator end = data.cend(); - auto it = jau::find( data.cbegin(), end, elem); - if( it != end ) { - return &(*it); - } -#else - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - if( elem == *iter ) { - return &(*iter); - } - } -#endif - return nullptr; -} - template<class T, typename Size_type> static int test_00_list_idx(T& data) { int some_number = 0; // add some validated work, avoiding any 'optimization away' @@ -113,20 +82,9 @@ static int test_00_list_idx(T& data) { template<class T> static int test_00_list_itr(T& data) { int some_number = 0; // add some validated work, avoiding any 'optimization away' -#if defined(USE_STD_ITER_ALGO) - // slower, why? - std::for_each(data.cbegin(), data.cend(), [&some_number](const DataType01 & e) { some_number += e.nop(); }); -#elif defined (USE_JAU_ITER_ALGO) - // same logic, faster - jau::for_each(data.cbegin(), data.cend(), [&some_number](const DataType01 & e) { some_number += e.nop(); }); -#else - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - const DataType01 & e = *iter; + jau::for_each_const(data, [&some_number](const DataType01 & e) { some_number += e.nop(); - } -#endif + } ); REQUIRE(some_number > 0); return some_number; } @@ -156,7 +114,7 @@ static void test_00_seq_find_itr(T& data) { for(; i<size && a0.next(); ++i) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01 *found = findDataSet01_itr<T>(data, elem); + const DataType01 *found = jau::find_const<T>(data, elem); if( nullptr != found ) { ++fi; found->nop(); @@ -199,7 +157,7 @@ static void test_00_seq_fill_unique_itr(T& data, const Size_type size) { for(; i<size && a0.next(); ++i) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01* exist = findDataSet01_itr<T>(data, elem); + const DataType01* exist = jau::find_const<T>(data, elem); if( nullptr == exist ) { data.push_back( std::move( elem ) ); ++fi; diff --git a/test/test_cow_iterator_01.cpp b/test/test_cow_iterator_01.cpp index 737d870..834a4c7 100644 --- a/test/test_cow_iterator_01.cpp +++ b/test/test_cow_iterator_01.cpp @@ -36,6 +36,7 @@ #include "test_datatype01.hpp" +#include <jau/basic_algos.hpp> #include <jau/basic_types.hpp> #include <jau/darray.hpp> #include <jau/cow_darray.hpp> @@ -67,30 +68,20 @@ JAU_TYPENAME_CUE_ALL(jau_cow_vector_DataType01) JAU_TYPENAME_CUE_ALL(jau_cow_darray_DataType01) template<class T> -const DataType01 * findDataSet01_itr(T& data, DataType01 const & elem) noexcept { - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - if( elem == *iter ) { - return &(*iter); - } - } - return nullptr; -} - -template<class T> -static void test_00_list_itr(T& data, const bool show) { +static int test_00_list_itr(T& data, const bool show) { Addr48Bit a0(start_addr); - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(std::size_t i = 0; iter != end && a0.next(); ++iter, ++i) { - const DataType01 & e = *iter; - e.nop(); + int some_number = 0, i=0; // add some validated work, avoiding any 'optimization away' + jau::for_each_const(data, [&some_number, &a0, &i, show](const DataType01 & e) { + some_number += e.nop(); if( show ) { - printf("data[%zu]: %s\n", i, e.toString().c_str()); + printf("data[%d]: %s\n", i, e.toString().c_str()); } + REQUIRE( a0.next() ); REQUIRE(e.address == a0); - } + ++i; + } ); + REQUIRE(some_number > 0); + return some_number; } template<class T> @@ -101,7 +92,7 @@ static void test_00_seq_find_itr(T& data) { for(; i<size && a0.next(); i++) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01 *found = findDataSet01_itr(data, elem); + const DataType01 *found = jau::find_const(data, elem); if( nullptr != found ) { fi++; found->nop(); @@ -419,11 +410,56 @@ static void test_iterator_arithmetic(const typename T::size_type size, } template<class T> -static bool test_const_iterator_ops(const std::string& type_id, T& data) { - printf("**** test_const_iterator_ops: %s\n", type_id.c_str()); +static bool test_const_iterator_ops(const std::string& type_id, T& data, + std::enable_if_t< is_cow_type<T>::value, bool> = true ) +{ + printf("**** test_const_iterator_ops(CoW): %s\n", type_id.c_str()); + { + typename T::const_iterator begin = data.cbegin(); // immutable new_store non-const iterator, gets held until destruction + typename T::const_iterator end = begin.end(); // no new store iterator, on same store as begin, obtained from begin + typename T::difference_type data_size = static_cast<typename T::difference_type>(data.size()); + typename T::difference_type begin_size = static_cast<typename T::difference_type>(begin.size()); + typename T::difference_type end_size = static_cast<typename T::difference_type>(end.size()); + REQUIRE( begin_size == data_size ); + REQUIRE( end_size == data_size ); + REQUIRE( end - begin == data_size ); + REQUIRE( end - end_size == begin ); + REQUIRE( begin + begin_size == end ); + REQUIRE( *( end - end_size ) == *begin ); + REQUIRE( *( begin + begin_size ) == *end ); + test_iterator_dereference<T, typename T::const_iterator>(begin.size(), begin, end); + } + { typename T::const_iterator begin = data.cbegin(); // no new store const_iterator - typename T::const_iterator end = data.cend(); // no new store const_iterator, on same store as begin + typename T::const_iterator end = begin.end(); // no new store const_iterator, on same store as begin, obtained from begin + test_iterator_arithmetic<T, typename T::const_iterator>(data.size(), begin, end); + } +#if 0 + { + // INTENIONAL FAILURES, checking behavior of error values etc + typename T::const_iterator begin = data.cbegin(); // no new store const_iterator + typename T::const_iterator iter = begin + 1; + CHECK( *begin == *iter ); + CHECK( begin == iter ); + } +#endif + return true; +} +template<class T> +static bool test_const_iterator_ops(const std::string& type_id, T& data, + std::enable_if_t< !is_cow_type<T>::value, bool> = true ) +{ + printf("**** test_const_iterator_ops: %s\n", type_id.c_str()); + { + typename T::const_iterator begin = data.cbegin(); // mutable new_store non-const iterator, gets held until destruction + typename T::const_iterator end = data.cend(); // no new store iterator, on same store as begin and from begin + typename T::difference_type data_size = static_cast<typename T::difference_type>(data.size()); + REQUIRE( end - begin == data_size ); + REQUIRE( end - data_size == begin ); + REQUIRE( begin + data_size == end ); + REQUIRE( *( end - data_size ) == *begin ); + REQUIRE( *( begin + data_size ) == *end ); test_iterator_dereference<T, typename T::const_iterator>(data.size(), begin, end); } @@ -445,11 +481,48 @@ static bool test_const_iterator_ops(const std::string& type_id, T& data) { } template<class T> -static bool test_mutable_iterator_ops(const std::string& type_id, T& data) { - printf("**** test_mutable_iterator_ops: %s\n", type_id.c_str()); +static bool test_mutable_iterator_ops(const std::string& type_id, T& data, + std::enable_if_t< is_cow_type<T>::value, bool> = true ) +{ + printf("**** test_mutable_iterator_ops(CoW): %s\n", type_id.c_str()); + { + typename T::iterator begin = data.begin(); // mutable new_store non-const iterator, gets held until destruction + typename T::iterator end = begin.end(); // no new store iterator, on same store as begin and from begin + typename T::difference_type data_size = static_cast<typename T::difference_type>(data.size()); + typename T::difference_type begin_size = static_cast<typename T::difference_type>(begin.size()); + typename T::difference_type end_size = static_cast<typename T::difference_type>(end.size()); + REQUIRE( begin_size == data_size ); + REQUIRE( end_size == data_size ); + REQUIRE( end - begin == data_size ); + REQUIRE( end - end_size == begin ); + REQUIRE( begin + begin_size == end ); + REQUIRE( *( end - end_size ) == *begin ); + REQUIRE( *( begin + begin_size ) == *end ); + test_iterator_dereference<T, typename T::iterator>(begin.size(), begin, end); + } + { typename T::iterator begin = data.begin(); // mutable new_store non-const iterator, gets held until destruction typename T::iterator end = begin + data.size(); // hence use iterator artihmetic to have end pointer + test_iterator_arithmetic<T, typename T::iterator>(data.size(), begin, end); + } + return true; +} + +template<class T> +static bool test_mutable_iterator_ops(const std::string& type_id, T& data, + std::enable_if_t< !is_cow_type<T>::value, bool> = true ) +{ + printf("**** test_mutable_iterator_ops(___): %s\n", type_id.c_str()); + { + typename T::iterator begin = data.begin(); // mutable new_store non-const iterator, gets held until destruction + typename T::iterator end = data.end(); // no new store iterator, on same store as begin and from begin + typename T::difference_type data_size = static_cast<typename T::difference_type>(data.size()); + REQUIRE( end - begin == data_size ); + REQUIRE( end - data_size == begin ); + REQUIRE( begin + data_size == end ); + REQUIRE( *( end - data_size ) == *begin ); + REQUIRE( *( begin + data_size ) == *end ); test_iterator_dereference<T, typename T::iterator>(data.size(), begin, end); } diff --git a/test/test_hashset_perf01.cpp b/test/test_hashset_perf01.cpp index ff8c8d2..223c05d 100644 --- a/test/test_hashset_perf01.cpp +++ b/test/test_hashset_perf01.cpp @@ -54,34 +54,6 @@ static Addr48Bit start_addr(start_addr_b); #define USE_JAU_ITER_ALGO 1 template<class T> -const DataType01 * findDataSet01_itr(T& data, DataType01 const & elem) noexcept { -#if defined(USE_STD_ITER_ALGO) - // much slower, approx 3x over 1000 * 1000, why? - typename T::const_iterator end = data.cend(); - auto it = std::find( data.cbegin(), end, elem); - if( it != end ) { - return &(*it); - } -#elif defined (USE_JAU_ITER_ALGO) - // same logic, much faster - typename T::const_iterator end = data.cend(); - auto it = jau::find( data.cbegin(), end, elem); - if( it != end ) { - return &(*it); - } -#else - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - if( elem == *iter ) { - return &(*iter); - } - } -#endif - return nullptr; -} - -template<class T> const DataType01 * findDataSet01_hash(T& data, DataType01 const & elem) noexcept { auto search = data.find(elem); if( search != data.end() ) { @@ -93,20 +65,9 @@ const DataType01 * findDataSet01_hash(T& data, DataType01 const & elem) noexcept template<class T> static int test_00_list_itr(T& data) { int some_number = 0; // add some validated work, avoiding any 'optimization away' -#if defined(USE_STD_ITER_ALGO) - // slower, why? - std::for_each(data.cbegin(), data.cend(), [&some_number](const DataType01 & e) { some_number += e.nop(); }); -#elif defined (USE_JAU_ITER_ALGO) - // same logic, faster - jau::for_each(data.cbegin(), data.cend(), [&some_number](const DataType01 & e) { some_number += e.nop(); }); -#else - typename T::const_iterator iter = data.cbegin(); - typename T::const_iterator end = data.cend(); - for(; iter != end ; ++iter) { - const DataType01 & e = *iter; + jau::for_each_const(data, [&some_number](const DataType01 & e) { some_number += e.nop(); - } -#endif + } ); REQUIRE(some_number > 0); return some_number; } @@ -119,7 +80,7 @@ static void test_00_seq_find_itr(T& data) { for(; i<size && a0.next(); ++i) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01 *found = findDataSet01_itr<T>(data, elem); + const DataType01 *found = jau::find_const<T>(data, elem); if( nullptr != found ) { ++fi; found->nop(); @@ -163,7 +124,7 @@ static void test_00_seq_fill_unique_itr(T& data, const Size_type size) { for(; i<size && a0.next(); ++i) { DataType01 elem(a0, static_cast<uint8_t>(1)); - const DataType01* exist = findDataSet01_itr<T>(data, elem); + const DataType01* exist = jau::find_const<T>(data, elem); if( nullptr == exist ) { data.push_back( std::move( elem ) ); ++fi; |