move Iterator, Enumerate, EvictingCacheMap, Foreach, Merge, and
authorJames Sedgwick <jsedgwick@fb.com>
Mon, 23 Oct 2017 19:19:27 +0000 (12:19 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Mon, 23 Oct 2017 19:35:38 +0000 (12:35 -0700)
Summary: this is all non-hphp includes that are going in container/

Reviewed By: mzlee, yfeldblum

Differential Revision: D6121745

fbshipit-source-id: b024bde8835fc7f332686793d75eb8e71591c912

53 files changed:
CMakeLists.txt
folly/AtomicHashMap.h
folly/Benchmark.cpp
folly/Enumerate.h [deleted file]
folly/EvictingCacheMap.h [deleted file]
folly/Foreach-inl.h [deleted file]
folly/Foreach.h [deleted file]
folly/Iterator.h [deleted file]
folly/Makefile.am
folly/SparseByteSet.h [deleted file]
folly/algorithm/Merge.h [deleted file]
folly/algorithm/test/MergeTest.cpp [deleted file]
folly/concurrency/CoreCachedSharedPtr.h
folly/container/Enumerate.h [new file with mode: 0644]
folly/container/EvictingCacheMap.h [new file with mode: 0644]
folly/container/Foreach-inl.h [new file with mode: 0644]
folly/container/Foreach.h [new file with mode: 0644]
folly/container/Iterator.h [new file with mode: 0644]
folly/container/Merge.h [new file with mode: 0644]
folly/container/SparseByteSet.h [new file with mode: 0644]
folly/container/test/EnumerateTest.cpp [new file with mode: 0644]
folly/container/test/EvictingCacheMapTest.cpp [new file with mode: 0644]
folly/container/test/ForeachBenchmark.cpp [new file with mode: 0644]
folly/container/test/ForeachTest.cpp [new file with mode: 0644]
folly/container/test/IteratorTest.cpp [new file with mode: 0644]
folly/container/test/MergeTest.cpp [new file with mode: 0644]
folly/container/test/SparseByteSetBenchmark.cpp [new file with mode: 0644]
folly/container/test/SparseByteSetTest.cpp [new file with mode: 0644]
folly/detail/RangeCommon.cpp
folly/detail/ThreadLocalDetail.h
folly/docs/Benchmark.md
folly/gen/test/StringBenchmark.cpp
folly/stats/test/HistogramBenchmark.cpp
folly/stats/test/TimeSeriesTest.cpp
folly/test/BenchmarkTest.cpp
folly/test/ConcurrentSkipListTest.cpp
folly/test/ConvBenchmark.cpp
folly/test/ConvTest.cpp
folly/test/EnumerateTest.cpp [deleted file]
folly/test/EvictingCacheMapTest.cpp [deleted file]
folly/test/FBStringBenchmark.cpp
folly/test/FBStringTest.cpp
folly/test/FBVectorBenchmark.cpp
folly/test/FBVectorTest.cpp
folly/test/ForeachBenchmark.cpp [deleted file]
folly/test/ForeachTest.cpp [deleted file]
folly/test/IteratorTest.cpp [deleted file]
folly/test/Makefile.am
folly/test/RandomBenchmark.cpp
folly/test/RangeFindBenchmark.cpp
folly/test/SparseByteSetBench.cpp [deleted file]
folly/test/SparseByteSetTest.cpp [deleted file]
folly/test/SynchronizedTestLib-inl.h

index 530d6a2a8ab03bedc4a35983c0aca15ca12104f8..dae65719ecbef73abd04beb04dbe42c5998ee364 100755 (executable)
@@ -311,6 +311,13 @@ if (BUILD_TESTS)
       TEST threaded_executor_test SOURCES ThreadedExecutorTest.cpp
       TEST unbounded_blocking_queue_test SOURCES UnboundedBlockingQueueTest.cpp
 
+    DIRECTORY container/test/
+      TEST enumerate_test SOURCES EnumerateTest.cpp
+      TEST evicting_cache_map_test SOURCES EvictingCacheMapTest.cpp
+      TEST foreach_test SOURCES ForeachTest.cpp
+      TEST merge_test SOURCES MergeTest.cpp
+      TEST sparse_byte_set_test SOURCES SparseByteSetTest.cpp
+
     DIRECTORY experimental/test/
       TEST autotimer_test SOURCES AutoTimerTest.cpp
       TEST bits_test_2 SOURCES BitsTest.cpp
@@ -520,8 +527,6 @@ if (BUILD_TESTS)
       TEST dynamic_converter_test SOURCES DynamicConverterTest.cpp
       TEST dynamic_other_test SOURCES DynamicOtherTest.cpp
       TEST endian_test SOURCES EndianTest.cpp
-      TEST enumerate_test SOURCES EnumerateTest.cpp
-      TEST evicting_cache_map_test SOURCES EvictingCacheMapTest.cpp
       TEST exception_test SOURCES ExceptionTest.cpp
       TEST exception_wrapper_test SOURCES ExceptionWrapperTest.cpp
       TEST expected_test SOURCES ExpectedTest.cpp
@@ -531,7 +536,6 @@ if (BUILD_TESTS)
       TEST file_util_test HANGING
         SOURCES FileUtilTest.cpp
       TEST fingerprint_test SOURCES FingerprintTest.cpp
-      TEST foreach_test SOURCES ForeachTest.cpp
       TEST format_other_test SOURCES FormatOtherTest.cpp
       TEST format_test SOURCES FormatTest.cpp
       TEST function_scheduler_test SOURCES FunctionSchedulerTest.cpp
@@ -563,7 +567,6 @@ if (BUILD_TESTS)
       TEST memcpy_test SOURCES MemcpyTest.cpp
       TEST memory_idler_test SOURCES MemoryIdlerTest.cpp
       TEST memory_test SOURCES MemoryTest.cpp
-      TEST merge SOURCES MergeTest.cpp
       TEST move_wrapper_test SOURCES MoveWrapperTest.cpp
       TEST mpmc_pipeline_test SOURCES MPMCPipelineTest.cpp
       TEST mpmc_queue_test SLOW
@@ -594,7 +597,6 @@ if (BUILD_TESTS)
       TEST singletonvault_c_test SOURCES SingletonVaultCTest.cpp
       TEST small_vector_test SOURCES small_vector_test.cpp
       TEST sorted_vector_types_test SOURCES sorted_vector_test.cpp
-      TEST sparse_byte_set_test SOURCES SparseByteSetTest.cpp
       TEST string_test SOURCES StringTest.cpp
       TEST synchronized_test SOURCES SynchronizedTest.cpp
       TEST thread_cached_int_test SOURCES ThreadCachedIntTest.cpp
index af8aa633f4a66c7409d2d7f245564e8c59a5a3e4..280b0dd97e77b582cde3ceac30d3eace56a578fc 100644 (file)
 #include <stdexcept>
 
 #include <folly/AtomicHashArray.h>
-#include <folly/Foreach.h>
 #include <folly/Hash.h>
 #include <folly/Likely.h>
 #include <folly/ThreadCachedInt.h>
+#include <folly/container/Foreach.h>
 
 namespace folly {
 
index b80eb6b3cf5c29c1a4107a822ecf912328fbd734..2e9a7ef48cae09f63c3ac0735c316f1c64fc1bcf 100644 (file)
@@ -30,9 +30,9 @@
 
 #include <boost/regex.hpp>
 
-#include <folly/Foreach.h>
 #include <folly/MapUtil.h>
 #include <folly/String.h>
+#include <folly/container/Foreach.h>
 #include <folly/json.h>
 
 using namespace std;
diff --git a/folly/Enumerate.h b/folly/Enumerate.h
deleted file mode 100644 (file)
index 94bcece..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <iterator>
-#include <memory>
-
-#include <folly/portability/SysTypes.h>
-
-/**
- * Similar to Python's enumerate(), folly::enumerate() can be used to
- * iterate a range with a for-range loop, and it also allows to
- * retrieve the count of iterations so far.
- *
- * For example:
- *
- * for (auto it : folly::enumerate(vec)) {
- *   // *it is a reference to the current element. Const if vec is const.
- *   // it->member can be used as well.
- *   // it.index contains the iteration count.
- * }
- *
- * If the iteration variable is const, the reference is too.
- *
- * for (const auto it : folly::enumerate(vec)) {
- *   // *it is always a const reference.
- * }
- *
- * @author Giuseppe Ottaviano <ott@fb.com>
- */
-
-namespace folly {
-
-namespace detail {
-
-template <class T>
-struct MakeConst {
-  using type = const T;
-};
-template <class T>
-struct MakeConst<T&> {
-  using type = const T&;
-};
-template <class T>
-struct MakeConst<T*> {
-  using type = const T*;
-};
-
-// Raw pointers don't have an operator->() member function, so the
-// second overload will be SFINAEd out in that case. Otherwise, the
-// second is preferred in the partial order for getPointer(_, 0).
-template <class Iterator>
-auto getPointer(const Iterator& it, long) -> decltype(std::addressof(*it)) {
-  return std::addressof(*it);
-}
-template <class Iterator>
-auto getPointer(const Iterator& it, int) -> decltype(it.operator->()) {
-  return it.operator->();
-}
-
-template <class Iterator>
-class Enumerator {
- public:
-  explicit Enumerator(Iterator it) : it_(std::move(it)) {}
-
-  class Proxy {
-   public:
-    using difference_type = ssize_t;
-    using value_type = typename std::iterator_traits<Iterator>::value_type;
-    using reference = typename std::iterator_traits<Iterator>::reference;
-    using pointer = typename std::iterator_traits<Iterator>::pointer;
-    using iterator_category = std::input_iterator_tag;
-
-    explicit Proxy(const Enumerator* e) : it_(e->it_), index(e->idx_) {}
-
-    // Non-const Proxy: Forward constness from Iterator.
-    reference operator*() {
-      return *it_;
-    }
-    pointer operator->() {
-      return getPointer(it_, 0);
-    }
-
-    // Const Proxy: Force const references.
-    typename MakeConst<reference>::type operator*() const {
-      return *it_;
-    }
-    typename MakeConst<pointer>::type operator->() const {
-      return getPointer(it_, 0);
-    }
-
-   private:
-    const Iterator& it_;
-
-   public:
-    const size_t index;
-  };
-
-  Proxy operator*() const {
-    return Proxy(this);
-  }
-
-  Enumerator& operator++() {
-    ++it_;
-    ++idx_;
-    return *this;
-  }
-
-  template <typename OtherIterator>
-  bool operator==(const Enumerator<OtherIterator>& rhs) {
-    return it_ == rhs.it_;
-  }
-
-  template <typename OtherIterator>
-  bool operator!=(const Enumerator<OtherIterator>& rhs) {
-    return !(*this == rhs);
-  }
-
- private:
-  template <typename OtherIterator>
-  friend class Enumerator;
-
-  Iterator it_;
-  size_t idx_ = 0;
-};
-
-template <class Range>
-class RangeEnumerator {
-  Range r_;
-  using BeginIteratorType = decltype(std::declval<Range>().begin());
-  using EndIteratorType = decltype(std::declval<Range>().end());
-
- public:
-  explicit RangeEnumerator(Range&& r) : r_(std::forward<Range>(r)) {}
-
-  Enumerator<BeginIteratorType> begin() {
-    return Enumerator<BeginIteratorType>(r_.begin());
-  }
-  Enumerator<EndIteratorType> end() {
-    return Enumerator<EndIteratorType>(r_.end());
-  }
-};
-
-} // namespace detail
-
-template <class Range>
-detail::RangeEnumerator<Range> enumerate(Range&& r) {
-  return detail::RangeEnumerator<Range>(std::forward<Range>(r));
-}
-
-} // namespace folly
diff --git a/folly/EvictingCacheMap.h b/folly/EvictingCacheMap.h
deleted file mode 100644 (file)
index e7e8149..0000000
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <algorithm>
-#include <exception>
-#include <functional>
-
-#include <boost/intrusive/list.hpp>
-#include <boost/intrusive/unordered_set.hpp>
-#include <boost/iterator/iterator_adaptor.hpp>
-#include <boost/utility.hpp>
-
-#include <folly/portability/BitsFunctexcept.h>
-
-namespace folly {
-
-/**
- * A general purpose LRU evicting cache. Designed to support constant time
- * set/get operations. It maintains a doubly linked list of items that are
- * threaded through an index (a hash map). The access ordered is maintained
- * on the list by moving an element to the front of list on a get. New elements
- * are added to the front of the list. The index size is set to half the
- * capacity (setting capacity to 0 is a special case. see notes at the end of
- * this section). So assuming uniform distribution of keys, set/get are both
- * constant time operations.
- *
- * On reaching capacity limit, clearSize_ LRU items are evicted at a time. If
- * a callback is specified with setPruneHook, it is invoked for each eviction.
- *
- * This is NOT a thread-safe implementation.
- *
- * Configurability: capacity of the cache, number of items to evict, eviction
- * callback and the hasher to hash the keys can all be supplied by the caller.
- *
- * If at a given state, N1 - N6 are the nodes in MRU to LRU order and hashing
- * to index keys as {(N1,N5)->H1, (N4,N5,N5)->H2, N3->Hi}, the datastructure
- * layout is as below. N1 .. N6 is a list threaded through the hash.
- * Assuming, each the number of nodes hashed to each index key is bounded, the
- * following operations run in constant time.
- * i) get computes the index key, walks the list of elements hashed to
- * the key and moves it to the front of the list, if found.
- * ii) set inserts a new node into the list and places the same node on to the
- * list of elements hashing to the corresponding index key.
- * ii) prune deletes nodes from the end of the list as well from the index.
- *
- * +----+     +----+     +----+
- * | H1 | <-> | N1 | <-> | N5 |
- * +----+     +----+     +----+
- *              ^        ^  ^
- *              |    ___/    \
- *              |   /         \
- *              |_ /________   \___
- *                /        |       \
- *               /         |        \
- *              v          v         v
- * +----+     +----+     +----+     +----+
- * | H2 | <-> | N4 | <-> | N2 | <-> | N6 |
- * +----+     +----+     +----+     +----+
- *   .          ^          ^
- *   .          |          |
- *   .          |          |
- *   .          |     _____|
- *   .          |    /
- *              v   v
- * +----+     +----+
- * | Hi | <-> | N3 |
- * +----+     +----+
- *
- * N.B 1 : Changing the capacity with setMaxSize does not change the index size
- * and it could end up in too many elements indexed to the same slot in index.
- * The set/get performance will get worse in this case. So it is best to avoid
- * resizing.
- *
- * N.B 2 : Setting capacity to 0, using setMaxSize or initialization, turns off
- * evictions based on sizeof the cache making it an INFINITE size cache
- * unless evictions of LRU items are triggered by calling prune() by clients
- * (using their own eviction criteria).
- */
-template <class TKey, class TValue, class THash = std::hash<TKey>>
-class EvictingCacheMap {
- private:
-  // typedefs for brevity
-  struct Node;
-  typedef boost::intrusive::link_mode<boost::intrusive::safe_link> link_mode;
-  typedef boost::intrusive::unordered_set<Node> NodeMap;
-  typedef boost::intrusive::list<Node> NodeList;
-  typedef std::pair<const TKey, TValue> TPair;
-
- public:
-  typedef std::function<void(TKey, TValue&&)> PruneHookCall;
-
-  // iterator base : returns TPair on dereference
-  template <typename Value, typename TIterator>
-  class iterator_base
-    : public boost::iterator_adaptor<iterator_base<Value, TIterator>,
-                                    TIterator,
-                                    Value,
-                                    boost::bidirectional_traversal_tag > {
-   public:
-    iterator_base() {
-    }
-    explicit iterator_base(TIterator it)
-        : iterator_base::iterator_adaptor_(it) {
-    }
-    Value& dereference() const {
-      return this->base_reference()->pr;
-    }
-  };
-
-  // iterators
-  typedef iterator_base<
-    TPair, typename NodeList::iterator> iterator;
-  typedef iterator_base<
-    const TPair, typename NodeList::const_iterator> const_iterator;
-  typedef iterator_base<
-    TPair, typename NodeList::reverse_iterator> reverse_iterator;
-  typedef iterator_base<
-    const TPair,
-    typename NodeList::const_reverse_iterator> const_reverse_iterator;
-
-  /**
-   * Construct a EvictingCacheMap
-   * @param maxSize maximum size of the cache map.  Once the map size exceeds
-   *     maxSize, the map will begin to evict.
-   * @param clearSize the number of elements to clear at a time when the
-   *     eviction size is reached.
-   */
-  explicit EvictingCacheMap(std::size_t maxSize, std::size_t clearSize = 1)
-      : nIndexBuckets_(std::max(maxSize / 2, std::size_t(kMinNumIndexBuckets))),
-        indexBuckets_(new typename NodeMap::bucket_type[nIndexBuckets_]),
-        indexTraits_(indexBuckets_.get(), nIndexBuckets_),
-        index_(indexTraits_),
-        maxSize_(maxSize),
-        clearSize_(clearSize) { }
-
-  EvictingCacheMap(const EvictingCacheMap&) = delete;
-  EvictingCacheMap& operator=(const EvictingCacheMap&) = delete;
-  EvictingCacheMap(EvictingCacheMap&&) = default;
-  EvictingCacheMap& operator=(EvictingCacheMap&&) = default;
-
-  ~EvictingCacheMap() {
-    setPruneHook(nullptr);
-    // ignore any potential exceptions from pruneHook_
-    pruneWithFailSafeOption(size(), nullptr, true);
-  }
-
-  /**
-   * Adjust the max size of EvictingCacheMap. Note that this does not update
-   * nIndexBuckets_ accordingly. This API can cause performance to get very
-   * bad, e.g., the nIndexBuckets_ is still 100 after maxSize is updated to 1M.
-   *
-   * Calling this function with an arugment of 0 removes the limit on the cache
-   * size and elements are not evicted unless clients explictly call prune.
-   *
-   * If you intend to resize dynamically using this, then picking an index size
-   * that works well and initializing with corresponding maxSize is the only
-   * reasonable option.
-   *
-   * @param maxSize new maximum size of the cache map.
-   * @param pruneHook callback to use on eviction.
-   */
-  void setMaxSize(size_t maxSize, PruneHookCall pruneHook = nullptr) {
-    if (maxSize != 0 && maxSize < size()) {
-      // Prune the excess elements with our new constraints.
-      prune(std::max(size() - maxSize, clearSize_), pruneHook);
-    }
-    maxSize_ = maxSize;
-  }
-
-  size_t getMaxSize() const {
-    return maxSize_;
-  }
-
-  void setClearSize(size_t clearSize) {
-    clearSize_ = clearSize;
-  }
-
-  /**
-   * Check for existence of a specific key in the map.  This operation has
-   *     no effect on LRU order.
-   * @param key key to search for
-   * @return true if exists, false otherwise
-   */
-  bool exists(const TKey& key) const  {
-    return findInIndex(key) != index_.end();
-  }
-
-  /**
-   * Get the value associated with a specific key.  This function always
-   *     promotes a found value to the head of the LRU.
-   * @param key key associated with the value
-   * @return the value if it exists
-   * @throw std::out_of_range exception of the key does not exist
-   */
-  TValue& get(const TKey& key) {
-    auto it = find(key);
-    if (it == end()) {
-      std::__throw_out_of_range("Key does not exist");
-    }
-    return it->second;
-  }
-
-  /**
-   * Get the iterator associated with a specific key.  This function always
-   *     promotes a found value to the head of the LRU.
-   * @param key key to associate with value
-   * @return the iterator of the object (a std::pair of const TKey, TValue) or
-   *     end() if it does not exist
-   */
-  iterator find(const TKey& key) {
-    auto it = findInIndex(key);
-    if (it == index_.end()) {
-      return end();
-    }
-    lru_.erase(lru_.iterator_to(*it));
-    lru_.push_front(*it);
-    return iterator(lru_.iterator_to(*it));
-  }
-
-  /**
-   * Get the value associated with a specific key.  This function never
-   *     promotes a found value to the head of the LRU.
-   * @param key key associated with the value
-   * @return the value if it exists
-   * @throw std::out_of_range exception of the key does not exist
-   */
-  const TValue& getWithoutPromotion(const TKey& key) const {
-    auto it = findWithoutPromotion(key);
-    if (it == end()) {
-      std::__throw_out_of_range("Key does not exist");
-    }
-    return it->second;
-  }
-
-  TValue& getWithoutPromotion(const TKey& key) {
-    auto const& cThis = *this;
-    return const_cast<TValue&>(cThis.getWithoutPromotion(key));
-  }
-
-  /**
-   * Get the iterator associated with a specific key.  This function never
-   *     promotes a found value to the head of the LRU.
-   * @param key key to associate with value
-   * @return the iterator of the object (a std::pair of const TKey, TValue) or
-   *     end() if it does not exist
-   */
-  const_iterator findWithoutPromotion(const TKey& key) const {
-    auto it = findInIndex(key);
-    return (it == index_.end()) ? end() : const_iterator(lru_.iterator_to(*it));
-  }
-
-  iterator findWithoutPromotion(const TKey& key) {
-    auto it = findInIndex(key);
-    return (it == index_.end()) ? end() : iterator(lru_.iterator_to(*it));
-  }
-
-  /**
-   * Erase the key-value pair associated with key if it exists.
-   * @param key key associated with the value
-   * @return true if the key existed and was erased, else false
-   */
-  bool erase(const TKey& key) {
-    auto it = findInIndex(key);
-    if (it == index_.end()) {
-      return false;
-    }
-    auto node = &(*it);
-    std::unique_ptr<Node> nptr(node);
-    lru_.erase(lru_.iterator_to(*node));
-    index_.erase(it);
-    return true;
-  }
-
-  /**
-   * Set a key-value pair in the dictionary
-   * @param key key to associate with value
-   * @param value value to associate with the key
-   * @param promote boolean flag indicating whether or not to move something
-   *     to the front of an LRU.  This only really matters if you're setting
-   *     a value that already exists.
-   * @param pruneHook callback to use on eviction (if it occurs).
-   */
-  void set(const TKey& key,
-           TValue value,
-           bool promote = true,
-           PruneHookCall pruneHook = nullptr) {
-    auto it = findInIndex(key);
-    if (it != index_.end()) {
-      it->pr.second = std::move(value);
-      if (promote) {
-        lru_.erase(lru_.iterator_to(*it));
-        lru_.push_front(*it);
-      }
-    } else {
-      auto node = new Node(key, std::move(value));
-      index_.insert(*node);
-      lru_.push_front(*node);
-
-      // no evictions if maxSize_ is 0 i.e. unlimited capacity
-      if (maxSize_ > 0 && size() > maxSize_) {
-        prune(clearSize_, pruneHook);
-      }
-    }
-  }
-
-  /**
-   * Get the number of elements in the dictionary
-   * @return the size of the dictionary
-   */
-  std::size_t size() const {
-    return index_.size();
-  }
-
-  /**
-   * Typical empty function
-   * @return true if empty, false otherwise
-   */
-  bool empty() const {
-    return index_.empty();
-  }
-
-  void clear(PruneHookCall pruneHook = nullptr) {
-    prune(size(), pruneHook);
-  }
-
-  /**
-   * Set the prune hook, which is the function invoked on the key and value
-   *     on each eviction.  Will throw If the pruneHook throws, unless the
-   *     EvictingCacheMap object is being destroyed in which case it will
-   *     be ignored.
-   * @param pruneHook new callback to use on eviction.
-   * @param promote boolean flag indicating whether or not to move something
-   *     to the front of an LRU.
-   * @return the iterator of the object (a std::pair of const TKey, TValue) or
-   *     end() if it does not exist
-   */
-  void setPruneHook(PruneHookCall pruneHook) {
-    pruneHook_ = pruneHook;
-  }
-
-
-  /**
-   * Prune the minimum of pruneSize and size() from the back of the LRU.
-   * Will throw if pruneHook throws.
-   * @param pruneSize minimum number of elements to prune
-   * @param pruneHook a custom pruneHook function
-   */
-  void prune(std::size_t pruneSize, PruneHookCall pruneHook = nullptr) {
-    // do not swallow exceptions for prunes not triggered from destructor
-    pruneWithFailSafeOption(pruneSize, pruneHook, false);
-  }
-
-  // Iterators and such
-  iterator begin() {
-    return iterator(lru_.begin());
-  }
-  iterator end() {
-    return iterator(lru_.end());
-  }
-  const_iterator begin() const {
-    return const_iterator(lru_.begin());
-  }
-  const_iterator end() const {
-    return const_iterator(lru_.end());
-  }
-
-  const_iterator cbegin() const {
-    return const_iterator(lru_.cbegin());
-  }
-  const_iterator cend() const {
-    return const_iterator(lru_.cend());
-  }
-
-  reverse_iterator rbegin() {
-    return reverse_iterator(lru_.rbegin());
-  }
-  reverse_iterator rend() {
-    return reverse_iterator(lru_.rend());
-  }
-
-  const_reverse_iterator rbegin() const {
-    return const_reverse_iterator(lru_.rbegin());
-  }
-  const_reverse_iterator rend() const {
-    return const_reverse_iterator(lru_.rend());
-  }
-
-  const_reverse_iterator crbegin() const {
-    return const_reverse_iterator(lru_.crbegin());
-  }
-  const_reverse_iterator crend() const {
-    return const_reverse_iterator(lru_.crend());
-  }
-
- private:
-  struct Node
-    : public boost::intrusive::unordered_set_base_hook<link_mode>,
-      public boost::intrusive::list_base_hook<link_mode> {
-    Node(const TKey& key, TValue&& value)
-        : pr(std::make_pair(key, std::move(value))) {
-    }
-    TPair pr;
-    friend bool operator==(const Node& lhs, const Node& rhs) {
-      return lhs.pr.first == rhs.pr.first;
-    }
-    friend std::size_t hash_value(const Node& node) {
-      return THash()(node.pr.first);
-    }
-  };
-
-  struct KeyHasher {
-    std::size_t operator()(const Node& node) {
-      return THash()(node.pr.first);
-    }
-    std::size_t operator()(const TKey& key) {
-      return THash()(key);
-    }
-  };
-
-  struct KeyValueEqual {
-    bool operator()(const TKey& lhs, const Node& rhs) {
-      return lhs == rhs.pr.first;
-    }
-    bool operator()(const Node& lhs, const TKey& rhs) {
-      return lhs.pr.first == rhs;
-    }
-  };
-
-  /**
-   * Get the iterator in in the index associated with a specific key. This is
-   * merely a search in the index and does not promote the object.
-   * @param key key to associate with value
-   * @return the NodeMap::iterator to the Node containing the object
-   *    (a std::pair of const TKey, TValue) or index_.end() if it does not exist
-   */
-  typename NodeMap::iterator findInIndex(const TKey& key) {
-    return index_.find(key, KeyHasher(), KeyValueEqual());
-  }
-
-  typename NodeMap::const_iterator findInIndex(const TKey& key) const {
-    return index_.find(key, KeyHasher(), KeyValueEqual());
-  }
-
-  /**
-   * Prune the minimum of pruneSize and size() from the back of the LRU.
-   * @param pruneSize minimum number of elements to prune
-   * @param pruneHook a custom pruneHook function
-   * @param failSafe true if exceptions are to ignored, false by default
-   */
-  void pruneWithFailSafeOption(std::size_t pruneSize,
-    PruneHookCall pruneHook, bool failSafe) {
-    auto& ph = (nullptr == pruneHook) ? pruneHook_ : pruneHook;
-
-    for (std::size_t i = 0; i < pruneSize && !lru_.empty(); i++) {
-      auto *node = &(*lru_.rbegin());
-      std::unique_ptr<Node> nptr(node);
-
-      lru_.erase(lru_.iterator_to(*node));
-      index_.erase(index_.iterator_to(*node));
-      if (ph) {
-        try {
-          ph(node->pr.first, std::move(node->pr.second));
-        } catch (...) {
-          if (!failSafe) {
-            throw;
-          }
-        }
-      }
-    }
-  }
-
-  static const std::size_t kMinNumIndexBuckets = 100;
-  PruneHookCall pruneHook_;
-  std::size_t nIndexBuckets_;
-  std::unique_ptr<typename NodeMap::bucket_type[]> indexBuckets_;
-  typename NodeMap::bucket_traits indexTraits_;
-  NodeMap index_;
-  NodeList lru_;
-  std::size_t maxSize_;
-  std::size_t clearSize_;
-};
-
-} // namespace folly
diff --git a/folly/Foreach-inl.h b/folly/Foreach-inl.h
deleted file mode 100644 (file)
index c037758..0000000
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright 2017-present Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cassert>
-#include <cstdint>
-#include <initializer_list>
-#include <iterator>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include <folly/Portability.h>
-#include <folly/Traits.h>
-
-namespace folly {
-
-namespace for_each_detail {
-
-namespace adl {
-
-/* using override */
-using std::begin;
-/* using override */
-using std::end;
-/* using override */
-using std::get;
-
-/**
- * The adl_ functions below lookup the function name in the namespace of the
- * type of the object being passed into the function.  If no function with
- * that name exists for the passed object then the default std:: versions are
- * going to be called
- */
-template <std::size_t Index, typename Type>
-auto adl_get(Type&& instance) -> decltype(get<Index>(std::declval<Type>())) {
-  return get<Index>(std::forward<Type>(instance));
-}
-template <typename Type>
-auto adl_begin(Type&& instance) -> decltype(begin(instance)) {
-  return begin(instance);
-}
-template <typename Type>
-auto adl_end(Type&& instance) -> decltype(end(instance)) {
-  return end(instance);
-}
-
-} // namespace adl
-
-/**
- * Enable if the range supports fetching via non member get<>()
- */
-template <typename T>
-using EnableIfNonMemberGetFound =
-    void_t<decltype(adl::adl_get<0>(std::declval<T>()))>;
-/**
- * Enable if the range supports fetching via a member get<>()
- */
-template <typename T>
-using EnableIfMemberGetFound =
-    void_t<decltype(std::declval<T>().template get<0>())>;
-
-/**
- * A get that tries ADL get<> first and if that is not found tries to execute
- * a member function get<> on the instance, just as proposed by the structured
- * bindings proposal here 11.5.3
- * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf
- */
-template <std::size_t Index, typename Type, typename = void>
-struct Get {
-  template <typename T>
-  static auto impl(T&& instance)
-      -> decltype(adl::adl_get<Index>(std::declval<T>())) {
-    return adl::adl_get<Index>(std::forward<T>(instance));
-  }
-};
-template <std::size_t Index, typename Type>
-struct Get<Index, Type, EnableIfMemberGetFound<Type>> {
-  template <typename T>
-  static auto impl(T&& instance)
-      -> decltype(std::declval<T>().template get<Index>()) {
-    return std::forward<T>(instance).template get<Index>();
-  }
-};
-
-/**
- * Concepts-ish
- */
-/**
- * Check if the range is a tuple or a range
- */
-template <typename Type, typename T = typename std::decay<Type>::type>
-using EnableIfTuple = void_t<
-    decltype(Get<0, T>::impl(std::declval<T>())),
-    decltype(std::tuple_size<T>::value)>;
-
-/**
- * Check if the range is a range
- */
-template <typename Type, typename T = typename std::decay<Type>::type>
-using EnableIfRange = void_t<
-    decltype(adl::adl_begin(std::declval<T>())),
-    decltype(adl::adl_end(std::declval<T>()))>;
-
-/**
- * Forwards the return value of the first element of the range, used to
- * determine the type of the first element in the range in SFINAE use cases
- */
-template <typename Sequence, typename = void>
-struct DeclvalSequence {
-  using type = decltype(*(adl::adl_begin(std::declval<Sequence>())));
-};
-
-template <typename Sequence>
-struct DeclvalSequence<Sequence, EnableIfTuple<Sequence>> {
-  using type = decltype(Get<0, Sequence>::impl(std::declval<Sequence>()));
-};
-
-/**
- * Check if the functor accepts one or two arguments, one of the first element
- * in the range, assuming that all the other elements can also be passed to the
- * functor, and the second being an instantiation of std::integral_constant,
- * and the third being an instantiation of LoopControl, to provide
- * breakability to the loop
- */
-template <typename Sequence, typename Func>
-using EnableIfAcceptsOneArgument = void_t<decltype(std::declval<Func>()(
-    std::declval<typename DeclvalSequence<Sequence>::type>()))>;
-template <typename Sequence, typename Func>
-using EnableIfAcceptsTwoArguments = void_t<decltype(std::declval<Func>()(
-    std::declval<typename DeclvalSequence<Sequence>::type>(),
-    std::integral_constant<std::size_t, 0>{}))>;
-template <typename Sequence, typename Func>
-using EnableIfAcceptsThreeArguments = void_t<decltype(std::declval<Func>()(
-    std::declval<typename DeclvalSequence<Sequence>::type>(),
-    std::integral_constant<std::size_t, 0>{},
-    adl::adl_begin(std::declval<Sequence>())))>;
-template <typename Sequence, typename Func>
-using EnableIfBreaksRange = std::enable_if_t<std::is_same<
-    typename std::decay<decltype(std::declval<Func>()(
-        std::declval<typename DeclvalSequence<Sequence>::type>(),
-        std::size_t{0},
-        adl::adl_begin(std::declval<Sequence>())))>::type,
-    LoopControl>::value>;
-template <typename Sequence, typename Func>
-using EnableIfBreaksTuple = std::enable_if_t<std::is_same<
-    typename std::decay<decltype(std::declval<Func>()(
-        std::declval<typename DeclvalSequence<Sequence>::type>(),
-        std::integral_constant<std::size_t, 0>{}))>::type,
-    LoopControl>::value>;
-/**
- * Enables if the sequence has random access iterators
- */
-template <typename Sequence>
-using EnableIfRandomAccessIterators = std::enable_if_t<std::is_same<
-    typename std::iterator_traits<typename std::decay<decltype(
-        adl::adl_begin(std::declval<Sequence>()))>::type>::iterator_category,
-    std::random_access_iterator_tag>::value>;
-template <typename Sequence, typename Index>
-using EnableIfHasIndexingOperator =
-    void_t<decltype(std::declval<Sequence>()[std::declval<Index>()])>;
-
-/**
- * Implementation for the range iteration, this provides specializations in
- * the case where the function returns a break or continue.
- */
-template <typename Seq, typename F, typename = void>
-struct ForEachRange {
-  template <typename Sequence, typename Func>
-  static void impl(Sequence&& range, Func& func) {
-    auto first = adl::adl_begin(range);
-    auto last = adl::adl_end(range);
-    for (auto index = std::size_t{0}; first != last; ++index) {
-      auto next = std::next(first);
-      func(*first, index, first);
-      first = next;
-    }
-  }
-};
-
-template <typename Seq, typename F>
-struct ForEachRange<Seq, F, EnableIfBreaksRange<Seq, F>> {
-  template <typename Sequence, typename Func>
-  static void impl(Sequence&& range, Func& func) {
-    auto first = adl::adl_begin(range);
-    auto last = adl::adl_end(range);
-    for (auto index = std::size_t{0}; first != last; ++index) {
-      auto next = std::next(first);
-      if (loop_break == func(*first, index, first)) {
-        break;
-      }
-      first = next;
-    }
-  }
-};
-
-/**
- * Implementations for the runtime function
- */
-template <
-    typename Sequence,
-    typename Func,
-    EnableIfAcceptsThreeArguments<Sequence, Func>* = nullptr>
-void for_each_range_impl(Sequence&& range, Func& func) {
-  ForEachRange<Sequence, Func>::impl(std::forward<Sequence>(range), func);
-}
-template <
-    typename Sequence,
-    typename Func,
-    EnableIfAcceptsTwoArguments<Sequence, Func>* = nullptr>
-void for_each_range_impl(Sequence&& range, Func& func) {
-  // make a three arg adaptor for the function passed in so that the main
-  // implementation function can be used
-  auto three_arg_adaptor = [&func](
-                               auto&& ele, auto index, auto) -> decltype(auto) {
-    return func(std::forward<decltype(ele)>(ele), index);
-  };
-  for_each_range_impl(std::forward<Sequence>(range), three_arg_adaptor);
-}
-
-template <
-    typename Sequence,
-    typename Func,
-    EnableIfAcceptsOneArgument<Sequence, Func>* = nullptr>
-void for_each_range_impl(Sequence&& range, Func& func) {
-  // make a three argument adaptor for the function passed in that just ignores
-  // the second and third argument
-  auto three_arg_adaptor = [&func](auto&& ele, auto, auto) -> decltype(auto) {
-    return func(std::forward<decltype(ele)>(ele));
-  };
-  for_each_range_impl(std::forward<Sequence>(range), three_arg_adaptor);
-}
-
-/**
- * Handlers for iteration
- */
-/**
- * The class provides a way to tell whether the function passed in to the
- * algorithm returns an instance of LoopControl, if it does then the break-able
- * implementation will be used.  If the function provided to the algorithm
- * does not use the break API, then the basic no break, 0 overhead
- * implementation will be used
- */
-template <typename Seq, typename F, typename = void>
-struct ForEachTupleImpl {
-  template <typename Sequence, typename Func, std::size_t... Indices>
-  static void
-  impl(Sequence&& seq, Func& func, std::index_sequence<Indices...>) {
-    // unroll the loop in an initializer list construction parameter expansion
-    // pack
-    static_cast<void>(std::initializer_list<int>{
-        (func(
-             Get<Indices, Sequence>::impl(std::forward<Sequence>(seq)),
-             std::integral_constant<std::size_t, Indices>{}),
-         0)...});
-  }
-};
-template <typename Seq, typename F>
-struct ForEachTupleImpl<Seq, F, EnableIfBreaksTuple<Seq, F>> {
-  template <typename Sequence, typename Func, std::size_t... Indices>
-  static void
-  impl(Sequence&& seq, Func& func, std::index_sequence<Indices...>) {
-    // unroll the loop in an initializer list construction parameter expansion
-    // pack
-    LoopControl break_or_not = LoopControl::CONTINUE;
-
-    // cast to void to ignore the result, use the initialzer list constructor
-    // to do the loop execution, the ternary conditional will decide whether
-    // or not to evaluate the result
-    static_cast<void>(std::initializer_list<int>{
-        (((break_or_not == loop_continue)
-              ? (break_or_not = func(
-                     Get<Indices, Sequence>::impl(std::forward<Sequence>(seq)),
-                     std::integral_constant<std::size_t, Indices>{}))
-              : (loop_continue)),
-         0)...});
-  }
-};
-
-/**
- * The two top level compile time loop iteration functions handle the dispatch
- * based on the number of arguments the passed in function can be passed, if 2
- * arguments can be passed then the implementation dispatches work further to
- * the implementation classes above.  If not then an adaptor is constructed
- * which is passed on to the 2 argument specialization, which then in turn
- * forwards implementation to the implementation classes above
- */
-template <
-    typename Sequence,
-    typename Func,
-    EnableIfAcceptsTwoArguments<Sequence, Func>* = nullptr>
-void for_each_tuple_impl(Sequence&& seq, Func& func) {
-  // pass the length as an index sequence to the implementation as an
-  // optimization over manual template "tail recursion" unrolling
-  constexpr auto length =
-      std::tuple_size<typename std::decay<Sequence>::type>::value;
-  ForEachTupleImpl<Sequence, Func>::impl(
-      std::forward<Sequence>(seq), func, std::make_index_sequence<length>{});
-}
-template <
-    typename Sequence,
-    typename Func,
-    EnableIfAcceptsOneArgument<Sequence, Func>* = nullptr>
-void for_each_tuple_impl(Sequence&& seq, Func& func) {
-  // make an adaptor for the function passed in, in case it can only be passed
-  // on argument
-  auto two_arg_adaptor = [&func](auto&& ele, auto) -> decltype(auto) {
-    return func(std::forward<decltype(ele)>(ele));
-  };
-  for_each_tuple_impl(std::forward<Sequence>(seq), two_arg_adaptor);
-}
-
-/**
- * Top level handlers for the for_each loop, the basic specialization handles
- * ranges and the specialized version handles compile time ranges (tuple like)
- *
- * This implies that if a range is a compile time range, its compile time
- * get<> API (whether through a member function or through a ADL looked up
- * method) will be used in preference over iterators
- */
-template <typename R, typename = void>
-struct ForEachImpl {
-  template <typename Sequence, typename Func>
-  static void impl(Sequence&& range, Func& func) {
-    for_each_tuple_impl(std::forward<Sequence>(range), func);
-  }
-};
-template <typename R>
-struct ForEachImpl<R, EnableIfRange<R>> {
-  template <typename Sequence, typename Func>
-  static void impl(Sequence&& range, Func& func) {
-    for_each_range_impl(std::forward<Sequence>(range), func);
-  }
-};
-
-template <typename S, typename I, typename = void>
-struct FetchIteratorIndexImpl {
-  template <typename Sequence, typename Index>
-  static decltype(auto) impl(Sequence&& sequence, Index&& index) {
-    return std::forward<Sequence>(sequence)[std::forward<Index>(index)];
-  }
-};
-template <typename S, typename I>
-struct FetchIteratorIndexImpl<S, I, EnableIfRandomAccessIterators<S>> {
-  template <typename Sequence, typename Index>
-  static decltype(auto) impl(Sequence&& sequence, Index index) {
-    return *(adl::adl_begin(std::forward<Sequence>(sequence)) + index);
-  }
-};
-template <typename S, typename = void>
-struct FetchImpl {
-  template <typename Sequence, typename Index>
-  static decltype(auto) impl(Sequence&& sequence, Index index) {
-    return Get<static_cast<std::size_t>(index), Sequence>::impl(
-        std::forward<Sequence>(sequence));
-  }
-};
-template <typename S>
-struct FetchImpl<S, EnableIfRange<S>> {
-  template <typename Sequence, typename Index>
-  static decltype(auto) impl(Sequence&& sequence, Index&& index) {
-    return FetchIteratorIndexImpl<Sequence, Index>::impl(
-        std::forward<Sequence>(sequence), std::forward<Index>(index));
-  }
-};
-
-} // namespace for_each_detail
-
-template <typename Sequence, typename Func>
-FOLLY_CPP14_CONSTEXPR Func for_each(Sequence&& range, Func func) {
-  for_each_detail::ForEachImpl<typename std::decay<Sequence>::type>::impl(
-      std::forward<Sequence>(range), func);
-  return func;
-}
-
-template <typename Sequence, typename Index>
-FOLLY_CPP14_CONSTEXPR decltype(auto) fetch(Sequence&& sequence, Index&& index) {
-  return for_each_detail::FetchImpl<Sequence>::impl(
-      std::forward<Sequence>(sequence), std::forward<Index>(index));
-}
-
-} // namespace folly
diff --git a/folly/Foreach.h b/folly/Foreach.h
deleted file mode 100644 (file)
index ae0e865..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <folly/Portability.h>
-#include <folly/Preprocessor.h>
-
-#include <type_traits>
-
-namespace folly {
-
-/**
- * @function for_each
- *
- * folly::for_each is a generalized iteration algorithm.  Example:
- *
- *  auto one = std::make_tuple(1, 2, 3);
- *  auto two = std::vector<int>{1, 2, 3};
- *  auto func = [](auto element, auto index) {
- *    cout << index << " : " << element << endl;
- *  };
- *  folly::for_each(one, func);
- *  folly::for_each(two, func);
- *
- * The for_each function allows iteration through sequences, these
- * can either be runtime sequences (i.e. entities for which std::begin and
- * std::end work) or compile time sequences (as deemed by the presence of
- * std::tuple_length<>, get<> (ADL resolved) functions)
- *
- * The function is made to provide a convenient library based alternative to
- * the proposal p0589r0, which aims to generalize the range based for loop
- * even further to work with compile time sequences.
- *
- * A drawback of using range based for loops is that sometimes you do not have
- * access to the index within the range.  This provides easy access to that,
- * even with compile time sequences.
- *
- * And breaking out is easy
- *
- *  auto range_one = std::vector<int>{1, 2, 3};
- *  auto range_two = std::make_tuple(1, 2, 3);
- *  auto func = [](auto ele, auto index) {
- *    cout << "Element at index " << index << " : " << ele;
- *    if (index == 1) {
- *      return folly::loop_break;
- *    }
- *    return folly::loop_continue;
- *  };
- *  folly_for_each(range_one, func);
- *  folly_for_each(range_two, func);
- *
- * A simple use case would be when using futures, if the user was doing calls
- * to n servers then they would accept the callback with the futures like this
- *
- *  auto vec = std::vector<std::future<int>>{request_one(), ...};
- *  when_all(vec.begin(), vec.end()).then([](auto futures) {
- *    folly::for_each(futures, [](auto& fut) { ... });
- *  });
- *
- * Now when this code switches to use tuples instead of the runtime
- * std::vector, then the loop does not need to change, the code will still
- * work just fine
- *
- *  when_all(future_one, future_two, future_three).then([](auto futures) {
- *    folly::for_each(futures, [](auto& fut) { ... });
- *  });
- */
-template <typename Range, typename Func>
-FOLLY_CPP14_CONSTEXPR Func for_each(Range&& range, Func func);
-
-/**
- * The user should return loop_break and loop_continue if they want to iterate
- * in such a way that they can preemptively stop the loop and break out when
- * certain conditions are met
- */
-namespace for_each_detail {
-enum class LoopControl : bool { BREAK, CONTINUE };
-} // namespace for_each_detail
-
-constexpr auto loop_break = for_each_detail::LoopControl::BREAK;
-constexpr auto loop_continue = for_each_detail::LoopControl::CONTINUE;
-
-/**
- * Utility method to help access elements of a sequence with one uniform
- * interface
- *
- * This can be useful for example when you are looping through a sequence and
- * want to modify another sequence based on the information in the current
- * sequence
- *
- *  auto range_one = std::make_tuple(1, 2, 3);
- *  auto range_two = std::make_tuple(4, 5, 6);
- *  folly::for_each(range_one, [&range_two](auto ele, auto index) {
- *    folly::fetch(range_two, index) = ele;
- *  });
- *
- * For non-tuple like ranges, this works by first trying to use the iterator
- * class if the iterator has been marked to be a random access iterator.  This
- * should be inspectable via the std::iterator_traits traits class.  If the
- * iterator class is not present or is not a random access iterator then the
- * implementation falls back to trying to use the indexing operator
- * (operator[]) to fetch the required element
- */
-template <typename Sequence, typename Index>
-FOLLY_CPP14_CONSTEXPR decltype(auto) fetch(Sequence&& sequence, Index&& index);
-
-} // namespace folly
-
-/**
- * Everything below this is deprecated.  Use the folly::for_each algorithm above
- * instead
- */
-/*
- * Form a local variable name from "FOR_EACH_" x __LINE__, so that
- * FOR_EACH can be nested without creating shadowed declarations.
- */
-#define _FE_ANON(x) FB_CONCATENATE(FOR_EACH_, FB_CONCATENATE(x, __LINE__))
-
-/*
- * If you just want the element values, please use:
- *
- *    for (auto&& element : collection)
- *
- * If you need access to the iterators please write an explicit iterator loop
- */
-#define FOR_EACH(i, c)                                  \
-  if (bool _FE_ANON(s1_) = false) {} else               \
-    for (auto && _FE_ANON(s2_) = (c);                   \
-         !_FE_ANON(s1_); _FE_ANON(s1_) = true)          \
-      for (auto i = _FE_ANON(s2_).begin();              \
-           i != _FE_ANON(s2_).end(); ++i)
-
-/*
- * If you just want the element values, please use this (ranges-v3) construct:
- *
- *    for (auto&& element : collection | view::reverse)
- *
- * If you need access to the iterators please write an explicit iterator loop
- */
-#define FOR_EACH_R(i, c)                                \
-  if (bool _FE_ANON(s1_) = false) {} else               \
-    for (auto && _FE_ANON(s2_) = (c);                   \
-         !_FE_ANON(s1_); _FE_ANON(s1_) = true)          \
-      for (auto i = _FE_ANON(s2_).rbegin();             \
-           i != _FE_ANON(s2_).rend(); ++i)
-
-/*
- * If you just want the element values, please use this (ranges-v3) construct:
- *
- *    for (auto&& element : collection | view::zip(view::ints))
- *
- * If you need access to the iterators please write an explicit iterator loop
- * and use a counter variable
- */
-#define FOR_EACH_ENUMERATE(count, i, c)                                \
-  if (bool _FE_ANON(s1_) = false) {} else                            \
-    for (auto && FOR_EACH_state2 = (c);                                \
-         !_FE_ANON(s1_); _FE_ANON(s1_) = true)                     \
-      if (size_t _FE_ANON(n1_) = 0) {} else                            \
-        if (const size_t& count = _FE_ANON(n1_)) {} else               \
-          for (auto i = FOR_EACH_state2.begin();                       \
-               i != FOR_EACH_state2.end(); ++_FE_ANON(n1_), ++i)
-/**
- * If you just want the keys, please use this (ranges-v3) construct:
- *
- *    for (auto&& element : collection | view::keys)
- *
- * If you just want the values, please use this (ranges-v3) construct:
- *
- *    for (auto&& element : collection | view::values)
- *
- * If you need to see both, use:
- *
- *    for (auto&& element : collection) {
- *      auto const& key = element.first;
- *      auto& value = element.second;
- *      ......
- *    }
- *
- */
-#define FOR_EACH_KV(k, v, c)                                  \
-  if (unsigned int _FE_ANON(s1_) = 0) {} else                 \
-    for (auto && _FE_ANON(s2_) = (c);                         \
-         !_FE_ANON(s1_); _FE_ANON(s1_) = 1)                   \
-      for (auto _FE_ANON(s3_) = _FE_ANON(s2_).begin();        \
-           _FE_ANON(s3_) != _FE_ANON(s2_).end();              \
-           _FE_ANON(s1_) == 2                                 \
-             ? ((_FE_ANON(s1_) = 0), ++_FE_ANON(s3_))         \
-             : (_FE_ANON(s3_) = _FE_ANON(s2_).end()))         \
-        for (auto &k = _FE_ANON(s3_)->first;                  \
-             !_FE_ANON(s1_); ++_FE_ANON(s1_))                 \
-          for (auto &v = _FE_ANON(s3_)->second;               \
-               !_FE_ANON(s1_); ++_FE_ANON(s1_))
-
-namespace folly { namespace detail {
-
-// Boost 1.48 lacks has_less, we emulate a subset of it here.
-template <typename T, typename U>
-class HasLess {
-  struct BiggerThanChar { char unused[2]; };
-  template <typename C, typename D> static char test(decltype(C() < D())*);
-  template <typename, typename> static BiggerThanChar test(...);
-
- public:
-  enum { value = sizeof(test<T, U>(nullptr)) == 1 };
-};
-
-/**
- * notThereYet helps the FOR_EACH_RANGE macro by opportunistically
- * using "<" instead of "!=" whenever available when checking for loop
- * termination. This makes e.g. examples such as FOR_EACH_RANGE (i,
- * 10, 5) execute zero iterations instead of looping virtually
- * forever. At the same time, some iterator types define "!=" but not
- * "<". The notThereYet function will dispatch differently for those.
- *
- * Below is the correct implementation of notThereYet. It is disabled
- * because of a bug in Boost 1.46: The filesystem::path::iterator
- * defines operator< (via boost::iterator_facade), but that in turn
- * uses distance_to which is undefined for that particular
- * iterator. So HasLess (defined above) identifies
- * boost::filesystem::path as properly comparable with <, but in fact
- * attempting to do so will yield a compile-time error.
- *
- * The else branch (active) contains a conservative
- * implementation.
- */
-
-#if 0
-
-template <class T, class U>
-typename std::enable_if<HasLess<T, U>::value, bool>::type
-notThereYet(T& iter, const U& end) {
-  return iter < end;
-}
-
-template <class T, class U>
-typename std::enable_if<!HasLess<T, U>::value, bool>::type
-notThereYet(T& iter, const U& end) {
-  return iter != end;
-}
-
-#else
-
-template <class T, class U>
-typename std::enable_if<
-  (std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) ||
-  (std::is_pointer<T>::value && std::is_pointer<U>::value),
-  bool>::type
-notThereYet(T& iter, const U& end) {
-  return iter < end;
-}
-
-template <class T, class U>
-typename std::enable_if<
-  !(
-    (std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) ||
-    (std::is_pointer<T>::value && std::is_pointer<U>::value)
-  ),
-  bool>::type
-notThereYet(T& iter, const U& end) {
-  return iter != end;
-}
-
-#endif
-
-
-/**
- * downTo is similar to notThereYet, but in reverse - it helps the
- * FOR_EACH_RANGE_R macro.
- */
-template <class T, class U>
-typename std::enable_if<HasLess<U, T>::value, bool>::type
-downTo(T& iter, const U& begin) {
-  return begin < iter--;
-}
-
-template <class T, class U>
-typename std::enable_if<!HasLess<U, T>::value, bool>::type
-downTo(T& iter, const U& begin) {
-  if (iter == begin) {
-    return false;
-  }
-  --iter;
-  return true;
-}
-
-} }
-
-/*
- * Look at the Ranges-v3 views and you'll probably find an easier way to build
- * the view you want but the equivalent is roughly:
- *
- *    for (auto& element : make_iterator_range(begin, end))
- */
-#define FOR_EACH_RANGE(i, begin, end)           \
-  for (auto i = (true ? (begin) : (end));       \
-       ::folly::detail::notThereYet(i, (end));  \
-       ++i)
-
-/*
- * Look at the Ranges-v3 views and you'll probably find an easier way to build
- * the view you want but the equivalent is roughly:
- *
- *    for (auto& element : make_iterator_range(begin, end) | view::reverse)
- */
-#define FOR_EACH_RANGE_R(i, begin, end) \
-  for (auto i = (false ? (begin) : (end)); ::folly::detail::downTo(i, (begin));)
-
-#include <folly/Foreach-inl.h>
diff --git a/folly/Iterator.h b/folly/Iterator.h
deleted file mode 100644 (file)
index c0b7b12..0000000
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-
-#include <folly/Functional.h>
-
-namespace folly {
-
-/**
- * Argument tuple for variadic emplace/constructor calls. Stores arguments by
- * (decayed) value. Restores original argument types with reference qualifiers
- * and adornments at unpack time to emulate perfect forwarding.
- *
- * Uses inheritance instead of a type alias to std::tuple so that emplace
- * iterators with implicit unpacking disabled can distinguish between
- * emplace_args and std::tuple parameters.
- *
- * @seealso folly::make_emplace_args
- * @seealso folly::get_emplace_arg
- */
-template <typename... Args>
-struct emplace_args : public std::tuple<std::decay_t<Args>...> {
-  using storage_type = std::tuple<std::decay_t<Args>...>;
-  using storage_type::storage_type;
-};
-
-/**
- * Pack arguments in a tuple for assignment to a folly::emplace_iterator,
- * folly::front_emplace_iterator, or folly::back_emplace_iterator. The
- * iterator's operator= will unpack the tuple and pass the unpacked arguments
- * to the container's emplace function, which in turn forwards the arguments to
- * the (multi-argument) constructor of the target class.
- *
- * Argument tuples generated with folly::make_emplace_args will be unpacked
- * before being passed to the container's emplace function, even for iterators
- * where implicit_unpack is set to false (so they will not implicitly unpack
- * std::pair or std::tuple arguments to operator=).
- *
- * Arguments are copied (lvalues) or moved (rvalues). To avoid copies and moves,
- * wrap references using std::ref(), std::cref(), and folly::rref(). Beware of
- * dangling references, especially references to temporary objects created with
- * folly::rref().
- *
- * Note that an argument pack created with folly::make_emplace_args is different
- * from an argument pack created with std::make_pair or std::make_tuple.
- * Specifically, passing a std::pair&& or std::tuple&& to an emplace iterator's
- * operator= will pass rvalue references to all fields of that tuple to the
- * container's emplace function, while passing an emplace_args&& to operator=
- * will cast those field references to the exact argument types as passed to
- * folly::make_emplace_args previously. If all arguments have been wrapped by
- * std::reference_wrappers or folly::rvalue_reference_wrappers, the result will
- * be the same as if the container's emplace function had been called directly
- * (perfect forwarding), with no temporary copies of the arguments.
- *
- * @seealso folly::rref
- *
- * @example
- *   class Widget { Widget(int, int); };
- *   std::vector<Widget> makeWidgets(const std::vector<int>& in) {
- *     std::vector<Widget> out;
- *     std::transform(
- *         in.begin(),
- *         in.end(),
- *         folly::back_emplacer(out),
- *         [](int i) { return folly::make_emplace_args(i, i); });
- *     return out;
- *   }
- */
-template <typename... Args>
-emplace_args<Args...> make_emplace_args(Args&&... args) noexcept(
-    noexcept(emplace_args<Args...>(std::forward<Args>(args)...))) {
-  return emplace_args<Args...>(std::forward<Args>(args)...);
-}
-
-namespace detail {
-template <typename Arg>
-decltype(auto) unwrap_emplace_arg(Arg&& arg) noexcept {
-  return std::forward<Arg>(arg);
-}
-template <typename Arg>
-decltype(auto) unwrap_emplace_arg(std::reference_wrapper<Arg> arg) noexcept {
-  return arg.get();
-}
-template <typename Arg>
-decltype(auto) unwrap_emplace_arg(
-    folly::rvalue_reference_wrapper<Arg> arg) noexcept {
-  return std::move(arg).get();
-}
-}
-
-/**
- * Getter function for unpacking a single emplace argument.
- *
- * Calling get_emplace_arg on an emplace_args rvalue reference results in
- * perfect forwarding of the original input types. A special case are
- * std::reference_wrapper and folly::rvalue_reference_wrapper objects within
- * folly::emplace_args. These are also unwrapped so that the bare reference is
- * returned.
- *
- * std::get is not a customization point in the standard library, so the
- * cleanest solution was to define our own getter function.
- */
-template <size_t I, typename... Args>
-decltype(auto) get_emplace_arg(emplace_args<Args...>&& args) noexcept {
-  using Out = std::tuple<Args...>;
-  return detail::unwrap_emplace_arg(
-      std::forward<std::tuple_element_t<I, Out>>(std::get<I>(args)));
-}
-template <size_t I, typename... Args>
-decltype(auto) get_emplace_arg(emplace_args<Args...>& args) noexcept {
-  return detail::unwrap_emplace_arg(std::get<I>(args));
-}
-template <size_t I, typename... Args>
-decltype(auto) get_emplace_arg(const emplace_args<Args...>& args) noexcept {
-  return detail::unwrap_emplace_arg(std::get<I>(args));
-}
-template <size_t I, typename Args>
-decltype(auto) get_emplace_arg(Args&& args) noexcept {
-  return std::get<I>(std::move(args));
-}
-template <size_t I, typename Args>
-decltype(auto) get_emplace_arg(Args& args) noexcept {
-  return std::get<I>(args);
-}
-template <size_t I, typename Args>
-decltype(auto) get_emplace_arg(const Args& args) noexcept {
-  return std::get<I>(args);
-}
-
-namespace detail {
-/**
- * Emplace implementation class for folly::emplace_iterator.
- */
-template <typename Container>
-struct Emplace {
-  Emplace(Container& c, typename Container::iterator i)
-      : container(std::addressof(c)), iter(std::move(i)) {}
-  template <typename... Args>
-  void emplace(Args&&... args) {
-    iter = container->emplace(iter, std::forward<Args>(args)...);
-    ++iter;
-  }
-  Container* container;
-  typename Container::iterator iter;
-};
-
-/**
- * Emplace implementation class for folly::hint_emplace_iterator.
- */
-template <typename Container>
-struct EmplaceHint {
-  EmplaceHint(Container& c, typename Container::iterator i)
-      : container(std::addressof(c)), iter(std::move(i)) {}
-  template <typename... Args>
-  void emplace(Args&&... args) {
-    iter = container->emplace_hint(iter, std::forward<Args>(args)...);
-    ++iter;
-  }
-  Container* container;
-  typename Container::iterator iter;
-};
-
-/**
- * Emplace implementation class for folly::front_emplace_iterator.
- */
-template <typename Container>
-struct EmplaceFront {
-  explicit EmplaceFront(Container& c) : container(std::addressof(c)) {}
-  template <typename... Args>
-  void emplace(Args&&... args) {
-    container->emplace_front(std::forward<Args>(args)...);
-  }
-  Container* container;
-};
-
-/**
- * Emplace implementation class for folly::back_emplace_iterator.
- */
-template <typename Container>
-struct EmplaceBack {
-  explicit EmplaceBack(Container& c) : container(std::addressof(c)) {}
-  template <typename... Args>
-  void emplace(Args&&... args) {
-    container->emplace_back(std::forward<Args>(args)...);
-  }
-  Container* container;
-};
-
-/**
- * Generic base class and implementation of all emplace iterator classes.
- *
- * Uses the curiously recurring template pattern (CRTP) to cast `this*` to
- * `Derived*`; i.e., to implement covariant return types in a generic manner.
- */
-template <typename Derived, typename EmplaceImpl, bool implicit_unpack>
-class emplace_iterator_base;
-
-/**
- * Partial specialization of emplace_iterator_base with implicit unpacking
- * disabled.
- */
-template <typename Derived, typename EmplaceImpl>
-class emplace_iterator_base<Derived, EmplaceImpl, false>
-    : protected EmplaceImpl /* protected implementation inheritance */ {
- public:
-  // Iterator traits.
-  using iterator_category = std::output_iterator_tag;
-  using value_type = void;
-  using difference_type = void;
-  using pointer = void;
-  using reference = void;
-  using container_type =
-      std::remove_reference_t<decltype(*EmplaceImpl::container)>;
-
-  using EmplaceImpl::EmplaceImpl;
-
-  /**
-   * Canonical output operator. Forwards single argument straight to container's
-   * emplace function.
-   */
-  template <typename T>
-  Derived& operator=(T&& arg) {
-    this->emplace(std::forward<T>(arg));
-    return static_cast<Derived&>(*this);
-  }
-
-  /**
-   * Special output operator for packed arguments. Unpacks args and performs
-   * variadic call to container's emplace function.
-   */
-  template <typename... Args>
-  Derived& operator=(emplace_args<Args...>& args) {
-    return unpackAndEmplace(args, std::index_sequence_for<Args...>{});
-  }
-  template <typename... Args>
-  Derived& operator=(const emplace_args<Args...>& args) {
-    return unpackAndEmplace(args, std::index_sequence_for<Args...>{});
-  }
-  template <typename... Args>
-  Derived& operator=(emplace_args<Args...>&& args) {
-    return unpackAndEmplace(
-        std::move(args), std::index_sequence_for<Args...>{});
-  }
-
-  // No-ops.
-  Derived& operator*() {
-    return static_cast<Derived&>(*this);
-  }
-  Derived& operator++() {
-    return static_cast<Derived&>(*this);
-  }
-  Derived& operator++(int) {
-    return static_cast<Derived&>(*this);
-  }
-
-  // We need all of these explicit defaults because the custom operator=
-  // overloads disable implicit generation of these functions.
-  emplace_iterator_base(const emplace_iterator_base&) = default;
-  emplace_iterator_base(emplace_iterator_base&&) noexcept = default;
-  emplace_iterator_base& operator=(emplace_iterator_base&) = default;
-  emplace_iterator_base& operator=(const emplace_iterator_base&) = default;
-  emplace_iterator_base& operator=(emplace_iterator_base&&) noexcept = default;
-
- protected:
-  template <typename Args, std::size_t... I>
-  Derived& unpackAndEmplace(Args& args, std::index_sequence<I...>) {
-    this->emplace(get_emplace_arg<I>(args)...);
-    return static_cast<Derived&>(*this);
-  }
-  template <typename Args, std::size_t... I>
-  Derived& unpackAndEmplace(const Args& args, std::index_sequence<I...>) {
-    this->emplace(get_emplace_arg<I>(args)...);
-    return static_cast<Derived&>(*this);
-  }
-  template <typename Args, std::size_t... I>
-  Derived& unpackAndEmplace(Args&& args, std::index_sequence<I...>) {
-    this->emplace(get_emplace_arg<I>(std::move(args))...);
-    return static_cast<Derived&>(*this);
-  }
-};
-
-/**
- * Partial specialization of emplace_iterator_base with implicit unpacking
- * enabled.
- *
- * Uses inheritance rather than SFINAE. operator= requires a single argument,
- * which makes it very tricky to use std::enable_if or similar.
- */
-template <typename Derived, typename EmplaceImpl>
-class emplace_iterator_base<Derived, EmplaceImpl, true>
-    : public emplace_iterator_base<Derived, EmplaceImpl, false> {
- private:
-  using Base = emplace_iterator_base<Derived, EmplaceImpl, false>;
-
- public:
-  using Base::Base;
-  using Base::operator=;
-
-  /**
-   * Special output operator for arguments packed into a std::pair. Unpacks
-   * the pair and performs variadic call to container's emplace function.
-   */
-  template <typename... Args>
-  Derived& operator=(std::pair<Args...>& args) {
-    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});
-  }
-  template <typename... Args>
-  Derived& operator=(const std::pair<Args...>& args) {
-    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});
-  }
-  template <typename... Args>
-  Derived& operator=(std::pair<Args...>&& args) {
-    return this->unpackAndEmplace(
-        std::move(args), std::index_sequence_for<Args...>{});
-  }
-
-  /**
-   * Special output operator for arguments packed into a std::tuple. Unpacks
-   * the tuple and performs variadic call to container's emplace function.
-   */
-  template <typename... Args>
-  Derived& operator=(std::tuple<Args...>& args) {
-    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});
-  }
-  template <typename... Args>
-  Derived& operator=(const std::tuple<Args...>& args) {
-    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});
-  }
-  template <typename... Args>
-  Derived& operator=(std::tuple<Args...>&& args) {
-    return this->unpackAndEmplace(
-        std::move(args), std::index_sequence_for<Args...>{});
-  }
-
-  // We need all of these explicit defaults because the custom operator=
-  // overloads disable implicit generation of these functions.
-  emplace_iterator_base(const emplace_iterator_base&) = default;
-  emplace_iterator_base(emplace_iterator_base&&) noexcept = default;
-  emplace_iterator_base& operator=(emplace_iterator_base&) = default;
-  emplace_iterator_base& operator=(const emplace_iterator_base&) = default;
-  emplace_iterator_base& operator=(emplace_iterator_base&&) noexcept = default;
-};
-
-/**
- * Concrete instantiation of emplace_iterator_base. All emplace iterator
- * classes; folly::emplace_iterator, folly::hint_emplace_iterator,
- * folly::front_emplace_iterator, and folly::back_emplace_iterator; are just
- * type aliases of this class.
- *
- * It is not possible to alias emplace_iterator_base directly, because type
- * aliases cannot be used for CRTP.
- */
-template <
-    template <typename> class EmplaceImplT,
-    typename Container,
-    bool implicit_unpack>
-class emplace_iterator_impl
-    : public emplace_iterator_base<
-          emplace_iterator_impl<EmplaceImplT, Container, implicit_unpack>,
-          EmplaceImplT<Container>,
-          implicit_unpack> {
- private:
-  using Base = emplace_iterator_base<
-      emplace_iterator_impl,
-      EmplaceImplT<Container>,
-      implicit_unpack>;
-
- public:
-  using Base::Base;
-  using Base::operator=;
-
-  // We need all of these explicit defaults because the custom operator=
-  // overloads disable implicit generation of these functions.
-  emplace_iterator_impl(const emplace_iterator_impl&) = default;
-  emplace_iterator_impl(emplace_iterator_impl&&) noexcept = default;
-  emplace_iterator_impl& operator=(emplace_iterator_impl&) = default;
-  emplace_iterator_impl& operator=(const emplace_iterator_impl&) = default;
-  emplace_iterator_impl& operator=(emplace_iterator_impl&&) noexcept = default;
-};
-} // namespace detail
-
-/**
- * Behaves just like std::insert_iterator except that it calls emplace()
- * instead of insert(). Uses perfect forwarding.
- */
-template <typename Container, bool implicit_unpack = true>
-using emplace_iterator =
-    detail::emplace_iterator_impl<detail::Emplace, Container, implicit_unpack>;
-
-/**
- * Behaves just like std::insert_iterator except that it calls emplace_hint()
- * instead of insert(). Uses perfect forwarding.
- */
-template <typename Container, bool implicit_unpack = true>
-using hint_emplace_iterator = detail::
-    emplace_iterator_impl<detail::EmplaceHint, Container, implicit_unpack>;
-
-/**
- * Behaves just like std::front_insert_iterator except that it calls
- * emplace_front() instead of insert(). Uses perfect forwarding.
- */
-template <typename Container, bool implicit_unpack = true>
-using front_emplace_iterator = detail::
-    emplace_iterator_impl<detail::EmplaceFront, Container, implicit_unpack>;
-
-/**
- * Behaves just like std::back_insert_iterator except that it calls
- * emplace_back() instead of insert(). Uses perfect forwarding.
- */
-template <typename Container, bool implicit_unpack = true>
-using back_emplace_iterator = detail::
-    emplace_iterator_impl<detail::EmplaceBack, Container, implicit_unpack>;
-
-/**
- * Convenience function to construct a folly::emplace_iterator, analogous to
- * std::inserter().
- *
- * Setting implicit_unpack to false will disable implicit unpacking of
- * single std::pair and std::tuple arguments to the iterator's operator=. That
- * may be desirable in case of constructors that expect a std::pair or
- * std::tuple argument.
- */
-template <bool implicit_unpack = true, typename Container>
-emplace_iterator<Container, implicit_unpack> emplacer(
-    Container& c,
-    typename Container::iterator i) {
-  return emplace_iterator<Container, implicit_unpack>(c, std::move(i));
-}
-
-/**
- * Convenience function to construct a folly::hint_emplace_iterator, analogous
- * to std::inserter().
- *
- * Setting implicit_unpack to false will disable implicit unpacking of
- * single std::pair and std::tuple arguments to the iterator's operator=. That
- * may be desirable in case of constructors that expect a std::pair or
- * std::tuple argument.
- */
-template <bool implicit_unpack = true, typename Container>
-hint_emplace_iterator<Container, implicit_unpack> hint_emplacer(
-    Container& c,
-    typename Container::iterator i) {
-  return hint_emplace_iterator<Container, implicit_unpack>(c, std::move(i));
-}
-
-/**
- * Convenience function to construct a folly::front_emplace_iterator, analogous
- * to std::front_inserter().
- *
- * Setting implicit_unpack to false will disable implicit unpacking of
- * single std::pair and std::tuple arguments to the iterator's operator=. That
- * may be desirable in case of constructors that expect a std::pair or
- * std::tuple argument.
- */
-template <bool implicit_unpack = true, typename Container>
-front_emplace_iterator<Container, implicit_unpack> front_emplacer(
-    Container& c) {
-  return front_emplace_iterator<Container, implicit_unpack>(c);
-}
-
-/**
- * Convenience function to construct a folly::back_emplace_iterator, analogous
- * to std::back_inserter().
- *
- * Setting implicit_unpack to false will disable implicit unpacking of
- * single std::pair and std::tuple arguments to the iterator's operator=. That
- * may be desirable in case of constructors that expect a std::pair or
- * std::tuple argument.
- */
-template <bool implicit_unpack = true, typename Container>
-back_emplace_iterator<Container, implicit_unpack> back_emplacer(Container& c) {
-  return back_emplace_iterator<Container, implicit_unpack>(c);
-}
-}
index b6ac68218a1043974a99169714ccf52e7926fed8..7d28d8c0548f94bb224734a7e278184fef3d19c3 100644 (file)
@@ -58,6 +58,12 @@ nobase_follyinclude_HEADERS = \
        concurrency/ConcurrentHashMap.h \
        concurrency/CoreCachedSharedPtr.h \
        concurrency/detail/ConcurrentHashMap-detail.h \
+       container/Iterator.h \
+       container/Enumerate.h \
+       container/EvictingCacheMap.h \
+       container/Foreach.h \
+       container/Foreach-inl.h \
+       container/SparseByteSet.h \
        ConstexprMath.h \
        detail/AtomicHashUtils.h \
        detail/AtomicUnorderedMapUtils.h \
@@ -111,8 +117,6 @@ nobase_follyinclude_HEADERS = \
        DynamicConverter.h \
        dynamic.h \
        dynamic-inl.h \
-       Enumerate.h \
-       EvictingCacheMap.h \
        Exception.h \
        ExceptionString.h \
        ExceptionWrapper.h \
@@ -199,8 +203,6 @@ nobase_follyinclude_HEADERS = \
        Fingerprint.h \
        FixedString.h \
        folly-config.h \
-       Foreach.h \
-       Foreach-inl.h \
        FormatArg.h \
        FormatTraits.h \
        Format.h \
@@ -309,7 +311,6 @@ nobase_follyinclude_HEADERS = \
        io/async/test/TimeUtil.h \
        io/async/test/UndelayedDestruction.h \
        io/async/test/Util.h \
-       Iterator.h \
        json.h \
        Launder.h \
        Lazy.h \
@@ -396,7 +397,6 @@ nobase_follyinclude_HEADERS = \
        small_vector.h \
        SocketAddress.h \
        sorted_vector_types.h \
-       SparseByteSet.h \
        SpinLock.h \
        ssl/Init.h \
        ssl/OpenSSLCertUtils.h \
diff --git a/folly/SparseByteSet.h b/folly/SparseByteSet.h
deleted file mode 100644 (file)
index f6393b0..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <glog/logging.h>
-
-namespace folly {
-
-/***
- *  SparseByteSet
- *
- *  A special-purpose data structure representing an insert-only set of bytes.
- *  May have better performance than std::bitset<256>, depending on workload.
- *
- *  Operations:
- *  - add(byte)
- *  - contains(byte)
- *
- *  Performance:
- *  - The entire capacity of the set is inline; the set never allocates.
- *  - The constructor zeros only the first two bytes of the object.
- *  - add and contains both run in constant time w.r.t. the size of the set.
- *    Constant time - not amortized constant - and with small constant factor.
- *
- *  This data structure is ideal for on-stack use.
- *
- *  Aho, Hopcroft, and Ullman refer to this trick in "The Design and Analysis
- *  of Computer Algorithms" (1974), but the best description is here:
- *  http://research.swtch.com/sparse
- *  http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.30.7319
- */
-class SparseByteSet {
- public:
-  //  There are this many possible values:
-  static constexpr uint16_t kCapacity = 256;
-
-  //  No init of byte-arrays required!
-  SparseByteSet() : size_(0) { }
-
-  /***
-   *  add(byte)
-   *
-   *  O(1), non-amortized.
-   */
-  inline bool add(uint8_t i) {
-    bool r = !contains(i);
-    if (r) {
-      DCHECK_LT(size_, kCapacity);
-      dense_[size_] = i;
-      sparse_[i] = uint8_t(size_);
-      size_++;
-    }
-    return r;
-  }
-
-  /***
-   *  contains(byte)
-   *
-   *  O(1), non-amortized.
-   */
-  inline bool contains(uint8_t i) const {
-    return sparse_[i] < size_ && dense_[sparse_[i]] == i;
-  }
-
- private:
-  uint16_t size_;  // can't use uint8_t because it would overflow if all
-                   // possible values were inserted.
-  uint8_t sparse_[kCapacity];
-  uint8_t dense_[kCapacity];
-};
-
-}
diff --git a/folly/algorithm/Merge.h b/folly/algorithm/Merge.h
deleted file mode 100644 (file)
index 1979ae7..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * folly::merge() is an implementation of std::merge with one additonal
- * guarantee: if the input ranges overlap, the order that values *from the two
- * different ranges* appear in the output is well defined (std::merge only
- * guarantees relative ordering is maintained within a single input range).
- * This semantic is very useful when the output container removes duplicates
- * (such as std::map) to guarantee that elements from b override elements from
- * a.
- *
- * ex. Let's say we have two vector<pair<int, int>> as input, and we are
- * merging into a vector<pair<int, int>>. The comparator is returns true if the
- * first argument has a lesser 'first' value in the pair.
- *
- * a = {{1, 1}, {2, 2}, {3, 3}};
- * b = {{1, 2}, {2, 3}};
- *
- * folly::merge<...>(a.begin(), a.end(), b.begin(), b.end(), outputIter) is
- * guaranteed to produce {{1, 1}, {1, 2}, {2, 2}, {2, 3}, {3, 3}}. That is,
- * if comp(it_a, it_b) == comp(it_b, it_a) == false, we first insert the element
- * from a.
- */
-
-#pragma once
-
-#include <algorithm>
-
-namespace folly {
-
-template <class InputIt1, class InputIt2, class OutputIt, class Compare>
-OutputIt merge(InputIt1 first1, InputIt1 last1,
-               InputIt2 first2, InputIt2 last2,
-               OutputIt d_first, Compare comp) {
-  for (; first1 != last1; ++d_first) {
-    if (first2 == last2) {
-      return std::copy(first1, last1, d_first);
-    }
-    if (comp(*first2, *first1)) {
-      *d_first = *first2;
-      ++first2;
-    } else {
-      *d_first = *first1;
-      ++first1;
-    }
-  }
-  return std::copy(first2, last2, d_first);
-}
-
-template <class InputIt1, class InputIt2, class OutputIt>
-OutputIt merge(InputIt1 first1, InputIt1 last1,
-               InputIt2 first2, InputIt2 last2,
-               OutputIt d_first) {
-  for (; first1 != last1; ++d_first) {
-    if (first2 == last2) {
-      return std::copy(first1, last1, d_first);
-    }
-    if (*first2 < *first1) {
-      *d_first = *first2;
-      ++first2;
-    } else {
-      *d_first = *first1;
-      ++first1;
-    }
-  }
-  return std::copy(first2, last2, d_first);
-}
-
-}
diff --git a/folly/algorithm/test/MergeTest.cpp b/folly/algorithm/test/MergeTest.cpp
deleted file mode 100644 (file)
index a963768..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/algorithm/Merge.h>
-
-#include <map>
-#include <vector>
-
-#include <folly/portability/GTest.h>
-
-TEST(MergeTest, NonOverlapping) {
-  std::vector<int> a = {0, 2, 4, 6};
-  std::vector<int> b = {1, 3, 5, 7};
-  std::vector<int> c;
-
-  folly::merge(a.begin(), a.end(),
-               b.begin(), b.end(),
-               std::back_inserter(c));
-  EXPECT_EQ(8, c.size());
-  for (size_t i = 0; i < 8; ++i) {
-    EXPECT_EQ(i, c[i]);
-  }
-}
-
-TEST(MergeTest, OverlappingInSingleInputRange) {
-  std::vector<std::pair<int, int>> a = {{0, 0}, {0, 1}};
-  std::vector<std::pair<int, int>> b = {{2, 2}, {3, 3}};
-  std::map<int, int> c;
-
-  folly::merge(a.begin(), a.end(),
-               b.begin(), b.end(),
-               std::inserter(c, c.begin()));
-  EXPECT_EQ(3, c.size());
-
-  // First value is inserted, second is not
-  EXPECT_EQ(c[0], 0);
-
-  EXPECT_EQ(c[2], 2);
-  EXPECT_EQ(c[3], 3);
-}
-
-TEST(MergeTest, OverlappingInDifferentInputRange) {
-  std::vector<std::pair<int, int>> a = {{0, 0}, {1, 1}};
-  std::vector<std::pair<int, int>> b = {{0, 2}, {3, 3}};
-  std::map<int, int> c;
-
-  folly::merge(a.begin(), a.end(),
-               b.begin(), b.end(),
-               std::inserter(c, c.begin()));
-  EXPECT_EQ(3, c.size());
-
-  // Value from a is inserted, value from b is not.
-  EXPECT_EQ(c[0], 0);
-
-  EXPECT_EQ(c[1], 1);
-  EXPECT_EQ(c[3], 3);
-}
index d294e75cf3a478dba5477aff135a105a626e8368..df7e325e65bae40b229ccefdae817d5a36830ff5 100644 (file)
@@ -19,9 +19,9 @@
 #include <array>
 #include <memory>
 
-#include <folly/Enumerate.h>
 #include <folly/concurrency/AtomicSharedPtr.h>
 #include <folly/concurrency/CacheLocality.h>
+#include <folly/container/Enumerate.h>
 #include <folly/experimental/hazptr/hazptr.h>
 
 namespace folly {
diff --git a/folly/container/Enumerate.h b/folly/container/Enumerate.h
new file mode 100644 (file)
index 0000000..94bcece
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iterator>
+#include <memory>
+
+#include <folly/portability/SysTypes.h>
+
+/**
+ * Similar to Python's enumerate(), folly::enumerate() can be used to
+ * iterate a range with a for-range loop, and it also allows to
+ * retrieve the count of iterations so far.
+ *
+ * For example:
+ *
+ * for (auto it : folly::enumerate(vec)) {
+ *   // *it is a reference to the current element. Const if vec is const.
+ *   // it->member can be used as well.
+ *   // it.index contains the iteration count.
+ * }
+ *
+ * If the iteration variable is const, the reference is too.
+ *
+ * for (const auto it : folly::enumerate(vec)) {
+ *   // *it is always a const reference.
+ * }
+ *
+ * @author Giuseppe Ottaviano <ott@fb.com>
+ */
+
+namespace folly {
+
+namespace detail {
+
+template <class T>
+struct MakeConst {
+  using type = const T;
+};
+template <class T>
+struct MakeConst<T&> {
+  using type = const T&;
+};
+template <class T>
+struct MakeConst<T*> {
+  using type = const T*;
+};
+
+// Raw pointers don't have an operator->() member function, so the
+// second overload will be SFINAEd out in that case. Otherwise, the
+// second is preferred in the partial order for getPointer(_, 0).
+template <class Iterator>
+auto getPointer(const Iterator& it, long) -> decltype(std::addressof(*it)) {
+  return std::addressof(*it);
+}
+template <class Iterator>
+auto getPointer(const Iterator& it, int) -> decltype(it.operator->()) {
+  return it.operator->();
+}
+
+template <class Iterator>
+class Enumerator {
+ public:
+  explicit Enumerator(Iterator it) : it_(std::move(it)) {}
+
+  class Proxy {
+   public:
+    using difference_type = ssize_t;
+    using value_type = typename std::iterator_traits<Iterator>::value_type;
+    using reference = typename std::iterator_traits<Iterator>::reference;
+    using pointer = typename std::iterator_traits<Iterator>::pointer;
+    using iterator_category = std::input_iterator_tag;
+
+    explicit Proxy(const Enumerator* e) : it_(e->it_), index(e->idx_) {}
+
+    // Non-const Proxy: Forward constness from Iterator.
+    reference operator*() {
+      return *it_;
+    }
+    pointer operator->() {
+      return getPointer(it_, 0);
+    }
+
+    // Const Proxy: Force const references.
+    typename MakeConst<reference>::type operator*() const {
+      return *it_;
+    }
+    typename MakeConst<pointer>::type operator->() const {
+      return getPointer(it_, 0);
+    }
+
+   private:
+    const Iterator& it_;
+
+   public:
+    const size_t index;
+  };
+
+  Proxy operator*() const {
+    return Proxy(this);
+  }
+
+  Enumerator& operator++() {
+    ++it_;
+    ++idx_;
+    return *this;
+  }
+
+  template <typename OtherIterator>
+  bool operator==(const Enumerator<OtherIterator>& rhs) {
+    return it_ == rhs.it_;
+  }
+
+  template <typename OtherIterator>
+  bool operator!=(const Enumerator<OtherIterator>& rhs) {
+    return !(*this == rhs);
+  }
+
+ private:
+  template <typename OtherIterator>
+  friend class Enumerator;
+
+  Iterator it_;
+  size_t idx_ = 0;
+};
+
+template <class Range>
+class RangeEnumerator {
+  Range r_;
+  using BeginIteratorType = decltype(std::declval<Range>().begin());
+  using EndIteratorType = decltype(std::declval<Range>().end());
+
+ public:
+  explicit RangeEnumerator(Range&& r) : r_(std::forward<Range>(r)) {}
+
+  Enumerator<BeginIteratorType> begin() {
+    return Enumerator<BeginIteratorType>(r_.begin());
+  }
+  Enumerator<EndIteratorType> end() {
+    return Enumerator<EndIteratorType>(r_.end());
+  }
+};
+
+} // namespace detail
+
+template <class Range>
+detail::RangeEnumerator<Range> enumerate(Range&& r) {
+  return detail::RangeEnumerator<Range>(std::forward<Range>(r));
+}
+
+} // namespace folly
diff --git a/folly/container/EvictingCacheMap.h b/folly/container/EvictingCacheMap.h
new file mode 100644 (file)
index 0000000..e7e8149
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <exception>
+#include <functional>
+
+#include <boost/intrusive/list.hpp>
+#include <boost/intrusive/unordered_set.hpp>
+#include <boost/iterator/iterator_adaptor.hpp>
+#include <boost/utility.hpp>
+
+#include <folly/portability/BitsFunctexcept.h>
+
+namespace folly {
+
+/**
+ * A general purpose LRU evicting cache. Designed to support constant time
+ * set/get operations. It maintains a doubly linked list of items that are
+ * threaded through an index (a hash map). The access ordered is maintained
+ * on the list by moving an element to the front of list on a get. New elements
+ * are added to the front of the list. The index size is set to half the
+ * capacity (setting capacity to 0 is a special case. see notes at the end of
+ * this section). So assuming uniform distribution of keys, set/get are both
+ * constant time operations.
+ *
+ * On reaching capacity limit, clearSize_ LRU items are evicted at a time. If
+ * a callback is specified with setPruneHook, it is invoked for each eviction.
+ *
+ * This is NOT a thread-safe implementation.
+ *
+ * Configurability: capacity of the cache, number of items to evict, eviction
+ * callback and the hasher to hash the keys can all be supplied by the caller.
+ *
+ * If at a given state, N1 - N6 are the nodes in MRU to LRU order and hashing
+ * to index keys as {(N1,N5)->H1, (N4,N5,N5)->H2, N3->Hi}, the datastructure
+ * layout is as below. N1 .. N6 is a list threaded through the hash.
+ * Assuming, each the number of nodes hashed to each index key is bounded, the
+ * following operations run in constant time.
+ * i) get computes the index key, walks the list of elements hashed to
+ * the key and moves it to the front of the list, if found.
+ * ii) set inserts a new node into the list and places the same node on to the
+ * list of elements hashing to the corresponding index key.
+ * ii) prune deletes nodes from the end of the list as well from the index.
+ *
+ * +----+     +----+     +----+
+ * | H1 | <-> | N1 | <-> | N5 |
+ * +----+     +----+     +----+
+ *              ^        ^  ^
+ *              |    ___/    \
+ *              |   /         \
+ *              |_ /________   \___
+ *                /        |       \
+ *               /         |        \
+ *              v          v         v
+ * +----+     +----+     +----+     +----+
+ * | H2 | <-> | N4 | <-> | N2 | <-> | N6 |
+ * +----+     +----+     +----+     +----+
+ *   .          ^          ^
+ *   .          |          |
+ *   .          |          |
+ *   .          |     _____|
+ *   .          |    /
+ *              v   v
+ * +----+     +----+
+ * | Hi | <-> | N3 |
+ * +----+     +----+
+ *
+ * N.B 1 : Changing the capacity with setMaxSize does not change the index size
+ * and it could end up in too many elements indexed to the same slot in index.
+ * The set/get performance will get worse in this case. So it is best to avoid
+ * resizing.
+ *
+ * N.B 2 : Setting capacity to 0, using setMaxSize or initialization, turns off
+ * evictions based on sizeof the cache making it an INFINITE size cache
+ * unless evictions of LRU items are triggered by calling prune() by clients
+ * (using their own eviction criteria).
+ */
+template <class TKey, class TValue, class THash = std::hash<TKey>>
+class EvictingCacheMap {
+ private:
+  // typedefs for brevity
+  struct Node;
+  typedef boost::intrusive::link_mode<boost::intrusive::safe_link> link_mode;
+  typedef boost::intrusive::unordered_set<Node> NodeMap;
+  typedef boost::intrusive::list<Node> NodeList;
+  typedef std::pair<const TKey, TValue> TPair;
+
+ public:
+  typedef std::function<void(TKey, TValue&&)> PruneHookCall;
+
+  // iterator base : returns TPair on dereference
+  template <typename Value, typename TIterator>
+  class iterator_base
+    : public boost::iterator_adaptor<iterator_base<Value, TIterator>,
+                                    TIterator,
+                                    Value,
+                                    boost::bidirectional_traversal_tag > {
+   public:
+    iterator_base() {
+    }
+    explicit iterator_base(TIterator it)
+        : iterator_base::iterator_adaptor_(it) {
+    }
+    Value& dereference() const {
+      return this->base_reference()->pr;
+    }
+  };
+
+  // iterators
+  typedef iterator_base<
+    TPair, typename NodeList::iterator> iterator;
+  typedef iterator_base<
+    const TPair, typename NodeList::const_iterator> const_iterator;
+  typedef iterator_base<
+    TPair, typename NodeList::reverse_iterator> reverse_iterator;
+  typedef iterator_base<
+    const TPair,
+    typename NodeList::const_reverse_iterator> const_reverse_iterator;
+
+  /**
+   * Construct a EvictingCacheMap
+   * @param maxSize maximum size of the cache map.  Once the map size exceeds
+   *     maxSize, the map will begin to evict.
+   * @param clearSize the number of elements to clear at a time when the
+   *     eviction size is reached.
+   */
+  explicit EvictingCacheMap(std::size_t maxSize, std::size_t clearSize = 1)
+      : nIndexBuckets_(std::max(maxSize / 2, std::size_t(kMinNumIndexBuckets))),
+        indexBuckets_(new typename NodeMap::bucket_type[nIndexBuckets_]),
+        indexTraits_(indexBuckets_.get(), nIndexBuckets_),
+        index_(indexTraits_),
+        maxSize_(maxSize),
+        clearSize_(clearSize) { }
+
+  EvictingCacheMap(const EvictingCacheMap&) = delete;
+  EvictingCacheMap& operator=(const EvictingCacheMap&) = delete;
+  EvictingCacheMap(EvictingCacheMap&&) = default;
+  EvictingCacheMap& operator=(EvictingCacheMap&&) = default;
+
+  ~EvictingCacheMap() {
+    setPruneHook(nullptr);
+    // ignore any potential exceptions from pruneHook_
+    pruneWithFailSafeOption(size(), nullptr, true);
+  }
+
+  /**
+   * Adjust the max size of EvictingCacheMap. Note that this does not update
+   * nIndexBuckets_ accordingly. This API can cause performance to get very
+   * bad, e.g., the nIndexBuckets_ is still 100 after maxSize is updated to 1M.
+   *
+   * Calling this function with an arugment of 0 removes the limit on the cache
+   * size and elements are not evicted unless clients explictly call prune.
+   *
+   * If you intend to resize dynamically using this, then picking an index size
+   * that works well and initializing with corresponding maxSize is the only
+   * reasonable option.
+   *
+   * @param maxSize new maximum size of the cache map.
+   * @param pruneHook callback to use on eviction.
+   */
+  void setMaxSize(size_t maxSize, PruneHookCall pruneHook = nullptr) {
+    if (maxSize != 0 && maxSize < size()) {
+      // Prune the excess elements with our new constraints.
+      prune(std::max(size() - maxSize, clearSize_), pruneHook);
+    }
+    maxSize_ = maxSize;
+  }
+
+  size_t getMaxSize() const {
+    return maxSize_;
+  }
+
+  void setClearSize(size_t clearSize) {
+    clearSize_ = clearSize;
+  }
+
+  /**
+   * Check for existence of a specific key in the map.  This operation has
+   *     no effect on LRU order.
+   * @param key key to search for
+   * @return true if exists, false otherwise
+   */
+  bool exists(const TKey& key) const  {
+    return findInIndex(key) != index_.end();
+  }
+
+  /**
+   * Get the value associated with a specific key.  This function always
+   *     promotes a found value to the head of the LRU.
+   * @param key key associated with the value
+   * @return the value if it exists
+   * @throw std::out_of_range exception of the key does not exist
+   */
+  TValue& get(const TKey& key) {
+    auto it = find(key);
+    if (it == end()) {
+      std::__throw_out_of_range("Key does not exist");
+    }
+    return it->second;
+  }
+
+  /**
+   * Get the iterator associated with a specific key.  This function always
+   *     promotes a found value to the head of the LRU.
+   * @param key key to associate with value
+   * @return the iterator of the object (a std::pair of const TKey, TValue) or
+   *     end() if it does not exist
+   */
+  iterator find(const TKey& key) {
+    auto it = findInIndex(key);
+    if (it == index_.end()) {
+      return end();
+    }
+    lru_.erase(lru_.iterator_to(*it));
+    lru_.push_front(*it);
+    return iterator(lru_.iterator_to(*it));
+  }
+
+  /**
+   * Get the value associated with a specific key.  This function never
+   *     promotes a found value to the head of the LRU.
+   * @param key key associated with the value
+   * @return the value if it exists
+   * @throw std::out_of_range exception of the key does not exist
+   */
+  const TValue& getWithoutPromotion(const TKey& key) const {
+    auto it = findWithoutPromotion(key);
+    if (it == end()) {
+      std::__throw_out_of_range("Key does not exist");
+    }
+    return it->second;
+  }
+
+  TValue& getWithoutPromotion(const TKey& key) {
+    auto const& cThis = *this;
+    return const_cast<TValue&>(cThis.getWithoutPromotion(key));
+  }
+
+  /**
+   * Get the iterator associated with a specific key.  This function never
+   *     promotes a found value to the head of the LRU.
+   * @param key key to associate with value
+   * @return the iterator of the object (a std::pair of const TKey, TValue) or
+   *     end() if it does not exist
+   */
+  const_iterator findWithoutPromotion(const TKey& key) const {
+    auto it = findInIndex(key);
+    return (it == index_.end()) ? end() : const_iterator(lru_.iterator_to(*it));
+  }
+
+  iterator findWithoutPromotion(const TKey& key) {
+    auto it = findInIndex(key);
+    return (it == index_.end()) ? end() : iterator(lru_.iterator_to(*it));
+  }
+
+  /**
+   * Erase the key-value pair associated with key if it exists.
+   * @param key key associated with the value
+   * @return true if the key existed and was erased, else false
+   */
+  bool erase(const TKey& key) {
+    auto it = findInIndex(key);
+    if (it == index_.end()) {
+      return false;
+    }
+    auto node = &(*it);
+    std::unique_ptr<Node> nptr(node);
+    lru_.erase(lru_.iterator_to(*node));
+    index_.erase(it);
+    return true;
+  }
+
+  /**
+   * Set a key-value pair in the dictionary
+   * @param key key to associate with value
+   * @param value value to associate with the key
+   * @param promote boolean flag indicating whether or not to move something
+   *     to the front of an LRU.  This only really matters if you're setting
+   *     a value that already exists.
+   * @param pruneHook callback to use on eviction (if it occurs).
+   */
+  void set(const TKey& key,
+           TValue value,
+           bool promote = true,
+           PruneHookCall pruneHook = nullptr) {
+    auto it = findInIndex(key);
+    if (it != index_.end()) {
+      it->pr.second = std::move(value);
+      if (promote) {
+        lru_.erase(lru_.iterator_to(*it));
+        lru_.push_front(*it);
+      }
+    } else {
+      auto node = new Node(key, std::move(value));
+      index_.insert(*node);
+      lru_.push_front(*node);
+
+      // no evictions if maxSize_ is 0 i.e. unlimited capacity
+      if (maxSize_ > 0 && size() > maxSize_) {
+        prune(clearSize_, pruneHook);
+      }
+    }
+  }
+
+  /**
+   * Get the number of elements in the dictionary
+   * @return the size of the dictionary
+   */
+  std::size_t size() const {
+    return index_.size();
+  }
+
+  /**
+   * Typical empty function
+   * @return true if empty, false otherwise
+   */
+  bool empty() const {
+    return index_.empty();
+  }
+
+  void clear(PruneHookCall pruneHook = nullptr) {
+    prune(size(), pruneHook);
+  }
+
+  /**
+   * Set the prune hook, which is the function invoked on the key and value
+   *     on each eviction.  Will throw If the pruneHook throws, unless the
+   *     EvictingCacheMap object is being destroyed in which case it will
+   *     be ignored.
+   * @param pruneHook new callback to use on eviction.
+   * @param promote boolean flag indicating whether or not to move something
+   *     to the front of an LRU.
+   * @return the iterator of the object (a std::pair of const TKey, TValue) or
+   *     end() if it does not exist
+   */
+  void setPruneHook(PruneHookCall pruneHook) {
+    pruneHook_ = pruneHook;
+  }
+
+
+  /**
+   * Prune the minimum of pruneSize and size() from the back of the LRU.
+   * Will throw if pruneHook throws.
+   * @param pruneSize minimum number of elements to prune
+   * @param pruneHook a custom pruneHook function
+   */
+  void prune(std::size_t pruneSize, PruneHookCall pruneHook = nullptr) {
+    // do not swallow exceptions for prunes not triggered from destructor
+    pruneWithFailSafeOption(pruneSize, pruneHook, false);
+  }
+
+  // Iterators and such
+  iterator begin() {
+    return iterator(lru_.begin());
+  }
+  iterator end() {
+    return iterator(lru_.end());
+  }
+  const_iterator begin() const {
+    return const_iterator(lru_.begin());
+  }
+  const_iterator end() const {
+    return const_iterator(lru_.end());
+  }
+
+  const_iterator cbegin() const {
+    return const_iterator(lru_.cbegin());
+  }
+  const_iterator cend() const {
+    return const_iterator(lru_.cend());
+  }
+
+  reverse_iterator rbegin() {
+    return reverse_iterator(lru_.rbegin());
+  }
+  reverse_iterator rend() {
+    return reverse_iterator(lru_.rend());
+  }
+
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(lru_.rbegin());
+  }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(lru_.rend());
+  }
+
+  const_reverse_iterator crbegin() const {
+    return const_reverse_iterator(lru_.crbegin());
+  }
+  const_reverse_iterator crend() const {
+    return const_reverse_iterator(lru_.crend());
+  }
+
+ private:
+  struct Node
+    : public boost::intrusive::unordered_set_base_hook<link_mode>,
+      public boost::intrusive::list_base_hook<link_mode> {
+    Node(const TKey& key, TValue&& value)
+        : pr(std::make_pair(key, std::move(value))) {
+    }
+    TPair pr;
+    friend bool operator==(const Node& lhs, const Node& rhs) {
+      return lhs.pr.first == rhs.pr.first;
+    }
+    friend std::size_t hash_value(const Node& node) {
+      return THash()(node.pr.first);
+    }
+  };
+
+  struct KeyHasher {
+    std::size_t operator()(const Node& node) {
+      return THash()(node.pr.first);
+    }
+    std::size_t operator()(const TKey& key) {
+      return THash()(key);
+    }
+  };
+
+  struct KeyValueEqual {
+    bool operator()(const TKey& lhs, const Node& rhs) {
+      return lhs == rhs.pr.first;
+    }
+    bool operator()(const Node& lhs, const TKey& rhs) {
+      return lhs.pr.first == rhs;
+    }
+  };
+
+  /**
+   * Get the iterator in in the index associated with a specific key. This is
+   * merely a search in the index and does not promote the object.
+   * @param key key to associate with value
+   * @return the NodeMap::iterator to the Node containing the object
+   *    (a std::pair of const TKey, TValue) or index_.end() if it does not exist
+   */
+  typename NodeMap::iterator findInIndex(const TKey& key) {
+    return index_.find(key, KeyHasher(), KeyValueEqual());
+  }
+
+  typename NodeMap::const_iterator findInIndex(const TKey& key) const {
+    return index_.find(key, KeyHasher(), KeyValueEqual());
+  }
+
+  /**
+   * Prune the minimum of pruneSize and size() from the back of the LRU.
+   * @param pruneSize minimum number of elements to prune
+   * @param pruneHook a custom pruneHook function
+   * @param failSafe true if exceptions are to ignored, false by default
+   */
+  void pruneWithFailSafeOption(std::size_t pruneSize,
+    PruneHookCall pruneHook, bool failSafe) {
+    auto& ph = (nullptr == pruneHook) ? pruneHook_ : pruneHook;
+
+    for (std::size_t i = 0; i < pruneSize && !lru_.empty(); i++) {
+      auto *node = &(*lru_.rbegin());
+      std::unique_ptr<Node> nptr(node);
+
+      lru_.erase(lru_.iterator_to(*node));
+      index_.erase(index_.iterator_to(*node));
+      if (ph) {
+        try {
+          ph(node->pr.first, std::move(node->pr.second));
+        } catch (...) {
+          if (!failSafe) {
+            throw;
+          }
+        }
+      }
+    }
+  }
+
+  static const std::size_t kMinNumIndexBuckets = 100;
+  PruneHookCall pruneHook_;
+  std::size_t nIndexBuckets_;
+  std::unique_ptr<typename NodeMap::bucket_type[]> indexBuckets_;
+  typename NodeMap::bucket_traits indexTraits_;
+  NodeMap index_;
+  NodeList lru_;
+  std::size_t maxSize_;
+  std::size_t clearSize_;
+};
+
+} // namespace folly
diff --git a/folly/container/Foreach-inl.h b/folly/container/Foreach-inl.h
new file mode 100644 (file)
index 0000000..c037758
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2017-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cassert>
+#include <cstdint>
+#include <initializer_list>
+#include <iterator>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include <folly/Portability.h>
+#include <folly/Traits.h>
+
+namespace folly {
+
+namespace for_each_detail {
+
+namespace adl {
+
+/* using override */
+using std::begin;
+/* using override */
+using std::end;
+/* using override */
+using std::get;
+
+/**
+ * The adl_ functions below lookup the function name in the namespace of the
+ * type of the object being passed into the function.  If no function with
+ * that name exists for the passed object then the default std:: versions are
+ * going to be called
+ */
+template <std::size_t Index, typename Type>
+auto adl_get(Type&& instance) -> decltype(get<Index>(std::declval<Type>())) {
+  return get<Index>(std::forward<Type>(instance));
+}
+template <typename Type>
+auto adl_begin(Type&& instance) -> decltype(begin(instance)) {
+  return begin(instance);
+}
+template <typename Type>
+auto adl_end(Type&& instance) -> decltype(end(instance)) {
+  return end(instance);
+}
+
+} // namespace adl
+
+/**
+ * Enable if the range supports fetching via non member get<>()
+ */
+template <typename T>
+using EnableIfNonMemberGetFound =
+    void_t<decltype(adl::adl_get<0>(std::declval<T>()))>;
+/**
+ * Enable if the range supports fetching via a member get<>()
+ */
+template <typename T>
+using EnableIfMemberGetFound =
+    void_t<decltype(std::declval<T>().template get<0>())>;
+
+/**
+ * A get that tries ADL get<> first and if that is not found tries to execute
+ * a member function get<> on the instance, just as proposed by the structured
+ * bindings proposal here 11.5.3
+ * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf
+ */
+template <std::size_t Index, typename Type, typename = void>
+struct Get {
+  template <typename T>
+  static auto impl(T&& instance)
+      -> decltype(adl::adl_get<Index>(std::declval<T>())) {
+    return adl::adl_get<Index>(std::forward<T>(instance));
+  }
+};
+template <std::size_t Index, typename Type>
+struct Get<Index, Type, EnableIfMemberGetFound<Type>> {
+  template <typename T>
+  static auto impl(T&& instance)
+      -> decltype(std::declval<T>().template get<Index>()) {
+    return std::forward<T>(instance).template get<Index>();
+  }
+};
+
+/**
+ * Concepts-ish
+ */
+/**
+ * Check if the range is a tuple or a range
+ */
+template <typename Type, typename T = typename std::decay<Type>::type>
+using EnableIfTuple = void_t<
+    decltype(Get<0, T>::impl(std::declval<T>())),
+    decltype(std::tuple_size<T>::value)>;
+
+/**
+ * Check if the range is a range
+ */
+template <typename Type, typename T = typename std::decay<Type>::type>
+using EnableIfRange = void_t<
+    decltype(adl::adl_begin(std::declval<T>())),
+    decltype(adl::adl_end(std::declval<T>()))>;
+
+/**
+ * Forwards the return value of the first element of the range, used to
+ * determine the type of the first element in the range in SFINAE use cases
+ */
+template <typename Sequence, typename = void>
+struct DeclvalSequence {
+  using type = decltype(*(adl::adl_begin(std::declval<Sequence>())));
+};
+
+template <typename Sequence>
+struct DeclvalSequence<Sequence, EnableIfTuple<Sequence>> {
+  using type = decltype(Get<0, Sequence>::impl(std::declval<Sequence>()));
+};
+
+/**
+ * Check if the functor accepts one or two arguments, one of the first element
+ * in the range, assuming that all the other elements can also be passed to the
+ * functor, and the second being an instantiation of std::integral_constant,
+ * and the third being an instantiation of LoopControl, to provide
+ * breakability to the loop
+ */
+template <typename Sequence, typename Func>
+using EnableIfAcceptsOneArgument = void_t<decltype(std::declval<Func>()(
+    std::declval<typename DeclvalSequence<Sequence>::type>()))>;
+template <typename Sequence, typename Func>
+using EnableIfAcceptsTwoArguments = void_t<decltype(std::declval<Func>()(
+    std::declval<typename DeclvalSequence<Sequence>::type>(),
+    std::integral_constant<std::size_t, 0>{}))>;
+template <typename Sequence, typename Func>
+using EnableIfAcceptsThreeArguments = void_t<decltype(std::declval<Func>()(
+    std::declval<typename DeclvalSequence<Sequence>::type>(),
+    std::integral_constant<std::size_t, 0>{},
+    adl::adl_begin(std::declval<Sequence>())))>;
+template <typename Sequence, typename Func>
+using EnableIfBreaksRange = std::enable_if_t<std::is_same<
+    typename std::decay<decltype(std::declval<Func>()(
+        std::declval<typename DeclvalSequence<Sequence>::type>(),
+        std::size_t{0},
+        adl::adl_begin(std::declval<Sequence>())))>::type,
+    LoopControl>::value>;
+template <typename Sequence, typename Func>
+using EnableIfBreaksTuple = std::enable_if_t<std::is_same<
+    typename std::decay<decltype(std::declval<Func>()(
+        std::declval<typename DeclvalSequence<Sequence>::type>(),
+        std::integral_constant<std::size_t, 0>{}))>::type,
+    LoopControl>::value>;
+/**
+ * Enables if the sequence has random access iterators
+ */
+template <typename Sequence>
+using EnableIfRandomAccessIterators = std::enable_if_t<std::is_same<
+    typename std::iterator_traits<typename std::decay<decltype(
+        adl::adl_begin(std::declval<Sequence>()))>::type>::iterator_category,
+    std::random_access_iterator_tag>::value>;
+template <typename Sequence, typename Index>
+using EnableIfHasIndexingOperator =
+    void_t<decltype(std::declval<Sequence>()[std::declval<Index>()])>;
+
+/**
+ * Implementation for the range iteration, this provides specializations in
+ * the case where the function returns a break or continue.
+ */
+template <typename Seq, typename F, typename = void>
+struct ForEachRange {
+  template <typename Sequence, typename Func>
+  static void impl(Sequence&& range, Func& func) {
+    auto first = adl::adl_begin(range);
+    auto last = adl::adl_end(range);
+    for (auto index = std::size_t{0}; first != last; ++index) {
+      auto next = std::next(first);
+      func(*first, index, first);
+      first = next;
+    }
+  }
+};
+
+template <typename Seq, typename F>
+struct ForEachRange<Seq, F, EnableIfBreaksRange<Seq, F>> {
+  template <typename Sequence, typename Func>
+  static void impl(Sequence&& range, Func& func) {
+    auto first = adl::adl_begin(range);
+    auto last = adl::adl_end(range);
+    for (auto index = std::size_t{0}; first != last; ++index) {
+      auto next = std::next(first);
+      if (loop_break == func(*first, index, first)) {
+        break;
+      }
+      first = next;
+    }
+  }
+};
+
+/**
+ * Implementations for the runtime function
+ */
+template <
+    typename Sequence,
+    typename Func,
+    EnableIfAcceptsThreeArguments<Sequence, Func>* = nullptr>
+void for_each_range_impl(Sequence&& range, Func& func) {
+  ForEachRange<Sequence, Func>::impl(std::forward<Sequence>(range), func);
+}
+template <
+    typename Sequence,
+    typename Func,
+    EnableIfAcceptsTwoArguments<Sequence, Func>* = nullptr>
+void for_each_range_impl(Sequence&& range, Func& func) {
+  // make a three arg adaptor for the function passed in so that the main
+  // implementation function can be used
+  auto three_arg_adaptor = [&func](
+                               auto&& ele, auto index, auto) -> decltype(auto) {
+    return func(std::forward<decltype(ele)>(ele), index);
+  };
+  for_each_range_impl(std::forward<Sequence>(range), three_arg_adaptor);
+}
+
+template <
+    typename Sequence,
+    typename Func,
+    EnableIfAcceptsOneArgument<Sequence, Func>* = nullptr>
+void for_each_range_impl(Sequence&& range, Func& func) {
+  // make a three argument adaptor for the function passed in that just ignores
+  // the second and third argument
+  auto three_arg_adaptor = [&func](auto&& ele, auto, auto) -> decltype(auto) {
+    return func(std::forward<decltype(ele)>(ele));
+  };
+  for_each_range_impl(std::forward<Sequence>(range), three_arg_adaptor);
+}
+
+/**
+ * Handlers for iteration
+ */
+/**
+ * The class provides a way to tell whether the function passed in to the
+ * algorithm returns an instance of LoopControl, if it does then the break-able
+ * implementation will be used.  If the function provided to the algorithm
+ * does not use the break API, then the basic no break, 0 overhead
+ * implementation will be used
+ */
+template <typename Seq, typename F, typename = void>
+struct ForEachTupleImpl {
+  template <typename Sequence, typename Func, std::size_t... Indices>
+  static void
+  impl(Sequence&& seq, Func& func, std::index_sequence<Indices...>) {
+    // unroll the loop in an initializer list construction parameter expansion
+    // pack
+    static_cast<void>(std::initializer_list<int>{
+        (func(
+             Get<Indices, Sequence>::impl(std::forward<Sequence>(seq)),
+             std::integral_constant<std::size_t, Indices>{}),
+         0)...});
+  }
+};
+template <typename Seq, typename F>
+struct ForEachTupleImpl<Seq, F, EnableIfBreaksTuple<Seq, F>> {
+  template <typename Sequence, typename Func, std::size_t... Indices>
+  static void
+  impl(Sequence&& seq, Func& func, std::index_sequence<Indices...>) {
+    // unroll the loop in an initializer list construction parameter expansion
+    // pack
+    LoopControl break_or_not = LoopControl::CONTINUE;
+
+    // cast to void to ignore the result, use the initialzer list constructor
+    // to do the loop execution, the ternary conditional will decide whether
+    // or not to evaluate the result
+    static_cast<void>(std::initializer_list<int>{
+        (((break_or_not == loop_continue)
+              ? (break_or_not = func(
+                     Get<Indices, Sequence>::impl(std::forward<Sequence>(seq)),
+                     std::integral_constant<std::size_t, Indices>{}))
+              : (loop_continue)),
+         0)...});
+  }
+};
+
+/**
+ * The two top level compile time loop iteration functions handle the dispatch
+ * based on the number of arguments the passed in function can be passed, if 2
+ * arguments can be passed then the implementation dispatches work further to
+ * the implementation classes above.  If not then an adaptor is constructed
+ * which is passed on to the 2 argument specialization, which then in turn
+ * forwards implementation to the implementation classes above
+ */
+template <
+    typename Sequence,
+    typename Func,
+    EnableIfAcceptsTwoArguments<Sequence, Func>* = nullptr>
+void for_each_tuple_impl(Sequence&& seq, Func& func) {
+  // pass the length as an index sequence to the implementation as an
+  // optimization over manual template "tail recursion" unrolling
+  constexpr auto length =
+      std::tuple_size<typename std::decay<Sequence>::type>::value;
+  ForEachTupleImpl<Sequence, Func>::impl(
+      std::forward<Sequence>(seq), func, std::make_index_sequence<length>{});
+}
+template <
+    typename Sequence,
+    typename Func,
+    EnableIfAcceptsOneArgument<Sequence, Func>* = nullptr>
+void for_each_tuple_impl(Sequence&& seq, Func& func) {
+  // make an adaptor for the function passed in, in case it can only be passed
+  // on argument
+  auto two_arg_adaptor = [&func](auto&& ele, auto) -> decltype(auto) {
+    return func(std::forward<decltype(ele)>(ele));
+  };
+  for_each_tuple_impl(std::forward<Sequence>(seq), two_arg_adaptor);
+}
+
+/**
+ * Top level handlers for the for_each loop, the basic specialization handles
+ * ranges and the specialized version handles compile time ranges (tuple like)
+ *
+ * This implies that if a range is a compile time range, its compile time
+ * get<> API (whether through a member function or through a ADL looked up
+ * method) will be used in preference over iterators
+ */
+template <typename R, typename = void>
+struct ForEachImpl {
+  template <typename Sequence, typename Func>
+  static void impl(Sequence&& range, Func& func) {
+    for_each_tuple_impl(std::forward<Sequence>(range), func);
+  }
+};
+template <typename R>
+struct ForEachImpl<R, EnableIfRange<R>> {
+  template <typename Sequence, typename Func>
+  static void impl(Sequence&& range, Func& func) {
+    for_each_range_impl(std::forward<Sequence>(range), func);
+  }
+};
+
+template <typename S, typename I, typename = void>
+struct FetchIteratorIndexImpl {
+  template <typename Sequence, typename Index>
+  static decltype(auto) impl(Sequence&& sequence, Index&& index) {
+    return std::forward<Sequence>(sequence)[std::forward<Index>(index)];
+  }
+};
+template <typename S, typename I>
+struct FetchIteratorIndexImpl<S, I, EnableIfRandomAccessIterators<S>> {
+  template <typename Sequence, typename Index>
+  static decltype(auto) impl(Sequence&& sequence, Index index) {
+    return *(adl::adl_begin(std::forward<Sequence>(sequence)) + index);
+  }
+};
+template <typename S, typename = void>
+struct FetchImpl {
+  template <typename Sequence, typename Index>
+  static decltype(auto) impl(Sequence&& sequence, Index index) {
+    return Get<static_cast<std::size_t>(index), Sequence>::impl(
+        std::forward<Sequence>(sequence));
+  }
+};
+template <typename S>
+struct FetchImpl<S, EnableIfRange<S>> {
+  template <typename Sequence, typename Index>
+  static decltype(auto) impl(Sequence&& sequence, Index&& index) {
+    return FetchIteratorIndexImpl<Sequence, Index>::impl(
+        std::forward<Sequence>(sequence), std::forward<Index>(index));
+  }
+};
+
+} // namespace for_each_detail
+
+template <typename Sequence, typename Func>
+FOLLY_CPP14_CONSTEXPR Func for_each(Sequence&& range, Func func) {
+  for_each_detail::ForEachImpl<typename std::decay<Sequence>::type>::impl(
+      std::forward<Sequence>(range), func);
+  return func;
+}
+
+template <typename Sequence, typename Index>
+FOLLY_CPP14_CONSTEXPR decltype(auto) fetch(Sequence&& sequence, Index&& index) {
+  return for_each_detail::FetchImpl<Sequence>::impl(
+      std::forward<Sequence>(sequence), std::forward<Index>(index));
+}
+
+} // namespace folly
diff --git a/folly/container/Foreach.h b/folly/container/Foreach.h
new file mode 100644 (file)
index 0000000..f24854d
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/Portability.h>
+#include <folly/Preprocessor.h>
+
+#include <type_traits>
+
+namespace folly {
+
+/**
+ * @function for_each
+ *
+ * folly::for_each is a generalized iteration algorithm.  Example:
+ *
+ *  auto one = std::make_tuple(1, 2, 3);
+ *  auto two = std::vector<int>{1, 2, 3};
+ *  auto func = [](auto element, auto index) {
+ *    cout << index << " : " << element << endl;
+ *  };
+ *  folly::for_each(one, func);
+ *  folly::for_each(two, func);
+ *
+ * The for_each function allows iteration through sequences, these
+ * can either be runtime sequences (i.e. entities for which std::begin and
+ * std::end work) or compile time sequences (as deemed by the presence of
+ * std::tuple_length<>, get<> (ADL resolved) functions)
+ *
+ * The function is made to provide a convenient library based alternative to
+ * the proposal p0589r0, which aims to generalize the range based for loop
+ * even further to work with compile time sequences.
+ *
+ * A drawback of using range based for loops is that sometimes you do not have
+ * access to the index within the range.  This provides easy access to that,
+ * even with compile time sequences.
+ *
+ * And breaking out is easy
+ *
+ *  auto range_one = std::vector<int>{1, 2, 3};
+ *  auto range_two = std::make_tuple(1, 2, 3);
+ *  auto func = [](auto ele, auto index) {
+ *    cout << "Element at index " << index << " : " << ele;
+ *    if (index == 1) {
+ *      return folly::loop_break;
+ *    }
+ *    return folly::loop_continue;
+ *  };
+ *  folly_for_each(range_one, func);
+ *  folly_for_each(range_two, func);
+ *
+ * A simple use case would be when using futures, if the user was doing calls
+ * to n servers then they would accept the callback with the futures like this
+ *
+ *  auto vec = std::vector<std::future<int>>{request_one(), ...};
+ *  when_all(vec.begin(), vec.end()).then([](auto futures) {
+ *    folly::for_each(futures, [](auto& fut) { ... });
+ *  });
+ *
+ * Now when this code switches to use tuples instead of the runtime
+ * std::vector, then the loop does not need to change, the code will still
+ * work just fine
+ *
+ *  when_all(future_one, future_two, future_three).then([](auto futures) {
+ *    folly::for_each(futures, [](auto& fut) { ... });
+ *  });
+ */
+template <typename Range, typename Func>
+FOLLY_CPP14_CONSTEXPR Func for_each(Range&& range, Func func);
+
+/**
+ * The user should return loop_break and loop_continue if they want to iterate
+ * in such a way that they can preemptively stop the loop and break out when
+ * certain conditions are met
+ */
+namespace for_each_detail {
+enum class LoopControl : bool { BREAK, CONTINUE };
+} // namespace for_each_detail
+
+constexpr auto loop_break = for_each_detail::LoopControl::BREAK;
+constexpr auto loop_continue = for_each_detail::LoopControl::CONTINUE;
+
+/**
+ * Utility method to help access elements of a sequence with one uniform
+ * interface
+ *
+ * This can be useful for example when you are looping through a sequence and
+ * want to modify another sequence based on the information in the current
+ * sequence
+ *
+ *  auto range_one = std::make_tuple(1, 2, 3);
+ *  auto range_two = std::make_tuple(4, 5, 6);
+ *  folly::for_each(range_one, [&range_two](auto ele, auto index) {
+ *    folly::fetch(range_two, index) = ele;
+ *  });
+ *
+ * For non-tuple like ranges, this works by first trying to use the iterator
+ * class if the iterator has been marked to be a random access iterator.  This
+ * should be inspectable via the std::iterator_traits traits class.  If the
+ * iterator class is not present or is not a random access iterator then the
+ * implementation falls back to trying to use the indexing operator
+ * (operator[]) to fetch the required element
+ */
+template <typename Sequence, typename Index>
+FOLLY_CPP14_CONSTEXPR decltype(auto) fetch(Sequence&& sequence, Index&& index);
+
+} // namespace folly
+
+/**
+ * Everything below this is deprecated.  Use the folly::for_each algorithm above
+ * instead
+ */
+/*
+ * Form a local variable name from "FOR_EACH_" x __LINE__, so that
+ * FOR_EACH can be nested without creating shadowed declarations.
+ */
+#define _FE_ANON(x) FB_CONCATENATE(FOR_EACH_, FB_CONCATENATE(x, __LINE__))
+
+/*
+ * If you just want the element values, please use:
+ *
+ *    for (auto&& element : collection)
+ *
+ * If you need access to the iterators please write an explicit iterator loop
+ */
+#define FOR_EACH(i, c)                                  \
+  if (bool _FE_ANON(s1_) = false) {} else               \
+    for (auto && _FE_ANON(s2_) = (c);                   \
+         !_FE_ANON(s1_); _FE_ANON(s1_) = true)          \
+      for (auto i = _FE_ANON(s2_).begin();              \
+           i != _FE_ANON(s2_).end(); ++i)
+
+/*
+ * If you just want the element values, please use this (ranges-v3) construct:
+ *
+ *    for (auto&& element : collection | view::reverse)
+ *
+ * If you need access to the iterators please write an explicit iterator loop
+ */
+#define FOR_EACH_R(i, c)                                \
+  if (bool _FE_ANON(s1_) = false) {} else               \
+    for (auto && _FE_ANON(s2_) = (c);                   \
+         !_FE_ANON(s1_); _FE_ANON(s1_) = true)          \
+      for (auto i = _FE_ANON(s2_).rbegin();             \
+           i != _FE_ANON(s2_).rend(); ++i)
+
+/*
+ * If you just want the element values, please use this (ranges-v3) construct:
+ *
+ *    for (auto&& element : collection | view::zip(view::ints))
+ *
+ * If you need access to the iterators please write an explicit iterator loop
+ * and use a counter variable
+ */
+#define FOR_EACH_ENUMERATE(count, i, c)                                \
+  if (bool _FE_ANON(s1_) = false) {} else                            \
+    for (auto && FOR_EACH_state2 = (c);                                \
+         !_FE_ANON(s1_); _FE_ANON(s1_) = true)                     \
+      if (size_t _FE_ANON(n1_) = 0) {} else                            \
+        if (const size_t& count = _FE_ANON(n1_)) {} else               \
+          for (auto i = FOR_EACH_state2.begin();                       \
+               i != FOR_EACH_state2.end(); ++_FE_ANON(n1_), ++i)
+/**
+ * If you just want the keys, please use this (ranges-v3) construct:
+ *
+ *    for (auto&& element : collection | view::keys)
+ *
+ * If you just want the values, please use this (ranges-v3) construct:
+ *
+ *    for (auto&& element : collection | view::values)
+ *
+ * If you need to see both, use:
+ *
+ *    for (auto&& element : collection) {
+ *      auto const& key = element.first;
+ *      auto& value = element.second;
+ *      ......
+ *    }
+ *
+ */
+#define FOR_EACH_KV(k, v, c)                                  \
+  if (unsigned int _FE_ANON(s1_) = 0) {} else                 \
+    for (auto && _FE_ANON(s2_) = (c);                         \
+         !_FE_ANON(s1_); _FE_ANON(s1_) = 1)                   \
+      for (auto _FE_ANON(s3_) = _FE_ANON(s2_).begin();        \
+           _FE_ANON(s3_) != _FE_ANON(s2_).end();              \
+           _FE_ANON(s1_) == 2                                 \
+             ? ((_FE_ANON(s1_) = 0), ++_FE_ANON(s3_))         \
+             : (_FE_ANON(s3_) = _FE_ANON(s2_).end()))         \
+        for (auto &k = _FE_ANON(s3_)->first;                  \
+             !_FE_ANON(s1_); ++_FE_ANON(s1_))                 \
+          for (auto &v = _FE_ANON(s3_)->second;               \
+               !_FE_ANON(s1_); ++_FE_ANON(s1_))
+
+namespace folly { namespace detail {
+
+// Boost 1.48 lacks has_less, we emulate a subset of it here.
+template <typename T, typename U>
+class HasLess {
+  struct BiggerThanChar { char unused[2]; };
+  template <typename C, typename D> static char test(decltype(C() < D())*);
+  template <typename, typename> static BiggerThanChar test(...);
+
+ public:
+  enum { value = sizeof(test<T, U>(nullptr)) == 1 };
+};
+
+/**
+ * notThereYet helps the FOR_EACH_RANGE macro by opportunistically
+ * using "<" instead of "!=" whenever available when checking for loop
+ * termination. This makes e.g. examples such as FOR_EACH_RANGE (i,
+ * 10, 5) execute zero iterations instead of looping virtually
+ * forever. At the same time, some iterator types define "!=" but not
+ * "<". The notThereYet function will dispatch differently for those.
+ *
+ * Below is the correct implementation of notThereYet. It is disabled
+ * because of a bug in Boost 1.46: The filesystem::path::iterator
+ * defines operator< (via boost::iterator_facade), but that in turn
+ * uses distance_to which is undefined for that particular
+ * iterator. So HasLess (defined above) identifies
+ * boost::filesystem::path as properly comparable with <, but in fact
+ * attempting to do so will yield a compile-time error.
+ *
+ * The else branch (active) contains a conservative
+ * implementation.
+ */
+
+#if 0
+
+template <class T, class U>
+typename std::enable_if<HasLess<T, U>::value, bool>::type
+notThereYet(T& iter, const U& end) {
+  return iter < end;
+}
+
+template <class T, class U>
+typename std::enable_if<!HasLess<T, U>::value, bool>::type
+notThereYet(T& iter, const U& end) {
+  return iter != end;
+}
+
+#else
+
+template <class T, class U>
+typename std::enable_if<
+  (std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) ||
+  (std::is_pointer<T>::value && std::is_pointer<U>::value),
+  bool>::type
+notThereYet(T& iter, const U& end) {
+  return iter < end;
+}
+
+template <class T, class U>
+typename std::enable_if<
+  !(
+    (std::is_arithmetic<T>::value && std::is_arithmetic<U>::value) ||
+    (std::is_pointer<T>::value && std::is_pointer<U>::value)
+  ),
+  bool>::type
+notThereYet(T& iter, const U& end) {
+  return iter != end;
+}
+
+#endif
+
+
+/**
+ * downTo is similar to notThereYet, but in reverse - it helps the
+ * FOR_EACH_RANGE_R macro.
+ */
+template <class T, class U>
+typename std::enable_if<HasLess<U, T>::value, bool>::type
+downTo(T& iter, const U& begin) {
+  return begin < iter--;
+}
+
+template <class T, class U>
+typename std::enable_if<!HasLess<U, T>::value, bool>::type
+downTo(T& iter, const U& begin) {
+  if (iter == begin) {
+    return false;
+  }
+  --iter;
+  return true;
+}
+
+} }
+
+/*
+ * Look at the Ranges-v3 views and you'll probably find an easier way to build
+ * the view you want but the equivalent is roughly:
+ *
+ *    for (auto& element : make_iterator_range(begin, end))
+ */
+#define FOR_EACH_RANGE(i, begin, end)           \
+  for (auto i = (true ? (begin) : (end));       \
+       ::folly::detail::notThereYet(i, (end));  \
+       ++i)
+
+/*
+ * Look at the Ranges-v3 views and you'll probably find an easier way to build
+ * the view you want but the equivalent is roughly:
+ *
+ *    for (auto& element : make_iterator_range(begin, end) | view::reverse)
+ */
+#define FOR_EACH_RANGE_R(i, begin, end) \
+  for (auto i = (false ? (begin) : (end)); ::folly::detail::downTo(i, (begin));)
+
+#include <folly/container/Foreach-inl.h>
diff --git a/folly/container/Iterator.h b/folly/container/Iterator.h
new file mode 100644 (file)
index 0000000..c0b7b12
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include <folly/Functional.h>
+
+namespace folly {
+
+/**
+ * Argument tuple for variadic emplace/constructor calls. Stores arguments by
+ * (decayed) value. Restores original argument types with reference qualifiers
+ * and adornments at unpack time to emulate perfect forwarding.
+ *
+ * Uses inheritance instead of a type alias to std::tuple so that emplace
+ * iterators with implicit unpacking disabled can distinguish between
+ * emplace_args and std::tuple parameters.
+ *
+ * @seealso folly::make_emplace_args
+ * @seealso folly::get_emplace_arg
+ */
+template <typename... Args>
+struct emplace_args : public std::tuple<std::decay_t<Args>...> {
+  using storage_type = std::tuple<std::decay_t<Args>...>;
+  using storage_type::storage_type;
+};
+
+/**
+ * Pack arguments in a tuple for assignment to a folly::emplace_iterator,
+ * folly::front_emplace_iterator, or folly::back_emplace_iterator. The
+ * iterator's operator= will unpack the tuple and pass the unpacked arguments
+ * to the container's emplace function, which in turn forwards the arguments to
+ * the (multi-argument) constructor of the target class.
+ *
+ * Argument tuples generated with folly::make_emplace_args will be unpacked
+ * before being passed to the container's emplace function, even for iterators
+ * where implicit_unpack is set to false (so they will not implicitly unpack
+ * std::pair or std::tuple arguments to operator=).
+ *
+ * Arguments are copied (lvalues) or moved (rvalues). To avoid copies and moves,
+ * wrap references using std::ref(), std::cref(), and folly::rref(). Beware of
+ * dangling references, especially references to temporary objects created with
+ * folly::rref().
+ *
+ * Note that an argument pack created with folly::make_emplace_args is different
+ * from an argument pack created with std::make_pair or std::make_tuple.
+ * Specifically, passing a std::pair&& or std::tuple&& to an emplace iterator's
+ * operator= will pass rvalue references to all fields of that tuple to the
+ * container's emplace function, while passing an emplace_args&& to operator=
+ * will cast those field references to the exact argument types as passed to
+ * folly::make_emplace_args previously. If all arguments have been wrapped by
+ * std::reference_wrappers or folly::rvalue_reference_wrappers, the result will
+ * be the same as if the container's emplace function had been called directly
+ * (perfect forwarding), with no temporary copies of the arguments.
+ *
+ * @seealso folly::rref
+ *
+ * @example
+ *   class Widget { Widget(int, int); };
+ *   std::vector<Widget> makeWidgets(const std::vector<int>& in) {
+ *     std::vector<Widget> out;
+ *     std::transform(
+ *         in.begin(),
+ *         in.end(),
+ *         folly::back_emplacer(out),
+ *         [](int i) { return folly::make_emplace_args(i, i); });
+ *     return out;
+ *   }
+ */
+template <typename... Args>
+emplace_args<Args...> make_emplace_args(Args&&... args) noexcept(
+    noexcept(emplace_args<Args...>(std::forward<Args>(args)...))) {
+  return emplace_args<Args...>(std::forward<Args>(args)...);
+}
+
+namespace detail {
+template <typename Arg>
+decltype(auto) unwrap_emplace_arg(Arg&& arg) noexcept {
+  return std::forward<Arg>(arg);
+}
+template <typename Arg>
+decltype(auto) unwrap_emplace_arg(std::reference_wrapper<Arg> arg) noexcept {
+  return arg.get();
+}
+template <typename Arg>
+decltype(auto) unwrap_emplace_arg(
+    folly::rvalue_reference_wrapper<Arg> arg) noexcept {
+  return std::move(arg).get();
+}
+}
+
+/**
+ * Getter function for unpacking a single emplace argument.
+ *
+ * Calling get_emplace_arg on an emplace_args rvalue reference results in
+ * perfect forwarding of the original input types. A special case are
+ * std::reference_wrapper and folly::rvalue_reference_wrapper objects within
+ * folly::emplace_args. These are also unwrapped so that the bare reference is
+ * returned.
+ *
+ * std::get is not a customization point in the standard library, so the
+ * cleanest solution was to define our own getter function.
+ */
+template <size_t I, typename... Args>
+decltype(auto) get_emplace_arg(emplace_args<Args...>&& args) noexcept {
+  using Out = std::tuple<Args...>;
+  return detail::unwrap_emplace_arg(
+      std::forward<std::tuple_element_t<I, Out>>(std::get<I>(args)));
+}
+template <size_t I, typename... Args>
+decltype(auto) get_emplace_arg(emplace_args<Args...>& args) noexcept {
+  return detail::unwrap_emplace_arg(std::get<I>(args));
+}
+template <size_t I, typename... Args>
+decltype(auto) get_emplace_arg(const emplace_args<Args...>& args) noexcept {
+  return detail::unwrap_emplace_arg(std::get<I>(args));
+}
+template <size_t I, typename Args>
+decltype(auto) get_emplace_arg(Args&& args) noexcept {
+  return std::get<I>(std::move(args));
+}
+template <size_t I, typename Args>
+decltype(auto) get_emplace_arg(Args& args) noexcept {
+  return std::get<I>(args);
+}
+template <size_t I, typename Args>
+decltype(auto) get_emplace_arg(const Args& args) noexcept {
+  return std::get<I>(args);
+}
+
+namespace detail {
+/**
+ * Emplace implementation class for folly::emplace_iterator.
+ */
+template <typename Container>
+struct Emplace {
+  Emplace(Container& c, typename Container::iterator i)
+      : container(std::addressof(c)), iter(std::move(i)) {}
+  template <typename... Args>
+  void emplace(Args&&... args) {
+    iter = container->emplace(iter, std::forward<Args>(args)...);
+    ++iter;
+  }
+  Container* container;
+  typename Container::iterator iter;
+};
+
+/**
+ * Emplace implementation class for folly::hint_emplace_iterator.
+ */
+template <typename Container>
+struct EmplaceHint {
+  EmplaceHint(Container& c, typename Container::iterator i)
+      : container(std::addressof(c)), iter(std::move(i)) {}
+  template <typename... Args>
+  void emplace(Args&&... args) {
+    iter = container->emplace_hint(iter, std::forward<Args>(args)...);
+    ++iter;
+  }
+  Container* container;
+  typename Container::iterator iter;
+};
+
+/**
+ * Emplace implementation class for folly::front_emplace_iterator.
+ */
+template <typename Container>
+struct EmplaceFront {
+  explicit EmplaceFront(Container& c) : container(std::addressof(c)) {}
+  template <typename... Args>
+  void emplace(Args&&... args) {
+    container->emplace_front(std::forward<Args>(args)...);
+  }
+  Container* container;
+};
+
+/**
+ * Emplace implementation class for folly::back_emplace_iterator.
+ */
+template <typename Container>
+struct EmplaceBack {
+  explicit EmplaceBack(Container& c) : container(std::addressof(c)) {}
+  template <typename... Args>
+  void emplace(Args&&... args) {
+    container->emplace_back(std::forward<Args>(args)...);
+  }
+  Container* container;
+};
+
+/**
+ * Generic base class and implementation of all emplace iterator classes.
+ *
+ * Uses the curiously recurring template pattern (CRTP) to cast `this*` to
+ * `Derived*`; i.e., to implement covariant return types in a generic manner.
+ */
+template <typename Derived, typename EmplaceImpl, bool implicit_unpack>
+class emplace_iterator_base;
+
+/**
+ * Partial specialization of emplace_iterator_base with implicit unpacking
+ * disabled.
+ */
+template <typename Derived, typename EmplaceImpl>
+class emplace_iterator_base<Derived, EmplaceImpl, false>
+    : protected EmplaceImpl /* protected implementation inheritance */ {
+ public:
+  // Iterator traits.
+  using iterator_category = std::output_iterator_tag;
+  using value_type = void;
+  using difference_type = void;
+  using pointer = void;
+  using reference = void;
+  using container_type =
+      std::remove_reference_t<decltype(*EmplaceImpl::container)>;
+
+  using EmplaceImpl::EmplaceImpl;
+
+  /**
+   * Canonical output operator. Forwards single argument straight to container's
+   * emplace function.
+   */
+  template <typename T>
+  Derived& operator=(T&& arg) {
+    this->emplace(std::forward<T>(arg));
+    return static_cast<Derived&>(*this);
+  }
+
+  /**
+   * Special output operator for packed arguments. Unpacks args and performs
+   * variadic call to container's emplace function.
+   */
+  template <typename... Args>
+  Derived& operator=(emplace_args<Args...>& args) {
+    return unpackAndEmplace(args, std::index_sequence_for<Args...>{});
+  }
+  template <typename... Args>
+  Derived& operator=(const emplace_args<Args...>& args) {
+    return unpackAndEmplace(args, std::index_sequence_for<Args...>{});
+  }
+  template <typename... Args>
+  Derived& operator=(emplace_args<Args...>&& args) {
+    return unpackAndEmplace(
+        std::move(args), std::index_sequence_for<Args...>{});
+  }
+
+  // No-ops.
+  Derived& operator*() {
+    return static_cast<Derived&>(*this);
+  }
+  Derived& operator++() {
+    return static_cast<Derived&>(*this);
+  }
+  Derived& operator++(int) {
+    return static_cast<Derived&>(*this);
+  }
+
+  // We need all of these explicit defaults because the custom operator=
+  // overloads disable implicit generation of these functions.
+  emplace_iterator_base(const emplace_iterator_base&) = default;
+  emplace_iterator_base(emplace_iterator_base&&) noexcept = default;
+  emplace_iterator_base& operator=(emplace_iterator_base&) = default;
+  emplace_iterator_base& operator=(const emplace_iterator_base&) = default;
+  emplace_iterator_base& operator=(emplace_iterator_base&&) noexcept = default;
+
+ protected:
+  template <typename Args, std::size_t... I>
+  Derived& unpackAndEmplace(Args& args, std::index_sequence<I...>) {
+    this->emplace(get_emplace_arg<I>(args)...);
+    return static_cast<Derived&>(*this);
+  }
+  template <typename Args, std::size_t... I>
+  Derived& unpackAndEmplace(const Args& args, std::index_sequence<I...>) {
+    this->emplace(get_emplace_arg<I>(args)...);
+    return static_cast<Derived&>(*this);
+  }
+  template <typename Args, std::size_t... I>
+  Derived& unpackAndEmplace(Args&& args, std::index_sequence<I...>) {
+    this->emplace(get_emplace_arg<I>(std::move(args))...);
+    return static_cast<Derived&>(*this);
+  }
+};
+
+/**
+ * Partial specialization of emplace_iterator_base with implicit unpacking
+ * enabled.
+ *
+ * Uses inheritance rather than SFINAE. operator= requires a single argument,
+ * which makes it very tricky to use std::enable_if or similar.
+ */
+template <typename Derived, typename EmplaceImpl>
+class emplace_iterator_base<Derived, EmplaceImpl, true>
+    : public emplace_iterator_base<Derived, EmplaceImpl, false> {
+ private:
+  using Base = emplace_iterator_base<Derived, EmplaceImpl, false>;
+
+ public:
+  using Base::Base;
+  using Base::operator=;
+
+  /**
+   * Special output operator for arguments packed into a std::pair. Unpacks
+   * the pair and performs variadic call to container's emplace function.
+   */
+  template <typename... Args>
+  Derived& operator=(std::pair<Args...>& args) {
+    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});
+  }
+  template <typename... Args>
+  Derived& operator=(const std::pair<Args...>& args) {
+    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});
+  }
+  template <typename... Args>
+  Derived& operator=(std::pair<Args...>&& args) {
+    return this->unpackAndEmplace(
+        std::move(args), std::index_sequence_for<Args...>{});
+  }
+
+  /**
+   * Special output operator for arguments packed into a std::tuple. Unpacks
+   * the tuple and performs variadic call to container's emplace function.
+   */
+  template <typename... Args>
+  Derived& operator=(std::tuple<Args...>& args) {
+    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});
+  }
+  template <typename... Args>
+  Derived& operator=(const std::tuple<Args...>& args) {
+    return this->unpackAndEmplace(args, std::index_sequence_for<Args...>{});
+  }
+  template <typename... Args>
+  Derived& operator=(std::tuple<Args...>&& args) {
+    return this->unpackAndEmplace(
+        std::move(args), std::index_sequence_for<Args...>{});
+  }
+
+  // We need all of these explicit defaults because the custom operator=
+  // overloads disable implicit generation of these functions.
+  emplace_iterator_base(const emplace_iterator_base&) = default;
+  emplace_iterator_base(emplace_iterator_base&&) noexcept = default;
+  emplace_iterator_base& operator=(emplace_iterator_base&) = default;
+  emplace_iterator_base& operator=(const emplace_iterator_base&) = default;
+  emplace_iterator_base& operator=(emplace_iterator_base&&) noexcept = default;
+};
+
+/**
+ * Concrete instantiation of emplace_iterator_base. All emplace iterator
+ * classes; folly::emplace_iterator, folly::hint_emplace_iterator,
+ * folly::front_emplace_iterator, and folly::back_emplace_iterator; are just
+ * type aliases of this class.
+ *
+ * It is not possible to alias emplace_iterator_base directly, because type
+ * aliases cannot be used for CRTP.
+ */
+template <
+    template <typename> class EmplaceImplT,
+    typename Container,
+    bool implicit_unpack>
+class emplace_iterator_impl
+    : public emplace_iterator_base<
+          emplace_iterator_impl<EmplaceImplT, Container, implicit_unpack>,
+          EmplaceImplT<Container>,
+          implicit_unpack> {
+ private:
+  using Base = emplace_iterator_base<
+      emplace_iterator_impl,
+      EmplaceImplT<Container>,
+      implicit_unpack>;
+
+ public:
+  using Base::Base;
+  using Base::operator=;
+
+  // We need all of these explicit defaults because the custom operator=
+  // overloads disable implicit generation of these functions.
+  emplace_iterator_impl(const emplace_iterator_impl&) = default;
+  emplace_iterator_impl(emplace_iterator_impl&&) noexcept = default;
+  emplace_iterator_impl& operator=(emplace_iterator_impl&) = default;
+  emplace_iterator_impl& operator=(const emplace_iterator_impl&) = default;
+  emplace_iterator_impl& operator=(emplace_iterator_impl&&) noexcept = default;
+};
+} // namespace detail
+
+/**
+ * Behaves just like std::insert_iterator except that it calls emplace()
+ * instead of insert(). Uses perfect forwarding.
+ */
+template <typename Container, bool implicit_unpack = true>
+using emplace_iterator =
+    detail::emplace_iterator_impl<detail::Emplace, Container, implicit_unpack>;
+
+/**
+ * Behaves just like std::insert_iterator except that it calls emplace_hint()
+ * instead of insert(). Uses perfect forwarding.
+ */
+template <typename Container, bool implicit_unpack = true>
+using hint_emplace_iterator = detail::
+    emplace_iterator_impl<detail::EmplaceHint, Container, implicit_unpack>;
+
+/**
+ * Behaves just like std::front_insert_iterator except that it calls
+ * emplace_front() instead of insert(). Uses perfect forwarding.
+ */
+template <typename Container, bool implicit_unpack = true>
+using front_emplace_iterator = detail::
+    emplace_iterator_impl<detail::EmplaceFront, Container, implicit_unpack>;
+
+/**
+ * Behaves just like std::back_insert_iterator except that it calls
+ * emplace_back() instead of insert(). Uses perfect forwarding.
+ */
+template <typename Container, bool implicit_unpack = true>
+using back_emplace_iterator = detail::
+    emplace_iterator_impl<detail::EmplaceBack, Container, implicit_unpack>;
+
+/**
+ * Convenience function to construct a folly::emplace_iterator, analogous to
+ * std::inserter().
+ *
+ * Setting implicit_unpack to false will disable implicit unpacking of
+ * single std::pair and std::tuple arguments to the iterator's operator=. That
+ * may be desirable in case of constructors that expect a std::pair or
+ * std::tuple argument.
+ */
+template <bool implicit_unpack = true, typename Container>
+emplace_iterator<Container, implicit_unpack> emplacer(
+    Container& c,
+    typename Container::iterator i) {
+  return emplace_iterator<Container, implicit_unpack>(c, std::move(i));
+}
+
+/**
+ * Convenience function to construct a folly::hint_emplace_iterator, analogous
+ * to std::inserter().
+ *
+ * Setting implicit_unpack to false will disable implicit unpacking of
+ * single std::pair and std::tuple arguments to the iterator's operator=. That
+ * may be desirable in case of constructors that expect a std::pair or
+ * std::tuple argument.
+ */
+template <bool implicit_unpack = true, typename Container>
+hint_emplace_iterator<Container, implicit_unpack> hint_emplacer(
+    Container& c,
+    typename Container::iterator i) {
+  return hint_emplace_iterator<Container, implicit_unpack>(c, std::move(i));
+}
+
+/**
+ * Convenience function to construct a folly::front_emplace_iterator, analogous
+ * to std::front_inserter().
+ *
+ * Setting implicit_unpack to false will disable implicit unpacking of
+ * single std::pair and std::tuple arguments to the iterator's operator=. That
+ * may be desirable in case of constructors that expect a std::pair or
+ * std::tuple argument.
+ */
+template <bool implicit_unpack = true, typename Container>
+front_emplace_iterator<Container, implicit_unpack> front_emplacer(
+    Container& c) {
+  return front_emplace_iterator<Container, implicit_unpack>(c);
+}
+
+/**
+ * Convenience function to construct a folly::back_emplace_iterator, analogous
+ * to std::back_inserter().
+ *
+ * Setting implicit_unpack to false will disable implicit unpacking of
+ * single std::pair and std::tuple arguments to the iterator's operator=. That
+ * may be desirable in case of constructors that expect a std::pair or
+ * std::tuple argument.
+ */
+template <bool implicit_unpack = true, typename Container>
+back_emplace_iterator<Container, implicit_unpack> back_emplacer(Container& c) {
+  return back_emplace_iterator<Container, implicit_unpack>(c);
+}
+}
diff --git a/folly/container/Merge.h b/folly/container/Merge.h
new file mode 100644 (file)
index 0000000..1979ae7
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * folly::merge() is an implementation of std::merge with one additonal
+ * guarantee: if the input ranges overlap, the order that values *from the two
+ * different ranges* appear in the output is well defined (std::merge only
+ * guarantees relative ordering is maintained within a single input range).
+ * This semantic is very useful when the output container removes duplicates
+ * (such as std::map) to guarantee that elements from b override elements from
+ * a.
+ *
+ * ex. Let's say we have two vector<pair<int, int>> as input, and we are
+ * merging into a vector<pair<int, int>>. The comparator is returns true if the
+ * first argument has a lesser 'first' value in the pair.
+ *
+ * a = {{1, 1}, {2, 2}, {3, 3}};
+ * b = {{1, 2}, {2, 3}};
+ *
+ * folly::merge<...>(a.begin(), a.end(), b.begin(), b.end(), outputIter) is
+ * guaranteed to produce {{1, 1}, {1, 2}, {2, 2}, {2, 3}, {3, 3}}. That is,
+ * if comp(it_a, it_b) == comp(it_b, it_a) == false, we first insert the element
+ * from a.
+ */
+
+#pragma once
+
+#include <algorithm>
+
+namespace folly {
+
+template <class InputIt1, class InputIt2, class OutputIt, class Compare>
+OutputIt merge(InputIt1 first1, InputIt1 last1,
+               InputIt2 first2, InputIt2 last2,
+               OutputIt d_first, Compare comp) {
+  for (; first1 != last1; ++d_first) {
+    if (first2 == last2) {
+      return std::copy(first1, last1, d_first);
+    }
+    if (comp(*first2, *first1)) {
+      *d_first = *first2;
+      ++first2;
+    } else {
+      *d_first = *first1;
+      ++first1;
+    }
+  }
+  return std::copy(first2, last2, d_first);
+}
+
+template <class InputIt1, class InputIt2, class OutputIt>
+OutputIt merge(InputIt1 first1, InputIt1 last1,
+               InputIt2 first2, InputIt2 last2,
+               OutputIt d_first) {
+  for (; first1 != last1; ++d_first) {
+    if (first2 == last2) {
+      return std::copy(first1, last1, d_first);
+    }
+    if (*first2 < *first1) {
+      *d_first = *first2;
+      ++first2;
+    } else {
+      *d_first = *first1;
+      ++first1;
+    }
+  }
+  return std::copy(first2, last2, d_first);
+}
+
+}
diff --git a/folly/container/SparseByteSet.h b/folly/container/SparseByteSet.h
new file mode 100644 (file)
index 0000000..f6393b0
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <glog/logging.h>
+
+namespace folly {
+
+/***
+ *  SparseByteSet
+ *
+ *  A special-purpose data structure representing an insert-only set of bytes.
+ *  May have better performance than std::bitset<256>, depending on workload.
+ *
+ *  Operations:
+ *  - add(byte)
+ *  - contains(byte)
+ *
+ *  Performance:
+ *  - The entire capacity of the set is inline; the set never allocates.
+ *  - The constructor zeros only the first two bytes of the object.
+ *  - add and contains both run in constant time w.r.t. the size of the set.
+ *    Constant time - not amortized constant - and with small constant factor.
+ *
+ *  This data structure is ideal for on-stack use.
+ *
+ *  Aho, Hopcroft, and Ullman refer to this trick in "The Design and Analysis
+ *  of Computer Algorithms" (1974), but the best description is here:
+ *  http://research.swtch.com/sparse
+ *  http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.30.7319
+ */
+class SparseByteSet {
+ public:
+  //  There are this many possible values:
+  static constexpr uint16_t kCapacity = 256;
+
+  //  No init of byte-arrays required!
+  SparseByteSet() : size_(0) { }
+
+  /***
+   *  add(byte)
+   *
+   *  O(1), non-amortized.
+   */
+  inline bool add(uint8_t i) {
+    bool r = !contains(i);
+    if (r) {
+      DCHECK_LT(size_, kCapacity);
+      dense_[size_] = i;
+      sparse_[i] = uint8_t(size_);
+      size_++;
+    }
+    return r;
+  }
+
+  /***
+   *  contains(byte)
+   *
+   *  O(1), non-amortized.
+   */
+  inline bool contains(uint8_t i) const {
+    return sparse_[i] < size_ && dense_[sparse_[i]] == i;
+  }
+
+ private:
+  uint16_t size_;  // can't use uint8_t because it would overflow if all
+                   // possible values were inserted.
+  uint8_t sparse_[kCapacity];
+  uint8_t dense_[kCapacity];
+};
+
+}
diff --git a/folly/container/test/EnumerateTest.cpp b/folly/container/test/EnumerateTest.cpp
new file mode 100644 (file)
index 0000000..c96abca
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include <folly/Range.h>
+#include <folly/container/Enumerate.h>
+#include <folly/portability/GTest.h>
+
+TEST(Enumerate, Basic) {
+  std::vector<std::string> v = {"abc", "a", "ab"};
+  size_t i = 0;
+  for (auto it : folly::enumerate(v)) {
+    EXPECT_EQ(it.index, i);
+    EXPECT_EQ(*it, v[i]);
+    EXPECT_EQ(it->size(), v[i].size());
+
+    // Test mutability.
+    std::string newValue = "x";
+    *it = newValue;
+    EXPECT_EQ(newValue, v[i]);
+
+    ++i;
+  }
+
+  EXPECT_EQ(i, v.size());
+}
+
+TEST(Enumerate, Temporary) {
+  std::vector<std::string> v = {"abc", "a", "ab"};
+  size_t i = 0;
+  for (auto it : folly::enumerate(decltype(v)(v))) { // Copy v.
+    EXPECT_EQ(it.index, i);
+    EXPECT_EQ(*it, v[i]);
+    EXPECT_EQ(it->size(), v[i].size());
+    ++i;
+  }
+
+  EXPECT_EQ(i, v.size());
+};
+
+namespace {
+
+template <class T>
+struct IsConstReference {
+  constexpr static bool value = false;
+};
+template <class T>
+struct IsConstReference<const T&> {
+  constexpr static bool value = true;
+};
+
+} // namespace
+
+TEST(Enumerate, BasicConstArg) {
+  const std::vector<std::string> v = {"abc", "a", "ab"};
+  size_t i = 0;
+  for (auto it : folly::enumerate(v)) {
+    static_assert(
+        IsConstReference<decltype(*it)>::value, "Enumerating a const vector");
+    EXPECT_EQ(it.index, i);
+    EXPECT_EQ(*it, v[i]);
+    EXPECT_EQ(it->size(), v[i].size());
+    ++i;
+  }
+
+  EXPECT_EQ(i, v.size());
+}
+
+TEST(Enumerate, BasicConstEnumerate) {
+  std::vector<std::string> v = {"abc", "a", "ab"};
+  size_t i = 0;
+  for (const auto it : folly::enumerate(v)) {
+    static_assert(IsConstReference<decltype(*it)>::value, "Const enumeration");
+    EXPECT_EQ(it.index, i);
+    EXPECT_EQ(*it, v[i]);
+    EXPECT_EQ(it->size(), v[i].size());
+    ++i;
+  }
+
+  EXPECT_EQ(i, v.size());
+}
+
+TEST(Enumerate, TemporaryConstEnumerate) {
+  std::vector<std::string> v = {"abc", "a", "ab"};
+  size_t i = 0;
+  for (const auto it : folly::enumerate(decltype(v)(v))) { // Copy v.
+    static_assert(IsConstReference<decltype(*it)>::value, "Const enumeration");
+    EXPECT_EQ(it.index, i);
+    EXPECT_EQ(*it, v[i]);
+    EXPECT_EQ(it->size(), v[i].size());
+    ++i;
+  }
+
+  EXPECT_EQ(i, v.size());
+}
+
+TEST(Enumerate, RangeSupport) {
+  std::vector<std::string> v = {"abc", "a", "ab"};
+  size_t i = 0;
+  for (const auto it : folly::enumerate(folly::range(v))) {
+    EXPECT_EQ(it.index, i);
+    EXPECT_EQ(*it, v[i]);
+    EXPECT_EQ(it->size(), v[i].size());
+    ++i;
+  }
+
+  EXPECT_EQ(i, v.size());
+}
+
+TEST(Enumerate, EmptyRange) {
+  std::vector<std::string> v;
+  for (auto it : folly::enumerate(v)) {
+    (void)it; // Silence warnings.
+    ADD_FAILURE();
+  }
+}
+
+class CStringRange {
+  const char* cstr;
+
+ public:
+  struct Sentinel {};
+
+  explicit CStringRange(const char* cstr) : cstr(cstr) {}
+
+  const char* begin() const {
+    return cstr;
+  }
+  Sentinel end() const {
+    return Sentinel{};
+  }
+};
+
+bool operator==(const char* c, CStringRange::Sentinel) {
+  return *c == 0;
+}
+
+TEST(Enumerate, Cpp17Support) {
+  std::array<char, 5> test = {"test"};
+  // Can't use range based for loop until C++17, so test manually
+  // Equivalent to:
+  // for (const auto it : folly::enumerate(CStringRange{test.data()})) { ... }
+  {
+    auto&& enumerate = folly::enumerate(CStringRange{test.data()});
+    auto begin = enumerate.begin();
+    auto end = enumerate.end();
+    for (; begin != end; ++begin) {
+      const auto it = *begin;
+
+      ASSERT_LT(it.index, test.size());
+      EXPECT_EQ(*it, test[it.index]);
+    }
+  }
+}
diff --git a/folly/container/test/EvictingCacheMapTest.cpp b/folly/container/test/EvictingCacheMapTest.cpp
new file mode 100644 (file)
index 0000000..2a81d09
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <set>
+
+#include <folly/container/EvictingCacheMap.h>
+#include <folly/portability/GTest.h>
+
+using namespace folly;
+
+TEST(EvictingCacheMap, SanityTest) {
+  EvictingCacheMap<int, int> map(0);
+
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  EXPECT_FALSE(map.exists(1));
+  map.set(1, 1);
+  EXPECT_EQ(1, map.size());
+  EXPECT_FALSE(map.empty());
+  EXPECT_EQ(1, map.get(1));
+  EXPECT_TRUE(map.exists(1));
+  map.set(1, 2);
+  EXPECT_EQ(1, map.size());
+  EXPECT_FALSE(map.empty());
+  EXPECT_EQ(2, map.get(1));
+  EXPECT_TRUE(map.exists(1));
+  map.erase(1);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  EXPECT_FALSE(map.exists(1));
+
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  EXPECT_FALSE(map.exists(1));
+  map.set(1, 1);
+  EXPECT_EQ(1, map.size());
+  EXPECT_FALSE(map.empty());
+  EXPECT_EQ(1, map.get(1));
+  EXPECT_TRUE(map.exists(1));
+  map.set(1, 2);
+  EXPECT_EQ(1, map.size());
+  EXPECT_FALSE(map.empty());
+  EXPECT_EQ(2, map.get(1));
+  EXPECT_TRUE(map.exists(1));
+
+  EXPECT_FALSE(map.exists(2));
+  map.set(2, 1);
+  EXPECT_TRUE(map.exists(2));
+  EXPECT_EQ(2, map.size());
+  EXPECT_FALSE(map.empty());
+  EXPECT_EQ(1, map.get(2));
+  map.set(2, 2);
+  EXPECT_EQ(2, map.size());
+  EXPECT_FALSE(map.empty());
+  EXPECT_EQ(2, map.get(2));
+  EXPECT_TRUE(map.exists(2));
+  map.erase(2);
+  EXPECT_EQ(1, map.size());
+  EXPECT_FALSE(map.empty());
+  EXPECT_FALSE(map.exists(2));
+  map.erase(1);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  EXPECT_FALSE(map.exists(1));
+}
+
+
+TEST(EvictingCacheMap, PruneTest) {
+  EvictingCacheMap<int, int> map(0);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  map.prune(1000000);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  map.prune(100);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  map.prune(99);
+  EXPECT_EQ(1, map.size());
+  EXPECT_FALSE(map.empty());
+  for (int i = 0; i < 99; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  EXPECT_TRUE(map.exists(99));
+  EXPECT_EQ(99, map.get(99));
+
+  map.prune(100);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  map.prune(90);
+  EXPECT_EQ(10, map.size());
+  EXPECT_FALSE(map.empty());
+  for (int i = 0; i < 90; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  for (int i = 90; i < 100; i++) {
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+}
+
+TEST(EvictingCacheMap, PruneHookTest) {
+  EvictingCacheMap<int, int> map(0);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  int sum = 0;
+  auto pruneCb = [&](int&& k, int&& v) {
+    EXPECT_EQ(k, v);
+    sum += k;
+  };
+
+  map.setPruneHook(pruneCb);
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  map.prune(1000000);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  EXPECT_EQ((99 * 100) / 2, sum);
+  sum = 0;
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  map.prune(100);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  EXPECT_EQ((99 * 100) / 2, sum);
+  sum = 0;
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  map.prune(99);
+  EXPECT_EQ(1, map.size());
+  EXPECT_FALSE(map.empty());
+  for (int i = 0; i < 99; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  EXPECT_TRUE(map.exists(99));
+  EXPECT_EQ(99, map.get(99));
+
+  EXPECT_EQ((98 * 99) / 2, sum);
+  sum = 0;
+
+  map.prune(100);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  EXPECT_EQ(99, sum);
+  sum = 0;
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  map.prune(90);
+  EXPECT_EQ(10, map.size());
+  EXPECT_FALSE(map.empty());
+  for (int i = 0; i < 90; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  for (int i = 90; i < 100; i++) {
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+  EXPECT_EQ((89 * 90) / 2, sum);
+  sum = 0;
+}
+
+TEST(EvictingCacheMap, SetMaxSize) {
+  EvictingCacheMap<int, int> map(100, 20);
+  for (int i = 0; i < 90; i++) {
+    map.set(i, i);
+    EXPECT_TRUE(map.exists(i));
+  }
+
+  EXPECT_EQ(90, map.size());
+  map.setMaxSize(50);
+  EXPECT_EQ(map.size(), 50);
+
+  for (int i = 0; i < 90; i++) {
+    map.set(i, i);
+    EXPECT_TRUE(map.exists(i));
+  }
+  EXPECT_EQ(40, map.size());
+  map.setMaxSize(0);
+  EXPECT_EQ(40, map.size());
+  map.setMaxSize(10);
+  EXPECT_EQ(10, map.size());
+}
+
+TEST(EvictingCacheMap, SetClearSize) {
+  EvictingCacheMap<int, int> map(100, 20);
+  for (int i = 0; i < 90; i++) {
+    map.set(i, i);
+    EXPECT_TRUE(map.exists(i));
+  }
+
+  EXPECT_EQ(90, map.size());
+  map.setClearSize(40);
+  map.setMaxSize(50);
+  EXPECT_EQ(map.size(), 50);
+
+  for (int i = 0; i < 90; i++) {
+    map.set(i, i);
+    EXPECT_TRUE(map.exists(i));
+  }
+  EXPECT_EQ(20, map.size());
+  map.setMaxSize(0);
+  EXPECT_EQ(20, map.size());
+  map.setMaxSize(10);
+  EXPECT_EQ(0, map.size());
+}
+
+TEST(EvictingCacheMap, DestructorInvocationTest) {
+  struct SumInt {
+    SumInt(int val, int* ref) : val(val), ref(ref) { }
+    ~SumInt() {
+      *ref += val;
+    }
+    int val;
+    int* ref;
+  };
+
+  int sum;
+  EvictingCacheMap<int, SumInt> map(0);
+
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, SumInt(i, &sum));
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i).val);
+  }
+
+  sum = 0;
+  map.prune(1000000);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  EXPECT_EQ((99 * 100) / 2, sum);
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, SumInt(i, &sum));
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i).val);
+  }
+
+  sum = 0;
+  map.prune(100);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  EXPECT_EQ((99 * 100) / 2, sum);
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, SumInt(i, &sum));
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i).val);
+  }
+
+  sum = 0;
+  map.prune(99);
+  EXPECT_EQ(1, map.size());
+  EXPECT_FALSE(map.empty());
+  for (int i = 0; i < 99; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  EXPECT_TRUE(map.exists(99));
+  EXPECT_EQ(99, map.get(99).val);
+
+  EXPECT_EQ((98 * 99) / 2, sum);
+
+  sum = 0;
+  map.prune(100);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  EXPECT_EQ(99, sum);
+  for (int i = 0; i < 100; i++) {
+    map.set(i, SumInt(i, &sum));
+    EXPECT_EQ(i + 1, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i).val);
+  }
+
+  sum = 0;
+  map.prune(90);
+  EXPECT_EQ(10, map.size());
+  EXPECT_FALSE(map.empty());
+  for (int i = 0; i < 90; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  for (int i = 90; i < 100; i++) {
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i).val);
+  }
+  EXPECT_EQ((89 * 90) / 2, sum);
+  sum = 0;
+}
+
+TEST(EvictingCacheMap, LruSanityTest) {
+  EvictingCacheMap<int, int> map(10);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_GE(10, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  EXPECT_EQ(10, map.size());
+  EXPECT_FALSE(map.empty());
+  for (int i = 0; i < 90; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  for (int i = 90; i < 100; i++) {
+    EXPECT_TRUE(map.exists(i));
+  }
+}
+
+TEST(EvictingCacheMap, LruPromotionTest) {
+  EvictingCacheMap<int, int> map(10);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_GE(10, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+    for (int j = 0; j < std::min(i + 1, 9); j++) {
+      EXPECT_TRUE(map.exists(j));
+      EXPECT_EQ(j, map.get(j));
+    }
+  }
+
+  EXPECT_EQ(10, map.size());
+  EXPECT_FALSE(map.empty());
+  for (int i = 0; i < 9; i++) {
+    EXPECT_TRUE(map.exists(i));
+  }
+  EXPECT_TRUE(map.exists(99));
+  for (int i = 10; i < 99; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+}
+
+TEST(EvictingCacheMap, LruNoPromotionTest) {
+  EvictingCacheMap<int, int> map(10);
+  EXPECT_EQ(0, map.size());
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < 100; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+
+  for (int i = 0; i < 100; i++) {
+    map.set(i, i);
+    EXPECT_GE(10, map.size());
+    EXPECT_FALSE(map.empty());
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+    for (int j = 0; j < std::min(i + 1, 9); j++) {
+      if (map.exists(j)) {
+        EXPECT_EQ(j, map.getWithoutPromotion(j));
+      }
+    }
+  }
+
+  EXPECT_EQ(10, map.size());
+  EXPECT_FALSE(map.empty());
+  for (int i = 0; i < 90; i++) {
+    EXPECT_FALSE(map.exists(i));
+  }
+  for (int i = 90; i < 100; i++) {
+    EXPECT_TRUE(map.exists(i));
+  }
+}
+
+TEST(EvictingCacheMap, IteratorSanityTest) {
+  const int nItems = 1000;
+  EvictingCacheMap<int, int> map(nItems);
+  EXPECT_TRUE(map.begin() == map.end());
+  for (int i = 0; i < nItems; i++) {
+    EXPECT_FALSE(map.exists(i));
+    map.set(i, i * 2);
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i * 2, map.get(i));
+  }
+
+  std::set<int> seen;
+  for (auto& it : map) {
+    EXPECT_EQ(0, seen.count(it.first));
+    seen.insert(it.first);
+    EXPECT_EQ(it.first * 2, it.second);
+  }
+  EXPECT_EQ(nItems, seen.size());
+}
+
+TEST(EvictingCacheMap, FindTest) {
+  const int nItems = 1000;
+  EvictingCacheMap<int, int> map(nItems);
+  for (int i = 0; i < nItems; i++) {
+    map.set(i * 2, i * 2);
+    EXPECT_TRUE(map.exists(i * 2));
+    EXPECT_EQ(i * 2, map.get(i * 2));
+  }
+  for (int i = 0; i < nItems * 2; i++) {
+    if (i % 2 == 0) {
+      auto it = map.find(i);
+      EXPECT_FALSE(it == map.end());
+      EXPECT_EQ(i, it->first);
+      EXPECT_EQ(i, it->second);
+    } else {
+      EXPECT_TRUE( map.find(i) == map.end());
+    }
+  }
+  for (int i = nItems * 2 - 1; i >= 0; i--) {
+    if (i % 2 == 0) {
+      auto it = map.find(i);
+      EXPECT_FALSE(it == map.end());
+      EXPECT_EQ(i, it->first);
+      EXPECT_EQ(i, it->second);
+    } else {
+      EXPECT_TRUE(map.find(i) == map.end());
+    }
+  }
+  EXPECT_EQ(0, map.begin()->first);
+}
+
+TEST(EvictingCacheMap, FindWithoutPromotionTest) {
+  const int nItems = 1000;
+  EvictingCacheMap<int, int> map(nItems);
+  for (int i = 0; i < nItems; i++) {
+    map.set(i * 2, i * 2);
+    EXPECT_TRUE(map.exists(i * 2));
+    EXPECT_EQ(i * 2, map.get(i * 2));
+  }
+  for (int i = nItems * 2 - 1; i >= 0; i--) {
+    if (i % 2 == 0) {
+      auto it = map.findWithoutPromotion(i);
+      EXPECT_FALSE(it == map.end());
+      EXPECT_EQ(i, it->first);
+      EXPECT_EQ(i, it->second);
+    } else {
+      EXPECT_TRUE(map.findWithoutPromotion(i) == map.end());
+    }
+  }
+  EXPECT_EQ((nItems - 1) * 2, map.begin()->first);
+}
+
+TEST(EvictingCacheMap, IteratorOrderingTest) {
+  const int nItems = 1000;
+  EvictingCacheMap<int, int> map(nItems);
+  for (int i = 0; i < nItems; i++) {
+    map.set(i, i);
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  int expected = nItems - 1;
+  for (auto it = map.begin(); it != map.end(); ++it) {
+    EXPECT_EQ(expected, it->first);
+    expected--;
+  }
+
+  expected = 0;
+  for (auto it = map.rbegin(); it != map.rend(); ++it) {
+    EXPECT_EQ(expected, it->first);
+    expected++;
+  }
+
+  {
+    auto it = map.end();
+    expected = 0;
+    EXPECT_TRUE(it != map.begin());
+    do {
+      --it;
+      EXPECT_EQ(expected, it->first);
+      expected++;
+    } while (it != map.begin());
+    EXPECT_EQ(nItems, expected);
+  }
+
+  {
+    auto it = map.rend();
+    expected = nItems - 1;
+    do {
+      --it;
+      EXPECT_EQ(expected, it->first);
+      expected--;
+    } while (it != map.rbegin());
+    EXPECT_EQ(-1, expected);
+  }
+}
+
+TEST(EvictingCacheMap, MoveTest) {
+  const int nItems = 1000;
+  EvictingCacheMap<int, int> map(nItems);
+  for (int i = 0; i < nItems; i++) {
+    map.set(i, i);
+    EXPECT_TRUE(map.exists(i));
+    EXPECT_EQ(i, map.get(i));
+  }
+
+  EvictingCacheMap<int, int> map2 = std::move(map);
+  EXPECT_TRUE(map.empty());
+  for (int i = 0; i < nItems; i++) {
+    EXPECT_TRUE(map2.exists(i));
+    EXPECT_EQ(i, map2.get(i));
+  }
+}
diff --git a/folly/container/test/ForeachBenchmark.cpp b/folly/container/test/ForeachBenchmark.cpp
new file mode 100644 (file)
index 0000000..5b741af
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/container/Foreach.h>
+
+#include <folly/Benchmark.h>
+#include <folly/portability/GTest.h>
+
+#include <map>
+
+using namespace folly;
+using namespace folly::detail;
+
+// Benchmarks:
+// 1. Benchmark iterating through the man with FOR_EACH, and also assign
+//    iter->first and iter->second to local vars inside the FOR_EACH loop.
+// 2. Benchmark iterating through the man with FOR_EACH, but use iter->first and
+//    iter->second as is, without assigning to local variables.
+// 3. Use FOR_EACH_KV loop to iterate through the map.
+
+std::map<int, std::string> bmMap; // For use in benchmarks below.
+std::vector<int> vec_one;
+std::vector<int> vec_two;
+
+void setupBenchmark(size_t iters) {
+  bmMap.clear();
+  for (size_t i = 0; i < iters; ++i) {
+    bmMap[i] = "teststring";
+  }
+
+  vec_one.clear();
+  vec_two.clear();
+  vec_one.resize(iters);
+  vec_two.resize(iters);
+}
+
+BENCHMARK(ForEachFunctionNoAssign, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    folly::for_each(bmMap, [&](auto& key_val_pair) {
+      sumKeys += key_val_pair.first;
+      sumValues += key_val_pair.second;
+    });
+    doNotOptimizeAway(sumKeys);
+  });
+}
+
+BENCHMARK(StdForEachFunctionNoAssign, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
+      sumKeys += key_val_pair.first;
+      sumValues += key_val_pair.second;
+    });
+    doNotOptimizeAway(sumKeys);
+  });
+}
+
+BENCHMARK(RangeBasedForLoopNoAssign, iters) {
+  BenchmarkSuspender suspender;
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    for (auto& key_val_pair : bmMap) {
+      sumKeys += key_val_pair.first;
+      sumValues += key_val_pair.second;
+    }
+    doNotOptimizeAway(sumKeys);
+  });
+}
+
+BENCHMARK(ManualLoopNoAssign, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {
+      sumKeys += iter->first;
+      sumValues += iter->second;
+    }
+    doNotOptimizeAway(sumKeys);
+  });
+}
+
+BENCHMARK(ForEachFunctionAssign, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    folly::for_each(bmMap, [&](auto& key_val_pair) {
+      const int k = key_val_pair.first;
+      const std::string v = key_val_pair.second;
+      sumKeys += k;
+      sumValues += v;
+    });
+  });
+}
+
+BENCHMARK(StdForEachFunctionAssign, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
+      const int k = key_val_pair.first;
+      const std::string v = key_val_pair.second;
+      sumKeys += k;
+      sumValues += v;
+    });
+  });
+}
+
+BENCHMARK(RangeBasedForLoopAssign, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    for (auto& key_val_pair : bmMap) {
+      const int k = key_val_pair.first;
+      const std::string v = key_val_pair.second;
+      sumKeys += k;
+      sumValues += v;
+    }
+  });
+}
+
+BENCHMARK(ManualLoopAssign, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {
+      const int k = iter->first;
+      const std::string v = iter->second;
+      sumKeys += k;
+      sumValues += v;
+    }
+  });
+}
+
+BENCHMARK(ForEachFunctionNoAssignWithIndexManipulation, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    folly::for_each(bmMap, [&](auto& key_val_pair, auto index) {
+      sumKeys += key_val_pair.first;
+      sumValues += key_val_pair.second;
+      sumValues += index;
+    });
+  });
+}
+
+BENCHMARK(StdForEachFunctionNoAssignWithIndexManipulation, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    auto index = std::size_t{0};
+    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
+      sumKeys += key_val_pair.first;
+      sumValues += key_val_pair.second;
+      sumValues += index;
+      ++index;
+    });
+  });
+}
+
+BENCHMARK(RangeBasedForLoopNoAssignWithIndexManipulation, iters) {
+  BenchmarkSuspender suspender;
+
+  int sumKeys = 0;
+  std::string sumValues;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    auto index = std::size_t{0};
+    for (auto& key_val_pair : bmMap) {
+      sumKeys += key_val_pair.first;
+      sumValues += key_val_pair.second;
+      sumValues += index;
+    }
+  });
+}
+
+BENCHMARK(ForEachFunctionFetch, iters) {
+  BenchmarkSuspender suspender;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    folly::for_each(bmMap, [&](auto& key_val_pair, auto index) {
+      folly::fetch(vec_one, index) = key_val_pair.first;
+    });
+  });
+}
+
+BENCHMARK(StdForEachFunctionFetch, iters) {
+  BenchmarkSuspender suspender;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    auto index = std::size_t{0};
+    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
+      *(vec_one.begin() + index++) = key_val_pair.first;
+    });
+  });
+}
+
+BENCHMARK(ForLoopFetch, iters) {
+  BenchmarkSuspender suspender;
+  setupBenchmark(iters);
+
+  suspender.dismissing([&]() {
+    auto index = std::size_t{0};
+    for (auto& key_val_pair : bmMap) {
+      *(vec_one.begin() + index++) = key_val_pair.first;
+    }
+  });
+}
+
+BENCHMARK(ForEachKVNoMacroAssign, iters) {
+  int sumKeys = 0;
+  std::string sumValues;
+
+  BENCHMARK_SUSPEND { setupBenchmark(iters); }
+
+  FOR_EACH(iter, bmMap) {
+    const int k = iter->first;
+    const std::string v = iter->second;
+    sumKeys += k;
+    sumValues += v;
+  }
+}
+
+BENCHMARK(ForEachKVNoMacroNoAssign, iters) {
+  int sumKeys = 0;
+  std::string sumValues;
+
+  BENCHMARK_SUSPEND { setupBenchmark(iters); }
+
+  FOR_EACH(iter, bmMap) {
+    sumKeys += iter->first;
+    sumValues += iter->second;
+  }
+}
+
+BENCHMARK(ForEachKVMacro, iters) {
+  int sumKeys = 0;
+  std::string sumValues;
+
+  BENCHMARK_SUSPEND { setupBenchmark(iters); }
+
+  FOR_EACH_KV(k, v, bmMap) {
+    sumKeys += k;
+    sumValues += v;
+  }
+}
+
+BENCHMARK(ForEachManual, iters) {
+  int sum = 1;
+  for (size_t i = 1; i < iters; ++i) {
+    sum *= i;
+  }
+  doNotOptimizeAway(sum);
+}
+
+BENCHMARK(ForEachRange, iters) {
+  int sum = 1;
+  FOR_EACH_RANGE(i, 1, iters) { sum *= i; }
+  doNotOptimizeAway(sum);
+}
+
+BENCHMARK(ForEachDescendingManual, iters) {
+  int sum = 1;
+  for (size_t i = iters; i-- > 1;) {
+    sum *= i;
+  }
+  doNotOptimizeAway(sum);
+}
+
+BENCHMARK(ForEachRangeR, iters) {
+  int sum = 1;
+  FOR_EACH_RANGE_R(i, 1U, iters) { sum *= i; }
+  doNotOptimizeAway(sum);
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  auto r = RUN_ALL_TESTS();
+  if (r) {
+    return r;
+  }
+  runBenchmarks();
+  return 0;
+}
diff --git a/folly/container/test/ForeachTest.cpp b/folly/container/test/ForeachTest.cpp
new file mode 100644 (file)
index 0000000..fd7c0dd
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/container/Foreach.h>
+
+#include <array>
+#include <initializer_list>
+#include <iterator>
+#include <list>
+#include <map>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <folly/portability/GTest.h>
+
+using namespace folly;
+using namespace folly::detail;
+
+namespace folly {
+namespace test {
+
+class TestRValueConstruct {
+ public:
+  TestRValueConstruct() = default;
+  TestRValueConstruct(TestRValueConstruct&&) noexcept {
+    this->constructed_from_rvalue = true;
+  }
+  TestRValueConstruct(const TestRValueConstruct&) {
+    this->constructed_from_rvalue = false;
+  }
+  TestRValueConstruct& operator=(const TestRValueConstruct&) = delete;
+  TestRValueConstruct& operator=(TestRValueConstruct&&) = delete;
+
+  bool constructed_from_rvalue{false};
+};
+
+class TestAdlIterable {
+ public:
+  std::vector<int> vec{0, 1, 2, 3};
+};
+
+auto begin(TestAdlIterable& instance) {
+  return instance.vec.begin();
+}
+auto begin(const TestAdlIterable& instance) {
+  return instance.vec.begin();
+}
+auto end(TestAdlIterable& instance) {
+  return instance.vec.end();
+}
+auto end(const TestAdlIterable& instance) {
+  return instance.vec.end();
+}
+
+class TestBothIndexingAndIter {
+ public:
+  class Iterator {
+   public:
+    using difference_type = std::size_t;
+    using value_type = int;
+    using pointer = int*;
+    using reference = int&;
+    using iterator_category = std::random_access_iterator_tag;
+    int& operator*() {
+      return this->val;
+    }
+    Iterator operator+(int) {
+      return *this;
+    }
+    explicit Iterator(int& val_in) : val{val_in} {}
+    int& val;
+  };
+  auto begin() {
+    this->called_begin = true;
+    return Iterator{val};
+  }
+  auto end() {
+    return Iterator{val};
+  }
+  int& operator[](int) {
+    return this->val;
+  }
+
+  int val{0};
+  bool called_begin = false;
+};
+} // namespace test
+} // namespace folly
+
+TEST(Foreach, ForEachFunctionBasic) {
+  auto range = std::make_tuple(1, 2, 3);
+  auto result_range = std::vector<int>{};
+  auto correct_result_range = std::vector<int>{1, 2, 3};
+
+  folly::for_each(range, [&](auto ele) { result_range.push_back(ele); });
+
+  EXPECT_TRUE(std::equal(
+      result_range.begin(), result_range.end(), correct_result_range.begin()));
+}
+
+TEST(Foreach, ForEachFunctionBasicRuntimeOneArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto current = 0;
+  folly::for_each(range, [&](auto ele) {
+    if (current == 0) {
+      EXPECT_EQ(ele, 1);
+    } else if (current == 1) {
+      EXPECT_EQ(ele, 2);
+    } else {
+      EXPECT_EQ(ele, 3);
+    }
+    ++current;
+  });
+}
+
+TEST(Foreach, ForEachFunctionBasicRuntimeTwoArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  folly::for_each(range, [](auto ele, auto index) {
+    EXPECT_TRUE(index < 3);
+    if (index == 0) {
+      EXPECT_EQ(ele, 1);
+    } else if (index == 1) {
+      EXPECT_EQ(ele, 2);
+    } else if (index == 2) {
+      EXPECT_EQ(ele, 3);
+    }
+  });
+}
+
+TEST(Foreach, ForEachFunctionBasicRuntimeThreeArg) {
+  auto range = std::list<int>{1, 2, 3};
+  auto result_range = std::list<int>{1, 3};
+  folly::for_each(range, [&](auto ele, auto, auto iter) {
+    if (ele == 2) {
+      range.erase(iter);
+    }
+  });
+  EXPECT_TRUE(std::equal(range.begin(), range.end(), result_range.begin()));
+}
+
+TEST(Foreach, ForEachFunctionBasicTupleOneArg) {
+  auto range = std::make_tuple(1, 2, 3);
+  auto current = 0;
+  folly::for_each(range, [&](auto ele) {
+    if (current == 0) {
+      EXPECT_EQ(ele, 1);
+    } else if (current == 1) {
+      EXPECT_EQ(ele, 2);
+    } else {
+      EXPECT_EQ(ele, 3);
+    }
+    ++current;
+  });
+}
+
+TEST(Foreach, ForEachFunctionBasicTupleTwoArg) {
+  auto range = std::make_tuple(1, 2, 3);
+  folly::for_each(range, [](auto ele, auto index) {
+    EXPECT_TRUE(index < 3);
+    if (index == 0) {
+      EXPECT_EQ(ele, 1);
+    } else if (index == 1) {
+      EXPECT_EQ(ele, 2);
+    } else if (index == 2) {
+      EXPECT_EQ(ele, 3);
+    }
+  });
+}
+
+TEST(Foreach, ForEachFunctionBreakRuntimeOneArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto) {
+    ++iterations;
+    if (iterations == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 1);
+}
+
+TEST(Foreach, ForEachFunctionBreakRuntimeTwoArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto, auto index) {
+    ++iterations;
+    if (index == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionBreakRuntimeThreeArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto, auto index, auto) {
+    ++iterations;
+    if (index == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionBreakTupleOneArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto) {
+    ++iterations;
+    if (iterations == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 1);
+}
+
+TEST(Foreach, ForEachFunctionBreakTupleTwoArg) {
+  auto range = std::vector<int>{1, 2, 3};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto, auto index) {
+    ++iterations;
+    if (index == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionArray) {
+  auto range = std::array<int, 3>{{1, 2, 3}};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto, auto index) {
+    ++iterations;
+    if (index == 1) {
+      return folly::loop_break;
+    }
+    return folly::loop_continue;
+  });
+  EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionInitializerListBasic) {
+  folly::for_each(std::initializer_list<int>{1, 2, 3}, [](auto ele) { ++ele; });
+}
+
+TEST(Foreach, ForEachFunctionTestForward) {
+  using folly::test::TestRValueConstruct;
+  auto range_one = std::vector<TestRValueConstruct>{};
+  range_one.resize(3);
+
+  folly::for_each(std::move(range_one), [](auto ele) {
+    EXPECT_FALSE(ele.constructed_from_rvalue);
+  });
+
+  folly::for_each(
+      std::make_tuple(TestRValueConstruct{}, TestRValueConstruct{}),
+      [](auto ele) { EXPECT_TRUE(ele.constructed_from_rvalue); });
+}
+
+TEST(Foreach, ForEachFunctionAdlIterable) {
+  auto range = test::TestAdlIterable{};
+  auto iterations = 0;
+  folly::for_each(range, [&](auto ele, auto index) {
+    ++iterations;
+    EXPECT_EQ(ele, index);
+  });
+  EXPECT_EQ(iterations, 4);
+}
+
+TEST(ForEach, FetchRandomAccessIterator) {
+  auto vec = std::vector<int>{1, 2, 3};
+  auto& second = folly::fetch(vec, 1);
+  EXPECT_EQ(second, 2);
+  second = 3;
+  EXPECT_EQ(second, 3);
+}
+
+TEST(ForEach, FetchIndexing) {
+  auto mp = std::map<int, int>{{1, 2}};
+  auto& ele = folly::fetch(mp, 1);
+  EXPECT_EQ(ele, 2);
+  ele = 3;
+  EXPECT_EQ(ele, 3);
+}
+
+TEST(ForEach, FetchTuple) {
+  auto mp = std::make_tuple(1, 2, 3);
+  auto& ele = folly::fetch(mp, std::integral_constant<int, 1>{});
+  EXPECT_EQ(ele, 2);
+  ele = 3;
+  EXPECT_EQ(ele, 3);
+}
+
+TEST(ForEach, FetchTestPreferIterator) {
+  auto range = test::TestBothIndexingAndIter{};
+  auto& ele = folly::fetch(range, 0);
+  EXPECT_TRUE(range.called_begin);
+  EXPECT_EQ(ele, 0);
+  ele = 2;
+  EXPECT_EQ(folly::fetch(range, 0), 2);
+}
+
+TEST(Foreach, ForEachRvalue) {
+  const char* const hello = "hello";
+  int n = 0;
+  FOR_EACH(it, std::string(hello)) {
+    ++n;
+  }
+  EXPECT_EQ(strlen(hello), n);
+  FOR_EACH_R(it, std::string(hello)) {
+    --n;
+    EXPECT_EQ(hello[n], *it);
+  }
+  EXPECT_EQ(0, n);
+}
+
+TEST(Foreach, ForEachNested) {
+  const std::string hello = "hello";
+  size_t n = 0;
+  FOR_EACH(i, hello) {
+    FOR_EACH(j, hello) {
+      ++n;
+    }
+  }
+  auto len = hello.size();
+  EXPECT_EQ(len * len, n);
+}
+
+TEST(Foreach, ForEachKV) {
+  std::map<std::string, int> testMap;
+  testMap["abc"] = 1;
+  testMap["def"] = 2;
+  std::string keys = "";
+  int values = 0;
+  int numEntries = 0;
+  FOR_EACH_KV (key, value, testMap) {
+    keys += key;
+    values += value;
+    ++numEntries;
+  }
+  EXPECT_EQ("abcdef", keys);
+  EXPECT_EQ(3, values);
+  EXPECT_EQ(2, numEntries);
+}
+
+TEST(Foreach, ForEachKVBreak) {
+  std::map<std::string, int> testMap;
+  testMap["abc"] = 1;
+  testMap["def"] = 2;
+  std::string keys = "";
+  int values = 0;
+  int numEntries = 0;
+  FOR_EACH_KV (key, value, testMap) {
+    keys += key;
+    values += value;
+    ++numEntries;
+    break;
+  }
+  EXPECT_EQ("abc", keys);
+  EXPECT_EQ(1, values);
+  EXPECT_EQ(1, numEntries);
+}
+
+TEST(Foreach, ForEachKvWithMultiMap) {
+  std::multimap<std::string, int> testMap;
+  testMap.insert(std::make_pair("abc", 1));
+  testMap.insert(std::make_pair("abc", 2));
+  testMap.insert(std::make_pair("def", 3));
+  std::string keys = "";
+  int values = 0;
+  int numEntries = 0;
+  FOR_EACH_KV (key, value, testMap) {
+    keys += key;
+    values += value;
+    ++numEntries;
+  }
+  EXPECT_EQ("abcabcdef", keys);
+  EXPECT_EQ(6, values);
+  EXPECT_EQ(3, numEntries);
+}
+
+TEST(Foreach, ForEachEnumerate) {
+  std::vector<int> vv;
+  int sumAA = 0;
+  int sumIter = 0;
+  int numIterations = 0;
+  FOR_EACH_ENUMERATE(aa, iter, vv) {
+    sumAA += aa;
+    sumIter += *iter;
+    ++numIterations;
+  }
+  EXPECT_EQ(sumAA, 0);
+  EXPECT_EQ(sumIter, 0);
+  EXPECT_EQ(numIterations, 0);
+
+  vv.push_back(1);
+  vv.push_back(3);
+  vv.push_back(5);
+  FOR_EACH_ENUMERATE(aa, iter, vv) {
+    sumAA += aa;
+    sumIter += *iter;
+    ++numIterations;
+  }
+  EXPECT_EQ(sumAA, 3);   // 0 + 1 + 2
+  EXPECT_EQ(sumIter, 9); // 1 + 3 + 5
+  EXPECT_EQ(numIterations, 3);
+}
+
+TEST(Foreach, ForEachEnumerateBreak) {
+  std::vector<int> vv;
+  int sumAA = 0;
+  int sumIter = 0;
+  int numIterations = 0;
+  vv.push_back(1);
+  vv.push_back(2);
+  vv.push_back(4);
+  vv.push_back(8);
+  FOR_EACH_ENUMERATE(aa, iter, vv) {
+    sumAA += aa;
+    sumIter += *iter;
+    ++numIterations;
+    if (aa == 1) {
+      break;
+    }
+  }
+  EXPECT_EQ(sumAA, 1);   // 0 + 1
+  EXPECT_EQ(sumIter, 3); // 1 + 2
+  EXPECT_EQ(numIterations, 2);
+}
+
+TEST(Foreach, ForEachRangeR) {
+  int sum = 0;
+
+  FOR_EACH_RANGE_R (i, 0, 0) {
+    sum += i;
+  }
+  EXPECT_EQ(0, sum);
+
+  FOR_EACH_RANGE_R (i, 0, -1) {
+    sum += i;
+  }
+  EXPECT_EQ(0, sum);
+
+  FOR_EACH_RANGE_R (i, 0, 5) {
+    sum += i;
+  }
+  EXPECT_EQ(10, sum);
+
+  std::list<int> lst = { 0, 1, 2, 3, 4 };
+  sum = 0;
+  FOR_EACH_RANGE_R (i, lst.begin(), lst.end()) {
+    sum += *i;
+  }
+  EXPECT_EQ(10, sum);
+}
diff --git a/folly/container/test/IteratorTest.cpp b/folly/container/test/IteratorTest.cpp
new file mode 100644 (file)
index 0000000..15822e6
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <deque>
+#include <functional>
+#include <map>
+#include <set>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <folly/container/Iterator.h>
+#include <folly/portability/GTest.h>
+
+namespace {
+/**
+ * Container type used for unit tests.
+ */
+template <typename T>
+using Container = std::deque<T>;
+
+// Constructor and assignment operator call counters for struct Object.
+std::size_t gDefaultCtrCnt;
+std::size_t gCopyCtrCnt;
+std::size_t gMoveCtrCnt;
+std::size_t gExplicitCtrCnt;
+std::size_t gMultiargCtrCnt;
+std::size_t gCopyOpCnt;
+std::size_t gMoveOpCnt;
+std::size_t gConvertOpCnt;
+
+/**
+ * Class that increases various counters to keep track of how objects have
+ * been constructed or assigned to, to verify iterator behavior.
+ */
+struct Object {
+  Object() {
+    ++gDefaultCtrCnt;
+  }
+  Object(const Object&) {
+    ++gCopyCtrCnt;
+  }
+  Object(Object&&) noexcept {
+    ++gMoveCtrCnt;
+  }
+  explicit Object(int) {
+    ++gExplicitCtrCnt;
+  }
+  explicit Object(int, int) {
+    ++gMultiargCtrCnt;
+  }
+  Object& operator=(const Object&) {
+    ++gCopyOpCnt;
+    return *this;
+  }
+  Object& operator=(Object&&) noexcept {
+    ++gMoveOpCnt;
+    return *this;
+  }
+  Object& operator=(int) noexcept {
+    ++gConvertOpCnt;
+    return *this;
+  }
+};
+
+/**
+ * Reset all call counters to 0.
+ */
+void init_counters() {
+  gDefaultCtrCnt = gCopyCtrCnt = gMoveCtrCnt = gExplicitCtrCnt =
+      gMultiargCtrCnt = gCopyOpCnt = gMoveOpCnt = gConvertOpCnt = 0;
+}
+
+/**
+ * Test for iterator copy and move.
+ */
+template <typename Iterator>
+void copy_and_move_test(Container<int>& q, Iterator it) {
+  assert(q.empty());
+  const auto it2(it); // copy construct
+  it = it2; // copy assign from const
+  it = it; // self assign
+  auto it3(std::move(it)); // move construct
+  it = std::move(it3); // move assign
+  // Make sure iterator still works.
+  it = 4711; // emplace
+  EXPECT_EQ(q, Container<int>{4711});
+}
+
+/**
+ * Test for emplacement with perfect forwarding.
+ */
+template <typename Iterator>
+void emplace_test(Container<Object>& q, Iterator it) {
+  using folly::make_emplace_args;
+  assert(q.empty());
+  init_counters();
+  it = Object{}; // default construct + move construct
+  Object obj; // default construct
+  it = obj; // copy construct
+  it = std::move(obj); // move construct
+  const Object obj2; // default construct
+  it = obj2; // copy construct from const
+  it = std::move(obj2); // copy construct (const defeats move)
+  it = 0; // explicit construct
+  it = make_emplace_args(0, 0); // explicit multiarg construct
+  it = std::make_pair(0, 0); // implicit multiarg construct
+  it = std::make_tuple(0, 0); // implicit multiarg construct
+  auto args = make_emplace_args(Object{}); // default construct + move construct
+  it = args; // copy construct
+  it = const_cast<const decltype(args)&>(args); // copy construct from const
+  it = std::move(args); // move construct
+  auto args2 = std::make_tuple(Object{}); // default construct + move construct
+  it = args2; // (implicit multiarg) copy construct
+  it = std::move(args2); // (implicit multiarg) move construct
+  auto args3 = std::make_pair(0, 0);
+  it = args3; // implicit multiarg construct
+  it = std::move(args3); // implicit multiarg construct
+  ASSERT_EQ(q.size(), 16);
+  EXPECT_EQ(gDefaultCtrCnt, 5);
+  EXPECT_EQ(gCopyCtrCnt, 6);
+  EXPECT_EQ(gMoveCtrCnt, 6);
+  EXPECT_EQ(gExplicitCtrCnt, 1);
+  EXPECT_EQ(gMultiargCtrCnt, 5);
+  EXPECT_EQ(gCopyOpCnt, 0);
+  EXPECT_EQ(gMoveOpCnt, 0);
+  EXPECT_EQ(gConvertOpCnt, 0);
+}
+}
+
+using namespace folly;
+
+/**
+ * Basic tests for folly::emplace_iterator.
+ */
+TEST(EmplaceIterator, EmplacerTest) {
+  {
+    Container<int> q;
+    copy_and_move_test(q, emplacer(q, q.begin()));
+  }
+  {
+    Container<Object> q;
+    emplace_test(q, emplacer(q, q.begin()));
+  }
+  {
+    Container<int> q;
+    auto it = emplacer(q, q.begin());
+    it = 0;
+    it = 1;
+    it = 2;
+    it = emplacer(q, q.begin());
+    it = 3;
+    it = 4;
+    EXPECT_EQ(q, Container<int>({3, 4, 0, 1, 2}));
+  }
+}
+
+/**
+ * Basic tests for folly::front_emplace_iterator.
+ */
+TEST(EmplaceIterator, FrontEmplacerTest) {
+  {
+    Container<int> q;
+    copy_and_move_test(q, front_emplacer(q));
+  }
+  {
+    Container<Object> q;
+    emplace_test(q, front_emplacer(q));
+  }
+  {
+    Container<int> q;
+    auto it = front_emplacer(q);
+    it = 0;
+    it = 1;
+    it = 2;
+    it = front_emplacer(q);
+    it = 3;
+    it = 4;
+    EXPECT_EQ(q, Container<int>({4, 3, 2, 1, 0}));
+  }
+}
+
+/**
+ * Basic tests for folly::back_emplace_iterator.
+ */
+TEST(EmplaceIterator, BackEmplacerTest) {
+  {
+    Container<int> q;
+    copy_and_move_test(q, back_emplacer(q));
+  }
+  {
+    Container<Object> q;
+    emplace_test(q, back_emplacer(q));
+  }
+  {
+    Container<int> q;
+    auto it = back_emplacer(q);
+    it = 0;
+    it = 1;
+    it = 2;
+    it = back_emplacer(q);
+    it = 3;
+    it = 4;
+    EXPECT_EQ(q, Container<int>({0, 1, 2, 3, 4}));
+  }
+}
+
+/**
+ * Basic tests for folly::hint_emplace_iterator.
+ */
+TEST(EmplaceIterator, HintEmplacerTest) {
+  {
+    init_counters();
+    std::map<int, Object> m;
+    auto it = hint_emplacer(m, m.end());
+    it = make_emplace_args(
+        std::piecewise_construct,
+        std::forward_as_tuple(0),
+        std::forward_as_tuple(0));
+    it = make_emplace_args(
+        std::piecewise_construct,
+        std::forward_as_tuple(1),
+        std::forward_as_tuple(0, 0));
+    it = make_emplace_args(
+        std::piecewise_construct,
+        std::forward_as_tuple(2),
+        std::forward_as_tuple(Object{}));
+    ASSERT_EQ(m.size(), 3);
+    EXPECT_EQ(gDefaultCtrCnt, 1);
+    EXPECT_EQ(gCopyCtrCnt, 0);
+    EXPECT_EQ(gMoveCtrCnt, 1);
+    EXPECT_EQ(gExplicitCtrCnt, 1);
+    EXPECT_EQ(gMultiargCtrCnt, 1);
+    EXPECT_EQ(gCopyOpCnt, 0);
+    EXPECT_EQ(gMoveOpCnt, 0);
+    EXPECT_EQ(gConvertOpCnt, 0);
+  }
+  {
+    struct O {
+      explicit O(int i) : i(i) {}
+      bool operator<(const O& other) const {
+        return i < other.i;
+      }
+      bool operator==(const O& other) const {
+        return i == other.i;
+      }
+      int i;
+    };
+    std::vector<int> v1 = {0, 1, 2, 3, 4};
+    std::vector<int> v2 = {0, 2, 4};
+    std::set<O> diff;
+    std::set_difference(
+        v1.begin(),
+        v1.end(),
+        v2.begin(),
+        v2.end(),
+        hint_emplacer(diff, diff.end()));
+    ASSERT_EQ(diff, std::set<O>({O(1), O(3)}));
+  }
+}
+
+/**
+ * Test std::copy() with explicit conversion. This would not compile with a
+ * std::back_insert_iterator, because the constructor of Object that takes a
+ * single int is explicit.
+ */
+TEST(EmplaceIterator, Copy) {
+  init_counters();
+  Container<int> in({0, 1, 2});
+  Container<Object> out;
+  std::copy(in.begin(), in.end(), back_emplacer(out));
+  EXPECT_EQ(3, out.size());
+  EXPECT_EQ(gDefaultCtrCnt, 0);
+  EXPECT_EQ(gCopyCtrCnt, 0);
+  EXPECT_EQ(gMoveCtrCnt, 0);
+  EXPECT_EQ(gExplicitCtrCnt, 3);
+  EXPECT_EQ(gMultiargCtrCnt, 0);
+  EXPECT_EQ(gCopyOpCnt, 0);
+  EXPECT_EQ(gMoveOpCnt, 0);
+  EXPECT_EQ(gConvertOpCnt, 0);
+}
+
+/**
+ * Test std::transform() with multi-argument constructors. This would require
+ * a temporary Object with std::back_insert_iterator.
+ */
+TEST(EmplaceIterator, Transform) {
+  init_counters();
+  Container<int> in({0, 1, 2});
+  Container<Object> out;
+  std::transform(in.begin(), in.end(), back_emplacer(out), [](int i) {
+    return make_emplace_args(i, i);
+  });
+  EXPECT_EQ(3, out.size());
+  EXPECT_EQ(gDefaultCtrCnt, 0);
+  EXPECT_EQ(gCopyCtrCnt, 0);
+  EXPECT_EQ(gMoveCtrCnt, 0);
+  EXPECT_EQ(gExplicitCtrCnt, 0);
+  EXPECT_EQ(gMultiargCtrCnt, 3);
+  EXPECT_EQ(gCopyOpCnt, 0);
+  EXPECT_EQ(gMoveOpCnt, 0);
+  EXPECT_EQ(gConvertOpCnt, 0);
+}
+
+/**
+ * Test multi-argument store and forward.
+ */
+TEST(EmplaceIterator, EmplaceArgs) {
+  Object o1;
+  const Object o2;
+  Object& o3 = o1;
+  const Object& o4 = o3;
+  Object o5;
+
+  {
+    // Test copy construction.
+    auto args = make_emplace_args(0, o1, o2, o3, o4, Object{}, std::cref(o2));
+    init_counters();
+    auto args2 = args;
+    EXPECT_EQ(gDefaultCtrCnt, 0);
+    EXPECT_EQ(gCopyCtrCnt, 5);
+    EXPECT_EQ(gMoveCtrCnt, 0);
+    EXPECT_EQ(gExplicitCtrCnt, 0);
+    EXPECT_EQ(gMultiargCtrCnt, 0);
+    EXPECT_EQ(gCopyOpCnt, 0);
+    EXPECT_EQ(gMoveOpCnt, 0);
+    EXPECT_EQ(gConvertOpCnt, 0);
+
+    // Test copy assignment.
+    init_counters();
+    args = args2;
+    EXPECT_EQ(gDefaultCtrCnt, 0);
+    EXPECT_EQ(gCopyCtrCnt, 0);
+    EXPECT_EQ(gMoveCtrCnt, 0);
+    EXPECT_EQ(gExplicitCtrCnt, 0);
+    EXPECT_EQ(gMultiargCtrCnt, 0);
+    EXPECT_EQ(gCopyOpCnt, 5);
+    EXPECT_EQ(gMoveOpCnt, 0);
+    EXPECT_EQ(gConvertOpCnt, 0);
+  }
+
+  {
+    // Test RVO.
+    init_counters();
+    auto args = make_emplace_args(
+        0, o1, o2, o3, o4, Object{}, std::cref(o2), rref(std::move(o5)));
+    EXPECT_EQ(gDefaultCtrCnt, 1);
+    EXPECT_EQ(gCopyCtrCnt, 4);
+    EXPECT_EQ(gMoveCtrCnt, 1);
+    EXPECT_EQ(gExplicitCtrCnt, 0);
+    EXPECT_EQ(gMultiargCtrCnt, 0);
+    EXPECT_EQ(gCopyOpCnt, 0);
+    EXPECT_EQ(gMoveOpCnt, 0);
+    EXPECT_EQ(gConvertOpCnt, 0);
+
+    // Test move construction.
+    init_counters();
+    auto args2 = std::move(args);
+    EXPECT_EQ(gDefaultCtrCnt, 0);
+    EXPECT_EQ(gCopyCtrCnt, 0);
+    EXPECT_EQ(gMoveCtrCnt, 5);
+    EXPECT_EQ(gExplicitCtrCnt, 0);
+    EXPECT_EQ(gMultiargCtrCnt, 0);
+    EXPECT_EQ(gCopyOpCnt, 0);
+    EXPECT_EQ(gMoveOpCnt, 0);
+    EXPECT_EQ(gConvertOpCnt, 0);
+
+    // Test move assignment.
+    init_counters();
+    args = std::move(args2);
+    EXPECT_EQ(gDefaultCtrCnt, 0);
+    EXPECT_EQ(gCopyCtrCnt, 0);
+    EXPECT_EQ(gMoveCtrCnt, 0);
+    EXPECT_EQ(gExplicitCtrCnt, 0);
+    EXPECT_EQ(gMultiargCtrCnt, 0);
+    EXPECT_EQ(gCopyOpCnt, 0);
+    EXPECT_EQ(gMoveOpCnt, 5);
+    EXPECT_EQ(gConvertOpCnt, 0);
+
+    // Make sure arguments are stored correctly. lvalues by reference, rvalues
+    // by (moved) copy. Rvalues cannot be stored by reference because they may
+    // refer to an expired temporary by the time they are accessed.
+    static_assert(
+        std::is_same<
+            int,
+            std::tuple_element_t<0, decltype(args)::storage_type>>::value,
+        "");
+    static_assert(
+        std::is_same<
+            Object,
+            std::tuple_element_t<1, decltype(args)::storage_type>>::value,
+        "");
+    static_assert(
+        std::is_same<
+            Object,
+            std::tuple_element_t<2, decltype(args)::storage_type>>::value,
+        "");
+    static_assert(
+        std::is_same<
+            Object,
+            std::tuple_element_t<3, decltype(args)::storage_type>>::value,
+        "");
+    static_assert(
+        std::is_same<
+            Object,
+            std::tuple_element_t<4, decltype(args)::storage_type>>::value,
+        "");
+    static_assert(
+        std::is_same<
+            Object,
+            std::tuple_element_t<5, decltype(args)::storage_type>>::value,
+        "");
+    static_assert(
+        std::is_same<
+            std::reference_wrapper<const Object>,
+            std::tuple_element_t<6, decltype(args)::storage_type>>::value,
+        "");
+    static_assert(
+        std::is_same<
+            rvalue_reference_wrapper<Object>,
+            std::tuple_element_t<7, decltype(args)::storage_type>>::value,
+        "");
+
+    // Check whether args.get() restores the original argument type for
+    // rvalue references to emplace_args.
+    static_assert(
+        std::is_same<int&&, decltype(get_emplace_arg<0>(std::move(args)))>::
+            value,
+        "");
+    static_assert(
+        std::is_same<Object&, decltype(get_emplace_arg<1>(std::move(args)))>::
+            value,
+        "");
+    static_assert(
+        std::is_same<
+            const Object&,
+            decltype(get_emplace_arg<2>(std::move(args)))>::value,
+        "");
+    static_assert(
+        std::is_same<Object&, decltype(get_emplace_arg<3>(std::move(args)))>::
+            value,
+        "");
+    static_assert(
+        std::is_same<
+            const Object&,
+            decltype(get_emplace_arg<4>(std::move(args)))>::value,
+        "");
+    static_assert(
+        std::is_same<Object&&, decltype(get_emplace_arg<5>(std::move(args)))>::
+            value,
+        "");
+    static_assert(
+        std::is_same<
+            const Object&,
+            decltype(get_emplace_arg<6>(std::move(args)))>::value,
+        "");
+    static_assert(
+        std::is_same<Object&&, decltype(get_emplace_arg<7>(std::move(args)))>::
+            value,
+        "");
+
+    // lvalue references to emplace_args should behave mostly like std::tuples.
+    // Note that get_emplace_arg<7>(args) does not compile, because
+    // folly::rvalue_reference_wrappers can only be unwrapped through an rvalue
+    // reference.
+    static_assert(
+        std::is_same<int&, decltype(get_emplace_arg<0>(args))>::value, "");
+    static_assert(
+        std::is_same<Object&, decltype(get_emplace_arg<1>(args))>::value, "");
+    static_assert(
+        std::is_same<Object&, decltype(get_emplace_arg<2>(args))>::value, "");
+    static_assert(
+        std::is_same<Object&, decltype(get_emplace_arg<3>(args))>::value, "");
+    static_assert(
+        std::is_same<Object&, decltype(get_emplace_arg<4>(args))>::value, "");
+    static_assert(
+        std::is_same<Object&, decltype(get_emplace_arg<5>(args))>::value, "");
+    static_assert(
+        std::is_same<const Object&, decltype(get_emplace_arg<6>(args))>::value,
+        "");
+  }
+}
+
+/**
+ * Test implicit unpacking.
+ */
+TEST(EmplaceIterator, ImplicitUnpack) {
+  static std::size_t multiCtrCnt;
+  static std::size_t pairCtrCnt;
+  static std::size_t tupleCtrCnt;
+
+  struct Object2 {
+    Object2(int, int) {
+      ++multiCtrCnt;
+    }
+    explicit Object2(const std::pair<int, int>&) {
+      ++pairCtrCnt;
+    }
+    explicit Object2(const std::tuple<int, int>&) {
+      ++tupleCtrCnt;
+    }
+  };
+
+  auto test = [](auto&& it, bool expectUnpack) {
+    multiCtrCnt = pairCtrCnt = tupleCtrCnt = 0;
+    it = std::make_pair(0, 0);
+    it = std::make_tuple(0, 0);
+    if (expectUnpack) {
+      EXPECT_EQ(multiCtrCnt, 2);
+      EXPECT_EQ(pairCtrCnt, 0);
+      EXPECT_EQ(tupleCtrCnt, 0);
+    } else {
+      EXPECT_EQ(multiCtrCnt, 0);
+      EXPECT_EQ(pairCtrCnt, 1);
+      EXPECT_EQ(tupleCtrCnt, 1);
+    }
+  };
+
+  Container<Object2> q;
+
+  test(emplacer(q, q.begin()), true);
+  test(emplacer<false>(q, q.begin()), false);
+  test(front_emplacer(q), true);
+  test(front_emplacer<false>(q), false);
+  test(back_emplacer(q), true);
+  test(back_emplacer<false>(q), false);
+}
diff --git a/folly/container/test/MergeTest.cpp b/folly/container/test/MergeTest.cpp
new file mode 100644 (file)
index 0000000..da05afe
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/container/Merge.h>
+
+#include <map>
+#include <vector>
+
+#include <folly/portability/GTest.h>
+
+TEST(MergeTest, NonOverlapping) {
+  std::vector<int> a = {0, 2, 4, 6};
+  std::vector<int> b = {1, 3, 5, 7};
+  std::vector<int> c;
+
+  folly::merge(a.begin(), a.end(),
+               b.begin(), b.end(),
+               std::back_inserter(c));
+  EXPECT_EQ(8, c.size());
+  for (size_t i = 0; i < 8; ++i) {
+    EXPECT_EQ(i, c[i]);
+  }
+}
+
+TEST(MergeTest, OverlappingInSingleInputRange) {
+  std::vector<std::pair<int, int>> a = {{0, 0}, {0, 1}};
+  std::vector<std::pair<int, int>> b = {{2, 2}, {3, 3}};
+  std::map<int, int> c;
+
+  folly::merge(a.begin(), a.end(),
+               b.begin(), b.end(),
+               std::inserter(c, c.begin()));
+  EXPECT_EQ(3, c.size());
+
+  // First value is inserted, second is not
+  EXPECT_EQ(c[0], 0);
+
+  EXPECT_EQ(c[2], 2);
+  EXPECT_EQ(c[3], 3);
+}
+
+TEST(MergeTest, OverlappingInDifferentInputRange) {
+  std::vector<std::pair<int, int>> a = {{0, 0}, {1, 1}};
+  std::vector<std::pair<int, int>> b = {{0, 2}, {3, 3}};
+  std::map<int, int> c;
+
+  folly::merge(a.begin(), a.end(),
+               b.begin(), b.end(),
+               std::inserter(c, c.begin()));
+  EXPECT_EQ(3, c.size());
+
+  // Value from a is inserted, value from b is not.
+  EXPECT_EQ(c[0], 0);
+
+  EXPECT_EQ(c[1], 1);
+  EXPECT_EQ(c[3], 3);
+}
diff --git a/folly/container/test/SparseByteSetBenchmark.cpp b/folly/container/test/SparseByteSetBenchmark.cpp
new file mode 100644 (file)
index 0000000..b5e8090
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/***
+ *  A benchmark comparing SparseByteSet to bitset<256> and bool[256].
+ */
+
+#include <folly/Benchmark.h>
+#include <folly/Format.h>
+#include <folly/container/SparseByteSet.h>
+#include <folly/portability/GFlags.h>
+#include <bitset>
+#include <random>
+#include <vector>
+
+using namespace std;
+using namespace folly;
+
+namespace {
+
+//  Interface-identical to SparseByteSet. So that we can do compile-time
+//  polymorphism.
+class BitSetWrapper {
+ public:
+  inline bool add(uint8_t i) {
+    auto r = !contains(i);
+    if (r) {
+      rep_[i] = true;
+    }
+    return r;
+  }
+  inline bool contains(uint8_t i) {
+    return rep_[i];
+  }
+ private:
+  bitset<256> rep_;
+};
+class BoolArraySet {
+ public:
+  BoolArraySet() {
+    memset(rep_, 0, sizeof(rep_));
+  }
+  inline bool add(uint8_t i) {
+    auto r = !contains(i);
+    if (r) {
+      rep_[i] = true;
+    }
+    return r;
+  }
+  inline bool contains(uint8_t i) {
+    return rep_[i];
+  }
+ private:
+  bool rep_[256];
+};
+
+template <typename Coll>
+void rand_bench(int iters, size_t size_add, size_t size_contains) {
+  BenchmarkSuspender braces;
+  vector<uint8_t> seq_add;
+  vector<uint8_t> seq_contains;
+  mt19937 rng;
+  uniform_int_distribution<uint8_t> dist;
+  for (size_t i = 0; i < size_add; ++i) {
+    seq_add.push_back(dist(rng));
+  }
+  for (size_t i = 0; i < size_contains; ++i) {
+    seq_contains.push_back(dist(rng));
+  }
+  braces.dismissing([&] {
+      while (iters--) {
+        Coll coll;
+        for (auto b : seq_add) {
+          coll.add(b);
+        }
+        bool q {};
+        for (auto b : seq_contains) {
+          q ^= coll.contains(b);
+        }
+        doNotOptimizeAway(q);
+      }
+  });
+}
+
+void setup_rand_bench() {
+  vector<pair<size_t, size_t>> rand_bench_params = {
+    {4, 4},
+    {4, 16},
+    {4, 64},
+    {4, 256},
+    {16, 4},
+    {16, 16},
+    {16, 64},
+    {16, 256},
+    {64, 4},
+    {64, 16},
+    {64, 64},
+    {64, 256},
+    {256, 4},
+    {256, 16},
+    {256, 64},
+    {256, 256},
+  };
+  for (auto kvp : rand_bench_params) {
+    size_t size_add, size_contains;
+    tie(size_add, size_contains) = kvp;
+    addBenchmark(
+        __FILE__,
+        sformat("bitset_rand_bench({}, {})",
+                size_add, size_contains).c_str(),
+        [=](int iters) {
+          rand_bench<BitSetWrapper>(iters, size_add, size_contains);
+          return iters;
+        });
+    addBenchmark(
+        __FILE__,
+        sformat("%bool_array_set_rand_bench({}, {})",
+                size_add, size_contains).c_str(),
+        [=](int iters) {
+          rand_bench<BoolArraySet>(iters, size_add, size_contains);
+          return iters;
+        });
+    addBenchmark(
+        __FILE__,
+        sformat("%sparse_byte_set_rand_bench({}, {})",
+                size_add, size_contains).c_str(),
+        [=](int iters) {
+          rand_bench<SparseByteSet>(iters, size_add, size_contains);
+          return iters;
+        });
+    addBenchmark(
+        __FILE__,
+        "-",
+        [](int) { return 0; });
+  }
+}
+
+}
+
+int main(int argc, char** argv) {
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+  setup_rand_bench();
+  runBenchmarks();
+  return 0;
+}
diff --git a/folly/container/test/SparseByteSetTest.cpp b/folly/container/test/SparseByteSetTest.cpp
new file mode 100644 (file)
index 0000000..b022794
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/container/SparseByteSet.h>
+
+#include <cstdint>
+#include <limits>
+#include <random>
+#include <set>
+
+#include <folly/portability/GTest.h>
+
+using namespace std;
+using namespace folly;
+
+namespace {
+
+class SparseByteSetTest : public testing::Test {
+ protected:
+  using lims = numeric_limits<uint8_t>;
+  SparseByteSet s;
+};
+
+}
+
+TEST_F(SparseByteSetTest, empty) {
+  for (auto c = lims::min(); c < lims::max(); ++c) {
+    EXPECT_FALSE(s.contains(c));
+  }
+}
+
+TEST_F(SparseByteSetTest, each) {
+  for (auto c = lims::min(); c < lims::max(); ++c) {
+    EXPECT_TRUE(s.add(c));
+    EXPECT_TRUE(s.contains(c));
+  }
+  for (auto c = lims::min(); c < lims::max(); ++c) {
+    EXPECT_FALSE(s.add(c));
+    EXPECT_TRUE(s.contains(c));
+  }
+}
+
+TEST_F(SparseByteSetTest, each_random) {
+  mt19937 rng;
+  uniform_int_distribution<uint16_t> dist{lims::min(), lims::max()};
+  set<uint8_t> added;
+  while (added.size() <= lims::max()) {
+    auto c = uint8_t(dist(rng));
+    EXPECT_EQ(added.count(c), s.contains(c));
+    EXPECT_EQ(!added.count(c), s.add(c));
+    added.insert(c);
+    EXPECT_TRUE(added.count(c)); // sanity
+    EXPECT_TRUE(s.contains(c));
+  }
+}
index badf9bdeecc5ed6048c0a4ac11553b12c242a172..e8949d3fd37a64615f21f70b94edbfb7e759f456 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <bitset>
 
-#include <folly/SparseByteSet.h>
+#include <folly/container/SparseByteSet.h>
 
 namespace folly {
 
index b0a2ae9fb48dbd20a4901f5cae507a6c66435e3f..3d5a2aa83c34433b0174399bbeccf3011771739d 100644 (file)
 #include <glog/logging.h>
 
 #include <folly/Exception.h>
-#include <folly/Foreach.h>
 #include <folly/Function.h>
 #include <folly/MicroSpinLock.h>
 #include <folly/Portability.h>
 #include <folly/ScopeGuard.h>
 #include <folly/SharedMutex.h>
+#include <folly/container/Foreach.h>
 #include <folly/memory/Malloc.h>
 #include <folly/portability/PThread.h>
 
index 6ac92df0e93667b13b7060466dbdabee5a4b42fd..425704c4d959d8f060da0bb5a509c7427a4658e5 100644 (file)
@@ -17,7 +17,7 @@ Using `folly/Benchmark.h` is very simple. Here's an example:
 
 ``` Cpp
     #include <folly/Benchmark.h>
-    #include <folly/Foreach.h>
+    #include <folly/container/Foreach.h>
     #include <vector>
     using namespace std;
     using namespace folly;
@@ -71,7 +71,7 @@ implicitly `unsigned`. Consider a slightly reworked example:
 
 ``` Cpp
     #include <folly/Benchmark.h>
-    #include <folly/Foreach.h>
+    #include <folly/container/Foreach.h>
     #include <vector>
     using namespace std;
     using namespace folly;
@@ -127,7 +127,7 @@ compares with it:
 
 ``` Cpp
     #include <folly/Benchmark.h>
-    #include <folly/Foreach.h>
+    #include <folly/container/Foreach.h>
     #include <vector>
     using namespace std;
     using namespace folly;
index df7dcec76dbdbf0e4583b449c59d18cf64a2ecf5..1300335fd373614effd93f821bfe9c3590e9238c 100644 (file)
@@ -19,8 +19,8 @@
 #include <glog/logging.h>
 
 #include <folly/Benchmark.h>
-#include <folly/Foreach.h>
 #include <folly/String.h>
+#include <folly/container/Foreach.h>
 #include <folly/gen/Base.h>
 #include <folly/gen/String.h>
 
index 3ccd81ec1b7dbf721fd61ac02b710de71e52aa23..d59fa77106ad34e6da229ef431b7edc8c64737f1 100644 (file)
@@ -16,7 +16,7 @@
 #include <folly/stats/Histogram.h>
 
 #include <folly/Benchmark.h>
-#include <folly/Foreach.h>
+#include <folly/container/Foreach.h>
 #include <folly/portability/GFlags.h>
 
 using folly::Histogram;
index 6f30341eb8407d759cad1b4b203cb847e2085afb..e30ec6c662ab484b2bdfe81dd842b959997bd3ca 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <glog/logging.h>
 
-#include <folly/Foreach.h>
+#include <folly/container/Foreach.h>
 #include <folly/portability/GTest.h>
 
 using std::chrono::seconds;
index 462849fb9bc9f82e049fbfccd31103f46bf14dc6..a9a4fee721c3b1d6f9c2f4468e3ec924087dcecf 100644 (file)
@@ -15,8 +15,8 @@
  */
 
 #include <folly/Benchmark.h>
-#include <folly/Foreach.h>
 #include <folly/String.h>
+#include <folly/container/Foreach.h>
 #include <algorithm>
 #include <iostream>
 #include <numeric>
index 4447463e39abbb4a573aef7455d9d7eb69310827..301a70548f015dea9ad998c9088a6cffb72762be 100644 (file)
@@ -27,9 +27,9 @@
 
 #include <glog/logging.h>
 
-#include <folly/Foreach.h>
 #include <folly/Memory.h>
 #include <folly/String.h>
+#include <folly/container/Foreach.h>
 #include <folly/memory/Arena.h>
 #include <folly/portability/GFlags.h>
 #include <folly/portability/GTest.h>
index f73f3f06f6c3ecb4960ca4ef06b668f2b5613b4e..fb00363d5f6702a1979eb78ee90c75d5389107af 100644 (file)
@@ -20,7 +20,7 @@
 
 #include <folly/Benchmark.h>
 #include <folly/CppAttributes.h>
-#include <folly/Foreach.h>
+#include <folly/container/Foreach.h>
 
 #include <array>
 #include <limits>
index 0f189590d92e6a20e0b70af94cc7fa64b6840e91..5fbbeed3a42bbb5c41b376b2c975d9b281a9ce72 100644 (file)
@@ -21,7 +21,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <folly/Conv.h>
-#include <folly/Foreach.h>
+#include <folly/container/Foreach.h>
 #include <folly/portability/GTest.h>
 
 #include <algorithm>
diff --git a/folly/test/EnumerateTest.cpp b/folly/test/EnumerateTest.cpp
deleted file mode 100644 (file)
index 67ffe31..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <array>
-#include <string>
-#include <vector>
-
-#include <folly/Enumerate.h>
-#include <folly/Range.h>
-#include <folly/portability/GTest.h>
-
-TEST(Enumerate, Basic) {
-  std::vector<std::string> v = {"abc", "a", "ab"};
-  size_t i = 0;
-  for (auto it : folly::enumerate(v)) {
-    EXPECT_EQ(it.index, i);
-    EXPECT_EQ(*it, v[i]);
-    EXPECT_EQ(it->size(), v[i].size());
-
-    // Test mutability.
-    std::string newValue = "x";
-    *it = newValue;
-    EXPECT_EQ(newValue, v[i]);
-
-    ++i;
-  }
-
-  EXPECT_EQ(i, v.size());
-}
-
-TEST(Enumerate, Temporary) {
-  std::vector<std::string> v = {"abc", "a", "ab"};
-  size_t i = 0;
-  for (auto it : folly::enumerate(decltype(v)(v))) { // Copy v.
-    EXPECT_EQ(it.index, i);
-    EXPECT_EQ(*it, v[i]);
-    EXPECT_EQ(it->size(), v[i].size());
-    ++i;
-  }
-
-  EXPECT_EQ(i, v.size());
-};
-
-namespace {
-
-template <class T>
-struct IsConstReference {
-  constexpr static bool value = false;
-};
-template <class T>
-struct IsConstReference<const T&> {
-  constexpr static bool value = true;
-};
-
-} // namespace
-
-TEST(Enumerate, BasicConstArg) {
-  const std::vector<std::string> v = {"abc", "a", "ab"};
-  size_t i = 0;
-  for (auto it : folly::enumerate(v)) {
-    static_assert(
-        IsConstReference<decltype(*it)>::value, "Enumerating a const vector");
-    EXPECT_EQ(it.index, i);
-    EXPECT_EQ(*it, v[i]);
-    EXPECT_EQ(it->size(), v[i].size());
-    ++i;
-  }
-
-  EXPECT_EQ(i, v.size());
-}
-
-TEST(Enumerate, BasicConstEnumerate) {
-  std::vector<std::string> v = {"abc", "a", "ab"};
-  size_t i = 0;
-  for (const auto it : folly::enumerate(v)) {
-    static_assert(IsConstReference<decltype(*it)>::value, "Const enumeration");
-    EXPECT_EQ(it.index, i);
-    EXPECT_EQ(*it, v[i]);
-    EXPECT_EQ(it->size(), v[i].size());
-    ++i;
-  }
-
-  EXPECT_EQ(i, v.size());
-}
-
-TEST(Enumerate, TemporaryConstEnumerate) {
-  std::vector<std::string> v = {"abc", "a", "ab"};
-  size_t i = 0;
-  for (const auto it : folly::enumerate(decltype(v)(v))) { // Copy v.
-    static_assert(IsConstReference<decltype(*it)>::value, "Const enumeration");
-    EXPECT_EQ(it.index, i);
-    EXPECT_EQ(*it, v[i]);
-    EXPECT_EQ(it->size(), v[i].size());
-    ++i;
-  }
-
-  EXPECT_EQ(i, v.size());
-}
-
-TEST(Enumerate, RangeSupport) {
-  std::vector<std::string> v = {"abc", "a", "ab"};
-  size_t i = 0;
-  for (const auto it : folly::enumerate(folly::range(v))) {
-    EXPECT_EQ(it.index, i);
-    EXPECT_EQ(*it, v[i]);
-    EXPECT_EQ(it->size(), v[i].size());
-    ++i;
-  }
-
-  EXPECT_EQ(i, v.size());
-}
-
-TEST(Enumerate, EmptyRange) {
-  std::vector<std::string> v;
-  for (auto it : folly::enumerate(v)) {
-    (void)it; // Silence warnings.
-    ADD_FAILURE();
-  }
-}
-
-class CStringRange {
-  const char* cstr;
-
- public:
-  struct Sentinel {};
-
-  explicit CStringRange(const char* cstr) : cstr(cstr) {}
-
-  const char* begin() const {
-    return cstr;
-  }
-  Sentinel end() const {
-    return Sentinel{};
-  }
-};
-
-bool operator==(const char* c, CStringRange::Sentinel) {
-  return *c == 0;
-}
-
-TEST(Enumerate, Cpp17Support) {
-  std::array<char, 5> test = {"test"};
-  // Can't use range based for loop until C++17, so test manually
-  // Equivalent to:
-  // for (const auto it : folly::enumerate(CStringRange{test.data()})) { ... }
-  {
-    auto&& enumerate = folly::enumerate(CStringRange{test.data()});
-    auto begin = enumerate.begin();
-    auto end = enumerate.end();
-    for (; begin != end; ++begin) {
-      const auto it = *begin;
-
-      ASSERT_LT(it.index, test.size());
-      EXPECT_EQ(*it, test[it.index]);
-    }
-  }
-}
diff --git a/folly/test/EvictingCacheMapTest.cpp b/folly/test/EvictingCacheMapTest.cpp
deleted file mode 100644 (file)
index 85a4a5e..0000000
+++ /dev/null
@@ -1,635 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <set>
-
-#include <folly/EvictingCacheMap.h>
-#include <folly/portability/GTest.h>
-
-using namespace folly;
-
-TEST(EvictingCacheMap, SanityTest) {
-  EvictingCacheMap<int, int> map(0);
-
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  EXPECT_FALSE(map.exists(1));
-  map.set(1, 1);
-  EXPECT_EQ(1, map.size());
-  EXPECT_FALSE(map.empty());
-  EXPECT_EQ(1, map.get(1));
-  EXPECT_TRUE(map.exists(1));
-  map.set(1, 2);
-  EXPECT_EQ(1, map.size());
-  EXPECT_FALSE(map.empty());
-  EXPECT_EQ(2, map.get(1));
-  EXPECT_TRUE(map.exists(1));
-  map.erase(1);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  EXPECT_FALSE(map.exists(1));
-
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  EXPECT_FALSE(map.exists(1));
-  map.set(1, 1);
-  EXPECT_EQ(1, map.size());
-  EXPECT_FALSE(map.empty());
-  EXPECT_EQ(1, map.get(1));
-  EXPECT_TRUE(map.exists(1));
-  map.set(1, 2);
-  EXPECT_EQ(1, map.size());
-  EXPECT_FALSE(map.empty());
-  EXPECT_EQ(2, map.get(1));
-  EXPECT_TRUE(map.exists(1));
-
-  EXPECT_FALSE(map.exists(2));
-  map.set(2, 1);
-  EXPECT_TRUE(map.exists(2));
-  EXPECT_EQ(2, map.size());
-  EXPECT_FALSE(map.empty());
-  EXPECT_EQ(1, map.get(2));
-  map.set(2, 2);
-  EXPECT_EQ(2, map.size());
-  EXPECT_FALSE(map.empty());
-  EXPECT_EQ(2, map.get(2));
-  EXPECT_TRUE(map.exists(2));
-  map.erase(2);
-  EXPECT_EQ(1, map.size());
-  EXPECT_FALSE(map.empty());
-  EXPECT_FALSE(map.exists(2));
-  map.erase(1);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  EXPECT_FALSE(map.exists(1));
-}
-
-
-TEST(EvictingCacheMap, PruneTest) {
-  EvictingCacheMap<int, int> map(0);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  map.prune(1000000);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  map.prune(100);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  map.prune(99);
-  EXPECT_EQ(1, map.size());
-  EXPECT_FALSE(map.empty());
-  for (int i = 0; i < 99; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  EXPECT_TRUE(map.exists(99));
-  EXPECT_EQ(99, map.get(99));
-
-  map.prune(100);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  map.prune(90);
-  EXPECT_EQ(10, map.size());
-  EXPECT_FALSE(map.empty());
-  for (int i = 0; i < 90; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  for (int i = 90; i < 100; i++) {
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-}
-
-TEST(EvictingCacheMap, PruneHookTest) {
-  EvictingCacheMap<int, int> map(0);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  int sum = 0;
-  auto pruneCb = [&](int&& k, int&& v) {
-    EXPECT_EQ(k, v);
-    sum += k;
-  };
-
-  map.setPruneHook(pruneCb);
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  map.prune(1000000);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  EXPECT_EQ((99 * 100) / 2, sum);
-  sum = 0;
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  map.prune(100);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  EXPECT_EQ((99 * 100) / 2, sum);
-  sum = 0;
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  map.prune(99);
-  EXPECT_EQ(1, map.size());
-  EXPECT_FALSE(map.empty());
-  for (int i = 0; i < 99; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  EXPECT_TRUE(map.exists(99));
-  EXPECT_EQ(99, map.get(99));
-
-  EXPECT_EQ((98 * 99) / 2, sum);
-  sum = 0;
-
-  map.prune(100);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  EXPECT_EQ(99, sum);
-  sum = 0;
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  map.prune(90);
-  EXPECT_EQ(10, map.size());
-  EXPECT_FALSE(map.empty());
-  for (int i = 0; i < 90; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  for (int i = 90; i < 100; i++) {
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-  EXPECT_EQ((89 * 90) / 2, sum);
-  sum = 0;
-}
-
-TEST(EvictingCacheMap, SetMaxSize) {
-  EvictingCacheMap<int, int> map(100, 20);
-  for (int i = 0; i < 90; i++) {
-    map.set(i, i);
-    EXPECT_TRUE(map.exists(i));
-  }
-
-  EXPECT_EQ(90, map.size());
-  map.setMaxSize(50);
-  EXPECT_EQ(map.size(), 50);
-
-  for (int i = 0; i < 90; i++) {
-    map.set(i, i);
-    EXPECT_TRUE(map.exists(i));
-  }
-  EXPECT_EQ(40, map.size());
-  map.setMaxSize(0);
-  EXPECT_EQ(40, map.size());
-  map.setMaxSize(10);
-  EXPECT_EQ(10, map.size());
-}
-
-TEST(EvictingCacheMap, SetClearSize) {
-  EvictingCacheMap<int, int> map(100, 20);
-  for (int i = 0; i < 90; i++) {
-    map.set(i, i);
-    EXPECT_TRUE(map.exists(i));
-  }
-
-  EXPECT_EQ(90, map.size());
-  map.setClearSize(40);
-  map.setMaxSize(50);
-  EXPECT_EQ(map.size(), 50);
-
-  for (int i = 0; i < 90; i++) {
-    map.set(i, i);
-    EXPECT_TRUE(map.exists(i));
-  }
-  EXPECT_EQ(20, map.size());
-  map.setMaxSize(0);
-  EXPECT_EQ(20, map.size());
-  map.setMaxSize(10);
-  EXPECT_EQ(0, map.size());
-}
-
-TEST(EvictingCacheMap, DestructorInvocationTest) {
-  struct SumInt {
-    SumInt(int val, int* ref) : val(val), ref(ref) { }
-    ~SumInt() {
-      *ref += val;
-    }
-    int val;
-    int* ref;
-  };
-
-  int sum;
-  EvictingCacheMap<int, SumInt> map(0);
-
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, SumInt(i, &sum));
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i).val);
-  }
-
-  sum = 0;
-  map.prune(1000000);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  EXPECT_EQ((99 * 100) / 2, sum);
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, SumInt(i, &sum));
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i).val);
-  }
-
-  sum = 0;
-  map.prune(100);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  EXPECT_EQ((99 * 100) / 2, sum);
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, SumInt(i, &sum));
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i).val);
-  }
-
-  sum = 0;
-  map.prune(99);
-  EXPECT_EQ(1, map.size());
-  EXPECT_FALSE(map.empty());
-  for (int i = 0; i < 99; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  EXPECT_TRUE(map.exists(99));
-  EXPECT_EQ(99, map.get(99).val);
-
-  EXPECT_EQ((98 * 99) / 2, sum);
-
-  sum = 0;
-  map.prune(100);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  EXPECT_EQ(99, sum);
-  for (int i = 0; i < 100; i++) {
-    map.set(i, SumInt(i, &sum));
-    EXPECT_EQ(i + 1, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i).val);
-  }
-
-  sum = 0;
-  map.prune(90);
-  EXPECT_EQ(10, map.size());
-  EXPECT_FALSE(map.empty());
-  for (int i = 0; i < 90; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  for (int i = 90; i < 100; i++) {
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i).val);
-  }
-  EXPECT_EQ((89 * 90) / 2, sum);
-  sum = 0;
-}
-
-TEST(EvictingCacheMap, LruSanityTest) {
-  EvictingCacheMap<int, int> map(10);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_GE(10, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  EXPECT_EQ(10, map.size());
-  EXPECT_FALSE(map.empty());
-  for (int i = 0; i < 90; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  for (int i = 90; i < 100; i++) {
-    EXPECT_TRUE(map.exists(i));
-  }
-}
-
-TEST(EvictingCacheMap, LruPromotionTest) {
-  EvictingCacheMap<int, int> map(10);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_GE(10, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-    for (int j = 0; j < std::min(i + 1, 9); j++) {
-      EXPECT_TRUE(map.exists(j));
-      EXPECT_EQ(j, map.get(j));
-    }
-  }
-
-  EXPECT_EQ(10, map.size());
-  EXPECT_FALSE(map.empty());
-  for (int i = 0; i < 9; i++) {
-    EXPECT_TRUE(map.exists(i));
-  }
-  EXPECT_TRUE(map.exists(99));
-  for (int i = 10; i < 99; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-}
-
-TEST(EvictingCacheMap, LruNoPromotionTest) {
-  EvictingCacheMap<int, int> map(10);
-  EXPECT_EQ(0, map.size());
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < 100; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-
-  for (int i = 0; i < 100; i++) {
-    map.set(i, i);
-    EXPECT_GE(10, map.size());
-    EXPECT_FALSE(map.empty());
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-    for (int j = 0; j < std::min(i + 1, 9); j++) {
-      if (map.exists(j)) {
-        EXPECT_EQ(j, map.getWithoutPromotion(j));
-      }
-    }
-  }
-
-  EXPECT_EQ(10, map.size());
-  EXPECT_FALSE(map.empty());
-  for (int i = 0; i < 90; i++) {
-    EXPECT_FALSE(map.exists(i));
-  }
-  for (int i = 90; i < 100; i++) {
-    EXPECT_TRUE(map.exists(i));
-  }
-}
-
-TEST(EvictingCacheMap, IteratorSanityTest) {
-  const int nItems = 1000;
-  EvictingCacheMap<int, int> map(nItems);
-  EXPECT_TRUE(map.begin() == map.end());
-  for (int i = 0; i < nItems; i++) {
-    EXPECT_FALSE(map.exists(i));
-    map.set(i, i * 2);
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i * 2, map.get(i));
-  }
-
-  std::set<int> seen;
-  for (auto& it : map) {
-    EXPECT_EQ(0, seen.count(it.first));
-    seen.insert(it.first);
-    EXPECT_EQ(it.first * 2, it.second);
-  }
-  EXPECT_EQ(nItems, seen.size());
-}
-
-TEST(EvictingCacheMap, FindTest) {
-  const int nItems = 1000;
-  EvictingCacheMap<int, int> map(nItems);
-  for (int i = 0; i < nItems; i++) {
-    map.set(i * 2, i * 2);
-    EXPECT_TRUE(map.exists(i * 2));
-    EXPECT_EQ(i * 2, map.get(i * 2));
-  }
-  for (int i = 0; i < nItems * 2; i++) {
-    if (i % 2 == 0) {
-      auto it = map.find(i);
-      EXPECT_FALSE(it == map.end());
-      EXPECT_EQ(i, it->first);
-      EXPECT_EQ(i, it->second);
-    } else {
-      EXPECT_TRUE( map.find(i) == map.end());
-    }
-  }
-  for (int i = nItems * 2 - 1; i >= 0; i--) {
-    if (i % 2 == 0) {
-      auto it = map.find(i);
-      EXPECT_FALSE(it == map.end());
-      EXPECT_EQ(i, it->first);
-      EXPECT_EQ(i, it->second);
-    } else {
-      EXPECT_TRUE(map.find(i) == map.end());
-    }
-  }
-  EXPECT_EQ(0, map.begin()->first);
-}
-
-TEST(EvictingCacheMap, FindWithoutPromotionTest) {
-  const int nItems = 1000;
-  EvictingCacheMap<int, int> map(nItems);
-  for (int i = 0; i < nItems; i++) {
-    map.set(i * 2, i * 2);
-    EXPECT_TRUE(map.exists(i * 2));
-    EXPECT_EQ(i * 2, map.get(i * 2));
-  }
-  for (int i = nItems * 2 - 1; i >= 0; i--) {
-    if (i % 2 == 0) {
-      auto it = map.findWithoutPromotion(i);
-      EXPECT_FALSE(it == map.end());
-      EXPECT_EQ(i, it->first);
-      EXPECT_EQ(i, it->second);
-    } else {
-      EXPECT_TRUE(map.findWithoutPromotion(i) == map.end());
-    }
-  }
-  EXPECT_EQ((nItems - 1) * 2, map.begin()->first);
-}
-
-TEST(EvictingCacheMap, IteratorOrderingTest) {
-  const int nItems = 1000;
-  EvictingCacheMap<int, int> map(nItems);
-  for (int i = 0; i < nItems; i++) {
-    map.set(i, i);
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  int expected = nItems - 1;
-  for (auto it = map.begin(); it != map.end(); ++it) {
-    EXPECT_EQ(expected, it->first);
-    expected--;
-  }
-
-  expected = 0;
-  for (auto it = map.rbegin(); it != map.rend(); ++it) {
-    EXPECT_EQ(expected, it->first);
-    expected++;
-  }
-
-  {
-    auto it = map.end();
-    expected = 0;
-    EXPECT_TRUE(it != map.begin());
-    do {
-      --it;
-      EXPECT_EQ(expected, it->first);
-      expected++;
-    } while (it != map.begin());
-    EXPECT_EQ(nItems, expected);
-  }
-
-  {
-    auto it = map.rend();
-    expected = nItems - 1;
-    do {
-      --it;
-      EXPECT_EQ(expected, it->first);
-      expected--;
-    } while (it != map.rbegin());
-    EXPECT_EQ(-1, expected);
-  }
-}
-
-TEST(EvictingCacheMap, MoveTest) {
-  const int nItems = 1000;
-  EvictingCacheMap<int, int> map(nItems);
-  for (int i = 0; i < nItems; i++) {
-    map.set(i, i);
-    EXPECT_TRUE(map.exists(i));
-    EXPECT_EQ(i, map.get(i));
-  }
-
-  EvictingCacheMap<int, int> map2 = std::move(map);
-  EXPECT_TRUE(map.empty());
-  for (int i = 0; i < nItems; i++) {
-    EXPECT_TRUE(map2.exists(i));
-    EXPECT_EQ(i, map2.get(i));
-  }
-}
index 342ede964f556b3d93eee2532bf2f0cd203b0241..003eee88ad8dbcf3220a46ef2ff21f8e754758e8 100644 (file)
@@ -27,8 +27,8 @@
 #include <boost/random.hpp>
 
 #include <folly/Benchmark.h>
-#include <folly/Foreach.h>
 #include <folly/Random.h>
+#include <folly/container/Foreach.h>
 #include <folly/portability/GFlags.h>
 
 using namespace std;
index 6ded8b4c0215a09e55837d88fe60c1c73c2ed636..629111db23f3919d9ce9807172edf4c325971ecf 100644 (file)
@@ -29,9 +29,9 @@
 #include <boost/random.hpp>
 
 #include <folly/Conv.h>
-#include <folly/Foreach.h>
 #include <folly/Portability.h>
 #include <folly/Random.h>
+#include <folly/container/Foreach.h>
 #include <folly/portability/GTest.h>
 
 using namespace std;
index 504c1c51d0bea37b6fa7256c2f0e744a58cc666f..c46c94e0ff344fcf03798939157f3888379ddce5 100644 (file)
@@ -25,9 +25,9 @@
 #include <folly/Benchmark.h>
 #include <folly/FBString.h>
 #include <folly/FBVector.h>
-#include <folly/Foreach.h>
 #include <folly/Random.h>
 #include <folly/Traits.h>
+#include <folly/container/Foreach.h>
 #include <folly/portability/GFlags.h>
 #include <folly/portability/GTest.h>
 
index 3194ee742a312eb1a92dbd37d738a8214876b114..2920a4f9594f66dc03658afc053574d3997f7fb1 100644 (file)
@@ -26,9 +26,9 @@
 #include <boost/random.hpp>
 
 #include <folly/FBString.h>
-#include <folly/Foreach.h>
 #include <folly/Random.h>
 #include <folly/Traits.h>
+#include <folly/container/Foreach.h>
 #include <folly/portability/GTest.h>
 
 using namespace std;
diff --git a/folly/test/ForeachBenchmark.cpp b/folly/test/ForeachBenchmark.cpp
deleted file mode 100644 (file)
index 63988c7..0000000
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/Foreach.h>
-
-#include <folly/Benchmark.h>
-#include <folly/portability/GTest.h>
-
-#include <map>
-
-using namespace folly;
-using namespace folly::detail;
-
-// Benchmarks:
-// 1. Benchmark iterating through the man with FOR_EACH, and also assign
-//    iter->first and iter->second to local vars inside the FOR_EACH loop.
-// 2. Benchmark iterating through the man with FOR_EACH, but use iter->first and
-//    iter->second as is, without assigning to local variables.
-// 3. Use FOR_EACH_KV loop to iterate through the map.
-
-std::map<int, std::string> bmMap; // For use in benchmarks below.
-std::vector<int> vec_one;
-std::vector<int> vec_two;
-
-void setupBenchmark(size_t iters) {
-  bmMap.clear();
-  for (size_t i = 0; i < iters; ++i) {
-    bmMap[i] = "teststring";
-  }
-
-  vec_one.clear();
-  vec_two.clear();
-  vec_one.resize(iters);
-  vec_two.resize(iters);
-}
-
-BENCHMARK(ForEachFunctionNoAssign, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    folly::for_each(bmMap, [&](auto& key_val_pair) {
-      sumKeys += key_val_pair.first;
-      sumValues += key_val_pair.second;
-    });
-    doNotOptimizeAway(sumKeys);
-  });
-}
-
-BENCHMARK(StdForEachFunctionNoAssign, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
-      sumKeys += key_val_pair.first;
-      sumValues += key_val_pair.second;
-    });
-    doNotOptimizeAway(sumKeys);
-  });
-}
-
-BENCHMARK(RangeBasedForLoopNoAssign, iters) {
-  BenchmarkSuspender suspender;
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    for (auto& key_val_pair : bmMap) {
-      sumKeys += key_val_pair.first;
-      sumValues += key_val_pair.second;
-    }
-    doNotOptimizeAway(sumKeys);
-  });
-}
-
-BENCHMARK(ManualLoopNoAssign, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {
-      sumKeys += iter->first;
-      sumValues += iter->second;
-    }
-    doNotOptimizeAway(sumKeys);
-  });
-}
-
-BENCHMARK(ForEachFunctionAssign, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    folly::for_each(bmMap, [&](auto& key_val_pair) {
-      const int k = key_val_pair.first;
-      const std::string v = key_val_pair.second;
-      sumKeys += k;
-      sumValues += v;
-    });
-  });
-}
-
-BENCHMARK(StdForEachFunctionAssign, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
-      const int k = key_val_pair.first;
-      const std::string v = key_val_pair.second;
-      sumKeys += k;
-      sumValues += v;
-    });
-  });
-}
-
-BENCHMARK(RangeBasedForLoopAssign, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    for (auto& key_val_pair : bmMap) {
-      const int k = key_val_pair.first;
-      const std::string v = key_val_pair.second;
-      sumKeys += k;
-      sumValues += v;
-    }
-  });
-}
-
-BENCHMARK(ManualLoopAssign, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {
-      const int k = iter->first;
-      const std::string v = iter->second;
-      sumKeys += k;
-      sumValues += v;
-    }
-  });
-}
-
-BENCHMARK(ForEachFunctionNoAssignWithIndexManipulation, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    folly::for_each(bmMap, [&](auto& key_val_pair, auto index) {
-      sumKeys += key_val_pair.first;
-      sumValues += key_val_pair.second;
-      sumValues += index;
-    });
-  });
-}
-
-BENCHMARK(StdForEachFunctionNoAssignWithIndexManipulation, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    auto index = std::size_t{0};
-    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
-      sumKeys += key_val_pair.first;
-      sumValues += key_val_pair.second;
-      sumValues += index;
-      ++index;
-    });
-  });
-}
-
-BENCHMARK(RangeBasedForLoopNoAssignWithIndexManipulation, iters) {
-  BenchmarkSuspender suspender;
-
-  int sumKeys = 0;
-  std::string sumValues;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    auto index = std::size_t{0};
-    for (auto& key_val_pair : bmMap) {
-      sumKeys += key_val_pair.first;
-      sumValues += key_val_pair.second;
-      sumValues += index;
-    }
-  });
-}
-
-BENCHMARK(ForEachFunctionFetch, iters) {
-  BenchmarkSuspender suspender;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    folly::for_each(bmMap, [&](auto& key_val_pair, auto index) {
-      folly::fetch(vec_one, index) = key_val_pair.first;
-    });
-  });
-}
-
-BENCHMARK(StdForEachFunctionFetch, iters) {
-  BenchmarkSuspender suspender;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    auto index = std::size_t{0};
-    std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
-      *(vec_one.begin() + index++) = key_val_pair.first;
-    });
-  });
-}
-
-BENCHMARK(ForLoopFetch, iters) {
-  BenchmarkSuspender suspender;
-  setupBenchmark(iters);
-
-  suspender.dismissing([&]() {
-    auto index = std::size_t{0};
-    for (auto& key_val_pair : bmMap) {
-      *(vec_one.begin() + index++) = key_val_pair.first;
-    }
-  });
-}
-
-BENCHMARK(ForEachKVNoMacroAssign, iters) {
-  int sumKeys = 0;
-  std::string sumValues;
-
-  BENCHMARK_SUSPEND { setupBenchmark(iters); }
-
-  FOR_EACH(iter, bmMap) {
-    const int k = iter->first;
-    const std::string v = iter->second;
-    sumKeys += k;
-    sumValues += v;
-  }
-}
-
-BENCHMARK(ForEachKVNoMacroNoAssign, iters) {
-  int sumKeys = 0;
-  std::string sumValues;
-
-  BENCHMARK_SUSPEND { setupBenchmark(iters); }
-
-  FOR_EACH(iter, bmMap) {
-    sumKeys += iter->first;
-    sumValues += iter->second;
-  }
-}
-
-BENCHMARK(ForEachKVMacro, iters) {
-  int sumKeys = 0;
-  std::string sumValues;
-
-  BENCHMARK_SUSPEND { setupBenchmark(iters); }
-
-  FOR_EACH_KV(k, v, bmMap) {
-    sumKeys += k;
-    sumValues += v;
-  }
-}
-
-BENCHMARK(ForEachManual, iters) {
-  int sum = 1;
-  for (size_t i = 1; i < iters; ++i) {
-    sum *= i;
-  }
-  doNotOptimizeAway(sum);
-}
-
-BENCHMARK(ForEachRange, iters) {
-  int sum = 1;
-  FOR_EACH_RANGE(i, 1, iters) { sum *= i; }
-  doNotOptimizeAway(sum);
-}
-
-BENCHMARK(ForEachDescendingManual, iters) {
-  int sum = 1;
-  for (size_t i = iters; i-- > 1;) {
-    sum *= i;
-  }
-  doNotOptimizeAway(sum);
-}
-
-BENCHMARK(ForEachRangeR, iters) {
-  int sum = 1;
-  FOR_EACH_RANGE_R(i, 1U, iters) { sum *= i; }
-  doNotOptimizeAway(sum);
-}
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-  gflags::ParseCommandLineFlags(&argc, &argv, true);
-  auto r = RUN_ALL_TESTS();
-  if (r) {
-    return r;
-  }
-  runBenchmarks();
-  return 0;
-}
diff --git a/folly/test/ForeachTest.cpp b/folly/test/ForeachTest.cpp
deleted file mode 100644 (file)
index 4833166..0000000
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/Foreach.h>
-
-#include <array>
-#include <initializer_list>
-#include <iterator>
-#include <list>
-#include <map>
-#include <string>
-#include <tuple>
-#include <vector>
-
-#include <folly/portability/GTest.h>
-
-using namespace folly;
-using namespace folly::detail;
-
-namespace folly {
-namespace test {
-
-class TestRValueConstruct {
- public:
-  TestRValueConstruct() = default;
-  TestRValueConstruct(TestRValueConstruct&&) noexcept {
-    this->constructed_from_rvalue = true;
-  }
-  TestRValueConstruct(const TestRValueConstruct&) {
-    this->constructed_from_rvalue = false;
-  }
-  TestRValueConstruct& operator=(const TestRValueConstruct&) = delete;
-  TestRValueConstruct& operator=(TestRValueConstruct&&) = delete;
-
-  bool constructed_from_rvalue{false};
-};
-
-class TestAdlIterable {
- public:
-  std::vector<int> vec{0, 1, 2, 3};
-};
-
-auto begin(TestAdlIterable& instance) {
-  return instance.vec.begin();
-}
-auto begin(const TestAdlIterable& instance) {
-  return instance.vec.begin();
-}
-auto end(TestAdlIterable& instance) {
-  return instance.vec.end();
-}
-auto end(const TestAdlIterable& instance) {
-  return instance.vec.end();
-}
-
-class TestBothIndexingAndIter {
- public:
-  class Iterator {
-   public:
-    using difference_type = std::size_t;
-    using value_type = int;
-    using pointer = int*;
-    using reference = int&;
-    using iterator_category = std::random_access_iterator_tag;
-    int& operator*() {
-      return this->val;
-    }
-    Iterator operator+(int) {
-      return *this;
-    }
-    explicit Iterator(int& val_in) : val{val_in} {}
-    int& val;
-  };
-  auto begin() {
-    this->called_begin = true;
-    return Iterator{val};
-  }
-  auto end() {
-    return Iterator{val};
-  }
-  int& operator[](int) {
-    return this->val;
-  }
-
-  int val{0};
-  bool called_begin = false;
-};
-} // namespace test
-} // namespace folly
-
-TEST(Foreach, ForEachFunctionBasic) {
-  auto range = std::make_tuple(1, 2, 3);
-  auto result_range = std::vector<int>{};
-  auto correct_result_range = std::vector<int>{1, 2, 3};
-
-  folly::for_each(range, [&](auto ele) { result_range.push_back(ele); });
-
-  EXPECT_TRUE(std::equal(
-      result_range.begin(), result_range.end(), correct_result_range.begin()));
-}
-
-TEST(Foreach, ForEachFunctionBasicRuntimeOneArg) {
-  auto range = std::vector<int>{1, 2, 3};
-  auto current = 0;
-  folly::for_each(range, [&](auto ele) {
-    if (current == 0) {
-      EXPECT_EQ(ele, 1);
-    } else if (current == 1) {
-      EXPECT_EQ(ele, 2);
-    } else {
-      EXPECT_EQ(ele, 3);
-    }
-    ++current;
-  });
-}
-
-TEST(Foreach, ForEachFunctionBasicRuntimeTwoArg) {
-  auto range = std::vector<int>{1, 2, 3};
-  folly::for_each(range, [](auto ele, auto index) {
-    EXPECT_TRUE(index < 3);
-    if (index == 0) {
-      EXPECT_EQ(ele, 1);
-    } else if (index == 1) {
-      EXPECT_EQ(ele, 2);
-    } else if (index == 2) {
-      EXPECT_EQ(ele, 3);
-    }
-  });
-}
-
-TEST(Foreach, ForEachFunctionBasicRuntimeThreeArg) {
-  auto range = std::list<int>{1, 2, 3};
-  auto result_range = std::list<int>{1, 3};
-  folly::for_each(range, [&](auto ele, auto, auto iter) {
-    if (ele == 2) {
-      range.erase(iter);
-    }
-  });
-  EXPECT_TRUE(std::equal(range.begin(), range.end(), result_range.begin()));
-}
-
-TEST(Foreach, ForEachFunctionBasicTupleOneArg) {
-  auto range = std::make_tuple(1, 2, 3);
-  auto current = 0;
-  folly::for_each(range, [&](auto ele) {
-    if (current == 0) {
-      EXPECT_EQ(ele, 1);
-    } else if (current == 1) {
-      EXPECT_EQ(ele, 2);
-    } else {
-      EXPECT_EQ(ele, 3);
-    }
-    ++current;
-  });
-}
-
-TEST(Foreach, ForEachFunctionBasicTupleTwoArg) {
-  auto range = std::make_tuple(1, 2, 3);
-  folly::for_each(range, [](auto ele, auto index) {
-    EXPECT_TRUE(index < 3);
-    if (index == 0) {
-      EXPECT_EQ(ele, 1);
-    } else if (index == 1) {
-      EXPECT_EQ(ele, 2);
-    } else if (index == 2) {
-      EXPECT_EQ(ele, 3);
-    }
-  });
-}
-
-TEST(Foreach, ForEachFunctionBreakRuntimeOneArg) {
-  auto range = std::vector<int>{1, 2, 3};
-  auto iterations = 0;
-  folly::for_each(range, [&](auto) {
-    ++iterations;
-    if (iterations == 1) {
-      return folly::loop_break;
-    }
-    return folly::loop_continue;
-  });
-  EXPECT_EQ(iterations, 1);
-}
-
-TEST(Foreach, ForEachFunctionBreakRuntimeTwoArg) {
-  auto range = std::vector<int>{1, 2, 3};
-  auto iterations = 0;
-  folly::for_each(range, [&](auto, auto index) {
-    ++iterations;
-    if (index == 1) {
-      return folly::loop_break;
-    }
-    return folly::loop_continue;
-  });
-  EXPECT_EQ(iterations, 2);
-}
-
-TEST(Foreach, ForEachFunctionBreakRuntimeThreeArg) {
-  auto range = std::vector<int>{1, 2, 3};
-  auto iterations = 0;
-  folly::for_each(range, [&](auto, auto index, auto) {
-    ++iterations;
-    if (index == 1) {
-      return folly::loop_break;
-    }
-    return folly::loop_continue;
-  });
-  EXPECT_EQ(iterations, 2);
-}
-
-TEST(Foreach, ForEachFunctionBreakTupleOneArg) {
-  auto range = std::vector<int>{1, 2, 3};
-  auto iterations = 0;
-  folly::for_each(range, [&](auto) {
-    ++iterations;
-    if (iterations == 1) {
-      return folly::loop_break;
-    }
-    return folly::loop_continue;
-  });
-  EXPECT_EQ(iterations, 1);
-}
-
-TEST(Foreach, ForEachFunctionBreakTupleTwoArg) {
-  auto range = std::vector<int>{1, 2, 3};
-  auto iterations = 0;
-  folly::for_each(range, [&](auto, auto index) {
-    ++iterations;
-    if (index == 1) {
-      return folly::loop_break;
-    }
-    return folly::loop_continue;
-  });
-  EXPECT_EQ(iterations, 2);
-}
-
-TEST(Foreach, ForEachFunctionArray) {
-  auto range = std::array<int, 3>{{1, 2, 3}};
-  auto iterations = 0;
-  folly::for_each(range, [&](auto, auto index) {
-    ++iterations;
-    if (index == 1) {
-      return folly::loop_break;
-    }
-    return folly::loop_continue;
-  });
-  EXPECT_EQ(iterations, 2);
-}
-
-TEST(Foreach, ForEachFunctionInitializerListBasic) {
-  folly::for_each(std::initializer_list<int>{1, 2, 3}, [](auto ele) { ++ele; });
-}
-
-TEST(Foreach, ForEachFunctionTestForward) {
-  using folly::test::TestRValueConstruct;
-  auto range_one = std::vector<TestRValueConstruct>{};
-  range_one.resize(3);
-
-  folly::for_each(std::move(range_one), [](auto ele) {
-    EXPECT_FALSE(ele.constructed_from_rvalue);
-  });
-
-  folly::for_each(
-      std::make_tuple(TestRValueConstruct{}, TestRValueConstruct{}),
-      [](auto ele) { EXPECT_TRUE(ele.constructed_from_rvalue); });
-}
-
-TEST(Foreach, ForEachFunctionAdlIterable) {
-  auto range = test::TestAdlIterable{};
-  auto iterations = 0;
-  folly::for_each(range, [&](auto ele, auto index) {
-    ++iterations;
-    EXPECT_EQ(ele, index);
-  });
-  EXPECT_EQ(iterations, 4);
-}
-
-TEST(ForEach, FetchRandomAccessIterator) {
-  auto vec = std::vector<int>{1, 2, 3};
-  auto& second = folly::fetch(vec, 1);
-  EXPECT_EQ(second, 2);
-  second = 3;
-  EXPECT_EQ(second, 3);
-}
-
-TEST(ForEach, FetchIndexing) {
-  auto mp = std::map<int, int>{{1, 2}};
-  auto& ele = folly::fetch(mp, 1);
-  EXPECT_EQ(ele, 2);
-  ele = 3;
-  EXPECT_EQ(ele, 3);
-}
-
-TEST(ForEach, FetchTuple) {
-  auto mp = std::make_tuple(1, 2, 3);
-  auto& ele = folly::fetch(mp, std::integral_constant<int, 1>{});
-  EXPECT_EQ(ele, 2);
-  ele = 3;
-  EXPECT_EQ(ele, 3);
-}
-
-TEST(ForEach, FetchTestPreferIterator) {
-  auto range = test::TestBothIndexingAndIter{};
-  auto& ele = folly::fetch(range, 0);
-  EXPECT_TRUE(range.called_begin);
-  EXPECT_EQ(ele, 0);
-  ele = 2;
-  EXPECT_EQ(folly::fetch(range, 0), 2);
-}
-
-TEST(Foreach, ForEachRvalue) {
-  const char* const hello = "hello";
-  int n = 0;
-  FOR_EACH(it, std::string(hello)) {
-    ++n;
-  }
-  EXPECT_EQ(strlen(hello), n);
-  FOR_EACH_R(it, std::string(hello)) {
-    --n;
-    EXPECT_EQ(hello[n], *it);
-  }
-  EXPECT_EQ(0, n);
-}
-
-TEST(Foreach, ForEachNested) {
-  const std::string hello = "hello";
-  size_t n = 0;
-  FOR_EACH(i, hello) {
-    FOR_EACH(j, hello) {
-      ++n;
-    }
-  }
-  auto len = hello.size();
-  EXPECT_EQ(len * len, n);
-}
-
-TEST(Foreach, ForEachKV) {
-  std::map<std::string, int> testMap;
-  testMap["abc"] = 1;
-  testMap["def"] = 2;
-  std::string keys = "";
-  int values = 0;
-  int numEntries = 0;
-  FOR_EACH_KV (key, value, testMap) {
-    keys += key;
-    values += value;
-    ++numEntries;
-  }
-  EXPECT_EQ("abcdef", keys);
-  EXPECT_EQ(3, values);
-  EXPECT_EQ(2, numEntries);
-}
-
-TEST(Foreach, ForEachKVBreak) {
-  std::map<std::string, int> testMap;
-  testMap["abc"] = 1;
-  testMap["def"] = 2;
-  std::string keys = "";
-  int values = 0;
-  int numEntries = 0;
-  FOR_EACH_KV (key, value, testMap) {
-    keys += key;
-    values += value;
-    ++numEntries;
-    break;
-  }
-  EXPECT_EQ("abc", keys);
-  EXPECT_EQ(1, values);
-  EXPECT_EQ(1, numEntries);
-}
-
-TEST(Foreach, ForEachKvWithMultiMap) {
-  std::multimap<std::string, int> testMap;
-  testMap.insert(std::make_pair("abc", 1));
-  testMap.insert(std::make_pair("abc", 2));
-  testMap.insert(std::make_pair("def", 3));
-  std::string keys = "";
-  int values = 0;
-  int numEntries = 0;
-  FOR_EACH_KV (key, value, testMap) {
-    keys += key;
-    values += value;
-    ++numEntries;
-  }
-  EXPECT_EQ("abcabcdef", keys);
-  EXPECT_EQ(6, values);
-  EXPECT_EQ(3, numEntries);
-}
-
-TEST(Foreach, ForEachEnumerate) {
-  std::vector<int> vv;
-  int sumAA = 0;
-  int sumIter = 0;
-  int numIterations = 0;
-  FOR_EACH_ENUMERATE(aa, iter, vv) {
-    sumAA += aa;
-    sumIter += *iter;
-    ++numIterations;
-  }
-  EXPECT_EQ(sumAA, 0);
-  EXPECT_EQ(sumIter, 0);
-  EXPECT_EQ(numIterations, 0);
-
-  vv.push_back(1);
-  vv.push_back(3);
-  vv.push_back(5);
-  FOR_EACH_ENUMERATE(aa, iter, vv) {
-    sumAA += aa;
-    sumIter += *iter;
-    ++numIterations;
-  }
-  EXPECT_EQ(sumAA, 3);   // 0 + 1 + 2
-  EXPECT_EQ(sumIter, 9); // 1 + 3 + 5
-  EXPECT_EQ(numIterations, 3);
-}
-
-TEST(Foreach, ForEachEnumerateBreak) {
-  std::vector<int> vv;
-  int sumAA = 0;
-  int sumIter = 0;
-  int numIterations = 0;
-  vv.push_back(1);
-  vv.push_back(2);
-  vv.push_back(4);
-  vv.push_back(8);
-  FOR_EACH_ENUMERATE(aa, iter, vv) {
-    sumAA += aa;
-    sumIter += *iter;
-    ++numIterations;
-    if (aa == 1) {
-      break;
-    }
-  }
-  EXPECT_EQ(sumAA, 1);   // 0 + 1
-  EXPECT_EQ(sumIter, 3); // 1 + 2
-  EXPECT_EQ(numIterations, 2);
-}
-
-TEST(Foreach, ForEachRangeR) {
-  int sum = 0;
-
-  FOR_EACH_RANGE_R (i, 0, 0) {
-    sum += i;
-  }
-  EXPECT_EQ(0, sum);
-
-  FOR_EACH_RANGE_R (i, 0, -1) {
-    sum += i;
-  }
-  EXPECT_EQ(0, sum);
-
-  FOR_EACH_RANGE_R (i, 0, 5) {
-    sum += i;
-  }
-  EXPECT_EQ(10, sum);
-
-  std::list<int> lst = { 0, 1, 2, 3, 4 };
-  sum = 0;
-  FOR_EACH_RANGE_R (i, lst.begin(), lst.end()) {
-    sum += *i;
-  }
-  EXPECT_EQ(10, sum);
-}
diff --git a/folly/test/IteratorTest.cpp b/folly/test/IteratorTest.cpp
deleted file mode 100644 (file)
index 0be32bf..0000000
+++ /dev/null
@@ -1,544 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <deque>
-#include <functional>
-#include <map>
-#include <set>
-#include <tuple>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include <folly/Iterator.h>
-#include <folly/portability/GTest.h>
-
-namespace {
-/**
- * Container type used for unit tests.
- */
-template <typename T>
-using Container = std::deque<T>;
-
-// Constructor and assignment operator call counters for struct Object.
-std::size_t gDefaultCtrCnt;
-std::size_t gCopyCtrCnt;
-std::size_t gMoveCtrCnt;
-std::size_t gExplicitCtrCnt;
-std::size_t gMultiargCtrCnt;
-std::size_t gCopyOpCnt;
-std::size_t gMoveOpCnt;
-std::size_t gConvertOpCnt;
-
-/**
- * Class that increases various counters to keep track of how objects have
- * been constructed or assigned to, to verify iterator behavior.
- */
-struct Object {
-  Object() {
-    ++gDefaultCtrCnt;
-  }
-  Object(const Object&) {
-    ++gCopyCtrCnt;
-  }
-  Object(Object&&) noexcept {
-    ++gMoveCtrCnt;
-  }
-  explicit Object(int) {
-    ++gExplicitCtrCnt;
-  }
-  explicit Object(int, int) {
-    ++gMultiargCtrCnt;
-  }
-  Object& operator=(const Object&) {
-    ++gCopyOpCnt;
-    return *this;
-  }
-  Object& operator=(Object&&) noexcept {
-    ++gMoveOpCnt;
-    return *this;
-  }
-  Object& operator=(int) noexcept {
-    ++gConvertOpCnt;
-    return *this;
-  }
-};
-
-/**
- * Reset all call counters to 0.
- */
-void init_counters() {
-  gDefaultCtrCnt = gCopyCtrCnt = gMoveCtrCnt = gExplicitCtrCnt =
-      gMultiargCtrCnt = gCopyOpCnt = gMoveOpCnt = gConvertOpCnt = 0;
-}
-
-/**
- * Test for iterator copy and move.
- */
-template <typename Iterator>
-void copy_and_move_test(Container<int>& q, Iterator it) {
-  assert(q.empty());
-  const auto it2(it); // copy construct
-  it = it2; // copy assign from const
-  it = it; // self assign
-  auto it3(std::move(it)); // move construct
-  it = std::move(it3); // move assign
-  // Make sure iterator still works.
-  it = 4711; // emplace
-  EXPECT_EQ(q, Container<int>{4711});
-}
-
-/**
- * Test for emplacement with perfect forwarding.
- */
-template <typename Iterator>
-void emplace_test(Container<Object>& q, Iterator it) {
-  using folly::make_emplace_args;
-  assert(q.empty());
-  init_counters();
-  it = Object{}; // default construct + move construct
-  Object obj; // default construct
-  it = obj; // copy construct
-  it = std::move(obj); // move construct
-  const Object obj2; // default construct
-  it = obj2; // copy construct from const
-  it = std::move(obj2); // copy construct (const defeats move)
-  it = 0; // explicit construct
-  it = make_emplace_args(0, 0); // explicit multiarg construct
-  it = std::make_pair(0, 0); // implicit multiarg construct
-  it = std::make_tuple(0, 0); // implicit multiarg construct
-  auto args = make_emplace_args(Object{}); // default construct + move construct
-  it = args; // copy construct
-  it = const_cast<const decltype(args)&>(args); // copy construct from const
-  it = std::move(args); // move construct
-  auto args2 = std::make_tuple(Object{}); // default construct + move construct
-  it = args2; // (implicit multiarg) copy construct
-  it = std::move(args2); // (implicit multiarg) move construct
-  auto args3 = std::make_pair(0, 0);
-  it = args3; // implicit multiarg construct
-  it = std::move(args3); // implicit multiarg construct
-  ASSERT_EQ(q.size(), 16);
-  EXPECT_EQ(gDefaultCtrCnt, 5);
-  EXPECT_EQ(gCopyCtrCnt, 6);
-  EXPECT_EQ(gMoveCtrCnt, 6);
-  EXPECT_EQ(gExplicitCtrCnt, 1);
-  EXPECT_EQ(gMultiargCtrCnt, 5);
-  EXPECT_EQ(gCopyOpCnt, 0);
-  EXPECT_EQ(gMoveOpCnt, 0);
-  EXPECT_EQ(gConvertOpCnt, 0);
-}
-}
-
-using namespace folly;
-
-/**
- * Basic tests for folly::emplace_iterator.
- */
-TEST(EmplaceIterator, EmplacerTest) {
-  {
-    Container<int> q;
-    copy_and_move_test(q, emplacer(q, q.begin()));
-  }
-  {
-    Container<Object> q;
-    emplace_test(q, emplacer(q, q.begin()));
-  }
-  {
-    Container<int> q;
-    auto it = emplacer(q, q.begin());
-    it = 0;
-    it = 1;
-    it = 2;
-    it = emplacer(q, q.begin());
-    it = 3;
-    it = 4;
-    EXPECT_EQ(q, Container<int>({3, 4, 0, 1, 2}));
-  }
-}
-
-/**
- * Basic tests for folly::front_emplace_iterator.
- */
-TEST(EmplaceIterator, FrontEmplacerTest) {
-  {
-    Container<int> q;
-    copy_and_move_test(q, front_emplacer(q));
-  }
-  {
-    Container<Object> q;
-    emplace_test(q, front_emplacer(q));
-  }
-  {
-    Container<int> q;
-    auto it = front_emplacer(q);
-    it = 0;
-    it = 1;
-    it = 2;
-    it = front_emplacer(q);
-    it = 3;
-    it = 4;
-    EXPECT_EQ(q, Container<int>({4, 3, 2, 1, 0}));
-  }
-}
-
-/**
- * Basic tests for folly::back_emplace_iterator.
- */
-TEST(EmplaceIterator, BackEmplacerTest) {
-  {
-    Container<int> q;
-    copy_and_move_test(q, back_emplacer(q));
-  }
-  {
-    Container<Object> q;
-    emplace_test(q, back_emplacer(q));
-  }
-  {
-    Container<int> q;
-    auto it = back_emplacer(q);
-    it = 0;
-    it = 1;
-    it = 2;
-    it = back_emplacer(q);
-    it = 3;
-    it = 4;
-    EXPECT_EQ(q, Container<int>({0, 1, 2, 3, 4}));
-  }
-}
-
-/**
- * Basic tests for folly::hint_emplace_iterator.
- */
-TEST(EmplaceIterator, HintEmplacerTest) {
-  {
-    init_counters();
-    std::map<int, Object> m;
-    auto it = hint_emplacer(m, m.end());
-    it = make_emplace_args(
-        std::piecewise_construct,
-        std::forward_as_tuple(0),
-        std::forward_as_tuple(0));
-    it = make_emplace_args(
-        std::piecewise_construct,
-        std::forward_as_tuple(1),
-        std::forward_as_tuple(0, 0));
-    it = make_emplace_args(
-        std::piecewise_construct,
-        std::forward_as_tuple(2),
-        std::forward_as_tuple(Object{}));
-    ASSERT_EQ(m.size(), 3);
-    EXPECT_EQ(gDefaultCtrCnt, 1);
-    EXPECT_EQ(gCopyCtrCnt, 0);
-    EXPECT_EQ(gMoveCtrCnt, 1);
-    EXPECT_EQ(gExplicitCtrCnt, 1);
-    EXPECT_EQ(gMultiargCtrCnt, 1);
-    EXPECT_EQ(gCopyOpCnt, 0);
-    EXPECT_EQ(gMoveOpCnt, 0);
-    EXPECT_EQ(gConvertOpCnt, 0);
-  }
-  {
-    struct O {
-      explicit O(int i) : i(i) {}
-      bool operator<(const O& other) const {
-        return i < other.i;
-      }
-      bool operator==(const O& other) const {
-        return i == other.i;
-      }
-      int i;
-    };
-    std::vector<int> v1 = {0, 1, 2, 3, 4};
-    std::vector<int> v2 = {0, 2, 4};
-    std::set<O> diff;
-    std::set_difference(
-        v1.begin(),
-        v1.end(),
-        v2.begin(),
-        v2.end(),
-        hint_emplacer(diff, diff.end()));
-    ASSERT_EQ(diff, std::set<O>({O(1), O(3)}));
-  }
-}
-
-/**
- * Test std::copy() with explicit conversion. This would not compile with a
- * std::back_insert_iterator, because the constructor of Object that takes a
- * single int is explicit.
- */
-TEST(EmplaceIterator, Copy) {
-  init_counters();
-  Container<int> in({0, 1, 2});
-  Container<Object> out;
-  std::copy(in.begin(), in.end(), back_emplacer(out));
-  EXPECT_EQ(3, out.size());
-  EXPECT_EQ(gDefaultCtrCnt, 0);
-  EXPECT_EQ(gCopyCtrCnt, 0);
-  EXPECT_EQ(gMoveCtrCnt, 0);
-  EXPECT_EQ(gExplicitCtrCnt, 3);
-  EXPECT_EQ(gMultiargCtrCnt, 0);
-  EXPECT_EQ(gCopyOpCnt, 0);
-  EXPECT_EQ(gMoveOpCnt, 0);
-  EXPECT_EQ(gConvertOpCnt, 0);
-}
-
-/**
- * Test std::transform() with multi-argument constructors. This would require
- * a temporary Object with std::back_insert_iterator.
- */
-TEST(EmplaceIterator, Transform) {
-  init_counters();
-  Container<int> in({0, 1, 2});
-  Container<Object> out;
-  std::transform(in.begin(), in.end(), back_emplacer(out), [](int i) {
-    return make_emplace_args(i, i);
-  });
-  EXPECT_EQ(3, out.size());
-  EXPECT_EQ(gDefaultCtrCnt, 0);
-  EXPECT_EQ(gCopyCtrCnt, 0);
-  EXPECT_EQ(gMoveCtrCnt, 0);
-  EXPECT_EQ(gExplicitCtrCnt, 0);
-  EXPECT_EQ(gMultiargCtrCnt, 3);
-  EXPECT_EQ(gCopyOpCnt, 0);
-  EXPECT_EQ(gMoveOpCnt, 0);
-  EXPECT_EQ(gConvertOpCnt, 0);
-}
-
-/**
- * Test multi-argument store and forward.
- */
-TEST(EmplaceIterator, EmplaceArgs) {
-  Object o1;
-  const Object o2;
-  Object& o3 = o1;
-  const Object& o4 = o3;
-  Object o5;
-
-  {
-    // Test copy construction.
-    auto args = make_emplace_args(0, o1, o2, o3, o4, Object{}, std::cref(o2));
-    init_counters();
-    auto args2 = args;
-    EXPECT_EQ(gDefaultCtrCnt, 0);
-    EXPECT_EQ(gCopyCtrCnt, 5);
-    EXPECT_EQ(gMoveCtrCnt, 0);
-    EXPECT_EQ(gExplicitCtrCnt, 0);
-    EXPECT_EQ(gMultiargCtrCnt, 0);
-    EXPECT_EQ(gCopyOpCnt, 0);
-    EXPECT_EQ(gMoveOpCnt, 0);
-    EXPECT_EQ(gConvertOpCnt, 0);
-
-    // Test copy assignment.
-    init_counters();
-    args = args2;
-    EXPECT_EQ(gDefaultCtrCnt, 0);
-    EXPECT_EQ(gCopyCtrCnt, 0);
-    EXPECT_EQ(gMoveCtrCnt, 0);
-    EXPECT_EQ(gExplicitCtrCnt, 0);
-    EXPECT_EQ(gMultiargCtrCnt, 0);
-    EXPECT_EQ(gCopyOpCnt, 5);
-    EXPECT_EQ(gMoveOpCnt, 0);
-    EXPECT_EQ(gConvertOpCnt, 0);
-  }
-
-  {
-    // Test RVO.
-    init_counters();
-    auto args = make_emplace_args(
-        0, o1, o2, o3, o4, Object{}, std::cref(o2), rref(std::move(o5)));
-    EXPECT_EQ(gDefaultCtrCnt, 1);
-    EXPECT_EQ(gCopyCtrCnt, 4);
-    EXPECT_EQ(gMoveCtrCnt, 1);
-    EXPECT_EQ(gExplicitCtrCnt, 0);
-    EXPECT_EQ(gMultiargCtrCnt, 0);
-    EXPECT_EQ(gCopyOpCnt, 0);
-    EXPECT_EQ(gMoveOpCnt, 0);
-    EXPECT_EQ(gConvertOpCnt, 0);
-
-    // Test move construction.
-    init_counters();
-    auto args2 = std::move(args);
-    EXPECT_EQ(gDefaultCtrCnt, 0);
-    EXPECT_EQ(gCopyCtrCnt, 0);
-    EXPECT_EQ(gMoveCtrCnt, 5);
-    EXPECT_EQ(gExplicitCtrCnt, 0);
-    EXPECT_EQ(gMultiargCtrCnt, 0);
-    EXPECT_EQ(gCopyOpCnt, 0);
-    EXPECT_EQ(gMoveOpCnt, 0);
-    EXPECT_EQ(gConvertOpCnt, 0);
-
-    // Test move assignment.
-    init_counters();
-    args = std::move(args2);
-    EXPECT_EQ(gDefaultCtrCnt, 0);
-    EXPECT_EQ(gCopyCtrCnt, 0);
-    EXPECT_EQ(gMoveCtrCnt, 0);
-    EXPECT_EQ(gExplicitCtrCnt, 0);
-    EXPECT_EQ(gMultiargCtrCnt, 0);
-    EXPECT_EQ(gCopyOpCnt, 0);
-    EXPECT_EQ(gMoveOpCnt, 5);
-    EXPECT_EQ(gConvertOpCnt, 0);
-
-    // Make sure arguments are stored correctly. lvalues by reference, rvalues
-    // by (moved) copy. Rvalues cannot be stored by reference because they may
-    // refer to an expired temporary by the time they are accessed.
-    static_assert(
-        std::is_same<
-            int,
-            std::tuple_element_t<0, decltype(args)::storage_type>>::value,
-        "");
-    static_assert(
-        std::is_same<
-            Object,
-            std::tuple_element_t<1, decltype(args)::storage_type>>::value,
-        "");
-    static_assert(
-        std::is_same<
-            Object,
-            std::tuple_element_t<2, decltype(args)::storage_type>>::value,
-        "");
-    static_assert(
-        std::is_same<
-            Object,
-            std::tuple_element_t<3, decltype(args)::storage_type>>::value,
-        "");
-    static_assert(
-        std::is_same<
-            Object,
-            std::tuple_element_t<4, decltype(args)::storage_type>>::value,
-        "");
-    static_assert(
-        std::is_same<
-            Object,
-            std::tuple_element_t<5, decltype(args)::storage_type>>::value,
-        "");
-    static_assert(
-        std::is_same<
-            std::reference_wrapper<const Object>,
-            std::tuple_element_t<6, decltype(args)::storage_type>>::value,
-        "");
-    static_assert(
-        std::is_same<
-            rvalue_reference_wrapper<Object>,
-            std::tuple_element_t<7, decltype(args)::storage_type>>::value,
-        "");
-
-    // Check whether args.get() restores the original argument type for
-    // rvalue references to emplace_args.
-    static_assert(
-        std::is_same<int&&, decltype(get_emplace_arg<0>(std::move(args)))>::
-            value,
-        "");
-    static_assert(
-        std::is_same<Object&, decltype(get_emplace_arg<1>(std::move(args)))>::
-            value,
-        "");
-    static_assert(
-        std::is_same<
-            const Object&,
-            decltype(get_emplace_arg<2>(std::move(args)))>::value,
-        "");
-    static_assert(
-        std::is_same<Object&, decltype(get_emplace_arg<3>(std::move(args)))>::
-            value,
-        "");
-    static_assert(
-        std::is_same<
-            const Object&,
-            decltype(get_emplace_arg<4>(std::move(args)))>::value,
-        "");
-    static_assert(
-        std::is_same<Object&&, decltype(get_emplace_arg<5>(std::move(args)))>::
-            value,
-        "");
-    static_assert(
-        std::is_same<
-            const Object&,
-            decltype(get_emplace_arg<6>(std::move(args)))>::value,
-        "");
-    static_assert(
-        std::is_same<Object&&, decltype(get_emplace_arg<7>(std::move(args)))>::
-            value,
-        "");
-
-    // lvalue references to emplace_args should behave mostly like std::tuples.
-    // Note that get_emplace_arg<7>(args) does not compile, because
-    // folly::rvalue_reference_wrappers can only be unwrapped through an rvalue
-    // reference.
-    static_assert(
-        std::is_same<int&, decltype(get_emplace_arg<0>(args))>::value, "");
-    static_assert(
-        std::is_same<Object&, decltype(get_emplace_arg<1>(args))>::value, "");
-    static_assert(
-        std::is_same<Object&, decltype(get_emplace_arg<2>(args))>::value, "");
-    static_assert(
-        std::is_same<Object&, decltype(get_emplace_arg<3>(args))>::value, "");
-    static_assert(
-        std::is_same<Object&, decltype(get_emplace_arg<4>(args))>::value, "");
-    static_assert(
-        std::is_same<Object&, decltype(get_emplace_arg<5>(args))>::value, "");
-    static_assert(
-        std::is_same<const Object&, decltype(get_emplace_arg<6>(args))>::value,
-        "");
-  }
-}
-
-/**
- * Test implicit unpacking.
- */
-TEST(EmplaceIterator, ImplicitUnpack) {
-  static std::size_t multiCtrCnt;
-  static std::size_t pairCtrCnt;
-  static std::size_t tupleCtrCnt;
-
-  struct Object2 {
-    Object2(int, int) {
-      ++multiCtrCnt;
-    }
-    explicit Object2(const std::pair<int, int>&) {
-      ++pairCtrCnt;
-    }
-    explicit Object2(const std::tuple<int, int>&) {
-      ++tupleCtrCnt;
-    }
-  };
-
-  auto test = [](auto&& it, bool expectUnpack) {
-    multiCtrCnt = pairCtrCnt = tupleCtrCnt = 0;
-    it = std::make_pair(0, 0);
-    it = std::make_tuple(0, 0);
-    if (expectUnpack) {
-      EXPECT_EQ(multiCtrCnt, 2);
-      EXPECT_EQ(pairCtrCnt, 0);
-      EXPECT_EQ(tupleCtrCnt, 0);
-    } else {
-      EXPECT_EQ(multiCtrCnt, 0);
-      EXPECT_EQ(pairCtrCnt, 1);
-      EXPECT_EQ(tupleCtrCnt, 1);
-    }
-  };
-
-  Container<Object2> q;
-
-  test(emplacer(q, q.begin()), true);
-  test(emplacer<false>(q, q.begin()), false);
-  test(front_emplacer(q), true);
-  test(front_emplacer<false>(q), false);
-  test(back_emplacer(q), true);
-  test(back_emplacer<false>(q), false);
-}
index e140d61f0c28ca1b1bdb4faa4897155a7aa8b642..2199f8906d1194163f022cefaab015c3986f2496 100644 (file)
@@ -82,10 +82,10 @@ sorted_vector_types_test_SOURCES = sorted_vector_test.cpp
 sorted_vector_types_test_LDADD = libfollytestmain.la
 
 
-foreach_test_SOURCES = ForeachTest.cpp
+foreach_test_SOURCES = ../container/test/ForeachTest.cpp
 foreach_test_LDADD = libfollytestmain.la
 
-foreach_benchmark_SOURCES = ForeachBenchmark.cpp
+foreach_benchmark_SOURCES = ../container/test/ForeachBenchmark.cpp
 foreach_benchmark_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la
 check_PROGRAMS += foreach_benchmark
 
index bd793aedb44b357dba858cfa2b10a8573021c9de..d1ba1867aae9ef873814103595b6c92a76a9342b 100644 (file)
@@ -22,7 +22,7 @@
 #include <glog/logging.h>
 
 #include <folly/Benchmark.h>
-#include <folly/Foreach.h>
+#include <folly/container/Foreach.h>
 
 #if FOLLY_HAVE_EXTRANDOM_SFMT19937
 #include <ext/random>
index 786553b27a451f761be3a65737191eed252871dd..7b2bda4168d445a5485aa9394073a9490865e790 100644 (file)
@@ -22,7 +22,7 @@
 #include <string>
 
 #include <folly/Benchmark.h>
-#include <folly/Foreach.h>
+#include <folly/container/Foreach.h>
 
 using namespace folly;
 using namespace std;
diff --git a/folly/test/SparseByteSetBench.cpp b/folly/test/SparseByteSetBench.cpp
deleted file mode 100644 (file)
index 05b8dd7..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/***
- *  A benchmark comparing SparseByteSet to bitset<256> and bool[256].
- */
-
-#include <folly/Benchmark.h>
-#include <folly/Format.h>
-#include <folly/SparseByteSet.h>
-#include <folly/portability/GFlags.h>
-#include <bitset>
-#include <random>
-#include <vector>
-
-using namespace std;
-using namespace folly;
-
-namespace {
-
-//  Interface-identical to SparseByteSet. So that we can do compile-time
-//  polymorphism.
-class BitSetWrapper {
- public:
-  inline bool add(uint8_t i) {
-    auto r = !contains(i);
-    if (r) {
-      rep_[i] = true;
-    }
-    return r;
-  }
-  inline bool contains(uint8_t i) {
-    return rep_[i];
-  }
- private:
-  bitset<256> rep_;
-};
-class BoolArraySet {
- public:
-  BoolArraySet() {
-    memset(rep_, 0, sizeof(rep_));
-  }
-  inline bool add(uint8_t i) {
-    auto r = !contains(i);
-    if (r) {
-      rep_[i] = true;
-    }
-    return r;
-  }
-  inline bool contains(uint8_t i) {
-    return rep_[i];
-  }
- private:
-  bool rep_[256];
-};
-
-template <typename Coll>
-void rand_bench(int iters, size_t size_add, size_t size_contains) {
-  BenchmarkSuspender braces;
-  vector<uint8_t> seq_add;
-  vector<uint8_t> seq_contains;
-  mt19937 rng;
-  uniform_int_distribution<uint8_t> dist;
-  for (size_t i = 0; i < size_add; ++i) {
-    seq_add.push_back(dist(rng));
-  }
-  for (size_t i = 0; i < size_contains; ++i) {
-    seq_contains.push_back(dist(rng));
-  }
-  braces.dismissing([&] {
-      while (iters--) {
-        Coll coll;
-        for (auto b : seq_add) {
-          coll.add(b);
-        }
-        bool q {};
-        for (auto b : seq_contains) {
-          q ^= coll.contains(b);
-        }
-        doNotOptimizeAway(q);
-      }
-  });
-}
-
-void setup_rand_bench() {
-  vector<pair<size_t, size_t>> rand_bench_params = {
-    {4, 4},
-    {4, 16},
-    {4, 64},
-    {4, 256},
-    {16, 4},
-    {16, 16},
-    {16, 64},
-    {16, 256},
-    {64, 4},
-    {64, 16},
-    {64, 64},
-    {64, 256},
-    {256, 4},
-    {256, 16},
-    {256, 64},
-    {256, 256},
-  };
-  for (auto kvp : rand_bench_params) {
-    size_t size_add, size_contains;
-    tie(size_add, size_contains) = kvp;
-    addBenchmark(
-        __FILE__,
-        sformat("bitset_rand_bench({}, {})",
-                size_add, size_contains).c_str(),
-        [=](int iters) {
-          rand_bench<BitSetWrapper>(iters, size_add, size_contains);
-          return iters;
-        });
-    addBenchmark(
-        __FILE__,
-        sformat("%bool_array_set_rand_bench({}, {})",
-                size_add, size_contains).c_str(),
-        [=](int iters) {
-          rand_bench<BoolArraySet>(iters, size_add, size_contains);
-          return iters;
-        });
-    addBenchmark(
-        __FILE__,
-        sformat("%sparse_byte_set_rand_bench({}, {})",
-                size_add, size_contains).c_str(),
-        [=](int iters) {
-          rand_bench<SparseByteSet>(iters, size_add, size_contains);
-          return iters;
-        });
-    addBenchmark(
-        __FILE__,
-        "-",
-        [](int) { return 0; });
-  }
-}
-
-}
-
-int main(int argc, char** argv) {
-  gflags::ParseCommandLineFlags(&argc, &argv, true);
-  setup_rand_bench();
-  runBenchmarks();
-  return 0;
-}
diff --git a/folly/test/SparseByteSetTest.cpp b/folly/test/SparseByteSetTest.cpp
deleted file mode 100644 (file)
index 76f4896..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2017 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <folly/SparseByteSet.h>
-
-#include <cstdint>
-#include <limits>
-#include <random>
-#include <set>
-
-#include <folly/portability/GTest.h>
-
-using namespace std;
-using namespace folly;
-
-namespace {
-
-class SparseByteSetTest : public testing::Test {
- protected:
-  using lims = numeric_limits<uint8_t>;
-  SparseByteSet s;
-};
-
-}
-
-TEST_F(SparseByteSetTest, empty) {
-  for (auto c = lims::min(); c < lims::max(); ++c) {
-    EXPECT_FALSE(s.contains(c));
-  }
-}
-
-TEST_F(SparseByteSetTest, each) {
-  for (auto c = lims::min(); c < lims::max(); ++c) {
-    EXPECT_TRUE(s.add(c));
-    EXPECT_TRUE(s.contains(c));
-  }
-  for (auto c = lims::min(); c < lims::max(); ++c) {
-    EXPECT_FALSE(s.add(c));
-    EXPECT_TRUE(s.contains(c));
-  }
-}
-
-TEST_F(SparseByteSetTest, each_random) {
-  mt19937 rng;
-  uniform_int_distribution<uint16_t> dist{lims::min(), lims::max()};
-  set<uint8_t> added;
-  while (added.size() <= lims::max()) {
-    auto c = uint8_t(dist(rng));
-    EXPECT_EQ(added.count(c), s.contains(c));
-    EXPECT_EQ(!added.count(c), s.add(c));
-    added.insert(c);
-    EXPECT_TRUE(added.count(c)); // sanity
-    EXPECT_TRUE(s.contains(c));
-  }
-}
index 48951a621ff7a922686435da671d6ded7a0f72cd..0d34da316d81520f6280e758521b60d4b4d93ac7 100644 (file)
@@ -16,9 +16,9 @@
 
 #pragma once
 
-#include <folly/Foreach.h>
 #include <folly/Random.h>
 #include <folly/Synchronized.h>
+#include <folly/container/Foreach.h>
 #include <folly/portability/GTest.h>
 #include <glog/logging.h>
 #include <algorithm>