Unsafe pre-sorted construction for sorted-vector containers
authorYedidya Feldblum <yfeldblum@fb.com>
Fri, 12 Jan 2018 05:56:07 +0000 (21:56 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Fri, 12 Jan 2018 06:07:04 +0000 (22:07 -0800)
Summary:
[Folly] Unsafe pre-sorted construction for sorted-vector containers.

If the backing container type can be constructed directly in sorted order or can be determined in advance to be in sorted order, then a special constructor can help code take advantage of this condition by avoiding an extra invocation of `std::sort`.

Reviewed By: spalamarchuk

Differential Revision: D6708379

fbshipit-source-id: 25d886b0814dc9230c6046ed1e7f199fac47754e

folly/Utility.h
folly/sorted_vector_types.h
folly/test/UtilityTest.cpp
folly/test/sorted_vector_test.cpp

index 1969a9644b20e63c9a0e92e5878c6b3df62de375..94141c181c4f56b4980c59e2938d9a0ebf27598c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2016-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.
@@ -262,6 +262,28 @@ inline in_place_index_tag<I> in_place_index(in_place_index_tag<I> = {}) {
 struct initlist_construct_t {};
 constexpr initlist_construct_t initlist_construct{};
 
+/**
+ * A generic tag type to indicate that some constructor or method is in some way
+ * unsafe and should be used only with extreme care and with full test coverage,
+ * if ever.
+ *
+ * Example:
+ *
+ *  void takes_numbers(std::vector<int> alist) {
+ *    std::sort(alist.begin(), alist.end());
+ *    takes_numbers_assume_sorted(folly::unsafe, alist);
+ *  }
+ *
+ *  void takes_numbers_assume_sorted(folly::unsafe_t, std::vector<int> alist) {
+ *    assert(std::is_sorted(alist.begin(), alist.end())); // debug mode only
+ *    for (i : alist) {
+ *      // some behavior which is defined and safe only when alist is sorted ...
+ *    }
+ *  }
+ */
+struct unsafe_t {};
+constexpr unsafe_t unsafe{};
+
 /**
  * A simple function object that passes its argument through unchanged.
  *
index f7fbba247e315a55e31dce46b66702a82a91320a..06d238ffa13518387364389f5664241b8f81ebed 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2011-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.
@@ -60,6 +60,7 @@
 #pragma once
 
 #include <algorithm>
+#include <cassert>
 #include <initializer_list>
 #include <iterator>
 #include <stdexcept>
@@ -70,6 +71,7 @@
 #include <boost/operators.hpp>
 
 #include <folly/Traits.h>
+#include <folly/Utility.h>
 #include <folly/portability/BitsFunctexcept.h>
 
 namespace folly {
@@ -193,6 +195,13 @@ void bulk_insert(
         cont.end());
   }
 }
+
+template <typename Container, typename Compare>
+Container&& as_sorted(Container&& container, Compare const& comp) {
+  using namespace std;
+  std::sort(begin(container), end(container), comp);
+  return static_cast<Container&&>(container);
+}
 } // namespace detail
 
 //////////////////////////////////////////////////////////////////////
@@ -285,10 +294,28 @@ class sorted_vector_set
   // Note that `sorted_vector_set(const Container& container)` is not provided,
   // since the purpose of this constructor is to avoid an unnecessary copy.
   explicit sorted_vector_set(
+      Container&& container,
+      const Compare& comp = Compare())
+      : sorted_vector_set(
+            unsafe,
+            detail::as_sorted(std::move(container), comp),
+            comp) {}
+
+  // Construct a sorted_vector_set by stealing the storage of a prefilled
+  // container. The container must be sorted, as unsafe_t hints. This supports
+  // bulk construction of sorted_vector_set with zero allocations, not counting
+  // those performed by the caller. (The iterator range constructor performs at
+  // least one allocation).
+  //
+  // Note that `sorted_vector_set(unsafe_t, const Container& container)` is not
+  // provided, since the purpose of this constructor is to avoid an unnecessary
+  // copy.
+  sorted_vector_set(
+      unsafe_t,
       Container&& container,
       const Compare& comp = Compare())
       : m_(comp, container.get_allocator()) {
-    std::sort(container.begin(), container.end(), value_comp());
+    assert(std::is_sorted(container.begin(), container.end(), value_comp()));
     m_.cont_.swap(container);
   }
 
@@ -596,10 +623,28 @@ class sorted_vector_map
   // Note that `sorted_vector_map(const Container& container)` is not provided,
   // since the purpose of this constructor is to avoid an unnecessary copy.
   explicit sorted_vector_map(
+      Container&& container,
+      const Compare& comp = Compare())
+      : sorted_vector_map(
+            unsafe,
+            detail::as_sorted(std::move(container), value_compare(comp)),
+            comp) {}
+
+  // Construct a sorted_vector_map by stealing the storage of a prefilled
+  // container. The container must be sorted, as unsafe_t hints. This supports
+  // bulk construction of sorted_vector_map with zero allocations, not counting
+  // those performed by the caller. (The iterator range constructor performs at
+  // least one allocation).
+  //
+  // Note that `sorted_vector_map(unsafe_t, const Container& container)` is not
+  // provided, since the purpose of this constructor is to avoid an unnecessary
+  // copy.
+  sorted_vector_map(
+      unsafe_t,
       Container&& container,
       const Compare& comp = Compare())
       : m_(value_compare(comp), container.get_allocator()) {
-    std::sort(container.begin(), container.end(), value_comp());
+    assert(std::is_sorted(container.begin(), container.end(), value_comp()));
     m_.cont_.swap(container);
   }
 
index c6f269e8345e9fedbe7f5343398fc468f08e54cf..d27f4675392e995a4af5868c1038e73ccd8cab8f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2016-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.
index 7efa4c8f0cfd351060f257cb7fcf7faddda74f34..e0692559442f0ffaaea16eeb554c752f68256cbd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2011-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.