// ***************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * // ***************************************************************************** #ifndef STL_TOOLS_H_84567184321434 #define STL_TOOLS_H_84567184321434 #include #include #include #include #include #include #include #include #include #include "type_traits.h" //enhancements for namespace zen { //unfortunately std::erase_if is useless garbage on GCC 12 (requires non-modifying predicate) template void eraseIf(std::vector& v, Predicate p); template void eraseIf(std::set& s, Predicate p); template void eraseIf(std::map& m, Predicate p); //append STL containers template void append(std::vector& v, const C& c); template void append(std::set& s, const C& c); template void append(std::map& m, const C& c); template void removeDuplicates(std::vector& v); template void removeDuplicates(std::vector& v, CompLess less); template void removeDuplicatesStable(std::vector& v, CompLess less); template void removeDuplicatesStable(std::vector& v); //searching STL containers template BidirectionalIterator findLast(BidirectionalIterator first, BidirectionalIterator last, const T& value); template inline RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last, const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast); template inline RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last, const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast, IsEq isEqual); //replacement for std::find_end taking advantage of bidirectional iterators (and giving the algorithm a reasonable name) template RandomAccessIterator1 searchLast(RandomAccessIterator1 first, RandomAccessIterator1 last, RandomAccessIterator2 needleFirst, RandomAccessIterator2 needleLast); //binary search returning an iterator template RandomAccessIterator binarySearch(RandomAccessIterator first, RandomAccessIterator last, const T& value, CompLess less); //read-only variant of std::merge; input: two sorted ranges template void mergeTraversal(Iterator first1, Iterator last1, Iterator first2, Iterator last2, FunctionLeftOnly lo, FunctionBoth bo, FunctionRightOnly ro, Compare compare); //why, oh why is there no std::optional::get()??? template inline T* get( std::optional& opt) { return opt ? &*opt : nullptr; } template inline const T* get(const std::optional& opt) { return opt ? &*opt : nullptr; } //=========================================================================== template class SharedRef //why is there no std::shared_ref??? { public: SharedRef() = delete; //no surprise memory allocations! explicit SharedRef(std::shared_ptr ptr) : ref_(std::move(ptr)) { assert(ref_); } template SharedRef(const SharedRef& other) : ref_(other.ref_) {} /**/ T& ref() { return *ref_; }; const T& ref() const { return *ref_; }; std::shared_ptr< T> ptr() { return ref_; }; std::shared_ptr ptr() const { return ref_; }; private: template friend class SharedRef; std::shared_ptr ref_; //always bound }; template inline SharedRef makeSharedRef(Args&& ... args) { return SharedRef(std::make_shared(std::forward(args)...)); } //######################## implementation ######################## template inline void eraseIf(std::vector& v, Predicate p) { v.erase(std::remove_if(v.begin(), v.end(), p), v.end()); } namespace impl { template inline void setOrMapEraseIf(S& s, Predicate p) { for (auto it = s.begin(); it != s.end();) if (p(*it)) s.erase(it++); else ++it; } } template inline void eraseIf(std::set& s, Predicate p) { impl::setOrMapEraseIf(s, p); } //don't make this any more generic! e.g. must not compile for std::vector!!! template inline void eraseIf(std::map& m, Predicate p) { impl::setOrMapEraseIf(m, p); } template inline void eraseIf(std::unordered_set& s, Predicate p) { impl::setOrMapEraseIf(s, p); } template inline void eraseIf(std::unordered_map& m, Predicate p) { impl::setOrMapEraseIf(m, p); } template inline void append(std::vector& v, const C& c) { v.insert(v.end(), c.begin(), c.end()); } template inline void append(std::set& s, const C& c) { s.insert(c.begin(), c.end()); } template inline void append(std::map& m, const C& c) { m.insert(c.begin(), c.end()); } template inline void removeDuplicates(std::vector& v, CompLess less, CompEqual eq) { std::sort(v.begin(), v.end(), less); v.erase(std::unique(v.begin(), v.end(), eq), v.end()); } template inline void removeDuplicates(std::vector& v, CompLess less) { removeDuplicates(v, less, [&](const auto& lhs, const auto& rhs) { return !less(lhs, rhs) && !less(rhs, lhs); }); } template inline void removeDuplicates(std::vector& v) { removeDuplicates(v, std::less{}, std::equal_to{}); } template inline void removeDuplicatesStable(std::vector& v, CompLess less) { std::set usedItems(less); v.erase(std::remove_if(v.begin(), v.end(), /**/[&usedItems](const T& e) { return !usedItems.insert(e).second; }), v.end()); } template inline void removeDuplicatesStable(std::vector& v) { removeDuplicatesStable(v, std::less{}); } template inline RandomAccessIterator binarySearch(RandomAccessIterator first, RandomAccessIterator last, const T& value, CompLess less) { static_assert(std::is_same_v::iterator_category, std::random_access_iterator_tag>); first = std::lower_bound(first, last, value, less); //alternative: std::partition_point if (first != last && !less(value, *first)) return first; else return last; } template inline BidirectionalIterator findLast(const BidirectionalIterator first, const BidirectionalIterator last, const T& value) { for (BidirectionalIterator it = last; it != first;) //reverse iteration: 1. check 2. decrement 3. evaluate { --it; // if (*it == value) return it; } return last; } template inline RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last, const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast, IsEq isEqual) { if (needleLast - needleFirst == 1) //don't use expensive std::search unless required! return std::find_if(first, last, [needleFirst, isEqual](const auto c) { return isEqual(*needleFirst, c); }); //"*needleFirst" could be improved with value rather than pointer access, at least for built-in types like "char" return std::search(first, last, needleFirst, needleLast, isEqual); } template inline RandomAccessIterator1 searchFirst(const RandomAccessIterator1 first, const RandomAccessIterator1 last, const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast) { return searchFirst(first, last, needleFirst, needleLast, std::equal_to{}); } template inline RandomAccessIterator1 searchLast(const RandomAccessIterator1 first, RandomAccessIterator1 last, const RandomAccessIterator2 needleFirst, const RandomAccessIterator2 needleLast) { if (needleLast - needleFirst == 1) //fast-path return findLast(first, last, *needleFirst); const RandomAccessIterator1 itNotFound = last; //reverse iteration: 1. check 2. decrement 3. evaluate for (;;) { RandomAccessIterator1 it1 = last; RandomAccessIterator2 it2 = needleLast; for (;;) { if (it2 == needleFirst) return it1; if (it1 == first) return itNotFound; --it1; --it2; if (*it1 != *it2) break; } --last; } } //--------------------------------------------------------------------------------------- //read-only variant of std::merge; input: two sorted ranges template inline void mergeTraversal(Iterator firstL, Iterator lastL, Iterator firstR, Iterator lastR, FunctionLeftOnly lo, FunctionBoth bo, FunctionRightOnly ro, Compare compare) { auto itL = firstL; auto itR = firstR; auto finishLeft = [&] { std::for_each(itL, lastL, lo); }; auto finishRight = [&] { std::for_each(itR, lastR, ro); }; if (itL == lastL) return finishRight(); if (itR == lastR) return finishLeft (); for (;;) if (const std::weak_ordering cmp = compare(*itL, *itR); cmp < 0) { lo(*itL); if (++itL == lastL) return finishRight(); } else if (cmp > 0) { ro(*itR); if (++itR == lastR) return finishLeft(); } else { bo(*itL, *itR); ++itL; // ++itR; //increment BOTH before checking for end of range! if (itL == lastL) return finishRight(); if (itR == lastR) return finishLeft (); //simplify loop by placing both EOB checks at the beginning? => slightly slower } } template class FNV1aHash //FNV-1a: https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function { public: FNV1aHash() {} explicit FNV1aHash(Num startVal) : hashVal_(startVal) { assert(startVal != 0); /*yes, might be a real hash, but most likely bad init value*/} void add(Num n) { hashVal_ ^= n; hashVal_ *= prime_; } Num get() const { return hashVal_; } private: static_assert(isUnsignedInt); static_assert(sizeof(Num) == 4 || sizeof(Num) == 8); static constexpr Num base_ = sizeof(Num) == 4 ? 2166136261U : 14695981039346656037ULL; static constexpr Num prime_ = sizeof(Num) == 4 ? 16777619U : 1099511628211ULL; Num hashVal_ = base_; }; } #endif //STL_TOOLS_H_84567184321434